Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions admin/slices/rancher/components/rancher/Provider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,10 @@ async function onDeploy() {
<CardTitle class="text-base">Configure S3 storage</CardTitle>
<CardDescription>
Required to seed template files and persist agent state. Set
<code>s3_bucket</code>, <code>aws_access_key_id</code> and
<code>aws_secret_access_key</code>. Local dev: use the included
MinIO defaults.
<code>s3_bucket</code> (and optionally <code>aws_region</code>).
<code>aws_access_key_id</code> / <code>aws_secret_access_key</code>
are optional — on EKS the pod's IRSA role is used automatically.
Local dev: use the included MinIO defaults.
</CardDescription>
</div>
<Badge v-if="stepDone.s3" variant="default">Done</Badge>
Expand Down
14 changes: 7 additions & 7 deletions api/src/slices/agent/file/data/file.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -666,15 +666,15 @@ export class S3FileGateway extends IFileGateway {
'S3 bucket is not configured (settings → integrations → s3_bucket)',
);
}
if (!accessKeyId || !secretAccessKey) {
throw new BadRequestException(
'AWS credentials are not configured (settings → integrations)',
);
}

// Credentials are optional: when both are present we pass them explicitly
// (static keys / local MinIO). When blank we OMIT `credentials` so the AWS
// SDK default provider chain resolves them — this is what lets an EKS pod
// authenticate through its IRSA / Pod Identity role with no static keys.
const client = new S3Client({
region: region || 'us-east-1',
credentials: { accessKeyId, secretAccessKey },
...(accessKeyId && secretAccessKey
? { credentials: { accessKeyId, secretAccessKey } }
: {}),
...(endpoint ? { endpoint, forcePathStyle: true } : {}),
});

Expand Down
11 changes: 5 additions & 6 deletions api/src/slices/agent/secret/data/awsSecret.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,20 +157,19 @@ export class AwsSecretGateway {
get('aws_secret_prefix'),
]);

if (!accessKeyId || !secretAccessKey) {
throw new BadRequestException(
'AWS credentials are not configured (settings → integrations)',
);
}
if (!prefix) {
throw new BadRequestException(
'AWS Secrets Manager prefix is not configured (settings → integrations → aws_secret_prefix)',
);
}

// Credentials optional — omit `credentials` when blank so the SDK default
// provider chain (IRSA / Pod Identity on EKS) supplies them.
const client = new SecretsManagerClient({
region: region || 'us-east-1',
credentials: { accessKeyId, secretAccessKey },
...(accessKeyId && secretAccessKey
? { credentials: { accessKeyId, secretAccessKey } }
: {}),
});

return { client, prefix: prefix.replace(/\/+$/, '') };
Expand Down
14 changes: 7 additions & 7 deletions api/src/slices/agent/secret/data/fileSecret.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,15 @@ export class FileSecretGateway {
'S3 bucket is not configured (settings → integrations → s3_bucket)',
);
}
if (!accessKeyId || !secretAccessKey) {
throw new BadRequestException(
'AWS credentials are not configured (settings → integrations)',
);
}

// Credentials are optional: when both are present we pass them explicitly
// (static keys / local MinIO). When blank we OMIT `credentials` so the AWS
// SDK default provider chain resolves them — this is what lets an EKS pod
// authenticate through its IRSA / Pod Identity role with no static keys.
const client = new S3Client({
region: region || 'us-east-1',
credentials: { accessKeyId, secretAccessKey },
...(accessKeyId && secretAccessKey
? { credentials: { accessKeyId, secretAccessKey } }
: {}),
...(endpoint ? { endpoint, forcePathStyle: true } : {}),
});

Expand Down
14 changes: 7 additions & 7 deletions api/src/slices/agent/templateFile/data/templateFile.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,15 +249,15 @@ export class S3TemplateFileGateway extends ITemplateFileGateway {
'S3 bucket is not configured (settings → integrations → s3_bucket)',
);
}
if (!accessKeyId || !secretAccessKey) {
throw new BadRequestException(
'AWS credentials are not configured (settings → integrations)',
);
}

// Credentials are optional: when both are present we pass them explicitly
// (static keys / local MinIO). When blank we OMIT `credentials` so the AWS
// SDK default provider chain resolves them — this is what lets an EKS pod
// authenticate through its IRSA / Pod Identity role with no static keys.
const client = new S3Client({
region: region || 'us-east-1',
credentials: { accessKeyId, secretAccessKey },
...(accessKeyId && secretAccessKey
? { credentials: { accessKeyId, secretAccessKey } }
: {}),
...(endpoint ? { endpoint, forcePathStyle: true } : {}),
});

Expand Down
15 changes: 7 additions & 8 deletions api/src/slices/aws/s3/s3.repository.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import {
DeleteObjectCommand,
GetObjectCommand,
Expand Down Expand Up @@ -80,16 +80,15 @@ export class S3Repository {
get('s3_secret_key'),
]);

if (!accessKeyId || !secretAccessKey) {
throw new BadRequestException(
'S3 is not configured (settings → integrations → s3_access_key, s3_secret_key)',
);
}

