stacco 0.1.10 → 0.1.17

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,67 @@
1
+ require 'stacco/template'
2
+
3
+ class Stacco::Template::Base < Stacco::Template
4
+ def cloudfront_hosted_zone_map
5
+ {"us-west-2" => {"HostedZoneId" => "Z2FDTNDATAQYW2"}}
6
+ end
7
+
8
+ def ubuntu_daily_ami_map(want_release, *args)
9
+ wants = [args.include?(:amd64), args.include?(:hvm), args.include?(:instance_store)]
10
+ ami_map = {}
11
+
12
+ doc = Nokogiri::XML.parse(open("https://cloud-images.ubuntu.com/rss/#{want_release}-Daily.xml"))
13
+ Nokogiri::HTML.parse(doc.xpath("//item").sort_by{ |it| Time.strptime(it.css("pubDate").text, "%a, %b %d %H:%M:%S %Z %Y") }.last.css('description').children[0].text).xpath('//p')[1].children.find_all{ |el| el.text? }[1..-1].each do |ln|
14
+ region, ami, arch, storage = ln.text.strip.split("\t")
15
+ is_hvm = storage.include?("hvm")
16
+ is_instance_store = storage.include?("instance")
17
+ is_sixtyfourbit = (arch == 'amd64')
18
+ next unless [is_sixtyfourbit, is_hvm, is_instance_store] == wants
19
+
20
+ ami_map[region] = {"AMI" => ami}
21
+ end
22
+
23
+ ami_map
24
+ end
25
+
26
+ def initialize
27
+ super
28
+
29
+ mapping [:ec2, :AMIsByRegion], ubuntu_daily_ami_map('trusty', :amd64)
30
+ mapping [:cloud_front, :hosted_zones_by_region], cloudfront_hosted_zone_map
31
+
32
+ parameter StackVar[:domain], :string,
33
+ min_length: 5,
34
+ max_length: 256
35
+
36
+ parameter StackVar[:environment], :string,
37
+ allowed_values: [:production, :staging, :development]
38
+
39
+ parameter StackVar[:hosted_zone], :string,
40
+ min_length: 13,
41
+ max_length: 16,
42
+ allowed_pattern: /[A-Z0-9]+/
43
+
44
+ parameter StackVar[:keypair], :string,
45
+ min_length: 1,
46
+ max_length: 256,
47
+ allowed_pattern: /[-a-z0-9]+/
48
+
49
+ parameter Var[:ec2, :common_user_data, :before], :string
50
+ parameter Var[:ec2, :common_user_data, :after], :string
51
+
52
+ vpc = resource Type[:ec2, :vpc],
53
+ cidr_block: '10.100.0.0/16',
54
+ instance_tenancy: 'default',
55
+ enable_dns_support: true,
56
+ enable_dns_hostnames: true
57
+
58
+ internet_gateway = resource Type[:ec2, :internet_gateway]
59
+
60
+ dhcp_options = resource Type[:ec2, :dhcp_options],
61
+ domain_name: StackVar[:domain],
62
+ domain_name_servers: [:amazon_provided_dns]
63
+
64
+ attach vpc, internet_gateway
65
+ attach vpc, dhcp_options
66
+ end
67
+ end
@@ -0,0 +1,85 @@
1
+ require 'ostruct'
2
+ require 'base64'
3
+ require 'shellwords'
4
+
5
+ require 'json'
6
+ require 'erb'
7
+
8
+ require 'stacco/template'
9
+
10
+ class Stacco::Template::Old
11
+ class RenderContext < OpenStruct
12
+ def initialize(stack, config, vars)
13
+ @stack = stack
14
+ @config = config
15
+ super(vars)
16
+ end
17
+
18
+ def j(str)
19
+ str.to_json
20
+ end
21
+
22
+ def ja(indent_n, lns)
23
+ indent = " " * indent_n
24
+ newline = "\n".to_json
25
+ lns.map do |ln|
26
+ if ln.chomp.empty?
27
+ "%s \n" % [indent]
28
+ else
29
+ "%s%s, %s,\n" % [indent, ln.chomp.to_json, newline]
30
+ end
31
+ end.join[indent_n .. -3]
32
+ end
33
+ end
34
+
35
+ def udscript(roles, vars)
36
+ roles = roles.map{ |role| role.intern }
37
+
38
+ unless roles.include? :backend
39
+ vars = vars.dup
40
+ vars.delete 'wallet_data'
41
+ end
42
+
43
+ lns = []
44
+ lns.concat(vars.map do |k, v|
45
+ var_val = v.include?("\0") ? Base64.encode64(v) : v
46
+ "export #{k.to_s.upcase}=#{Shellwords.escape(var_val)}\n"
47
+ end)
48
+ lns.concat Stacco::Resources::CloudInit::CommonScripts[:before]
49
+ roles.each{ |role| lns.concat Stacco::Resources::CloudInit::RoleScripts[role] }
50
+ lns.concat Stacco::Resources::CloudInit::CommonScripts[:after]
51
+ lns
52
+ end
53
+
54
+ def initialize
55
+ end
56
+
57
+ def to_json(opts = {})
58
+ @stack = opts.delete(:stack)
59
+ @template_body = @stack.cloudformation_template_body
60
+
61
+ vars = @stack.config
62
+
63
+ cloudformation_vars = {}
64
+ cloudformation_vars.update vars
65
+ cloudformation_vars.update vars['cloudformation']
66
+
67
+ cloudinit_vars = {}
68
+ cloudinit_vars.update vars
69
+ cloudinit_vars.update vars['cloud-init']
70
+ cloudinit_vars.each do |k, v|
71
+ cloudinit_vars.delete(k) if v.kind_of?(Hash) or v.kind_of?(Array)
72
+ end
73
+
74
+ cloudformation_vars['userdata'] = {}
75
+ cloudformation_vars['roles'].each do |host_name, host_roles|
76
+ cloudformation_vars['userdata'][host_name] = udscript(host_roles, cloudinit_vars)
77
+ end
78
+
79
+ cloudformation_vars['userdata_before_script'] = Stacco::Resources::CloudInit::CommonScripts[:before]
80
+ cloudformation_vars['userdata_after_script'] = Stacco::Resources::CloudInit::CommonScripts[:after]
81
+
82
+ b = RenderContext.new(@stack, vars, cloudformation_vars).instance_eval{ binding }
83
+ ERB.new(@template_body).result(b)
84
+ end
85
+ end
@@ -1,4 +1,5 @@
1
1
  # hit aws bootstrapping webhook
2
+ if [ -n "${AWS_WAIT_HANDLE}" ]; then
2
3
  unique_id=$(uuidgen -t)
3
4
 
4
5
  aws_wait_handle_response_file="/tmp/cf-wait-handle-response.json"
@@ -13,5 +14,6 @@ EOF
13
14
 
14
15
  curl -s -T "${aws_wait_handle_response_file}" "${AWS_WAIT_HANDLE}"
15
16
  rm "${aws_wait_handle_response_file}"
17
+ fi
16
18
 
17
19
  echo "done bootstrapping!"
