Implement proper AWS Credentials precedence

This properly configures the precedence for explicit assume-role ARNs,
explicit AWS credentials (via access keys), and the default fallback for
the AWS SDK.

The general precedence of using:
 1. explicitly provided credentials
 2. AWS SDK config (AWS.config.credentials)
 3. AWS SDK credentials provider chain (AWS.config.credentialProviders)

has been respected. This removes the need to explicitly configure
`AWS.config.credentials` in Backstage installations also.

Signed-off-by: Joel Low <joel@joelsplace.sg>
This commit is contained in:
Joel Low
2021-03-09 13:20:24 +08:00
parent a546de9eb2
commit 2ef5bc7ea1
4 changed files with 59 additions and 39 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/techdocs-common': patch
'@backstage/plugin-catalog-backend': patch
---
Implement proper AWS Credentials precedence with assume-role and explicit credentials
@@ -19,6 +19,8 @@ import fs from 'fs-extra';
import os from 'os';
import path from 'path';
export { Credentials } from 'aws-sdk';
const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
/**
@@ -63,32 +63,7 @@ export class AwsS3Publish implements PublisherBase {
const credentialsConfig = config.getOptionalConfig(
'techdocs.publisher.awsS3.credentials',
);
let accessKeyId = undefined;
let secretAccessKey = undefined;
let credentials: Credentials | CredentialsOptions | undefined = undefined;
if (credentialsConfig) {
const roleArn = credentialsConfig.getOptionalString('roleArn');
if (roleArn && aws.config.credentials instanceof Credentials) {
credentials = new aws.ChainableTemporaryCredentials({
masterCredentials: aws.config.credentials as Credentials,
params: {
RoleSessionName: 'backstage-aws-techdocs-s3-publisher',
RoleArn: roleArn,
},
});
} else {
accessKeyId = credentialsConfig.getOptionalString('accessKeyId');
secretAccessKey = credentialsConfig.getOptionalString(
'secretAccessKey',
);
if (accessKeyId && secretAccessKey) {
credentials = {
accessKeyId,
secretAccessKey,
};
}
}
}
const credentials = AwsS3Publish.buildCredentials(credentialsConfig);
// AWS Region is an optional config. If missing, default AWS env variable AWS_REGION
// or AWS shared credentials file at ~/.aws/credentials will be used.
@@ -133,6 +108,37 @@ export class AwsS3Publish implements PublisherBase {
return new AwsS3Publish(storageClient, bucketName, logger);
}
private static buildCredentials(
config?: Config,
): Credentials | CredentialsOptions | undefined {
if (!config) {
return undefined;
}
const accessKeyId = config.getOptionalString('accessKeyId');
const secretAccessKey = config.getOptionalString('secretAccessKey');
let explicitCredentials: Credentials | undefined;
if (accessKeyId && secretAccessKey) {
explicitCredentials = new Credentials({
accessKeyId,
secretAccessKey,
});
}
const roleArn = config.getOptionalString('roleArn');
if (roleArn) {
return new aws.ChainableTemporaryCredentials({
masterCredentials: explicitCredentials,
params: {
RoleSessionName: 'backstage-aws-techdocs-s3-publisher',
RoleArn: roleArn,
},
});
}
return explicitCredentials;
}
constructor(
private readonly storageClient: aws.S3,
private readonly bucketName: string,
@@ -51,25 +51,31 @@ export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor {
});
}
private static buildCredentials(
config: AwsOrganizationProviderConfig,
): Credentials | undefined {
const roleArn = config.roleArn;
if (!roleArn) {
return undefined;
}
return new AWS.ChainableTemporaryCredentials({
params: {
RoleSessionName: 'backstage-aws-organization-processor',
RoleArn: roleArn,
},
});
}
constructor(options: {
provider: AwsOrganizationProviderConfig;
logger: Logger;
}) {
this.provider = options.provider;
this.logger = options.logger;
let credentials = undefined;
if (
this.provider.roleArn !== undefined &&
AWS.config.credentials instanceof Credentials
) {
credentials = new AWS.ChainableTemporaryCredentials({
masterCredentials: AWS.config.credentials as Credentials,
params: {
RoleSessionName: 'backstage-aws-organization-processor',
RoleArn: this.provider.roleArn,
},
});
}
const credentials = AwsOrganizationCloudAccountProcessor.buildCredentials(
this.provider,
);
this.organizations = new AWS.Organizations({
credentials,
region: AWS_ORGANIZATION_REGION,