Skip to content

Commit

Permalink
fix(codebuild): correctly handle permissions for Projects inside VPC.
Browse files Browse the repository at this point in the history
A CodeBuild Project needs to have appropriate EC2 permissions on creation
when it uses a VPC. However, the default Policy that a Project Role has
depends on the Project itself (for CloudWatch Logs permissions).
Because of that, add a dependency between the Policy containing the EC2
permissions and the Project.

Also correctly handle the case when the Project's Role is imported.

BREAKING CHANGE: the method addToRoleInlinePolicy in CodeBuild's Project class has been removed.

Fixes #2651
Fixes #2652

comment out the imported check to see if it works now
  • Loading branch information
skinny85 committed Jun 17, 2019
1 parent be574a1 commit cc7aa81
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 35 deletions.
79 changes: 46 additions & 33 deletions packages/@aws-cdk/aws-codebuild/lib/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import ecr = require('@aws-cdk/aws-ecr');
import events = require('@aws-cdk/aws-events');
import iam = require('@aws-cdk/aws-iam');
import kms = require('@aws-cdk/aws-kms');
import { Aws, Construct, IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk';
import { Aws, CfnResource, Construct, IResource, Lazy, PhysicalName, Resource, ResourceIdentifiers, Stack } from '@aws-cdk/cdk';
import { BuildArtifacts, CodePipelineBuildArtifacts, NoBuildArtifacts } from './artifacts';
import { BuildSpec, mergeBuildSpecs } from './build-spec';
import { Cache } from './cache';
Expand Down Expand Up @@ -705,6 +705,8 @@ export class Project extends ProjectBase {
vpcConfig: this.configureVpc(props),
});

this.addVpcRequiredPermissions(props, resource);

const resourceIdentifiers = new ResourceIdentifiers(this, {
arn: resource.projectArn,
name: resource.projectName,
Expand Down Expand Up @@ -738,20 +740,6 @@ export class Project extends ProjectBase {
}
}

/**
* Add a permission only if there's a policy attached.
* @param statement The permissions statement to add
*/
public addToRoleInlinePolicy(statement: iam.PolicyStatement) {
if (this.role) {
const policy = new iam.Policy(this, 'PolicyDocument', {
policyName: 'CodeBuildEC2Policy',
statements: [statement]
});
this.role.attachInlinePolicy(policy);
}
}

/**
* Adds a secondary source to the Project.
*
Expand Down Expand Up @@ -888,31 +876,56 @@ export class Project extends ProjectBase {
});
this._securityGroups = [securityGroup];
}
this.addToRoleInlinePolicy(new iam.PolicyStatement({
resources: ['*'],
actions: [
'ec2:CreateNetworkInterface', 'ec2:DescribeNetworkInterfaces',
'ec2:DeleteNetworkInterface', 'ec2:DescribeSubnets',
'ec2:DescribeSecurityGroups', 'ec2:DescribeDhcpOptions',
'ec2:DescribeVpcs']
}));
this.addToRolePolicy(new iam.PolicyStatement({

return {
vpcId: props.vpc.vpcId,
subnets: props.vpc.selectSubnets(props.subnetSelection).subnetIds,
securityGroupIds: this._securityGroups.map(s => s.securityGroupId)
};
}

private addVpcRequiredPermissions(props: ProjectProps, project: CfnProject): void {
if (!props.vpc || !this.role) {
return;
}

this.role.addToPolicy(new iam.PolicyStatement({
resources: [`arn:aws:ec2:${Aws.region}:${Aws.accountId}:network-interface/*`],
actions: ['ec2:CreateNetworkInterfacePermission'],
conditions: {
StringEquals: {
"ec2:Subnet": props.vpc
'ec2:Subnet': props.vpc
.selectSubnets(props.subnetSelection).subnetIds
.map(si => `arn:aws:ec2:${Aws.region}:${Aws.accountId}:subnet/${si}`),
"ec2:AuthorizedService": "codebuild.amazonaws.com"
}
}
'ec2:AuthorizedService': 'codebuild.amazonaws.com'
},
},
}));
return {
vpcId: props.vpc.vpcId,
subnets: props.vpc.selectSubnets(props.subnetSelection).subnetIds,
securityGroupIds: this._securityGroups.map(s => s.securityGroupId)
};

const policy = new iam.Policy(this, 'PolicyDocument', {
policyName: 'CodeBuildEC2Policy',
statements: [
new iam.PolicyStatement({
resources: ['*'],
actions: [
'ec2:CreateNetworkInterface',
'ec2:DescribeNetworkInterfaces',
'ec2:DeleteNetworkInterface',
'ec2:DescribeSubnets',
'ec2:DescribeSecurityGroups',
'ec2:DescribeDhcpOptions',
'ec2:DescribeVpcs',
],
}),
],
});
this.role.attachInlinePolicy(policy);

// add an explicit dependency between the EC2 Policy and this Project -
// otherwise, creating the Project fails,
// as it requires these permissions to be already attached to the Project's Role
const cfnPolicy = policy.node.findChild('Resource') as CfnResource;
project.addDependsOn(cfnPolicy);
}

private parseArtifacts(props: ProjectProps) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,10 @@
"Ref": "MyVPCAFB07A31"
}
}
}
},
"DependsOn": [
"MyProjectPolicyDocument646EE0F2"
]
}
},
"Parameters": {
Expand All @@ -541,4 +544,4 @@
"Description": "Artifact hash for asset \"aws-cdk-codebuild-project-vpc/Bundle\""
}
}
}
}
24 changes: 24 additions & 0 deletions packages/@aws-cdk/aws-codebuild/test/test.project.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { expect, haveResource, haveResourceLike, not } from '@aws-cdk/assert';
import assets = require('@aws-cdk/assets');
import ec2 = require('@aws-cdk/aws-ec2');
import iam = require('@aws-cdk/aws-iam');
import { Bucket } from '@aws-cdk/aws-s3';
import cdk = require('@aws-cdk/cdk');
import { Test } from 'nodeunit';
Expand Down Expand Up @@ -266,4 +268,26 @@ export = {

test.done();
},

'can use an imported Role for a Project within a VPC'(test: Test) {
const stack = new cdk.Stack();

const importedRole = iam.Role.fromRoleArn(stack, 'Role', 'arn:aws:iam::1234567890:role/service-role/codebuild-bruiser-service-role');
const vpc = new ec2.Vpc(stack, 'Vpc');

new codebuild.Project(stack, 'Project', {
source: new codebuild.GitHubEnterpriseSource({
httpsCloneUrl: 'https://mygithub-enterprise.com/myuser/myrepo',
}),
artifacts: new codebuild.NoBuildArtifacts(),
role: importedRole,
vpc,
});

expect(stack).to(haveResourceLike('AWS::CodeBuild::Project', {
// no need to do any assertions
}));

test.done();
},
};

0 comments on commit cc7aa81

Please sign in to comment.