cfn-vpn 0.4.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build-gem.yml +25 -0
  3. data/.github/workflows/release-gem.yml +34 -0
  4. data/.github/workflows/release-image.yml +33 -0
  5. data/Dockerfile +26 -0
  6. data/Gemfile.lock +30 -38
  7. data/README.md +1 -224
  8. data/cfn-vpn.gemspec +3 -5
  9. data/docs/README.md +44 -0
  10. data/docs/certificate-users.md +89 -0
  11. data/docs/getting-started.md +99 -0
  12. data/docs/modifying.md +67 -0
  13. data/docs/routes.md +84 -0
  14. data/docs/scheduling.md +32 -0
  15. data/docs/sessions.md +27 -0
  16. data/lib/cfnvpn.rb +32 -24
  17. data/lib/cfnvpn/{client.rb → actions/client.rb} +11 -8
  18. data/lib/cfnvpn/actions/embedded.rb +110 -0
  19. data/lib/cfnvpn/actions/init.rb +130 -0
  20. data/lib/cfnvpn/actions/modify.rb +149 -0
  21. data/lib/cfnvpn/actions/params.rb +73 -0
  22. data/lib/cfnvpn/{revoke.rb → actions/revoke.rb} +10 -8
  23. data/lib/cfnvpn/actions/routes.rb +144 -0
  24. data/lib/cfnvpn/{sessions.rb → actions/sessions.rb} +7 -6
  25. data/lib/cfnvpn/{share.rb → actions/share.rb} +10 -10
  26. data/lib/cfnvpn/actions/subnets.rb +78 -0
  27. data/lib/cfnvpn/certificates.rb +70 -20
  28. data/lib/cfnvpn/clientvpn.rb +34 -68
  29. data/lib/cfnvpn/compiler.rb +23 -0
  30. data/lib/cfnvpn/config.rb +34 -77
  31. data/lib/cfnvpn/{cloudformation.rb → deployer.rb} +48 -20
  32. data/lib/cfnvpn/globals.rb +16 -0
  33. data/lib/cfnvpn/log.rb +26 -26
  34. data/lib/cfnvpn/s3.rb +13 -3
  35. data/lib/cfnvpn/string.rb +29 -0
  36. data/lib/cfnvpn/templates/helper.rb +14 -0
  37. data/lib/cfnvpn/templates/vpn.rb +344 -0
  38. data/lib/cfnvpn/version.rb +1 -1
  39. metadata +56 -41
  40. data/lib/cfnvpn/cfhighlander.rb +0 -49
  41. data/lib/cfnvpn/init.rb +0 -107
  42. data/lib/cfnvpn/modify.rb +0 -102
  43. data/lib/cfnvpn/routes.rb +0 -83
  44. data/lib/cfnvpn/templates/cfnvpn.cfhighlander.rb.tt +0 -27
data/docs/sessions.md ADDED
@@ -0,0 +1,27 @@
1
+ # Managing Client-VPN Sessions
2
+
3
+ ## Show Sessions
4
+
5
+ This is show a table of current connections on the vpn. You can then kill sessions by using the connection id.
6
+
7
+ ```sh
8
+ cfn-vpn sessions [name]
9
+ ```
10
+
11
+ The sessions are displayed in a table format
12
+
13
+ ```bash
14
+ +-------------+---------------------+--------+-------------+-----------------------------------+---------------+--------------+
15
+ | Common Name | Connected (UTC) | Status | IP Address | Connection ID | Ingress Bytes | Egress Bytes |
16
+ +-------------+---------------------+--------+-------------+-----------------------------------+---------------+--------------+
17
+ | user1 | 2019-06-28 04:58:19 | active | 10.250.0.98 | cvpn-connection-05bcc579cb3fdf9a3 | 3000 | 2679 |
18
+ +-------------+---------------------+--------+-------------+-----------------------------------+---------------+--------------+
19
+ ```
20
+
21
+ ## Terminate a Session
22
+
23
+ To terminate a Client-VPN session specify the `--kill` flag with the connection id to kill the session.
24
+
25
+ ```sh
26
+ cfn-vpn sessions myvpn --kill cvpn-connection-05bcc579cb3fdf9a3
27
+ ```
data/lib/cfnvpn.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  require 'thor'
2
2
  require 'cfnvpn/version'
