├── .gitignore ├── LICENSE ├── README.md ├── packer ├── aws │ ├── ubuntu │ │ ├── base.json │ │ ├── consul.json │ │ ├── haproxy.json │ │ ├── nodejs.json │ │ └── vault.json │ └── windows │ │ ├── base.json │ │ └── web.json ├── config │ ├── consul │ │ ├── consul_client.json │ │ ├── consul_server.json │ │ ├── haproxy.json │ │ ├── nodejs.json │ │ └── vault.json │ ├── consul_template │ │ ├── base.hcl │ │ ├── haproxy.hcl │ │ ├── nodejs.hcl │ │ ├── nodejs_aws.hcl │ │ └── templates │ │ │ ├── haproxy.ctmpl │ │ │ ├── vault_aws.ctmpl │ │ │ └── vault_generic.ctmpl │ ├── envconsul │ │ ├── base.hcl │ │ └── nodejs.hcl │ └── vault │ │ ├── policies │ │ ├── aws_nodejs.json │ │ ├── nodejs.hcl │ │ └── nodejs.json │ │ ├── scripts │ │ ├── setup_aws.sh │ │ ├── setup_transit.sh │ │ ├── setup_vault.sh │ │ ├── write_policy.sh │ │ └── write_role.sh │ │ └── vault.hcl ├── google │ └── ubuntu │ │ ├── base.json │ │ ├── consul.json │ │ ├── haproxy.json │ │ ├── nodejs.json │ │ └── vault.json └── scripts │ ├── ubuntu │ ├── cleanup.sh │ ├── consul.sh │ ├── consul_template.sh │ ├── dependencies.sh │ ├── dnsmasq.sh │ ├── envconsul.sh │ ├── haproxy.sh │ ├── nodejs.sh │ ├── nodejs_app │ │ ├── compile.json │ │ ├── package.json │ │ └── server.js │ ├── upstart │ │ ├── consul.conf │ │ ├── consul_template.conf │ │ ├── haproxy.conf │ │ ├── nodejs.conf │ │ └── vault.conf │ └── vault.sh │ └── windows │ ├── config │ └── ec2_user_data.conf │ ├── consul_client.json │ ├── install_consul.ps1 │ ├── install_web_server.ps1 │ ├── install_windows_updates.ps1 │ └── set_ec2_config.ps1 ├── setup ├── gen_cert.sh ├── gen_key.sh └── openssl.cnf └── terraform ├── empty.tf ├── modules ├── aws │ ├── compute │ │ ├── compute.tf │ │ ├── haproxy │ │ │ ├── haproxy.sh.tpl │ │ │ └── haproxy.tf │ │ └── nodejs │ │ │ ├── nodejs.sh.tpl │ │ │ └── nodejs.tf │ ├── data │ │ ├── consul │ │ │ ├── consul.sh.tpl │ │ │ └── consul.tf │ │ ├── data.tf │ │ └── vault │ │ │ ├── vault.sh.tpl │ │ │ └── vault.tf │ ├── network │ │ ├── bastion │ │ │ └── bastion.tf │ │ ├── nat │ │ │ └── nat.tf │ │ ├── network.tf │ │ ├── openvpn │ │ │ └── openvpn.tf │ │ ├── private_subnet │ │ │ └── private_subnet.tf │ │ ├── public_subnet │ │ │ └── public_subnet.tf │ │ └── vpc │ │ │ └── vpc.tf │ └── util │ │ ├── artifact │ │ └── artifact.tf │ │ ├── deploy │ │ └── deploy.tf │ │ ├── iam │ │ └── iam.tf │ │ └── website │ │ └── website.tf └── google │ ├── compute │ ├── compute.tf │ ├── haproxy │ │ ├── haproxy.sh.tpl │ │ └── haproxy.tf │ └── nodejs │ │ ├── nodejs.sh.tpl │ │ └── nodejs.tf │ ├── data │ ├── consul │ │ ├── consul.sh.tpl │ │ └── consul.tf │ ├── data.tf │ └── vault │ │ ├── vault.sh.tpl │ │ └── vault.tf │ └── network │ ├── bastion │ └── bastion.tf │ ├── network.tf │ ├── private_subnet │ └── private_subnet.tf │ └── public_subnet │ └── public_subnet.tf └── providers ├── aws ├── README.md ├── global │ ├── global.tf │ └── terraform.tfvars ├── us_east_1_prod │ ├── terraform.tfvars │ └── us_east_1_prod.tf └── us_east_1_staging │ ├── terraform.tfvars │ └── us_east_1_staging.tf └── google ├── README.md └── us_central1_prod ├── terraform.tfvars └── us_central1_prod.tf /.gitignore: -------------------------------------------------------------------------------- 1 | account.json 2 | .terraform 3 | *tfstate* 4 | setup/* 5 | crash.log 6 | dist 7 | *.zip 8 | logs 9 | *.srl 10 | *.crt 11 | *.csr 12 | *.key 13 | *.pub 14 | *.pem 15 | *.swp 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated 2 | 3 | This repository is deprecated. Please checkout the official [Terraform Module Registry](https://registry.terraform.io) instead. 4 | 5 | ## Best Practices Ops 6 | 7 | Below are the infrastructures we currently have best practices for. Navigate to each provider to see what will be provisioned. 8 | 9 | - [AWS](terraform/providers/aws/README.md) 10 | - [Google](terraform/providers/google/README.md) 11 | 12 | ### Getting Started 13 | 14 | This repository contains best-practice infrastructures across different cloud providers, regions, environments, and operating systems. 15 | 16 | You can think of this as a library of Packer templates and Terraform modules that allow you to provision unique infrastructures by referencing the different templates and modules. We've tried to set this repository up in a way that we don't have to duplicate code, allowing templates and modules to be used across multiple environments. 17 | 18 | Each environment is a best practices guide for how to use HashiCorp tooling to provision that specific type of infrastructure. Use each as a reference when building your own infrastructure. The best way to get started is to pick an environment that resembles an infrastructure you are looking to build, get it up and running, then configure and modify it to meet your specific needs. 19 | 20 | No example will be exactly what you need, but it should provide you with enough examples to get you headed in the right direction. 21 | 22 | A couple things to keep in mind... 23 | 24 | - Each environment's README will reference different sections in [General Setup](https://github.com/hashicorp/atlas-examples/blob/master/setup/general.md) to get your environment properly setup to build the infrastructure at hand. 25 | - Each environment will assume you're using Atlas. If you plan on doing anything locally, there are portions of environments that may not work due to the extra features Atlas provides that we are taking advantage of. 26 | - Each environment's instructional documentation is based off of the assumption that certain information will be saved as environment variables. If you do not wish to use environment variables, there are different ways to pass this information, but you may have to take extra undocumented steps to get commands to work properly. 27 | - Any `packer push` commands must be performed in the base [packer/.](packer) directory. 28 | - Any `terraform push` commands must be performed in the appropriate Terraform environment directory (e.g. [terraform/providers/aws/us\_east\_1\_staging](terraform/providers/aws/us_east_1_staging)). 29 | -------------------------------------------------------------------------------- /packer/aws/ubuntu/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", 4 | "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}", 5 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 6 | "us_east_1_ami": "ami-9a562df2", 7 | "name": "aws-ubuntu-base", 8 | "us_east_1_name": "aws-us-east-1-ubuntu-base", 9 | "ssh_username": "ubuntu", 10 | "scripts_dir": "scripts/ubuntu", 11 | "config_dir": "config", 12 | "dns_listen_addr": "127.0.0.1" 13 | }, 14 | "push": { 15 | "name": "{{user `atlas_username`}}/{{user `name`}}", 16 | "base_dir": "../../.", 17 | "include": [ 18 | "{{user `scripts_dir`}}/*", 19 | "{{user `scripts_dir`}}/upstart/*", 20 | "{{user `config_dir`}}/*", 21 | "{{user `config_dir`}}/consul/*", 22 | "{{user `config_dir`}}/consul_template/*", 23 | "{{user `config_dir`}}/consul_template/templates/*", 24 | "{{user `config_dir`}}/envconsul/*", 25 | "{{user `config_dir`}}/vault/*", 26 | "{{user `config_dir`}}/vault/policies/*" 27 | ], 28 | "vcs": false 29 | }, 30 | "builders": [ 31 | { 32 | "name": "aws-us-east-1-ubuntu-base", 33 | "type": "amazon-ebs", 34 | "access_key": "{{user `aws_access_key`}}", 35 | "secret_key": "{{user `aws_secret_key`}}", 36 | "region": "us-east-1", 37 | "vpc_id": "", 38 | "subnet_id": "", 39 | "source_ami": "{{user `us_east_1_ami`}}", 40 | "instance_type": "c3.large", 41 | "ssh_username": "{{user `ssh_username`}}", 42 | "ssh_timeout": "10m", 43 | "ami_name": "{{user `us_east_1_name`}} {{timestamp}}", 44 | "ami_description": "{{user `us_east_1_name`}} AMI", 45 | "run_tags": { "ami-create": "{{user `us_east_1_name`}}" }, 46 | "tags": { "ami": "{{user `us_east_1_name`}}" }, 47 | "ssh_private_ip": false, 48 | "associate_public_ip_address": true 49 | } 50 | ], 51 | "provisioners": [ 52 | { 53 | "type": "shell", 54 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 55 | "inline": [ 56 | "mkdir -p /ops/{{user `scripts_dir`}}", 57 | "chmod a+w /ops/{{user `scripts_dir`}}", 58 | "mkdir -p /ops/{{user `config_dir`}}", 59 | "chmod a+w /ops/{{user `config_dir`}}" 60 | ] 61 | }, 62 | { 63 | "type": "file", 64 | "source": "{{user `scripts_dir`}}/.", 65 | "destination": "/ops/{{user `scripts_dir`}}" 66 | }, 67 | { 68 | "type": "file", 69 | "source": "{{user `config_dir`}}/.", 70 | "destination": "/ops/{{user `config_dir`}}" 71 | }, 72 | { 73 | "type": "shell", 74 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 75 | "inline": [ 76 | "sh /ops/{{user `scripts_dir`}}/dependencies.sh", 77 | "sh /ops/{{user `scripts_dir`}}/consul.sh {{user `config_dir`}} {{user `scripts_dir`}}", 78 | "sh /ops/{{user `scripts_dir`}}/consul_template.sh {{user `config_dir`}} {{user `scripts_dir`}}", 79 | "sh /ops/{{user `scripts_dir`}}/envconsul.sh {{user `config_dir`}}", 80 | "sh /ops/{{user `scripts_dir`}}/dnsmasq.sh {{user `dns_listen_addr`}}", 81 | "sh /ops/{{user `scripts_dir`}}/cleanup.sh" 82 | ] 83 | } 84 | ], 85 | "post-processors": [ 86 | { 87 | "type": "atlas", 88 | "only": ["aws-us-east-1-ubuntu-base"], 89 | "artifact": "{{user `atlas_username`}}/{{user `us_east_1_name`}}", 90 | "artifact_type": "amazon.image", 91 | "metadata": { 92 | "created_at": "{{timestamp}}" 93 | } 94 | } 95 | ] 96 | } 97 | -------------------------------------------------------------------------------- /packer/aws/ubuntu/consul.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", 4 | "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}", 5 | "aws_region": "{{env `AWS_DEFAULT_REGION`}}", 6 | "base_ami": "{{env `ATLAS_BASE_ARTIFACT_AMAZON_IMAGE_ID`}}", 7 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 8 | "name": "aws-{{env `AWS_DEFAULT_REGION`}}-ubuntu-consul", 9 | "ssh_username": "ubuntu", 10 | "scripts_dir": "scripts/ubuntu", 11 | "config_dir": "config", 12 | "dns_listen_addr": "0.0.0.0" 13 | }, 14 | "push": { 15 | "name": "{{user `atlas_username`}}/{{user `name`}}", 16 | "base_dir": "../../.", 17 | "include": [ 18 | "{{user `scripts_dir`}}/*", 19 | "{{user `config_dir`}}/*", 20 | "{{user `config_dir`}}/consul/*" 21 | ], 22 | "vcs": false 23 | }, 24 | "builders": [ 25 | { 26 | "type": "amazon-ebs", 27 | "access_key": "{{user `aws_access_key`}}", 28 | "secret_key": "{{user `aws_secret_key`}}", 29 | "region": "{{user `aws_region`}}", 30 | "vpc_id": "", 31 | "subnet_id": "", 32 | "source_ami": "{{user `base_ami`}}", 33 | "instance_type": "c3.large", 34 | "ssh_username": "{{user `ssh_username`}}", 35 | "ssh_timeout": "10m", 36 | "ami_name": "{{user `name`}} {{timestamp}}", 37 | "ami_description": "{{user `name`}} AMI", 38 | "run_tags": { "ami-create": "{{user `name`}}" }, 39 | "tags": { "ami": "{{user `name`}}" }, 40 | "ssh_private_ip": false, 41 | "associate_public_ip_address": true 42 | } 43 | ], 44 | "provisioners": [ 45 | { 46 | "type": "shell", 47 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 48 | "inline": [ 49 | "mkdir -p /ops/{{user `scripts_dir`}}", 50 | "chmod a+w /ops/{{user `scripts_dir`}}", 51 | "mkdir -p /ops/{{user `config_dir`}}", 52 | "chmod a+w /ops/{{user `config_dir`}}" 53 | ] 54 | }, 55 | { 56 | "type": "file", 57 | "source": "{{user `scripts_dir`}}/.", 58 | "destination": "/ops/{{user `scripts_dir`}}" 59 | }, 60 | { 61 | "type": "file", 62 | "source": "{{user `config_dir`}}/.", 63 | "destination": "/ops/{{user `config_dir`}}" 64 | }, 65 | { 66 | "type": "shell", 67 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 68 | "inline": [ 69 | "cp /ops/{{user `config_dir`}}/consul/consul_server.json /etc/consul.d/base.json", 70 | "sh /ops/{{user `scripts_dir`}}/dnsmasq.sh {{user `dns_listen_addr`}}", 71 | "sh /ops/{{user `scripts_dir`}}/cleanup.sh" 72 | ] 73 | } 74 | ], 75 | "post-processors": [ 76 | { 77 | "type": "atlas", 78 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 79 | "artifact_type": "amazon.image", 80 | "metadata": { 81 | "created_at": "{{timestamp}}" 82 | } 83 | } 84 | ] 85 | } 86 | -------------------------------------------------------------------------------- /packer/aws/ubuntu/haproxy.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", 4 | "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}", 5 | "aws_region": "{{env `AWS_DEFAULT_REGION`}}", 6 | "base_ami": "{{env `ATLAS_BASE_ARTIFACT_AMAZON_IMAGE_ID`}}", 7 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 8 | "name": "aws-{{env `AWS_DEFAULT_REGION`}}-ubuntu-haproxy", 9 | "ssh_username": "ubuntu", 10 | "scripts_dir": "scripts/ubuntu", 11 | "config_dir": "config" 12 | }, 13 | "push": { 14 | "name": "{{user `atlas_username`}}/{{user `name`}}", 15 | "base_dir": "../../.", 16 | "include": [ 17 | "{{user `scripts_dir`}}/*", 18 | "{{user `scripts_dir`}}/upstart/*", 19 | "{{user `config_dir`}}/*", 20 | "{{user `config_dir`}}/consul/*", 21 | "{{user `config_dir`}}/consul_template/*", 22 | "{{user `config_dir`}}/consul_template/templates/*" 23 | ], 24 | "vcs": false 25 | }, 26 | "builders": [ 27 | { 28 | "type": "amazon-ebs", 29 | "access_key": "{{user `aws_access_key`}}", 30 | "secret_key": "{{user `aws_secret_key`}}", 31 | "region": "{{user `aws_region`}}", 32 | "vpc_id": "", 33 | "subnet_id": "", 34 | "source_ami": "{{user `base_ami`}}", 35 | "instance_type": "c3.large", 36 | "ssh_username": "{{user `ssh_username`}}", 37 | "ssh_timeout": "10m", 38 | "ami_name": "{{user `name`}} {{timestamp}}", 39 | "ami_description": "{{user `name`}} AMI", 40 | "run_tags": { "ami-create": "{{user `name`}}" }, 41 | "tags": { "ami": "{{user `name`}}" }, 42 | "ssh_private_ip": false, 43 | "associate_public_ip_address": true 44 | } 45 | ], 46 | "provisioners": [ 47 | { 48 | "type": "shell", 49 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 50 | "inline": [ 51 | "mkdir -p /ops/{{user `scripts_dir`}}", 52 | "chmod a+w /ops/{{user `scripts_dir`}}", 53 | "mkdir -p /ops/{{user `config_dir`}}", 54 | "chmod a+w /ops/{{user `config_dir`}}" 55 | ] 56 | }, 57 | { 58 | "type": "file", 59 | "source": "{{user `scripts_dir`}}/.", 60 | "destination": "/ops/{{user `scripts_dir`}}" 61 | }, 62 | { 63 | "type": "file", 64 | "source": "{{user `config_dir`}}/.", 65 | "destination": "/ops/{{user `config_dir`}}" 66 | }, 67 | { 68 | "type": "shell", 69 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 70 | "inline": [ 71 | "sh /ops/{{user `scripts_dir`}}/haproxy.sh {{user `config_dir`}} {{user `scripts_dir`}}", 72 | "sh /ops/{{user `scripts_dir`}}/cleanup.sh" 73 | ] 74 | } 75 | ], 76 | "post-processors": [ 77 | { 78 | "type": "atlas", 79 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 80 | "artifact_type": "amazon.image", 81 | "metadata": { 82 | "created_at": "{{timestamp}}" 83 | } 84 | } 85 | ] 86 | } 87 | -------------------------------------------------------------------------------- /packer/aws/ubuntu/nodejs.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", 4 | "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}", 5 | "aws_region": "{{env `AWS_DEFAULT_REGION`}}", 6 | "base_ami": "{{env `ATLAS_BASE_ARTIFACT_AMAZON_IMAGE_ID`}}", 7 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 8 | "name": "aws-{{env `AWS_DEFAULT_REGION`}}-ubuntu-nodejs", 9 | "ssh_username": "ubuntu", 10 | "scripts_dir": "scripts/ubuntu", 11 | "config_dir": "config", 12 | "host_app_dir": "/application" 13 | }, 14 | "push": { 15 | "name": "{{user `atlas_username`}}/{{user `name`}}", 16 | "base_dir": "../../.", 17 | "include": [ 18 | "{{user `scripts_dir`}}/*", 19 | "{{user `scripts_dir`}}/upstart/*", 20 | "{{user `scripts_dir`}}/nodejs_app/*", 21 | "{{user `config_dir`}}/*", 22 | "{{user `config_dir`}}/consul/*", 23 | "{{user `config_dir`}}/consul_template/*", 24 | "{{user `config_dir`}}/consul_template/templates/*", 25 | "{{user `config_dir`}}/envconsul/*", 26 | "{{user `config_dir`}}/vault/*", 27 | "{{user `config_dir`}}/vault/policies/*" 28 | ], 29 | "vcs": false 30 | }, 31 | "builders": [ 32 | { 33 | "type": "amazon-ebs", 34 | "access_key": "{{user `aws_access_key`}}", 35 | "secret_key": "{{user `aws_secret_key`}}", 36 | "region": "{{user `aws_region`}}", 37 | "vpc_id": "", 38 | "subnet_id": "", 39 | "source_ami": "{{user `base_ami`}}", 40 | "instance_type": "c3.large", 41 | "ssh_username": "{{user `ssh_username`}}", 42 | "ssh_timeout": "10m", 43 | "ami_name": "{{user `name`}} {{timestamp}}", 44 | "ami_description": "{{user `name`}} AMI", 45 | "run_tags": { "ami-create": "{{user `name`}}" }, 46 | "tags": { "ami": "{{user `name`}}" }, 47 | "ssh_private_ip": false, 48 | "associate_public_ip_address": true 49 | } 50 | ], 51 | "provisioners": [ 52 | { 53 | "type": "shell", 54 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 55 | "inline": [ 56 | "mkdir -p /ops/{{user `scripts_dir`}}", 57 | "chmod a+w /ops/{{user `scripts_dir`}}", 58 | "mkdir -p /ops/{{user `config_dir`}}", 59 | "chmod a+w /ops/{{user `config_dir`}}" 60 | ] 61 | }, 62 | { 63 | "type": "file", 64 | "source": "{{user `scripts_dir`}}/.", 65 | "destination": "/ops/{{user `scripts_dir`}}" 66 | }, 67 | { 68 | "type": "file", 69 | "source": "{{user `config_dir`}}/.", 70 | "destination": "/ops/{{user `config_dir`}}" 71 | }, 72 | { 73 | "type": "shell", 74 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 75 | "inline": [ 76 | "mkdir -p {{user `host_app_dir`}}", 77 | "chmod a+w {{user `host_app_dir`}}" 78 | ] 79 | }, 80 | { 81 | "type": "file", 82 | "source": "{{user `scripts_dir`}}/nodejs_app/", 83 | "destination": "{{user `host_app_dir`}}" 84 | }, 85 | { 86 | "type": "shell", 87 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 88 | "inline": [ 89 | "sh /ops/{{user `scripts_dir`}}/nodejs.sh {{user `config_dir`}} {{user `scripts_dir`}}", 90 | "sh /ops/{{user `scripts_dir`}}/cleanup.sh" 91 | ] 92 | } 93 | ], 94 | "post-processors": [ 95 | { 96 | "type": "atlas", 97 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 98 | "artifact_type": "amazon.image", 99 | "metadata": { 100 | "created_at": "{{timestamp}}" 101 | } 102 | } 103 | ] 104 | } 105 | -------------------------------------------------------------------------------- /packer/aws/ubuntu/vault.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", 4 | "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}", 5 | "aws_region": "{{env `AWS_DEFAULT_REGION`}}", 6 | "base_ami": "{{env `ATLAS_BASE_ARTIFACT_AMAZON_IMAGE_ID`}}", 7 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 8 | "name": "aws-{{env `AWS_DEFAULT_REGION`}}-ubuntu-vault", 9 | "ssh_username": "ubuntu", 10 | "scripts_dir": "scripts/ubuntu", 11 | "config_dir": "config" 12 | }, 13 | "push": { 14 | "name": "{{user `atlas_username`}}/{{user `name`}}", 15 | "base_dir": "../../.", 16 | "include": [ 17 | "{{user `scripts_dir`}}/*", 18 | "{{user `scripts_dir`}}/upstart/*", 19 | "{{user `config_dir`}}/*", 20 | "{{user `config_dir`}}/consul/*", 21 | "{{user `config_dir`}}/vault/*", 22 | "{{user `config_dir`}}/vault/policies/*", 23 | "{{user `config_dir`}}/vault/scripts/*" 24 | ], 25 | "vcs": false 26 | }, 27 | "builders": [ 28 | { 29 | "type": "amazon-ebs", 30 | "access_key": "{{user `aws_access_key`}}", 31 | "secret_key": "{{user `aws_secret_key`}}", 32 | "region": "{{user `aws_region`}}", 33 | "vpc_id": "", 34 | "subnet_id": "", 35 | "source_ami": "{{user `base_ami`}}", 36 | "instance_type": "c3.large", 37 | "ssh_username": "{{user `ssh_username`}}", 38 | "ssh_timeout": "10m", 39 | "ami_name": "{{user `name`}} {{timestamp}}", 40 | "ami_description": "{{user `name`}} AMI", 41 | "run_tags": { "ami-create": "{{user `name`}}" }, 42 | "tags": { "ami": "{{user `name`}}" }, 43 | "ssh_private_ip": false, 44 | "associate_public_ip_address": true 45 | } 46 | ], 47 | "provisioners": [ 48 | { 49 | "type": "shell", 50 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 51 | "inline": [ 52 | "mkdir -p /ops/{{user `scripts_dir`}}", 53 | "chmod a+w /ops/{{user `scripts_dir`}}", 54 | "mkdir -p /ops/{{user `config_dir`}}", 55 | "chmod a+w /ops/{{user `config_dir`}}" 56 | ] 57 | }, 58 | { 59 | "type": "file", 60 | "source": "{{user `scripts_dir`}}/.", 61 | "destination": "/ops/{{user `scripts_dir`}}" 62 | }, 63 | { 64 | "type": "file", 65 | "source": "{{user `config_dir`}}/.", 66 | "destination": "/ops/{{user `config_dir`}}" 67 | }, 68 | { 69 | "type": "shell", 70 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 71 | "inline": [ 72 | "sh /ops/{{user `scripts_dir`}}/vault.sh {{user `config_dir`}} {{user `scripts_dir`}}", 73 | "sh /ops/{{user `scripts_dir`}}/cleanup.sh" 74 | ] 75 | } 76 | ], 77 | "post-processors": [ 78 | { 79 | "type": "atlas", 80 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 81 | "artifact_type": "amazon.image", 82 | "metadata": { 83 | "created_at": "{{timestamp}}" 84 | } 85 | } 86 | ] 87 | } 88 | -------------------------------------------------------------------------------- /packer/aws/windows/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "{{env `AWS_ACCESS_KEY`}}", 4 | "aws_secret_key": "{{env `AWS_SECRET_KEY`}}", 5 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 6 | "name": "aws-windows-base", 7 | "region": "us-east-1", 8 | "vpc_id": "", 9 | "subnet_id": "", 10 | "source_ami": "ami-cd9339a6", 11 | "instance_type": "t2.micro", 12 | "winrm_username": "Administrator", 13 | "user_data_file": "packer/scripts/windows/config/ec2_user_data.conf", 14 | "scripts_dir": "packer/scripts/windows", 15 | "consul_config_dir": "C:\\etc\\consul.d" 16 | }, 17 | "push": { 18 | "name": "{{user `atlas_username`}}/{{user `name`}}", 19 | "base_dir": "../../../.", 20 | "include": [ 21 | "{{user `user_data_file`}}", 22 | "{{user `scripts_dir`}}/*" 23 | ], 24 | "vcs": false 25 | }, 26 | "builders": [ 27 | { 28 | "type": "amazon-ebs", 29 | "access_key": "{{user `aws_access_key`}}", 30 | "secret_key": "{{user `aws_secret_key`}}", 31 | "region": "{{user `region`}}", 32 | "vpc_id": "{{user `vpc_id`}}", 33 | "subnet_id": "{{user `subnet_id`}}", 34 | "source_ami": "{{user `source_ami`}}", 35 | "instance_type": "{{user `instance_type`}}", 36 | "communicator": "winrm", 37 | "winrm_username": "{{user `winrm_username`}}", 38 | "winrm_timeout": "60m", 39 | "user_data_file": "{{user `user_data_file`}}", 40 | "ami_name": "{{user `name`}} {{timestamp}}", 41 | "ami_description": "{{user `name`}} AMI", 42 | "run_tags": { "ami-create": "{{user `name`}}" }, 43 | "tags": { "ami": "{{user `name`}}" }, 44 | "associate_public_ip_address": true 45 | } 46 | ], 47 | "provisioners": [ 48 | { 49 | "type": "powershell", 50 | "scripts": [ 51 | "{{user `scripts_dir`}}/install_web_server.ps1", 52 | "{{user `scripts_dir`}}/install_windows_updates.ps1" 53 | ] 54 | }, 55 | { 56 | "type": "windows-restart", 57 | "restart_command": "powershell \"& {(Get-WmiObject win32_operatingsystem).LastBootUpTime > C:\\ProgramData\\lastboot.txt; Restart-Computer -force}\"", 58 | "restart_check_command": "powershell -command \"& {if ((get-content C:\\ProgramData\\lastboot.txt) -eq (Get-WmiObject win32_operatingsystem).LastBootUpTime) {Write-Output 'Waiting for restart'; start-sleep 600} else {Write-Output 'Restart complete'}}\"" 59 | }, 60 | { 61 | "type": "powershell", 62 | "scripts": [ 63 | "{{user `scripts_dir`}}/install_windows_updates.ps1" 64 | ] 65 | }, 66 | { 67 | "type": "windows-restart", 68 | "restart_command": "powershell \"& {(Get-WmiObject win32_operatingsystem).LastBootUpTime > C:\\ProgramData\\lastboot.txt; Restart-Computer -force}\"", 69 | "restart_check_command": "powershell -command \"& {if ((get-content C:\\ProgramData\\lastboot.txt) -eq (Get-WmiObject win32_operatingsystem).LastBootUpTime) {Write-Output 'Waiting for restart'; start-sleep 600} else {Write-Output 'Restart complete'}}\"" 70 | }, 71 | { 72 | "type": "powershell", 73 | "scripts": [ 74 | "{{user `scripts_dir`}}/set_ec2_config.ps1" 75 | ] 76 | }, 77 | { 78 | "type": "powershell", 79 | "inline": [ 80 | "New-Item -Path {{user `consul_config_dir`}} -ItemType Directory -Force" 81 | ] 82 | }, 83 | { 84 | "type": "file", 85 | "source": "{{user `scripts_dir`}}/consul_client.json", 86 | "destination": "{{user `consul_config_dir`}}\\config.json" 87 | }, 88 | { 89 | "type": "powershell", 90 | "scripts": [ 91 | "{{user `scripts_dir`}}/install_consul.ps1" 92 | ] 93 | } 94 | ], 95 | "post-processors": [ 96 | { 97 | "type": "atlas", 98 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 99 | "artifact_type": "amazon.ami", 100 | "metadata": { 101 | "created_at": "{{timestamp}}" 102 | } 103 | } 104 | ] 105 | } 106 | -------------------------------------------------------------------------------- /packer/aws/windows/web.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "{{env `AWS_ACCESS_KEY`}}", 4 | "aws_secret_key": "{{env `AWS_SECRET_KEY`}}", 5 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 6 | "base_ami": "{{env `ATLAS_BASE_ARTIFACT_AMAZON_AMI_ID`}}", 7 | "name": "aws-windows-web", 8 | "region": "us-east-1", 9 | "vpc_id": "", 10 | "subnet_id": "", 11 | "instance_type": "t2.micro", 12 | "winrm_username": "Administrator", 13 | "user_data_file": "packer/scripts/windows/config/ec2_user_data.conf", 14 | "scripts_dir": "packer/scripts/windows", 15 | "slug_app_dir": "app/" 16 | }, 17 | "push": { 18 | "name": "{{user `atlas_username`}}/{{user `name`}}", 19 | "base_dir": "../../../.", 20 | "include": [ 21 | "{{user `scripts_dir`}}/*" 22 | ], 23 | "vcs": false 24 | }, 25 | "builders": [ 26 | { 27 | "type": "amazon-ebs", 28 | "access_key": "{{user `aws_access_key`}}", 29 | "secret_key": "{{user `aws_secret_key`}}", 30 | "region": "{{user `region`}}", 31 | "vpc_id": "{{user `vpc_id`}}", 32 | "subnet_id": "{{user `subnet_id`}}", 33 | "source_ami": "{{user `base_ami`}}", 34 | "instance_type": "{{user `instance_type`}}", 35 | "communicator": "winrm", 36 | "winrm_username": "{{user `winrm_username`}}", 37 | "winrm_timeout": "30m", 38 | "user_data_file": "{{user `user_data_file`}}", 39 | "ami_name": "{{user `name`}} {{timestamp}}", 40 | "ami_description": "{{user `name`}} AMI", 41 | "run_tags": { "ami-create": "{{user `name`}}" }, 42 | "tags": { "ami": "{{user `name`}}" }, 43 | "associate_public_ip_address": true 44 | } 45 | ], 46 | "provisioners": [ 47 | { 48 | "type": "powershell", 49 | "scripts": [ 50 | "{{user `scripts_dir`}}/set_ec2_config.ps1" 51 | ] 52 | }, 53 | { 54 | "type": "powershell", 55 | "inline": [ 56 | "New-Item C:\\{{user `name`}} -type Directory" 57 | ] 58 | }, 59 | { 60 | "type": "file", 61 | "source": "{{user `slug_app_dir`}}", 62 | "destination": "C:\\{{user `name`}}" 63 | }, 64 | { 65 | "type": "powershell", 66 | "inline": [ 67 | "Import-Module WebAdministration", 68 | "Clear-ItemProperty \"IIS:\\Sites\\Default Web Site\" -Name bindings", 69 | "Remove-Website -Name \"Default Web Site\"", 70 | "New-Item \"IIS:\\AppPools\\{{user `name`}} AppPool\"", 71 | "New-Item IIS:\\Sites\\{{user `name`}} -physicalPath C:\\{{user `name`}} -bindings @{protocol=\"http\";bindingInformation=\":80:\"}", 72 | "Set-ItemProperty IIS:\\Sites\\{{user `name`}} -name applicationPool -value \"{{user `name`}} AppPool\"" 73 | ] 74 | } 75 | ], 76 | "post-processors": [ 77 | { 78 | "type": "atlas", 79 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 80 | "artifact_type": "amazon.ami", 81 | "metadata": { 82 | "created_at": "{{timestamp}}" 83 | } 84 | } 85 | ] 86 | } 87 | -------------------------------------------------------------------------------- /packer/config/consul/consul_client.json: -------------------------------------------------------------------------------- 1 | { 2 | "log_level": "INFO", 3 | "data_dir": "/opt/consul/data", 4 | "ui_dir": "/opt/consul/ui", 5 | "client_addr": "0.0.0.0", 6 | "bind_addr": "0.0.0.0", 7 | "atlas_join": true, 8 | "atlas_infrastructure": "{{ atlas_username }}/{{ atlas_environment }}", 9 | "atlas_token": "{{ atlas_token }}", 10 | "datacenter": "{{ datacenter }}", 11 | "node_name": "{{ node_name }}", 12 | "skip_leave_on_interrupt": true, 13 | "leave_on_terminate": true 14 | } 15 | -------------------------------------------------------------------------------- /packer/config/consul/consul_server.json: -------------------------------------------------------------------------------- 1 | { 2 | "log_level": "INFO", 3 | "server": true, 4 | "data_dir": "/opt/consul/data", 5 | "ui_dir": "/opt/consul/ui", 6 | "client_addr": "0.0.0.0", 7 | "bind_addr": "0.0.0.0", 8 | "atlas_join": true, 9 | "atlas_infrastructure": "{{ atlas_username }}/{{ atlas_environment }}", 10 | "atlas_token": "{{ atlas_token }}", 11 | "bootstrap_expect": {{ consul_server_count }}, 12 | "datacenter": "{{ datacenter }}", 13 | "node_name": "{{ node_name }}", 14 | "skip_leave_on_interrupt": true, 15 | "leave_on_terminate": true, 16 | "service": { 17 | "name": "consul", 18 | "tags": ["{{ node_name }}"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packer/config/consul/haproxy.json: -------------------------------------------------------------------------------- 1 | { 2 | "service": { 3 | "name": "haproxy" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packer/config/consul/nodejs.json: -------------------------------------------------------------------------------- 1 | { 2 | "recursors": ["localhost:8600"], 3 | "service": { 4 | "name": "web", 5 | "port": 8888, 6 | "tags": ["nodejs", "{{ deploy }}"] 7 | }, 8 | "check": { 9 | "id": "nodejs", 10 | "service_id": "web", 11 | "name": "Running on port 8888", 12 | "http": "http://localhost:8888", 13 | "interval": "10s", 14 | "timeout": "1s" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packer/config/consul/vault.json: -------------------------------------------------------------------------------- 1 | { 2 | "service": { 3 | "name": "vault", 4 | "port": 8200, 5 | "tags": ["{{ node_name }}"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packer/config/consul_template/base.hcl: -------------------------------------------------------------------------------- 1 | consul = "127.0.0.1:8500" 2 | max_stale = "10m" 3 | retry = "5s" 4 | log_level = "warn" 5 | -------------------------------------------------------------------------------- /packer/config/consul_template/haproxy.hcl: -------------------------------------------------------------------------------- 1 | template { 2 | source = "/opt/consul_template/haproxy.ctmpl" 3 | destination = "/etc/haproxy/haproxy.cfg" 4 | command = "service haproxy restart" 5 | } 6 | -------------------------------------------------------------------------------- /packer/config/consul_template/nodejs.hcl: -------------------------------------------------------------------------------- 1 | vault { 2 | address = "https://vault.service.consul:8200" 3 | token = "{{ vault_token }}" 4 | renew = true 5 | 6 | ssl { 7 | enabled = true 8 | verify = true 9 | ca_cert = "{{ cert_path }}" 10 | } 11 | } 12 | 13 | template { 14 | source = "/opt/consul_template/vault_generic.ctmpl" 15 | destination = "/application/vault/generic.html" 16 | command = "service nodejs restart" 17 | } 18 | -------------------------------------------------------------------------------- /packer/config/consul_template/nodejs_aws.hcl: -------------------------------------------------------------------------------- 1 | template { 2 | source = "/opt/consul_template/vault_aws.ctmpl" 3 | destination = "/application/vault/aws.html" 4 | command = "service nodejs restart" 5 | } 6 | -------------------------------------------------------------------------------- /packer/config/consul_template/templates/haproxy.ctmpl: -------------------------------------------------------------------------------- 1 | global 2 | maxconn 4 3 | log 127.0.0.1 local0 notice 4 | user haproxy 5 | group haproxy 6 | 7 | defaults 8 | log global 9 | retries 2 10 | timeout connect 3000 11 | timeout server 5000 12 | timeout client 5000 13 | 14 | listen localnodes 15 | bind *:80 16 | mode http 17 | default_backend webs 18 | 19 | backend webs 20 | balance roundrobin 21 | mode http{{range service "nodejs.web"}} 22 | server {{.Node}} {{.Address}}:{{.Port}}{{end}} 23 | 24 | listen stats *:1936 25 | mode http 26 | stats enable 27 | stats uri /haproxy?stats 28 | stats hide-version 29 | -------------------------------------------------------------------------------- /packer/config/consul_template/templates/vault_aws.ctmpl: -------------------------------------------------------------------------------- 1 | {{with vault "{{ cred_path }}"}} 2 |

3 | Consul Template is setup with the Vault AWS backend! 4 |

5 | The dynamically generated AWS IAM credentials for {{ node_name }} are... 6 |

7 | Access Key: {{.Data.access_key}}
8 | Secret Key: {{.Data.secret_key}} 9 | {{end}} 10 | -------------------------------------------------------------------------------- /packer/config/consul_template/templates/vault_generic.ctmpl: -------------------------------------------------------------------------------- 1 | {{with vault "{{ secret_path }}"}} 2 |

3 | Consul Template is setup with the Vault Generic Secret backend! 4 |

5 | The Vault secret for {{ node_name }} is: "{{.Data.{{ secret_key }}}}" 6 | {{end}} 7 | -------------------------------------------------------------------------------- /packer/config/envconsul/base.hcl: -------------------------------------------------------------------------------- 1 | consul = "127.0.0.1:8500" 2 | max_stale = "10m" 3 | timeout = "5s" 4 | retry = "5s" 5 | splay = "5s" 6 | sanitize = true 7 | upcase = true 8 | log_level = "warn" 9 | -------------------------------------------------------------------------------- /packer/config/envconsul/nodejs.hcl: -------------------------------------------------------------------------------- 1 | prefix { 2 | path = "service/nodejs" 3 | } 4 | -------------------------------------------------------------------------------- /packer/config/vault/policies/aws_nodejs.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": { 4 | "Effect": "Allow", 5 | "Action": "iam:*", 6 | "Resource": "*" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packer/config/vault/policies/nodejs.hcl: -------------------------------------------------------------------------------- 1 | # Allow renewal of leases for secrets 2 | path "sys/renew/*" { 3 | policy = "write" 4 | } 5 | 6 | # Allow renewal of token leases 7 | path "auth/token/renew/*" { 8 | policy = "write" 9 | } 10 | 11 | # Transit backend 12 | path "transit/encrypt/nodejs_*" { 13 | policy = "write" 14 | } 15 | 16 | path "transit/decrypt/nodejs_*" { 17 | policy = "write" 18 | } 19 | 20 | # Secrets backend 21 | path "secret/nodejs/*" { 22 | policy = "read" 23 | } 24 | 25 | # AWS backend 26 | path "aws/creds/*" { 27 | policy = "read" 28 | } 29 | -------------------------------------------------------------------------------- /packer/config/vault/policies/nodejs.json: -------------------------------------------------------------------------------- 1 | { 2 | "path": { 3 | "sys/renew/*": { 4 | "policy": "write" 5 | }, 6 | "auth/token/renew/*": { 7 | "policy": "write" 8 | }, 9 | "transit/encrypt/nodejs_*": { 10 | "policy": "write" 11 | }, 12 | "transit/decrypt/nodejs_*": { 13 | "policy": "write" 14 | }, 15 | "secret/nodejs/*": { 16 | "policy": "read" 17 | }, 18 | "aws/creds/*": { 19 | "policy": "read" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packer/config/vault/scripts/setup_aws.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | usage() { 5 | cat < 17 | 18 | Where ACCESS_KEY is your Vault AWS Access Key ID, SECRET_KEY is your Vault AWS Secret Access Key, and REGION is the AWS region for API calls. 19 | EOF 20 | 21 | exit 1 22 | } 23 | 24 | if ! which vault > /dev/null; then 25 | echo 26 | echo "ERROR: The vault executable was not found. This script requires vault" 27 | echo 28 | usage 29 | fi 30 | 31 | ACCESSKEY=$1 32 | 33 | if [ -z "${ACCESSKEY}" ]; then 34 | echo 35 | echo "ERROR: Specify the Vault AWS Access Key ID as the first argument" 36 | echo 37 | usage 38 | fi 39 | 40 | SECRETKEY=$2 41 | 42 | if [ -z "${SECRETKEY}" ]; then 43 | echo 44 | echo "ERROR: Specify the Vault AWS Secret Access Key as the second argument" 45 | echo 46 | usage 47 | fi 48 | 49 | REGION=$3 50 | 51 | if [ -z "${REGION}" ]; then 52 | echo 53 | echo "ERROR: Specify the AWS region as the third argument, e.g. us-east-1" 54 | echo 55 | usage 56 | fi 57 | 58 | if vault status | grep standby > /dev/null; then 59 | echo "Mounts only run on the leader. Exiting." 60 | exit 0 61 | fi 62 | 63 | echo "Authenticating as root..." 64 | 65 | cget() { curl -sf "http://127.0.0.1:8500/v1/kv/service/vault/$1?raw"; } 66 | cget root-token | vault auth - 67 | 68 | if vault mounts | grep aws > /dev/null; then 69 | echo "AWS backend already mounted." 70 | else 71 | echo "Mounting AWS backend..." 72 | vault mount aws 73 | fi 74 | 75 | echo "Writing root AWS IAM credentials..." 76 | 77 | vault write aws/config/root \ 78 | access_key=$ACCESSKEY \ 79 | secret_key=$SECRETKEY \ 80 | region=$REGION 81 | 82 | echo "Writing lease settings for generated credentials..." 83 | 84 | vault write aws/config/lease \ 85 | lease="1m" \ 86 | lease_max="2m" 87 | 88 | shred -u -z ~/.vault-token 89 | -------------------------------------------------------------------------------- /packer/config/vault/scripts/setup_transit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | usage() { 5 | cat < /dev/null; then 19 | echo 20 | echo "ERROR: The vault executable was not found. This script requires vault" 21 | echo 22 | usage 23 | fi 24 | 25 | if vault status | grep standby > /dev/null; then 26 | echo "Mounts only run on the leader. Exiting." 27 | exit 0 28 | fi 29 | 30 | echo "Authenticating as root..." 31 | 32 | cget() { curl -sf "http://127.0.0.1:8500/v1/kv/service/vault/$1?raw"; } 33 | cget root-token | vault auth - 34 | 35 | if vault mounts | grep transit > /dev/null; then 36 | echo "Transit backend already mounted." 37 | else 38 | echo "Mounting Transit backend." 39 | vault mount transit 40 | fi 41 | 42 | shred -u -z ~/.vault-token 43 | -------------------------------------------------------------------------------- /packer/config/vault/scripts/setup_vault.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cr=`echo '\n.'` 5 | cr=${cr%.} 6 | read -p "Running this script will initialize & unseal Vault,${cr}then put your unseal keys and root token into Consul KV.${cr}${cr}If you're sure you want to continue, type 'yes': `echo '\n> '`" ANSWER 7 | 8 | if [ "$ANSWER" != "yes" ]; then 9 | echo 10 | echo "Exiting without intializing & unsealing Vault, no keys or tokens were stored." 11 | echo 12 | exit 1 13 | fi 14 | 15 | cget() { curl -sf "http://127.0.0.1:8500/v1/kv/service/vault/$1?raw"; } 16 | 17 | if [ ! $(cget root-token) ]; then 18 | echo "Initialize Vault" 19 | vault init | tee /tmp/vault.init > /dev/null 20 | 21 | # Store master keys in consul for operator to retrieve and remove 22 | COUNTER=1 23 | cat /tmp/vault.init | grep '^Unseal' | awk '{print $4}' | for key in $(cat -); do 24 | curl -fX PUT 127.0.0.1:8500/v1/kv/service/vault/unseal-key-$COUNTER -d $key 25 | COUNTER=$((COUNTER + 1)) 26 | done 27 | 28 | export ROOT_TOKEN=$(cat /tmp/vault.init | grep '^Initial' | awk '{print $4}') 29 | curl -fX PUT 127.0.0.1:8500/v1/kv/service/vault/root-token -d $ROOT_TOKEN 30 | 31 | echo "Remove master keys from disk" 32 | shred /tmp/vault.init 33 | 34 | echo "Setup Vault demo" 35 | curl -fX PUT 127.0.0.1:8500/v1/kv/service/nodejs/show_vault -d "true" 36 | curl -fX PUT 127.0.0.1:8500/v1/kv/service/nodejs/vault_files -d "aws.html,generic.html" 37 | else 38 | echo "Vault has already been initialized, skipping." 39 | fi 40 | 41 | echo "Unsealing Vault" 42 | vault unseal $(cget unseal-key-1) 43 | vault unseal $(cget unseal-key-2) 44 | vault unseal $(cget unseal-key-3) 45 | 46 | echo "Vault setup complete." 47 | 48 | instructions() { 49 | cat < 17 | 18 | Where POLICY_NAME is the name of the policy you wish to create, and POLICY_PATH is the path to the policy. 19 | EOF 20 | 21 | exit 1 22 | } 23 | 24 | if ! which vault > /dev/null; then 25 | echo 26 | echo "ERROR: The vault executable was not found. This script requires vault" 27 | echo 28 | usage 29 | fi 30 | 31 | POLICYNAME=$1 32 | 33 | if [ -z "${POLICYNAME}" ]; then 34 | echo 35 | echo "ERROR: Specify the name of your policy as the first argument, e.g. nodejs" 36 | echo 37 | usage 38 | fi 39 | 40 | POLICYPATH=$2 41 | 42 | if [ -z "${POLICYPATH}" ]; then 43 | echo 44 | echo "ERROR: Specify the path to your policy as the second argument, e.g. /opt/vault/policies/nodejs.hcl" 45 | echo 46 | usage 47 | fi 48 | 49 | echo "Authenticating as root..." 50 | 51 | cget() { curl -sf "http://127.0.0.1:8500/v1/kv/service/vault/$1?raw"; } 52 | cget root-token | vault auth - 53 | 54 | echo "Writing Vault $POLICYNAME policy..." 55 | 56 | vault policy-write "${POLICYNAME}" $POLICYPATH 57 | 58 | shred -u -z ~/.vault-token 59 | -------------------------------------------------------------------------------- /packer/config/vault/scripts/write_role.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | usage() { 5 | cat < 17 | 18 | Where ROLE_NAME is the name of the role you wish to create, and POLICY_PATH is the path to the policy for this role. 19 | EOF 20 | 21 | exit 1 22 | } 23 | 24 | if ! which vault > /dev/null; then 25 | echo 26 | echo "ERROR: The vault executable was not found. This script requires vault" 27 | echo 28 | usage 29 | fi 30 | 31 | ROLENAME=$1 32 | 33 | if [ -z "${ROLENAME}" ]; then 34 | echo 35 | echo "ERROR: Specify the name of your role as the first argument, e.g. nodejs/my_app" 36 | echo 37 | usage 38 | fi 39 | 40 | POLICYPATH=$2 41 | 42 | if [ -z "${POLICYPATH}" ]; then 43 | echo 44 | echo "ERROR: Specify the path to your policy as the second argument, e.g. /opt/vault/policies/aws_nodejs.json" 45 | echo 46 | usage 47 | fi 48 | 49 | echo "Authenticating as root..." 50 | 51 | cget() { curl -sf "http://127.0.0.1:8500/v1/kv/service/vault/$1?raw"; } 52 | cget root-token | vault auth - 53 | 54 | echo "Writing Vault $ROLENAME role..." 55 | 56 | vault write aws/roles/$ROLENAME policy=@$POLICYPATH 57 | 58 | shred -u -z ~/.vault-token 59 | -------------------------------------------------------------------------------- /packer/config/vault/vault.hcl: -------------------------------------------------------------------------------- 1 | backend "consul" { 2 | path = "vault" 3 | address = "127.0.0.1:8500" 4 | advertise_addr = "https://{{ node_name }}.node.consul:8200" 5 | } 6 | 7 | listener "atlas" { 8 | infrastructure = "{{ atlas_username }}/{{ atlas_environment }}" 9 | token = "{{ atlas_token }}" 10 | node_id = "{{ node_name }}" 11 | tls_cert_file = "{{ tls_cert_file }}" 12 | tls_key_file = "{{ tls_key_file }}" 13 | } 14 | 15 | cluster_name = "{{ datacenter }}" 16 | 17 | listener "tcp" { 18 | address = "0.0.0.0:8200" 19 | tls_cert_file = "{{ tls_cert_file }}" 20 | tls_key_file = "{{ tls_key_file }}" 21 | } 22 | -------------------------------------------------------------------------------- /packer/google/ubuntu/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 4 | "gce_credentials": "{{env `GCE_CREDENTIALS`}}", 5 | "gce_project_id": "{{env `GCE_PROJECT_ID`}}", 6 | "gce_zone": "{{env `GCE_DEFAULT_ZONE`}}", 7 | "gce_source_image": "{{env `GCE_SOURCE_IMAGE`}}", 8 | "name": "google-ubuntu-base", 9 | "scripts_dir": "scripts", 10 | "config_dir": "config", 11 | "ssh_username": "ubuntu", 12 | "dns_listen_addr": "127.0.0.1" 13 | }, 14 | "push": { 15 | "name": "{{user `atlas_username`}}/{{user `name`}}", 16 | "base_dir": "../../../packer", 17 | "include": [ 18 | "{{user `scripts_dir`}}/*", 19 | "{{user `scripts_dir`}}/ubuntu/*", 20 | "{{user `scripts_dir`}}/ubuntu/upstart/*", 21 | "{{user `config_dir`}}/*", 22 | "{{user `config_dir`}}/consul/*", 23 | "{{user `config_dir`}}/consul_template/*", 24 | "{{user `config_dir`}}/consul_template/templates/*", 25 | "{{user `config_dir`}}/envconsul/*", 26 | "{{user `config_dir`}}/vault/*", 27 | "{{user `config_dir`}}/vault/policies/*", 28 | "{{user `config_dir`}}/vault/scripts/*" 29 | ], 30 | "vcs": false 31 | }, 32 | "builders": [ 33 | { 34 | "type": "googlecompute", 35 | "project_id": "{{user `gce_project_id`}}", 36 | "account_file": "{{user `gce_credentials`}}", 37 | "zone": "{{user `gce_zone`}}", 38 | "network": "default", 39 | "source_image": "{{user `gce_source_image`}}", 40 | "ssh_username": "{{user `ssh_username`}}", 41 | "image_name": "packer-{{user `name`}}-{{timestamp}}", 42 | "image_description": "packer-{{user `name`}}-image", 43 | "use_internal_ip": false, 44 | "tags": [ 45 | "{{user `name`}}" 46 | ] 47 | } 48 | ], 49 | "provisioners": [ 50 | { 51 | "type": "shell", 52 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 53 | "inline": [ 54 | "mkdir -p /ops/{{user `scripts_dir`}}", 55 | "chmod a+w /ops/{{user `scripts_dir`}}", 56 | "mkdir -p /ops/{{user `config_dir`}}", 57 | "chmod a+w /ops/{{user `config_dir`}}" 58 | ] 59 | }, 60 | { 61 | "type": "file", 62 | "source": "{{user `scripts_dir`}}/", 63 | "destination": "/ops/{{user `scripts_dir`}}" 64 | }, 65 | { 66 | "type": "file", 67 | "source": "{{user `config_dir`}}/", 68 | "destination": "/ops/{{user `config_dir`}}" 69 | }, 70 | { 71 | "type": "shell", 72 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 73 | "inline": [ 74 | "sh /ops/{{user `scripts_dir`}}/ubuntu/dependencies.sh", 75 | "sh /ops/{{user `scripts_dir`}}/ubuntu/consul.sh {{user `config_dir`}} {{user `scripts_dir`}}/ubuntu", 76 | "sh /ops/{{user `scripts_dir`}}/ubuntu/consul_template.sh {{user `config_dir`}} {{user `scripts_dir`}}/ubuntu", 77 | "sh /ops/{{user `scripts_dir`}}/ubuntu/envconsul.sh {{user `config_dir`}}", 78 | "sh /ops/{{user `scripts_dir`}}/ubuntu/dnsmasq.sh {{user `dns_listen_addr`}}", 79 | "sh /ops/{{user `scripts_dir`}}/ubuntu/cleanup.sh" 80 | ] 81 | } 82 | ], 83 | "post-processors": [ 84 | { 85 | "type": "atlas", 86 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 87 | "artifact_type": "google.image", 88 | "metadata": { 89 | "created_at": "{{timestamp}}", 90 | "zone": "{{user `gce_zone`}}" 91 | } 92 | } 93 | ] 94 | } 95 | -------------------------------------------------------------------------------- /packer/google/ubuntu/consul.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 4 | "gce_project_id": "{{env `GCE_PROJECT_ID`}}", 5 | "gce_zone": "{{env `GCE_DEFAULT_ZONE`}}", 6 | "gce_source_image": "{{env `ATLAS_BASE_ARTIFACT_GOOGLE_IMAGE_ID`}}", 7 | "gce_credentials": "{{env `GCE_CREDENTIALS`}}", 8 | "name": "google-ubuntu-consul", 9 | "scripts_dir": "scripts", 10 | "config_dir": "config", 11 | "ssh_username": "ubuntu", 12 | "dns_listen_addr": "127.0.0.1" 13 | }, 14 | "push": { 15 | "name": "{{user `atlas_username`}}/{{user `name`}}", 16 | "base_dir": "../../../packer", 17 | "include": [ 18 | "{{user `scripts_dir`}}/*", 19 | "{{user `scripts_dir`}}/ubuntu/*", 20 | "{{user `scripts_dir`}}/ubuntu/upstart/*", 21 | "{{user `config_dir`}}/*", 22 | "{{user `config_dir`}}/consul/*", 23 | "{{user `config_dir`}}/consul_template/*", 24 | "{{user `config_dir`}}/consul_template/templates/*", 25 | "{{user `config_dir`}}/envconsul/*", 26 | "{{user `config_dir`}}/vault/*", 27 | "{{user `config_dir`}}/vault/policies/*", 28 | "{{user `config_dir`}}/vault/scripts/*" 29 | ], 30 | "vcs": false 31 | }, 32 | "builders": [ 33 | { 34 | "type": "googlecompute", 35 | "project_id": "{{user `gce_project_id`}}", 36 | "account_file": "{{user `gce_credentials`}}", 37 | "zone": "{{user `gce_zone`}}", 38 | "network": "default", 39 | "source_image": "{{user `gce_source_image`}}", 40 | "ssh_username": "{{user `ssh_username`}}", 41 | "image_name": "packer-{{user `name`}}-{{timestamp}}", 42 | "image_description": "packer-{{user `name`}}-image", 43 | "use_internal_ip": false, 44 | "tags": [ 45 | "{{user `name`}}" 46 | ] 47 | } 48 | ], 49 | "provisioners": [ 50 | { 51 | "type": "shell", 52 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 53 | "inline": [ 54 | "mkdir -p /ops/{{user `scripts_dir`}}", 55 | "chmod a+w /ops/{{user `scripts_dir`}}", 56 | "mkdir -p /ops/{{user `config_dir`}}", 57 | "chmod a+w /ops/{{user `config_dir`}}" 58 | ] 59 | }, 60 | { 61 | "type": "file", 62 | "source": "{{user `scripts_dir`}}/", 63 | "destination": "/ops/{{user `scripts_dir`}}" 64 | }, 65 | { 66 | "type": "file", 67 | "source": "{{user `config_dir`}}/", 68 | "destination": "/ops/{{user `config_dir`}}" 69 | }, 70 | { 71 | "type": "shell", 72 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 73 | "inline": [ 74 | "cp /ops/{{user `config_dir`}}/consul/consul_server.json /etc/consul.d/base.json", 75 | "sh /ops/{{user `scripts_dir`}}/ubuntu/dnsmasq.sh {{user `dns_listen_addr`}}", 76 | "sh /ops/{{user `scripts_dir`}}/ubuntu/cleanup.sh" 77 | ] 78 | } 79 | ], 80 | "post-processors": [ 81 | { 82 | "type": "atlas", 83 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 84 | "artifact_type": "google.image", 85 | "metadata": { 86 | "created_at": "{{timestamp}}", 87 | "zone": "{{user `gce_zone`}}" 88 | } 89 | } 90 | ] 91 | } 92 | -------------------------------------------------------------------------------- /packer/google/ubuntu/haproxy.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 4 | "gce_project_id": "{{env `GCE_PROJECT_ID`}}", 5 | "gce_zone": "{{env `GCE_DEFAULT_ZONE`}}", 6 | "gce_source_image": "{{env `ATLAS_BASE_ARTIFACT_GOOGLE_IMAGE_ID`}}", 7 | "gce_credentials": "{{env `GCE_CREDENTIALS`}}", 8 | "name": "google-ubuntu-haproxy", 9 | "scripts_dir": "scripts", 10 | "config_dir": "config", 11 | "ssh_username": "ubuntu", 12 | "dns_listen_addr": "127.0.0.1" 13 | }, 14 | "push": { 15 | "name": "{{user `atlas_username`}}/{{user `name`}}", 16 | "base_dir": "../../../packer", 17 | "include": [ 18 | "{{user `scripts_dir`}}/*", 19 | "{{user `scripts_dir`}}/ubuntu/*", 20 | "{{user `scripts_dir`}}/ubuntu/upstart/*", 21 | "{{user `config_dir`}}/*", 22 | "{{user `config_dir`}}/consul/*", 23 | "{{user `config_dir`}}/consul_template/*", 24 | "{{user `config_dir`}}/consul_template/templates/*", 25 | "{{user `config_dir`}}/envconsul/*", 26 | "{{user `config_dir`}}/vault/*", 27 | "{{user `config_dir`}}/vault/policies/*", 28 | "{{user `config_dir`}}/vault/scripts/*" 29 | ], 30 | "vcs": false 31 | }, 32 | "builders": [ 33 | { 34 | "type": "googlecompute", 35 | "project_id": "{{user `gce_project_id`}}", 36 | "account_file": "{{user `gce_credentials`}}", 37 | "zone": "{{user `gce_zone`}}", 38 | "network": "default", 39 | "source_image": "{{user `gce_source_image`}}", 40 | "ssh_username": "{{user `ssh_username`}}", 41 | "image_name": "packer-{{user `name`}}-{{timestamp}}", 42 | "image_description": "packer-{{user `name`}}-image", 43 | "use_internal_ip": false, 44 | "tags": [ 45 | "{{user `name`}}" 46 | ] 47 | } 48 | ], 49 | "provisioners": [ 50 | { 51 | "type": "shell", 52 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 53 | "inline": [ 54 | "mkdir -p /ops/{{user `scripts_dir`}}", 55 | "chmod a+w /ops/{{user `scripts_dir`}}", 56 | "mkdir -p /ops/{{user `config_dir`}}", 57 | "chmod a+w /ops/{{user `config_dir`}}" 58 | ] 59 | }, 60 | { 61 | "type": "file", 62 | "source": "{{user `scripts_dir`}}/", 63 | "destination": "/ops/{{user `scripts_dir`}}" 64 | }, 65 | { 66 | "type": "file", 67 | "source": "{{user `config_dir`}}/", 68 | "destination": "/ops/{{user `config_dir`}}" 69 | }, 70 | { 71 | "type": "shell", 72 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 73 | "inline": [ 74 | "sh /ops/{{user `scripts_dir`}}/ubuntu/haproxy.sh {{user `config_dir`}} {{user `scripts_dir`}}/ubuntu", 75 | "sh /ops/{{user `scripts_dir`}}/ubuntu/cleanup.sh" 76 | ] 77 | } 78 | ], 79 | "post-processors": [ 80 | { 81 | "type": "atlas", 82 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 83 | "artifact_type": "google.image", 84 | "metadata": { 85 | "created_at": "{{timestamp}}", 86 | "zone": "{{user `gce_zone`}}" 87 | } 88 | } 89 | ] 90 | } 91 | -------------------------------------------------------------------------------- /packer/google/ubuntu/nodejs.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 4 | "gce_project_id": "{{env `GCE_PROJECT_ID`}}", 5 | "gce_zone": "{{env `GCE_DEFAULT_ZONE`}}", 6 | "gce_source_image": "{{env `ATLAS_BASE_ARTIFACT_GOOGLE_IMAGE_ID`}}", 7 | "gce_credentials": "{{env `GCE_CREDENTIALS`}}", 8 | "name": "google-ubuntu-nodejs", 9 | "scripts_dir": "scripts", 10 | "config_dir": "config", 11 | "ssh_username": "ubuntu", 12 | "host_app_dir": "/application" 13 | }, 14 | "push": { 15 | "name": "{{user `atlas_username`}}/{{user `name`}}", 16 | "base_dir": "../../../packer", 17 | "include": [ 18 | "{{user `scripts_dir`}}/*", 19 | "{{user `scripts_dir`}}/ubuntu/*", 20 | "{{user `scripts_dir`}}/ubuntu/upstart/*", 21 | "{{user `scripts_dir`}}/ubuntu/nodejs_app/*", 22 | "{{user `config_dir`}}/*", 23 | "{{user `config_dir`}}/consul/*", 24 | "{{user `config_dir`}}/consul_template/*", 25 | "{{user `config_dir`}}/consul_template/templates/*", 26 | "{{user `config_dir`}}/envconsul/*", 27 | "{{user `config_dir`}}/vault/*", 28 | "{{user `config_dir`}}/vault/policies/*", 29 | "{{user `config_dir`}}/vault/scripts/*" 30 | ], 31 | "vcs": false 32 | }, 33 | "builders": [ 34 | { 35 | "type": "googlecompute", 36 | "project_id": "{{user `gce_project_id`}}", 37 | "account_file": "{{user `gce_credentials`}}", 38 | "zone": "{{user `gce_zone`}}", 39 | "network": "default", 40 | "source_image": "{{user `gce_source_image`}}", 41 | "ssh_username": "{{user `ssh_username`}}", 42 | "image_name": "packer-{{user `name`}}-{{timestamp}}", 43 | "image_description": "packer-{{user `name`}}-image", 44 | "use_internal_ip": false, 45 | "tags": [ 46 | "{{user `name`}}" 47 | ] 48 | } 49 | ], 50 | "provisioners": [ 51 | { 52 | "type": "shell", 53 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 54 | "inline": [ 55 | "mkdir -p /ops/{{user `scripts_dir`}}", 56 | "chmod a+w /ops/{{user `scripts_dir`}}", 57 | "mkdir -p /ops/{{user `config_dir`}}", 58 | "chmod a+w /ops/{{user `config_dir`}}" 59 | ] 60 | }, 61 | { 62 | "type": "file", 63 | "source": "{{user `scripts_dir`}}/", 64 | "destination": "/ops/{{user `scripts_dir`}}" 65 | }, 66 | { 67 | "type": "file", 68 | "source": "{{user `config_dir`}}/", 69 | "destination": "/ops/{{user `config_dir`}}" 70 | }, 71 | { 72 | "type": "shell", 73 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 74 | "inline": [ 75 | "mkdir -p {{user `host_app_dir`}}", 76 | "chmod a+w {{user `host_app_dir`}}" 77 | ] 78 | }, 79 | { 80 | "type": "file", 81 | "source": "{{user `scripts_dir`}}/ubuntu/nodejs_app/", 82 | "destination": "{{user `host_app_dir`}}" 83 | }, 84 | { 85 | "type": "shell", 86 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 87 | "inline": [ 88 | "sh /ops/{{user `scripts_dir`}}/ubuntu/nodejs.sh {{user `config_dir`}} {{user `scripts_dir`}}/ubuntu", 89 | "sh /ops/{{user `scripts_dir`}}/ubuntu/cleanup.sh" 90 | ] 91 | } 92 | ], 93 | "post-processors": [ 94 | { 95 | "type": "atlas", 96 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 97 | "artifact_type": "google.image", 98 | "metadata": { 99 | "created_at": "{{timestamp}}", 100 | "zone": "{{user `gce_zone`}}" 101 | } 102 | } 103 | ] 104 | } 105 | -------------------------------------------------------------------------------- /packer/google/ubuntu/vault.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "atlas_username": "{{env `ATLAS_USERNAME`}}", 4 | "gce_project_id": "{{env `GCE_PROJECT_ID`}}", 5 | "gce_zone": "{{env `GCE_DEFAULT_ZONE`}}", 6 | "gce_source_image": "{{env `ATLAS_BASE_ARTIFACT_GOOGLE_IMAGE_ID`}}", 7 | "gce_credentials": "{{env `GCE_CREDENTIALS`}}", 8 | "name": "google-ubuntu-vault", 9 | "scripts_dir": "scripts", 10 | "config_dir": "config", 11 | "ssh_username": "ubuntu", 12 | "dns_listen_addr": "127.0.0.1" 13 | }, 14 | "push": { 15 | "name": "{{user `atlas_username`}}/{{user `name`}}", 16 | "base_dir": "../../../packer", 17 | "include": [ 18 | "{{user `scripts_dir`}}/*", 19 | "{{user `scripts_dir`}}/ubuntu/*", 20 | "{{user `scripts_dir`}}/ubuntu/upstart/*", 21 | "{{user `config_dir`}}/*", 22 | "{{user `config_dir`}}/consul/*", 23 | "{{user `config_dir`}}/consul_template/*", 24 | "{{user `config_dir`}}/consul_template/templates/*", 25 | "{{user `config_dir`}}/envconsul/*", 26 | "{{user `config_dir`}}/vault/*", 27 | "{{user `config_dir`}}/vault/policies/*", 28 | "{{user `config_dir`}}/vault/scripts/*" 29 | ], 30 | "vcs": false 31 | }, 32 | "builders": [ 33 | { 34 | "type": "googlecompute", 35 | "project_id": "{{user `gce_project_id`}}", 36 | "account_file": "{{user `gce_credentials`}}", 37 | "zone": "{{user `gce_zone`}}", 38 | "network": "default", 39 | "source_image": "{{user `gce_source_image`}}", 40 | "ssh_username": "{{user `ssh_username`}}", 41 | "image_name": "packer-{{user `name`}}-{{timestamp}}", 42 | "image_description": "packer-{{user `name`}}-image", 43 | "use_internal_ip": false, 44 | "tags": [ 45 | "{{user `name`}}" 46 | ] 47 | } 48 | ], 49 | "provisioners": [ 50 | { 51 | "type": "shell", 52 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 53 | "inline": [ 54 | "mkdir -p /ops/{{user `scripts_dir`}}", 55 | "chmod a+w /ops/{{user `scripts_dir`}}", 56 | "mkdir -p /ops/{{user `config_dir`}}", 57 | "chmod a+w /ops/{{user `config_dir`}}" 58 | ] 59 | }, 60 | { 61 | "type": "file", 62 | "source": "{{user `scripts_dir`}}/", 63 | "destination": "/ops/{{user `scripts_dir`}}" 64 | }, 65 | { 66 | "type": "file", 67 | "source": "{{user `config_dir`}}/", 68 | "destination": "/ops/{{user `config_dir`}}" 69 | }, 70 | { 71 | "type": "shell", 72 | "execute_command": "echo {{user `ssh_username`}} | {{ .Vars }} sudo -E -S sh '{{ .Path }}'", 73 | "inline": [ 74 | "sh /ops/{{user `scripts_dir`}}/ubuntu/vault.sh {{user `config_dir`}} {{user `scripts_dir`}}/ubuntu", 75 | "sh /ops/{{user `scripts_dir`}}/ubuntu/cleanup.sh" 76 | ] 77 | } 78 | ], 79 | "post-processors": [ 80 | { 81 | "type": "atlas", 82 | "artifact": "{{user `atlas_username`}}/{{user `name`}}", 83 | "artifact_type": "google.image", 84 | "metadata": { 85 | "created_at": "{{timestamp}}", 86 | "zone": "{{user `gce_zone`}}" 87 | } 88 | } 89 | ] 90 | } 91 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo Cleanup... 5 | apt-get -y autoremove 6 | apt-get -y clean 7 | 8 | rm -rf /tmp/* 9 | rm -rf /ops 10 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/consul.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd /tmp 5 | 6 | CONSULVERSION=0.7.0 7 | CONFIGDIR=/ops/$1 8 | SCRIPTSDIR=/ops/$2 9 | CONSULDOWNLOAD=https://releases.hashicorp.com/consul/${CONSULVERSION}/consul_${CONSULVERSION}_linux_amd64.zip 10 | CONSULWEBUI=https://releases.hashicorp.com/consul/${CONSULVERSION}/consul_${CONSULVERSION}_web_ui.zip 11 | CONSULCONFIGDIR=/etc/consul.d 12 | CONSULDIR=/opt/consul 13 | 14 | echo Fetching Consul... 15 | curl -L $CONSULDOWNLOAD > consul.zip 16 | 17 | echo Installing Consul... 18 | unzip consul.zip -d /usr/local/bin 19 | chmod 0755 /usr/local/bin/consul 20 | chown root:root /usr/local/bin/consul 21 | 22 | echo Configuring Consul... 23 | mkdir -p $CONSULCONFIGDIR 24 | chmod 755 $CONSULCONFIGDIR 25 | mkdir -p $CONSULDIR 26 | chmod 755 $CONSULDIR 27 | 28 | # Consul config 29 | cp $CONFIGDIR/consul/consul_client.json $CONSULCONFIGDIR/base.json 30 | 31 | # Upstart config 32 | cp $SCRIPTSDIR/upstart/consul.conf /etc/init/consul.conf 33 | 34 | curl -L $CONSULWEBUI > ui.zip 35 | unzip ui.zip -d $CONSULDIR/ui 36 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/consul_template.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd /tmp 5 | 6 | CTVERSION=0.16.0 7 | CONFIGDIR=/ops/$1 8 | SCRIPTSDIR=/ops/$2 9 | CTDOWNLOAD=https://releases.hashicorp.com/consul-template/${CTVERSION}/consul-template_${CTVERSION}_linux_amd64.zip 10 | CTCONFIGDIR=/etc/consul_template.d 11 | CTDIR=/opt/consul_template 12 | 13 | echo Fetching Consul Template... 14 | curl -L $CTDOWNLOAD > consul_template.zip 15 | 16 | echo Installing Consul Template... 17 | unzip consul_template.zip -d /usr/local/bin 18 | chmod 0755 /usr/local/bin/consul-template 19 | chown root:root /usr/local/bin/consul-template 20 | 21 | echo Configuring Consul Template... 22 | mkdir -p $CTCONFIGDIR 23 | chmod 755 $CTCONFIGDIR 24 | mkdir -p $CTDIR 25 | chmod 755 $CTDIR 26 | 27 | # Consul Template config 28 | cp $CONFIGDIR/consul_template/base.hcl $CTCONFIGDIR/base.hcl 29 | 30 | # Upstart config 31 | cp $SCRIPTSDIR/upstart/consul_template.conf /etc/init/consul_template.conf 32 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo Install dependencies... 5 | # Update the box 6 | apt-get -y update 7 | apt-get -y upgrade 8 | 9 | # Install dependencies 10 | apt-get -y install curl unzip jq 11 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/dnsmasq.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | DNSLISTENADDR=$1 5 | 6 | echo Installing Dnsmasq... 7 | 8 | apt-get -y update 9 | apt-get -y upgrade 10 | apt-get -y install dnsmasq-base dnsmasq 11 | 12 | echo Configuring Dnsmasq... 13 | 14 | cat </etc/dnsmasq.d/consul 15 | server=/consul/127.0.0.1#8600 16 | listen-address=$DNSLISTENADDR 17 | bind-interfaces 18 | EOF 19 | 20 | cat /etc/dnsmasq.d/consul 21 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/envconsul.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd /tmp 5 | 6 | ECVERSION=0.6.1 7 | CONFIGDIR=/ops/$1 8 | ECDOWNLOAD=https://releases.hashicorp.com/envconsul/${ECVERSION}/envconsul_${ECVERSION}_linux_amd64.zip 9 | ECCONFIGDIR=/etc/envconsul.d 10 | 11 | echo Fetching envconsul... 12 | curl -L $ECDOWNLOAD > envconsul.zip 13 | 14 | echo Installing envconsul... 15 | unzip envconsul.zip -d /usr/local/bin 16 | chmod 0755 /usr/local/bin/envconsul 17 | chown root:root /usr/local/bin/envconsul 18 | 19 | echo Configuring envconsul... 20 | mkdir -p $ECCONFIGDIR 21 | chmod 755 $ECCONFIGDIR 22 | 23 | # envconsul config 24 | cp $CONFIGDIR/envconsul/base.hcl $ECCONFIGDIR/base.hcl 25 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/haproxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | CONFIGDIR=/ops/$1 5 | SCRIPTSDIR=/ops/$2 6 | RSYSLOG=/etc/rsyslog.conf 7 | 8 | echo Installing HAProxy... 9 | 10 | apt-get -y update 11 | apt-get install -y haproxy 12 | chmod a+w /etc/rsyslog.conf 13 | 14 | echo '$ModLoad imudp' >> $RSYSLOG 15 | echo '$UDPServerAddress 127.0.0.1' >> $RSYSLOG 16 | echo '$UDPServerRun 514' >> $RSYSLOG 17 | 18 | echo Configuring HAProxy... 19 | 20 | # Consul config 21 | cp $CONFIGDIR/consul/haproxy.json /etc/consul.d/haproxy.json 22 | 23 | # Consul Template config 24 | cp $CONFIGDIR/consul_template/haproxy.hcl /etc/consul_template.d/haproxy.hcl 25 | cp $CONFIGDIR/consul_template/templates/haproxy.ctmpl /opt/consul_template/haproxy.ctmpl 26 | 27 | # Upstart config 28 | cp $SCRIPTSDIR/upstart/haproxy.conf /etc/init/haproxy.conf 29 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/nodejs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | CONFIGDIR=/ops/$1 5 | SCRIPTSDIR=/ops/$2 6 | VAULTPOLICIES=/opt/vault/policies 7 | 8 | echo Installing Node.js... 9 | 10 | # Setup a proper node PPA 11 | curl -sL https://deb.nodesource.com/setup_4.x — Node.js v4 LTS "Argon" | sudo bash - 12 | 13 | apt-get -y update 14 | apt-get install -y -qq nodejs 15 | 16 | echo Configuring Node.js application... 17 | mkdir -p $VAULTPOLICIES 18 | chmod 755 $VAULTPOLICIES 19 | 20 | # Consul config 21 | cp $CONFIGDIR/consul/nodejs.json /etc/consul.d/nodejs.json 22 | 23 | # Consul Template config 24 | cp $CONFIGDIR/consul_template/nodejs.hcl /etc/consul_template.d/nodejs.hcl 25 | cp $CONFIGDIR/consul_template/nodejs_aws.hcl /etc/consul_template.d/nodejs_aws.hcl 26 | cp $CONFIGDIR/consul_template/templates/vault_aws.ctmpl /opt/consul_template/vault_aws.ctmpl 27 | cp $CONFIGDIR/consul_template/templates/vault_generic.ctmpl /opt/consul_template/vault_generic.ctmpl 28 | 29 | # envconsul config 30 | cp $CONFIGDIR/envconsul/nodejs.hcl /etc/envconsul.d/nodejs.hcl 31 | 32 | # Vault Policy config 33 | cp $CONFIGDIR/vault/policies/nodejs.json $VAULTPOLICIES/nodejs.json 34 | cp $CONFIGDIR/vault/policies/aws_nodejs.json $VAULTPOLICIES/aws_nodejs.json 35 | 36 | # Upstart config 37 | cp $SCRIPTSDIR/upstart/nodejs.conf /etc/init/nodejs.conf 38 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/nodejs_app/compile.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "app_slug": "{{ env `ATLAS_APPLICATION_SLUG` }}" 4 | }, 5 | "builders": [ 6 | { 7 | "type": "docker", 8 | "image": "progrium/cedarish:cedar14", 9 | "commit": true 10 | } 11 | ], 12 | "provisioners": [ 13 | { 14 | "type": "shell", 15 | "inline": [ 16 | "curl --silent http://dl.gliderlabs.com/herokuish/latest/linux_x86_64.tgz | tar -xzC /bin" 17 | ] 18 | }, 19 | { 20 | "type": "file", 21 | "source": ".", 22 | "destination": "/tmp/app" 23 | }, 24 | { 25 | "type": "shell", 26 | "inline": [ 27 | "ls -lRa /tmp/app" 28 | ] 29 | }, 30 | { 31 | "type": "shell", 32 | "inline": [ 33 | "herokuish buildpack install https://github.com/heroku/heroku-buildpack-nodejs", 34 | "herokuish buildpack build", 35 | "cd /app", 36 | "tar czvf /tmp/slug.tar.gz .", 37 | "sleep 10" 38 | ] 39 | }, 40 | { 41 | "type": "file", 42 | "source": "/tmp/slug.tar.gz", 43 | "destination": "slug.tar.gz", 44 | "direction": "download" 45 | } 46 | ], 47 | "post-processors": [ 48 | [ 49 | { 50 | "type": "artifice", 51 | "files": ["slug.tar.gz"] 52 | }, 53 | { 54 | "type": "atlas", 55 | "artifact": "{{user `app_slug` }}", 56 | "artifact_type": "archive" 57 | } 58 | ] 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/nodejs_app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo-app", 3 | "description": "Node.js Application", 4 | "version": "1.0.0", 5 | "engines": { 6 | "node": "4.1.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/nodejs_app/server.js: -------------------------------------------------------------------------------- 1 | var http = require("http"), 2 | fs = require("fs"), 3 | vaultDir = "/application/vault/", 4 | showVault = process.env.SHOW_VAULT, 5 | vaultFiles = process.env.VAULT_FILES, 6 | vaultSecret = process.env.SECRET_KEY, 7 | files = [], 8 | port = 8888; 9 | 10 | function handleRequest(req, res) { 11 | res.writeHead(200, {"Content-type":"text/html"}); 12 | res.write("Hello, World! This is Node.js app v99."); 13 | 14 | // Only show Vault files if the SHOW_VAULT KV is set to true in Consul 15 | if (fs.existsSync(vaultDir) && showVault && (showVault.toUpperCase() === "TRUE" || showVault === "1")) { 16 | files = fs.readdirSync(vaultDir); 17 | 18 | for (var i = 0; i < files.length; i++) { 19 | file = files[i]; 20 | 21 | // Only show this file if included in the VAULT_FILES KV in Consul 22 | if (vaultFiles && vaultFiles.indexOf(file) > -1) { 23 | res.write(fs.readFileSync(vaultDir + file, "binary")); 24 | } 25 | } 26 | } 27 | 28 | res.end(); 29 | } 30 | 31 | http.createServer(handleRequest).listen(port); 32 | 33 | console.log("Static file server running at\n => http://localhost:" + port); 34 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/upstart/consul.conf: -------------------------------------------------------------------------------- 1 | description "Consul" 2 | 3 | start on runlevel [2345] 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | 8 | console log 9 | 10 | script 11 | if [ -f "/etc/service/consul" ]; then 12 | . /etc/service/consul 13 | fi 14 | 15 | # Make sure to use all our CPUs, because Consul can block a scheduler thread 16 | export GOMAXPROCS=`nproc` 17 | 18 | exec /usr/local/bin/consul agent \ 19 | -config-dir="/etc/consul.d" \ 20 | \$${CONSUL_FLAGS} \ 21 | >>/var/log/consul.log 2>&1 22 | end script 23 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/upstart/consul_template.conf: -------------------------------------------------------------------------------- 1 | description "Consul Template" 2 | 3 | start on vagrant-ready or runlevel [2345] 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | 8 | console log 9 | 10 | script 11 | exec /usr/local/bin/consul-template \ 12 | -config "/etc/consul_template.d" \ 13 | >>/var/log/consul_template.log 2>&1 14 | end script 15 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/upstart/haproxy.conf: -------------------------------------------------------------------------------- 1 | description "HAProxy" 2 | 3 | start on runlevel [2345] 4 | stop on runlevel [016] 5 | 6 | respawn 7 | 8 | env CONF=/etc/haproxy/haproxy.cfg 9 | 10 | post-stop script 11 | sleep 5 12 | end script 13 | 14 | pre-start script 15 | [ -r $CONF ] 16 | end script 17 | 18 | exec /usr/sbin/haproxy -f $CONF >> /var/log/haproxy_start.log 2>&1 19 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/upstart/nodejs.conf: -------------------------------------------------------------------------------- 1 | description "nodejs" 2 | 3 | start on vagrant-ready or runlevel [2345] 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | 8 | script 9 | envconsul \ 10 | -config /etc/envconsul.d \ 11 | /usr/bin/node /application/server.js >> /var/log/nodejs.log 2>&1 12 | end script 13 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/upstart/vault.conf: -------------------------------------------------------------------------------- 1 | description "Vault" 2 | 3 | start on runlevel [2345] 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | 8 | console log 9 | 10 | script 11 | if [ -f "/etc/service/vault" ]; then 12 | . /etc/service/vault 13 | fi 14 | 15 | # Make sure to use all our CPUs, because Vault can block a scheduler thread 16 | export GOMAXPROCS=`nproc` 17 | 18 | exec /usr/local/bin/vault server \ 19 | -config="/etc/vault.d/vault.hcl" \ 20 | \$${VAULT_FLAGS} \ 21 | >>/var/log/vault.log 2>&1 22 | end script 23 | -------------------------------------------------------------------------------- /packer/scripts/ubuntu/vault.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | cd /tmp 5 | 6 | VAULTVERSION=0.6.2 7 | CONFIGDIR=/ops/$1 8 | SCRIPTSDIR=/ops/$2 9 | VAULTDOWNLOAD=https://releases.hashicorp.com/vault/${VAULTVERSION}/vault_${VAULTVERSION}_linux_amd64.zip 10 | VAULTCONFIGDIR=/etc/vault.d 11 | VAULTDIR=/opt/vault 12 | VAULTPOLICIES=$VAULTDIR/policies 13 | VAULTSCRIPTS=$VAULTDIR/scripts 14 | 15 | echo Fetching Vault... 16 | curl -L $VAULTDOWNLOAD > vault.zip 17 | 18 | echo Installing Vault... 19 | unzip vault.zip -d /usr/local/bin 20 | chmod 0755 /usr/local/bin/vault 21 | chown root:root /usr/local/bin/vault 22 | 23 | echo Creating Vault configuration... 24 | mkdir -p $VAULTCONFIGDIR 25 | chmod 755 $VAULTCONFIGDIR 26 | mkdir -p $VAULTPOLICIES 27 | chmod 755 $VAULTPOLICIES 28 | mkdir -p $VAULTSCRIPTS 29 | chmod 755 $VAULTSCRIPTS 30 | 31 | # Consul config 32 | cp $CONFIGDIR/consul/vault.json /etc/consul.d/vault.json 33 | 34 | # Vault config 35 | cp $CONFIGDIR/vault/vault.hcl $VAULTCONFIGDIR/vault.hcl 36 | 37 | # Vault setup scripts & policies 38 | cp -R $CONFIGDIR/vault/policies/* $VAULTPOLICIES/. 39 | cp -R $CONFIGDIR/vault/scripts/* $VAULTSCRIPTS/. 40 | 41 | # Upstart config 42 | cp $SCRIPTSDIR/upstart/vault.conf /etc/init/vault.conf 43 | -------------------------------------------------------------------------------- /packer/scripts/windows/config/ec2_user_data.conf: -------------------------------------------------------------------------------- 1 | 2 | write-output "Starting instance userdata script" 3 | 4 | Set-ExecutionPolicy -ExecutionPolicy Bypass -Force 5 | 6 | # WinRM 7 | write-output "Setting up WinRM" 8 | 9 | & winrm quickconfig `-q 10 | & winrm set winrm/config '@{MaxTimeoutms="1800000"}' 11 | & winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}' 12 | & winrm set winrm/config/client/auth '@{Basic="true"}' 13 | & winrm set winrm/config/service/auth '@{Basic="true"}' 14 | & winrm set winrm/config/client '@{AllowUnencrypted="true"}' 15 | & winrm set winrm/config/service '@{AllowUnencrypted="true"}' 16 | 17 | # Firewall 18 | write-output "Setting up Firewall" 19 | 20 | netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" profile=public new remoteip=any 21 | 22 | write-output "Ending instance userdata script" 23 | 24 | -------------------------------------------------------------------------------- /packer/scripts/windows/consul_client.json: -------------------------------------------------------------------------------- 1 | { 2 | "log_level": "INFO", 3 | "datacenter": "{{ datacenter }}", 4 | "data_dir": "C:\\opt\\consul\\data", 5 | "ui_dir": "C:\\opt\\consul\\ui", 6 | "client_addr": "0.0.0.0", 7 | "bind_addr": "0.0.0.0", 8 | "atlas_infrastructure": "{{ atlas_username }}/{{ atlas_environment }}", 9 | "atlas_join": true, 10 | "atlas_token": "{{ atlas_token }}", 11 | "node_name": "{{ node_name }}", 12 | "skip_leave_on_interrupt": true, 13 | "leave_on_terminate": true 14 | } 15 | -------------------------------------------------------------------------------- /packer/scripts/windows/install_consul.ps1: -------------------------------------------------------------------------------- 1 | write-output "Creating Consul directories" 2 | foreach ($dir in @('log', 'data')) { 3 | New-Item -Path "C:\opt\consul\$dir" -ItemType Directory -Force 4 | } 5 | 6 | write-output "Creating nssm directory" 7 | New-Item -Path "C:\opt\nssm" -ItemType Directory -Force 8 | 9 | write-output "Setting urls" 10 | $nssmUrl = "http://nssm.cc/release/nssm-2.24.zip" 11 | $consulUrl = "https://releases.hashicorp.com/consul/0.6.0-rc1/consul_0.6.0-rc1_windows_amd64.zip" 12 | $uiUrl = "https://releases.hashicorp.com/consul/0.6.0-rc1/consul_0.6.0-rc1_web_ui.zip" 13 | 14 | write-output "Setting file paths" 15 | $nssmFilePath = "$($env:TEMP)\nssm.zip" 16 | $consulFilePath = "$($env:TEMP)\consul.zip" 17 | $uiFilePath = "$($env:TEMP)\consulwebui.zip" 18 | 19 | write-output "Downloading nssm" 20 | (New-Object System.Net.WebClient).DownloadFile($nssmUrl, $nssmFilePath) 21 | write-output "Downloading Consul" 22 | (New-Object System.Net.WebClient).DownloadFile($consulUrl, $consulFilePath) 23 | write-output "Downloading Consul Web UI" 24 | (New-Object System.Net.WebClient).DownloadFile($uiUrl, $uiFilePath) 25 | 26 | write-output "Creating shell object" 27 | $shell = New-Object -ComObject Shell.Application 28 | 29 | write-output "Setting namespaces" 30 | $nssmZip = $shell.NameSpace($nssmFilePath) 31 | $consulZip = $shell.NameSpace($consulFilePath) 32 | $uiZip = $shell.NameSpace($uiFilePath) 33 | 34 | write-output "Setting destinations" 35 | $nssmDestination = $shell.NameSpace("C:\opt\nssm") 36 | $consulDestination = $shell.NameSpace("C:\opt\consul") 37 | $uiDestination = $shell.NameSpace("C:\opt\consul") 38 | 39 | write-output "Setting copy flags" 40 | $copyFlags = 0x00 41 | $copyFlags += 0x04 # Hide progress dialogs 42 | $copyFlags += 0x10 # Overwrite existing files 43 | 44 | write-output "Copying nssm" 45 | $nssmDestination.CopyHere($nssmZip.Items(), $copyFlags) 46 | write-output "Copying Consul" 47 | $consulDestination.CopyHere($consulZip.Items(), $copyFlags) 48 | write-output "Copying Consul Web UI" 49 | $uiDestination.CopyHere($uiZip.Items(), $copyFlags) 50 | 51 | # Alternative way to unzip 52 | # cmd /c "7z e C:\install\consul\0.5.2_windows_386.zip -oC:\opt\consul > C:\install_log\consul.log" 53 | 54 | # Move nssm exe to /opt 55 | write-output "Moving nssm" 56 | Move-Item -Path "C:\opt\nssm\nssm-2.24\win32\nssm.exe" "C:\opt" -Force 57 | write-output "Moving Consul Web UI" 58 | Move-Item -Path "C:\opt\consul\dist" "C:\opt\consul\ui" -Force 59 | 60 | # Clean up 61 | write-output "Cleanup" 62 | #Remove-Item -Force -Path "C:\opt\nssm" 63 | Remove-Item -Force -Path $consulFilePath 64 | Remove-Item -Force -Path $uiFilePath 65 | Remove-Item -Force -Path $nssmFilePath 66 | 67 | # Create the Consul service and set its options 68 | write-output "Creating Consul service" 69 | C:\opt\nssm.exe install consul "C:\opt\consul\consul.exe" agent -config-dir "C:\etc\consul.d" 70 | write-output "Setting Consul options" 71 | C:\opt\nssm.exe set consul AppEnvironmentExtra "GOMAXPROCS=%NUMBER_OF_PROCESSORS%" 72 | C:\opt\nssm.exe set consul AppRotateFiles 1 73 | C:\opt\nssm.exe set consul AppRotateOnline 1 74 | C:\opt\nssm.exe set consul AppRotateBytes 10485760 75 | C:\opt\nssm.exe set consul AppStdout C:\opt\consul\log\consul.log 76 | C:\opt\nssm.exe set consul AppStderr C:\opt\consul\log\consul.log 77 | 78 | write-output "Stopping Consul service" 79 | Stop-Service consul -EA silentlycontinue 80 | Set-Service consul -StartupType Manual 81 | 82 | # Disable negative DNS response caching 83 | write-output "Disable negative DNS response caching" 84 | Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters -Name MaxNegativeCacheTtl -Value 0 -Type DWord 85 | 86 | # Allow Consul Serf traffic through the firewall 87 | write-output "Set firewall" 88 | netsh advfirewall firewall add rule name="Consul Serf LAN TCP" dir=in action=allow protocol=TCP localport=8301 89 | netsh advfirewall firewall add rule name="Consul Serf LAN UDP" dir=in action=allow protocol=UDP localport=8301 90 | -------------------------------------------------------------------------------- /packer/scripts/windows/install_web_server.ps1: -------------------------------------------------------------------------------- 1 | # Silence progress bars in PowerShell, which can sometimes feed back strange 2 | # XML data to the Packer output. 3 | $ProgressPreference = "SilentlyContinue" 4 | 5 | Write-Output "Starting IIS Installation" 6 | 7 | Import-Module ServerManager 8 | 9 | # IIS 10 | 11 | # Only needed for .NET 3, .NET 4/4.5 is already installed. 12 | #Add-WindowsFeature NET-Framework-Core 13 | 14 | Add-WindowsFeature Web-Server -IncludeAllSubFeature 15 | 16 | Write-Output "Ended IIS Installation" 17 | 18 | # WebDeploy 19 | Write-Output "Starting WebDeploy Installation" 20 | 21 | # Install Microsoft Web Deploy to be able to deploy website packages easily 22 | $webDeployURL = "http://download.microsoft.com/download/D/4/4/D446D154-2232-49A1-9D64-F5A9429913A4/WebDeploy_amd64_en-US.msi" 23 | $filePath = "$($env:TEMP)\WebDeploy_amd64_en-US.msi" 24 | 25 | (New-Object System.Net.WebClient).DownloadFile($webDeployURL, $filePath) 26 | 27 | Start-Process -FilePath msiexec -ArgumentList /i, $filePath, /qn -Wait 28 | 29 | # Clean up 30 | Remove-Item -Force -Path $filePath 31 | 32 | Write-Output "Ended WebDeploy Installation" 33 | -------------------------------------------------------------------------------- /packer/scripts/windows/install_windows_updates.ps1: -------------------------------------------------------------------------------- 1 | # Silence progress bars in PowerShell, which can sometimes feed back strange 2 | # XML data to the Packer output. 3 | $ProgressPreference = "SilentlyContinue" 4 | 5 | Write-Output "Starting PSWindowsUpdate Installation" 6 | # Install PSWindowsUpdate for scriptable Windows Updates 7 | $webDeployURL = "https://gallery.technet.microsoft.com/scriptcenter/2d191bcd-3308-4edd-9de2-88dff796b0bc/file/66095/1/PSWindowsUpdate_1.4.5.zip" 8 | $filePath = "$($env:TEMP)\PSWindowsUpdate.zip" 9 | 10 | (New-Object System.Net.WebClient).DownloadFile($webDeployURL, $filePath) 11 | 12 | # Older versions of Powershell do not have 'Expand Archive' 13 | # Use Shell.Application custom object to unzip 14 | # https://stackoverflow.com/questions/27768303/how-to-unzip-a-file-in-powershell 15 | $shell = New-Object -ComObject Shell.Application 16 | $zipFile = $shell.NameSpace($filePath) 17 | $destinationFolder = $shell.NameSpace("C:\Program Files\WindowsPowerShell\Modules") 18 | 19 | $copyFlags = 0x00 20 | $copyFlags += 0x04 # Hide progress dialogs 21 | $copyFlags += 0x10 # Overwrite existing files 22 | 23 | $destinationFolder.CopyHere($zipFile.Items(), $copyFlags) 24 | # Clean up 25 | Remove-Item -Force -Path $filePath 26 | 27 | Write-Output "Ended PSWindowsUpdate Installation" 28 | 29 | Write-Output "Starting Windows Update Installation" 30 | 31 | Try 32 | { 33 | Import-Module PSWindowsUpdate -ErrorAction Stop 34 | } 35 | Catch 36 | { 37 | Write-Error "Unable to install PSWindowsUpdate" 38 | exit 1 39 | } 40 | 41 | if (Test-Path C:\Windows\Temp\PSWindowsUpdate.log) { 42 | # Save old logs 43 | Rename-Item -Path C:\Windows\Temp\PSWindowsUpdate.log -NewName PSWindowsUpdate-$((Get-Date).Ticks).log 44 | 45 | # Uncomment the line below to delete old logs instead 46 | #Remove-Item -Path C:\Windows\Temp\PSWindowsUpdate.log 47 | } 48 | 49 | try { 50 | $updateCommand = {ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll -IgnoreReboot | Out-File C:\Windows\Temp\PSWindowsUpdate.log} 51 | $TaskName = "PackerUpdate" 52 | 53 | $User = [Security.Principal.WindowsIdentity]::GetCurrent() 54 | $Scheduler = New-Object -ComObject Schedule.Service 55 | 56 | $Task = $Scheduler.NewTask(0) 57 | 58 | $RegistrationInfo = $Task.RegistrationInfo 59 | $RegistrationInfo.Description = $TaskName 60 | $RegistrationInfo.Author = $User.Name 61 | 62 | $Settings = $Task.Settings 63 | $Settings.Enabled = $True 64 | $Settings.StartWhenAvailable = $True 65 | $Settings.Hidden = $False 66 | 67 | $Action = $Task.Actions.Create(0) 68 | $Action.Path = "powershell" 69 | $Action.Arguments = "-Command $updateCommand" 70 | 71 | $Task.Principal.RunLevel = 1 72 | 73 | $Scheduler.Connect() 74 | $RootFolder = $Scheduler.GetFolder("\") 75 | $RootFolder.RegisterTaskDefinition($TaskName, $Task, 6, "SYSTEM", $Null, 1) | Out-Null 76 | $RootFolder.GetTask($TaskName).Run(0) | Out-Null 77 | 78 | Write-Output "The Windows Update log will be displayed below this message. No additional output indicates no updates were needed." 79 | do { 80 | sleep 1 81 | if ((Test-Path C:\Windows\Temp\PSWindowsUpdate.log) -and $script:reader -eq $null) { 82 | $script:stream = New-Object System.IO.FileStream -ArgumentList "C:\Windows\Temp\PSWindowsUpdate.log", "Open", "Read", "ReadWrite" 83 | $script:reader = New-Object System.IO.StreamReader $stream 84 | } 85 | if ($script:reader -ne $null) { 86 | $line = $Null 87 | do {$script:reader.ReadLine() 88 | $line = $script:reader.ReadLine() 89 | Write-Output $line 90 | } while ($line -ne $null) 91 | } 92 | } while ($Scheduler.GetRunningTasks(0) | Where-Object {$_.Name -eq $TaskName}) 93 | } finally { 94 | $RootFolder.DeleteTask($TaskName,0) 95 | [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Scheduler) | Out-Null 96 | if ($script:reader -ne $null) { 97 | $script:reader.Close() 98 | $script:stream.Dispose() 99 | } 100 | } 101 | Write-Output "Ended Windows Update Installation" 102 | -------------------------------------------------------------------------------- /packer/scripts/windows/set_ec2_config.ps1: -------------------------------------------------------------------------------- 1 | # This script will set up the system to request a password reset on the next boot. 2 | # The new password will be exposed via the EC2 console. 3 | # It will also make the system handle any new userdata passed in. 4 | $EC2SettingsFile="C:\\Program Files\\Amazon\\Ec2ConfigService\\Settings\\Config.xml" 5 | $xml = [xml](get-content $EC2SettingsFile) 6 | $xmlElement = $xml.get_DocumentElement() 7 | $xmlElementToModify = $xmlElement.Plugins 8 | 9 | foreach ($element in $xmlElementToModify.Plugin) 10 | { 11 | if ($element.name -eq "Ec2SetPassword") 12 | { 13 | $element.State="Enabled" 14 | } 15 | elseif ($element.name -eq "Ec2SetComputerName") 16 | { 17 | $element.State="Disabled" 18 | } 19 | elseif ($element.name -eq "Ec2HandleUserData") 20 | { 21 | $element.State="Enabled" 22 | } 23 | } 24 | $xml.Save($EC2SettingsFile) 25 | 26 | Write-Output "Set EC2 Machine Configuration" 27 | -------------------------------------------------------------------------------- /setup/gen_cert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | usage() { 6 | cat < 16 | 17 | Where DOMAIN is the domain to be deployed and COMPANY is your companies name. 18 | 19 | This will generate a self-signed site cert with the following subjectAltNames in the directory specified. 20 | 21 | * DOMAIN 22 | * vault.DOMAIN 23 | * vpn.DOMAIN 24 | * nodejs.DOMAIN 25 | * haproxy.DOMAIN 26 | * private.haproxy.DOMAIN 27 | 28 | And a self-signed cert for Consul/Vault with the following subjectAltNames in the directory specified. 29 | 30 | * DOMAIN 31 | * *.node.consul 32 | * *.service.consul 33 | 34 | * IP 35 | * 0.0.0.0 36 | * 127.0.0.1 37 | EOF 38 | 39 | exit 1 40 | } 41 | 42 | create_cert() { 43 | local base="$1" 44 | local domain="$2" 45 | local company="$3" 46 | local sslconf="$4" 47 | 48 | echo "Creating $base cert" 49 | 50 | local os="$(uname -s)" 51 | local csr="${base}.csr" 52 | local key="${base}.key" 53 | local crt="${base}.crt" 54 | 55 | # MinGW/MSYS issue: http://stackoverflow.com/questions/31506158/running-openssl-from-a-bash-script-on-windows-subject-does-not-start-with 56 | local subj="/C=US/ST=California/L=San Francisco/O=${company}/OU=${base}/CN=${domain}" 57 | if [[ "${os}" == "MINGW32"* || "${os}" == "MINGW64"* || "${os}" == "MSYS"* ]]; then 58 | subj="//C=US\ST=California\L=San Francisco\O=${company}\OU=${base}\CN=${domain}" 59 | fi 60 | 61 | openssl genrsa -out "$key" 2048 62 | openssl req -new -out "$csr" -key "$key" -subj "${subj}" -config "$sslconf" 63 | openssl x509 -req -days 3650 -in "$csr" -signkey "$key" -out "$crt" -extensions v3_req -extfile "$sslconf" 64 | } 65 | 66 | main() { 67 | local domain="$1" 68 | local company="$2" 69 | 70 | if ! which openssl > /dev/null; then 71 | echo 72 | echo "ERROR: The openssl executable was not found. This script requires openssl." 73 | echo 74 | usage 75 | fi 76 | 77 | if [[ -z "$domain" ]]; then 78 | echo 79 | echo "ERROR: Specify base domain as the first argument, e.g. mycompany.com" 80 | echo 81 | usage 82 | fi 83 | 84 | if [[ -z "$company" ]]; then 85 | echo 86 | echo "ERROR: Specify company as the third argument, e.g. HashiCorp" 87 | echo 88 | usage 89 | fi 90 | 91 | umask 077 92 | 93 | # Create a temporary build dir and make sure we clean it up. For 94 | # debugging, comment out the trap line. 95 | local builddir="$(mktemp -d /tmp/ssl-XXXXXX)" 96 | trap "rm -rf '$builddir'" INT TERM EXIT 97 | 98 | local sslconf="${builddir}/site_selfsigned_openssl.cnf" 99 | cp openssl.cnf "${sslconf}" 100 | (cat <> "$sslconf" 110 | create_cert "site" "$domain" "$company" "$sslconf" 111 | 112 | domain="consul" 113 | sslconf=${builddir}/vault_selfsigned_openssl.cnf 114 | cp openssl.cnf "${sslconf}" 115 | (cat <> "$sslconf" 123 | create_cert "vault" "$domain" "$company" "$sslconf" 124 | } 125 | 126 | main "$@" 127 | 128 | -------------------------------------------------------------------------------- /setup/gen_key.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | usage() { 6 | cat < [EXISTING KEY] 12 | 13 | Where ENVIRONMENT is the Atlas Environment specified in terraform.tfvars. There 14 | is an optional second argument you can include that uses an existing private 15 | key. 16 | 17 | This will generate a .pem private key and a .pub public key in the directory 18 | specified. 19 | EOF 20 | } 21 | 22 | main() { 23 | local environment="$1" 24 | local existingkey="$2" 25 | local key="$environment" 26 | 27 | if [[ -z "$environment" || $# -eq 0 ]]; then 28 | printf "ERROR: Specify environment as the second argument, e.g. aws-us-east-1-prod\n\n" >&2 29 | usage 30 | exit 1 31 | fi 32 | 33 | if [[ -s "$key.pem" && -s "$key.pub" && -z "$existingkey" ]]; then 34 | echo "Using existing key pair" 35 | return 0 36 | fi 37 | 38 | umask 277 39 | 40 | if [[ -z "$existingkey" ]]; then 41 | echo "No key pair exists and no private key arg was passed, generating new keys..." 42 | rm -f "${key}.pem" 43 | openssl genrsa -out "$key.pem" 1024 44 | 45 | elif [[ -s "$existingkey" ]]; then 46 | echo "Using private key $existingkey for key pair..." 47 | cp "$existingkey" "$key.pem" 48 | 49 | else 50 | echo "ERROR: Missing or empty existing private key $existingkey!" 51 | exit 1 52 | fi 53 | 54 | rm -f "${key}.pub" 55 | ssh-keygen -y -f "$key.pem" > "$key.pub" 56 | } 57 | 58 | main "$@" 59 | 60 | -------------------------------------------------------------------------------- /setup/openssl.cnf: -------------------------------------------------------------------------------- 1 | # OpenSSL example configuration file. 2 | # This is mostly being used for generation of certificate requests. 3 | # 4 | 5 | # This definition stops the following lines choking if HOME isn't 6 | # defined. 7 | HOME = . 8 | RANDFILE = $ENV::HOME/.rnd 9 | 10 | # Extra OBJECT IDENTIFIER info: 11 | #oid_file = $ENV::HOME/.oid 12 | oid_section = new_oids 13 | 14 | # To use this configuration file with the "-extfile" option of the 15 | # "openssl x509" utility, name here the section containing the 16 | # X.509v3 extensions to use: 17 | # extensions = 18 | # (Alternatively, use a configuration file that has only 19 | # X.509v3 extensions in its main [= default] section.) 20 | 21 | [ new_oids ] 22 | 23 | # We can add new OIDs in here for use by 'ca' and 'req'. 24 | # Add a simple OID like this: 25 | # testoid1=1.2.3.4 26 | # Or use config file substitution like this: 27 | # testoid2=${testoid1}.5.6 28 | 29 | #################################################################### 30 | [ ca ] 31 | default_ca = CA_default # The default ca section 32 | 33 | #################################################################### 34 | [ CA_default ] 35 | 36 | dir = ./demoCA # Where everything is kept 37 | certs = $dir/certs # Where the issued certs are kept 38 | crl_dir = $dir/crl # Where the issued crl are kept 39 | database = $dir/index.txt # database index file. 40 | #unique_subject = no # Set to 'no' to allow creation of 41 | # several ctificates with same subject. 42 | new_certs_dir = $dir/newcerts # default place for new certs. 43 | 44 | certificate = $dir/cacert.pem # The CA certificate 45 | serial = $dir/serial # The current serial number 46 | crlnumber = $dir/crlnumber # the current crl number 47 | # must be commented out to leave a V1 CRL 48 | crl = $dir/crl.pem # The current CRL 49 | private_key = $dir/private/cakey.pem# The private key 50 | RANDFILE = $dir/private/.rand # private random number file 51 | 52 | x509_extensions = usr_cert # The extentions to add to the cert 53 | 54 | # Comment out the following two lines for the "traditional" 55 | # (and highly broken) format. 56 | name_opt = ca_default # Subject Name options 57 | cert_opt = ca_default # Certificate field options 58 | 59 | # Extension copying option: use with caution. 60 | copy_extensions = copy 61 | 62 | # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs 63 | # so this is commented out by default to leave a V1 CRL. 64 | # crlnumber must also be commented out to leave a V1 CRL. 65 | # crl_extensions = crl_ext 66 | 67 | default_days = 365 # how long to certify for 68 | default_crl_days= 30 # how long before next CRL 69 | default_md = sha1 # which md to use. 70 | preserve = no # keep passed DN ordering 71 | 72 | # A few difference way of specifying how similar the request should look 73 | # For type CA, the listed attributes must be the same, and the optional 74 | # and supplied fields are just that :-) 75 | policy = policy_match 76 | 77 | # For the CA policy 78 | [ policy_match ] 79 | countryName = match 80 | stateOrProvinceName = match 81 | organizationName = match 82 | organizationalUnitName = optional 83 | commonName = supplied 84 | emailAddress = optional 85 | 86 | # For the 'anything' policy 87 | # At this point in time, you must list all acceptable 'object' 88 | # types. 89 | [ policy_anything ] 90 | countryName = optional 91 | stateOrProvinceName = optional 92 | localityName = optional 93 | organizationName = optional 94 | organizationalUnitName = optional 95 | commonName = supplied 96 | emailAddress = optional 97 | 98 | #################################################################### 99 | [ req ] 100 | default_bits = 1024 101 | default_keyfile = privkey.pem 102 | distinguished_name = req_distinguished_name 103 | attributes = req_attributes 104 | x509_extensions = v3_ca # The extentions to add to the self signed cert 105 | 106 | # Passwords for private keys if not present they will be prompted for 107 | # input_password = secret 108 | # output_password = secret 109 | 110 | # This sets a mask for permitted string types. There are several options. 111 | # default: PrintableString, T61String, BMPString. 112 | # pkix : PrintableString, BMPString. 113 | # utf8only: only UTF8Strings. 114 | # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). 115 | # MASK:XXXX a literal mask value. 116 | # WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings 117 | # so use this option with caution! 118 | string_mask = nombstr 119 | 120 | req_extensions = v3_req # The extensions to add to a certificate request 121 | 122 | [ req_distinguished_name ] 123 | countryName = Country Name (2 letter code) 124 | countryName_default = AU 125 | countryName_min = 2 126 | countryName_max = 2 127 | 128 | stateOrProvinceName = State or Province Name (full name) 129 | stateOrProvinceName_default = Some-State 130 | 131 | localityName = Locality Name (eg, city) 132 | 133 | 0.organizationName = Organization Name (eg, company) 134 | 0.organizationName_default = Internet Widgits Pty Ltd 135 | 136 | # we can do this but it is not needed normally :-) 137 | #1.organizationName = Second Organization Name (eg, company) 138 | #1.organizationName_default = World Wide Web Pty Ltd 139 | 140 | organizationalUnitName = Organizational Unit Name (eg, section) 141 | #organizationalUnitName_default = 142 | 143 | commonName = Common Name (e.g. server FQDN or YOUR name) 144 | commonName_max = 64 145 | 146 | emailAddress = Email Address 147 | emailAddress_max = 64 148 | 149 | # SET-ex3 = SET extension number 3 150 | 151 | [ req_attributes ] 152 | challengePassword = A challenge password 153 | challengePassword_min = 4 154 | challengePassword_max = 20 155 | 156 | unstructuredName = An optional company name 157 | 158 | [ usr_cert ] 159 | 160 | # These extensions are added when 'ca' signs a request. 161 | 162 | # This goes against PKIX guidelines but some CAs do it and some software 163 | # requires this to avoid interpreting an end user certificate as a CA. 164 | 165 | basicConstraints=CA:TRUE 166 | 167 | # Here are some examples of the usage of nsCertType. If it is omitted 168 | # the certificate can be used for anything *except* object signing. 169 | 170 | # This is OK for an SSL server. 171 | # nsCertType = server 172 | 173 | # For an object signing certificate this would be used. 174 | # nsCertType = objsign 175 | 176 | # For normal client use this is typical 177 | # nsCertType = client, email 178 | 179 | # and for everything including object signing: 180 | # nsCertType = client, email, objsign 181 | 182 | # This is typical in keyUsage for a client certificate. 183 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment 184 | 185 | # This will be displayed in Netscape's comment listbox. 186 | nsComment = "OpenSSL Generated Certificate" 187 | 188 | # PKIX recommendations harmless if included in all certificates. 189 | subjectKeyIdentifier=hash 190 | authorityKeyIdentifier=keyid,issuer 191 | 192 | # This stuff is for subjectAltName and issuerAltname. 193 | # Import the email address. 194 | # subjectAltName=email:copy 195 | # An alternative to produce certificates that aren't 196 | # deprecated according to PKIX. 197 | # subjectAltName=email:move 198 | 199 | # Copy subject details 200 | # issuerAltName=issuer:copy 201 | 202 | #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem 203 | #nsBaseUrl 204 | #nsRevocationUrl 205 | #nsRenewalUrl 206 | #nsCaPolicyUrl 207 | #nsSslServerName 208 | 209 | [ v3_req ] 210 | 211 | # Extensions to add to a certificate request 212 | 213 | basicConstraints = CA:TRUE 214 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment 215 | subjectAltName = @alt_names 216 | 217 | [ v3_ca ] 218 | 219 | 220 | # Extensions for a typical CA 221 | 222 | 223 | # PKIX recommendation. 224 | 225 | subjectKeyIdentifier=hash 226 | 227 | authorityKeyIdentifier=keyid:always,issuer:always 228 | 229 | # This is what PKIX recommends but some broken software chokes on critical 230 | # extensions. 231 | #basicConstraints = critical,CA:true 232 | # So we do this instead. 233 | basicConstraints = CA:TRUE 234 | 235 | # Key usage: this is typical for a CA certificate. However since it will 236 | # prevent it being used as an test self-signed certificate it is best 237 | # left out by default. 238 | keyUsage = cRLSign, keyCertSign 239 | 240 | # Some might want this also 241 | # nsCertType = sslCA, emailCA 242 | 243 | # Include email address in subject alt name: another PKIX recommendation 244 | # subjectAltName=email:copy 245 | # Copy issuer details 246 | # issuerAltName=issuer:copy 247 | 248 | # DER hex encoding of an extension: beware experts only! 249 | # obj=DER:02:03 250 | # Where 'obj' is a standard or added object 251 | # You can even override a supported extension: 252 | # basicConstraints= critical, DER:30:03:01:01:FF 253 | 254 | [ crl_ext ] 255 | 256 | # CRL extensions. 257 | # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. 258 | 259 | # issuerAltName=issuer:copy 260 | authorityKeyIdentifier=keyid:always,issuer:always 261 | 262 | [ proxy_cert_ext ] 263 | # These extensions should be added when creating a proxy certificate 264 | 265 | # This goes against PKIX guidelines but some CAs do it and some software 266 | # requires this to avoid interpreting an end user certificate as a CA. 267 | 268 | basicConstraints=CA:FALSE 269 | 270 | # Here are some examples of the usage of nsCertType. If it is omitted 271 | # the certificate can be used for anything *except* object signing. 272 | 273 | # This is OK for an SSL server. 274 | # nsCertType = server 275 | 276 | # For an object signing certificate this would be used. 277 | # nsCertType = objsign 278 | 279 | # For normal client use this is typical 280 | # nsCertType = client, email 281 | 282 | # and for everything including object signing: 283 | # nsCertType = client, email, objsign 284 | 285 | # This is typical in keyUsage for a client certificate. 286 | # keyUsage = nonRepudiation, digitalSignature, keyEncipherment 287 | 288 | # This will be displayed in Netscape's comment listbox. 289 | nsComment = "OpenSSL Generated Certificate" 290 | 291 | # PKIX recommendations harmless if included in all certificates. 292 | subjectKeyIdentifier=hash 293 | authorityKeyIdentifier=keyid,issuer:always 294 | 295 | # This stuff is for subjectAltName and issuerAltname. 296 | # Import the email address. 297 | # subjectAltName=email:copy 298 | # An alternative to produce certificates that aren't 299 | # deprecated according to PKIX. 300 | # subjectAltName=email:move 301 | 302 | # Copy subject details 303 | # issuerAltName=issuer:copy 304 | 305 | #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem 306 | #nsBaseUrl 307 | #nsRevocationUrl 308 | #nsRenewalUrl 309 | #nsCaPolicyUrl 310 | #nsSslServerName 311 | 312 | # This really needs to be in place for it to be a proxy certificate. 313 | proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo 314 | -------------------------------------------------------------------------------- /terraform/empty.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Intentionally empty. Required to allow Terraform push 3 | * commands from the top-level terraform directory. 4 | */ 5 | -------------------------------------------------------------------------------- /terraform/modules/aws/compute/compute.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module creates all compute resources 3 | #-------------------------------------------------------------- 4 | 5 | variable "name" { } 6 | variable "region" { } 7 | variable "vpc_id" { } 8 | variable "vpc_cidr" { } 9 | variable "key_name" { } 10 | variable "azs" { } 11 | variable "private_subnet_ids" { } 12 | variable "public_subnet_ids" { } 13 | variable "site_ssl_cert" { } 14 | variable "site_ssl_key" { } 15 | variable "vault_ssl_cert" { } 16 | variable "atlas_username" { } 17 | variable "atlas_environment" { } 18 | variable "atlas_aws_global" { } 19 | variable "atlas_token" { } 20 | variable "sub_domain" { } 21 | variable "route_zone_id" { } 22 | variable "vault_token" { default = "" } 23 | 24 | variable "haproxy_amis" { } 25 | variable "haproxy_node_count" { } 26 | variable "haproxy_instance_type" { } 27 | 28 | variable "nodejs_blue_ami" { } 29 | variable "nodejs_blue_node_count" { } 30 | variable "nodejs_blue_instance_type" { } 31 | variable "nodejs_blue_weight" { } 32 | variable "nodejs_green_ami" { } 33 | variable "nodejs_green_node_count" { } 34 | variable "nodejs_green_instance_type" { } 35 | variable "nodejs_green_weight" { } 36 | 37 | module "haproxy" { 38 | source = "./haproxy" 39 | 40 | name = "${var.name}-haproxy" 41 | vpc_id = "${var.vpc_id}" 42 | vpc_cidr = "${var.vpc_cidr}" 43 | key_name = "${var.key_name}" 44 | subnet_ids = "${var.public_subnet_ids}" 45 | atlas_username = "${var.atlas_username}" 46 | atlas_environment = "${var.atlas_environment}" 47 | atlas_token = "${var.atlas_token}" 48 | amis = "${var.haproxy_amis}" 49 | nodes = "${var.haproxy_node_count}" 50 | instance_type = "${var.haproxy_instance_type}" 51 | sub_domain = "${var.sub_domain}" 52 | route_zone_id = "${var.route_zone_id}" 53 | } 54 | 55 | module "nodejs" { 56 | source = "./nodejs" 57 | 58 | name = "${var.name}-nodejs" 59 | region = "${var.region}" 60 | vpc_id = "${var.vpc_id}" 61 | vpc_cidr = "${var.vpc_cidr}" 62 | key_name = "${var.key_name}" 63 | azs = "${var.azs}" 64 | private_subnet_ids = "${var.private_subnet_ids}" 65 | public_subnet_ids = "${var.public_subnet_ids}" 66 | site_ssl_cert = "${var.site_ssl_cert}" 67 | site_ssl_key = "${var.site_ssl_key}" 68 | vault_ssl_cert = "${var.vault_ssl_cert}" 69 | atlas_username = "${var.atlas_username}" 70 | atlas_environment = "${var.atlas_environment}" 71 | atlas_aws_global = "${var.atlas_aws_global}" 72 | atlas_token = "${var.atlas_token}" 73 | blue_weight = "${var.nodejs_blue_weight}" 74 | blue_ami = "${var.nodejs_blue_ami}" 75 | blue_nodes = "${var.nodejs_blue_node_count}" 76 | blue_instance_type = "${var.nodejs_blue_instance_type}" 77 | green_ami = "${var.nodejs_green_ami}" 78 | green_nodes = "${var.nodejs_green_node_count}" 79 | green_instance_type = "${var.nodejs_green_instance_type}" 80 | green_weight = "${var.nodejs_green_weight}" 81 | sub_domain = "${var.sub_domain}" 82 | route_zone_id = "${var.route_zone_id}" 83 | vault_token = "${var.vault_token}" 84 | } 85 | 86 | output "haproxy_private_ips" { value = "${module.haproxy.private_ips}" } 87 | output "haproxy_public_ips" { value = "${module.haproxy.public_ips}" } 88 | output "haproxy_private_fqdn" { value = "${module.haproxy.private_fqdn}" } 89 | output "haproxy_public_fqdn" { value = "${module.haproxy.public_fqdn}" } 90 | 91 | output "nodejs_blue_elb_zone_id" { value = "${module.nodejs.blue_elb_zone_id}" } 92 | output "nodejs_blue_private_fqdn" { value = "${module.nodejs.blue_private_fqdn}" } 93 | output "nodejs_blue_elb_dns" { value = "${module.nodejs.blue_elb_dns}" } 94 | output "nodejs_green_elb_zone_id" { value = "${module.nodejs.green_elb_zone_id}" } 95 | output "nodejs_green_private_fqdn" { value = "${module.nodejs.green_private_fqdn}" } 96 | output "nodejs_green_elb_dns" { value = "${module.nodejs.green_elb_dns}" } 97 | -------------------------------------------------------------------------------- /terraform/modules/aws/compute/haproxy/haproxy.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | NODENAME="${node_name}-$(hostname)" 5 | 6 | echo "Configuring Consul..." 7 | 8 | sed -i -- "s/{{ atlas_username }}/${atlas_username}/g" /etc/consul.d/base.json 9 | sed -i -- "s/{{ atlas_environment }}/${atlas_environment}/g" /etc/consul.d/base.json 10 | sed -i -- "s/{{ atlas_token }}/${atlas_token}/g" /etc/consul.d/base.json 11 | sed -i -- "s/{{ datacenter }}/${atlas_environment}/g" /etc/consul.d/base.json 12 | sed -i -- "s/{{ node_name }}/$NODENAME/g" /etc/consul.d/base.json 13 | 14 | service consul restart 15 | 16 | exit 0 17 | -------------------------------------------------------------------------------- /terraform/modules/aws/compute/haproxy/haproxy.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module creates all resources necessary for HAProxy 3 | #-------------------------------------------------------------- 4 | 5 | variable "name" { default = "haproxy" } 6 | variable "vpc_id" { } 7 | variable "vpc_cidr" { } 8 | variable "key_name" { } 9 | variable "subnet_ids" { } 10 | variable "atlas_username" { } 11 | variable "atlas_environment" { } 12 | variable "atlas_token" { } 13 | variable "amis" { } 14 | variable "nodes" { } 15 | variable "instance_type" { } 16 | variable "sub_domain" { } 17 | variable "route_zone_id" { } 18 | 19 | resource "aws_security_group" "haproxy" { 20 | name = "${var.name}" 21 | vpc_id = "${var.vpc_id}" 22 | description = "HAProxy security group" 23 | 24 | tags { 25 | Name = "${var.name}" 26 | Demo = "true" 27 | } 28 | lifecycle { create_before_destroy = true } 29 | 30 | ingress { 31 | protocol = "tcp" 32 | from_port = 80 33 | to_port = 80 34 | cidr_blocks = ["0.0.0.0/0"] 35 | } 36 | 37 | ingress { 38 | protocol = "tcp" 39 | from_port = 443 40 | to_port = 443 41 | cidr_blocks = ["0.0.0.0/0"] 42 | } 43 | 44 | ingress { 45 | protocol = -1 46 | from_port = 0 47 | to_port = 0 48 | cidr_blocks = ["${var.vpc_cidr}"] 49 | } 50 | 51 | egress { 52 | protocol = -1 53 | from_port = 0 54 | to_port = 0 55 | cidr_blocks = ["0.0.0.0/0"] 56 | } 57 | } 58 | 59 | resource "template_file" "user_data" { 60 | template = "${path.module}/haproxy.sh.tpl" 61 | count = "${var.nodes}" 62 | 63 | lifecycle { create_before_destroy = true } 64 | 65 | vars { 66 | atlas_username = "${var.atlas_username}" 67 | atlas_environment = "${var.atlas_environment}" 68 | atlas_token = "${var.atlas_token}" 69 | node_name = "${var.name}-${count.index+1}" 70 | } 71 | } 72 | 73 | resource "aws_instance" "haproxy" { 74 | ami = "${element(split(",", var.amis), count.index)}" 75 | count = "${var.nodes}" 76 | instance_type = "${var.instance_type}" 77 | key_name = "${var.key_name}" 78 | subnet_id = "${element(split(",", var.subnet_ids), count.index)}" 79 | user_data = "${element(template_file.user_data.*.rendered, count.index)}" 80 | 81 | vpc_security_group_ids = ["${aws_security_group.haproxy.id}"] 82 | 83 | tags { Name = "${var.name}" } 84 | lifecycle { create_before_destroy = true } 85 | } 86 | 87 | resource "aws_route53_record" "haproxy_public" { 88 | zone_id = "${var.route_zone_id}" 89 | name = "haproxy.${var.sub_domain}" 90 | type = "A" 91 | ttl = "300" 92 | records = ["${aws_instance.haproxy.*.public_ip}"] 93 | } 94 | 95 | resource "aws_route53_record" "haproxy_private" { 96 | zone_id = "${var.route_zone_id}" 97 | name = "private.haproxy.${var.sub_domain}" 98 | type = "A" 99 | ttl = "300" 100 | records = ["${aws_instance.haproxy.*.private_ip}"] 101 | } 102 | 103 | output "public_ips" { value = "${join(",", aws_instance.haproxy.*.public_ip)}" } 104 | output "private_ips" { value = "${join(",", aws_instance.haproxy.*.private_ip)}" } 105 | output "public_fqdn" { value = "${aws_route53_record.haproxy_public.fqdn}" } 106 | output "private_fqdn" { value = "${aws_route53_record.haproxy_private.fqdn}" } 107 | -------------------------------------------------------------------------------- /terraform/modules/aws/compute/nodejs/nodejs.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NAME="${node_name}-$(hostname)" 4 | SANITIZEDNAME=$${NAME//-/_} # Replace hyphens with underscores, Consul Template doesn't like hyphens 5 | SSLCERTDIR=/usr/local/etc 6 | SSLSITECERTPATH=$SSLCERTDIR/site.crt 7 | SSLVAULTCERTPATH=$SSLCERTDIR/vault.crt 8 | NODEJSPOLICYNAME=${vault_policy} 9 | NODEJSPOLICY=/opt/vault/policies/$NODEJSPOLICYNAME.json 10 | GENERICSECRETPATH=secret/$NODEJSPOLICYNAME/$SANITIZEDNAME 11 | GENERICSECRETKEY=secret_key 12 | GENERICSECRET="This is a secret stored in Vault for $NAME using the $NODEJSPOLICYNAME policy" 13 | AWSROLEPOLICY=/opt/vault/policies/aws_$NODEJSPOLICYNAME.json 14 | AWSROLEPATH=aws/roles/$NODEJSPOLICYNAME 15 | AWSCREDPATH=aws/creds/$NODEJSPOLICYNAME 16 | VAULT=https://vault.service.consul:8200 17 | CONSUL=http://127.0.0.1:8500 18 | LOGS=/var/log/user_data.log 19 | 20 | logger() { 21 | DT=$(date '+%Y/%m/%d %H:%M:%S') 22 | echo "$DT nodejs.sh: $1" | sudo tee -a $LOGS > /dev/null 23 | } 24 | 25 | preparepolicy() { 26 | FILEPATH=$1 27 | PARAMETER=$2 28 | 29 | sed -i -- 's/\"/\\"/g' $FILEPATH 30 | sed -i '1s/^/{"{{ parameter }}": "/' $FILEPATH 31 | sed -i '$s|$|"}|' $FILEPATH 32 | sed -i -- "s/{{ parameter }}/$PARAMETER/g" $FILEPATH 33 | } 34 | 35 | logger "Configuring Consul..." 36 | 37 | sed -i -- "s/{{ atlas_username }}/${atlas_username}/g" /etc/consul.d/base.json 38 | sed -i -- "s/{{ atlas_environment }}/${atlas_environment}/g" /etc/consul.d/base.json 39 | sed -i -- "s/{{ atlas_token }}/${atlas_token}/g" /etc/consul.d/base.json 40 | sed -i -- "s/{{ datacenter }}/${atlas_environment}/g" /etc/consul.d/base.json 41 | sed -i -- "s/{{ node_name }}/$NAME/g" /etc/consul.d/base.json 42 | sed -i -- "s/{{ deploy }}/${deploy}/g" /etc/consul.d/nodejs.json 43 | 44 | service consul restart 45 | 46 | logger "Updating certs..." 47 | 48 | mkdir -p $SSLCERTDIR 49 | chmod -R 0600 $SSLCERTDIR 50 | 51 | echo "${site_ssl_cert}" | sudo tee $SSLSITECERTPATH > /dev/null 52 | echo "${vault_ssl_cert}" | sudo tee $SSLVAULTCERTPATH > /dev/null 53 | 54 | cp $SSLSITECERTPATH /usr/local/share/ca-certificates/. 55 | cp $SSLVAULTCERTPATH /usr/local/share/ca-certificates/. 56 | update-ca-certificates 57 | 58 | logger "Checking for Vault token..." 59 | 60 | if [[ "x${vault_token}" == "x" || "${vault_token}" == "REPLACE_IN_ATLAS" ]]; then 61 | logger "Setting consul_template retry to 1h and stopping service." 62 | sed -i -- "s/retry = \"5s\"/retry = \"1h\"/g" /etc/consul_template.d/base.hcl 63 | service consul_template stop 64 | 65 | logger "Setting envconsul retry to 1h." 66 | sed -i -- "s/retry = \"5s\"/retry = \"1h\"/g" /etc/envconsul.d/base.hcl 67 | service nodejs restart 68 | 69 | logger "Exiting without setting Vault policy due to no Vault token." 70 | 71 | exit 1 72 | fi 73 | 74 | logger "Waiting for Vault to become ready..." 75 | 76 | SLEEPTIME=1 77 | cget() { curl -sf "$VAULT/v1/sys/health?standbyok"; } 78 | 79 | while ! cget | grep "\"initialized\":true,\"sealed\":false"; do 80 | if [ $SLEEPTIME -gt 15 ]; then 81 | logger "ERROR: VAULT SETUP NOT COMPLETE! Manual intervention required." 82 | exit 2 83 | else 84 | logger "Blocking until Vault is ready, waiting $SLEEPTIME second(s)..." 85 | sleep $SLEEPTIME 86 | SLEEPTIME=$((SLEEPTIME + 1)) 87 | fi 88 | done 89 | 90 | logger "--- Vault Policy Setup ---" 91 | logger "Preparing Vault $NODEJSPOLICYNAME policy..." 92 | 93 | preparepolicy $NODEJSPOLICY rules 94 | 95 | logger "Generating Vault $NODEJSPOLICYNAME policy..." 96 | 97 | logger $( 98 | curl \ 99 | -H "X-Vault-Token: ${vault_token}" \ 100 | -H "Content-Type: application/json" \ 101 | -LX PUT \ 102 | -d @$NODEJSPOLICY \ 103 | $VAULT/v1/sys/policy/$NODEJSPOLICYNAME 104 | ) 105 | 106 | logger "Generating Vault $NODEJSPOLICYNAME token..." 107 | 108 | (cat < /tmp/$NODEJSPOLICYNAME-token.json 119 | 120 | TOKEN=$( 121 | curl \ 122 | -H "X-Vault-Token: ${vault_token}" \ 123 | -H "Content-Type: application/json" \ 124 | -LX POST \ 125 | -d @/tmp/$NODEJSPOLICYNAME-token.json \ 126 | $VAULT/v1/auth/token/create \ 127 | | grep -Po '"client_token":.*?[^\\]",' | awk -F\" '{print $4}' 128 | ) 129 | 130 | rm -rf /tmp/$NODEJSPOLICYNAME-token.json 131 | 132 | SSLVAULTCERTPATH=$${SSLVAULTCERTPATH//\//\\/} 133 | 134 | logger "Update /etc/consul_template.d/nodejs.hcl with vault_token and cert_path" 135 | 136 | sed -i -- "s/{{ vault_token }}/$TOKEN/g" /etc/consul_template.d/nodejs.hcl 137 | sed -i -- "s/{{ cert_path }}/$SSLVAULTCERTPATH/g" /etc/consul_template.d/nodejs.hcl 138 | 139 | logger "Update /etc/envconsul.d/nodejs.hcl with vault_token and cert_path" 140 | 141 | sed -i -- "s/{{ vault_token }}/$TOKEN/g" /etc/envconsul.d/nodejs.hcl 142 | sed -i -- "s/{{ cert_path }}/$SSLVAULTCERTPATH/g" /etc/envconsul.d/nodejs.hcl 143 | 144 | logger "--- Generic Secret Backend Setup ---" 145 | logger "Writing $NAME secret..." 146 | 147 | logger $( 148 | curl \ 149 | -H "X-Vault-Token: ${vault_token}" \ 150 | -H "Content-Type: application/json" \ 151 | -LX POST \ 152 | -d "{\"$GENERICSECRETKEY\": \"$GENERICSECRET\", \"ttl\": \"1m\"}" \ 153 | $VAULT/v1/$GENERICSECRETPATH 154 | ) 155 | 156 | GENERICSECRETPATH=$${GENERICSECRETPATH//\//\\/} 157 | 158 | logger "Update /opt/consul_template/vault_generic.ctmpl" 159 | 160 | sed -i -- "s/{{ node_name }}/$NAME/g" /opt/consul_template/vault_generic.ctmpl 161 | sed -i -- "s/{{ secret_path }}/$GENERICSECRETPATH/g" /opt/consul_template/vault_generic.ctmpl 162 | sed -i -- "s/{{ secret_key }}/$GENERICSECRETKEY/g" /opt/consul_template/vault_generic.ctmpl 163 | 164 | logger "Update /etc/envconsul.d/nodejs.hcl with secret_path" 165 | 166 | sed -i -- "s/{{ secret_path }}/$GENERICSECRETPATH/g" /etc/envconsul.d/nodejs.hcl 167 | 168 | logger "--- AWS Backend Setup ---" 169 | logger "Checking if AWS backend is mounted..." 170 | 171 | AWSMOUNTED=$( 172 | curl \ 173 | -H "X-Vault-Token: ${vault_token}" \ 174 | -LX GET \ 175 | $VAULT/v1/sys/mounts \ 176 | | grep -c "aws" 177 | ) 178 | 179 | echo "AWS backend mount status: $AWSMOUNTED" 180 | 181 | if [ $AWSMOUNTED -eq 0 ]; then 182 | logger "Mounting AWS backend..." 183 | 184 | logger $( 185 | curl \ 186 | -H "X-Vault-Token: ${vault_token}" \ 187 | -H "Content-Type: application/json" \ 188 | -LX POST \ 189 | -d "{\"type\":\"aws\", \"description\":\"dynamic aws iam credentials\"}" \ 190 | $VAULT/v1/sys/mounts/aws 191 | ) 192 | 193 | logger "AWS backend mounted." 194 | else 195 | logger "AWS backend already mounted." 196 | fi 197 | 198 | logger "Writing root IAM credentials..." 199 | 200 | logger $( 201 | curl \ 202 | -H "X-Vault-Token: ${vault_token}" \ 203 | -H "Content-Type: application/json" \ 204 | -LX POST \ 205 | -d "{\"access_key\":\"${aws_access_id}\", \"secret_key\":\"${aws_secret_key}\", \"region\":\"${aws_region}\"}" \ 206 | $VAULT/v1/aws/config/root 207 | ) 208 | 209 | logger "Writing lease settings for generated credentials..." 210 | 211 | logger $( 212 | curl \ 213 | -H "X-Vault-Token: ${vault_token}" \ 214 | -H "Content-Type: application/json" \ 215 | -LX POST \ 216 | -d "{\"lease\": \"1m\", \"lease_max\": \"2m\"}" \ 217 | $VAULT/v1/aws/config/lease 218 | ) 219 | 220 | logger "Preparing Vault AWS $NODEJSPOLICYNAME policy..." 221 | 222 | preparepolicy $AWSROLEPOLICY policy 223 | 224 | logger "Generating Vault AWS $NODEJSPOLICYNAME role..." 225 | 226 | logger $( 227 | curl \ 228 | -H "X-Vault-Token: ${vault_token}" \ 229 | -H "Content-Type: application/json" \ 230 | -LX PUT \ 231 | -d @$AWSROLEPOLICY \ 232 | $VAULT/v1/$AWSROLEPATH 233 | ) 234 | 235 | logger "Update /opt/consul_template/vault_aws.ctmpl" 236 | 237 | AWSCREDPATH=$${AWSCREDPATH//\//\\/} 238 | 239 | sed -i -- "s/{{ node_name }}/$NAME/g" /opt/consul_template/vault_aws.ctmpl 240 | sed -i -- "s/{{ cred_path }}/$AWSCREDPATH/g" /opt/consul_template/vault_aws.ctmpl 241 | 242 | service nodejs restart 243 | service consul_template restart 244 | 245 | logger "Create Consul KV envconsul watches to display Vault secret" 246 | 247 | curl -X PUT -d '1' $CONSUL/v1/kv/service/nodejs/show_vault 248 | curl -X PUT -d 'aws.html,generic.html' $CONSUL/v1/kv/service/nodejs/vault_files 249 | 250 | logger "Node.js configuration complete" 251 | 252 | exit 0 253 | -------------------------------------------------------------------------------- /terraform/modules/aws/compute/nodejs/nodejs.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module creates all resources necessary for the 3 | # Node.js application 4 | #-------------------------------------------------------------- 5 | 6 | variable "name" { default = "nodejs" } 7 | variable "region" { } 8 | variable "vpc_id" { } 9 | variable "vpc_cidr" { } 10 | variable "key_name" { } 11 | variable "azs" { } 12 | variable "private_subnet_ids" { } 13 | variable "public_subnet_ids" { } 14 | variable "site_ssl_cert" { } 15 | variable "site_ssl_key" { } 16 | variable "vault_ssl_cert" { } 17 | variable "atlas_username" { } 18 | variable "atlas_environment" { } 19 | variable "atlas_aws_global" { } 20 | variable "atlas_token" { } 21 | variable "blue_ami" { } 22 | variable "blue_nodes" { } 23 | variable "blue_instance_type" { } 24 | variable "blue_weight" { } 25 | variable "green_ami" { } 26 | variable "green_nodes" { } 27 | variable "green_instance_type" { } 28 | variable "green_weight" { } 29 | variable "sub_domain" { } 30 | variable "route_zone_id" { } 31 | variable "vault_token" { default = "" } 32 | variable "vault_policy" { default = "nodejs" } 33 | 34 | resource "aws_security_group" "elb" { 35 | name = "${var.name}.elb" 36 | vpc_id = "${var.vpc_id}" 37 | description = "Security group for Nodejs ELB" 38 | 39 | tags { Name = "${var.name}-elb" } 40 | lifecycle { create_before_destroy = true } 41 | 42 | ingress { 43 | protocol = "tcp" 44 | from_port = 80 45 | to_port = 80 46 | cidr_blocks = ["0.0.0.0/0"] 47 | } 48 | 49 | ingress { 50 | protocol = "tcp" 51 | from_port = 443 52 | to_port = 443 53 | cidr_blocks = ["0.0.0.0/0"] 54 | } 55 | 56 | egress { 57 | protocol = -1 58 | from_port = 0 59 | to_port = 0 60 | cidr_blocks = ["0.0.0.0/0"] 61 | } 62 | } 63 | 64 | resource "aws_iam_server_certificate" "nodejs" { 65 | name = "${var.region}-${var.name}" 66 | certificate_body = "${var.site_ssl_cert}" 67 | private_key = "${var.site_ssl_key}" 68 | 69 | lifecycle { create_before_destroy = true } 70 | 71 | provisioner "local-exec" { 72 | command = < /dev/null 25 | echo "${ssl_key}" | sudo tee $SSLKEYPATH > /dev/null 26 | 27 | cp "$SSLCERTPATH" /usr/local/share/ca-certificates/. 28 | update-ca-certificates 29 | 30 | echo "Configuring Vault..." 31 | 32 | SSLCERTPATH=$${SSLCERTPATH//\//\\/} 33 | SSLKEYPATH=$${SSLKEYPATH//\//\\/} 34 | 35 | sed -i -- "s/{{ node_name }}/${node_name}/g" /etc/vault.d/vault.hcl 36 | sed -i -- "s/{{ atlas_username }}/${atlas_username}/g" /etc/vault.d/vault.hcl 37 | sed -i -- "s/{{ atlas_environment }}/${atlas_environment}/g" /etc/vault.d/vault.hcl 38 | sed -i -- "s/{{ atlas_token }}/${atlas_token}/g" /etc/vault.d/vault.hcl 39 | sed -i -- "s/{{ tls_cert_file }}/$SSLCERTPATH/g" /etc/vault.d/vault.hcl 40 | sed -i -- "s/{{ tls_key_file }}/$SSLKEYPATH/g" /etc/vault.d/vault.hcl 41 | sed -i -- "s/{{ datacenter }}/${atlas_environment}/g" /etc/vault.d/vault.hcl 42 | 43 | service vault restart 44 | 45 | exit 0 46 | -------------------------------------------------------------------------------- /terraform/modules/aws/data/vault/vault.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module creates all resources necessary for Vault 3 | #-------------------------------------------------------------- 4 | 5 | variable "name" { default = "vault" } 6 | variable "region" { } 7 | variable "vpc_id" { } 8 | variable "vpc_cidr" { } 9 | variable "private_subnet_ids" { } 10 | variable "public_subnet_ids" { } 11 | variable "ssl_cert" { } 12 | variable "ssl_key" { } 13 | variable "key_name" { } 14 | variable "atlas_username" { } 15 | variable "atlas_environment" { } 16 | variable "atlas_token" { } 17 | variable "amis" { } 18 | variable "nodes" { } 19 | variable "instance_type" { } 20 | variable "sub_domain" { } 21 | variable "route_zone_id" { } 22 | 23 | resource "aws_security_group" "vault" { 24 | name = "${var.name}" 25 | vpc_id = "${var.vpc_id}" 26 | description = "Security group for Vault" 27 | 28 | tags { Name = "${var.name}" } 29 | lifecycle { create_before_destroy = true } 30 | 31 | ingress { 32 | protocol = -1 33 | from_port = 0 34 | to_port = 0 35 | cidr_blocks = ["${var.vpc_cidr}"] 36 | } 37 | 38 | egress { 39 | protocol = -1 40 | from_port = 0 41 | to_port = 0 42 | cidr_blocks = ["0.0.0.0/0"] 43 | } 44 | } 45 | 46 | resource "template_file" "user_data" { 47 | count = "${var.nodes}" 48 | template = "${path.module}/vault.sh.tpl" 49 | 50 | lifecycle { create_before_destroy = true } 51 | 52 | vars { 53 | atlas_username = "${var.atlas_username}" 54 | atlas_environment = "${var.atlas_environment}" 55 | atlas_token = "${var.atlas_token}" 56 | node_name = "${var.name}-${count.index+1}" 57 | ssl_cert = "${var.ssl_cert}" 58 | ssl_key = "${var.ssl_key}" 59 | } 60 | } 61 | 62 | resource "aws_instance" "vault" { 63 | count = "${var.nodes}" 64 | ami = "${element(split(",", var.amis), count.index)}" 65 | instance_type = "${var.instance_type}" 66 | key_name = "${var.key_name}" 67 | subnet_id = "${element(split(",", var.private_subnet_ids), count.index)}" 68 | user_data = "${element(template_file.user_data.*.rendered, count.index)}" 69 | 70 | vpc_security_group_ids = ["${aws_security_group.vault.id}"] 71 | 72 | tags { Name = "${var.name}.${count.index+1}" } 73 | } 74 | 75 | resource "aws_security_group" "elb" { 76 | name = "${var.name}-elb" 77 | vpc_id = "${var.vpc_id}" 78 | description = "Security group for Vault ELB" 79 | 80 | tags { Name = "${var.name}-elb" } 81 | 82 | ingress { 83 | protocol = "tcp" 84 | from_port = 80 85 | to_port = 80 86 | cidr_blocks = ["${var.vpc_cidr}"] 87 | } 88 | 89 | ingress { 90 | protocol = "tcp" 91 | from_port = 443 92 | to_port = 443 93 | cidr_blocks = ["${var.vpc_cidr}"] 94 | } 95 | 96 | egress { 97 | protocol = -1 98 | from_port = 0 99 | to_port = 0 100 | cidr_blocks = ["0.0.0.0/0"] 101 | } 102 | } 103 | 104 | resource "aws_iam_server_certificate" "vault" { 105 | name = "${var.region}-${var.name}" 106 | certificate_body = "${var.ssl_cert}" 107 | private_key = "${var.ssl_key}" 108 | 109 | provisioner "local-exec" { 110 | command = < 72 | # https://docs.openvpn.net/how-to-tutorialsguides/virtual-platforms/amazon-ec2-appliance-ami-quick-start-guide/ 73 | user_data = < /dev/null", 90 | "echo '${var.ssl_key}' | sudo tee /usr/local/openvpn_as/etc/web-ssl/server.key > /dev/null", 91 | # Set VPN network info 92 | "sudo /usr/local/openvpn_as/scripts/sacli -k vpn.daemon.0.client.network -v ${element(split("/", var.vpn_cidr), 0)} ConfigPut", 93 | "sudo /usr/local/openvpn_as/scripts/sacli -k vpn.daemon.0.client.netmask_bits -v ${element(split("/", var.vpn_cidr), 1)} ConfigPut", 94 | # Do a warm restart so the config is picked up 95 | "sudo /usr/local/openvpn_as/scripts/sacli start", 96 | ] 97 | } 98 | } 99 | 100 | resource "aws_route53_record" "openvpn" { 101 | zone_id = "${var.route_zone_id}" 102 | name = "vpn.${var.sub_domain}" 103 | type = "A" 104 | ttl = "300" 105 | records = ["${aws_instance.openvpn.public_ip}"] 106 | } 107 | 108 | output "private_ip" { value = "${aws_instance.openvpn.private_ip}" } 109 | output "public_ip" { value = "${aws_instance.openvpn.public_ip}" } 110 | output "public_fqdn" { value = "${aws_route53_record.openvpn.fqdn}" } 111 | -------------------------------------------------------------------------------- /terraform/modules/aws/network/private_subnet/private_subnet.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module creates all resources necessary for a private 3 | # subnet 4 | #-------------------------------------------------------------- 5 | 6 | variable "name" { default = "private"} 7 | variable "vpc_id" { } 8 | variable "cidrs" { } 9 | variable "azs" { } 10 | variable "nat_gateway_ids" { } 11 | 12 | resource "aws_subnet" "private" { 13 | vpc_id = "${var.vpc_id}" 14 | cidr_block = "${element(split(",", var.cidrs), count.index)}" 15 | availability_zone = "${element(split(",", var.azs), count.index)}" 16 | count = "${length(split(",", var.cidrs))}" 17 | 18 | tags { Name = "${var.name}.${element(split(",", var.azs), count.index)}" } 19 | lifecycle { create_before_destroy = true } 20 | } 21 | 22 | resource "aws_route_table" "private" { 23 | vpc_id = "${var.vpc_id}" 24 | count = "${length(split(",", var.cidrs))}" 25 | 26 | route { 27 | cidr_block = "0.0.0.0/0" 28 | nat_gateway_id = "${element(split(",", var.nat_gateway_ids), count.index)}" 29 | } 30 | 31 | tags { Name = "${var.name}.${element(split(",", var.azs), count.index)}" } 32 | lifecycle { create_before_destroy = true } 33 | } 34 | 35 | resource "aws_route_table_association" "private" { 36 | count = "${length(split(",", var.cidrs))}" 37 | subnet_id = "${element(aws_subnet.private.*.id, count.index)}" 38 | route_table_id = "${element(aws_route_table.private.*.id, count.index)}" 39 | 40 | lifecycle { create_before_destroy = true } 41 | } 42 | 43 | output "subnet_ids" { value = "${join(",", aws_subnet.private.*.id)}" } 44 | -------------------------------------------------------------------------------- /terraform/modules/aws/network/public_subnet/public_subnet.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module creates all resources necessary for a public 3 | # subnet 4 | #-------------------------------------------------------------- 5 | 6 | variable "name" { default = "public" } 7 | variable "vpc_id" { } 8 | variable "cidrs" { } 9 | variable "azs" { } 10 | 11 | resource "aws_internet_gateway" "public" { 12 | vpc_id = "${var.vpc_id}" 13 | 14 | tags { Name = "${var.name}" } 15 | } 16 | 17 | resource "aws_subnet" "public" { 18 | vpc_id = "${var.vpc_id}" 19 | cidr_block = "${element(split(",", var.cidrs), count.index)}" 20 | availability_zone = "${element(split(",", var.azs), count.index)}" 21 | count = "${length(split(",", var.cidrs))}" 22 | 23 | tags { Name = "${var.name}.${element(split(",", var.azs), count.index)}" } 24 | lifecycle { create_before_destroy = true } 25 | 26 | map_public_ip_on_launch = true 27 | } 28 | 29 | resource "aws_route_table" "public" { 30 | vpc_id = "${var.vpc_id}" 31 | 32 | route { 33 | cidr_block = "0.0.0.0/0" 34 | gateway_id = "${aws_internet_gateway.public.id}" 35 | } 36 | 37 | tags { Name = "${var.name}.${element(split(",", var.azs), count.index)}" } 38 | } 39 | 40 | resource "aws_route_table_association" "public" { 41 | count = "${length(split(",", var.cidrs))}" 42 | subnet_id = "${element(aws_subnet.public.*.id, count.index)}" 43 | route_table_id = "${aws_route_table.public.id}" 44 | } 45 | 46 | output "subnet_ids" { value = "${join(",", aws_subnet.public.*.id)}" } 47 | -------------------------------------------------------------------------------- /terraform/modules/aws/network/vpc/vpc.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module creates all resources necessary for a VPC 3 | #-------------------------------------------------------------- 4 | 5 | variable "name" { default = "vpc" } 6 | variable "cidr" { } 7 | 8 | resource "aws_vpc" "vpc" { 9 | cidr_block = "${var.cidr}" 10 | enable_dns_support = true 11 | enable_dns_hostnames = true 12 | 13 | tags { Name = "${var.name}" } 14 | lifecycle { create_before_destroy = true } 15 | } 16 | 17 | output "vpc_id" { value = "${aws_vpc.vpc.id}" } 18 | output "vpc_cidr" { value = "${aws_vpc.vpc.cidr_block}" } 19 | -------------------------------------------------------------------------------- /terraform/modules/aws/util/artifact/artifact.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module is used to for creating Atlas artifacts 3 | #-------------------------------------------------------------- 4 | 5 | variable "type" { default = "amazon.ami" } 6 | variable "region" { } 7 | variable "atlas_username" { } 8 | variable "artifact_name" { } 9 | variable "artifact_version" { default = "latest" } 10 | 11 | resource "atlas_artifact" "artifact" { 12 | name = "${var.atlas_username}/${var.artifact_name}" 13 | type = "${var.type}" 14 | count = "${length(split(",", var.artifact_version))}" 15 | version = "${element(split(",", var.artifact_version), count.index)}" 16 | 17 | lifecycle { create_before_destroy = true } 18 | metadata { region = "${var.region}" } 19 | } 20 | 21 | output "amis" { value = "${join(",", atlas_artifact.artifact.*.metadata_full.ami_id)}" } 22 | -------------------------------------------------------------------------------- /terraform/modules/aws/util/deploy/deploy.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module is used to achieve a blue/green deploy strategy 3 | #-------------------------------------------------------------- 4 | 5 | variable "name" { default = "deploy" } 6 | variable "vpc_id" { } 7 | variable "vpc_cidr" { } 8 | variable "key_name" { } 9 | variable "azs" { } 10 | variable "private_subnet_ids" { } 11 | variable "blue_elb_id" { } 12 | variable "blue_ami" { } 13 | variable "blue_nodes" { } 14 | variable "blue_instance_type" { } 15 | variable "blue_user_data" { } 16 | variable "green_elb_id" { } 17 | variable "green_ami" { } 18 | variable "green_nodes" { } 19 | variable "green_instance_type" { } 20 | variable "green_user_data" { } 21 | 22 | resource "aws_security_group" "deploy" { 23 | vpc_id = "${var.vpc_id}" 24 | description = "Security group for ${var.name} Blue/Green deploy Launch Configuration" 25 | 26 | tags { Name = "${var.name}" } 27 | lifecycle { create_before_destroy = true } 28 | 29 | ingress { 30 | protocol = -1 31 | from_port = 0 32 | to_port = 0 33 | cidr_blocks = ["${var.vpc_cidr}"] 34 | } 35 | 36 | egress { 37 | protocol = -1 38 | from_port = 0 39 | to_port = 0 40 | cidr_blocks = ["0.0.0.0/0"] 41 | } 42 | } 43 | 44 | resource "aws_launch_configuration" "blue" { 45 | name_prefix = "${var.name}.blue." 46 | image_id = "${var.blue_ami}" 47 | instance_type = "${var.blue_instance_type}" 48 | key_name = "${var.key_name}" 49 | security_groups = ["${aws_security_group.deploy.id}"] 50 | user_data = "${var.blue_user_data}" 51 | 52 | lifecycle { create_before_destroy = true } 53 | } 54 | 55 | resource "aws_autoscaling_group" "blue" { 56 | name = "${aws_launch_configuration.blue.name}" 57 | launch_configuration = "${aws_launch_configuration.blue.name}" 58 | desired_capacity = "${var.blue_nodes}" 59 | min_size = "${var.blue_nodes}" 60 | max_size = "${var.blue_nodes}" 61 | wait_for_elb_capacity = "${var.blue_nodes}" 62 | availability_zones = ["${split(",", var.azs)}"] 63 | vpc_zone_identifier = ["${split(",", var.private_subnet_ids)}"] 64 | load_balancers = ["${var.blue_elb_id}"] 65 | 66 | lifecycle { create_before_destroy = true } 67 | 68 | tag { 69 | key = "Name" 70 | value = "${var.name}.blue" 71 | propagate_at_launch = true 72 | } 73 | } 74 | 75 | resource "aws_launch_configuration" "green" { 76 | name_prefix = "${var.name}.green." 77 | image_id = "${var.green_ami}" 78 | instance_type = "${var.green_instance_type}" 79 | key_name = "${var.key_name}" 80 | security_groups = ["${aws_security_group.deploy.id}"] 81 | user_data = "${var.green_user_data}" 82 | 83 | lifecycle { create_before_destroy = true } 84 | } 85 | 86 | resource "aws_autoscaling_group" "green" { 87 | name = "${aws_launch_configuration.green.name}" 88 | launch_configuration = "${aws_launch_configuration.green.name}" 89 | desired_capacity = "${var.green_nodes}" 90 | min_size = "${var.green_nodes}" 91 | max_size = "${var.green_nodes}" 92 | wait_for_elb_capacity = "${var.green_nodes}" 93 | availability_zones = ["${split(",", var.azs)}"] 94 | vpc_zone_identifier = ["${split(",", var.private_subnet_ids)}"] 95 | load_balancers = ["${var.green_elb_id}"] 96 | 97 | lifecycle { create_before_destroy = true } 98 | 99 | tag { 100 | key = "Name" 101 | value = "${var.name}.green" 102 | propagate_at_launch = true 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /terraform/modules/aws/util/iam/iam.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module is used to create an AWS IAM group and its users 3 | #-------------------------------------------------------------- 4 | 5 | variable "name" { default = "iam" } 6 | variable "users" { } 7 | variable "policy" { } 8 | 9 | resource "aws_iam_group" "group" { 10 | name = "${var.name}" 11 | } 12 | 13 | resource "aws_iam_group_policy" "policy" { 14 | name = "${var.name}" 15 | group = "${aws_iam_group.group.id}" 16 | policy = "${var.policy}" 17 | } 18 | 19 | resource "aws_iam_user" "user" { 20 | count = "${length(split(",", var.users))}" 21 | name = "${element(split(",", var.users), count.index)}" 22 | } 23 | 24 | resource "aws_iam_access_key" "key" { 25 | count = "${length(split(",", var.users))}" 26 | user = "${element(aws_iam_user.user.*.name, count.index)}" 27 | } 28 | 29 | resource "aws_iam_group_membership" "membership" { 30 | name = "${var.name}" 31 | group = "${aws_iam_group.group.name}" 32 | users = ["${aws_iam_user.user.*.name}"] 33 | } 34 | 35 | output "users" { value = "${join(",", aws_iam_access_key.key.*.user)}" } 36 | output "access_ids" { value = "${join(",", aws_iam_access_key.key.*.id)}" } 37 | output "secret_keys" { value = "${join(",", aws_iam_access_key.key.*.secret)}" } 38 | -------------------------------------------------------------------------------- /terraform/modules/aws/util/website/website.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | # This module is used to create an S3 bucket website 3 | #-------------------------------------------------------------- 4 | 5 | variable "fqdn" { } 6 | variable "sub_domain" { } 7 | variable "route_zone_id" { } 8 | variable "index_page" { default = "index.html" } 9 | variable "error_page" { default = "error.html" } 10 | 11 | resource "aws_s3_bucket" "website" { 12 | bucket = "${var.fqdn}" 13 | acl = "public-read" 14 | force_destroy = true 15 | 16 | website { 17 | index_document = "${var.index_page}" 18 | error_document = "${var.error_page}" 19 | } 20 | 21 | policy = < /dev/null 20 | } 21 | 22 | preparepolicy() { 23 | FILEPATH=$1 24 | PARAMETER=$2 25 | 26 | sed -i -- 's/\"/\\"/g' $FILEPATH 27 | sed -i '1s/^/{"{{ parameter }}": "/' $FILEPATH 28 | sed -i '$s|$|"}|' $FILEPATH 29 | sed -i -- "s/{{ parameter }}/$PARAMETER/g" $FILEPATH 30 | } 31 | 32 | logger "Configuring Consul..." 33 | 34 | sed -i -- "s/{{ atlas_username }}/${atlas_username}/g" /etc/consul.d/base.json 35 | sed -i -- "s/{{ atlas_environment }}/${atlas_environment}/g" /etc/consul.d/base.json 36 | sed -i -- "s/{{ atlas_token }}/${atlas_token}/g" /etc/consul.d/base.json 37 | sed -i -- "s/{{ datacenter }}/${atlas_environment}/g" /etc/consul.d/base.json 38 | sed -i -- "s/{{ node_name }}/${node_name}/g" /etc/consul.d/base.json 39 | sed -i -- "s/{{ deploy }}/${deploy}/g" /etc/consul.d/nodejs.json 40 | 41 | service consul restart 42 | 43 | logger "Updating certs..." 44 | 45 | mkdir -p $SSLCERTDIR 46 | chmod -R 0600 $SSLCERTDIR 47 | 48 | echo "${site_ssl_cert}" | sudo tee $SSLSITECERTPATH > /dev/null 49 | echo "${vault_ssl_cert}" | sudo tee $SSLVAULTCERTPATH > /dev/null 50 | 51 | cp $SSLSITECERTPATH /usr/local/share/ca-certificates/. 52 | cp $SSLVAULTCERTPATH /usr/local/share/ca-certificates/. 53 | update-ca-certificates 54 | 55 | logger "Checking for Vault token..." 56 | 57 | if [[ "x${vault_token}" == "x" || "${vault_token}" == "REPLACE_IN_ATLAS" ]]; then 58 | logger "Setting consul_template retry to 1h and stopping service." 59 | sed -i -- "s/retry = \"5s\"/retry = \"1h\"/g" /etc/consul_template.d/base.hcl 60 | service consul_template stop 61 | 62 | logger "Setting envconsul retry to 1h." 63 | sed -i -- "s/retry = \"5s\"/retry = \"1h\"/g" /etc/envconsul.d/base.hcl 64 | service nodejs restart 65 | 66 | logger "Exiting without setting Vault policy due to no Vault token." 67 | 68 | exit 1 69 | fi 70 | 71 | logger "Waiting for Vault to become ready..." 72 | 73 | SLEEPTIME=1 74 | cget() { curl -sf "$VAULT/v1/sys/health?standbyok"; } 75 | 76 | while ! cget | grep "\"initialized\":true,\"sealed\":false"; do 77 | if [ $SLEEPTIME -gt 15 ]; then 78 | logger "ERROR: VAULT SETUP NOT COMPLETE! Manual intervention required." 79 | exit 2 80 | else 81 | logger "Blocking until Vault is ready, waiting $SLEEPTIME second(s)..." 82 | sleep $SLEEPTIME 83 | SLEEPTIME=$((SLEEPTIME + 1)) 84 | fi 85 | done 86 | 87 | logger "--- Vault Policy Setup ---" 88 | logger "Preparing Vault $NODEJSPOLICYNAME policy..." 89 | 90 | preparepolicy $NODEJSPOLICY rules 91 | 92 | logger "Generating Vault $NODEJSPOLICYNAME policy..." 93 | 94 | logger $( 95 | curl \ 96 | -H "X-Vault-Token: ${vault_token}" \ 97 | -H "Content-Type: application/json" \ 98 | -LX PUT \ 99 | -d @$NODEJSPOLICY \ 100 | $VAULT/v1/sys/policy/$NODEJSPOLICYNAME 101 | ) 102 | 103 | logger "Generating Vault $NODEJSPOLICYNAME token..." 104 | 105 | (cat < /tmp/$NODEJSPOLICYNAME-token.json 116 | 117 | TOKEN=$( 118 | curl \ 119 | -H "X-Vault-Token: ${vault_token}" \ 120 | -H "Content-Type: application/json" \ 121 | -LX POST \ 122 | -d @/tmp/$NODEJSPOLICYNAME-token.json \ 123 | $VAULT/v1/auth/token/create \ 124 | | grep -Po '"client_token":.*?[^\\]",' | awk -F\" '{print $4}' 125 | ) 126 | 127 | rm -rf /tmp/$NODEJSPOLICYNAME-token.json 128 | 129 | SSLVAULTCERTPATH=$${SSLVAULTCERTPATH//\//\\/} 130 | 131 | logger "Update /etc/consul_template.d/nodejs.hcl with vault_token and cert_path" 132 | 133 | sed -i -- "s/{{ vault_token }}/$TOKEN/g" /etc/consul_template.d/nodejs.hcl 134 | sed -i -- "s/{{ cert_path }}/$SSLVAULTCERTPATH/g" /etc/consul_template.d/nodejs.hcl 135 | 136 | logger "Remove AWS specific config from /etc/consul_template.d/" 137 | 138 | rm /etc/consul_template.d/nodejs_aws.hcl 139 | 140 | logger "Update /etc/envconsul.d/nodejs.hcl with vault_token and cert_path" 141 | 142 | sed -i -- "s/{{ vault_token }}/$TOKEN/g" /etc/envconsul.d/nodejs.hcl 143 | sed -i -- "s/{{ cert_path }}/$SSLVAULTCERTPATH/g" /etc/envconsul.d/nodejs.hcl 144 | 145 | logger "--- Generic Secret Backend Setup ---" 146 | logger "Writing $NAME secret..." 147 | 148 | logger $( 149 | curl \ 150 | -H "X-Vault-Token: ${vault_token}" \ 151 | -H "Content-Type: application/json" \ 152 | -LX POST \ 153 | -d "{\"$GENERICSECRETKEY\": \"$GENERICSECRET\", \"ttl\": \"1m\"}" \ 154 | $VAULT/v1/$GENERICSECRETPATH 155 | ) 156 | 157 | GENERICSECRETPATH=$${GENERICSECRETPATH//\//\\/} 158 | 159 | logger "Update /opt/consul_template/vault_generic.ctmpl" 160 | 161 | sed -i -- "s/{{ node_name }}/$NAME/g" /opt/consul_template/vault_generic.ctmpl 162 | sed -i -- "s/{{ secret_path }}/$GENERICSECRETPATH/g" /opt/consul_template/vault_generic.ctmpl 163 | sed -i -- "s/{{ secret_key }}/$GENERICSECRETKEY/g" /opt/consul_template/vault_generic.ctmpl 164 | 165 | logger "Update /etc/envconsul.d/nodejs.hcl with secret_path" 166 | 167 | sed -i -- "s/{{ secret_path }}/$GENERICSECRETPATH/g" /etc/envconsul.d/nodejs.hcl 168 | 169 | service nodejs restart 170 | service consul_template restart 171 | 172 | logger "Create Consul KV envconsul watches to display Vault secret" 173 | 174 | curl -X PUT -d '1' $CONSUL/v1/kv/service/nodejs/show_vault 175 | curl -X PUT -d 'generic.html' $CONSUL/v1/kv/service/nodejs/vault_files 176 | 177 | logger "Node.js configuration complete" 178 | 179 | exit 0 180 | -------------------------------------------------------------------------------- /terraform/modules/google/compute/nodejs/nodejs.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | 3 | # This module creates all resources necessary for the 4 | 5 | # Node.js application 6 | 7 | #-------------------------------------------------------------- 8 | 9 | variable "name" {} 10 | 11 | variable "zones" { 12 | type = "list" 13 | } 14 | 15 | variable "atlas_username" {} 16 | 17 | variable "atlas_environment" {} 18 | 19 | variable "atlas_token" {} 20 | 21 | variable "private_subnet_names" { 22 | type = "list" 23 | } 24 | 25 | variable "public_subnet_names" { 26 | type = "list" 27 | } 28 | 29 | variable "image" {} 30 | 31 | variable "nodes" {} 32 | 33 | variable "instance_type" {} 34 | 35 | variable "site_ssl_cert" {} 36 | 37 | variable "site_ssl_key" {} 38 | 39 | variable "vault_ssl_cert" {} 40 | 41 | variable "vault_token" { 42 | default = "" 43 | } 44 | 45 | variable "vault_policy" { 46 | default = "nodejs" 47 | } 48 | 49 | variable "ssh_keys" {} 50 | 51 | resource "template_file" "nodejs_config" { 52 | template = "${file("${path.module}/nodejs.sh.tpl")}" 53 | count = "${var.nodes}" 54 | 55 | lifecycle { create_before_destroy = true } 56 | 57 | vars { 58 | atlas_username = "${var.atlas_username}" 59 | atlas_environment = "${var.atlas_environment}" 60 | atlas_token = "${var.atlas_token}" 61 | node_name = "${var.name}-${count.index}" 62 | deploy = "deploy" 63 | site_ssl_cert = "${var.site_ssl_cert}" 64 | vault_ssl_cert = "${var.vault_ssl_cert}" 65 | vault_token = "${var.vault_token}" 66 | vault_policy = "${var.vault_policy}" 67 | } 68 | } 69 | 70 | resource "google_compute_instance" "nodejs" { 71 | name = "${var.name}-${count.index}" 72 | count = "${var.nodes}" 73 | machine_type = "${var.instance_type}" 74 | zone = "${element(var.zones, count.index)}" 75 | 76 | metadata_startup_script = "${element(template_file.nodejs_config.*.rendered, count.index)}" 77 | 78 | metadata { 79 | sshKeys = "${var.ssh_keys}" 80 | } 81 | 82 | disk { 83 | image = "${var.image}" 84 | } 85 | 86 | network_interface { 87 | subnetwork = "${element(var.private_subnet_names, count.index)}" 88 | 89 | access_config { 90 | # ephemeral 91 | } 92 | } 93 | 94 | tags = ["nodejs"] 95 | } 96 | 97 | output "private_ips" { 98 | value = ["${google_compute_instance.nodejs.*.network_interface.0.address}"] 99 | } 100 | -------------------------------------------------------------------------------- /terraform/modules/google/data/consul/consul.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "Configuring Consul..." 5 | 6 | sed -i -- "s/{{ atlas_username }}/${atlas_username}/g" /etc/consul.d/base.json 7 | sed -i -- "s/{{ atlas_environment }}/${atlas_environment}/g" /etc/consul.d/base.json 8 | sed -i -- "s/{{ atlas_token }}/${atlas_token}/g" /etc/consul.d/base.json 9 | sed -i -- "s/{{ consul_server_count }}/${consul_server_count}/g" /etc/consul.d/base.json 10 | sed -i -- "s/{{ datacenter }}/${atlas_environment}/g" /etc/consul.d/base.json 11 | sed -i -- "s/{{ node_name }}/${node_name}/g" /etc/consul.d/base.json 12 | 13 | service consul restart 14 | 15 | exit 0 16 | -------------------------------------------------------------------------------- /terraform/modules/google/data/consul/consul.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | 3 | # This module creates all resources necessary for Consul 4 | 5 | #-------------------------------------------------------------- 6 | 7 | variable "name" {} 8 | 9 | variable "project" {} 10 | 11 | variable "region" {} 12 | 13 | variable "zones" { 14 | type = "list" 15 | } 16 | 17 | variable "atlas_username" {} 18 | 19 | variable "atlas_environment" {} 20 | 21 | variable "atlas_token" {} 22 | 23 | variable "private_subnet_names" { 24 | type = "list" 25 | } 26 | 27 | variable "image" {} 28 | 29 | variable "nodes" {} 30 | 31 | variable "instance_type" {} 32 | 33 | variable "ssh_keys" {} 34 | 35 | resource "template_file" "consul_config" { 36 | template = "${file("${path.module}/consul.sh.tpl")}" 37 | count = "${var.nodes}" 38 | 39 | vars { 40 | atlas_username = "${var.atlas_username}" 41 | atlas_environment = "${var.atlas_environment}" 42 | atlas_token = "${var.atlas_token}" 43 | consul_server_count = "${var.nodes}" 44 | node_name = "${var.name}-${count.index}" 45 | } 46 | 47 | lifecycle { 48 | create_before_destroy = true 49 | } 50 | } 51 | 52 | resource "google_compute_instance" "consul" { 53 | name = "${var.name}-${count.index}" 54 | count = "${var.nodes}" 55 | machine_type = "${var.instance_type}" 56 | zone = "${element(var.zones, count.index)}" 57 | 58 | metadata_startup_script = "${element(template_file.consul_config.*.rendered, count.index)}" 59 | 60 | metadata { 61 | sshKeys = "${var.ssh_keys}" 62 | } 63 | 64 | disk { 65 | image = "${var.image}" 66 | } 67 | 68 | network_interface { 69 | subnetwork = "${element(var.private_subnet_names, count.index)}" 70 | 71 | access_config { 72 | # ephemeral 73 | } 74 | } 75 | 76 | tags = ["consul"] 77 | } 78 | 79 | output "private_ips" { 80 | value = ["${google_compute_instance.consul.*.network_interface.0.address}"] 81 | } 82 | -------------------------------------------------------------------------------- /terraform/modules/google/data/data.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | 3 | # This module creates all data resources 4 | 5 | #-------------------------------------------------------------- 6 | 7 | variable "name" {} 8 | 9 | variable "project" {} 10 | 11 | variable "region" {} 12 | 13 | variable "zones" { 14 | type = "list" 15 | } 16 | 17 | variable "atlas_username" {} 18 | 19 | variable "atlas_environment" {} 20 | 21 | variable "atlas_token" {} 22 | 23 | variable "private_subnet_names" { 24 | type = "list" 25 | } 26 | 27 | variable "public_subnet_names" { 28 | type = "list" 29 | } 30 | 31 | variable "consul_image" {} 32 | 33 | variable "consul_node_count" {} 34 | 35 | variable "consul_instance_type" {} 36 | 37 | variable "vault_ssl_cert" {} 38 | 39 | variable "vault_ssl_key" {} 40 | 41 | variable "vault_image" {} 42 | 43 | variable "vault_node_count" {} 44 | 45 | variable "vault_instance_type" {} 46 | 47 | variable "ssh_keys" {} 48 | 49 | module "consul" { 50 | source = "./consul" 51 | 52 | name = "${var.name}-consul" 53 | project = "${var.project}" 54 | region = "${var.region}" 55 | zones = "${var.zones}" 56 | atlas_username = "${var.atlas_username}" 57 | atlas_environment = "${var.atlas_environment}" 58 | atlas_token = "${var.atlas_token}" 59 | private_subnet_names = "${var.private_subnet_names}" 60 | image = "${var.consul_image}" 61 | nodes = "${var.consul_node_count}" 62 | instance_type = "${var.consul_instance_type}" 63 | ssh_keys = "${var.ssh_keys}" 64 | } 65 | 66 | module "vault" { 67 | source = "./vault" 68 | 69 | name = "${var.name}-vault" 70 | project = "${var.project}" 71 | region = "${var.region}" 72 | zones = "${var.zones}" 73 | 74 | atlas_username = "${var.atlas_username}" 75 | atlas_environment = "${var.atlas_environment}" 76 | atlas_token = "${var.atlas_token}" 77 | private_subnet_names = "${var.private_subnet_names}" 78 | public_subnet_names = "${var.public_subnet_names}" 79 | ssl_cert = "${var.vault_ssl_cert}" 80 | ssl_key = "${var.vault_ssl_key}" 81 | image = "${var.vault_image}" 82 | nodes = "${var.vault_node_count}" 83 | instance_type = "${var.vault_instance_type}" 84 | ssh_keys = "${var.ssh_keys}" 85 | } 86 | 87 | # Consul 88 | output "consul_private_ips" { 89 | value = "${module.consul.private_ips}" 90 | } 91 | 92 | # Vault 93 | output "vault_private_ips" { 94 | value = "${module.vault.private_ips}" 95 | } 96 | -------------------------------------------------------------------------------- /terraform/modules/google/data/vault/vault.sh.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | SSLDIR=/usr/local/etc 5 | SSLCERTPATH=$SSLDIR/vault.crt 6 | SSLKEYPATH=$SSLDIR/vault.key 7 | 8 | echo "Configuring Consul..." 9 | 10 | sed -i -- "s/{{ atlas_username }}/${atlas_username}/g" /etc/consul.d/base.json 11 | sed -i -- "s/{{ atlas_environment }}/${atlas_environment}/g" /etc/consul.d/base.json 12 | sed -i -- "s/{{ atlas_token }}/${atlas_token}/g" /etc/consul.d/base.json 13 | sed -i -- "s/{{ datacenter }}/${atlas_environment}/g" /etc/consul.d/base.json 14 | sed -i -- "s/{{ node_name }}/${node_name}/g" /etc/consul.d/base.json 15 | sed -i -- "s/{{ node_name }}/${node_name}/g" /etc/consul.d/vault.json 16 | 17 | service consul restart 18 | 19 | echo "Updating cert..." 20 | 21 | mkdir -p $SSLDIR 22 | chmod -R 0600 $SSLDIR 23 | 24 | echo "${ssl_cert}" | sudo tee $SSLCERTPATH > /dev/null 25 | echo "${ssl_key}" | sudo tee $SSLKEYPATH > /dev/null 26 | 27 | cp "$SSLCERTPATH" /usr/local/share/ca-certificates/. 28 | update-ca-certificates 29 | 30 | echo "Configuring Vault..." 31 | 32 | SSLCERTPATH=$${SSLCERTPATH//\//\\/} 33 | SSLKEYPATH=$${SSLKEYPATH//\//\\/} 34 | 35 | sed -i -- "s/{{ node_name }}/${node_name}/g" /etc/vault.d/vault.hcl 36 | sed -i -- "s/{{ tls_cert_file }}/$SSLCERTPATH/g" /etc/vault.d/vault.hcl 37 | sed -i -- "s/{{ tls_key_file }}/$SSLKEYPATH/g" /etc/vault.d/vault.hcl 38 | 39 | service vault restart 40 | 41 | exit 0 42 | -------------------------------------------------------------------------------- /terraform/modules/google/data/vault/vault.tf: -------------------------------------------------------------------------------- 1 | # This module creates all resources necessary for Vault 2 | 3 | variable "name" {} 4 | 5 | variable "project" {} 6 | 7 | variable "region" {} 8 | 9 | variable "zones" { 10 | type = "list" 11 | } 12 | 13 | variable "atlas_username" {} 14 | 15 | variable "atlas_environment" {} 16 | 17 | variable "atlas_token" {} 18 | 19 | variable "private_subnet_names" { 20 | type = "list" 21 | } 22 | 23 | variable "public_subnet_names" { 24 | type = "list" 25 | } 26 | 27 | variable "image" {} 28 | 29 | variable "nodes" {} 30 | 31 | variable "instance_type" {} 32 | 33 | variable "ssl_cert" {} 34 | 35 | variable "ssl_key" {} 36 | 37 | variable "ssh_keys" {} 38 | 39 | resource "template_file" "vault_config" { 40 | template = "${file("${path.module}/vault.sh.tpl")}" 41 | count = "${var.nodes}" 42 | 43 | vars { 44 | atlas_username = "${var.atlas_username}" 45 | atlas_environment = "${var.atlas_environment}" 46 | atlas_token = "${var.atlas_token}" 47 | node_name = "${var.name}-${count.index}" 48 | ssl_cert = "${var.ssl_cert}" 49 | ssl_key = "${var.ssl_key}" 50 | } 51 | 52 | lifecycle { 53 | create_before_destroy = true 54 | } 55 | } 56 | 57 | resource "google_compute_instance" "vault" { 58 | name = "${var.name}-${count.index}" 59 | count = "${var.nodes}" 60 | machine_type = "${var.instance_type}" 61 | zone = "${element(var.zones, count.index)}" 62 | 63 | metadata_startup_script = "${element(template_file.vault_config.*.rendered, count.index)}" 64 | 65 | metadata { 66 | sshKeys = "${var.ssh_keys}" 67 | } 68 | 69 | disk { 70 | image = "${var.image}" 71 | } 72 | 73 | network_interface { 74 | subnetwork = "${element(var.private_subnet_names, count.index)}" 75 | 76 | access_config { 77 | # ephemeral 78 | } 79 | } 80 | 81 | tags = ["vault"] 82 | } 83 | 84 | output "private_ips" { 85 | value = ["${google_compute_instance.vault.*.network_interface.0.address}"] 86 | } 87 | -------------------------------------------------------------------------------- /terraform/modules/google/network/bastion/bastion.tf: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------- 2 | 3 | # This module creates all resources necessary for a Bastion 4 | 5 | # host 6 | 7 | #-------------------------------------------------------------- 8 | 9 | variable "name" {} 10 | 11 | variable "zones" { 12 | type = "list" 13 | } 14 | 15 | variable "public_subnet_names" { 16 | type = "list" 17 | } 18 | 19 | variable "image" {} 20 | 21 | variable "instance_type" {} 22 | 23 | variable "ssh_keys" {} 24 | 25 | resource "google_compute_instance" "bastion" { 26 | name = "${var.name}" 27 | machine_type = "${var.instance_type}" 28 | zone = "${element(var.zones, 0)}" 29 | 30 | metadata { 31 | sshKeys = "${var.ssh_keys}" 32 | } 33 | 34 | disk { 35 | image = "${var.image}" 36 | } 37 | 38 | network_interface { 39 | subnetwork = "${element(var.public_subnet_names, 0)}" 40 | 41 | access_config { 42 | # ephemeral 43 | } 44 | } 45 | 46 | tags = ["bastion"] 47 | } 48 | 49 | output "private_ip" { 50 | value = "${google_compute_instance.bastion.network_interface.0.address}" 51 | } 52 | 53 | output "public_ip" { 54 | value = "${google_compute_instance.bastion.network_interface.0.access_config.0.assigned_nat_ip}" 55 | } 56 | -------------------------------------------------------------------------------- /terraform/modules/google/network/network.tf: -------------------------------------------------------------------------------- 1 | variable "name" {} 2 | 3 | variable "region" {} 4 | 5 | variable "zones" { 6 | type = "list" 7 | } 8 | 9 | variable "cidr" {} 10 | 11 | variable "public_subnets" { 12 | type = "list" 13 | } 14 | 15 | variable "private_subnets" { 16 | type = "list" 17 | } 18 | 19 | variable "bastion_image" {} 20 | 21 | variable "bastion_instance_type" {} 22 | 23 | variable "ssh_keys" {} 24 | 25 | resource "google_compute_network" "network" { 26 | name = "${var.name}" 27 | } 28 | 29 | resource "google_compute_firewall" "allow-internal" { 30 | name = "${var.name}-allow-internal" 31 | network = "${google_compute_network.network.name}" 32 | 33 | allow { 34 | protocol = "icmp" 35 | } 36 | 37 | allow { 38 | protocol = "tcp" 39 | ports = ["0-65535"] 40 | } 41 | 42 | allow { 43 | protocol = "udp" 44 | ports = ["0-65535"] 45 | } 46 | 47 | source_ranges = [ 48 | "${var.cidr}", 49 | ] 50 | } 51 | 52 | resource "google_compute_firewall" "allow-ssh" { 53 | name = "${var.name}-allow-ssh" 54 | network = "${google_compute_network.network.name}" 55 | 56 | allow { 57 | protocol = "tcp" 58 | ports = ["22"] 59 | } 60 | 61 | source_ranges = ["0.0.0.0/0"] 62 | 63 | # uncomment to restrict public ssh to the bastion host 64 | target_tags = ["bastion"] 65 | } 66 | 67 | module "public_subnet" { 68 | source = "./public_subnet" 69 | 70 | name = "${var.name}-public" 71 | region = "${var.region}" 72 | network = "${google_compute_network.network.self_link}" 73 | cidrs = "${var.public_subnets}" 74 | } 75 | 76 | module "private_subnet" { 77 | source = "./private_subnet" 78 | 79 | name = "${var.name}-private" 80 | region = "${var.region}" 81 | network = "${google_compute_network.network.self_link}" 82 | cidrs = "${var.private_subnets}" 83 | } 84 | 85 | module "bastion" { 86 | source = "./bastion" 87 | 88 | name = "${var.name}-bastion" 89 | zones = "${var.zones}" 90 | public_subnet_names = "${module.public_subnet.subnet_names}" 91 | image = "${var.bastion_image}" 92 | instance_type = "${var.bastion_instance_type}" 93 | ssh_keys = "${var.ssh_keys}" 94 | } 95 | 96 | output "name" { 97 | value = "${google_compute_network.network.name}" 98 | } 99 | 100 | output "public_subnet_names" { 101 | value = "${module.public_subnet.subnet_names}" 102 | } 103 | 104 | output "private_subnet_names" { 105 | value = "${module.private_subnet.subnet_names}" 106 | } 107 | 108 | output "bastion_public_ip" { 109 | value = "${module.bastion.public_ip}" 110 | } 111 | -------------------------------------------------------------------------------- /terraform/modules/google/network/private_subnet/private_subnet.tf: -------------------------------------------------------------------------------- 1 | variable "name" {} 2 | 3 | variable "region" {} 4 | 5 | variable "network" {} 6 | 7 | variable "cidrs" { 8 | type = "list" 9 | } 10 | 11 | resource "google_compute_subnetwork" "private" { 12 | name = "${var.name}-${count.index}" 13 | count = "${length(var.cidrs)}" 14 | ip_cidr_range = "${element(var.cidrs, count.index)}" 15 | network = "${var.network}" 16 | region = "${var.region}" 17 | } 18 | 19 | output "subnet_names" { 20 | value = ["${google_compute_subnetwork.private.*.name}"] 21 | } 22 | -------------------------------------------------------------------------------- /terraform/modules/google/network/public_subnet/public_subnet.tf: -------------------------------------------------------------------------------- 1 | variable "name" {} 2 | 3 | variable "region" {} 4 | 5 | variable "network" {} 6 | 7 | variable "cidrs" { 8 | type = "list" 9 | } 10 | 11 | resource "google_compute_subnetwork" "public" { 12 | name = "${var.name}-${count.index}" 13 | count = "${length(var.cidrs)}" 14 | ip_cidr_range = "${element(var.cidrs, count.index)}" 15 | network = "${var.network}" 16 | region = "${var.region}" 17 | } 18 | 19 | output "subnet_names" { 20 | value = ["${google_compute_subnetwork.public.*.name}"] 21 | } 22 | -------------------------------------------------------------------------------- /terraform/providers/aws/global/global.tf: -------------------------------------------------------------------------------- 1 | variable "domain" { } 2 | variable "atlas_username" { } 3 | variable "atlas_environment" { } 4 | variable "name" { } 5 | variable "region" { } 6 | variable "iam_admins" { } 7 | variable "iam_vault_envs" { } 8 | 9 | provider "aws" { 10 | region = "${var.region}" 11 | } 12 | 13 | atlas { 14 | name = "${var.atlas_username}/${var.atlas_environment}" 15 | } 16 | 17 | module "iam_admin" { 18 | source = "../../../modules/aws/util/iam" 19 | 20 | name = "${var.name}-admin" 21 | users = "${var.iam_admins}" 22 | policy = <