forked from aws/aws-cdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(codepipeline): migrate to configurable KMS keys (cross-account w…
…orkflow) Use custom KMS keys, which are passed or generated by CDK in order to encrypt artifacts stored in S3. Customers would be able to pass own configuration of artifacts store - including S3 Bucker and KMS Encryption Key. Set of changes: * add KMS key & alias (with statically generated name) to scaffold stack; * reference scaffold KMS key in ArtifactStore resource; * scaffold stack artifact names derive name from pipeline name (or other identifier) to prevent collision with other projects. This change is mainly motivated to enable cross-account deployments. The work should enable to construct stacks like described in reference architecture https://github.com/awslabs/aws-refarch-cross-account-pipeline In such a case foreign role have to have access to stored artifacts in S3 buckets. However in order to achieve this two things must be achieved * foreign account (role) has to be whitelisted on artifacts' bucket policy; and * foreign account (role) has to be whitelisted on KMS keys used to encrypt artifacts in above bucket (which is impossible to do with AWS managed one). In order to enable above cross-region scaffold stacks will contain KMS keys. As KMS key can't be directly reference in cross-region / cross-account the alias with known name is generated, in similar way as it happens for S3 buckets. Together with aws#1449 customers should be able to create on their own roles, and import those in pipeline stack. When Some parts of this workflow could be automated, however even without it users would be enabled to create cross-account pipelines. * Create stack for deployments in foreign account * create *jump* role which would be set on action (aws#1449) this role would mainly allow passing Cloud Formation deployment role; * grant *jump* role read / write (as required) permissions to S3 buckets and KMS keys (right now whole foreign account is whitelisted) * create deployment role; * In pipeline account * import both roles (require role ARNs to be known) * set imported *jump* role on Cloud Formation actions; * set imported deployment role as Cloud Formation role
- Loading branch information
Rado Smogura
committed
Jan 12, 2019
1 parent
dac9bfa
commit 5b4c5c7
Showing
8 changed files
with
542 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* Represents configuration of artifact store used for cross-region and cross account replication of deployment artifacts. | ||
* | ||
* Artifacts store is a set of AWS artifacts (like buckets and KMS keys) which are used by pipeline to store | ||
* input and output to and from actions. | ||
*/ | ||
export interface ArtifactsStoreProps { | ||
/** | ||
* The name of the S3 Bucket used for replicating the Pipeline's artifacts into the region. | ||
*/ | ||
replicationBucketName: string; | ||
|
||
/** | ||
* Encryption key used to encrypt artifacts, it can represent key ARN or it can be an alias to key. | ||
*/ | ||
replicationKeyArn?: string; | ||
} |
145 changes: 145 additions & 0 deletions
145
packages/@aws-cdk/aws-codepipeline/lib/cross-account-scaffold-stack.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import cdk = require('@aws-cdk/cdk'); | ||
import iam = require('@aws-cdk/aws-iam'); | ||
import {App} from "@aws-cdk/cdk/lib/app"; | ||
|
||
// PENDING CLASS | ||
// Actually customers may want to generate as much roles as possible | ||
// CDK primarily should support setting those permission. | ||
export interface CrossAccountStackProps { | ||
/** | ||
* Name of cloud formation role used to deploy and execute stack. | ||
*/ | ||
cfDeployerRoleName: string; | ||
|
||
/** | ||
* Name of pipeline role used to execute stack and cloud formation. | ||
*/ | ||
actionRoleName:string; | ||
|
||
/** | ||
* Id of target AWS account. Required to synthesize proper roles ARNs. | ||
*/ | ||
account:string; | ||
} | ||
|
||
/** | ||
* Represents stack on target account required to preform | ||
* cross account deployments. | ||
* | ||
* This stack is typically auto generated, to make creation of | ||
* stack easier. However it's possible not to deploy this stack, | ||
* and configure all roles manually. | ||
* | ||
* The requirements for stacks are as follow: | ||
* * in target there account there must exists cloud formation deployer | ||
* role, which will be used to execute deployment, this role must have | ||
* to have all required permissions, including access to artifacts buckets | ||
* (main and / or cross region), KMS keys - those two access requires | ||
* setting **resource** policy for account and / or role. | ||
* * pipeline role - used by pipeline and allowing passing and assuming | ||
* cloud formation role | ||
*/ | ||
export class CrossAccountScaffoldStack extends cdk.Stack { | ||
/** | ||
* Role used to execute cloud formation. | ||
*/ | ||
public readonly deployerRole: iam.Role; | ||
|
||
/** | ||
* Pipeline role used to update, build and execute stage and actions. | ||
*/ | ||
public readonly actionRole: iam.Role; | ||
|
||
/** | ||
* Synthesized ARN of cloud formation deployer role used to deploy and execute stack. | ||
*/ | ||
public readonly deployerRoleArn:string; | ||
|
||
/** | ||
* Synthesized ARN of of pipeline role used to update, build and execute stage and actions. | ||
*/ | ||
public readonly actionRoleArn:string; | ||
|
||
/** | ||
* Id of target AWS account. Required to synthesize proper role ARNs. | ||
*/ | ||
public readonly account:string; | ||
|
||
/** | ||
* Generates deployer roles used by for performing cloud formation deploy. | ||
* | ||
* @param props the properties used to bootsrap stack | ||
*/ | ||
protected generateDeployerRole(props:CrossAccountStackProps): iam.Role { | ||
return new iam.Role(this, 'CloudFormationRole', { | ||
roleName: props.cfDeployerRoleName, | ||
assumedBy: new iam.ServicePrincipal("cloudformation.amazonaws.com") | ||
}); | ||
} | ||
|
||
/** | ||
* Generates pipeline roles used to execute deployment. | ||
* | ||
* @param props the properties used to bootsrap stack | ||
*/ | ||
protected generateActionRole(props:CrossAccountStackProps): iam.Role { | ||
return new iam.Role(this, 'PipelineRole', { | ||
roleName: props.actionRoleName, | ||
assumedBy: new iam.ServicePrincipal("*") // If we could specify here the pipeline role | ||
}); | ||
} | ||
|
||
constructor(parent: App, props:CrossAccountStackProps) { | ||
super(parent, CrossAccountScaffoldStack.generateStackName(props), { | ||
env: { | ||
account: props.account | ||
} | ||
}); | ||
|
||
this.account = props.account; | ||
|
||
this.deployerRole = this.generateDeployerRole(props); | ||
this.actionRole = this.generateActionRole(props); | ||
|
||
this.deployerRoleArn = CrossAccountScaffoldStack.synthesizeRoleArn(props.account, props.cfDeployerRoleName); | ||
this.actionRoleArn = CrossAccountScaffoldStack.synthesizeRoleArn(props.account, props.actionRoleName); | ||
|
||
// Policy base ond aws cross account refarch | ||
this.actionRole.addToPolicy(new iam.PolicyStatement() | ||
.addActions("cloudformation:*", "iam:PassRole") | ||
.addAllResources() | ||
); | ||
|
||
// If possible S3 should be narrowed, same like in ref-arch, however good for now | ||
// S3 bucket name is something we have in hand from artifacts-store, so it | ||
// should be possible | ||
this.actionRole.addToPolicy(new iam.PolicyStatement() | ||
.addActions("s3:*") | ||
.addAllResources() | ||
); | ||
|
||
// KMS is bit problematic here, as it's UUID based, so we can't | ||
// statically synthesize name here, all we can do is to give access | ||
// to key alias (but it's don't include encrypt / decrypt), | ||
// work around would be to tag kms keys and make conditional access based on KMS key tag | ||
// | ||
// In any way we can narrow required permissions do decryption and alias de-ref | ||
// however good for now | ||
// TODO Narrow those permissions to required for encryption and decryption, exclude admin ones | ||
this.actionRole.addToPolicy(new iam.PolicyStatement() | ||
.addActions("kms:*") | ||
.addAllResources() | ||
); | ||
} | ||
|
||
static generateStackName(props:CrossAccountStackProps):string { | ||
return `account-${props.account}-stack`; | ||
} | ||
|
||
static synthesizeRoleArn(account:string, roleName:string):string { | ||
// Can't use ARN utils as name must be string, as it can be shared to other | ||
// constructs | ||
return `arn:aws:iam::${account}:role/${roleName}`; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.