ciinabox-ecs 0.2.9 → 0.2.10.alpha.1529494989

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97e6e52b7ab9e04b778eacf504833653f164384ee2fc0e4be6404f9f5088a3fa
4
- data.tar.gz: b9b53e675fb107d2966b03059410589c30f506c49b67fd0fa7c0d4f426c39d82
3
+ metadata.gz: 5925c9f4071982d31b38d632acbd0783cf6fa89417d2c5bc8dd46f3805bebdb1
4
+ data.tar.gz: c627c7dad29dea09241c7ea8b589b790a91d5a9850e6c8eaaa1514ec148cb731
5
5
  SHA512:
6
- metadata.gz: 8cb3e6f8be9729adfb82069a484faa560947fb38f1b28a680d9ee10b098bfec1015d9e7fb576e107b9ead8406183b1ddccabcb41ea29269c12d08321a12f57be
7
- data.tar.gz: c1777aa1ccdfe53f48e1e9178993604b637623f409392e22a32fe905cf561c71f2930e736995942f33bec8ab3f9019eb9ae9bad774ebefaaed9608396b70379b
6
+ metadata.gz: 8211b47987f88749bf234d50233c27e4a5368c709379918f6d2a776f5229dc9efd252aa25a4b9bf94b70fe10bcb27f54f6aec48a7d08edc88c9a029db0ea0c4e
7
+ data.tar.gz: 86196ce321cef4b8cb5efa3b38d6c5a10b7298d4e2decc441215b5aae6d11cfe2c1857faac8d8acac65b5131c5a8e2e7e88b8a1d81968cec2731dd96ff04b715
data/Gemfile CHANGED
@@ -1,7 +1,9 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'rake'
4
- gem 'cfndsl','0.15.2'
4
+ gem 'cfndsl', '~>0.16'
5
5
  gem 'cfn_manage'
6
6
  gem 'deep_merge'
7
7
  gem 'rubyzip'
