A system for providing AWS CodePipeline with more secure access to an organization's Github repositories.
A CodePipeline requires a Github personal access token in order to read code from an organization's private repositories. For a large organization, it does not make sense to allow its members to use their own tokens to generate pipelines, and Github actively discourages the use of "machine users" for this purpose.
Furthermore, defining a pipeline in CloudFormation requires the Github token be provided in plain text, either as a stack parameter, or hard-coded into the definition of the AWS::CodePipeline::Pipeline
resource.
This library is an attempt to minimize the security footprint involved in using CodePipeline & Github by providing an organization's security team with a system that makes the Github personal tokens easier to track, rotate, and maintain.
The basic architecture of this system is:
-
An organization stores a Github personal access token in AWS Secrets Manager.
-
A CloudFormation stack is launched using
cloudformation/code-pipeline-helper.template.js
. This stack's inputs are:
- The version of code-pipeline-helper code being deployed
- The
SecretId
for the token stored in AWS Secrets Manager.
The stack generates a Lambda Function that is designed to be the backend for a custom CloudFormation resource -- in effect a replacement for CloudFormation's native AWS::CodePipeline::Pipeline
resource.
- Individuals who wish to build a CodePipeline continuous integration/deployment pipeline from one of the organization's Github repositories write CloudFormation templates to define their pipelines. They use the Lambda function generated by the main stack as a custom resource to define their pipeline. The properties that are required to create a pipeline using this Lambda function are identical to CloudFormation's native
AWS::CodePipeline::Pipeline
resource with the following exceptions:
-
The user must not provide the first
Source
stage of the pipeline. This will be created for them by the Lambda function. -
The user must provide the Github organization name, repository name and branch from which they want to start pipeline actions.
-
The user must provide the ARN of the Lambda function setup in the main code-pipeline-helper stack. Use
Fn::ImportValue
and provide the name of the code-pipeline-helper-stack.
- When the individual's CloudFormation template is launched, the Lambda function accepts the property values provided by the stack, and creates, updates, or deletes the pipeline as neccessary. In the event of a
CREATE
action, the Lambda function looks up the Github personal access token to use in AWS Secrets Manager, then uses that token to provide access to Github for the pipeline.
-
Create a Github personal access token with access to one or more of your organization's private repositories.
-
Store the token in AWS Secrets Manager. You can use any SecretId you wish, but the code here assumes a default SecretId:
code-pipeline-helper/access-token
. -
Clone this repository and launch a CloudFormation stack using cfn-config using the
code-pipeline-helper.template.js
template file. -
You can set up several stacks, each using different access tokens, or a single stack with access to all your organization's repositories. This is an implementation decision that depends on your organization's security model.
-
Identify a Github repository and branch from which you want to build a pipeline.
-
You must know the name of the code-pipeline-helper stack that is set up for your organization and provides access to this repository.
-
Build a CloudFormation template to define your pipeline. Instead of using CloudFormation's native
AWS::CodePipeline::Pipeline
resource type, use a custom resource. Your custom resource should look like this:
{
"Type": "Custom::CodePipeline::Pipeline",
"Properties": {
"ServiceToken": {
"Fn::ImportValue": "name-of-code-pipeline-helper-stack-goes-here"
},
"Owner": "name-of-your-github-organization-goes-here",
"Repo": "name-of-your-repository-goes-here",
"Branch": "name-of-your-branch-goes-here",
"... other properties as required for defining an AWS::CodePipeline::Pipeline"
}
}
- Launch the stack.
In this library, cloudformation/bootstrap.template.js
is an example of a template that defines a pipeline. This pipeline reacts to commits to this repository by executing a CodeBuild project. That project builds a .zip
file of the code in this library and uploads it to S3 with --acl public-read
. These "bundles" contain the code required to run code-pipeline-helper stack's primary Lambda functions.