@@ -1,21 +1,23 @@
1
- # configure hostname from metadata service
2
- public_hostname_response_code=404
3
- while [ "${public_hostname_response_code}" != "200" ]; do
4
- public_hostname_response_code=$(curl -sL -w "%{http_code}" "http://169.254.169.254/latest/meta-data/public-hostname" -o /dev/null)
5
- sleep 3
1
+ # ensure the nat is up before configuring
2
+ while true; do
3
+ nat_status=$(curl -s -o /dev/null -I -w "%{http_code}" -m 2 http://bex-status.s3.amazonaws.com/status.json)
4
+ if [ "${nat_status}" = "200" ]; then
5
+ break
6
+ fi
7
+ sleep 2
6
8
  done
7
9
 
8
- public_hostname=$(curl http://169.254.169.254/latest/meta-data/public-hostname)
10
+
11
+ # configure hostname from metadata service
9
12
  private_hostname=$(curl http://169.254.169.254/latest/meta-data/hostname)
13
+ echo "${private_hostname}" > /etc/hostname
10
14
 
11
- echo "${public_hostname}" > /etc/hostname
12
15
  hostname -b -F /etc/hostname
13
-
14
16
  short_hostname=$(hostname -s)
15
17
 
16
18
  sed -i '/127\.0\.0\.1/d' /etc/hosts
17
19
  cat >>/etc/hosts <<EOF
18
- 127.0.0.1 localhost ${short_hostname} ${public_hostname} ${private_hostname}
20
+ 127.0.0.1 localhost ${short_hostname} ${private_hostname}
19
21
  EOF
20
22
 
21
23
 
@@ -37,14 +37,14 @@ cat >"/usr/local/openvpn_as/etc/config.json" <<EOF
37
37
  "admin_ui.https.ip_address": "eth0",
38
38
  "admin_ui.https.port": "943",
39
39
  "aui.eula_version": "2",
40
- "auth.ldap.0.name": "My LDAP servers",
40
+ "auth.ldap.0.name": "My LDAP Servers",
41
41
  "auth.ldap.0.ssl_verify": "never",
42
42
  "auth.ldap.0.timeout": "4",
43
43
  "auth.ldap.0.use_ssl": "never",
44
44
  "auth.module.type": "pam",
45
45
  "auth.pam.0.service": "openvpnas",
46
46
  "auth.radius.0.acct_enable": "false",
47
- "auth.radius.0.name": "My Radius servers",
47
+ "auth.radius.0.name": "My Radius Servers",
48
48
  "cs.cws_proto_v2": "true",
49
49
  "cs.https.ip_address": "eth0",
50
50
  "cs.https.port": "943",
@@ -52,35 +52,41 @@ cat >"/usr/local/openvpn_as/etc/config.json" <<EOF
52
52
  "host.name": "vpn.${DOMAIN}",
53
53
  "sa.initial_run_groups.0": "web_group",
54
54
  "sa.initial_run_groups.1": "openvpn_group",
55
+ "vpn.client.config_text": "",
55
56
  "vpn.client.routing.inter_client": "false",
56
57
  "vpn.client.routing.reroute_dns": "true",
57
58
  "vpn.client.routing.reroute_gw": "false",
58
- "vpn.daemon.0.client.netmask_bits": "20",
59
- "vpn.daemon.0.client.network": "172.27.224.0",
59
+ "vpn.daemon.0.client.netmask_bits": "24",
60
+ "vpn.daemon.0.client.network": "10.0.23.0",
60
61
  "vpn.daemon.0.listen.ip_address": "eth0",
61
62
  "vpn.daemon.0.listen.port": "443",
62
63
  "vpn.daemon.0.listen.protocol": "tcp",
63
64
  "vpn.daemon.0.server.ip_address": "eth0",
65
+ "vpn.general.osi_layer": "3",
66
+ "vpn.server.config_text": "",
64
67
  "vpn.server.daemon.enable": "true",
65
- "vpn.server.daemon.tcp.n_daemons": 1,
68
+ "vpn.server.daemon.tcp.n_daemons": "1",
66
69
  "vpn.server.daemon.tcp.port": "443",
67
- "vpn.server.daemon.udp.n_daemons": 1,
70
+ "vpn.server.daemon.udp.n_daemons": "1",
68
71
  "vpn.server.daemon.udp.port": "1194",
69
- "vpn.server.group_pool.0": "172.27.240.0/20",
72
+ "vpn.server.duplicate_cn": "true",
73
+ "vpn.server.group_pool.0": "10.0.23.0/24",
70
74
  "vpn.server.port_share.enable": "true",
71
75
  "vpn.server.port_share.ip_address": "1.2.3.4",
72
76
  "vpn.server.port_share.port": "1234",
73
77
  "vpn.server.port_share.service": "admin+client",
78
+ "vpn.server.routing.allow_private_nets_to_clients": "true",
74
79
  "vpn.server.routing.gateway_access": "true",
75
80
  "vpn.server.routing.private_access": "nat",
76
- "vpn.server.routing.private_network.0": "10.100.0.0/24",
77
- "vpn.server.routing.private_network.1": "10.100.1.0/24",
81
+ "vpn.server.routing.private_network.0": "10.0.0.0/24",
82
+ "vpn.server.routing.private_network.1": "10.0.1.0/24",
78
83
  "vpn.tls_refresh.do_reauth": "true",
79
84
  "vpn.tls_refresh.interval": "360"
80
85
  },
81
86
  "_INTERNAL": {
82
87
  "run_api.active_profile": "Default",
83
- "webui.edit_profile": "Default"
88
+ "webui.edit_profile": "Default",
89
+ "webui.welcome_shown": "true"
84
90
  }
85
91
  }
86
92
  EOF
@@ -1,13 +1,4 @@
1
- <%
2
- ubuntu_daily_ami = "ami-6ac2a85a"
3
- docker_library_snapshot = "snap-d37e4721"
4
- %>
5
-
6
- {
7
- "AWSTemplateFormatVersion": "2010-09-09",
8
- "Description": <%= j @stack.description %>,
9
-
10
- "Resources": {
1
+ class Stacco::Template::BexNG < Stacco::Template::Base
11
2
 
12
3
  "VPC": {"Type": "AWS::EC2::VPC", "Properties": {
13
4
  "CidrBlock": "10.100.0.0/16",
@@ -320,13 +311,6 @@
320
311
  "arn:aws:iam::", {"Ref": "AWS::AccountId"}, ":server-certificate/",
321
312
  <%= j frontend_cert_path %>, ".pem"
322
313
  ]]}
323
- }],
324
-
325
- "Policies": [{
326
- "PolicyName": "EnableProxyProtocol",
327
- "PolicyType": "ProxyProtocolPolicyType",
328
- "Attributes": [{"Name": "ProxyProtocol", "Value": "true"}],
329
- "InstancePorts": ["80"]
330
314
  }]
331
315
  }},
332
316
 
@@ -437,13 +421,6 @@
437
421
  "arn:aws:iam::", {"Ref": "AWS::AccountId"}, ":server-certificate/",
438
422
  <%= j backend_cert_path %>, ".pem"
439
423
  ]]}
440
- }],
441
-
442
- "Policies": [{
443
- "PolicyName": "EnableProxyProtocol",
444
- "PolicyType": "ProxyProtocolPolicyType",
445
- "Attributes": [{"Name": "ProxyProtocol", "Value": "true"}],
446
- "InstancePorts": ["80"]
447
424
  }]
448
425
  }},
449
426
 
@@ -1,313 +1,453 @@
1
1
  <%
2
2
  ubuntu_daily_ami = "ami-6ac2a85a"
3
3
  docker_library_snapshot = "snap-d37e4721"
4
+ layers_enabled = Set[:bastion, :instances, :api, :static]
4
5
  %>
5
6
 