// Credentials optional — when both are present we use them explicitly
// (static keys / MinIO); when blank we omit `credentials` so the SDK
// default provider chain (IRSA / Pod Identity on EKS) supplies them.
this.client = new S3Client({
endpoint: endpoint || undefined,
region: region || 'us-east-1',
credentials: { accessKeyId, secretAccessKey },
...(accessKeyId && secretAccessKey
? { credentials: { accessKeyId, secretAccessKey } }
: {}),
forcePathStyle: Boolean(endpoint),
});
return this.client;
Expand Down
11 changes: 5 additions & 6 deletions api/src/slices/paddock/evaluation/data/paddockReport.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,13 @@ export class PaddockReportGateway extends IPaddockReportGateway {
'S3 bucket is not configured (settings → integrations → s3_bucket)',
);
}
if (!accessKeyId || !secretAccessKey) {
throw new BadRequestException(
'AWS credentials are not configured (settings → integrations)',
);
}
// Credentials optional — omit `credentials` when blank so the SDK default
// provider chain (IRSA / Pod Identity on EKS) supplies them.
const client = new S3Client({
region: region || 'us-east-1',
credentials: { accessKeyId, secretAccessKey },
...(accessKeyId && secretAccessKey
? { credentials: { accessKeyId, secretAccessKey } }
: {}),
...(endpoint ? { endpoint, forcePathStyle: true } : {}),
});
return { client, bucket };
Expand Down
14 changes: 10 additions & 4 deletions api/src/slices/rancher/domain/rancher.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,13 @@ export class RancherService implements OnApplicationBootstrap {
};
}

// S3 is "configured" when bucket + AWS credentials are all present in
// settings → integrations. Endpoint/region are optional (region falls
// back to us-east-1; endpoint blank means real AWS).
// S3 is "configured" once a bucket is set. AWS credentials are OPTIONAL:
// on EKS the pod authenticates through its IRSA / Pod Identity role via the
// SDK's default provider chain, so no static keys are needed. When keys are
// supplied (static keys / local MinIO) they must come as a pair — a lone
// key or secret is a half-filled, unusable state, so we don't count it.
// Endpoint/region stay optional (region falls back to us-east-1; blank
// endpoint means real AWS).
private async checkS3Configured(): Promise<boolean> {
const get = async (name: string): Promise<string> => {
const s = await this.settingGateway.findByKey('integrations', name);
Expand All @@ -85,7 +89,9 @@ export class RancherService implements OnApplicationBootstrap {
get('aws_access_key_id'),
get('aws_secret_access_key'),
]);
return !!(bucket && key && secret);
// both-or-neither: reject a partially-entered credential pair
const credsConsistent = !!key === !!secret;
return !!bucket && credsConsistent;
}

// Idempotent — returns the existing Rancher template if one exists,
Expand Down
14 changes: 7 additions & 7 deletions api/src/slices/user/browserState/data/browserState.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,15 @@ export class UserBrowserStateGateway extends IUserBrowserStateGateway {
'S3 bucket is not configured (settings → integrations → s3_bucket)',
);
}
if (!accessKeyId || !secretAccessKey) {
throw new BadRequestException(
'AWS credentials are not configured (settings → integrations)',
);
}

// Credentials are optional: when both are present we pass them explicitly
// (static keys / local MinIO). When blank we OMIT `credentials` so the AWS
// SDK default provider chain resolves them — this is what lets an EKS pod
// authenticate through its IRSA / Pod Identity role with no static keys.
const client = new S3Client({
region: region || 'us-east-1',
credentials: { accessKeyId, secretAccessKey },
...(accessKeyId && secretAccessKey
? { credentials: { accessKeyId, secretAccessKey } }
: {}),
...(endpoint ? { endpoint, forcePathStyle: true } : {}),
});

Expand Down
12 changes: 6 additions & 6 deletions api/src/slices/user/secret/data/awsSecret.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,20 +160,20 @@ export class AwsUserSecretGateway {
get('aws_user_secret_prefix'),
]);

if (!accessKeyId || !secretAccessKey) {
throw new BadRequestException(
'AWS credentials are not configured (settings → integrations)',
);
}
if (!prefix) {
throw new BadRequestException(
'AWS Secrets Manager user prefix is not configured (settings → integrations → aws_user_secret_prefix)',
);
}

// Credentials are optional: when both are present we pass them explicitly,
// otherwise we omit `credentials` so the SDK default provider chain (IRSA /
// Pod Identity on EKS) supplies them.
const client = new SecretsManagerClient({
region: region || 'us-east-1',
credentials: { accessKeyId, secretAccessKey },
...(accessKeyId && secretAccessKey
? { credentials: { accessKeyId, secretAccessKey } }
: {}),
});

return { client, prefix: prefix.replace(/\/+$/, '') };
Expand Down
14 changes: 7 additions & 7 deletions api/src/slices/user/secret/data/fileSecret.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,15 @@ export class FileUserSecretGateway {
'S3 bucket is not configured (settings → integrations → s3_bucket)',
);
}
if (!accessKeyId || !secretAccessKey) {
throw new BadRequestException(
'AWS credentials are not configured (settings → integrations)',
);
}

// Credentials are optional: when both are present we pass them explicitly
// (static keys / local MinIO). When blank we OMIT `credentials` so the AWS
// SDK default provider chain resolves them — this is what lets an EKS pod
// authenticate through its IRSA / Pod Identity role with no static keys.
const client = new S3Client({
region: region || 'us-east-1',
credentials: { accessKeyId, secretAccessKey },
...(accessKeyId && secretAccessKey
? { credentials: { accessKeyId, secretAccessKey } }
: {}),
...(endpoint ? { endpoint, forcePathStyle: true } : {}),
});

Expand Down
Loading