-
Notifications
You must be signed in to change notification settings - Fork 53.3k
Description
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