Skip to content
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

feat(deadline): add Deadline Secrets Management integration in the Render Queue #528

Merged
Merged
Show file tree
Hide file tree
Changes from 5 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
61 changes: 59 additions & 2 deletions packages/aws-rfdk/lib/deadline/lib/render-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
Ec2TaskDefinition,
LogDriver,
PlacementConstraint,
Scope,
UlimitName,
} from '@aws-cdk/aws-ecs';
import {
Expand Down Expand Up @@ -214,15 +215,21 @@ export class RenderQueue extends RenderQueueBase implements IGrantable {
*/
private static readonly MINIMUM_LOAD_BALANCING_VERSION = new Version([10, 1, 10, 0]);

/**
* The minimum Deadline version required to enable Deadline Secrets Management on the Render Queue.
*/
private static readonly MINIMUM_SECRETS_MANAGEMENT_VERSION = new Version([10, 1, 18, 0]);
horsmand marked this conversation as resolved.
Show resolved Hide resolved

/**
* Regular expression that validates a hostname (portion in front of the subdomain).
*/
private static readonly RE_VALID_HOSTNAME = /^[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;

/**
* UID/GID for the RCS user.
* Information for the RCS user.
*/
private static readonly RCS_USER = { uid: 1000, gid: 1000 };

private static readonly RCS_USER = { uid: 1000, gid: 1000, username: 'ec2-user' };

/**
* The principal to grant permissions to.
Expand Down Expand Up @@ -412,12 +419,34 @@ export class RenderQueue extends RenderQueueBase implements IGrantable {
});
this.logGroup.grantWrite(this.asg);

if (props.repository.secretsManagementSettings.enabled) {
const errors = [];
if (props.version.isLessThan(RenderQueue.MINIMUM_SECRETS_MANAGEMENT_VERSION)) {
errors.push(`The supplied Deadline version is lower than the minimum required version: ${RenderQueue.MINIMUM_SECRETS_MANAGEMENT_VERSION.toString()}`);
jusiskin marked this conversation as resolved.
Show resolved Hide resolved
}
if (props.repository.secretsManagementSettings.credentials === undefined) {
errors.push('The Repository does not have Secrets Management credentials');
}
if (internalProtocol !== ApplicationProtocol.HTTPS) {
errors.push('The internal protocol on the Render Queue is not HTTPS.');
}
if (externalProtocol !== ApplicationProtocol.HTTPS) {
errors.push('External TLS on the Render Queue is not enabled.');
}
if (errors.length > 0) {
throw new Error(`Deadline Secrets Management is enabled on the supplied Repository but cannot be enabled on the Render Queue for the following reasons:\n${errors.join('\n')}`);
}
}
const taskDefinition = this.createTaskDefinition({
image: props.images.remoteConnectionServer,
portNumber: internalPortNumber,
protocol: internalProtocol,
repository: props.repository,
runAsUser: RenderQueue.RCS_USER,
secretsManagementOptions: props.repository.secretsManagementSettings.enabled ? {
credentials: props.repository.secretsManagementSettings.credentials!,
posixUsername: RenderQueue.RCS_USER.username,
} : undefined,
});
this.taskDefinition = taskDefinition;

Expand Down Expand Up @@ -530,6 +559,8 @@ export class RenderQueue extends RenderQueueBase implements IGrantable {
});
}

props.repository.secretsManagementSettings.credentials?.grantRead(this);

this.ecsServiceStabilized = new WaitForStableService(this, 'WaitForStableService', {
service: this.pattern.service,
});
Expand Down Expand Up @@ -648,6 +679,7 @@ export class RenderQueue extends RenderQueueBase implements IGrantable {
protocol: ApplicationProtocol,
repository: IRepository,
runAsUser?: { uid: number, gid?: number },
secretsManagementOptions?: { credentials: ISecret, posixUsername: string },
}) {
const { image, portNumber, protocol, repository } = props;

Expand Down Expand Up @@ -685,6 +717,10 @@ export class RenderQueue extends RenderQueueBase implements IGrantable {
environment.RCS_TLS_REQUIRE_CLIENT_CERT = 'no';
}

if (props.secretsManagementOptions !== undefined) {
environment.RCS_SM_CREDENTIALS_URI = props.secretsManagementOptions.credentials.secretArn;
}

// We can ignore this in test coverage because we always use RenderQueue.RCS_USER
/* istanbul ignore next */
const user = props.runAsUser ? `${props.runAsUser.uid}:${props.runAsUser.gid}` : undefined;
Expand All @@ -701,6 +737,27 @@ export class RenderQueue extends RenderQueueBase implements IGrantable {

containerDefinition.addMountPoints(connection.readWriteMountPoint);

if (props.secretsManagementOptions !== undefined) {
horsmand marked this conversation as resolved.
Show resolved Hide resolved
// Create volume to persist the RSA keypairs generated by Deadline between ECS tasks
// This makes it so subsequent ECS tasks use the same initial Secrets Management identity
const volumeName = 'deadline-user-keypairs';
taskDefinition.addVolume({
name: volumeName,
dockerVolumeConfiguration: {
scope: Scope.SHARED,
autoprovision: true,
driver: 'local',
},
});

// Mount the volume into the container at the location where Deadline expects it
containerDefinition.addMountPoints({
readOnly: false,
sourceVolume: volumeName,
containerPath: `/home/${props.secretsManagementOptions.posixUsername}/.config/.mono/keypairs`,
});
}

// Increase ulimits
containerDefinition.addUlimits(
{
Expand Down
7 changes: 6 additions & 1 deletion packages/aws-rfdk/lib/deadline/lib/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ export interface IRepository extends IConstruct {
*/
readonly version: IVersion;

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

/**
* Configures an ECS Container Instance and Task Definition for deploying a Deadline Client that directly connects to
* this repository.
Expand Down Expand Up @@ -557,7 +562,7 @@ export class Repository extends Construct implements IRepository {
private readonly installerGroup: AutoScalingGroup;

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Required arguments:
Optional arguments
-s Deadline Repository settings file to import.
-o The UID[:GID] that this script will chown the Repository files for. If GID is not specified, it defults to be the same as UID.
-c Secret management admin credentials ARN. If this parameter is specified, secrets management will be enabled.
-c Secrets management admin credentials ARN. If this parameter is specified, secrets management will be enabled.
-r Region where stacks are deployed. Required to get secret management credentials."

while getopts "i:p:v:s:o:c:r:" opt; do
Expand Down Expand Up @@ -102,20 +102,21 @@ chmod +x $REPO_INSTALLER

set +x

INSTALLER_DB_ARGS_STRING=''
for key in "${!INSTALLER_DB_ARGS[@]}"; do INSTALLER_DB_ARGS_STRING=$INSTALLER_DB_ARGS_STRING"${key} ${INSTALLER_DB_ARGS[$key]} "; done
REPO_ARGS=()

for key in "${!INSTALLER_DB_ARGS[@]}"; do
REPO_ARGS+=("${key}" "${INSTALLER_DB_ARGS[$key]}")
done

REPOSITORY_SETTINGS_ARG_STRING=''
if [ ! -z "${DEADLINE_REPOSITORY_SETTINGS_FILE+x}" ]; then
if [ ! -f "$DEADLINE_REPOSITORY_SETTINGS_FILE" ]; then
echo "ERROR: Repository settings file was specified but is not a file: $DEADLINE_REPOSITORY_SETTINGS_FILE."
exit 1
else
REPOSITORY_SETTINGS_ARG_STRING="--importrepositorysettings true --repositorysettingsimportoperation append --repositorysettingsimportfile \"$DEADLINE_REPOSITORY_SETTINGS_FILE\""
REPO_ARGS+=("--importrepositorysettings" "true" "--repositorysettingsimportoperation" "append" "--repositorysettingsimportfile" "$DEADLINE_REPOSITORY_SETTINGS_FILE")
fi
fi

SECRET_MANAGEMENT_ARG=''
if [ ! -z "${SECRET_MANAGEMENT_ARN+x}" ]; then
sudo yum install -y jq
SM_SECRET_VALUE=$(aws secretsmanager get-secret-value --secret-id=$SECRET_MANAGEMENT_ARN --region=$AWS_REGION)
Expand All @@ -131,8 +132,10 @@ if [ ! -z "${SECRET_MANAGEMENT_ARN+x}" ]; then
echo "ERROR: Admin password is too weak. It must be at least 8 characters long and contain at least one lowercase letter, one uppercase letter, one symbol and one digit."
exit 1
fi
echo "Secret management is enabled. Credentials are stored in secret: $SECRET_MANAGEMENT_ARN"
SECRET_MANAGEMENT_ARG="--installSecretsManagement true --secretsAdminName \"$SECRET_MANAGEMENT_USER\" --secretsAdminPassword \"$SECRET_MANAGEMENT_PASSWORD\""
echo "Secrets management is enabled. Credentials are stored in secret: $SECRET_MANAGEMENT_ARN"
REPO_ARGS+=("--installSecretsManagement" "true" "--secretsAdminName" "$SECRET_MANAGEMENT_USER" "--secretsAdminPassword" "$SECRET_MANAGEMENT_PASSWORD")
else
echo "Secrets management is not enabled."
fi

if [[ -n "${DEADLINE_REPOSITORY_OWNER+x}" ]]; then
Expand Down Expand Up @@ -164,7 +167,7 @@ if [[ -n "${DEADLINE_REPOSITORY_OWNER+x}" ]]; 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
$REPO_INSTALLER --mode unattended --setpermissions false --prefix "$PREFIX" --installmongodb false --backuprepo false ${REPO_ARGS[@]+"${REPO_ARGS[@]}"}

if [[ -n "${REPOSITORY_OWNER_UID+x}" ]]; then
echo "Changing ownership of Deadline Repository files to UID=$REPOSITORY_OWNER_UID GID=$REPOSITORY_OWNER_GID"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ describe('ConfigureSpotEventPlugin', () => {
repository: new Repository(stack, 'Repository', {
vpc,
version,
secretsManagementSettings: { enabled: false },
}),
trafficEncryption: { externalTLS: { enabled: false } },
version,
Expand Down Expand Up @@ -688,6 +689,7 @@ describe('ConfigureSpotEventPlugin', () => {
repository: new Repository(stack, 'Repository2', {
vpc,
version,
secretsManagementSettings: { enabled: false },
}),
trafficEncryption: { externalTLS: { enabled: false } },
version,
Expand Down Expand Up @@ -910,6 +912,7 @@ describe('ConfigureSpotEventPlugin', () => {
repository: new Repository(newStack, 'Repository', {
vpc,
version,
secretsManagementSettings: { enabled: false },
}),
trafficEncryption: { externalTLS: { enabled: false } },
version,
Expand Down Expand Up @@ -945,6 +948,7 @@ describe('ConfigureSpotEventPlugin', () => {
repository: new Repository(newStack, 'Repository', {
vpc,
version,
secretsManagementSettings: { enabled: false },
}),
trafficEncryption: { externalTLS: { enabled: false } },
version,
Expand Down
Loading