3
- require 'cfnvpn/init'
4
- require 'cfnvpn/modify'
5
- require 'cfnvpn/config'
6
- require 'cfnvpn/client'
7
- require 'cfnvpn/revoke'
8
- require 'cfnvpn/sessions'
9
- require 'cfnvpn/routes'
10
- require 'cfnvpn/share'
3
+ require 'cfnvpn/actions/init'
4
+ require 'cfnvpn/actions/modify'
5
+ require 'cfnvpn/actions/client'
6
+ require 'cfnvpn/actions/revoke'
7
+ require 'cfnvpn/actions/sessions'
8
+ require 'cfnvpn/actions/routes'
9
+ require 'cfnvpn/actions/share'
10
+ require 'cfnvpn/actions/embedded'
11
+ require 'cfnvpn/actions/subnets'
12
+ require 'cfnvpn/actions/params'
11
13
 
12
14
  module CfnVpn
13
15
  class Cli < Thor
@@ -18,29 +20,35 @@ module CfnVpn
18
20
  puts CfnVpn::VERSION
19
21
  end
20
22
 
21
- register CfnVpn::Init, 'init', 'init [name]', 'Create a AWS Client VPN'
22
- tasks["init"].options = CfnVpn::Init.class_options
23
+ register CfnVpn::Actions::Init, 'init', 'init [name]', 'Create a AWS Client VPN'
24
+ tasks["init"].options = CfnVpn::Actions::Init.class_options
23
25
 
24
- register CfnVpn::Modify, 'modify', 'modify [name]', 'Modify your AWS Client VPN'
25
- tasks["modify"].options = CfnVpn::Modify.class_options
26
+ register CfnVpn::Actions::Modify, 'modify', 'modify [name]', 'Modify your AWS Client VPN'
27
+ tasks["modify"].options = CfnVpn::Actions::Modify.class_options
26
28
 
27
- register CfnVpn::Config, 'config', 'config [name]', 'Retrieve the config for the AWS Client VPN'
28
- tasks["config"].options = CfnVpn::Config.class_options
29
+ register CfnVpn::Actions::Client, 'client', 'client [name]', 'Create a new client certificate'
30
+ tasks["client"].options = CfnVpn::Actions::Client.class_options
29
31
 
30
- register CfnVpn::Client, 'client', 'client [name]', 'Create a new client certificate'
31
- tasks["client"].options = CfnVpn::Client.class_options
32
+ register CfnVpn::Actions::Revoke, 'revoke', 'revoke [name]', 'Revoke a client certificate'
33
+ tasks["revoke"].options = CfnVpn::Actions::Revoke.class_options
32
34
 
33
- register CfnVpn::Revoke, 'revoke', 'revoke [name]', 'Revoke a client certificate'
34
- tasks["revoke"].options = CfnVpn::Revoke.class_options
35
+ register CfnVpn::Actions::Sessions, 'sessions', 'sessions [name]', 'List and kill current vpn connections'
36
+ tasks["sessions"].options = CfnVpn::Actions::Sessions.class_options
35
37
 
36
- register CfnVpn::Sessions, 'sessions', 'sessions [name]', 'List and kill current vpn connections'
37
- tasks["sessions"].options = CfnVpn::Sessions.class_options
38
+ register CfnVpn::Actions::Routes, 'routes', 'routes [name]', 'List, add or delete client vpn routes'
39
+ tasks["routes"].options = CfnVpn::Actions::Routes.class_options
38
40
 
