Skip to content

Commit

Permalink
feat: add Secret Management support for repository
Browse files Browse the repository at this point in the history
  • Loading branch information
kozlove-aws committed Aug 4, 2021
1 parent 1e3eb63 commit c6e4eb8
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 4 deletions.
70 changes: 70 additions & 0 deletions packages/aws-rfdk/lib/deadline/lib/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ import {
import {
Asset,
} from '@aws-cdk/aws-s3-assets';
import {
ISecret,
Secret,
} from '@aws-cdk/aws-secretsmanager';
import {
Annotations,
Construct,
Expand Down Expand Up @@ -271,6 +275,34 @@ export interface RepositorySecurityGroupsOptions {
readonly installer?: ISecurityGroup;
}

/**
* Settings used by Deadline Secrets Management, a feature introduced in Deadline 10.1.10 for securely managing storage
* and access of Secrets for your render farm.
* More details at:
* https://docs.thinkboxsoftware.com/products/deadline/10.1/1_User%20Manual/manual/secrets-management/deadline-secrets-management.html
* Using Secrets Management requires TLS to be enabled between the RenderQueue and its clients. If this feature is enabled, the
* `externalTLS` on the `RenderQueueTrafficEncryptionProps` interface on the RenderQueue cannot be disabled.
*/
export interface SecretsManagementProps {
/**
* Whether or not to enable the Secrets Management feature.
* @default true
*/
readonly enabled?: boolean;
/**
* A Secret containing the username and password to use for the admin role.
* The contents of this secret must be a JSON document with the keys "username" and "password". ex:
* {
* "username": <admin user name>,
* "password": <admin user password>,
* }
* Password should contain at least one lowercase letter, one uppercase letter, one symbol and one number.
*
* @default: A random username and password will be generated in a Secret with ID `SMAdminUser` and will need to be retrieved from AWS Secrets Manager if it is needed
*/
readonly credentials?: ISecret;
}

/**
* Properties for the Deadline repository
*/
Expand Down Expand Up @@ -387,6 +419,15 @@ export interface RepositoryProps {
* @default Repository settings are not imported.
*/
readonly repositorySettings?: Asset;

/**
* Define the settings used by Deadline Secrets Management, a feature introduced in Deadline 10.1.10 for securely managing storage
* and access of Secrets for your render farm.
* More details at:
* https://docs.thinkboxsoftware.com/products/deadline/10.1/1_User%20Manual/manual/secrets-management/deadline-secrets-management.html
* @default: Secrets Management will be enabled and a username and password will be automatically generated if none are supplied.
*/
readonly secretsManagementSettings?: SecretsManagementProps
}

/**
Expand Down Expand Up @@ -501,6 +542,11 @@ export class Repository extends Construct implements IRepository {
*/
private readonly installerGroup: AutoScalingGroup;

/**
* Deadline Secrets Management settings.
*/
public readonly secretsManagementSettings: SecretsManagementProps

constructor(scope: Construct, id: string, props: RepositoryProps) {
super(scope, id);

Expand All @@ -519,6 +565,23 @@ export class Repository extends Construct implements IRepository {

this.version = props.version;

this.secretsManagementSettings = {
enabled: props.secretsManagementSettings?.enabled ?? true,
credentials: props.secretsManagementSettings?.credentials ??
((props.secretsManagementSettings?.enabled ?? true) ? new Secret(this, 'SMAdminUser', {
description: 'Admin credentials for Secret Management',
generateSecretString: {
excludeCharacters: '\"$&\'()-/<>[\\]\`{|}',
includeSpace: false,
passwordLength: 24,
requireEachIncludedType: true,

generateStringKey: 'password',
secretStringTemplate: JSON.stringify({ username: 'admin' }),
},
}) : undefined),
};

this.fileSystem = props.fileSystem ?? (() => {
const fs = new EfsFileSystem(this, 'FileSystem', {
vpc: props.vpc,
Expand Down Expand Up @@ -923,6 +986,13 @@ export class Repository extends Construct implements IRepository {
version.linuxFullVersionString(),
];

if (this.secretsManagementSettings.enabled) {
installerArgs.push('true');
installerArgs.push(Stack.of(this.secretsManagementSettings.credentials ?? this).region);
this.secretsManagementSettings.credentials?.grantRead(installerGroup);
installerArgs.push(this.secretsManagementSettings.credentials?.secretArn ?? '');
}

if (settings) {
const repositorySettingsFilePath = installerGroup.userData.addS3DownloadCommand({
bucket: settings.bucket,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,25 @@
# $1: s3 path for the deadline repository installer.
# $2: Path where deadline repository needs to be installed.
# $3: Deadline Repository Version being installed.
# $4: (Optional) Deadline Repository settings file to import.
# $4: (Optional) Secret management is enabled.
# $5: (Optional) Region where stacks are deployed.
# $6: (Optional) Secret management admin credentials ARN.
# $7: (Optional) Deadline Repository settings file to import.

# exit when any command fails
set -xeuo pipefail

S3PATH=$1
PREFIX=$2
DEADLINE_REPOSITORY_VERSION=$3
DEADLINE_REPOSITORY_SETTINGS_FILE=${4:-}
SECRET_MANAGEMENT_ENABLED=${4:-}
if [ "$SECRET_MANAGEMENT_ENABLED" == "true" ]; then
AWS_REGION=${5}
SECRET_MANAGEMENT_ARN=${6}
DEADLINE_REPOSITORY_SETTINGS_FILE=${7:-}
else
DEADLINE_REPOSITORY_SETTINGS_FILE=$SECRET_MANAGEMENT_ENABLED
fi
shift;shift;

# check if repository is already installed at the given path
Expand Down Expand Up @@ -82,7 +92,18 @@ if [ ! -z "$DEADLINE_REPOSITORY_SETTINGS_FILE" ]; then
fi
fi

$REPO_INSTALLER --mode unattended --setpermissions false --prefix "$PREFIX" --installmongodb false --backuprepo false ${INSTALLER_DB_ARGS_STRING} $REPOSITORY_SETTINGS_ARG_STRING
SECRET_MANAGEMENT_ARG=''
if [ "$SECRET_MANAGEMENT_ENABLED" == "true" ]; then
sudo yum install -y jq
echo "Secret management is enabled. Credentials are stored in secret: $SECRET_MANAGEMENT_ARN"
SM_SECRET_VALUE=$(aws secretsmanager get-secret-value --secret-id=$SECRET_MANAGEMENT_ARN --region=$AWS_REGION)
SM_SECRET_STRING=$(jq -r '.SecretString' <<< "$SM_SECRET_VALUE")
SECRET_MANAGEMENT_USER=$(jq -r '.username' <<< "$SM_SECRET_STRING")
SECRET_MANAGEMENT_PASSWORD=$(jq -r '.password' <<< "$SM_SECRET_STRING")
SECRET_MANAGEMENT_ARG="--installSecretsManagement true --secretsAdminName $SECRET_MANAGEMENT_USER --secretsAdminPassword $SECRET_MANAGEMENT_PASSWORD"
fi

$REPO_INSTALLER --mode unattended --setpermissions false --prefix "$PREFIX" --installmongodb false --backuprepo false ${INSTALLER_DB_ARGS_STRING} $REPOSITORY_SETTINGS_ARG_STRING $SECRET_MANAGEMENT_ARG

set -x

Expand Down
57 changes: 56 additions & 1 deletion packages/aws-rfdk/lib/deadline/test/repository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
} from '@aws-cdk/aws-efs';
import { Bucket } from '@aws-cdk/aws-s3';
import { Asset } from '@aws-cdk/aws-s3-assets';
import { Secret } from '@aws-cdk/aws-secretsmanager';
import {
App,
CfnElement,
Expand Down Expand Up @@ -1069,7 +1070,7 @@ describe('tagging', () => {
'AWS::EC2::SecurityGroup': 3,
'AWS::DocDB::DBClusterParameterGroup': 1,
'AWS::DocDB::DBSubnetGroup': 1,
'AWS::SecretsManager::Secret': 1,
'AWS::SecretsManager::Secret': 2,
'AWS::DocDB::DBCluster': 1,
'AWS::DocDB::DBInstance': 1,
'AWS::IAM::Role': 1,
Expand Down Expand Up @@ -1224,3 +1225,57 @@ test('imports repository settings', () => {
const installerGroup = repository.node.tryFindChild('Installer') as AutoScalingGroup;
expect(installerGroup.userData.render()).toContain(`aws s3 cp '${repositorySettings.s3ObjectUrl}'`);
});

test('secret manager enabled', () => {
// GIVEN
const expectedCredentials = new Secret(stack, 'CustomSMAdminUser', {
description: 'Custom admin credentials for the Secret Management',
generateSecretString: {
excludeCharacters: '\"$&\'()-/<>[\\]\`{|}',
includeSpace: false,
passwordLength: 24,
requireEachIncludedType: true,
generateStringKey: 'password',
secretStringTemplate: JSON.stringify({ username: 'admin' }),
},
});

// WHEN
const repository = new Repository(stack, 'Repository', {
vpc,
version,
secretsManagementSettings: {
enabled: true,
credentials: expectedCredentials,
},
});

// THEN
expect(repository.secretsManagementSettings.credentials).toBe(expectedCredentials);
});

test('secret manager is enabled by default', () => {
// WHEN
const repository = new Repository(stack, 'Repository', {
vpc,
version,
});

// THEN
expect(repository.secretsManagementSettings.enabled).toBeTruthy();
expect(repository.secretsManagementSettings.credentials).toBeDefined();
});

test('secret manager disabled', () => {
// WHEN
const repository = new Repository(stack, 'Repository', {
vpc,
version,
secretsManagementSettings: {
enabled: false,
},
});

// THEN
expect(repository.secretsManagementSettings.credentials).toBeUndefined();
});

0 comments on commit c6e4eb8

Please sign in to comment.