6
7
  {
7
- "AWSTemplateFormatVersion": "2010-09-09",
8
+ "AWSTemplateFormatVersion" : "2010-09-09",
8
9
  "Description": <%= j @stack.description %>,
9
10
 
10
- "Resources": {
11
-
12
- "VPC": {"Type": "AWS::EC2::VPC", "Properties": {
13
- "CidrBlock": "10.100.0.0/16",
14
- "InstanceTenancy": "default",
15
- "EnableDnsSupport": true,
16
- "EnableDnsHostnames": true
17
- }},
18
-
19
- "InternetGateway": {"Type": "AWS::EC2::InternetGateway", "Properties": {
20
- }},
21
-
22
- "InternetGatewayAttachment": {"Type": "AWS::EC2::VPCGatewayAttachment", "Properties": {
23
- "VpcId": {"Ref": "VPC"},
24
- "InternetGatewayId": {"Ref": "InternetGateway"}
25
- }},
26
-
27
- "NetworkAcl": {"Type": "AWS::EC2::NetworkAcl", "Properties": {
28
- "VpcId": {"Ref": "VPC"}
29
- }},
30
-
31
- "PublicSubnet": {"Type": "AWS::EC2::Subnet", "Properties": {
32
- "VpcId": {"Ref": "VPC"},
33
- "CidrBlock": "10.100.0.0/24"
34
- }},
35
- "PublicSubnetAcl": {"Type": "AWS::EC2::SubnetNetworkAclAssociation", "Properties": {
36
- "NetworkAclId": {"Ref": "NetworkAcl"},
37
- "SubnetId": {"Ref": "PublicSubnet"}
38
- }},
39
- "PublicRouteTable": {"Type": "AWS::EC2::RouteTable", "Properties": {
40
- "VpcId": {"Ref": "VPC"}
41
- }},
42
- "PublicSubnetRoute": {"Type": "AWS::EC2::SubnetRouteTableAssociation", "Properties": {
43
- "SubnetId": {"Ref": "PublicSubnet"},
44
- "RouteTableId": {"Ref": "PublicRouteTable"}
45
- }},
46
- "PublicSubnetGatewayRoute": {"Type": "AWS::EC2::Route", "DependsOn": "InternetGatewayAttachment", "Properties": {
47
- "DestinationCidrBlock": "0.0.0.0/0",
48
- "RouteTableId": {"Ref": "PublicRouteTable"},
49
- "GatewayId": {"Ref": "InternetGateway"}
50
- }},
51
- "PublicIngressAcl": {"Type": "AWS::EC2::NetworkAclEntry", "Properties": {
52
- "NetworkAclId": {"Ref": "NetworkAcl"},
53
- "RuleNumber": "100",
54
-
55
- "CidrBlock": "0.0.0.0/0",
56
- "Protocol": "-1",
57
- "RuleAction": "allow"
58
- }},
59
- "PublicEgressAcl": {"Type": "AWS::EC2::NetworkAclEntry", "Properties": {
60
- "NetworkAclId": {"Ref": "NetworkAcl"},
61
- "RuleNumber": "100",
62
-
63
- "Egress": true,
64
- "CidrBlock": "0.0.0.0/0",
65
- "Protocol": "-1",
66
- "RuleAction": "allow"
67
- }},
11
+ "Parameters" : {
12
+ "NATInstanceType" : {
13
+ "Description" : "NAT Device EC2 instance type",
14
+ "Type" : "String",
15
+ "Default" : "m3.medium"
16
+ }
17
+ },
18
+
19
+ "Mappings" : {
20
+ "AWSNATAMI" : {
21
+ "us-east-1" : { "AMI" : "ami-c6699baf" },
22
+ "us-west-2" : { "AMI" : "ami-52ff7262" },
23
+ "us-west-1" : { "AMI" : "ami-3bcc9e7e" },
24
+ "eu-west-1" : { "AMI" : "ami-0b5b6c7f" },
25
+ "ap-southeast-1" : { "AMI" : "ami-02eb9350" },
26
+ "ap-southeast-2" : { "AMI" : "ami-ab990e91" },
27
+ "ap-northeast-1" : { "AMI" : "ami-14d86d15" },
28
+ "sa-east-1" : { "AMI" : "ami-0439e619" }
29
+ },
68
30
 
69
- "PrivateSubnet": {"Type": "AWS::EC2::Subnet", "Properties": {
70
- "VpcId": {"Ref": "VPC"},
71
- "AvailabilityZone": {"Fn::GetAtt": ["PublicSubnet", "AvailabilityZone"]},
72
- "CidrBlock": "10.100.1.0/24"
73
- }},
74
- "PrivateSubnetAcl": {"Type": "AWS::EC2::SubnetNetworkAclAssociation", "Properties": {
75
- "NetworkAclId": {"Ref": "NetworkAcl"},
76
- "SubnetId": {"Ref": "PrivateSubnet"}
77
- }},
78
- "PrivateRouteTable": {"Type": "AWS::EC2::RouteTable", "Properties": {
79
- "VpcId": {"Ref": "VPC"}
80
- }},
81
- "PrivateSubnetRoute": {"Type": "AWS::EC2::SubnetRouteTableAssociation", "Properties": {
82
- "SubnetId": {"Ref": "PrivateSubnet"},
83
- "RouteTableId": {"Ref": "PrivateRouteTable"}
84
- }},
85
- "PrivateSubnetGatewayRoute": {"Type": "AWS::EC2::Route", "DependsOn": "InternetGatewayAttachment", "Properties": {
86
- "DestinationCidrBlock": "0.0.0.0/0",
87
- "RouteTableId": {"Ref": "PrivateRouteTable"},
88
- "GatewayId": {"Ref": "InternetGateway"}
89
- }},
31
+ "SubnetConfig" : {
32
+ "VPC" : { "CIDR" : "10.0.0.0/16" },
33
+ "Public" : { "CIDR" : "10.0.0.0/24" },
34
+ "Private" : { "CIDR" : "10.0.1.0/24" }
35
+ }
36
+ },
37
+
38
+ "Resources" : {
39
+
40
+ "VPC" : {
41
+ "Type" : "AWS::EC2::VPC",
42
+ "Properties" : {
43
+ "CidrBlock" : { "Fn::FindInMap" : [ "SubnetConfig", "VPC", "CIDR" ]},
44
+ "EnableDnsSupport": "true",
45
+ "EnableDnsHostnames": "true",
46
+ "Tags" : [
47
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
48
+ { "Key" : "Network", "Value" : "Public" }
49
+ ]
50
+ }
51
+ },
90
52
 
91
53
  "DHCPOptions": {"Type": "AWS::EC2::DHCPOptions", "Properties": {
92
54
  "DomainName": <%= j domain %>,
93
55
  "DomainNameServers": ["AmazonProvidedDNS"]
94
56
  }},
57
+
95
58
  "VPCDHCPOptionsAssociation": {"Type": "AWS::EC2::VPCDHCPOptionsAssociation", "Properties": {
96
59
  "VpcId": {"Ref": "VPC"},
97
60
  "DhcpOptionsId": {"Ref": "DHCPOptions"}
98
61
  }},
99
62
 
63
+ "PublicSubnet" : {
64
+ "Type" : "AWS::EC2::Subnet",
65
+ "Properties" : {
66
+ "VpcId" : { "Ref" : "VPC" },
67
+ "CidrBlock" : { "Fn::FindInMap" : [ "SubnetConfig", "Public", "CIDR" ]},
68
+ "Tags" : [
69
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
70
+ { "Key" : "Network", "Value" : "Public" }
71
+ ]
72
+ }
73
+ },
100
74
 
75
+ "InternetGateway" : {
76
+ "Type" : "AWS::EC2::InternetGateway",
77
+ "Properties" : {
78
+ "Tags" : [
79
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
80
+ { "Key" : "Network", "Value" : "Public" }
81
+ ]
82
+ }
83
+ },
101
84
 
85
+ "GatewayToInternet" : {
86
+ "Type" : "AWS::EC2::VPCGatewayAttachment",
87
+ "Properties" : {
88
+ "VpcId" : { "Ref" : "VPC" },
89
+ "InternetGatewayId" : { "Ref" : "InternetGateway" }
90
+ }
91
+ },
102
92
 
93
+ "PublicRouteTable" : {
94
+ "Type" : "AWS::EC2::RouteTable",
95
+ "Properties" : {
96
+ "VpcId" : { "Ref" : "VPC" },
97
+ "Tags" : [
98
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
99
+ { "Key" : "Network", "Value" : "Public" }
100
+ ]
101
+ }
102
+ },
103
103
 
104
+ "PublicRoute" : {
105
+ "Type" : "AWS::EC2::Route",
106
+ "Properties" : {
107
+ "RouteTableId" : { "Ref" : "PublicRouteTable" },
108
+ "DestinationCidrBlock" : "0.0.0.0/0",
109
+ "GatewayId" : { "Ref" : "InternetGateway" }
110
+ }
111
+ },
104
112
 
105
-
106
- "AppS3Bucket": {"Type": "AWS::S3::Bucket", "Properties": {
107
- "BucketName": <%= j domain %>,
108
- "AccessControl": "PublicRead",
109
- "WebsiteConfiguration": {
110
- "IndexDocument": "index.html",
111
- "ErrorDocument": "404.html"
113
+ "PublicSubnetRouteTableAssociation" : {
114
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
115
+ "Properties" : {
116
+ "SubnetId" : { "Ref" : "PublicSubnet" },
117
+ "RouteTableId" : { "Ref" : "PublicRouteTable" }
112
118
  }
113
- }},
119
+ },
114
120
 
115
- "AppS3BucketDistribution": {"Type": "AWS::CloudFront::Distribution", "Properties": {
116
- "DistributionConfig": {
117
- "Aliases": [<%= j domain %>],
118
- "Enabled": "true",
121
+ "PublicNetworkAcl" : {
122
+ "Type" : "AWS::EC2::NetworkAcl",
123
+ "Properties" : {
124
+ "VpcId" : { "Ref" : "VPC" },
125
+ "Tags" : [
126
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
127
+ { "Key" : "Network", "Value" : "Public" }
128
+ ]
129
+ }
130
+ },
119
131
 
120
- "Origins": [{
121
- "Id": "AppS3BucketOrigin",
122
- "DomainName": {"Fn::Join": ["", [<%= j domain %>, ".s3-website-", {"Ref": "AWS::Region"}, ".amazonaws.com"]]},
132
+ "InboundHTTPPublicNetworkAclEntry" : {
133
+ "Type" : "AWS::EC2::NetworkAclEntry",
134
+ "Properties" : {
135
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
136
+ "RuleNumber" : "100",
137
+ "Protocol" : "6",
138
+ "RuleAction" : "allow",
139
+ "Egress" : "false",
140
+ "CidrBlock" : "0.0.0.0/0",
141
+ "PortRange" : { "From" : "80", "To" : "80" }
142
+ }
143
+ },
123
144
 
124
- "CustomOriginConfig": {
125
- "HTTPPort": "80",
126
- "HTTPSPort": "443",
127
- "OriginProtocolPolicy": "http-only"
128
- }
129
- }],
145
+ "InboundHTTPSPublicNetworkAclEntry" : {
146
+ "Type" : "AWS::EC2::NetworkAclEntry",
147
+ "Properties" : {
148
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
149
+ "RuleNumber" : "101",
150
+ "Protocol" : "6",
151
+ "RuleAction" : "allow",
152
+ "Egress" : "false",
153
+ "CidrBlock" : "0.0.0.0/0",
154
+ "PortRange" : { "From" : "443", "To" : "443" }
155
+ }
156
+ },
130
157
 
131
- "DefaultCacheBehavior": {
132
- "TargetOriginId": "AppS3BucketOrigin",
133
- "ForwardedValues": {"QueryString": "false"},
134
- "ViewerProtocolPolicy": "redirect-to-https"
135
- }
158
+ "InboundSSHPublicNetworkAclEntry" : {
159
+ "Type" : "AWS::EC2::NetworkAclEntry",
160
+ "Properties" : {
161
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
162
+ "RuleNumber" : "102",
163
+ "Protocol" : "6",
164
+ "RuleAction" : "allow",
165
+ "Egress" : "false",
166
+ "CidrBlock" : "0.0.0.0/0",
167
+ "PortRange" : { "From" : "22", "To" : "22" }
136
168
  }
137
- }},
169
+ },
138
170
 
139
- "AppS3BucketDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
140
- "HostedZoneId": <%= j hosted_zone %>,
141
- "Type": "A",
142
- "Name": <%= j "#{domain}." %>,
143
- "AliasTarget": {
144
- "HostedZoneId": <%= j @config['cloudfront']['hosted_zone'] %>,
145
- "DNSName": {"Fn::GetAtt": ["AppS3BucketDistribution", "DomainName"]}
146
- }
147
- }},
171
+ "InboundEmphemeralPublicNetworkAclEntry" : {
172
+ "Type" : "AWS::EC2::NetworkAclEntry",
173
+ "Properties" : {
174
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
175
+ "RuleNumber" : "103",
176
+ "Protocol" : "6",
177
+ "RuleAction" : "allow",
178
+ "Egress" : "false",
179
+ "CidrBlock" : "0.0.0.0/0",
180
+ "PortRange" : { "From" : "1024", "To" : "65535" }
181
+ }
182
+ },
148
183
 
149
- "WwwS3Bucket": {"Type": "AWS::S3::Bucket", "Properties": {
150
- "BucketName": <%= j "www.#{domain}" %>,
151
- "AccessControl": "PublicRead",
152
- "WebsiteConfiguration": {
153
- "RedirectAllRequestsTo": {
154
- "HostName": <%= j domain %>,
155
- "Protocol": "https"
156
- }
184
+ "InboundVPNAccessPublicNetworkAclEntry" : {
185
+ "Type" : "AWS::EC2::NetworkAclEntry",
186
+ "Properties" : {
187
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
188
+ "RuleNumber" : "104",
189
+ "Protocol" : "-1",
190
+ "RuleAction" : "allow",
191
+ "Egress" : "false",
192
+ "CidrBlock" : "0.0.0.0/0",
193
+ "PortRange" : { "From" : "1192", "To" : "1194" }
157
194
  }
158
- }},
195
+ },
159
196
 
160
- "WwwS3BucketDistribution": {"Type": "AWS::CloudFront::Distribution", "Properties": {
161
- "DistributionConfig": {
162
- "Aliases": [<%= j "www.#{domain}" %>],
163
- "Enabled": "true",
197
+ "InboundVPNAdministrationPublicNetworkAclEntry" : {
198
+ "Type" : "AWS::EC2::NetworkAclEntry",
199
+ "Properties" : {
200
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
201
+ "RuleNumber" : "105",
202
+ "Protocol" : "-1",
203
+ "RuleAction" : "allow",
204
+ "Egress" : "false",
205
+ "CidrBlock" : "0.0.0.0/0",
206
+ "PortRange" : { "From" : "943", "To" : "943" }
207
+ }
208
+ },
164
209
 
165
- "Origins": [{
166
- "Id": "WwwS3BucketOrigin",
167
- "DomainName": {"Fn::Join": ["", [<%= j "www.#{domain}" %>, ".s3-website-", {"Ref": "AWS::Region"}, ".amazonaws.com"]]},
210
+ "OutboundPublicNetworkAclEntry" : {
211
+ "Type" : "AWS::EC2::NetworkAclEntry",
212
+ "Properties" : {
213
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
214
+ "RuleNumber" : "100",
215
+ "Protocol" : "-1",
216
+ "RuleAction" : "allow",
217
+ "Egress" : "true",
218
+ "CidrBlock" : "0.0.0.0/0",
219
+ "PortRange" : { "From" : "0", "To" : "65535" }
220
+ }
221
+ },
168
222
 
169
- "CustomOriginConfig": {
170
- "HTTPPort": "80",
171
- "HTTPSPort": "443",
172
- "OriginProtocolPolicy": "http-only"
173
- }
174
- }],
223
+ "PublicSubnetNetworkAclAssociation" : {
224
+ "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
225
+ "Properties" : {
226
+ "SubnetId" : { "Ref" : "PublicSubnet" },
227
+ "NetworkAclId" : { "Ref" : "PublicNetworkAcl" }
228
+ }
229
+ },
175
230
 
176
- "DefaultCacheBehavior": {
177
- "TargetOriginId": "WwwS3BucketOrigin",
178
- "ForwardedValues": {"QueryString": "false"},
179
- "ViewerProtocolPolicy": "redirect-to-https"
180
- }
231
+ "PrivateSubnet" : {
232
+ "Type" : "AWS::EC2::Subnet",
233
+ "Properties" : {
234
+ "VpcId" : { "Ref" : "VPC" },
235
+ "AvailabilityZone": {"Fn::GetAtt": ["PublicSubnet", "AvailabilityZone"]},
236
+ "CidrBlock" : { "Fn::FindInMap" : [ "SubnetConfig", "Private", "CIDR" ]},
237
+ "Tags" : [
238
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
239
+ { "Key" : "Name", "Value" : "Private" }
240
+ ]
181
241
  }
182
- }},
242
+ },
183
243
 
184
- "WwwS3BucketDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
185
- "HostedZoneId": <%= j hosted_zone %>,
186
- "Type": "CNAME", "TTL": "300",
187
- "Name": <%= j "www.#{domain}." %>,
188
- "ResourceRecords": [{"Fn::GetAtt": ["WwwS3BucketDistribution", "DomainName"]}]
189
- }},
244
+ "PrivateRouteTable" : {
245
+ "Type" : "AWS::EC2::RouteTable",
246
+ "Properties" : {
247
+ "VpcId" : { "Ref" : "VPC" },
248
+ "Tags" : [
249
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
250
+ { "Key" : "Network", "Value" : "Private" }
251
+ ]
252
+ }
253
+ },
190
254
 
255
+ "PrivateSubnetRouteTableAssociation" : {
256
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
257
+ "Properties" : {
258
+ "SubnetId" : { "Ref" : "PrivateSubnet" },
259
+ "RouteTableId" : { "Ref" : "PrivateRouteTable" }
260
+ }
261
+ },
191
262
 
192
- "AdminS3Bucket": {"Type": "AWS::S3::Bucket", "Properties": {
193
- "BucketName": <%= j "admin.#{domain}" %>,
194
- "AccessControl": "PublicRead",
195
- "WebsiteConfiguration": {
196
- "IndexDocument": "index.html",
197
- "ErrorDocument": "404.html"
263
+ "PrivateRoute" : {
264
+ "Type" : "AWS::EC2::Route",
265
+ "Properties" : {
266
+ "RouteTableId" : { "Ref" : "PrivateRouteTable" },
267
+ "DestinationCidrBlock" : "0.0.0.0/0",
268
+ "InstanceId" : { "Ref" : "NATDevice" }
198
269
  }
199
- }},
270
+ },
200
271
 
201
- "AdminS3BucketDistribution": {"Type": "AWS::CloudFront::Distribution", "Properties": {
202
- "DistributionConfig": {
203
- "Aliases": [<%= j "admin.#{domain}" %>],
204
- "Enabled": "true",
272
+ "PrivateNetworkAcl" : {
273
+ "Type" : "AWS::EC2::NetworkAcl",
274
+ "Properties" : {
275
+ "VpcId" : { "Ref" : "VPC" },
276
+ "Tags" : [
277
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackName" } },
278
+ { "Key" : "Network", "Value" : "Private" }
279
+ ]
280
+ }
281
+ },
205
282
 
206
- "Origins": [{
207
- "Id": "AdminS3BucketOrigin",
208
- "DomainName": {"Fn::Join": ["", [<%= j "admin.#{domain}" %>, ".s3-website-", {"Ref": "AWS::Region"}, ".amazonaws.com"]]},
283
+ "InboundPrivateNetworkAclEntry" : {
284
+ "Type" : "AWS::EC2::NetworkAclEntry",
285
+ "Properties" : {
286
+ "NetworkAclId" : { "Ref" : "PrivateNetworkAcl" },
287
+ "RuleNumber" : "100",
288
+ "Protocol" : "-1",
289
+ "RuleAction" : "allow",
290
+ "Egress" : "false",
291
+ "CidrBlock" : "0.0.0.0/0",
292
+ "PortRange" : { "From" : "0", "To" : "65535" }
293
+ }
294
+ },
209
295
 
210
- "CustomOriginConfig": {
211
- "HTTPPort": "80",
212
- "HTTPSPort": "443",
213
- "OriginProtocolPolicy": "http-only"
214
- }
215
- }],
296
+ "OutboundPrivateNetworkAclEntry" : {
297
+ "Type" : "AWS::EC2::NetworkAclEntry",
298
+ "Properties" : {
299
+ "NetworkAclId" : { "Ref" : "PrivateNetworkAcl" },
300
+ "RuleNumber" : "100",
301
+ "Protocol" : "-1",
302
+ "RuleAction" : "allow",
303
+ "Egress" : "true",
304
+ "CidrBlock" : "0.0.0.0/0",
305
+ "PortRange" : { "From" : "0", "To" : "65535" }
306
+ }
307
+ },
216
308
 
217
- "DefaultCacheBehavior": {
218
- "TargetOriginId": "AdminS3BucketOrigin",
219
- "ForwardedValues": {"QueryString": "false"},
220
- "ViewerProtocolPolicy": "redirect-to-https"
221
- }
309
+ "PrivateSubnetNetworkAclAssociation" : {
310
+ "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
311
+ "Properties" : {
312
+ "SubnetId" : { "Ref" : "PrivateSubnet" },
313
+ "NetworkAclId" : { "Ref" : "PrivateNetworkAcl" }
222
314
  }
223
- }},
315
+ },
224
316
 
225
- "AdminS3BucketDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
226
- "HostedZoneId": <%= j hosted_zone %>,
227
- "Type": "CNAME", "TTL": "300",
228
- "Name": <%= j "admin.#{domain}." %>,
229
- "ResourceRecords": [{"Fn::GetAtt": ["AdminS3BucketDistribution", "DomainName"]}]
230
- }},
231
317
 
232
318
 
233
319
 
234
320
 
235
- "InstanceBootstrapWaitHandle": {"Type": "AWS::CloudFormation::WaitConditionHandle", "Properties": {}},
236
- "InstanceBootstrap": {"Type": "AWS::CloudFormation::WaitCondition", "DependsOn": ["Frontend", "Backend", "OpenVPN"], "Properties": {
237
- "Handle": {"Ref": "InstanceBootstrapWaitHandle"},
238
- "Count": "3",
239
- "Timeout": "6000"
240
- }},
241
321
 
322
+ "NATIPAddress" : {
323
+ "Type" : "AWS::EC2::EIP",
324
+ "DependsOn" : "GatewayToInternet",
325
+ "Properties" : {
326
+ "Domain" : "vpc",
327
+ "InstanceId" : { "Ref" : "NATDevice" }
328
+ }
329
+ },
242
330
 
331
+ "NATDevice" : {
332
+ "Type" : "AWS::EC2::Instance",
333
+ "Properties" : {
334
+ "InstanceType" : { "Ref" : "NATInstanceType" },
335
+ "SubnetId" : { "Ref" : "PublicSubnet" },
336
+ "SourceDestCheck" : "false",
337
+ "ImageId" : { "Fn::FindInMap" : [ "AWSNATAMI", { "Ref" : "AWS::Region" }, "AMI" ]},
338
+ "SecurityGroupIds" : [{ "Ref" : "NATSecurityGroup" }],
243
339
 
244
- "Frontend": {"Type": "AWS::EC2::Instance", "Properties": {
245
- "InstanceType": "m1.small",
246
- "ImageId": <%= j ubuntu_daily_ami %>,
340
+ "UserData": {"Fn::Base64": {"Fn::Join": ["", [
341
+ "#!/bin/bash -e\n",
342
+ "export AWS_WAIT_HANDLE='", {"Ref": "NATDeviceConfigurationWaitHandle"}, "'\n",
343
+ <%= ja 8, userdata_after_script %>
344
+ ]]}}
345
+ }
346
+ },
247
347
 
248
- "Tags": [
249
- { "Key": "Name", "Value": "web01" },
250
- { "Key": "Environment", "Value": <%= j environment %> }
251
- ],
348
+ "NATDeviceConfigurationWaitHandle": {"Type": "AWS::CloudFormation::WaitConditionHandle", "Properties": {}},
252
349
 
253
- "KeyName": <%= j @stack.iam_keypair_name %>,
350
+ "NATDeviceConfiguration": {"Type": "AWS::CloudFormation::WaitCondition", "DependsOn": ["NATDevice"], "Properties": {
351
+ "Handle": {"Ref": "NATDeviceConfigurationWaitHandle"},
352
+ "Count": "1",
353
+ "Timeout": "1200"
354
+ }},
254
355
 
255
- "BlockDeviceMappings": [
256
- {"DeviceName": "/dev/xvdc", "Ebs": {
257
- "SnapshotId": <%= j docker_library_snapshot %>,
258
- "VolumeSize": "50"
259
- }}
260
- ],
356
+ "NATSecurityGroup" : {
357
+ "Type" : "AWS::EC2::SecurityGroup",
358
+ "Properties" : {
359
+ "GroupDescription" : "Enable internal access to the NAT device",
360
+ "VpcId" : { "Ref" : "VPC" },
361
+ "SecurityGroupIngress" : [
362
+ {"IpProtocol": "-1", "FromPort": "0", "ToPort": "65535", "CidrIp": "0.0.0.0/0"}
363
+ ],
364
+ "SecurityGroupEgress" : [
365
+ {"IpProtocol": "-1", "FromPort": "0", "ToPort": "65535", "CidrIp": "0.0.0.0/0"}
366
+ ]
367
+ }
368
+ },
261
369
 
262
- "NetworkInterfaces": [{
263
- "Description": "Primary network interface",
264
- "DeviceIndex": 0,
265
- "DeleteOnTermination": true,
266
370
 
371
+ <% if layers_enabled.member? :bastion %>
372
+ "BastionInstanceSecurityGroup" : {
373
+ "Type" : "AWS::EC2::SecurityGroup",
374
+ "Properties" : {
375
+ "GroupDescription" : "Allow the application instances to access the NAT device",
376
+ "VpcId" : { "Ref" : "VPC" },
377
+
378
+ "SecurityGroupIngress": [
379
+ {"IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": "0.0.0.0/0"},
380
+ {"IpProtocol": "tcp", "FromPort": "443", "ToPort": "443", "CidrIp": "0.0.0.0/0"},
381
+ {"IpProtocol": "udp", "FromPort": "1192", "ToPort": "1194", "CidrIp": "0.0.0.0/0"},
382
+ {"IpProtocol": "tcp", "FromPort": "943", "ToPort": "943", "CidrIp": "0.0.0.0/0"},
383
+ {"IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": "0.0.0.0/0"}
384
+ ],
385
+ "SecurityGroupEgress": [
386
+ {"IpProtocol": "-1", "CidrIp": "0.0.0.0/0"}
387
+ ]
388
+ }
389
+ },
390
+
391
+ "BastionInstance" : {
392
+ "Type" : "AWS::EC2::Instance",
393
+ "Properties" : {
394
+ "InstanceType": "m1.small",
395
+ "ImageId": <%= j ubuntu_daily_ami %>,
396
+ "SourceDestCheck": "false",
397
+
398
+ "NetworkInterfaces": [{
399
+ "DeviceIndex": "0",
400
+ "AssociatePublicIpAddress": "true",
401
+ "DeleteOnTermination": "true",
267
402
  "SubnetId": {"Ref": "PublicSubnet"},
268
- "GroupSet": [{"Ref": "FrontendSecurityGroup"}]
269
- }],
403
+ "GroupSet" : [{"Ref" : "BastionInstanceSecurityGroup"}]
404
+ }],
270
405
 
271
- "UserData": {"Fn::Base64": {"Fn::Join": ["", [
272
- "#!/bin/bash -e\n",
273
- "export AWS_WAIT_HANDLE='", {"Ref": "InstanceBootstrapWaitHandle"}, "'\n",
274
- "export BACKEND_HOST='", {"Fn::GetAtt": ["Backend", "PrivateDnsName"]}, "'\n",
275
- <%= ja 8, userdata['frontend'] %>
276
- ]]}}
406
+ "KeyName": <%= j @stack.iam_keypair_name %>,
407
+ "UserData": {"Fn::Base64": {"Fn::Join": ["", [
408
+ "#!/bin/bash -e\n",
409
+ <%= ja 8, userdata['bastion'] %>
410
+ ]]}}
411
+ }
412
+ },
413
+
414
+ "BastionDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
415
+ "HostedZoneId": <%= j hosted_zone %>,
416
+ "Type": "CNAME", "TTL": "300",
417
+ "Name": <%= j "vpn.#{domain}." %>,
418
+ "ResourceRecords": [{"Fn::GetAtt": ["BastionInstance", "PublicDnsName"]}]
277
419
  }},
420
+ <% end %>
421
+
422
+
423
+
278
424
 
279
- "FrontendSecurityGroup": {"Type": "AWS::EC2::SecurityGroup", "Properties": {
280
- "GroupDescription": "Enable SSH access via port 22 and HTTP via 80",
425
+
426
+
427
+ <% if layers_enabled.member? :api %>
428
+ "ClientAPISecurityGroup": {"Type": "AWS::EC2::SecurityGroup", "Properties": {
429
+ "GroupDescription": "Frontend API security group",
281
430
  "VpcId": {"Ref": "VPC"},
282
431
 
283
432
  "SecurityGroupIngress": [
284
- {"IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": <%= j external_ssh_cidr %>},
285
- {"IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": <%= j bastion_ssh_cidr %>},
286
- {"IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": "10.100.0.0/24"}
433
+ {"IpProtocol": "tcp", "FromPort": "443", "ToPort": "443", "CidrIp": "0.0.0.0/0"}
287
434
  ],
288
- "SecurityGroupEgress": [{"IpProtocol": "-1", "CidrIp": "0.0.0.0/0"}]
289
- }},
290
-
291
- "FrontendEIP": {"Type": "AWS::EC2::EIP", "Properties": {
292
- "Domain": "VPC"
293
- }},
294
-
295
- "FrontendEIPAssociation": {"Type": "AWS::EC2::EIPAssociation", "Properties": {
296
- "AllocationId": {"Fn::GetAtt": ["FrontendEIP", "AllocationId"]},
297
- "InstanceId": {"Ref": "Frontend"}
435
+ "SecurityGroupEgress": [
436
+ {"IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": "0.0.0.0/0"}
437
+ ]
298
438
  }},
299
439
 
300
- "FrontendEndpoint": {"Type": "AWS::ElasticLoadBalancing::LoadBalancer", "Properties": {
301
- "Instances": [{"Ref": "Frontend"}],
440
+ "ClientAPILoadBalancer": {"Type": "AWS::ElasticLoadBalancing::LoadBalancer", "Properties": {
441
+ "Instances": [{"Ref": "FrontendInstance"}],
302
442
  "Subnets": [{"Ref": "PublicSubnet"}],
303
- "SecurityGroups": [{"Ref": "FrontendEndpointSecurityGroup"}],
443
+ "SecurityGroups": [{"Ref": "ClientAPISecurityGroup"}],
304
444
 
305
445
  "HealthCheck": {
306
- "HealthyThreshold": "5",
307
- "Interval": "30",
446
+ "HealthyThreshold": "3",
447
+ "Interval": "15",
308
448
  "Target": "HTTP:80/v1/info",
309
449
  "Timeout": "5",
310
- "UnhealthyThreshold": "2"
450
+ "UnhealthyThreshold": "3"
311
451
  },
312
452
 
313
453
  "Listeners": [{
@@ -317,107 +457,43 @@
317
457
  "InstanceProtocol": "TCP",
318
458
 
319
459
  "SSLCertificateId": {"Fn::Join": ["", [
320
- "arn:aws:iam::", {"Ref": "AWS::AccountId"}, ":server-certificate/",
321
- <%= j frontend_cert_path %>, ".pem"
460
+ "arn:aws:iam::", {"Ref": "AWS::AccountId"}, ":server-certificate/cloudfront/",
461
+ <%= j @stack.name %>, "/", <%= j frontend_cert_path %>
322
462
  ]]}
323
463
  }]
324
464
  }},
325
465
 
326
- "FrontendEndpointDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
466
+ "ClientAPIDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
327
467
  "HostedZoneId": <%= j hosted_zone %>,
328
468
  "Type": "CNAME", "TTL": "300",
329
469
  "Name": <%= j "api.#{domain}." %>,
330
- "ResourceRecords": [{"Fn::GetAtt": ["FrontendEndpoint", "DNSName"]}]
470
+ "ResourceRecords": [{"Fn::GetAtt": ["ClientAPILoadBalancer", "DNSName"]}]
331
471
  }},
332
472
 
333
- "FrontendEndpointSecurityGroup": {"Type": "AWS::EC2::SecurityGroup", "Properties": {
334
- "GroupDescription": "Frontend ELB security group",
473
+ "AdminAPISecurityGroup": {"Type": "AWS::EC2::SecurityGroup", "Properties": {
474
+ "GroupDescription": "Admin API security group",
335
475
  "VpcId": {"Ref": "VPC"},
336
476
 
337
477
  "SecurityGroupIngress": [
338
- {"IpProtocol": "tcp", "FromPort": "443", "ToPort": "443", "CidrIp": "0.0.0.0/0"}
478
+ {"IpProtocol": "tcp", "FromPort": "443", "ToPort": "443", "CidrIp": "10.0.0.0/16"}
339
479
  ],
340
480
  "SecurityGroupEgress": [
341
481
  {"IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": "0.0.0.0/0"}
342
482
  ]
343
483
  }},
344
484
 
345
-
346
-
347
-
348
- "Backend": {"Type": "AWS::EC2::Instance", "Properties": {
349
- "InstanceType": "m3.medium",
350
- "ImageId": <%= j ubuntu_daily_ami %>,
351
-
352
- "Tags": [
353
- { "Key": "Name", "Value": "app01" },
354
- { "Key": "Environment", "Value": <%= j environment %> }
355
- ],
356
-
357
- "KeyName": <%= j @stack.iam_keypair_name %>,
358
-
359
- "BlockDeviceMappings": [
360
- {"DeviceName": "/dev/xvdc", "Ebs": {
361
- "SnapshotId": <%= j docker_library_snapshot %>,
362
- "VolumeSize": "50"
363
- }},
364
-
365
- {"DeviceName": "/dev/xvdd", "Ebs": {
366
- "VolumeSize": "300"
367
- }}
368
- ],
369
-
370
- "NetworkInterfaces": [{
371
- "Description": "Primary network interface",
372
- "DeviceIndex": 0,
373
- "DeleteOnTermination": true,
374
-
375
- "SubnetId": {"Ref": "PrivateSubnet"},
376
- "GroupSet": [{"Ref": "BackendSecurityGroup"}]
377
- }],
378
-
379
- "UserData": {"Fn::Base64": {"Fn::Join": ["", [
380
- "#!/bin/bash -e\n",
381
- "export AWS_WAIT_HANDLE='", {"Ref": "InstanceBootstrapWaitHandle"}, "'\n",
382
- <%= ja 8, userdata['backend'] %>
383
- ]]}}
384
- }},
385
-
386
- "BackendSecurityGroup": {"Type": "AWS::EC2::SecurityGroup", "Properties": {
387
- "GroupDescription": "Enable SSH access via port 22",
388
- "VpcId": {"Ref": "VPC"},
389
-
390
- "SecurityGroupIngress": [
391
- {"IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": <%= j external_ssh_cidr %>},
392
- {"IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": <%= j bastion_ssh_cidr %>},
393
- {"IpProtocol": "tcp", "FromPort": "51607", "ToPort": "51607", "CidrIp": "10.100.0.0/24"}
394
- ],
395
- "SecurityGroupEgress": [
396
- {"IpProtocol": "-1", "CidrIp": "0.0.0.0/0"}
397
- ]
398
- }
399
- },
400
-
401
- "BackendEIP": {"Type": "AWS::EC2::EIP", "Properties": {
402
- "Domain": "VPC"
403
- }},
404
- "BackendEIPAssociation": {"Type": "AWS::EC2::EIPAssociation", "Properties": {
405
- "AllocationId": {"Fn::GetAtt": ["BackendEIP", "AllocationId"]},
406
- "InstanceId": {"Ref": "Backend"}
407
- }},
408
-
409
- "BackendEndpoint": {"Type": "AWS::ElasticLoadBalancing::LoadBalancer", "Properties": {
410
- "Instances": [{"Ref": "Backend"}],
411
- "Subnets": [{"Ref": "PrivateSubnet"}],
412
- "SecurityGroups": [{"Ref": "BackendEndpointSecurityGroup"}],
485
+ "AdminAPILoadBalancer": {"Type": "AWS::ElasticLoadBalancing::LoadBalancer", "Properties": {
486
+ "Instances": [{"Ref": "BackendInstance"}],
487
+ "Subnets": [{"Ref": "PublicSubnet"}],
413
488
  "Scheme": "internal",
489
+ "SecurityGroups": [{"Ref": "AdminAPISecurityGroup"}],
414
490
 
415
491
  "HealthCheck": {
416
- "HealthyThreshold": "5",
417
- "Interval": "30",
492
+ "HealthyThreshold": "3",
493
+ "Interval": "15",
418
494
  "Target": "HTTP:80/v1/info",
419
495
  "Timeout": "5",
420
- "UnhealthyThreshold": "2"
496
+ "UnhealthyThreshold": "3"
421
497
  },
422
498
 
423
499
  "Listeners": [{
@@ -427,111 +503,269 @@
427
503
  "InstanceProtocol": "TCP",
428
504
 
429
505
  "SSLCertificateId": {"Fn::Join": ["", [
430
- "arn:aws:iam::", {"Ref": "AWS::AccountId"}, ":server-certificate/",
431
- <%= j backend_cert_path %>, ".pem"
506
+ "arn:aws:iam::", {"Ref": "AWS::AccountId"}, ":server-certificate/cloudfront/",
507
+ <%= j @stack.name %>, "/", <%= j backend_cert_path %>
432
508
  ]]}
433
509
  }]
434
510
  }},
435
511
 
436
- "BackendEndpointDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
512
+ "AdminAPIDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
437
513
  "HostedZoneId": <%= j hosted_zone %>,
438
514
  "Type": "CNAME", "TTL": "300",
439
515
  "Name": <%= j "api.admin.#{domain}." %>,
440
- "ResourceRecords": [{"Fn::GetAtt": ["BackendEndpoint", "DNSName"]}]
516
+ "ResourceRecords": [{"Fn::GetAtt": ["AdminAPILoadBalancer", "DNSName"]}]
441
517
  }},
518
+ <% end %>
442
519
 
443
- "BackendEndpointSecurityGroup": {"Type": "AWS::EC2::SecurityGroup", "Properties": {
444
- "GroupDescription": "Backend ELB security group",
445
- "VpcId": {"Ref": "VPC"},
446
520
 
447
- "SecurityGroupIngress": [
448
- {"IpProtocol": "tcp", "FromPort": "443", "ToPort": "443", "CidrIp": "10.100.0.0/24"}
449
- ],
450
- "SecurityGroupEgress": [
451
- {"IpProtocol": "tcp", "FromPort": "80", "ToPort": "80", "CidrIp": "0.0.0.0/0"}
452
- ]
453
- }},
454
521
 
455
- "BackendIngress": {"Type": "AWS::EC2::SecurityGroupIngress", "Properties": {
456
- "GroupId": {"Ref": "BackendSecurityGroup"},
457
- "SourceSecurityGroupId": {"Ref": "BackendEndpointSecurityGroup"},
458
522
 
459
- "IpProtocol": "tcp", "FromPort": "80", "ToPort": "80"
460
- }},
461
523
 
462
524
 
463
525
 
464
526
 
465
527
 
466
528
 
467
- "OpenVPN": {"Type": "AWS::EC2::Instance", "Properties": {
468
- "InstanceType": "m1.small",
469
- "ImageId": <%= j ubuntu_daily_ami %>,
470
- "AvailabilityZone": {"Fn::GetAtt": ["PublicSubnet", "AvailabilityZone"]},
529
+ <% if layers_enabled.member? :static %>
530
+ "ClientAppBucket": {"Type": "AWS::S3::Bucket", "Properties": {
531
+ "BucketName": <%= j domain %>,
532
+ "AccessControl": "PublicRead",
533
+ "WebsiteConfiguration": {
534
+ "IndexDocument": "index.html",
535
+ "ErrorDocument": "404.html"
536
+ }
537
+ }},
471
538
 
472
- "Tags": [
473
- { "Key": "Name", "Value": "vpn01" },
474
- { "Key": "Environment", "Value": <%= j environment %> }
475
- ],
539
+ "ClientAppDistribution": {"Type": "AWS::CloudFront::Distribution", "Properties": {
540
+ "DistributionConfig": {
541
+ "Aliases": [<%= j domain %>],
542
+ "Enabled": "true",
476
543
 
477
- "KeyName": <%= j @stack.iam_keypair_name %>,
544
+ "Origins": [{
545
+ "Id": "ClientAppBucketOrigin",
546
+ "DomainName": {"Fn::Join": ["", [<%= j domain %>, ".s3-website-", {"Ref": "AWS::Region"}, ".amazonaws.com"]]},
478
547
 
479
- "NetworkInterfaces": [
480
- {
481
- "NetworkInterfaceId": {"Ref": "OpenVPNPublicInterface"},
482
- "DeviceIndex": "0"
483
- },
484
- {
485
- "Description": "Private network interface",
486
- "DeviceIndex": "1",
487
- "DeleteOnTermination": true,
548
+ "CustomOriginConfig": {
549
+ "HTTPPort": "80",
550
+ "HTTPSPort": "443",
551
+ "OriginProtocolPolicy": "http-only"
552
+ }
553
+ }],
488
554
 
489
- "SubnetId": {"Ref": "PrivateSubnet"},
490
- "GroupSet": [{"Ref": "OpenVPNSecurityGroup"}]
555
+ "DefaultCacheBehavior": {
556
+ "TargetOriginId": "ClientAppBucketOrigin",
557
+ "ForwardedValues": {"QueryString": "false"},
558
+ "ViewerProtocolPolicy": "redirect-to-https"
491
559
  }
492
- ],
560
+ }
561
+ }},
493
562
 
494
- "UserData": {"Fn::Base64": {"Fn::Join": ["", [
495
- "#!/bin/bash -e\n",
496
- "export AWS_WAIT_HANDLE='", {"Ref": "InstanceBootstrapWaitHandle"}, "'\n",
497
- <%= ja 8, userdata['openvpn'] %>
498
- ]]}}
563
+ "ClientAppDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
564
+ "HostedZoneId": <%= j hosted_zone %>,
565
+ "Type": "A",
566
+ "Name": <%= j "#{domain}." %>,
567
+ "AliasTarget": {
568
+ "HostedZoneId": <%= j @config['cloudfront']['hosted_zone'] %>,
569
+ "DNSName": {"Fn::GetAtt": ["ClientAppDistribution", "DomainName"]}
570
+ }
571
+ }},
572
+
573
+
574
+
575
+ "AdminAppBucket": {"Type": "AWS::S3::Bucket", "Properties": {
576
+ "BucketName": <%= j "admin.#{domain}" %>,
577
+ "AccessControl": "PublicRead",
578
+ "WebsiteConfiguration": {
579
+ "IndexDocument": "index.html",
580
+ "ErrorDocument": "404.html"
581
+ }
499
582
  }},
500
583
 
501
- "OpenVPNPublicInterface": {"Type": "AWS::EC2::NetworkInterface", "Properties": {
502
- "Description": "Public network interface",
584
+ "AdminAppDistribution": {"Type": "AWS::CloudFront::Distribution", "Properties": {
585
+ "DistributionConfig": {
586
+ "Aliases": [<%= j "admin.#{domain}" %>],
587
+ "Enabled": "true",
588
+
589
+ "Origins": [{
590
+ "Id": "AdminAppBucketOrigin",
591
+ "DomainName": {"Fn::Join": ["", [<%= j "admin.#{domain}" %>, ".s3-website-", {"Ref": "AWS::Region"}, ".amazonaws.com"]]},
592
+
593
+ "CustomOriginConfig": {
594
+ "HTTPPort": "80",
595
+ "HTTPSPort": "443",
596
+ "OriginProtocolPolicy": "http-only"
597
+ }
598
+ }],
503
599
 
504
- "SubnetId": {"Ref": "PublicSubnet"},
505
- "GroupSet": [{"Ref": "OpenVPNSecurityGroup"}]
600
+ "DefaultCacheBehavior": {
601
+ "TargetOriginId": "AdminAppBucketOrigin",
602
+ "ForwardedValues": {"QueryString": "false"},
603
+ "ViewerProtocolPolicy": "redirect-to-https"
604
+ }
605
+ }
506
606
  }},
507
607
 
508
- "OpenVPNEIP": {"Type": "AWS::EC2::EIP", "Properties": {
509
- "Domain": "VPC"
608
+ "AdminAppDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
609
+ "HostedZoneId": <%= j hosted_zone %>,
610
+ "Type": "CNAME", "TTL": "300",
611
+ "Name": <%= j "admin.#{domain}." %>,
612
+ "ResourceRecords": [{"Fn::GetAtt": ["AdminAppDistribution", "DomainName"]}]
510
613
  }},
511
614
 
512
- "OpenVPNEIPAssociation": {"Type": "AWS::EC2::EIPAssociation", "Properties": {
513
- "AllocationId": {"Fn::GetAtt": ["OpenVPNEIP", "AllocationId"]},
514
- "NetworkInterfaceId": {"Ref": "OpenVPNPublicInterface"}
615
+
616
+
617
+ "WwwRedirectorBucket": {"Type": "AWS::S3::Bucket", "Properties": {
618
+ "BucketName": <%= j "www.#{domain}" %>,
619
+ "AccessControl": "PublicRead",
620
+ "WebsiteConfiguration": {
621
+ "RedirectAllRequestsTo": {
622
+ "HostName": <%= j domain %>,
623
+ "Protocol": "https"
624
+ }
625
+ }
626
+ }},
627
+
628
+ "WwwRedirectorDistribution": {"Type": "AWS::CloudFront::Distribution", "Properties": {
629
+ "DistributionConfig": {
630
+ "Aliases": [<%= j "www.#{domain}" %>],
631
+ "Enabled": "true",
632
+
633
+ "Origins": [{
634
+ "Id": "WwwRedirectorBucketOrigin",
635
+ "DomainName": {"Fn::Join": ["", [<%= j "www.#{domain}" %>, ".s3-website-", {"Ref": "AWS::Region"}, ".amazonaws.com"]]},
636
+
637
+ "CustomOriginConfig": {
638
+ "HTTPPort": "80",
639
+ "HTTPSPort": "443",
640
+ "OriginProtocolPolicy": "http-only"
641
+ }
642
+ }],
643
+
644
+ "DefaultCacheBehavior": {
645
+ "TargetOriginId": "WwwRedirectorBucketOrigin",
646
+ "ForwardedValues": {"QueryString": "false"},
647
+ "ViewerProtocolPolicy": "redirect-to-https"
648
+ }
649
+ }
515
650
  }},
516
651
 
517
- "OpenVPNDNSRecord": {"Type": "AWS::Route53::RecordSet", "DependsOn": "OpenVPNEIPAssociation", "Properties": {
652
+ "WwwRedirectorDNSRecord": {"Type": "AWS::Route53::RecordSet", "Properties": {
518
653
  "HostedZoneId": <%= j hosted_zone %>,
519
654
  "Type": "CNAME", "TTL": "300",
520
- "Name": <%= j "vpn.#{domain}." %>,
521
- "ResourceRecords": [{"Fn::GetAtt": ["OpenVPN", "PublicDnsName"]}]
655
+ "Name": <%= j "www.#{domain}." %>,
656
+ "ResourceRecords": [{"Fn::GetAtt": ["WwwRedirectorDistribution", "DomainName"]}]
522
657
  }},
658
+ <% end %>
659
+
660
+
661
+
662
+
663
+ <% if layers_enabled.member? :instances %>
664
+ "BackendSecurityGroup" : {
665
+ "Type" : "AWS::EC2::SecurityGroup",
666
+ "Properties" : {
667
+ "GroupDescription" : "Allow the application instances to access the NAT device",
668
+ "VpcId" : { "Ref" : "VPC" },
669
+ "SecurityGroupIngress": [
670
+ { "IpProtocol": "tcp", "FromPort" : "80", "ToPort" : "80", "SourceSecurityGroupId" : { "Ref" : "AdminAPISecurityGroup" }},
671
+ {"IpProtocol": "tcp", "FromPort": "51607", "ToPort": "51607", "SourceSecurityGroupId": { "Ref" : "FrontendSecurityGroup" }},
672
+ {"IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": "0.0.0.0/0"}
673
+ ],
674
+ "SecurityGroupEgress": [
675
+ {"IpProtocol": "-1", "CidrIp": "0.0.0.0/0"}
676
+ ]
677
+ }
678
+ },
679
+
680
+ "BackendInstanceConfigurationWaitHandle": {"Type": "AWS::CloudFormation::WaitConditionHandle", "Properties": {}},
681
+
682
+ "BackendInstanceConfiguration": {"Type": "AWS::CloudFormation::WaitCondition", "DependsOn": ["BackendInstance"], "Properties": {
683
+ "Handle": {"Ref": "BackendInstanceConfigurationWaitHandle"},
684
+ "Count": "1",
685
+ "Timeout": "1200"
686
+ }},
687
+
688
+ "BackendInstance" : {
689
+ "Type" : "AWS::EC2::Instance",
690
+ "DependsOn": "NATDeviceConfiguration",
691
+ "Properties" : {
692
+ "InstanceType": "m3.medium",
693
+ "ImageId": <%= j ubuntu_daily_ami %>,
694
+ "SubnetId" : { "Ref" : "PrivateSubnet" },
695
+ "SecurityGroupIds" : [{ "Ref" : "BackendSecurityGroup" }],
696
+ "KeyName": <%= j @stack.iam_keypair_name %>,
697
+ "BlockDeviceMappings": [
698
+ {"DeviceName": "/dev/xvdc", "Ebs": {
699
+ "SnapshotId": <%= j docker_library_snapshot %>,
700
+ "VolumeSize": "50"
701
+ }},
702
+
703
+ {"DeviceName": "/dev/xvdd", "Ebs": {
704
+ "VolumeSize": "300"
705
+ }}
706
+ ],
707
+
708
+ "UserData": {"Fn::Base64": {"Fn::Join": ["", [
709
+ "#!/bin/bash -e\n",
710
+ "export AWS_WAIT_HANDLE='", {"Ref": "BackendInstanceConfigurationWaitHandle"}, "'\n",
711
+ <%= ja 8, userdata['backend'] %>
712
+ ]]}}
713
+ }
714
+ },
715
+
716
+
717
+
718
+ "FrontendSecurityGroup" : {
719
+ "Type" : "AWS::EC2::SecurityGroup",
720
+ "Properties" : {
721
+ "GroupDescription" : "Allow the application instances to access the NAT device",
722
+ "VpcId" : { "Ref" : "VPC" },
723
+ "SecurityGroupIngress": [
724
+ {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp": "0.0.0.0/0"},
725
+ {"IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": "0.0.0.0/0"}
726
+ ],
727
+ "SecurityGroupEgress": [
728
+ {"IpProtocol": "-1", "CidrIp": "0.0.0.0/0"}
729
+ ]
730
+ }
731
+ },
732
+
733
+ "FrontendInstance" : {
734
+ "Type" : "AWS::EC2::Instance",
735
+ "DependsOn": "BackendInstanceConfiguration",
736
+ "Properties" : {
737
+ "InstanceType": "m1.small",
738
+ "ImageId": <%= j ubuntu_daily_ami %>,
739
+ "SubnetId" : { "Ref" : "PrivateSubnet" },
740
+ "SecurityGroupIds" : [{ "Ref" : "FrontendSecurityGroup" }],
741
+ "KeyName": <%= j @stack.iam_keypair_name %>,
742
+ "BlockDeviceMappings": [
743
+ {"DeviceName": "/dev/xvdc", "Ebs": {
744
+ "SnapshotId": <%= j docker_library_snapshot %>,
745
+ "VolumeSize": "50"
746
+ }},
747
+
748
+ {"DeviceName": "/dev/xvdd", "Ebs": {
749
+ "VolumeSize": "300"
750
+ }}
751
+ ],
752
+
753
+ "UserData": {"Fn::Base64": {"Fn::Join": ["", [
754
+ "#!/bin/bash -e\n",
755
+ "export BACKEND_HOST='", {"Fn::GetAtt": ["BackendInstance", "PrivateDnsName"]}, "'\n",
756
+ <%= ja 8, userdata['frontend'] %>
757
+ ]]}}
758
+ }
759
+ }
760
+ <% end %>
761
+
762
+
763
+
764
+
765
+
766
+
523
767
 
524
- "OpenVPNSecurityGroup": {"Type": "AWS::EC2::SecurityGroup", "Properties": {
525
- "GroupDescription": "Enable everything",
526
- "VpcId": {"Ref": "VPC"},
527
768
 
528
- "SecurityGroupIngress": [
529
- {"IpProtocol": "tcp", "FromPort": "443", "ToPort": "443", "CidrIp": "0.0.0.0/0"},
530
- {"IpProtocol": "udp", "FromPort": "1192", "ToPort": "1194", "CidrIp": "0.0.0.0/0"},
531
- {"IpProtocol": "tcp", "FromPort": "943", "ToPort": "943", "CidrIp": "0.0.0.0/0"},
532
- {"IpProtocol": "tcp", "FromPort": "22", "ToPort": "22", "CidrIp": "0.0.0.0/0"}
533
- ]
534
- }}
535
769
 
536
770
  }
537
771
  }