39
- register CfnVpn::Routes, 'routes', 'routes [name]', 'List, add or delete client vpn routes'
40
- tasks["routes"].options = CfnVpn::Routes.class_options
41
+ register CfnVpn::Actions::Share, 'share', 'share [name]', 'Provide a user with a s3 signed download for certificates and config'
42
+ tasks["share"].options = CfnVpn::Actions::Share.class_options
41
43
 
42
- register CfnVpn::Share, 'share', 'share [name]', 'Provide a user with a s3 signed download for certificates and config'
43
- tasks["share"].options = CfnVpn::Share.class_options
44
+ register CfnVpn::Actions::Embedded, 'embedded', 'embedded [name]', 'Embed client certs into config and generate S3 presigned URL'
45
+ tasks["embedded"].options = CfnVpn::Actions::Embedded.class_options
46
+
47
+ register CfnVpn::Actions::Subnets, 'subnets', 'subnets [name]', 'Manage subnet associations for the client vpn'
48
+ tasks["subnets"].options = CfnVpn::Actions::Subnets.class_options
49
+
50
+ register CfnVpn::Actions::Params, 'params', 'params [name]', 'Display or dump to yaml the current cfnvpn params'
51
+ tasks["params"].options = CfnVpn::Actions::Params.class_options
44
52
 
45
53
  end
46
54
  end
@@ -1,11 +1,12 @@
1
1
  require 'thor'
2
+ require 'fileutils'
2
3
  require 'cfnvpn/log'
3
4
  require 'cfnvpn/s3'
5
+ require 'cfnvpn/globals'
4
6
 
5
- module CfnVpn
7
+ module CfnVpn::Actions
6
8
  class Client < Thor::Group
7
- include Thor::Actions
8
- include CfnVpn::Log
9
+ include Thor::Actions
9
10
 
10
11
  argument :name
11
12
 
@@ -15,26 +16,28 @@ module CfnVpn
15
16
 
16
17
  class_option :bucket, desc: 's3 bucket', required: true
17
18
  class_option :client_cn, desc: 'client certificate common name', required: true
19
+ class_option :easyrsa_local, type: :boolean, default: false, desc: 'run the easyrsa executable from your local rather than from docker'
18
20
 
19
21
  def self.source_root
20
22
  File.dirname(__FILE__)
21
23
  end
22
24
 
23
25
  def set_loglevel
24
- Log.logger.level = Logger::DEBUG if @options['verbose']
26
+ CfnVpn::Log.logger.level = Logger::DEBUG if @options['verbose']
25
27
  end
26
28
 
27
29
  def set_directory
28
- @build_dir = "#{ENV['HOME']}/.cfnvpn/#{@name}"
30
+ @build_dir = "#{CfnVpn.cfnvpn_path}/#{@name}"
29
31
  @cert_dir = "#{@build_dir}/certificates"
32
+ FileUtils.mkdir_p(@cert_dir)
30
33
  end
31
34
 
32
35
  def create_certificate
33
36
  s3 = CfnVpn::S3.new(@options['region'],@options['bucket'],@name)
34
37
  s3.get_object("#{@cert_dir}/ca.tar.gz")
35
- Log.logger.info "Generating new client certificate #{@options['client_cn']} using openvpn easy-rsa"
36
- cert = CfnVpn::Certificates.new(@build_dir,@name)
37
- Log.logger.debug cert.generate_client(@options['client_cn'])
38
+ CfnVpn::Log.logger.info "Generating new client certificate #{@options['client_cn']} using openvpn easy-rsa"
39
+ cert = CfnVpn::Certificates.new(@build_dir,@name,@options['easyrsa_local'])
40
+ CfnVpn::Log.logger.debug cert.generate_client(@options['client_cn'])
38
41
  s3.store_object("#{@cert_dir}/#{@options['client_cn']}.tar.gz")
39
42
  end
40
43
 
