Skip to content

Vault login ommited and going straight to test #24000

@lcx

Description

@lcx

Bug Description

I have been playing with hashicorp vault in the past weeks and had no idea why I currently kept getting error 400 when trying to connect to the vault even though using curl has no issues.
I am using AppRole login and checking the nginx logfile of the hashicorp server showed me only a GET to /v1/auth/token/lookup-self without the prior login using POST /v1/auth/approle/login

To better understand what is going on I added some debug lines in the vault.js and injected that into the docker container and logs confirmed that test() is called directly before doConnect() in packages/cli/src/modules/external-secrets.ee/providers/vault.ts

Switch from appRole to Token login also resulted with the same issue that the test() -> GET to /v1/auth/token/lookup-self is called without an prior authentication resulting in an empty currentToken.

To Reproduce

Good question. I would have to retry to set up the whole thing from scratch. Get it running with hashicorp, bring the whole thing somehow in an error state, edit the cofinguration again with knowing working data and it should trigger the error.

Expected behavior

Changes to the credentials for the vault should first try a reconnect.

Debug Info

I (or rather Claude) added a small workaround in test to trigger the reconnect if the currentToken is not set.
This solved the issue finally, test was triggered (before doConnect) due to the empty currentToken.
I replaced the test() in this file: https://github.com/n8n-io/n8n/blob/master/packages/cli/src/modules/external-secrets.ee/providers/vault.ts

The resulting JS looks like this:

async test() {
        this.logger.debug('[Test Debug] test() called');
        
        // If no token is set, we need to authenticate first
        if (!__classPrivateFieldGet(this, _VaultProvider_currentToken, "f")) {
            this.logger.debug('[Test Debug] No token set, attempting authentication');
            if (this.settings.authMethod === 'token') {
                __classPrivateFieldSet(this, _VaultProvider_currentToken, this.settings.token, "f");
            } else if (this.settings.authMethod === 'usernameAndPassword') {
                __classPrivateFieldSet(this, _VaultProvider_currentToken, await this.authUsernameAndPassword(this.settings.username, this.settings.password), "f");
            } else if (this.settings.authMethod === 'appRole') {
                __classPrivateFieldSet(this, _VaultProvider_currentToken, await this.authAppRole(this.settings.roleId, this.settings.secretId), "f");
            }
            if (!__classPrivateFieldGet(this, _VaultProvider_currentToken, "f")) {
                return [false, 'Authentication failed'];
            }
        }
        
        try {
            const [token, tokenResp] = await this.getTokenInfo();
            if (token === null) {
                this.logger.debug(`[Test Debug] Token is null, response status: ${tokenResp.status}`);
                if (tokenResp.status === 404) {
                    return [false, 'Could not find auth path. Try adding /v1/ to the end of your base URL.'];
                }
                return [false, 'Invalid credentials'];
            }
            const resp = await __classPrivateFieldGet(this, _VaultProvider_http, "f").request({
                method: 'GET',
                url: 'sys/mounts',
                responseType: 'json',
                validateStatus: () => true,
            });
            if (resp.status === 403) {
                return [
                    false,
                    "Couldn't list mounts. Please give these credentials 'read' access to sys/mounts.",
                ];
            }
            else if (resp.status !== 200) {
                return [
                    false,
                    "Couldn't list mounts but wasn't a permissions issue. Please consult your Vault admin.",
                ];
            }
            return [true];
        }
        catch (e) {
            this.logger.error(`[Test Debug] Exception caught: ${e}`);
            if (axios_1.default.isAxiosError(e)) {
                this.logger.error(`[Test Debug] Axios error - code: ${e.code}, message: ${e.message}`);
                if (e.code === 'ECONNREFUSED') {
                    return [
                        false,
                        'Connection refused. Please check the host and port of the server are correct.',
                    ];
                }
            }
            return [false];
        }
    }

after a successful connect it continues to work even when the workaround is removed.

this should be the workaround to the .ts file.

async test(): Promise<[boolean] | [boolean, string]> {
  // If no token is set, we need to authenticate first
  if (!this.#currentToken) {
    if (this.settings.authMethod === 'token') {
      this.#currentToken = this.settings.token;
    } else if (this.settings.authMethod === 'usernameAndPassword') {
      this.#currentToken = await this.authUsernameAndPassword(
        this.settings.username,
        this.settings.password,
      );
    } else if (this.settings.authMethod === 'appRole') {
      this.#currentToken = await this.authAppRole(
        this.settings.roleId,
        this.settings.secretId,
      );
    }
    if (!this.#currentToken) {
      return [false, 'Authentication failed'];
    }
  }

Operating System

docker

n8n Version

2.2.3

Node.js Version

?

Database

PostgreSQL

Execution mode

main (default)

Hosting

self hosted

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions