Current Behavior
The s3 file storage provider only supports static credentials. In S3FileStorageProvider.create() the MinIO client is built like this:
final var accessKey = config.getOptionalValue("dt.file-storage.s3.access-key", String.class).orElse(null);
final var secretKey = config.getOptionalValue("dt.file-storage.s3.secret-key", String.class).orElse(null);
if (accessKey != null && secretKey != null) {
clientBuilder.credentials(accessKey, secretKey);
}
When access-key / secret-key are not configured, no credentials provider is installed at all, so the client sends unsigned (anonymous) requests (BaseS3Client.executeAsync: Credentials credentials = (provider == null) ? null : provider.fetch();). There is no fallback to ambient AWS credential resolution — environment variables, ~/.aws config, ECS task roles (AWS_CONTAINER_CREDENTIALS_RELATIVE_URI), EKS IRSA (web identity), EKS Pod Identity, and EC2 instance profiles are all ignored.
On AWS this makes the s3 provider effectively unusable in well-configured environments: on ECS and Kubernetes/EKS the standard (and often org-mandated) way to grant S3 access is the task role / IRSA, not long-lived IAM user keys. Minting a static key pair just for file storage is an anti-pattern (no rotation, secret sprawl) and is frequently prohibited outright.
This matters more than it first appears, because the default local provider silently breaks horizontally scaled deployments: with more than one apiserver replica, a BOM accepted by one replica may have its import-bom workflow picked up by another (the workflow engine coordinates through PostgreSQL), where FileStorage#get throws NoSuchFileException, the workflow fails terminally, and the project is left empty — while /api/v1/event/token/{uuid} reports processing complete. We hit exactly this running 2 replicas on ECS (verified on 5.0.3). Shared storage is the fix, and S3 is the natural choice — but only static keys are supported today. (We worked around it with an EFS volume mounted over the data directory.)
Observed on 5.0.3; the code on main is unchanged.
Proposed Behavior
When dt.file-storage.s3.access-key / secret-key are not configured, fall back to ambient credential resolution instead of anonymous access. The MinIO client already bundled with Dependency-Track ships everything needed in io.minio.credentials — no new dependencies:
if (accessKey != null && secretKey != null) {
clientBuilder.credentials(accessKey, secretKey);
} else {
clientBuilder.credentialsProvider(new ChainedProvider(
new AwsEnvironmentProvider(),
new AwsConfigProvider(),
new IamAwsProvider(null, httpClient)));
}
IamAwsProvider already handles the ECS container credentials endpoint (AWS_CONTAINER_CREDENTIALS_RELATIVE_URI / _FULL_URI), EKS IRSA (AWS_WEB_IDENTITY_TOKEN_FILE), and EC2 IMDS, so this single change makes the provider work with ECS task roles and Kubernetes service-account-based auth out of the box, while keeping current static-key behavior fully backward compatible. If truly anonymous access should remain reachable, it could be kept behind an explicit opt-in flag rather than being the silent default.
Happy to submit a PR for this.
Checklist
Current Behavior
The
s3file storage provider only supports static credentials. InS3FileStorageProvider.create()the MinIO client is built like this:When
access-key/secret-keyare not configured, no credentials provider is installed at all, so the client sends unsigned (anonymous) requests (BaseS3Client.executeAsync:Credentials credentials = (provider == null) ? null : provider.fetch();). There is no fallback to ambient AWS credential resolution — environment variables,~/.awsconfig, ECS task roles (AWS_CONTAINER_CREDENTIALS_RELATIVE_URI), EKS IRSA (web identity), EKS Pod Identity, and EC2 instance profiles are all ignored.On AWS this makes the
s3provider effectively unusable in well-configured environments: on ECS and Kubernetes/EKS the standard (and often org-mandated) way to grant S3 access is the task role / IRSA, not long-lived IAM user keys. Minting a static key pair just for file storage is an anti-pattern (no rotation, secret sprawl) and is frequently prohibited outright.This matters more than it first appears, because the default
localprovider silently breaks horizontally scaled deployments: with more than one apiserver replica, a BOM accepted by one replica may have itsimport-bomworkflow picked up by another (the workflow engine coordinates through PostgreSQL), whereFileStorage#getthrowsNoSuchFileException, the workflow fails terminally, and the project is left empty — while/api/v1/event/token/{uuid}reports processing complete. We hit exactly this running 2 replicas on ECS (verified on 5.0.3). Shared storage is the fix, and S3 is the natural choice — but only static keys are supported today. (We worked around it with an EFS volume mounted over the data directory.)Observed on 5.0.3; the code on
mainis unchanged.Proposed Behavior
When
dt.file-storage.s3.access-key/secret-keyare not configured, fall back to ambient credential resolution instead of anonymous access. The MinIO client already bundled with Dependency-Track ships everything needed inio.minio.credentials— no new dependencies:IamAwsProvideralready handles the ECS container credentials endpoint (AWS_CONTAINER_CREDENTIALS_RELATIVE_URI/_FULL_URI), EKS IRSA (AWS_WEB_IDENTITY_TOKEN_FILE), and EC2 IMDS, so this single change makes the provider work with ECS task roles and Kubernetes service-account-based auth out of the box, while keeping current static-key behavior fully backward compatible. If truly anonymous access should remain reachable, it could be kept behind an explicit opt-in flag rather than being the silent default.Happy to submit a PR for this.
Checklist