@@ -0,0 +1,110 @@
1
+ require 'cfnvpn/log'
2
+ require 'cfnvpn/s3'
3
+ require 'cfnvpn/globals'
4
+
5
+ module CfnVpn::Actions
6
+ class Embedded < Thor::Group
7
+ include Thor::Actions
8
+
9
+
10
+ argument :name
11
+
12
+ class_option :profile, desc: 'AWS Profile'
13
+ class_option :region, default: ENV['AWS_REGION'], desc: 'AWS Region'
14
+ class_option :verbose, desc: 'set log level to debug', type: :boolean
15
+
16
+ class_option :bucket, required: true, desc: 'S3 bucket'
17
+ class_option :client_cn, required: true, default: false, desc: 'Client certificates to download'
18
+ class_option :easyrsa_local, type: :boolean, default: false, desc: 'run the easyrsa executable from your local rather than from docker'
19
+ class_option :ignore_routes, alias: :i, type: :boolean, desc: 'Ignore client VPN pushed routes and set routes in config file'
20
+
21
+ def self.source_root
22
+ File.dirname(__FILE__)
23
+ end
24
+
25
+ def set_loglevel
26
+ CfnVpn::Log.logger.level = Logger::DEBUG if @options['verbose']
27
+ end
28
+
29
+ def create_config_directory
30
+ @build_dir = "#{CfnVpn.cfnvpn_path}/#{@name}"
31
+ @config_dir = "#{@build_dir}/config"
32
+ CfnVpn::Log.logger.debug("Creating config directory #{@config_dir}")
33
+ FileUtils.mkdir_p(@config_dir)
34
+ end
35
+
36
+ def download_certificates
37
+ download = true
38
+ if File.exists?("#{@config_dir}/#{@options['client_cn']}.crt")
39
+ download = yes? "Certificates for #{@options['client_cn']} already exist in #{@config_dir}. Do you want to download again? ", :green
40
+ end
41
+
42
+ if download
43
+ CfnVpn::Log.logger.info "Downloading certificates for #{@options['client_cn']} to #{@config_dir}"
44
+ s3 = CfnVpn::S3.new(@options['region'],@options['bucket'],@name)
45
+ s3.get_object("#{@config_dir}/#{@options['client_cn']}.tar.gz")
46
+ cert = CfnVpn::Certificates.new(@build_dir,@name,@options['easyrsa_local'])
47
+ CfnVpn::Log.logger.debug cert.extract_certificate(@options['client_cn'])
48
+ end
49
+ end
50
+
51
+ def download_config
52
+ vpn = CfnVpn::ClientVpn.new(@name,@options['region'])
53
+ @endpoint_id = vpn.get_endpoint_id()
54
+ CfnVpn::Log.logger.debug "downloading client config for #{@endpoint_id}"
55
+ @config = vpn.get_config(@endpoint_id)
56
+ string = (0...8).map { (65 + rand(26)).chr.downcase }.join
57
+ @config.sub!(@endpoint_id, "#{string}.#{@endpoint_id}")
58
+ end
59
+
60
+ def add_routes
61
+ if @options['ignore_routes']
62
+ CfnVpn::Log.logger.debug "Ignoring routes pushed by the client vpn"
63
+ @config.concat("\nroute-nopull\n")
64
+ vpn = CfnVpn::ClientVpn.new(@name,@options['region'])
65
+ routes = vpn.get_route_with_mask
66
+ CfnVpn::Log.logger.debug "Found routes #{routes}"
67
+ routes.each do |r|
68
+ @config.concat("route #{r[:route]} #{r[:mask]}\n")
69
+ end
70
+ dns_servers = vpn.get_dns_servers()
71
+ if dns_servers.any?
72
+ CfnVpn::Log.logger.debug "Found DNS servers #{dns_servers.join(' ')}"
73
+ @config.concat("dhcp-option DNS #{dns_servers.first}\n")
74
+ end
75
+ end
76
+ end
77
+
78
+ def embed_certs
79
+ cert = CfnVpn::Certificates.new(@build_dir,@name,@options['easyrsa_local'])
80
+ CfnVpn::Log.logger.debug cert.extract_certificate(@options['client_cn'])
81
+ CfnVpn::Log.logger.debug "Reading extracted certificate and private key"
82
+ key = File.read("#{@config_dir}/#{@options['client_cn']}.key")
83
+ crt = File.read("#{@config_dir}/#{@options['client_cn']}.crt")
84
+ CfnVpn::Log.logger.debug "Embedding certificate and private key into config"
85
+ @config.concat("\n<key>\n#{key}\n</key>\n")
86
+ @config.concat("\n<cert>\n#{crt}\n</cert>\n")
87
+ end
88
+
89
+ def upload_embedded_config
90
+ @s3 = CfnVpn::S3.new(@options['region'],@options['bucket'],@name)
91
+ @s3.store_embedded_config(@config, @options['client_cn'])
92
+ end
93
+
94
+ def get_presigned_url
95
+ @cn = @options['client_cn']
96
+ @config_url = @s3.get_url("#{@name}_#{@cn}.config.ovpn")
97
+ CfnVpn::Log.logger.debug "Config presigned url: #{@config_url}"
98
+ end
99
+
100
+ def display_url
101
+ CfnVpn::Log.logger.info "Share the below instructions with the user..."
102
+ say "\nDownload the embedded config from the below presigned URL which will expire in 1 hour."
103
+ say "\nConfig:\n"
104
+ say "\tcurl #{@config_url} > #{@name}_#{@cn}.config.ovpn", :cyan
105
+ say "\nOpen #{@name}_#{@cn}.config.ovpn with your favourite openvpn client."
106
+ end
107
+
108
+ end
109
+
110
+ end
@@ -0,0 +1,130 @@
1
+ require 'thor'
2
+ require 'fileutils'
3
+ require 'cfnvpn/deployer'
4
+ require 'cfnvpn/certificates'
5
+ require 'cfnvpn/compiler'
6
+ require 'cfnvpn/log'
7
+ require 'cfnvpn/clientvpn'
8
+ require 'cfnvpn/globals'
9
+
10
+ module CfnVpn::Actions
11
+ class Init < Thor::Group
12
+ include Thor::Actions
13
+
14
+
15
+ argument :name
16
+
17
+ class_option :region, aliases: :r, default: ENV['AWS_REGION'], desc: 'AWS Region'
18
+ class_option :verbose, desc: 'set log level to debug', type: :boolean
19
+
20
+ class_option :server_cn, required: true, desc: 'server certificate common name'
21
+ class_option :client_cn, desc: 'client certificate common name'
22
+ class_option :easyrsa_local, type: :boolean, default: false, desc: 'run the easyrsa executable from your local rather than from docker'
23
+ class_option :bucket, desc: 's3 bucket'
24
+
25
+ class_option :subnet_ids, required: true, type: :array, desc: 'subnet id to associate your vpn with'
26
+ class_option :default_groups, default: [], type: :array, desc: 'groups to allow through the subnet associations when using federated auth'
27
+ class_option :cidr, default: '10.250.0.0/16', desc: 'cidr from which to assign client IP addresses'
28
+ class_option :dns_servers, default: [], type: :array, desc: 'DNS Servers to push to clients.'
29
+
30
+ class_option :split_tunnel, type: :boolean, default: true, desc: 'only push routes to the client on the vpn endpoint'
31
+ class_option :internet_route, type: :string, desc: '[subnet-id] create a default route to the internet through a subnet'
32
+ class_option :protocol, type: :string, default: 'udp', enum: ['udp','tcp'], desc: 'set the protocol for the vpn connections'
33
+
34
+ class_option :start, type: :string, desc: 'cloudwatch event cron schedule in UTC to associate subnets to the client vpn'
35
+ class_option :stop, type: :string, desc: 'cloudwatch event cron schedule in UTC to disassociate subnets to the client vpn'
36
+
37
+ class_option :saml_arn, desc: 'IAM SAML idenditiy providor arn if using SAML federated authentication'
38
+
39
+ def self.source_root
40
+ File.dirname(__FILE__)
41
+ end
42
+
43
+ def set_loglevel
44
+ CfnVpn::Log.logger.level = Logger::DEBUG if @options['verbose']
45
+ end
46
+
47
+ def create_build_directory
48
+ @build_dir = "#{CfnVpn.cfnvpn_path}/#{@name}"
49
+ CfnVpn::Log.logger.debug "creating directory #{@build_dir}"
50
+ FileUtils.mkdir_p(@build_dir)
51
+ end
52
+
53
+ def initialize_config
54
+ @config = {
55
+ region: @options['region'],
56
+ subnet_ids: @options['subnet_ids'],
57
+ cidr: @options['cidr'],
58
+ dns_servers: @options['dns_servers'],
59
+ split_tunnel: @options['split_tunnel'],
60
+ internet_route: @options['internet_route'],
61
+ protocol: @options['protocol'],
62
+ start: @options['start'],
63
+ stop: @options['stop'],
64
+ saml_arn: @options['saml_arn'],
65
+ routes: []
66
+ }
67
+ end
68
+
69
+ def set_type
70
+ @config[:type] = @options['saml_arn'] ? 'federated' : 'certificate'
71
+ @config[:default_groups] = @options['saml_arn'] ? @options['default_groups'] : []
72
+ CfnVpn::Log.logger.info "initialising #{@config[:type]} client vpn"
73
+ end
74
+
75
+ def conditional_options_check
76
+ if @config[:type] == 'certificate'
77
+ if !@options['bucket']
78
+ CfnVpn::Log.logger.error "--bucket option must be specified if creating a client vpn with certificate based authentication"
79
+ exit 1
80
+ end
81
+ end
82
+ end
83
+
84
+ def stack_exist
85
+ @deployer = CfnVpn::Deployer.new(@options['region'],@name)
86
+ if @deployer.does_cf_stack_exist()
87
+ CfnVpn::Log.logger.error "#{@name}-cfnvpn stack already exists in this account in region #{@options['region']}, use the modify command to alter the stack"
88
+ exit 1
89
+ end
90
+ end
91
+
92
+ # create certificates
93
+ def generate_server_certificates
94
+ CfnVpn::Log.logger.info "Generating certificates using openvpn easy-rsa"
95
+ cert = CfnVpn::Certificates.new(@build_dir,@name,@options['easyrsa_local'])
96
+ @client_cn = @options['client_cn'] ? @options['client_cn'] : "client-vpn.#{@options['server_cn']}"
97
+ cert.generate_ca(@options['server_cn'],@client_cn)
98
+ end
99
+
100
+ def upload_certificates
101
+ cert = CfnVpn::Certificates.new(@build_dir,@name,@options['easyrsa_local'])
102
+ @config[:server_cert_arn] = cert.upload_certificates(@options['region'],'server','server',@options['server_cn'])
103
+ if @config[:type] == 'certificate'
104
+ # we only need the server certificate to ACM if it is a SAML federated client vpn
105
+ @config[:client_cert_arn] = cert.upload_certificates(@options['region'],@client_cn,'client')
106
+ # and only need to upload the certs to s3 if using certificate authenitcation
107
+ s3 = CfnVpn::S3.new(@options['region'],@options['bucket'],@name)
108
+ s3.store_object("#{@build_dir}/certificates/ca.tar.gz")
109
+ end
110
+ end
111
+
112
+ def deploy_vpn
113
+ compiler = CfnVpn::Compiler.new(@name, @config)
114
+ template_body = compiler.compile
115
+ CfnVpn::Log.logger.info "Launching cloudformation stack #{@name}-cfnvpn in #{@options['region']}"
116
+ change_set, change_set_type = @deployer.create_change_set(template_body: template_body)
117
+ @deployer.wait_for_changeset(change_set.id)
118
+ @deployer.execute_change_set(change_set.id)
119
+ @deployer.wait_for_execute(change_set_type)
120
+ CfnVpn::Log.logger.info "Changeset #{change_set_type} complete"
121
+ end
122
+
123
+ def finish
124
+ vpn = CfnVpn::ClientVpn.new(@name,@options['region'])
125
+ @endpoint_id = vpn.get_endpoint_id()
126
+ CfnVpn::Log.logger.info "Client VPN #{@endpoint_id} created. Run `cfn-vpn config #{@name}` to setup the client config"
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,149 @@
1
+ require 'thor'
2
+ require 'fileutils'
3
+ require 'terminal-table'
4
+ require 'cfnvpn/deployer'
5
+ require 'cfnvpn/certificates'
6
+ require 'cfnvpn/compiler'
7
+ require 'cfnvpn/log'
8
+ require 'cfnvpn/clientvpn'
9
+ require 'cfnvpn/globals'
10
+ require 'cfnvpn/config'
11
+
12
+ module CfnVpn::Actions
13
+ class Modify < Thor::Group
14
+ include Thor::Actions
15
+
16
+
17
+ argument :name
18
+
19
+ class_option :region, aliases: :r, default: ENV['AWS_REGION'], desc: 'AWS Region'
20
+ class_option :verbose, desc: 'set log level to debug', type: :boolean
21
+
22
+ class_option :subnet_ids, type: :array, desc: 'overwrite all subnet associations'
23
+ class_option :add_subnet_ids, type: :array, desc: 'add to existing subnet associations'
24
+ class_option :del_subnet_ids, type: :array, desc: 'delete subnet associations'
25
+
26
+ class_option :default_groups, type: :array, desc: 'groups to allow through the subnet associations when using federated auth'
27
+
28
+ class_option :dns_servers, type: :array, desc: 'DNS Servers to push to clients.'
29
+ class_option :no_dns_servers, type: :boolean, desc: 'Remove the DNS Servers from the client vpn'
30
+
31
+ class_option :cidr, desc: 'cidr from which to assign client IP addresses'
32
+ class_option :split_tunnel, type: :boolean, desc: 'only push routes to the client on the vpn endpoint'
33
+ class_option :internet_route, type: :string, desc: '[subnet-id] create a default route to the internet through a subnet'
34
+ class_option :protocol, type: :string, enum: ['udp','tcp'], desc: 'set the protocol for the vpn connections'
35
+
36
+ class_option :start, type: :string, desc: 'cloudwatch event cron schedule in UTC to associate subnets to the client vpn'
37
+ class_option :stop, type: :string, desc: 'cloudwatch event cron schedule in UTC to disassociate subnets to the client vpn'
38
+
39
+ class_option :param_yaml, type: :string, desc: 'pass in cfnvpn params through YAML file'
40
+
41
+ def self.source_root
42
+ File.dirname(__FILE__)
43
+ end
44
+
45
+ def set_loglevel
46
+ CfnVpn::Log.logger.level = Logger::DEBUG if @options['verbose']
47
+ end
48
+
49
+ def create_build_directory
50
+ @build_dir = "#{CfnVpn.cfnvpn_path}/#{@name}"
51
+ CfnVpn::Log.logger.debug "creating directory #{@build_dir}"
52
+ FileUtils.mkdir_p(@build_dir)
53
+ end
54
+
55
+ def stack_exist
56
+ @deployer = CfnVpn::Deployer.new(@options['region'],@name)
57
+ if !@deployer.does_cf_stack_exist()
58
+ CfnVpn::Log.logger.error "#{@name}-cfnvpn stack doesn't exists in this account in region #{@options['region']}\n Try running `cfn-vpn init #{@name}` to setup the stack"
59
+ exit 1
60
+ end
61
+ end
62
+
63
+ def initialize_config
64
+ @config = CfnVpn::Config.get_config(@options[:region], @name)
65
+
66
+ CfnVpn::Log.logger.debug "Current config:\n#{@config}"
67
+
68
+ if @options[:param_yaml]
69
+ CfnVpn::Log.logger.debug "Loading config from YAML file #{@options[:param_yaml]}"
70
+ @config = CfnVpn::Config.get_config_from_yaml_file(@options[:param_yaml])
71
+ else
72
+ CfnVpn::Log.logger.debug "Loading config from options"
73
+ @options.each do |key, value|
74
+ next if [:verbose].include? key
75
+ @config[key.to_sym] = value
76
+ end
77
+
78
+ if @options['add_subnet_ids']
79
+ @config[:subnet_ids].concat @options['add_subnet_ids']
80
+ end
81
+
82
+ if @options['del_subnet_ids']
83
+ @config[:subnet_ids].reject!{ |subnet| @options['del_subnet_ids'].include? subnet }
84
+ end
85
+
86
+ if @options['no_dns_servers']
87
+ @config[:dns_servers] = []
88
+ end
89
+ end
90
+
91
+ if @config[:saml_arn] && @options[:default_groups]
92
+ @config[:default_groups] = @options[:default_groups]
93
+ end
94
+
95
+ CfnVpn::Log.logger.debug "Modified config:\n#{@config}"
96
+ end
97
+
98
+ def deploy_vpn
99
+ compiler = CfnVpn::Compiler.new(@name, @config)
100
+ template_body = compiler.compile
101
+ CfnVpn::Log.logger.info "Creating cloudformation changeset for stack #{@name}-cfnvpn in #{@options['region']}"
102
+ change_set, change_set_type = @deployer.create_change_set(template_body: template_body)
103
+ @deployer.wait_for_changeset(change_set.id)
104
+ changeset_response = @deployer.get_change_set(change_set.id)
105
+
106
+ changes = {"Add" => [], "Modify" => [], "Remove" => []}
107
+ change_colours = {"Add" => "green", "Modify" => 'yellow', "Remove" => 'red'}
108
+
109
+ changeset_response.changes.each do |change|
110
+ action = change.resource_change.action
111
+ changes[action].push([
112
+ change.resource_change.logical_resource_id,
113
+ change.resource_change.resource_type,
114
+ change.resource_change.replacement ? change.resource_change.replacement : 'N/A',
115
+ change.resource_change.details.collect {|detail| detail.target.name }.join(' , ')
116
+ ])
117
+ end
118
+
119
+ changes.each do |type, rows|
120
+ next if !rows.any?
121
+ puts "\n"
122
+ table = Terminal::Table.new(
123
+ :title => type,
124
+ :headings => ['Logical Resource Id', 'Resource Type', 'Replacement', 'Changes'],
125
+ :rows => rows)
126
+ puts table.to_s.send(change_colours[type])
127
+ end
128
+
129
+ CfnVpn::Log.logger.info "Cloudformation changeset changes:"
130
+ puts "\n"
131
+ continue = yes? "Continue?", :green
132
+ if !continue
133
+ CfnVpn::Log.logger.info("Cancelled cfn-vpn modifiy #{@name}")
134
+ exit 1
135
+ end
136
+
137
+ @deployer.execute_change_set(change_set.id)
138
+ @deployer.wait_for_execute(change_set_type)
139
+ CfnVpn::Log.logger.info "Changeset #{change_set_type} complete"
140
+ end
141
+
142
+ def finish
143
+ vpn = CfnVpn::ClientVpn.new(@name,@options['region'])
144
+ @endpoint_id = vpn.get_endpoint_id()
145
+ CfnVpn::Log.logger.info "Client VPN #{@endpoint_id} modified."
146
+ end
147
+
148
+ end
149
+ end