8
+ gem 'aws-sdk-s3', '~>1'
9
+ gem 'aws-sdk-cloudformation', '~>1'
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Build Status](https://travis-ci.com/base2services/ciinabox-ecs.svg?branch=develop)](https://travis-ci.com/base2services/ciinabox-ecs.svg?branch=develop)
2
+
1
3
  # ciinabox ECS
2
4
 
3
5
  ciinabox pronounced ciin a box is a set of automation for building
@@ -67,7 +69,7 @@ If you wish to add additional containers to your ciinabox environment, you can s
67
69
  ciinaboxes/ciinabox_name/config/services.yml
68
70
 
69
71
  e.g:
70
-
72
+
71
73
  ```yaml
72
74
  services:
73
75
  - jenkins:
@@ -80,7 +82,7 @@ e.g:
80
82
  - artifactory:
81
83
  - drone:
82
84
  ```
83
-
85
+
84
86
  Please note that if you wish to do this, that you also need to create a CFNDSL template for the service under templates/services, with the name of the service as the filename (e.g. bitbucket.rb)
85
87
 
86
88
  ## Getting Started
@@ -91,10 +93,10 @@ To get started install `ciinabox-ecs` ruby gem
91
93
  $ gem install ciinabox-ecs
92
94
  ```
93
95
 
94
- During the setup process, you'll need to provide domain for the tools (e.g. `*.tools.example.com`) that has
96
+ During the setup process, you'll need to provide domain for the tools (e.g. `*.tools.example.com`) that has
95
97
  matching Route53 zone in same AWS account where you are creating ciinabox. Optionally you can use local hosts file
96
98
  hack in order to get routing working, but in this case usage of ACM certificates is not an option, and you'll need
97
- to use selfsigned IAM server certificates.
99
+ to use selfsigned IAM server certificates.
98
100
 
99
101
  ### Quick setup
100
102
 
@@ -146,7 +148,7 @@ $ ciinabox-ecs full_install
146
148
 
147
149
  5. Create ciinabox S3 source deployment bucket
148
150
  ```bash
149
- $ ciinabox-ecs create_source_bucket [ciinabox_name]
151
+ $ ciinabox-ecs create_source_bucket [ciinabox_name]
150
152
  Successfully created S3 source deployment bucket source.myciinabox.com
151
153
  ```
152
154
 
@@ -158,7 +160,7 @@ $ ciinabox-ecs full_install
158
160
 
159
161
  7. Generate ciinabox cloudformation templates
160
162
  ```bash
161
- $ ciinabox-ecs generate [ciinabox_name]
163
+ $ ciinabox-ecs generate [ciinabox_name]
162
164
  Writing to output/ciinabox.json
163
165
  using extras [[:yaml, "ciinaboxes/myciinabox/config/default_params.yml"], [:yaml, "config/services.yml"], [:ruby, "ext/helper.rb"]]
164
166
  Loading YAML file ciinaboxes/myciinabox/config/default_params.yml
@@ -307,6 +309,16 @@ internal_elb: false
307
309
  # needs internal_elb: true
308
310
  ```
309
311
 
312
+ ## Nginx Reverse Proxy Config
313
+
314
+ If you need to pass in extra nginx configuration such as `client_max_body_size 100m;` to the proxy you can by adding the following text block to you params.yaml
315
+
316
+ ```yaml
317
+ proxy_config: |
318
+ server_tokens off;
319
+ client_max_body_size 100m;
320
+ ```
321
+
310
322
  # Ciinabox configuration
311
323
 
312
324
  ## Bastion (Jumpbox) instance
@@ -348,6 +360,19 @@ bastionAMI:
348
360
 
349
361
  ```
350
362
 
363
+ ## Vpn (OpenVpn) instance
364
+
365
+ You can create a openvpn access server instance complete by using the bellow config. It will create a new ecs cluster in the public subnet and launch [base2/openvpn-as](https://hub.docker.com/r/base2/openvpn-as/) container. It mounts a data volume to persist all configuration and logs in `/data`. Uses the existing `ecs_ami` as the underlying instance ami.
366
+
367
+ ```yaml
368
+ include_vpn_stack: true
369
+ ```
370
+
371
+ It is also possible to override the vpn instance type used for Launch Configuration. Defaults are below
372
+
373
+ ```yaml
374
+ vpnInstanceType: t2.small
375
+ ```
351
376
 
352
377
  ## IAM Roles
353
378
 
@@ -379,14 +404,14 @@ allow_nat_connections: false
379
404
 
380
405
  ## Automatic issuance and validation of ACM SSL certificate
381
406
 
382
- This setting is enabled by default in default parameters. During the ciinabox init stage, you will be
383
- asked if you want to utilise this functionality. Essentially, custom cloudformation resource based on
384
- python [aws-acm-validator](https://pypi.python.org/pypi/aws-acm-cert-validator) python package will
385
- request and validate ACM certificate through appropriate Route 53 DNS validation record.
407
+ This setting is enabled by default in default parameters. During the ciinabox init stage, you will be
408
+ asked if you want to utilise this functionality. Essentially, custom cloudformation resource based on
409
+ python [aws-acm-validator](https://pypi.python.org/pypi/aws-acm-cert-validator) python package will
410
+ request and validate ACM certificate through appropriate Route 53 DNS validation record.
386
411
 
387
412
  ### To disable during ciinabox setup
388
413
 
389
- Answer question below with 'y' during ciinabox init stage
414
+ Answer question below with 'y' during ciinabox init stage
390
415
 
391
416
  ```text
392
417
  Use selfsigned rather than ACM issued and validated certificate (y/n)? [n]
data/Rakefile CHANGED
@@ -11,6 +11,8 @@ require 'tempfile'
11
11
  require 'json'
12
12
  require_relative './ext/common_helper'
13
13
  require_relative './ext/zip_helper'
14
+ require 'aws-sdk-s3'
15
+ require 'aws-sdk-cloudformation'
14
16
  require 'ciinabox-ecs' if Gem::Specification::find_all_by_name('ciinabox-ecs').any?
15
17
 
16
18
  namespace :ciinabox do
@@ -38,7 +40,7 @@ namespace :ciinabox do
38
40
  config = default_params
39
41
  end
40
42
 
41
- Dir["#{ciinaboxes_dir}/#{ciinabox_name}/config/*.yml"].each { |config_file|
43
+ Dir["#{ciinaboxes_dir}/#{ciinabox_name}/config/*.yml"].each {|config_file|
42
44
  if not config_file.include?('params.yml')
43
45
  config = config.merge(YAML.load(File.read(config_file)))
44
46
  end
@@ -48,12 +50,12 @@ namespace :ciinabox do
48
50
 
49
51
  # ciinabox binary version
50
52
  if Gem.loaded_specs['ciinabox-ecs'].nil?
51
- config['ciinabox_binary_version'] = `git rev-parse --short HEAD`.gsub("\n",'')
53
+ config['ciinabox_binary_version'] = `git rev-parse --short HEAD`.gsub("\n", '')
52
54
  else
53
55
  config['ciinabox_binary_version'] = Gem.loaded_specs['ciinabox-ecs'].version.to_s
54
56
  end
55
57
 
56
- File.write('debug-ciinabox.config.yaml',config.to_yaml) if ENV['DEBUG']
58
+ File.write('debug-ciinabox.config.yaml', config.to_yaml) if ENV['DEBUG']
57
59
 
58
60
  stack_name = config["stack_name"] || "ciinabox"
59
61
 
@@ -64,8 +66,8 @@ namespace :ciinabox do
64
66
  templates2 = Dir["#{ciinaboxes_dir}/#{ciinabox_name}/templates/**/*.rb"]
65
67
 
66
68
  ## we want to exclude overridden templates
67
- templatesLocalFileNames = templates2.collect { |templateFile| File.basename(templateFile) }
68
- templates = templates.select { |templateFile| not templatesLocalFileNames.include? File.basename(templateFile) }
69
+ templatesLocalFileNames = templates2.collect {|templateFile| File.basename(templateFile)}
70
+ templates = templates.select {|templateFile| not templatesLocalFileNames.include? File.basename(templateFile)}
69
71
  templates = templates + templates2
70
72
  end
71
73
 
@@ -87,13 +89,13 @@ namespace :ciinabox do
87
89
  tmp_file = write_config_tmp_file(config)
88
90
 
89
91
  CfnDsl::RakeTask.new do |t|
90
- extras = [[:yaml,"#{current_dir}/config/default_params.yml"]]
92
+ extras = [[:yaml, "#{current_dir}/config/default_params.yml"]]
91
93
  extras << [:yaml, "#{current_dir}/config/default_lambdas.yml"]
92
94
  if File.exist? "#{ciinaboxes_dir}/ciinabox_config.yml"
93
95
  extras << [:yaml, "#{ciinaboxes_dir}/ciinabox_config.yml"]
94
96
  end
95
- (Dir["#{ciinaboxes_dir}/#{ciinabox_name}/config/*.yml"].map { |f| [:yaml,f]}).each {|c| extras<<c}
96
- extras << [:ruby,"#{current_dir}/ext/helper.rb"]
97
+ (Dir["#{ciinaboxes_dir}/#{ciinabox_name}/config/*.yml"].map {|f| [:yaml, f]}).each {|c| extras << c}
98
+ extras << [:ruby, "#{current_dir}/ext/helper.rb"]
97
99
  extras << [:yaml, tmp_file.path]
98
100
  t.cfndsl_opts = {
99
101
  verbose: true,
@@ -165,16 +167,16 @@ namespace :ciinabox do
165
167
  if File.exist?("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml")
166
168
  config_output = user_params.merge(input_hash) #Merges input hash into user-provided template
167
169
  config_yaml = config_output.to_yaml #Convert output to YAML for writing
168
- File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml", 'w') { |f| f.write(config_yaml) }
170
+ File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml", 'w') {|f| f.write(config_yaml)}
169
171
  else
170
- File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml", 'w') { |f| f.write(input_result) }
172
+ File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml", 'w') {|f| f.write(input_result)}
171
173
  end
172
174
 
173
175
  default_services = YAML.load(File.read("#{current_dir}/config/default_services.yml"))
174
176
 
175
177
  class ::Hash
176
178
  def deep_merge(second)
177
- merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
179
+ merger = proc {|key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2}
178
180
  self.merge(second.to_h, &merger)
179
181
  end
180
182
  end
@@ -184,10 +186,10 @@ namespace :ciinabox do
184
186
  user_services = YAML.load(File.read("#{ciinaboxes_dir}/#{ciinabox_name}/config/services.yml"))
185
187
  combined_services = default_services.deep_merge(user_services)
186
188
  yml_combined_services = combined_services.to_yaml
187
- File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/services.yml", 'w') { |f| f.write(yml_combined_services) }
189
+ File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/services.yml", 'w') {|f| f.write(yml_combined_services)}
188
190
  else
189
191
  yml_default_services = default_services.to_yaml
190
- File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/services.yml", 'w') { |f| f.write(yml_default_services) }
192
+ File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/services.yml", 'w') {|f| f.write(yml_default_services)}
191
193
  end
192
194
 
193
195
  display_active_ciinabox ciinaboxes_dir, ciinabox_name
@@ -251,13 +253,12 @@ namespace :ciinabox do
251
253
  check_active_ciinabox(config)
252
254
  ciinabox_name = config['ciinabox_name']
253
255
  cert_dir = "#{ciinaboxes_dir}/#{ciinabox_name}"
254
- status, result = aws_execute( config, [
256
+ status, result = aws_execute(config, [
255
257
  'iam', 'upload-server-certificate',
256
258
  '--server-certificate-name ciinabox',
257
259
  "--certificate-body file://#{cert_dir}/ssl/ciinabox.crt",
258
260
  "--private-key file://#{cert_dir}/ssl/ciinabox.key",
259
- "--certificate-chain file://#{cert_dir}/ssl/ciinabox.crt",
260
- '--output json'
261
+ "--certificate-chain file://#{cert_dir}/ssl/ciinabox.crt"
261
262
  ])
262
263
  if status != 0
263
264
  puts "fail to create or update IAM server-certificates. See error logs for details"
@@ -388,6 +389,13 @@ namespace :ciinabox do
388
389
  desc('Package Lambda Functions as ZipFiles')
389
390
  task :package_lambdas do
390
391
  check_active_ciinabox(config)
392
+
393
+ # custom lambda modification
394
+ lambda_stack_required = config['acm_auto_issue_validate']
395
+ # in future any new conditions for lambda stack would be added here
396
+ # lambda_stack_required ||= some_new_condition
397
+ config['lambdas'] = nil unless lambda_stack_required
398
+
391
399
  if !config['lambdas'].nil? && !config['lambdas']['functions'].nil?
392
400
  log_header 'Package lambda functions'
393
401
 
@@ -514,7 +522,7 @@ namespace :ciinabox do
514
522
  '--out json'
515
523
  ])
516
524
  resp = JSON.parse(result)
517
- cert_output = resp['Stacks'][0]['Outputs'].find { |k| k['OutputKey'] == 'DefaultSSLCertificate' }
525
+ cert_output = resp['Stacks'][0]['Outputs'].find {|k| k['OutputKey'] == 'DefaultSSLCertificate'}
518
526
  if cert_output.nil?
519
527
  STDERR.puts("ACM certificate is not present in stack outputs")
520
528
  exit -1
@@ -526,6 +534,64 @@ namespace :ciinabox do
526
534
  puts "Set #{cert_arn} as default_cert_arn"
527
535
  end
528
536
 
537
+
538
+ desc('validate cloudformation templates')
539
+ task :validate do
540
+ cfn = Aws::CloudFormation::Client.new(region: config['source_region'])
541
+ s3 = Aws::S3::Client.new(region: config['source_region'])
542
+ Dir.glob("output/**/*.json") do |file|
543
+ template_content = IO.read(file)
544
+ # Skip if empty template generated
545
+ next if (template_content == "null\n")
546
+ template = File.open(file, 'rb')
547
+ filename = File.basename file
548
+ template_bytesize = template_content.bytesize
549
+ file_size = File.size file
550
+ local_validation = (template_content.bytesize < 51200)
551
+ puts "INFO - #{file}: Filesize: #{file_size}, Bytesize: #{template_bytesize}, local validation: #{local_validation}"
552
+ begin
553
+ if not local_validation
554
+ puts "INFO - Copy #{file} -> s3://#{config['source_bucket']}/cloudformation/#{project_name}/validate/#{filename}"
555
+ s3.put_object({
556
+ body: template,
557
+ bucket: "#{config['source_bucket']}",
558
+ key: "cloudformation/#{project_name}/validate/#{filename}",
559
+ })
560
+ template_url = "https://#{config['source_bucket']}.s3.amazonaws.com/cloudformation/#{project_name}/validate/#{filename}"
561
+ puts "INFO - Copied #{file} to s3://#{config['source_bucket']}/cloudformation/#{project_name}/validate/#{filename}"
562
+ puts "INFO - Validating #{template_url}"
563
+ else
564
+ puts "INFO - Validating #{file}"
565
+ end
566
+ begin
567
+ resp = cfn.validate_template({ template_url: template_url }) unless local_validation
568
+ resp = cfn.validate_template({ template_body: template_content }) if local_validation
569
+ puts "INFO - Template #{filename} validated successfully"
570
+ rescue => e
571
+ puts "ERROR - Template #{filename} failed to validate: #{e}"
572
+ exit 1
573
+ end
574
+
575
+ rescue => e
576
+ puts "ERROR - #{e.class}, #{e}"
577
+ exit 1
578
+ end
579
+ end
580
+ puts "INFO - #{Dir["output/**/*.json"].count} templates validated successfully"
581
+ end
582
+
583
+ desc('vendor templates from ciinabox gem into ciinabox folder')
584
+ task :vendor do
585
+ Dir["#{current_dir}/templates/**/*.rb"].each do |template_path|
586
+ relative_path = template_path.gsub("#{current_dir}/", '')
587
+ target_path = "#{@ciinaboxes_dir}/#{@ciinabox_name}/#{relative_path}"
588
+ target_dir = File.dirname(target_path)
589
+ FileUtils.mkdir_p target_dir
590
+ puts "#{relative_path} -> #{target_path}"
591
+ FileUtils.copy template_path, target_path
592
+ end
593
+ end
594
+
529
595
  def add_ciinabox_config_setting(element, value)
530
596
  file_name = "#{@ciinaboxes_dir}/#{@ciinabox_name}/config/params.yml"
531
597
  File.write(file_name,
@@ -535,7 +601,7 @@ namespace :ciinabox do
535
601
  )
536
602
  end
537
603
 
538
- def remove_update_ciinabox_config_setting(element, new_value='')
604
+ def remove_update_ciinabox_config_setting(element, new_value = '')
539
605
  f = File.new("#{@ciinaboxes_dir}/#{@ciinabox_name}/config/params.yml", 'r+')
540
606
  found = false
541
607
  f.each do |line|
@@ -551,7 +617,7 @@ namespace :ciinabox do
551
617
  end
552
618
  end
553
619
  f.close
554
- add_ciinabox_config_setting(element, new_value) if((not found) and (not new_value.empty?))
620
+ add_ciinabox_config_setting(element, new_value) if ((not found) and (not new_value.empty?))
555
621
  end
556
622
 
557
623
  def check_active_ciinabox(config)
@@ -609,12 +675,12 @@ namespace :ciinabox do
609
675
  question = ("#{question} (y/n)? [#{default ? 'y' : 'n'}]")
610
676
  while true
611
677
  case get_input(question)
612
- when ''
613
- return default
614
- when 'Y', 'y', 'yes'
615
- return true
616
- when /\A[nN]o?\Z/ #n or no
617
- return false
678
+ when ''
679
+ return default
680
+ when 'Y', 'y', 'yes'
681
+ return true
682
+ when /\A[nN]o?\Z/ #n or no
683
+ return false
618
684
  end
619
685
  end
620
686
  end
@@ -287,6 +287,7 @@ ecs_iam_role_permissions_default:
287
287
  # CertName: x
288
288
  # StackOctetA: 11
289
289
  # StackOctetB: 12
290
+ vpnInstanceType: t2.small
290
291
  bastionInstanceType: t2.micro
291
292
  bastionAMI:
292
293
  us-east-1:
@@ -309,3 +310,12 @@ bastionAMI:
309
310
  ami: ami-c7ee5ca8
310
311
 
311
312
  acm_auto_issue_validate: true
313
+
314
+ # Enbale open VPN
315
+ include_vpn_stack: false
316
+ # open the vpn upd connection port 1194 to all ips
317
+ vpn_udp_public: false
318
+
319
+ # Default public access is considered whole internet
320
+ publicAccess:
321
+ - 0.0.0.0/0
@@ -16,5 +16,5 @@ function pipinstall () {
16
16
  if [ $(which docker) == '' ]; then
17
17
  pipinstall
18
18
  else
19
- docker run --rm -v $DIR/..:/dst -w /dst -u 1000 python:3-alpine pip install aws-acm-cert-validator==0.1.11 -t lib
19
+ docker run --rm -v $DIR/..:/dst -w /dst -u $UID python:3-alpine pip install aws-acm-cert-validator==0.1.11 -t lib
20
20
  fi
@@ -35,11 +35,7 @@ CloudFormation do
35
35
  }
36
36
 
37
37
  # ECS Services Stack
38
- Resource('ECSServicesStack') {
39
- Type 'AWS::CloudFormation::Stack'
40
- Property('TemplateURL', "https://#{source_bucket}.s3.amazonaws.com/ciinabox/#{ciinabox_version}/ecs-services.json")
41
- Property('TimeoutInMinutes', 15)
42
- Property('Parameters',{
38
+ services_params = {
43
39
  ECSCluster: Ref(cluster_name),
44
40
  VPC: FnGetAtt('VPCStack', 'Outputs.VPCId'),
45
41
  SubnetPublicA: FnGetAtt('VPCStack', 'Outputs.SubnetPublicA'),
@@ -51,8 +47,14 @@ CloudFormation do
51
47
  SecurityGroupOps: FnGetAtt('VPCStack', 'Outputs.SecurityGroupOps'),
52
48
  SecurityGroupDev: FnGetAtt('VPCStack', 'Outputs.SecurityGroupDev'),
53
49
  SecurityGroupNatGateway: FnGetAtt('VPCStack', 'Outputs.SecurityGroupNatGateway'),
54
- CRAcmCertArn: FnGetAtt('LambdasStack','Outputs.LambdaCRIssueACMCertificateArn')
55
- })
50
+ }
51
+ services_params[:CRAcmCertArn] = FnGetAtt('LambdasStack','Outputs.LambdaCRIssueACMCertificateArn') if acm_auto_issue_validate
52
+
53
+ Resource('ECSServicesStack') {
54
+ Type 'AWS::CloudFormation::Stack'
55
+ Property('TemplateURL', "https://#{source_bucket}.s3.amazonaws.com/ciinabox/#{ciinabox_version}/ecs-services.json")
56
+ Property('TimeoutInMinutes', 15)
57
+ Property('Parameters',services_params)
56
58
  }
57
59
 
58
60
  #These are the commona params for use below in "foreign templates
@@ -70,12 +72,15 @@ CloudFormation do
70
72
  base_params.merge!("RouteTablePrivate#{az}" => FnGetAtt('VPCStack', "Outputs.RouteTablePrivate#{az}"))
71
73
  end
72
74
 
73
- # Lambda functions stack
75
+ lambda_stack_required = acm_auto_issue_validate
76
+ # in future any new conditions for lambda stack would be added here
77
+ # lambda_stack_required ||= some_new_condition
78
+ # in future any new conditions for lambda stack would be added here
74
79
  Resource('LambdasStack') do
75
80
  Type 'AWS::CloudFormation::Stack'
76
81
  Property('TemplateURL', "https://#{source_bucket}.s3.amazonaws.com/ciinabox/#{ciinabox_version}/lambdas.json")
77
82
  Property('Parameters', base_params)
78
- end
83
+ end if lambda_stack_required
79
84
 
80
85
 
81
86
  # Bastion if required
@@ -85,7 +90,12 @@ CloudFormation do
85
90
  Property('Parameters', base_params)
86
91
  end if include_bastion_stack
87
92
 
88
-
93
+ # Vpn if required
94
+ Resource('VpnStack') do
95
+ Type 'AWS::CloudFormation::Stack'
96
+ Property('TemplateURL', "https://#{source_bucket}.s3.amazonaws.com/ciinabox/#{ciinabox_version}/vpn.json")
97
+ Property('Parameters', base_params)
98
+ end if include_vpn_stack
89
99
 
90
100
  #Foreign templates
91
101
  #e.g CIINABOXES_DIR/CIINABOX/templates/x.rb
@@ -162,6 +162,22 @@ CloudFormation {
162
162
  if not ecs_block_device_mapping.empty?
163
163
  Property("BlockDeviceMappings", ecs_block_device_mapping)
164
164
  end
165
+ if defined? proxy_config
166
+ Metadata(
167
+ 'AWS::CloudFormation::Init': {
168
+ config: {
169
+ files: {
170
+ '/opt/proxy/proxy_config.conf': {
171
+ content: proxy_config,
172
+ mode: "000644",
173
+ owner: "root",
174
+ group: "root"
175
+ }
176
+ }
177
+ }
178
+ }
179
+ )
180
+ end
165
181
  UserData FnBase64(FnJoin("", [
166
182
  "#!/bin/bash\n",
167
183
  "echo ECS_CLUSTER=", Ref('ECSCluster'), " >> /etc/ecs/ecs.config\n",
@@ -261,6 +261,23 @@ CloudFormation {
261
261
  end
262
262
  end
263
263
 
264
+ volumes = []
265
+ mount_points = []
266
+
267
+ # Timezone
268
+ volumes << { Name: 'timezone', Host: { SourcePath: '/etc/localtime' }}
269
+ mount_points << { ContainerPath: '/etc/localtime', SourceVolume: 'timezone', ReadOnly: true }
270
+
271
+ # Docker Socket
272
+ volumes << { Name: 'docker_sock', Host: { SourcePath: '/var/run/docker.sock' }}
273
+ mount_points << { ContainerPath: '/tmp/docker.sock', SourceVolume: 'docker_sock', ReadOnly: false }
274
+
275
+ # Proxy Config
276
+ if defined? proxy_config
277
+ volumes << { Name: 'proxy_config', Host: { SourcePath: '/opt/proxy/proxy_config.conf' }}
278
+ mount_points << { ContainerPath: '/etc/nginx/conf.d/proxy_config.conf', SourceVolume: 'proxy_config', ReadOnly: true }
279
+ end
280
+
264
281
  Resource('ProxyTask') {
265
282
  Type "AWS::ECS::TaskDefinition"
266
283
  Property('ContainerDefinitions', [
@@ -274,34 +291,10 @@ CloudFormation {
274
291
  ContainerPort: 80
275
292
  }],
276
293
  Essential: true,
277
- MountPoints: [
278
- {
279
- ContainerPath: '/etc/localtime',
280
- SourceVolume: 'timezone',
281
- ReadOnly: true
282
- },
283
- {
284
- ContainerPath: '/tmp/docker.sock',
285
- SourceVolume: 'docker_sock',
286
- ReadOnly: false
287
- }
288
- ]
289
- }
290
- ])
291
- Property('Volumes', [
292
- {
293
- Name: 'timezone',
294
- Host: {
295
- SourcePath: '/etc/localtime'
296
- }
297
- },
298
- {
299
- Name: 'docker_sock',
300
- Host: {
301
- SourcePath: '/var/run/docker.sock'
302
- }
294
+ MountPoints: mount_points
303
295
  }
304
296
  ])
297
+ Property('Volumes', volumes)
305
298
  }
306
299
 
307
300
  Resource('ProxyService') {
data/templates/lambdas.rb CHANGED
@@ -158,7 +158,7 @@ class Lambdas
158
158
  end
159
159
  end
160
160
 
161
- end
161
+ end if (config.key? 'lambdas' and (not config['lambdas'].nil?))
162
162
 
163
163
  end
164
164
  end
data/templates/vpn.rb ADDED
@@ -0,0 +1,280 @@
1
+ CloudFormation do
2
+
3
+ # Template metadata
4
+ AWSTemplateFormatVersion '2010-09-09'
5
+ Description "ciinabox - Vpn v#{ciinabox_version}"
6
+
7
+ # Parameters
8
+ Parameter('EnvironmentType'){ Type 'String' }
9
+ Parameter('EnvironmentName'){ Type 'String' }
10
+ Parameter('VPC'){ Type 'String' }
11
+ Parameter('RouteTablePrivateA'){ Type 'String' }
12
+ Parameter('RouteTablePrivateB'){ Type 'String' }
13
+ Parameter('SubnetPublicA'){ Type 'String' }
14
+ Parameter('SubnetPublicB'){ Type 'String' }
15
+ Parameter('SecurityGroupBackplane'){ Type 'String' }
16
+ Parameter('SecurityGroupOps'){ Type 'String' }
17
+ Parameter('SecurityGroupDev'){ Type 'String' }
18
+
19
+ # Global mappings
20
+ Mapping('EnvironmentType', Mappings['EnvironmentType'])
21
+ Mapping('ecsAMI', ecs_ami)
22
+
23
+ security_groups = []
24
+
25
+ rules = []
26
+ opsAccess.each do |ip|
27
+ rules << { IpProtocol: 'tcp', FromPort: '22', ToPort: '22', CidrIp: ip }
28
+ rules << { IpProtocol: 'tcp', FromPort: '443', ToPort: '443', CidrIp: ip }
29
+ rules << { IpProtocol: 'tcp', FromPort: '9443', ToPort: '9443', CidrIp: ip }
30
+ rules << { IpProtocol: 'tcp', FromPort: '943', ToPort: '943', CidrIp: ip }
31
+ rules << { IpProtocol: 'udp', FromPort: '1194', ToPort: '1194', CidrIp: ip }
32
+ end
33
+
34
+ Resource("VpnSecurityGroupOps") {
35
+ Type 'AWS::EC2::SecurityGroup'
36
+ Property('VpcId', Ref('VPC'))
37
+ Property('GroupDescription', 'Ops External Access')
38
+ Property('SecurityGroupIngress', rules)
39
+ }
40
+
41
+ security_groups << Ref('VpnSecurityGroupOps')
42
+
43
+ rules = []
44
+ devAccess.each do |ip|
45
+ rules << { IpProtocol: 'tcp', FromPort: '443', ToPort: '443', CidrIp: ip }
46
+ rules << { IpProtocol: 'tcp', FromPort: '9443', ToPort: '9443', CidrIp: ip }
47
+ rules << { IpProtocol: 'tcp', FromPort: '943', ToPort: '943', CidrIp: ip }
48
+ rules << { IpProtocol: 'udp', FromPort: '1194', ToPort: '1194', CidrIp: ip }
49
+ end
50
+
51
+ Resource("VpnSecurityGroupDev") {
52
+ Type 'AWS::EC2::SecurityGroup'
53
+ Property('VpcId', Ref('VPC'))
54
+ Property('GroupDescription', 'Dev Team Access')
55
+ Property('SecurityGroupIngress', rules)
56
+ }
57
+
58
+ security_groups << Ref('VpnSecurityGroupDev')
59
+
60
+ if vpn_udp_public
61
+
62
+ rules = []
63
+ publicAccess.each do |ip|
64
+ rules << { IpProtocol: 'udp', FromPort: '1194', ToPort: '1194', CidrIp: ip }
65
+ end
66
+
67
+ Resource("VpnSecurityGroupPublic") {
68
+ Type 'AWS::EC2::SecurityGroup'
69
+ Property('VpcId', Ref('VPC'))
70
+ Property('GroupDescription', 'VPN Access')
71
+ Property('SecurityGroupIngress', rules)
72
+ }
73
+
74
+ security_groups << Ref('VpnSecurityGroupPublic')
75
+ end
76
+
77
+ IAM_Role("Role") {
78
+ AssumeRolePolicyDocument({
79
+ Statement: [
80
+ Effect: 'Allow',
81
+ Principal: { Service: [ 'ec2.amazonaws.com' ] },
82
+ Action: [ 'sts:AssumeRole' ]
83
+ ]
84
+ })
85
+ Path '/'
86
+ Policies([
87
+ {
88
+ PolicyName: 'read-only',
89
+ PolicyDocument: {
90
+ Statement: [
91
+ {
92
+ Effect: 'Allow',
93
+ Action: [ 'autoscaling:Describe*', 'ec2:Describe*', 's3:Get*', 's3:List*'],
94
+ Resource: '*'
95
+ }
96
+ ]
97
+ }
98
+ },
99
+ {
100
+ PolicyName: 'attach-ec2-volumes',
101
+ PolicyDocument: {
102
+ Statement: [
103
+ {
104
+ Effect: 'Allow',
105
+ Action: [ 'ec2:AttachVolume','ec2:CreateVolume', 'ec2:Describe*', 'ec2:DetachVolume' ],
106
+ Resource: '*'
107
+ }
108
+ ]
109
+ }
110
+ },
111
+ {
112
+ PolicyName: 'AttachNetworkInterface',
113
+ PolicyDocument: {
114
+ Statement: [
115
+ {
116
+ Effect: 'Allow',
117
+ Action: [ 'ec2:DescribeNetworkInterfaces', 'ec2:AttachNetworkInterface','ec2:AssociateAddress' ],
118
+ Resource: '*'
119
+ }
120
+ ]
121
+ }
122
+ },
123
+ {
124
+ PolicyName: 'ECSServiceRole',
125
+ PolicyDocument: {
126
+ Statement: [
127
+ {
128
+ Effect: 'Allow',
129
+ Action: [
130
+ 'ecs:CreateCluster',
131
+ 'ecs:DeregisterContainerInstance',
132
+ 'ecs:DiscoverPollEndpoint',
133
+ 'ecs:Poll',
134
+ 'ecs:RegisterContainerInstance',
135
+ 'ecs:StartTelemetrySession',
136
+ 'ecs:Submit*',
137
+ 'ec2:AuthorizeSecurityGroupIngress'
138
+ ],
139
+ Resource: '*'
140
+ }
141
+ ]
142
+ }
143
+ }
144
+ ])
145
+ }
146
+
147
+ ECS_Cluster('CiinaboxVpn')
148
+
149
+ InstanceProfile('InstanceProfile') {
150
+ Path '/'
151
+ Roles [ Ref('Role') ]
152
+ }
153
+
154
+ EC2_EIP('VpnIPAddress') {
155
+ Domain 'vpc'
156
+ }
157
+
158
+ EC2_Volume("VpnVolume") {
159
+ DeletionPolicy 'Snapshot'
160
+ Size '10'
161
+ VolumeType 'gp2'
162
+ if defined? vpn_data_volume_snapshot
163
+ SnapshotId vpn_data_volume_snapshot
164
+ end
165
+ AvailabilityZone FnSelect(0, FnGetAZs(""))
166
+ addTag('Name', 'ciinabox-vpn-config-xx')
167
+ addTag('Environment', 'ciinabox')
168
+ addTag('EnvironmentType', 'ciinabox')
169
+ addTag('shelvery:create_backup','true')
170
+ addTag('shelvery:config:shelvery_keep_daily_backups', '7')
171
+ addTag('shelvery:config:shelvery_keep_weekly_backups', '4')
172
+ addTag('shelvery:config:shelvery_keep_monthly_backups', '12')
173
+ }
174
+
175
+ AutoScaling_LaunchConfiguration('LaunchConfig') {
176
+ DependsOn ['VpnIPAddress']
177
+ ImageId FnFindInMap('ecsAMI', Ref('AWS::Region'), 'ami')
178
+ KeyName FnFindInMap('EnvironmentType', 'ciinabox', 'KeyName')
179
+ AssociatePublicIpAddress true
180
+ IamInstanceProfile Ref('InstanceProfile')
181
+ SecurityGroups security_groups
182
+ Property('InstanceType', vpnInstanceType)
183
+ UserData FnBase64(FnJoin('',[
184
+ "#!/bin/bash\n",
185
+ "echo ECS_CLUSTER=", Ref('CiinaboxVpn'), " >> /etc/ecs/ecs.config\n",
186
+ "INSTANCE_ID=$(echo `/opt/aws/bin/ec2-metadata -i | cut -f2 -d:`)\n",
187
+ 'export NEW_HOSTNAME=', Ref('EnvironmentName'),"-vpn-xx-`/opt/aws/bin/ec2-metadata --instance-id|/usr/bin/awk '{print $2}'`", "\n",
188
+ "echo \"NEW_HOSTNAME=$NEW_HOSTNAME\" \n",
189
+ "hostname $NEW_HOSTNAME\n",
190
+ "sed -i \"s/^HOSTNAME=.*/HOSTNAME=$NEW_HOSTNAME/\" /etc/sysconfig/network\n",
191
+ "stop ecs\n",
192
+ "service docker stop\n",
193
+ "aws --region ", Ref("AWS::Region"), " ec2 attach-volume --volume-id ", Ref('VpnVolume'), " --instance-id ${INSTANCE_ID} --device /dev/sdb\n",
194
+ 'aws --region ', Ref('AWS::Region'), ' ec2 associate-address --allocation-id ', FnGetAtt('VpnIPAddress','AllocationId') ," --instance-id ${INSTANCE_ID}\n",
195
+ "sleep 3\n",
196
+ "mkdir /data \n",
197
+ "e2fsck -fy /dev/xvdb ; if [ $? -eq 8 ]; then mkfs.ext4 /dev/xvdb && mount /dev/xvdb /data; else mount /dev/xvdb /data; fi\n",
198
+ "service docker start\n",
199
+ "start ecs\n",
200
+ ]))
201
+ }
202
+
203
+ AutoScalingGroup('AutoScaleGroup') {
204
+ UpdatePolicy('AutoScalingRollingUpdate', {
205
+ 'MinInstancesInService' => '0',
206
+ 'MaxBatchSize' => '1',
207
+ })
208
+ LaunchConfigurationName Ref('LaunchConfig')
209
+ HealthCheckGracePeriod '500'
210
+ HealthCheckType 'EC2'
211
+ MinSize 1
212
+ MaxSize 1
213
+ VPCZoneIdentifier [ Ref('SubnetPublicA') ]
214
+ addTag('Name', FnJoin('-',[Ref('EnvironmentName'), 'vpn' , 'xx']), true)
215
+ addTag('Environment', Ref('EnvironmentName'), true)
216
+ addTag('EnvironmentType', Ref('EnvironmentType'), true)
217
+ addTag('Role', 'vpn', true)
218
+ }
219
+
220
+ Route53_RecordSet('VpnRecordSet') {
221
+ DependsOn ['VpnIPAddress']
222
+ HostedZoneName FnJoin('', [ dns_domain, '.' ])
223
+ Comment 'Vpn record set'
224
+ Name FnJoin('', [ 'opvn.', dns_domain, '.' ])
225
+ Type 'A'
226
+ TTL '60'
227
+ ResourceRecords [ Ref('VpnIPAddress') ]
228
+ }
229
+
230
+ ECS_TaskDefinition('OpenVpnTask') {
231
+ ContainerDefinitions([
232
+ {
233
+ Name: 'openvpn',
234
+ MemoryReservation: '1024',
235
+ Cpu: '1024',
236
+ Image: 'base2/openvpn-as',
237
+ Essential: true,
238
+ Privileged: true,
239
+ MountPoints: [
240
+ {
241
+ ContainerPath: '/etc/localtime',
242
+ SourceVolume: 'timezone',
243
+ ReadOnly: true
244
+ },
245
+ {
246
+ ContainerPath: '/config',
247
+ SourceVolume: 'data',
248
+ ReadOnly: false
249
+ }
250
+ ]
251
+ }
252
+ ])
253
+ NetworkMode 'host'
254
+ Volumes([
255
+ {
256
+ Name: 'timezone',
257
+ Host: {
258
+ SourcePath: '/etc/localtime'
259
+ }
260
+ },
261
+ {
262
+ Name: 'data',
263
+ Host: {
264
+ SourcePath: '/data'
265
+ }
266
+ }
267
+ ])
268
+ }
269
+
270
+ ECS_Service('OpenVpnService') {
271
+ Cluster Ref('CiinaboxVpn')
272
+ DesiredCount 1
273
+ DeploymentConfiguration({
274
+ MaximumPercent: 100,
275
+ MinimumHealthyPercent: 0
276
+ })
277
+ TaskDefinition Ref('OpenVpnTask')
278
+ }
279
+
280
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ciinabox-ecs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 0.2.10.alpha.1529494989
5
5
  platform: ruby
6
6
  authors:
7
7
  - Base2Services
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-07 00:00:00.000000000 Z
11
+ date: 2018-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -24,34 +24,62 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-s3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-cloudformation
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: cfndsl
29
57
  requirement: !ruby/object:Gem::Requirement
30
58
  requirements:
31
59
  - - "~>"
32
60
  - !ruby/object:Gem::Version
33
- version: 0.15.2
61
+ version: '0.16'
34
62
  type: :runtime
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
38
66
  - - "~>"
39
67
  - !ruby/object:Gem::Version
40
- version: 0.15.2
68
+ version: '0.16'
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: cfn_manage
43
71
  requirement: !ruby/object:Gem::Requirement
44
72
  requirements:
45
73
  - - "~>"
46
74
  - !ruby/object:Gem::Version
47
- version: 0.3.0
75
+ version: '0'
48
76
  type: :runtime
49
77
  prerelease: false
50
78
  version_requirements: !ruby/object:Gem::Requirement
51
79
  requirements:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
- version: 0.3.0
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: deep_merge
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -104,7 +132,6 @@ files:
104
132
  - ext/helper.rb
105
133
  - ext/policies.rb
106
134
  - ext/zip_helper.rb
107
- - lambdas/acm_issuer_validator/install.sh
108
135
  - lambdas/acm_issuer_validator/lib/install.sh
109
136
  - lib/ciinabox-ecs.rb
110
137
  - templates/bastion.rb
@@ -120,6 +147,7 @@ files:
120
147
  - templates/services/jenkins.rb
121
148
  - templates/services/nexus.rb
122
149
  - templates/vpc.rb
150
+ - templates/vpn.rb
123
151
  homepage: https://github.com/base2Services/ciinabox-ecs
124
152
  licenses:
125
153
  - MIT
@@ -135,12 +163,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
163
  version: '0'
136
164
  required_rubygems_version: !ruby/object:Gem::Requirement
137
165
  requirements:
138
- - - ">="
166
+ - - ">"
139
167
  - !ruby/object:Gem::Version
140
- version: '0'
168
+ version: 1.3.1
141
169
  requirements: []
142
170
  rubyforge_project:
143
- rubygems_version: 2.7.6
171
+ rubygems_version: 2.7.7
144
172
  signing_key:
145
173
  specification_version: 4
146
174
  summary: Manage ciinabox on Aws Ecs
@@ -1,8 +0,0 @@
1
- #!/bin/bash
2
-
3
- rm -rf lib/*
4
-
5
- DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
6
- rm -rf $DIR/lib/*
7
-
8
- docker run --rm -v $DIR:/dst -w /dst -u 1000 python:3-alpine pip install aws-acm-cert-validator==0.1.11 -t lib