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.
- checksums.yaml +4 -4
- data/.github/workflows/build-gem.yml +25 -0
- data/.github/workflows/release-gem.yml +34 -0
- data/.github/workflows/release-image.yml +33 -0
- data/Dockerfile +26 -0
- data/Gemfile.lock +30 -38
- data/README.md +1 -224
- data/cfn-vpn.gemspec +3 -5
- data/docs/README.md +44 -0
- data/docs/certificate-users.md +89 -0
- data/docs/getting-started.md +99 -0
- data/docs/modifying.md +67 -0
- data/docs/routes.md +84 -0
- data/docs/scheduling.md +32 -0
- data/docs/sessions.md +27 -0
- data/lib/cfnvpn.rb +32 -24
- data/lib/cfnvpn/{client.rb → actions/client.rb} +11 -8
- data/lib/cfnvpn/actions/embedded.rb +110 -0
- data/lib/cfnvpn/actions/init.rb +130 -0
- data/lib/cfnvpn/actions/modify.rb +149 -0
- data/lib/cfnvpn/actions/params.rb +73 -0
- data/lib/cfnvpn/{revoke.rb → actions/revoke.rb} +10 -8
- data/lib/cfnvpn/actions/routes.rb +144 -0
- data/lib/cfnvpn/{sessions.rb → actions/sessions.rb} +7 -6
- data/lib/cfnvpn/{share.rb → actions/share.rb} +10 -10
- data/lib/cfnvpn/actions/subnets.rb +78 -0
- data/lib/cfnvpn/certificates.rb +70 -20
- data/lib/cfnvpn/clientvpn.rb +34 -68
- data/lib/cfnvpn/compiler.rb +23 -0
- data/lib/cfnvpn/config.rb +34 -77
- data/lib/cfnvpn/{cloudformation.rb → deployer.rb} +48 -20
- data/lib/cfnvpn/globals.rb +16 -0
- data/lib/cfnvpn/log.rb +26 -26
- data/lib/cfnvpn/s3.rb +13 -3
- data/lib/cfnvpn/string.rb +29 -0
- data/lib/cfnvpn/templates/helper.rb +14 -0
- data/lib/cfnvpn/templates/vpn.rb +344 -0
- data/lib/cfnvpn/version.rb +1 -1
- metadata +56 -41
- data/lib/cfnvpn/cfhighlander.rb +0 -49
- data/lib/cfnvpn/init.rb +0 -107
- data/lib/cfnvpn/modify.rb +0 -102
- data/lib/cfnvpn/routes.rb +0 -83
- 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/
|
6
|
-
require 'cfnvpn/
|
7
|
-
require 'cfnvpn/
|
8
|
-
require 'cfnvpn/
|
9
|
-
require 'cfnvpn/
|
10
|
-
require 'cfnvpn/
|
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::
|
28
|
-
tasks["
|
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::
|
31
|
-
tasks["
|
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::
|
34
|
-
tasks["
|
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::
|
37
|
-
tasks["
|
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::
|
40
|
-
tasks["
|
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::
|
43
|
-
tasks["
|
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 = "#{
|
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
|