Provisioning AWS Infrastructure
for Security and Continuous Delivery
with Terraform and Elastic Beanstalk

JAX DevOps, London 2018

Stein Inge Morisbak

@steinim

stein.inge.morisbak@BEKK.no

Slides & Repo

 

https://steinim.github.io/slides/aws-terraform-workshop

 

git@github.com:steinim/aws-terraform-workshop.git

 

 

NSB

 

 

Let the cloud vendor cover your *aaS

    On premises

  •  Applications
  •  Data
  •  Runtime
  •  OS
  •  Virtualization 
  •  Servers
  •  Storage
  •  Network

    IaaS (host)

  •  Applications
  •  Data
  •  Runtime
  •  OS
  •  Virtualization 
  •  Servers
  •  Storage
  •  Network

    PaaS (build)

  •  Applications
  •  Data
  •  Runtime
  •  OS
  •  Virtualization 
  •  Servers
  •  Storage
  •  Network

    SaaS (consume)

  •  Applications
  •  Data
  •  Runtime
  •  Os
  •  Virtualization 
  •  Servers
  •  Storage
  •  Network

Use public cloud!

Buy/use services

instead of rolling your own

Automate against APIs

(no clicking in GUI or fiddling in prod)

The juicy stuff

Tools

Verktøykasse

 

 

Passwords, secrets and keys in an automated pipeline

Padlock

 

 

cloud-config.yml


vars:
  - name: AWS_DEFAULT_REGION
    value: eu-central-1

  - name: TF_VAR_env
    value: prod

  - name: TF_VAR_public_key
    value: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzwGCC+nMTL6QIg+Z7SXtIXhiJJ5caa5tZkWQ1E6jDgHp7NqDAb21ZzjK3mGFlugF81h3OBF8uZPE5E+A0mRCB/pMzEMi/SBv0nvuPhijT81OeJxiF11Zxejc6gk8YiJIywQcYD3OcmmWvP2gK7MU1VIf3SQjHEROMaz+4uNJlC6QpDJfepxevQVJ4GVk7uq71NJjXV91gyT4/smOz5dP6tT7dYuP5Zn3lr5VN/BmHmtpklK9AWhwoIyRi+t97T1ihgiDvrmg7QSH9hIM+zKH2oxWi0RGV99t+ac6DV54ys4XD7OLAhHdlL7qV2G1TSIFqXXOaV98Cj2Mkxd2vJhhZ"

secret-vars:
  - name: TF_VAR_db_root_password
    key: app/helloworld/prod/db_root_password

     
passwordstore

pass


~/.password-store
├── AWS_ACCESS_KEY_ID
├── AWS_SECRET_ACCESS_KEY
├── hello
│   ├── prod
│   │   └── db_password.gpg
│   ├── test
│       └── db_password.gpg        
├── nexus
│   └── users
│       ├── admin.gpg
│       └── deployment.gpg
├── ...
│   ├── maven
│       └── settings.xml.gpg
│   └── travis
│       └── buildserver.gpg
...
     

✗ pass show -c hello/test/db_password
     

┌────────────────────────────────────────────────────────────────────────────────────┐
│ Please enter the passphrase to unlock the secret key for the OpenPGP certificate:  │
│ "Stein Inge Morisbak <stein.inge.morisbak@BEKK.no>"                                │
│ 2048-bit RSA key, ID 38380D80,                                                     │
│ created 2014-05-15 (main key ID 0CB573BF).                                         │
│                                                                                    │
│                                                                                    │
│ Passphrase ***********____________________________________________________________ │
│                                                                                    │
│            <OK>                                                  <Cancel>          │
└────────────────────────────────────────────────────────────────────────────────────┘
     

✗ pass show -c hello/test/db_password
Copied hello/test/db_password to clipboard. Will clear in 45 seconds.
     

envchain

envchain

✗ envchain --set aws AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
aws.AWS_ACCESS_KEY_ID: AKIAXXXXXXXXXXXXXXXX
aws.AWS_SECRET_ACCESS_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     

✗ envchain aws env | grep AWS_
AWS_ACCESS_KEY_ID=AKIAXXXXXXXXXXXXXXXX
AWS_SECRET_ACCESS_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     

✗ envchain aws terraform-wrapper apply
     

You are provisioning PROD. Type PROD to continue... PROD
     

Started terraform operation at: 2017-09-08 12:46:57.870942455 +0200 CEST m=+6.052644916

null_resource.upload_efs_backup_script: Refreshing state... (ID: 8456414560025152219)
aws_route53_record.environment_route53_record: Refreshing state... (ID: Z1FHZIDFG100FA_prod.cloud.nsb.no_CNAME)
aws_route53_record.environment_route53_record: Refreshing state... (ID: Z1FHZIDFG100FA_app1.cloud.nsb.no_CNAME)
aws_route53_record.environment_route53_record: Refreshing state... (ID: Z1FHZIDFG100FA_app1.prod.cloud.nsb.no_CNAME)
aws_security_group.app_security_group: Refreshing state... (ID: sg-5f731534)
aws_security_group.efs_backup_security_group: Refreshing state... (ID: sg-279d864c)
aws_route53_record.environment_route53_record: Refreshing state... (ID: Z1FHZIDFG100FA_cloud.nsb.no_A)
aws_security_group.efs_security_group: Refreshing state... (ID: sg-a43d30cf)
aws_iam_user.user: Refreshing state... (ID: nsbno-prod)
aws_iam_role.role: Refreshing state... (ID: prod-nsbno-s3-full-access)
aws_iam_policy.policy: Refreshing state... (ID: arn:aws:iam::635004941268:policy/AmazonS3FullAccess-ElasticBeanstalk-NSBNO-prod)
aws_security_group.db_sg: Refreshing state... (ID: sg-817214ea)
aws_efs_file_system.efs: Refreshing state... (ID: fs-985cb8c1)
...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
     

