-
-
Notifications
You must be signed in to change notification settings - Fork 480
Make accessKey and secretKey optional and add support to use IRSA Iam role #1716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
All contributors have signed the CLA ✍️ ✅ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello,
Thank you for your contribution
It sounds like there exist tests that are failing after your work.
This documentation for developers is a guide to help you, take a look and feel free to ask questions (we think there are rooms for improvements)
13) UsersManager
multi-worker
"before all" hook in "multi-worker":
Error: Unable to get credentials: Error: Failed to get Credentials: TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["X-aws-ec2-metadata-token"]
at Client.checkAndRefreshCreds (node_modules/minio/dist/main/internal/client.ts:541:15)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Client.makeRequestStreamAsync (node_modules/minio/dist/main/internal/client.ts:706:5)
at Client.getBucketVersioning (node_modules/minio/dist/main/internal/client.ts:2159:21)
at MinIOExternalStorage.hasVersioning (app/server/lib/MinIOExternalStorage.ts:164:24)
at checkMinIOBucket (app/server/lib/configureMinIOExternalStorage.ts:74:8)
at CoreCreate.checkBackend (app/server/lib/ICreate.ts:176:9)
at FlexServer.addDoc (app/server/lib/FlexServer.ts:1463:7)
at MergedServer.run (app/server/MergedServer.ts:198:9)
at MergedServer.run (app/server/MergedServer.ts:220:11)
at Context.<anonymous> (test/gen-server/lib/homedb/UsersManager.ts:1220:7)
Caused by: Error: Failed to get Credentials: TypeError [ERR_INVALID_CHAR]: Invalid character in header content ["X-aws-ec2-metadata-token"]
at IamAwsProvider.fetchCredentials (node_modules/minio/dist/main/IamAwsProvider.ts:91:13)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at IamAwsProvider.getCredentials (node_modules/minio/dist/main/IamAwsProvider.ts:60:27)
at Client.checkAndRefreshCreds (node_modules/minio/dist/main/internal/client.ts:536:33)
at Client.makeRequestStreamAsync (node_modules/minio/dist/main/internal/client.ts:706:5)
at Client.getBucketVersioning (node_modules/minio/dist/main/internal/client.ts:2159:21)
at MinIOExternalStorage.hasVersioning (app/server/lib/MinIOExternalStorage.ts:164:24)
at checkMinIOBucket (app/server/lib/configureMinIOExternalStorage.ts:74:8)
at CoreCreate.checkBackend (app/server/lib/ICreate.ts:176:9)
at FlexServer.addDoc (app/server/lib/FlexServer.ts:1463:7)
at MergedServer.run (app/server/MergedServer.ts:198:9)
at MergedServer.run (app/server/MergedServer.ts:220:11)
at Context.<anonymous> (test/gen-server/lib/homedb/UsersManager.ts:1220:7)
Caused by: TypeError: Invalid character in header content ["X-aws-ec2-metadata-token"]
at ClientRequest.setHeader (node:_http_outgoing:703:3)
at new ClientRequest (node:_http_client:302:14)
at Object.request (node:http:102:10)
at /home/runner/work/grist-core/grist-core/node_modules/minio/dist/main/internal/request.ts:17:34
at new Promise (<anonymous>)
at request (node_modules/minio/dist/main/internal/request.ts:16:10)
at IamAwsProvider.getIamRoleName (node_modules/minio/dist/main/IamAwsProvider.ts:184:30)
at IamAwsProvider.getIamRoleNamedUrl (node_modules/minio/dist/main/IamAwsProvider.ts:166:33)
at IamAwsProvider.fetchCredentials (node_modules/minio/dist/main/IamAwsProvider.ts:86:26)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at IamAwsProvider.getCredentials (node_modules/minio/dist/main/IamAwsProvider.ts:60:27)
at Client.checkAndRefreshCreds (node_modules/minio/dist/main/internal/client.ts:536:33)
at Client.makeRequestStreamAsync (node_modules/minio/dist/main/internal/client.ts:706:5)
at Client.getBucketVersioning (node_modules/minio/dist/main/internal/client.ts:2159:21)
at MinIOExternalStorage.hasVersioning (app/server/lib/MinIOExternalStorage.ts:164:24)
at checkMinIOBucket (app/server/lib/configureMinIOExternalStorage.ts:74:8)
at CoreCreate.checkBackend (app/server/lib/ICreate.ts:176:9)
at FlexServer.addDoc (app/server/lib/FlexServer.ts:1463:7)
at MergedServer.run (app/server/MergedServer.ts:198:9)
at MergedServer.run (app/server/MergedServer.ts:220:11)
at Context.<anonymous> (test/gen-server/lib/homedb/UsersManager.ts:1220:7)
import {wrapWithKeyMappedStorage} from 'app/server/lib/ExternalStorage'; | ||
import {appSettings} from 'app/server/lib/AppSettings'; | ||
import {MinIOExternalStorage} from 'app/server/lib/MinIOExternalStorage'; | ||
import {IamAwsProvider} from 'minio/dist/main/IamAwsProvider.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: please remove the .js
extension
import {IamAwsProvider} from 'minio/dist/main/IamAwsProvider.js'; | |
import {IamAwsProvider} from 'minio/dist/main/IamAwsProvider'; |
secretKey: string, | ||
accessKey?: string, | ||
secretKey?: string, | ||
credentialsProvider?: any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you type this property?
Import IamAwsProvider
like in app/server/lib/configureMinIOExternalStorage.ts
and then:
credentialsProvider?: any, | |
credentialsProvider?: IamAwsProvider, |
envVar: ['GRIST_DOCS_MINIO_SECRET_KEY'], | ||
censor: true, | ||
}); | ||
const credentialsProvider = new IamAwsProvider({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if we want to use accessKey + secretKey and not IRSA?
I have read the CLA Document and I hereby sign the CLA |
) { | ||
if (!options.accessKey || !options.secretKey) { | ||
options.credentialsProvider = new IamAwsProvider({}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagine an administrator having written a typo in the env variables for either the access key and the secret key.
I think they would be confused by having switched implicitly to the IRSA authentication method.
Also it seems that it is not the only alternative to exist to authenticate with MinIO, currently there also exists AssumeRoleProvider
(I don't ask to implement it, if people ask for supporting this, then it should be in another future PR).
What do you think of something like the following instead:
- introduce an env variable, like
GRIST_DOCS_MINIO_AUTH_TYPE
(or another meaningful name) that would either equal tocredentials
or toIRSA
; - by default,
GRIST_DOCS_MINIO_AUTH_TYPE
equals tocredentials
for the sake of backward compatibility; - if
GRIST_DOCS_MINIO_AUTH_TYPE
equals tocredentials
, the env variablesGRIST_DOCS_MINIO_ACCESS_KEY
andGRIST_DOCS_MINIO_SECRET_KEY
are mandatory inconfigureMinIOExternalStorage.ts
. Otherwise these values are not read.
Also you may indeed instantiate directly a CredentialProvider
or a IamAwsProvider
directly in configureMinIOExternalStorage.ts
and pass it as the credentialsProvider
property to this class (and remove the accessKey
and secretKey
from the options here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it probably should be similar to official sdk that just goes through the credential provider chain without having to explicitly specify what method we wanna use.
I believe the minio client kind of does that here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Therefore I think you should probably detect the presence of the env variables that should be set for the IRSA, and if they are missing, then switch back to the classic auth method with credentials.
Please note that I am not an authority on this project, I'll let Grist Labs review your work on their side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a env var GRIST_DOCS_MINIO_AUTH_AUTO_DETECT
to try and load the credentials according to credential provider chain if GRIST_DOCS_MINIO_AUTH_AUTO_DETECT: true
@paulfitz Requesting a review for this PR. |
) { | ||
super(bucket, options, batchSize); | ||
if (mockClient) { | ||
(this as any)._s3 = mockClient; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this differ from what MinIOExternalStorage
already does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified the MinIOExternalStorage
constructor a bit, so we don't need this
import * as fse from 'fs-extra'; | ||
import * as minio from 'minio'; | ||
import * as stream from 'node:stream'; | ||
import {IamAwsProvider} from 'minio/dist/main/IamAwsProvider.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imports should be ordered alphabetically. The ".js" presumably isn't needed? Also if the import could be just from 'minio' that would be more consistent than reaching into the specific package layout (minio/dist/main). But sometimes packages are wonky, so, fine if unavoidable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Not a js/ts expert)
I was not able to import from minio directly, looks like it does not export IamAwsProvider
.
Also, when remove .js
and just use import {IamAwsProvider} from 'minio/dist/main/IamAwsProvider;
I get the below error.
node:internal/modules/cjs/loader:657
throw e;
^
Error: Cannot find module '/grist/node_modules/minio/dist/main/IamAwsProvider'
at createEsmNotFoundErr (node:internal/modules/cjs/loader:1419:15)
at finalizeEsmResolution (node:internal/modules/cjs/loader:1408:15)
at resolveExports (node:internal/modules/cjs/loader:650:14)
at Function._findPath (node:internal/modules/cjs/loader:717:31)
at Function._resolveFilename (node:internal/modules/cjs/loader:1369:27)
at defaultResolveImpl (node:internal/modules/cjs/loader:1025:19)
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1030:22)
at Function._load (node:internal/modules/cjs/loader:1192:37)
at TracingChannel.traceSync (node:diagnostics_channel:322:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:237:24)
at Module.require (node:internal/modules/cjs/loader:1463:12)
at require (node:internal/modules/helpers:147:16)
at Object.<anonymous> (/grist/_build/app/server/lib/MinIOExternalStorage.js:39:26)
at Module._compile (node:internal/modules/cjs/loader:1706:14)
at Object..js (node:internal/modules/cjs/loader:1839:10)
at Module.load (node:internal/modules/cjs/loader:1441:32) {
code: 'MODULE_NOT_FOUND',
path: '/grist/node_modules/minio'
}
Node.js v22.19.0
private _batchSize?: number | ||
) { | ||
if (options.authAutoDetect) { | ||
options.credentialsProvider = new IamAwsProvider({}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this work? If the id/secret are set, and autodetect is also set, will the id/secret be used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like if autodetect is set, id/secret will be ignored.
It's due to how minio-js works right now. ref code.
Initially, my thought was to not have authAutoDetect
flag.
If id/secret env vars are not set we can by default try and load IRSA creds.
Let me know your thoughts.
Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds support for using IRSA (IAM Role for Service Accounts) authentication with MinIO storage, making accessKey
and secretKey
optional when running on EKS with appropriate IAM role configuration.
- Makes
accessKey
andsecretKey
optional in MinIO configuration - Adds
authAutoDetect
flag to enable IRSA authentication via IamAwsProvider - Updates test infrastructure to support mocking of MinIO clients
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
File | Description |
---|---|
configureMinIOExternalStorage.ts | Changes accessKey/secretKey from required to optional and adds authAutoDetect configuration flag |
MinIOExternalStorage.ts | Refactors constructor to support optional credentials and IRSA authentication with IamAwsProvider |
test/server/lib/MinIOExternalStorage.ts | Creates TestMinIOExternalStorage class to enable dependency injection for testing |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
private _batchSize?: number, | ||
private _s3 = new minio.Client(options) as unknown as MinIOClient | ||
private _batchSize?: number | ||
) { |
Copilot
AI
Sep 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing validation to ensure that either credentials (accessKey/secretKey) or authAutoDetect is provided. The code should validate that at least one authentication method is configured to prevent runtime failures.
) { | |
) { | |
// Validate that at least one authentication method is configured | |
const hasAccessKeys = Boolean(options.accessKey && options.secretKey); | |
if (!hasAccessKeys && !options.authAutoDetect) { | |
throw new ApiError('MinIOExternalStorage requires either accessKey/secretKey or authAutoDetect to be set for authentication.', 400); | |
} |
Copilot uses AI. Check for mistakes.
const accessKey = settings.flag('accessKey').readString({ | ||
envVar: ['GRIST_DOCS_MINIO_ACCESS_KEY'], | ||
censor: true, | ||
}); | ||
const secretKey = settings.flag('secretKey').requireString({ | ||
const secretKey = settings.flag('secretKey').readString({ | ||
envVar: ['GRIST_DOCS_MINIO_SECRET_KEY'], | ||
censor: true, | ||
}); |
Copilot
AI
Sep 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since accessKey and secretKey are now optional, there should be validation logic to ensure that when authAutoDetect is false, both accessKey and secretKey are provided. Currently, the function could return undefined values for these required credentials.
Copilot uses AI. Check for mistakes.
Context
Currently
accesskey
andsecretkey
are required config needed for integration with Object StorageProposed solution
Minio supports IRSA EKS role already.
Related issues
Has this been tested?
I have only tested this when running on EKS configured with IRSA role. Will need guidance to make this MR mergeable.
Screenshots / Screencasts