Skip to content

Commit

Permalink
Add support for use of existing VPC with Cloudformation (#1957)
Browse files Browse the repository at this point in the history
- Cloudformation now expects an existing VPC, 2 public and 4 private subnets to run
- Updated Teleport to version 2.6.0
- Randomised domain name to make SSL certificate issuing work more frequently
- S3 buckets are now deleted after running
- Old AMIs/snapshots are deleted automtaically
  • Loading branch information
webvictim authored Jun 1, 2018
1 parent 7978158 commit bc6aa78
Show file tree
Hide file tree
Showing 9 changed files with 593 additions and 294 deletions.
2 changes: 1 addition & 1 deletion assets/marketplace/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
files/system/license.pem

*.swp
36 changes: 30 additions & 6 deletions assets/marketplace/Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,37 @@ pipeline {
}
steps {
dir('assets/marketplace') {
sh 'make jenkins-create-stack'
sh 'make jenkins-create-stack-vpc'
sh 'make jenkins-wait-for-stack-creation-vpc'
// get VPC stack outputs as a JSON list of {ParameterKey: key, ParameterValue: value} pairs
// write this to vpc.json
sh 'make jenkins-describe-stack-outputs-vpc | jq -r "map ( { ParameterKey: ( .OutputKey | tostring ), ParameterValue: ( .OutputValue | tostring) } )" > vpc.json'
// use jq to combine Jenkins JSON stack parameters and vpc.json into one list of pairs
sh "jq -s add ${JENKINS_CLOUDFORMATION_PARAMETERS_PATH} vpc.json > full-parameters.json"
// generate 16 random characters and update the domain name we're using (beware: there is a 64 character limit on the common name field)
sh 'dd if=/dev/urandom bs=128 count=1 | tr -dc "a-z0-9" | fold -w 16 | head -n 1 > random-string'
sh 'sed -i "s/jenkins-teleport-quickstart-test-stack.gravitational.io/jenkins-teleport-quickstart-$(cat random-string).gravitational.io/g" full-parameters.json'
sh 'jq -r \'.[] | select(.ParameterKey == "DomainName") | .ParameterValue\' full-parameters.json > generated-domain-name'
sh 'cat full-parameters.json'
sh 'cat generated-domain-name'
// update path to parameters file and create ASG stack
sh 'JENKINS_CLOUDFORMATION_PARAMETERS_PATH=full-parameters.json make jenkins-create-stack'
sh 'make jenkins-wait-for-stack-creation'
sh 'make jenkins-get-s3-bucket-id > s3-bucket-id'
sh 'cat s3-bucket-id'
}
}
}
stage('Wait for Teleport Web UI to come up') {
steps {
timeout(5) {
waitUntil {
script {
def r = sh script: 'wget -q https://jenkins-teleport-quickstart-test-stack.gravitational.io/web/login', returnStatus: true
return (r == 0);
dir('assets/marketplace') {
script {
sh 'cat generated-domain-name'
def r = sh script: 'wget -q https://$(cat generated-domain-name)/web/login', returnStatus: true
return (r == 0);
}
}
}
}
Expand All @@ -56,8 +75,13 @@ pipeline {
always {
dir('assets/marketplace') {
script {
sh 'make jenkins-delete-stack'
sh 'make jenkins-wait-for-stack-deletion'
// we want to run all these shutdown steps in order regardless of success/failure (in case part of the ASG fails)
// so we use '|| true' to suppress return codes > 0
sh 'make jenkins-delete-stack || true'
sh 'make jenkins-wait-for-stack-deletion || true'
sh 'make jenkins-delete-stack-vpc || true'
sh 'make jenkins-wait-for-stack-deletion-vpc || true'
sh 'S3_BUCKET_ID=$(cat s3-bucket-id) make jenkins-delete-s3-bucket || true'
}
}
}
Expand Down
96 changes: 88 additions & 8 deletions assets/marketplace/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ BUILD_SUBNET_ID ?=
AWS_REGION ?= us-west-2

# Teleport version
TELEPORT_VERSION ?= 2.6.0-alpha.8
TELEPORT_VERSION ?= 2.6.0

# Teleport UID is a UID of a non-privileged user ID of a teleport
# Teleport UID is the UID of a non-privileged 'teleport' user
TELEPORT_UID ?= 1007

# Instance type is a single value, sorry
Expand All @@ -36,10 +36,16 @@ TELEPORT_LICENSE_URI ?=
BUILD_TIMESTAMP := $(shell TZ=UTC /bin/date "+%Y%m%d-%H%M%S%Z")

# Jenkins test CloudFormation stack name
JENKINS_TEST_STACK_NAME := jenkins-teleport-quickstart-test-stack
JENKINS_TEST_STACK_NAME ?= jenkins-teleport-quickstart-test-stack

# Jenkins Cloudformation parameters path
JENKINS_CLOUDFORMATION_PARAMETERS_PATH ?= ./files/build/jenkins-parameters.json

# YAML filename
CF_YAML_FILENAME ?= ./oss.yaml

# S3 Bucket ID
S3_BUCKET_ID ?=
export

.PHONY: oss
Expand Down Expand Up @@ -69,47 +75,121 @@ update-ami-ids-ent:

.PHONY: validate-template
validate-template:
aws cloudformation validate-template --template-body file://./oss.yaml
aws cloudformation validate-template --template-body file://$(CF_YAML_FILENAME)


# Stack functionality
# Create
.PHONY: create-stack
create-stack:
$(MAKE) validate-template
aws --region=$(AWS_REGION) cloudformation create-stack --capabilities CAPABILITY_IAM --stack-name $(STACK) --template-body file://./oss.yaml --parameters $(STACK_PARAMS)
aws --region=$(AWS_REGION) cloudformation create-stack --capabilities CAPABILITY_IAM --stack-name $(STACK) --template-body file://$(CF_YAML_FILENAME) --parameters $(STACK_PARAMS)

.PHONY: create-stack-vpc
create-stack-vpc: CF_YAML_FILENAME=./oss-vpc.yaml
create-stack-vpc:
$(MAKE) create-stack


# Update
.PHONY: update-stack
update-stack:
$(MAKE) validate-template
aws --region=$(AWS_REGION) cloudformation update-stack --capabilities CAPABILITY_IAM --stack-name $(STACK) --template-body file://./oss.yaml --parameters $(STACK_PARAMS)
aws --region=$(AWS_REGION) cloudformation update-stack --capabilities CAPABILITY_IAM --stack-name $(STACK) --template-body file://$(CF_YAML_FILENAME) --parameters $(STACK_PARAMS)

.PHONY: update-stack-vpc
update-stack-vpc: CF_YAML_FILENAME=./oss-vpc.yaml
update-stack-vpc:
$(MAKE) update-stack


# Describe
.PHONY: describe-stack
describe-stack:
aws --region=$(AWS_REGION) cloudformation describe-stacks --stack-name $(STACK)
@aws --region=$(AWS_REGION) cloudformation describe-stacks --stack-name $(STACK)

.PHONY: describe-stack-outputs
describe-stack-outputs:
@aws --region=$(AWS_REGION) cloudformation describe-stacks --stack-name $(STACK) --query 'Stacks[].Outputs[]'

.PHONY: describe-stack-outputs-vpc
describe-stack-outputs-vpc: JENKINS_TEST_STACK_NAME=jenkins-teleport-quickstart-test-stack-vpc
describe-stack-outputs-vpc:
@aws --region=$(AWS_REGION) cloudformation describe-stacks --stack-name $(STACK) --query 'Stacks[].Outputs[]'


# Delete
.PHONY: delete-stack
delete-stack:
aws --region=$(AWS_REGION) cloudformation delete-stack --stack-name $(STACK)


# Jenkins helpers
.PHONY: jenkins-create-stack
jenkins-create-stack:
$(MAKE) validate-template
aws --region=$(AWS_REGION) cloudformation create-stack --capabilities CAPABILITY_IAM --stack-name $(JENKINS_TEST_STACK_NAME) --template-body file://./oss.yaml --parameters file://$(JENKINS_CLOUDFORMATION_PARAMETERS_PATH)
aws --region=$(AWS_REGION) cloudformation create-stack --capabilities CAPABILITY_IAM --stack-name $(JENKINS_TEST_STACK_NAME) --template-body file://$(CF_YAML_FILENAME) --parameters file://$(JENKINS_CLOUDFORMATION_PARAMETERS_PATH)

.PHONY: jenkins-create-stack-vpc
jenkins-create-stack-vpc: CF_YAML_FILENAME=./oss-vpc.yaml
jenkins-create-stack-vpc: JENKINS_TEST_STACK_NAME=jenkins-teleport-quickstart-test-stack-vpc
jenkins-create-stack-vpc:
$(MAKE) jenkins-create-stack

.PHONY: jenkins-wait-for-stack-creation
jenkins-wait-for-stack-creation:
aws --region=$(AWS_REGION) cloudformation wait stack-create-complete --stack-name $(JENKINS_TEST_STACK_NAME)

.PHONY: jenkins-wait-for-stack-creation-vpc
jenkins-wait-for-stack-creation-vpc: JENKINS_TEST_STACK_NAME=jenkins-teleport-quickstart-test-stack-vpc
jenkins-wait-for-stack-creation-vpc:
$(MAKE) jenkins-wait-for-stack-creation

.PHONY: jenkins-describe-stack
jenkins-describe-stack:
@aws --region=$(AWS_REGION) cloudformation describe-stacks --stack-name $(JENKINS_TEST_STACK_NAME)

.PHONY: jenkins-describe-stack-outputs
jenkins-describe-stack-outputs:
@aws --region=$(AWS_REGION) cloudformation describe-stacks --stack-name $(JENKINS_TEST_STACK_NAME) --query 'Stacks[].Outputs[]'

.PHONY: jenkins-describe-stack-vpc
jenkins-describe-stack-vpc: JENKINS_TEST_STACK_NAME=jenkins-teleport-quickstart-test-stack-vpc
jenkins-describe-stack-vpc:
$(MAKE) jenkins-describe-stack

.PHONY: jenkins-describe-stack-outputs-vpc
jenkins-describe-stack-outputs-vpc: JENKINS_TEST_STACK_NAME=jenkins-teleport-quickstart-test-stack-vpc
jenkins-describe-stack-outputs-vpc:
@$(MAKE) -s jenkins-describe-stack-outputs

.PHONY: jenkins-get-s3-bucket-id
jenkins-get-s3-bucket-id:
@$(MAKE) -s jenkins-describe-stack-outputs | jq -r '.[] | select(.OutputKey == "S3BucketID") | .OutputValue'

.PHONY: jenkins-delete-s3-bucket
jenkins-delete-s3-bucket:
files/s3-delete-all-object-versions.sh $(S3_BUCKET_ID)
aws s3api delete-bucket --region=$(AWS_REGION) --bucket $(S3_BUCKET_ID)

.PHONY: jenkins-delete-stack
jenkins-delete-stack:
aws --region=$(AWS_REGION) cloudformation delete-stack --stack-name $(JENKINS_TEST_STACK_NAME)

.PHONY: jenkins-delete-stack-vpc
jenkins-delete-stack-vpc: JENKINS_TEST_STACK_NAME=jenkins-teleport-quickstart-test-stack-vpc
jenkins-delete-stack-vpc:
$(MAKE) jenkins-delete-stack

.PHONY: jenkins-wait-for-stack-deletion
jenkins-wait-for-stack-deletion:
aws --region=$(AWS_REGION) cloudformation wait stack-delete-complete --stack-name $(JENKINS_TEST_STACK_NAME)

.PHONY: jenkins-wait-for-stack-deletion-vpc
jenkins-wait-for-stack-deletion-vpc: JENKINS_TEST_STACK_NAME=jenkins-test-stack-vpc
jenkins-wait-for-stack-deletion-vpc:
$(MAKE) jenkins-wait-for-stack-deletion

.PHONY: check-ent-vars
check-ent-vars:
@if [ -z "$(TELEPORT_VERSION)" ]; then \
Expand Down
59 changes: 52 additions & 7 deletions assets/marketplace/README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,78 @@
# Teleport AWS Quickstart Guide

AWS quickstart for teleport
AWS Quickstart for Teleport

## Development instructions

**Prerequisites**

AWS CLI and Packer are required to build and launch a CloudFormation stack.

On macOS:

```
brew install awscli
brew install packer
```

On Linux:

```
apt install awscli
Follow instructions at: https://www.packer.io/docs/install/index.html
```

**To build an AMI**

```
make oss
```

Update oss.yaml with new AMI image IDs.
**Update YAML files with the new AMI image IDs**

```
make update-ami-ids-oss
```

**Launch a dev cloudformation stack using an existing VPC**

**Launch a dev cloudformation stack**
When using an existing VPC it must have both DNS support and DNS hostnames enabled.

The deployment needs six VPC subnet IDs provided - two public (for the proxy) and four private (for auth and nodes).
For redundancy, the subnets should be split across availability zones - odd numbers in AZ A and even numbers in AZ B, for example.

Replace the placeholder values in the exports below.

```
export STACK=test1
export STACK_PARAMS="ParameterKey=KeyName,ParameterValue=KeyName ParameterKey=DomainName,ParameterValue=teleport.example.com ParameterKey=DomainAdminEmail,ParameterValue=admin@example.com ParameterKey=HostedZoneID,ParameterValue=AWSZONEID"
export STACK_PARAMS="\
ParameterKey=VPCID,ParameterValue=EXISTING_VPC_ID \
ParameterKey=ProxySubnetA,ParameterValue=PUBLIC_SUBNET_ID_1 \
ParameterKey=ProxySubnetB,ParameterValue=PUBLIC_SUBNET_ID_2 \
ParameterKey=AuthSubnetA,ParameterValue=PRIVATE_SUBNET_ID_1 \
ParameterKey=AuthSubnetB,ParameterValue=PRIVATE_SUBNET_ID_2 \
ParameterKey=NodeSubnetA,ParameterValue=PRIVATE_SUBNET_ID_3 \
ParameterKey=NodeSubnetB,ParameterValue=PRIVATE_SUBNET_ID_4 \
ParameterKey=KeyName,ParameterValue=KeyName \
ParameterKey=DomainName,ParameterValue=teleport.example.com \
ParameterKey=DomainAdminEmail,ParameterValue=admin@example.com \
ParameterKey=HostedZoneID,ParameterValue=AWS_ZONE_ID"
make create-stack
```

## Usage instructions

After stack has been provisioned, add admin user:
After stack has been provisioned, login to the AWS Console and capture the IP address of a Proxy Server and a Auth Server then type the following to add a admin user:

```
ssh -i key.pem -o ProxyCommand="ssh -i key.pem -W %h:%p ec2-user@PROXY_SERVER" ec2-user@$AUTH_SERVER
sudo -u teleport tctl users add bob --roles=admin
```
# For OSS
sudo -u teleport tctl users add bob bob
# For Enterprise
sudo -u teleport tctl users add bob --roles=admin
```

2 changes: 1 addition & 1 deletion assets/marketplace/files/bin/teleport-lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ aws dynamodb put-item \
if [ $? -eq 0 ]; then
echo "Renewed or locked the lease for $PROCESS until $(date -d @$TTL)"
else
echo "Could get renew lease, locked by other process"
echo "Could not get renew lease, locked by other process"
exit 255
fi
41 changes: 41 additions & 0 deletions assets/marketplace/files/s3-delete-all-object-versions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash
if [[ "${AWS_REGION}" == "" ]]; then
echo "AWS_REGION must be set"
exit 1
fi
bucket=$1
if [[ "${bucket}" == "" ]]; then
echo "Usage: $(basename $0) <bucket>"
exit 1
fi

set -e
echo "Removing all versions from ${bucket}"

versions=$(aws s3api list-object-versions --bucket ${bucket} | jq '.Versions')
markers=$(aws s3api list-object-versions --bucket ${bucket} | jq '.DeleteMarkers')
let count=$(echo ${versions} | jq 'length')-1

if [ ${count} -gt -1 ]; then
echo "removing files"
for i in $(seq 0 ${count}); do
key=$(echo ${versions} | jq .[$i].Key | sed -e 's/\"//g')
versionId=$(echo ${versions} | jq .[$i].VersionId | sed -e 's/\"//g')
cmd="aws s3api delete-object --region=${AWS_REGION} --bucket ${bucket} --key ${key} --version-id ${versionId}"
echo $cmd
$cmd
done
fi

let count=$(echo ${markers} | jq 'length')-1

if [ ${count} -gt -1 ]; then
echo "removing delete markers"
for i in $(seq 0 ${count}); do
key=$(echo ${markers} | jq .[$i].Key | sed -e 's/\"//g')
versionId=$(echo ${markers} | jq .[$i].VersionId | sed -e 's/\"//g')
cmd="aws s3api delete-object --region=${AWS_REGION} --bucket ${bucket} --key ${key} --version-id ${versionId}"
echo $cmd
$cmd
done
fi
Loading

0 comments on commit bc6aa78

Please sign in to comment.