Source code


./infrastructure          #1
./app-infrastructure...   #2
./app1                    #3
./app2
...
./appN
     

Terraform

Terraform

 

 

What does it do?

  • Documents (infrastructure as code)
  • Plans (no surprises)
  • Graphs (parallelizes where possible)
  • Automates (takes the human out of the equation)

 

 

IaaS

  • Networking
    • VPC
    • Availability zones
    • Public and private subnets
    • Internet gateway
    • NAT
    • Security groups (FW rules)
  • Bastion host

IaaS

./infrastructure

Layout


./infrastructure
├── <environment>
│   ├── main.tf
│   └── vars.tf
└── modules
    ├── vpc
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── vars.tf
    ├── subnets
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── vars.tf
    ├── security-groups
    │   ├── main.tf
    │   └── vars.tf
    ├── instance   
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── vars.tf
    ├── ...
     

Networking

Nettverk

 

 

VPC
Availability Zones
Subnets
DB Subnet Group
Instances
Security group

SaaS

./app-infrastructure

  • RDS - Relational Database Service
  • Route53 (DNS)
  • Certificate Manager (SSL/TLS certificates)
  • EFS - Elastic File System (NFS)
  • S3 (Scalable storage)
  • CloudWatch (monitoring, events, logs)
  • CloudTrail (audit)
  • IAM (Identity & Access Management)

Layout


./app-infrastructure
└── modules
    ├── rds
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── vars.tf
    └── security_groups
        ├── main.tf
        ├── outputs.tf
        └── vars.tf
     
DB

PaaS

{./app1,./app2,...}

Elastic Beanstalk

Elastic Beanstalk

Elastic Beanstalk

Easy to begin, Impossible to outgrow
    Automates and manages:
  • configuration
  • deploy
  • capacity provisioning
  • load balancing
  • auto scaling
  • monitoring
  • Without extra cost beyond the resources you use

3 commands


eb create
eb deploy
eb terminate
     

./create.sh


#!/bin/bash
...
envchain aws eb create ${env}-helloworld \
  --keyname ${env} \
  --vpc.id ${vpc_id} \
  --vpc.dbsubnets ${private_subnet_ids} \
  --vpc.ec2subnets ${private_subnet_ids}  \
  --vpc.elbpublic \
  --vpc.elbsubnets ${public_subnet_ids} \
  --vpc.securitygroups ${security_group_ids} \
  --instance_type t2.micro \
  --platform java-8 \
  --cname ${env}-helloworld \
  --scale 2
...
     

Thank you!

Tutorial:
https://github.com/steinim/aws-terraform-workshop

Slides:
https://steinim.github.io/slides/aws-terraform-workshop

Stein Inge Morisbak

@steinim

stein.inge.morisbak@BEKK.no

Extra


.<app1>
├──.ebextensions
|  ├── 00-set-timezone.config
|  ├── 03-secure-listener.config
|  ├── 05-cw-logging.config
|  └── ...
├── Procfile
├── create.sh
├── deploy.sh
├── terminate.sh
     
.ebextensions/00-set-timezone.config

commands:
  link_Oslo:
    command: "ln -sf /usr/share/zoneinfo/Europe/Oslo /etc/localtime"
     
.ebextensions/
01-set-number-of-file-descriptors.config

files:
  "/etc/security/limits.d/00-webapp.conf":
    content: |
      webapp           soft    nofile          65535
      webapp           hard    nofile          65535
     
.ebextensions/03-securelistener.config

option_settings:
  aws:elb:listener:443:
    SSLCertificateId: arn:aws:acm:eu-central-1:635004941268:certificate/6502eb35-99a7-4004-8e47-a5a0ec194c6a
    ListenerProtocol: HTTPS
    InstancePort: 80
  aws:elb:listener:80:
    ListenerEnabled: false
  aws:elasticbeanstalk:application:
    Application Healthcheck URL: /health
     
.ebextensions/05-cw-logging.config

option_settings:
  aws:elasticbeanstalk:cloudwatch:logs:
    StreamLogs: true
    DeleteOnTerminate: false
    RetentionInDays: 7
     

Tens of thousands of metrics

CloudTrail

CloudTrail

Automating SSH Audit

SSH Audit

Thank you!

Tutorial:
https://github.com/steinim/aws-terraform-workshop

Slides:
https://steinim.github.io/slides/aws-terraform-workshop

Stein Inge Morisbak

@steinim

stein.inge.morisbak@BEKK.no