cfn-vpn 1.2.0 → 1.3.0
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/Gemfile.lock +21 -19
- data/cfn-vpn.gemspec +1 -0
- data/docs/README.md +2 -2
- data/docs/routes.md +34 -20
- data/lib/cfnvpn/actions/init.rb +15 -11
- data/lib/cfnvpn/actions/modify.rb +20 -0
- data/lib/cfnvpn/actions/routes.rb +59 -7
- data/lib/cfnvpn/clientvpn.rb +18 -0
- data/lib/cfnvpn/s3.rb +30 -0
- data/lib/cfnvpn/s3_bucket.rb +48 -0
- data/lib/cfnvpn/string.rb +4 -0
- data/lib/cfnvpn/templates/lambdas.rb +35 -0
- data/lib/cfnvpn/templates/lambdas/auto_route_populator/app.py +175 -0
- data/lib/cfnvpn/templates/lambdas/scheduler/app.py +36 -0
- data/lib/cfnvpn/templates/vpn.rb +188 -92
- data/lib/cfnvpn/version.rb +1 -1
- metadata +20 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e435821426e5e0e4cf69244013bf0ac1cd2ae030a024fb7b12827543f81c87af
|
|
4
|
+
data.tar.gz: 9c8f7968839a339966f5eb8aa9680c625764c326f84946eb8bcf08d6da818cd1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 305a236f13203b9ffd4d5d68fc6a87ff912700d3947420b02fa477b787f4c57768e9d4485c8a22bba354547449b3d39ef57811ee43a1d9093623ecad24b690ee
|
|
7
|
+
data.tar.gz: 82f8ba462728afb71fc18e886c20fee0d9a5ea201a071262b6ead3bd83549f4c5eb58495c0603ee1567c94da90d8a3290b9d37fcf05312e1922678db529dcb15
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
cfn-vpn (1.
|
|
4
|
+
cfn-vpn (1.2.0)
|
|
5
5
|
aws-sdk-acm (~> 1, < 2)
|
|
6
6
|
aws-sdk-cloudformation (~> 1, < 2)
|
|
7
7
|
aws-sdk-ec2 (~> 1.95, < 2)
|
|
@@ -9,46 +9,48 @@ PATH
|
|
|
9
9
|
aws-sdk-ssm (~> 1, < 2)
|
|
10
10
|
cfndsl (~> 1, < 2)
|
|
11
11
|
netaddr (= 2.0.4)
|
|
12
|
+
rubyzip (~> 2.3)
|
|
12
13
|
terminal-table (~> 1, < 2)
|
|
13
14
|
thor (~> 0.20)
|
|
14
15
|
|
|
15
16
|
GEM
|
|
16
17
|
remote: https://rubygems.org/
|
|
17
18
|
specs:
|
|
18
|
-
aws-eventstream (1.1.
|
|
19
|
-
aws-partitions (1.
|
|
20
|
-
aws-sdk-acm (1.
|
|
21
|
-
aws-sdk-core (~> 3, >= 3.
|
|
19
|
+
aws-eventstream (1.1.1)
|
|
20
|
+
aws-partitions (1.432.0)
|
|
21
|
+
aws-sdk-acm (1.39.0)
|
|
22
|
+
aws-sdk-core (~> 3, >= 3.112.0)
|
|
22
23
|
aws-sigv4 (~> 1.1)
|
|
23
|
-
aws-sdk-cloudformation (1.
|
|
24
|
-
aws-sdk-core (~> 3, >= 3.
|
|
24
|
+
aws-sdk-cloudformation (1.49.0)
|
|
25
|
+
aws-sdk-core (~> 3, >= 3.112.0)
|
|
25
26
|
aws-sigv4 (~> 1.1)
|
|
26
|
-
aws-sdk-core (3.
|
|
27
|
+
aws-sdk-core (3.113.0)
|
|
27
28
|
aws-eventstream (~> 1, >= 1.0.2)
|
|
28
29
|
aws-partitions (~> 1, >= 1.239.0)
|
|
29
30
|
aws-sigv4 (~> 1.1)
|
|
30
31
|
jmespath (~> 1.0)
|
|
31
|
-
aws-sdk-ec2 (1.
|
|
32
|
+
aws-sdk-ec2 (1.220.0)
|
|
32
33
|
aws-sdk-core (~> 3, >= 3.109.0)
|
|
33
34
|
aws-sigv4 (~> 1.1)
|
|
34
|
-
aws-sdk-kms (1.
|
|
35
|
-
aws-sdk-core (~> 3, >= 3.
|
|
35
|
+
aws-sdk-kms (1.43.0)
|
|
36
|
+
aws-sdk-core (~> 3, >= 3.112.0)
|
|
36
37
|
aws-sigv4 (~> 1.1)
|
|
37
|
-
aws-sdk-s3 (1.
|
|
38
|
-
aws-sdk-core (~> 3, >= 3.
|
|
38
|
+
aws-sdk-s3 (1.91.0)
|
|
39
|
+
aws-sdk-core (~> 3, >= 3.112.0)
|
|
39
40
|
aws-sdk-kms (~> 1)
|
|
40
41
|
aws-sigv4 (~> 1.1)
|
|
41
|
-
aws-sdk-ssm (1.
|
|
42
|
-
aws-sdk-core (~> 3, >= 3.
|
|
42
|
+
aws-sdk-ssm (1.104.0)
|
|
43
|
+
aws-sdk-core (~> 3, >= 3.112.0)
|
|
43
44
|
aws-sigv4 (~> 1.1)
|
|
44
|
-
aws-sigv4 (1.2.
|
|
45
|
+
aws-sigv4 (1.2.3)
|
|
45
46
|
aws-eventstream (~> 1, >= 1.0.2)
|
|
46
|
-
cfndsl (1.
|
|
47
|
+
cfndsl (1.3.1)
|
|
47
48
|
hana (~> 1.3)
|
|
48
|
-
hana (1.3.
|
|
49
|
+
hana (1.3.7)
|
|
49
50
|
jmespath (1.4.0)
|
|
50
51
|
netaddr (2.0.4)
|
|
51
52
|
rake (13.0.1)
|
|
53
|
+
rubyzip (2.3.0)
|
|
52
54
|
terminal-table (1.8.0)
|
|
53
55
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
|
54
56
|
thor (0.20.3)
|
|
@@ -63,4 +65,4 @@ DEPENDENCIES
|
|
|
63
65
|
rake (~> 13.0)
|
|
64
66
|
|
|
65
67
|
BUNDLED WITH
|
|
66
|
-
2.
|
|
68
|
+
2.1.4
|
data/cfn-vpn.gemspec
CHANGED
|
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec|
|
|
|
37
37
|
spec.add_dependency "terminal-table", '~> 1', '<2'
|
|
38
38
|
spec.add_dependency 'cfndsl', '~> 1', '<2'
|
|
39
39
|
spec.add_dependency 'netaddr', '2.0.4'
|
|
40
|
+
spec.add_dependency 'rubyzip', '~> 2.3'
|
|
40
41
|
spec.add_runtime_dependency 'aws-sdk-ec2', '~> 1.95', '<2'
|
|
41
42
|
spec.add_runtime_dependency 'aws-sdk-acm', '~> 1', '<2'
|
|
42
43
|
spec.add_runtime_dependency 'aws-sdk-s3', '~> 1', '<2'
|
data/docs/README.md
CHANGED
|
@@ -31,7 +31,7 @@ dig @10.0.0.2 google.com
|
|
|
31
31
|
|
|
32
32
|
## Authentication Types
|
|
33
33
|
|
|
34
|
-
`cfn-vpn` supports certificate and
|
|
34
|
+
`cfn-vpn` supports certificate, federated and active directory type authentication for AWS Client-VPN.
|
|
35
35
|
For further information on the authentication types please visit https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/client-authentication.html
|
|
36
36
|
|
|
37
37
|
## CfnVpn Documentation
|
|
@@ -41,4 +41,4 @@ For further information on the authentication types please visit https://docs.aw
|
|
|
41
41
|
3. [Managing Certificate Users](certificate-users.md)
|
|
42
42
|
4. [Managing Routes](routes.md)
|
|
43
43
|
5. [Stop and Start Client-VPN](scheduling.md)
|
|
44
|
-
6. [Managing Sessions](sessions.md)
|
|
44
|
+
6. [Managing Sessions](sessions.md)
|
data/docs/routes.md
CHANGED
|
@@ -4,24 +4,34 @@ Management of the VPN routes can be altered using the `routes` command or by usi
|
|
|
4
4
|
|
|
5
5
|
**Note:** The default route via subnet association cannot be modified through this command. Use the `modify` command to alter the subnet associations.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
CfnVpn can create static routes for CIDRs as well as dynamically lookup IPs for dns endpoints and continue to monitor and update the routes if the IPs change.
|
|
8
8
|
|
|
9
|
+
```sh
|
|
10
|
+
cfn-vpn help routes
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Dynamic DNS Routes
|
|
14
|
+
|
|
15
|
+
Dynamic DNS routes takes a dns endpoint and will query the record every 5 minutes to see if the IPs have changed and update the routes.
|
|
16
|
+
|
|
17
|
+
### Add New
|
|
18
|
+
|
|
19
|
+
to add a new route run the routes command along with the `--dns` option
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
cfn-vpn routes [name] --dns example.com
|
|
9
23
|
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
[--groups=one two three] # override all authorised groups on thr route
|
|
18
|
-
[--add-groups=one two three] # add authorised groups to an existing route
|
|
19
|
-
[--del-groups=one two three] # remove authorised groups from an existing route
|
|
20
|
-
[--delete], [--no-delete] # delete the route from the client vpn
|
|
21
|
-
|
|
22
|
-
List, add or delete client vpn routes
|
|
24
|
+
|
|
25
|
+
### Delete
|
|
26
|
+
|
|
27
|
+
to delete a route run the routes command along with the `--dns` option of the route to delete and the delete option
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
cfn-vpn routes [name] --dns example.com --delete
|
|
23
31
|
```
|
|
24
32
|
|
|
33
|
+
## Static CIDR Routes
|
|
34
|
+
|
|
25
35
|
### Add New
|
|
26
36
|
|
|
27
37
|
to add a new route run the routes command along with the `--cidr` option
|
|
@@ -32,32 +42,32 @@ cfn-vpn routes [name] --cidr 10.151.0.0/16
|
|
|
32
42
|
|
|
33
43
|
### Delete
|
|
34
44
|
|
|
35
|
-
to delete a
|
|
45
|
+
to delete a route run the routes command along with the `--cidr` option of the route to delete and the delete option
|
|
36
46
|
|
|
37
47
|
```sh
|
|
38
48
|
cfn-vpn routes [name] --cidr 10.151.0.0/16 --delete
|
|
39
49
|
```
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
## Manage Authorization Groups
|
|
42
52
|
|
|
43
|
-
When using federated authentication groups can be used to control access to certain routes. These can be managed on the routes by providing the `--groups [list of groups]` along with a space delimited list of groups to the `routes` command.
|
|
53
|
+
When using federated or active directory authentication groups can be used to control access to certain routes. These can be managed on the routes by providing the `--groups [list of groups]` along with a space delimited list of groups to the `routes` command. This is available for both DNS and CIDR routes
|
|
44
54
|
|
|
45
55
|
To add groups to a new route or to override all groups on an exiting route use the `--groups` options
|
|
46
56
|
|
|
47
57
|
```sh
|
|
48
|
-
cfn-vpn routes [name] --cidr 10.151.0.0/16 --groups devs ops
|
|
58
|
+
cfn-vpn routes [name] [--cidr 10.151.0.0/16] [--dns example.com] --groups devs ops
|
|
49
59
|
```
|
|
50
60
|
|
|
51
61
|
To add groups to an existing route use the `--add-groups` options
|
|
52
62
|
|
|
53
63
|
```sh
|
|
54
|
-
cfn-vpn routes [name] --cidr 10.151.0.0/16 --add-groups admin
|
|
64
|
+
cfn-vpn routes [name] [--cidr 10.151.0.0/16] [--dns example.com] --add-groups admin
|
|
55
65
|
```
|
|
56
66
|
|
|
57
67
|
To delete groups from an existing route use the `--del-groups` options
|
|
58
68
|
|
|
59
69
|
```sh
|
|
60
|
-
cfn-vpn routes [name] --cidr 10.151.0.0/16 --del-groups dev
|
|
70
|
+
cfn-vpn routes [name] [--cidr 10.151.0.0/16] [--dns example.com] --del-groups dev
|
|
61
71
|
```
|
|
62
72
|
|
|
63
73
|
## Modify Command
|
|
@@ -75,6 +85,10 @@ routes:
|
|
|
75
85
|
desc: route to prod peered vpc
|
|
76
86
|
groups:
|
|
77
87
|
- ops
|
|
88
|
+
- cidr: example.com
|
|
89
|
+
desc: my dev alb
|
|
90
|
+
groups:
|
|
91
|
+
- dev
|
|
78
92
|
```
|
|
79
93
|
|
|
80
94
|
run the `modify` command and supply the yaml file to apply the changes
|
data/lib/cfnvpn/actions/init.rb
CHANGED
|
@@ -6,6 +6,7 @@ require 'cfnvpn/compiler'
|
|
|
6
6
|
require 'cfnvpn/log'
|
|
7
7
|
require 'cfnvpn/clientvpn'
|
|
8
8
|
require 'cfnvpn/globals'
|
|
9
|
+
require 'cfnvpn/s3_bucket'
|
|
9
10
|
|
|
10
11
|
module CfnVpn::Actions
|
|
11
12
|
class Init < Thor::Group
|
|
@@ -20,7 +21,7 @@ module CfnVpn::Actions
|
|
|
20
21
|
class_option :server_cn, required: true, desc: 'server certificate common name'
|
|
21
22
|
class_option :client_cn, desc: 'client certificate common name'
|
|
22
23
|
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
|
+
class_option :bucket, desc: 's3 bucket, if not set one will be generated for you'
|
|
24
25
|
|
|
25
26
|
class_option :subnet_ids, required: true, type: :array, desc: 'subnet id to associate your vpn with'
|
|
26
27
|
class_option :default_groups, default: [], type: :array, desc: 'groups to allow through the subnet associations when using federated auth'
|
|
@@ -68,6 +69,18 @@ module CfnVpn::Actions
|
|
|
68
69
|
}
|
|
69
70
|
end
|
|
70
71
|
|
|
72
|
+
def create_bucket_if_bucket_not_set
|
|
73
|
+
if !@options['bucket']
|
|
74
|
+
CfnVpn::Log.logger.info "creating s3 bucket"
|
|
75
|
+
bucket = CfnVpn::S3Bucket.new(@options['region'], @name)
|
|
76
|
+
bucket_name = bucket.generate_bucket_name
|
|
77
|
+
bucket.create_bucket(bucket_name)
|
|
78
|
+
@config[:bucket] = bucket_name
|
|
79
|
+
else
|
|
80
|
+
@config[:bucket] = @options['bucket']
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
71
84
|
def set_type
|
|
72
85
|
if @options['saml_arn']
|
|
73
86
|
@config[:type] = 'federated'
|
|
@@ -82,15 +95,6 @@ module CfnVpn::Actions
|
|
|
82
95
|
CfnVpn::Log.logger.info "initialising #{@config[:type]} client vpn"
|
|
83
96
|
end
|
|
84
97
|
|
|
85
|
-
def conditional_options_check
|
|
86
|
-
if @config[:type] == 'certificate'
|
|
87
|
-
if !@options['bucket']
|
|
88
|
-
CfnVpn::Log.logger.error "--bucket option must be specified if creating a client vpn with certificate based authentication"
|
|
89
|
-
exit 1
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
98
|
def stack_exist
|
|
95
99
|
@deployer = CfnVpn::Deployer.new(@options['region'],@name)
|
|
96
100
|
if @deployer.does_cf_stack_exist()
|
|
@@ -114,7 +118,7 @@ module CfnVpn::Actions
|
|
|
114
118
|
# we only need the server certificate to ACM if it is a SAML federated client vpn
|
|
115
119
|
@config[:client_cert_arn] = cert.upload_certificates(@options['region'],@client_cn,'client')
|
|
116
120
|
# and only need to upload the certs to s3 if using certificate authenitcation
|
|
117
|
-
s3 = CfnVpn::S3.new(@options['region'],@
|
|
121
|
+
s3 = CfnVpn::S3.new(@options['region'],@config[:bucket],@name)
|
|
118
122
|
s3.store_object("#{@build_dir}/certificates/ca.tar.gz")
|
|
119
123
|
end
|
|
120
124
|
end
|
|
@@ -8,6 +8,7 @@ require 'cfnvpn/log'
|
|
|
8
8
|
require 'cfnvpn/clientvpn'
|
|
9
9
|
require 'cfnvpn/globals'
|
|
10
10
|
require 'cfnvpn/config'
|
|
11
|
+
require 'cfnvpn/s3_bucket'
|
|
11
12
|
|
|
12
13
|
module CfnVpn::Actions
|
|
13
14
|
class Modify < Thor::Group
|
|
@@ -38,6 +39,8 @@ module CfnVpn::Actions
|
|
|
38
39
|
|
|
39
40
|
class_option :param_yaml, type: :string, desc: 'pass in cfnvpn params through YAML file'
|
|
40
41
|
|
|
42
|
+
class_option :bucket, desc: 's3 bucket'
|
|
43
|
+
|
|
41
44
|
def self.source_root
|
|
42
45
|
File.dirname(__FILE__)
|
|
43
46
|
end
|
|
@@ -95,6 +98,23 @@ module CfnVpn::Actions
|
|
|
95
98
|
CfnVpn::Log.logger.debug "Modified config:\n#{@config}"
|
|
96
99
|
end
|
|
97
100
|
|
|
101
|
+
def create_bucket_if_bucket_not_set
|
|
102
|
+
if !@options['bucket'] && !@config.has_key?(:bucket)
|
|
103
|
+
if yes? "no s3 bucket supplied in the command or found in the config, select (Y) to generate a new one ot select (N) and re run teh command with the --bucket flag to import the existing bucket."
|
|
104
|
+
CfnVpn::Log.logger.info "creating s3 bucket"
|
|
105
|
+
bucket = CfnVpn::S3Bucket.new(@options['region'], @name)
|
|
106
|
+
bucket_name = bucket.generate_bucket_name
|
|
107
|
+
bucket.create_bucket(bucket_name)
|
|
108
|
+
@config[:bucket] = bucket_name
|
|
109
|
+
else
|
|
110
|
+
CfnVpn::Log.logger.info "rerun cfn-vpn modify #{name} command with the --bucket [BUCKET] flag"
|
|
111
|
+
exit 1
|
|
112
|
+
end
|
|
113
|
+
elsif @options['bucket']
|
|
114
|
+
@config[:bucket] = @options['bucket']
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
98
118
|
def deploy_vpn
|
|
99
119
|
compiler = CfnVpn::Compiler.new(@name, @config)
|
|
100
120
|
template_body = compiler.compile
|
|
@@ -13,6 +13,7 @@ module CfnVpn::Actions
|
|
|
13
13
|
class_option :verbose, desc: 'set log level to debug', type: :boolean
|
|
14
14
|
|
|
15
15
|
class_option :cidr, desc: 'cidr range'
|
|
16
|
+
class_option :dns, desc: 'dns record to auto lookup ip'
|
|
16
17
|
class_option :subnet, desc: 'the target vpc subnet to route through, if none is supplied the default subnet is used'
|
|
17
18
|
class_option :desc, desc: 'description of the route'
|
|
18
19
|
|
|
@@ -32,26 +33,50 @@ module CfnVpn::Actions
|
|
|
32
33
|
|
|
33
34
|
def set_config
|
|
34
35
|
@config = CfnVpn::Config.get_config(@options[:region], @name)
|
|
35
|
-
|
|
36
|
+
|
|
37
|
+
if @options[:cidr] && @options[:dns]
|
|
38
|
+
CfnVpn::Log.logger.error "only one of --dns or --cidr can be set"
|
|
39
|
+
exit 1
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if @options[:dns]
|
|
43
|
+
if @options[:dns].include?("*")
|
|
44
|
+
CfnVpn::Log.logger.error("wild card DNS resolution is not supported, use a record that will be resolved by the wild card instead")
|
|
45
|
+
exit 1
|
|
46
|
+
end
|
|
47
|
+
@route = @config[:routes].detect {|route| route[:dns] == @options[:dns]}
|
|
48
|
+
elsif @options[:cidr]
|
|
49
|
+
@route = @config[:routes].detect {|route| route[:cidr] == @options[:cidr]}
|
|
50
|
+
end
|
|
36
51
|
end
|
|
37
52
|
|
|
38
53
|
def set_route
|
|
39
54
|
@skip_update = false
|
|
55
|
+
@dns_route_cleanup = nil
|
|
40
56
|
if @route && @options[:delete]
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
if @options[:dns]
|
|
58
|
+
CfnVpn::Log.logger.info "deleting auto lookup route for endpoint #{@options[:dns]}"
|
|
59
|
+
@config[:routes].reject! {|route| route[:dns] == @options[:dns]}
|
|
60
|
+
@dns_route_cleanup = @options[:dns]
|
|
61
|
+
elsif @options[:cidr]
|
|
62
|
+
CfnVpn::Log.logger.info "deleting route #{@options[:cidr]}"
|
|
63
|
+
@config[:routes].reject! {|route| route[:cidr] == @options[:cidr]}
|
|
64
|
+
end
|
|
43
65
|
elsif @route
|
|
44
|
-
CfnVpn::Log.logger.info "
|
|
66
|
+
CfnVpn::Log.logger.info "existing route for #{@options[:cidr] ? @options[:cidr] : @options[:dns]} found"
|
|
45
67
|
if @options[:groups]
|
|
68
|
+
CfnVpn::Log.logger.info "replacing groups #{@route[:groups]} with new #{@options[:groups]} for route authorization rule"
|
|
46
69
|
@route[:groups] = @options[:groups]
|
|
47
70
|
end
|
|
48
71
|
|
|
49
72
|
if @options[:add_groups]
|
|
73
|
+
CfnVpn::Log.logger.info "adding new group(s) #{@options[:add_groups]} to route authorization rule"
|
|
50
74
|
@route[:groups].concat(@options[:add_groups]).uniq!
|
|
51
75
|
end
|
|
52
76
|
|
|
53
77
|
if @options[:del_groups]
|
|
54
|
-
|
|
78
|
+
CfnVpn::Log.logger.info "removing new group(s) #{@options[:del_groups]} to route authorization rule"
|
|
79
|
+
@route[:groups].reject! {|group| @options[:del_groups].include? group}
|
|
55
80
|
end
|
|
56
81
|
|
|
57
82
|
if @options[:desc]
|
|
@@ -65,7 +90,15 @@ module CfnVpn::Actions
|
|
|
65
90
|
CfnVpn::Log.logger.info "adding new route for #{@options[:cidr]}"
|
|
66
91
|
@config[:routes] << {
|
|
67
92
|
cidr: @options[:cidr],
|
|
68
|
-
desc: @options.fetch(:desc, "
|
|
93
|
+
desc: @options.fetch(:desc, ""),
|
|
94
|
+
subnet: @options.fetch(:subnet, @config[:subnet_ids].first),
|
|
95
|
+
groups: @options.fetch(@options[:groups], []) + @options.fetch(@options[:add_groups], [])
|
|
96
|
+
}
|
|
97
|
+
elsif !@route && @options[:dns]
|
|
98
|
+
CfnVpn::Log.logger.info "adding new route lookup for dns record #{@options[:dns]}"
|
|
99
|
+
@config[:routes] << {
|
|
100
|
+
dns: @options[:dns],
|
|
101
|
+
desc: @options.fetch(:desc, ""),
|
|
69
102
|
subnet: @options.fetch(:subnet, @config[:subnet_ids].first),
|
|
70
103
|
groups: @options.fetch(@options[:groups], []) + @options.fetch(@options[:add_groups], [])
|
|
71
104
|
}
|
|
@@ -76,6 +109,13 @@ module CfnVpn::Actions
|
|
|
76
109
|
CfnVpn::Log.logger.debug "CONFIG: #{@config}"
|
|
77
110
|
end
|
|
78
111
|
|
|
112
|
+
def create_bucket_if_bucket_not_set
|
|
113
|
+
if !@config.has_key?(:bucket)
|
|
114
|
+
CfnVpn::Log.logger.error "no bucket found in the config, run the cfn-vpn modify #{name} command to add a bucket"
|
|
115
|
+
exit 1
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
79
119
|
def deploy_vpn
|
|
80
120
|
unless @skip_update
|
|
81
121
|
compiler = CfnVpn::Compiler.new(@name, @config)
|
|
@@ -123,8 +163,20 @@ module CfnVpn::Actions
|
|
|
123
163
|
end
|
|
124
164
|
end
|
|
125
165
|
|
|
126
|
-
def
|
|
166
|
+
def cleanup_dns_routes
|
|
127
167
|
@vpn = CfnVpn::ClientVpn.new(@name,@options['region'])
|
|
168
|
+
unless @dns_route_cleanup.nil?
|
|
169
|
+
routes = @vpn.get_routes()
|
|
170
|
+
CfnVpn::Log.logger.info("Cleaning up expired routes for #{@dns_route_cleanup}")
|
|
171
|
+
expired_routes = routes.select {|route| route.description.include?(@dns_route_cleanup) }
|
|
172
|
+
expired_routes.each do |route|
|
|
173
|
+
@vpn.delete_route(route.destination_cidr, route.target_subnet)
|
|
174
|
+
@vpn.revoke_auth(route.destination_cidr)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def get_routes
|
|
128
180
|
@endpoint = @vpn.get_endpoint_id()
|
|
129
181
|
@routes = @vpn.get_routes()
|
|
130
182
|
end
|
data/lib/cfnvpn/clientvpn.rb
CHANGED
|
@@ -9,6 +9,7 @@ module CfnVpn
|
|
|
9
9
|
def initialize(name,region)
|
|
10
10
|
@client = Aws::EC2::Client.new(region: region)
|
|
11
11
|
@name = name
|
|
12
|
+
@endpoint_id = self.get_endpoint_id()
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def get_endpoint()
|
|
@@ -116,5 +117,22 @@ module CfnVpn
|
|
|
116
117
|
return associations
|
|
117
118
|
end
|
|
118
119
|
|
|
120
|
+
def delete_route(cidr, subnet)
|
|
121
|
+
@client.delete_client_vpn_route({
|
|
122
|
+
client_vpn_endpoint_id: @endpoint_id,
|
|
123
|
+
target_vpc_subnet_id: subnet,
|
|
124
|
+
destination_cidr_block: cidr
|
|
125
|
+
})
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def revoke_auth(cidr)
|
|
129
|
+
endpoint_id = get_endpoint_id()
|
|
130
|
+
@client.revoke_client_vpn_ingress({
|
|
131
|
+
client_vpn_endpoint_id: @endpoint_id,
|
|
132
|
+
target_network_cidr: cidr,
|
|
133
|
+
revoke_all_groups: true
|
|
134
|
+
})
|
|
135
|
+
end
|
|
136
|
+
|
|
119
137
|
end
|
|
120
138
|
end
|
data/lib/cfnvpn/s3.rb
CHANGED
|
@@ -63,5 +63,35 @@ module CfnVpn
|
|
|
63
63
|
})
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
def create_bucket
|
|
67
|
+
@client.create_bucket({
|
|
68
|
+
bucket: bucket,
|
|
69
|
+
acl: 'private'
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
@client.put_public_access_block({
|
|
73
|
+
bucket: bucket,
|
|
74
|
+
public_access_block_configuration: {
|
|
75
|
+
block_public_acls: true,
|
|
76
|
+
ignore_public_acls: true,
|
|
77
|
+
block_public_policy: true,
|
|
78
|
+
restrict_public_buckets: true,
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
@client.put_bucket_encryption({
|
|
83
|
+
bucket: bucket,
|
|
84
|
+
server_side_encryption_configuration: {
|
|
85
|
+
rules: [
|
|
86
|
+
{
|
|
87
|
+
apply_server_side_encryption_by_default: {
|
|
88
|
+
sse_algorithm: "AES256"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
end
|
|
95
|
+
|
|
66
96
|
end
|
|
67
97
|
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'aws-sdk-s3'
|
|
2
|
+
require 'fileutils'
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module CfnVpn
|
|
6
|
+
class S3Bucket
|
|
7
|
+
|
|
8
|
+
def initialize(region, name)
|
|
9
|
+
@client = Aws::S3::Client.new(region: region)
|
|
10
|
+
@name = name
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def generate_bucket_name
|
|
14
|
+
return "cfnvpn-#{@name}-#{SecureRandom.hex}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create_bucket(bucket)
|
|
18
|
+
@client.create_bucket({
|
|
19
|
+
bucket: bucket,
|
|
20
|
+
acl: 'private'
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
@client.put_public_access_block({
|
|
24
|
+
bucket: bucket,
|
|
25
|
+
public_access_block_configuration: {
|
|
26
|
+
block_public_acls: true,
|
|
27
|
+
ignore_public_acls: true,
|
|
28
|
+
block_public_policy: true,
|
|
29
|
+
restrict_public_buckets: true,
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
@client.put_bucket_encryption({
|
|
34
|
+
bucket: bucket,
|
|
35
|
+
server_side_encryption_configuration: {
|
|
36
|
+
rules: [
|
|
37
|
+
{
|
|
38
|
+
apply_server_side_encryption_by_default: {
|
|
39
|
+
sse_algorithm: "AES256"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/cfnvpn/string.rb
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'zip'
|
|
2
|
+
require 'securerandom'
|
|
3
|
+
require 'aws-sdk-s3'
|
|
4
|
+
require 'cfnvpn/log'
|
|
5
|
+
|
|
6
|
+
module CfnVpn
|
|
7
|
+
module Templates
|
|
8
|
+
class Lambdas
|
|
9
|
+
|
|
10
|
+
def self.package_lambda(name:, bucket:, func:, files:)
|
|
11
|
+
lambdas_dir = File.join(File.dirname(File.expand_path(__FILE__)), 'lambdas')
|
|
12
|
+
FileUtils.mkdir_p(lambdas_dir)
|
|
13
|
+
|
|
14
|
+
CfnVpn::Log.logger.debug "zipping lambda function #{func}"
|
|
15
|
+
zipfile_name = "#{func}-#{SecureRandom.hex}.zip"
|
|
16
|
+
zipfile_path = "#{CfnVpn.cfnvpn_path}/#{name}/lambdas"
|
|
17
|
+
FileUtils.mkdir_p(zipfile_path)
|
|
18
|
+
Zip::File.open("#{zipfile_path}/#{zipfile_name}", Zip::File::CREATE) do |zipfile|
|
|
19
|
+
files.each do |file|
|
|
20
|
+
zipfile.add(file, File.join("#{lambdas_dir}/#{func}", file))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
bucket = Aws::S3::Bucket.new(bucket)
|
|
25
|
+
object = bucket.object("cfnvpn/lambdas/#{name}/#{zipfile_name}")
|
|
26
|
+
CfnVpn::Log.logger.debug "uploading #{zipfile_name} to s3://#{bucket}/#{object.key}"
|
|
27
|
+
object.upload_file("#{zipfile_path}/#{zipfile_name}")
|
|
28
|
+
|
|
29
|
+
return object.key
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
import boto3
|
|
3
|
+
from botocore.exceptions import ClientError
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger()
|
|
7
|
+
logger.setLevel(logging.INFO)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def delete_route(client, vpn_endpoint, subnet, cidr):
|
|
11
|
+
client.delete_client_vpn_route(
|
|
12
|
+
ClientVpnEndpointId=vpn_endpoint,
|
|
13
|
+
TargetVpcSubnetId=subnet,
|
|
14
|
+
DestinationCidrBlock=cidr,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def create_route(client, event, cidr):
|
|
19
|
+
client.create_client_vpn_route(
|
|
20
|
+
ClientVpnEndpointId=event['ClientVpnEndpointId'],
|
|
21
|
+
DestinationCidrBlock=cidr,
|
|
22
|
+
TargetVpcSubnetId=event['TargetSubnet'],
|
|
23
|
+
Description=f"cfnvpn auto generated route for endpoint {event['Record']}. {event['Description']}"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def revoke_route_auth(client, event, cidr, group = None):
|
|
28
|
+
args = {
|
|
29
|
+
'ClientVpnEndpointId': event['ClientVpnEndpointId'],
|
|
30
|
+
'TargetNetworkCidr': cidr
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if group is None:
|
|
34
|
+
args['RevokeAllGroups'] = True
|
|
35
|
+
else:
|
|
36
|
+
args['AccessGroupId'] = group
|
|
37
|
+
|
|
38
|
+
client.revoke_client_vpn_ingress(**args)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def authorize_route(client, event, cidr, group = None):
|
|
42
|
+
args = {
|
|
43
|
+
'ClientVpnEndpointId': event['ClientVpnEndpointId'],
|
|
44
|
+
'TargetNetworkCidr': cidr,
|
|
45
|
+
'Description': f"cfnvpn auto generated authorization for endpoint {event['Record']}. {event['Description']}"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if group is None:
|
|
49
|
+
args['AuthorizeAllGroups'] = True
|
|
50
|
+
else:
|
|
51
|
+
args['AccessGroupId'] = group
|
|
52
|
+
|
|
53
|
+
client.authorize_client_vpn_ingress(**args)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_routes(client, event):
|
|
57
|
+
response = client.describe_client_vpn_routes(
|
|
58
|
+
ClientVpnEndpointId=event['ClientVpnEndpointId'],
|
|
59
|
+
Filters=[
|
|
60
|
+
{
|
|
61
|
+
'Name': 'origin',
|
|
62
|
+
'Values': ['add-route']
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
routes = [route for route in response['Routes'] if event['Record'] in route['Description']]
|
|
68
|
+
logger.info(f"found {len(routes)} exisiting routes for {event['Record']}")
|
|
69
|
+
return routes
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_rules(client, vpn_endpoint, cidr):
|
|
73
|
+
response = client.describe_client_vpn_authorization_rules(
|
|
74
|
+
ClientVpnEndpointId=vpn_endpoint,
|
|
75
|
+
Filters=[
|
|
76
|
+
{
|
|
77
|
+
'Name': 'destination-cidr',
|
|
78
|
+
'Values': [cidr]
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
)
|
|
82
|
+
return response['AuthorizationRules']
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def handler(event,context):
|
|
86
|
+
|
|
87
|
+
# DNS lookup on the dns record and return all IPS for the endpoint
|
|
88
|
+
try:
|
|
89
|
+
cidrs = [ ip + "/32" for ip in socket.gethostbyname_ex(event['Record'])[-1]]
|
|
90
|
+
logger.info(f"resolved endpoint {event['Record']} to {cidrs}")
|
|
91
|
+
except socket.gaierror as e:
|
|
92
|
+
logger.exception(f"failed to resolve record {event['Record']}")
|
|
93
|
+
return 'KO'
|
|
94
|
+
|
|
95
|
+
client = boto3.client('ec2')
|
|
96
|
+
routes = get_routes(client, event)
|
|
97
|
+
|
|
98
|
+
for cidr in cidrs:
|
|
99
|
+
route = next((route for route in routes if route['DestinationCidr'] == cidr), None)
|
|
100
|
+
|
|
101
|
+
# if there are no existing routes for the endpoint cidr create a new route
|
|
102
|
+
if route is None:
|
|
103
|
+
try:
|
|
104
|
+
create_route(client, event, cidr)
|
|
105
|
+
if 'Groups' in event:
|
|
106
|
+
for group in event['Groups']:
|
|
107
|
+
authorize_route(client, event, cidr, group)
|
|
108
|
+
else:
|
|
109
|
+
authorize_route(client, event, cidr)
|
|
110
|
+
except ClientError as e:
|
|
111
|
+
if e.response['Error']['Code'] == 'InvalidClientVpnDuplicateRoute':
|
|
112
|
+
logger.error(f"route for CIDR {cidr} already exists with a different endpoint")
|
|
113
|
+
continue
|
|
114
|
+
raise e
|
|
115
|
+
|
|
116
|
+
# if the route already exists
|
|
117
|
+
else:
|
|
118
|
+
|
|
119
|
+
logger.info(f"route for cidr {cidr} is already in place")
|
|
120
|
+
|
|
121
|
+
# if the target subnet has changed in the payload, recreate the routes to use the new subnet
|
|
122
|
+
if route['TargetSubnet'] != event['TargetSubnet']:
|
|
123
|
+
logger.info(f"target subnet for route for {cidr} has changed, recreating the route")
|
|
124
|
+
delete_route(client, event['ClientVpnEndpointId'], route['TargetSubnet'], cidr)
|
|
125
|
+
create_route(client, event, cidr)
|
|
126
|
+
|
|
127
|
+
logger.info(f"checking authorization rules for the route")
|
|
128
|
+
|
|
129
|
+
# check the rules match the payload
|
|
130
|
+
rules = get_rules(client, event['ClientVpnEndpointId'], cidr)
|
|
131
|
+
existing_groups = [rule['GroupId'] for rule in rules]
|
|
132
|
+
if 'Groups' in event:
|
|
133
|
+
# remove expired rules not defined in the payload anymore
|
|
134
|
+
expired_rules = [rule for rule in rules if rule['GroupId'] not in event['Groups']]
|
|
135
|
+
for rule in expired_rules:
|
|
136
|
+
logger.info(f"removing expired authorization rule for group {rule['GroupId']} for route {cidr}")
|
|
137
|
+
revoke_route_auth(client, event, cidr, rule['GroupId'])
|
|
138
|
+
# add new rules defined in the payload
|
|
139
|
+
new_rules = [group for group in event['Groups'] if group not in existing_groups]
|
|
140
|
+
for group in new_rules:
|
|
141
|
+
logger.info(f"creating new authorization rule for group {rule['GroupId']} for route {cidr}")
|
|
142
|
+
authorize_route(client, event, cidr, group)
|
|
143
|
+
else:
|
|
144
|
+
# if amount of rules for the cidr is greater than 1 when no groups are specified in the payload
|
|
145
|
+
# we'll assume that all groups have been removed from the payload so we'll remove all existing rules and add a rule for allow all
|
|
146
|
+
if len(rules) > 1:
|
|
147
|
+
logger.info(f"creating an allow all rule for route {cidr}")
|
|
148
|
+
revoke_route_auth(client, event, cidr)
|
|
149
|
+
authorize_route(client, event, cidr)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# clean up any expired routes when the ips for an endpoint change
|
|
155
|
+
expired_routes = [route for route in routes if route['DestinationCidr'] not in cidrs]
|
|
156
|
+
for route in expired_routes:
|
|
157
|
+
logger.info(f"removing expired route {route['DestinationCidr']} for endpoint {event['Record']}")
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
revoke_route_auth(client, event, route['DestinationCidr'])
|
|
161
|
+
except ClientError as e:
|
|
162
|
+
if e.response['Error']['Code'] == 'InvalidClientVpnEndpointAuthorizationRuleNotFound':
|
|
163
|
+
pass
|
|
164
|
+
else:
|
|
165
|
+
raise e
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
delete_route(client, event['ClientVpnEndpointId'], route['TargetSubnet'], route['DestinationCidr'])
|
|
169
|
+
except ClientError as e:
|
|
170
|
+
if e.response['Error']['Code'] == 'InvalidClientVpnRouteNotFound':
|
|
171
|
+
pass
|
|
172
|
+
else:
|
|
173
|
+
raise e
|
|
174
|
+
|
|
175
|
+
return 'OK'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger()
|
|
5
|
+
logger.setLevel(logging.INFO)
|
|
6
|
+
|
|
7
|
+
def handler(event, context):
|
|
8
|
+
|
|
9
|
+
logger.info(f"updating cfn-vpn stack {event['StackName']} parameter AssociateSubnets with value {event['AssociateSubnets']}")
|
|
10
|
+
|
|
11
|
+
if event['AssociateSubnets'] == 'false':
|
|
12
|
+
logger.info(f"terminating current vpn sessions to {event['ClientVpnEndpointId']}")
|
|
13
|
+
ec2 = boto3.client('ec2')
|
|
14
|
+
resp = ec2.describe_client_vpn_connections(ClientVpnEndpointId=event['ClientVpnEndpointId'])
|
|
15
|
+
for conn in resp['Connections']:
|
|
16
|
+
if conn['Status']['Code'] == 'active':
|
|
17
|
+
ec2.terminate_client_vpn_connections(
|
|
18
|
+
ClientVpnEndpointId=event['ClientVpnEndpointId'],
|
|
19
|
+
ConnectionId=conn['ConnectionId']
|
|
20
|
+
)
|
|
21
|
+
logger.info(f"terminated session {conn['ConnectionId']}")
|
|
22
|
+
|
|
23
|
+
client = boto3.client('cloudformation')
|
|
24
|
+
logger.info(client.update_stack(
|
|
25
|
+
StackName=event['StackName'],
|
|
26
|
+
UsePreviousTemplate=True,
|
|
27
|
+
Capabilities=['CAPABILITY_IAM'],
|
|
28
|
+
Parameters=[
|
|
29
|
+
{
|
|
30
|
+
'ParameterKey': 'AssociateSubnets',
|
|
31
|
+
'ParameterValue': event['AssociateSubnets']
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
))
|
|
35
|
+
|
|
36
|
+
return 'OK'
|
data/lib/cfnvpn/templates/vpn.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'cfndsl'
|
|
2
2
|
require 'cfnvpn/templates/helper'
|
|
3
|
+
require 'cfnvpn/templates/lambdas'
|
|
3
4
|
|
|
4
5
|
module CfnVpn
|
|
5
6
|
module Templates
|
|
@@ -69,6 +70,7 @@ module CfnVpn
|
|
|
69
70
|
SplitTunnel config[:split_tunnel]
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
network_assoc_dependson = []
|
|
72
74
|
config[:subnet_ids].each_with_index do |subnet, index|
|
|
73
75
|
suffix = index == 0 ? "" : "For#{subnet.resource_safe}"
|
|
74
76
|
|
|
@@ -78,74 +80,112 @@ module CfnVpn
|
|
|
78
80
|
SubnetId subnet
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
AccessGroupId group
|
|
88
|
-
ClientVpnEndpointId Ref(:ClientVpnEndpoint)
|
|
89
|
-
TargetNetworkCidr CfnVpn::Templates::Helper.get_auth_cidr(config[:region], subnet)
|
|
90
|
-
}
|
|
91
|
-
end
|
|
92
|
-
else
|
|
93
|
-
EC2_ClientVpnAuthorizationRule(:"TargetNetworkAuthorizationRule#{suffix}") {
|
|
83
|
+
network_assoc_dependson << "ClientVpnTargetNetworkAssociation#{suffix}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if config[:default_groups].any?
|
|
87
|
+
config[:default_groups].each do |group|
|
|
88
|
+
EC2_ClientVpnAuthorizationRule(:"TargetNetworkAuthorizationRule#{group.resource_safe}"[0..255]) {
|
|
94
89
|
Condition(:EnableSubnetAssociation)
|
|
95
|
-
DependsOn
|
|
90
|
+
DependsOn network_assoc_dependson if network_assoc_dependson.any?
|
|
96
91
|
Description FnSub("#{name} client-vpn auth rule for subnet association")
|
|
97
|
-
|
|
92
|
+
AccessGroupId group
|
|
98
93
|
ClientVpnEndpointId Ref(:ClientVpnEndpoint)
|
|
99
|
-
TargetNetworkCidr CfnVpn::Templates::Helper.get_auth_cidr(config[:region],
|
|
94
|
+
TargetNetworkCidr CfnVpn::Templates::Helper.get_auth_cidr(config[:region], config[:subnet_ids].first)
|
|
100
95
|
}
|
|
101
96
|
end
|
|
97
|
+
else
|
|
98
|
+
EC2_ClientVpnAuthorizationRule(:"TargetNetworkAuthorizationRule") {
|
|
99
|
+
Condition(:EnableSubnetAssociation)
|
|
100
|
+
DependsOn network_assoc_dependson if network_assoc_dependson.any?
|
|
101
|
+
Description FnSub("#{name} client-vpn auth rule for subnet association")
|
|
102
|
+
AuthorizeAllGroups true
|
|
103
|
+
ClientVpnEndpointId Ref(:ClientVpnEndpoint)
|
|
104
|
+
TargetNetworkCidr CfnVpn::Templates::Helper.get_auth_cidr(config[:region], config[:subnet_ids].first)
|
|
105
|
+
}
|
|
106
|
+
end
|
|
102
107
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
if !config[:internet_route].nil?
|
|
109
|
+
EC2_ClientVpnRoute(:RouteToInternet) {
|
|
110
|
+
Condition(:EnableSubnetAssociation)
|
|
111
|
+
DependsOn network_assoc_dependson if network_assoc_dependson.any?
|
|
112
|
+
Description "Route to the internet through subnet #{config[:internet_route]}"
|
|
113
|
+
ClientVpnEndpointId Ref(:ClientVpnEndpoint)
|
|
114
|
+
DestinationCidrBlock '0.0.0.0/0'
|
|
115
|
+
TargetVpcSubnetId config[:internet_route]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
EC2_ClientVpnAuthorizationRule(:RouteToInternetAuthorizationRule) {
|
|
119
|
+
Condition(:EnableSubnetAssociation)
|
|
120
|
+
DependsOn network_assoc_dependson if network_assoc_dependson.any?
|
|
121
|
+
Description "Internet route authorization from subnet #{config[:internet_route]}"
|
|
122
|
+
AuthorizeAllGroups true
|
|
123
|
+
ClientVpnEndpointId Ref(:ClientVpnEndpoint)
|
|
124
|
+
TargetNetworkCidr '0.0.0.0/0'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
output(:InternetRoute, config[:internet_route])
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
dns_routes = config[:routes].select {|route| route.has_key?(:dns)}
|
|
131
|
+
cidr_routes = config[:routes].select {|route| route.has_key?(:cidr)}
|
|
132
|
+
|
|
133
|
+
if dns_routes.any?
|
|
134
|
+
auto_route_populator(name, config[:bucket])
|
|
135
|
+
|
|
136
|
+
dns_routes.each do |route|
|
|
137
|
+
input = {
|
|
138
|
+
Record: route[:dns],
|
|
139
|
+
ClientVpnEndpointId: "${ClientVpnEndpoint}",
|
|
140
|
+
TargetSubnet: route[:subnet],
|
|
141
|
+
Description: route[:desc]
|
|
111
142
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
143
|
+
|
|
144
|
+
if route[:groups].any?
|
|
145
|
+
input[:Groups] = route[:groups]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
Events_Rule(:"CfnVpnAutoRoutePopulatorEvent#{route[:dns].resource_safe}"[0..255]) {
|
|
149
|
+
State 'ENABLED'
|
|
150
|
+
Description "cfnvpn auto route populator schedule for #{route[:dns]}"
|
|
151
|
+
ScheduleExpression "rate(5 minutes)"
|
|
152
|
+
Targets([
|
|
153
|
+
{
|
|
154
|
+
Arn: FnGetAtt(:CfnVpnAutoRoutePopulator, :Arn),
|
|
155
|
+
Id: "cfnvpnautoroutepopulator#{route[:dns].event_id_safe}",
|
|
156
|
+
Input: FnSub(input.to_json)
|
|
157
|
+
}
|
|
158
|
+
])
|
|
120
159
|
}
|
|
121
|
-
|
|
122
|
-
output(:InternetRoute, config[:internet_route])
|
|
123
160
|
end
|
|
124
161
|
end
|
|
125
162
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
163
|
+
if cidr_routes.any?
|
|
164
|
+
cidr_routes.each do |route|
|
|
165
|
+
EC2_ClientVpnRoute(:"#{route[:cidr].resource_safe}VpnRoute") {
|
|
166
|
+
Description "cfnvpn static route for #{route[:cidr]}. #{route[:desc]}"
|
|
167
|
+
ClientVpnEndpointId Ref(:ClientVpnEndpoint)
|
|
168
|
+
DestinationCidrBlock route[:cidr]
|
|
169
|
+
TargetVpcSubnetId route[:subnet]
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if route[:groups].any?
|
|
173
|
+
route[:groups].each do |group|
|
|
174
|
+
EC2_ClientVpnAuthorizationRule(:"#{route[:cidr].resource_safe}AuthorizationRule#{group.resource_safe}"[0..255]) {
|
|
175
|
+
Description "cfnvpn static authorization rule for group #{group} to route #{route[:cidr]}. #{route[:desc]}"
|
|
176
|
+
AccessGroupId group
|
|
177
|
+
ClientVpnEndpointId Ref(:ClientVpnEndpoint)
|
|
178
|
+
TargetNetworkCidr route[:cidr]
|
|
179
|
+
}
|
|
180
|
+
end
|
|
181
|
+
else
|
|
182
|
+
EC2_ClientVpnAuthorizationRule(:"#{route[:cidr].resource_safe}AllowAllAuthorizationRule") {
|
|
183
|
+
Description "cfnvpn static allow all authorization rule to route #{route[:cidr]}. #{route[:desc]}"
|
|
184
|
+
AuthorizeAllGroups true
|
|
138
185
|
ClientVpnEndpointId Ref(:ClientVpnEndpoint)
|
|
139
186
|
TargetNetworkCidr route[:cidr]
|
|
140
187
|
}
|
|
141
188
|
end
|
|
142
|
-
else
|
|
143
|
-
EC2_ClientVpnAuthorizationRule(:"#{route[:cidr].resource_safe}AllowAllAuthorizationRule") {
|
|
144
|
-
Description route[:desc]
|
|
145
|
-
AuthorizeAllGroups true
|
|
146
|
-
ClientVpnEndpointId Ref(:ClientVpnEndpoint)
|
|
147
|
-
TargetNetworkCidr route[:cidr]
|
|
148
|
-
}
|
|
149
189
|
end
|
|
150
190
|
end
|
|
151
191
|
|
|
@@ -156,13 +196,13 @@ module CfnVpn
|
|
|
156
196
|
Type 'String'
|
|
157
197
|
Value config.to_json
|
|
158
198
|
Tags({
|
|
159
|
-
Name:
|
|
199
|
+
Name: "#{name}-cfnvpn-config",
|
|
160
200
|
Environment: 'cfnvpn'
|
|
161
201
|
})
|
|
162
202
|
}
|
|
163
203
|
|
|
164
204
|
if config[:start] || config[:stop]
|
|
165
|
-
scheduler(name, config[:start], config[:stop])
|
|
205
|
+
scheduler(name, config[:start], config[:stop], config[:bucket])
|
|
166
206
|
output(:Start, config[:start]) if config[:start]
|
|
167
207
|
output(:Stop, config[:stop]) if config[:stop]
|
|
168
208
|
end
|
|
@@ -188,7 +228,92 @@ module CfnVpn
|
|
|
188
228
|
Output(name) { Value value }
|
|
189
229
|
end
|
|
190
230
|
|
|
191
|
-
def
|
|
231
|
+
def auto_route_populator(name, bucket)
|
|
232
|
+
IAM_Role(:CfnVpnAutoRoutePopulatorRole) {
|
|
233
|
+
AssumeRolePolicyDocument({
|
|
234
|
+
Version: '2012-10-17',
|
|
235
|
+
Statement: [{
|
|
236
|
+
Effect: 'Allow',
|
|
237
|
+
Principal: { Service: [ 'lambda.amazonaws.com' ] },
|
|
238
|
+
Action: [ 'sts:AssumeRole' ]
|
|
239
|
+
}]
|
|
240
|
+
})
|
|
241
|
+
Path '/cfnvpn/'
|
|
242
|
+
Policies([
|
|
243
|
+
{
|
|
244
|
+
PolicyName: 'client-vpn',
|
|
245
|
+
PolicyDocument: {
|
|
246
|
+
Version: '2012-10-17',
|
|
247
|
+
Statement: [{
|
|
248
|
+
Effect: 'Allow',
|
|
249
|
+
Action: [
|
|
250
|
+
'ec2:AuthorizeClientVpnIngress',
|
|
251
|
+
'ec2:RevokeClientVpnIngress',
|
|
252
|
+
'ec2:DescribeClientVpnAuthorizationRules',
|
|
253
|
+
'ec2:DescribeClientVpnEndpoints',
|
|
254
|
+
'ec2:DescribeClientVpnRoutes',
|
|
255
|
+
'ec2:CreateClientVpnRoute',
|
|
256
|
+
'ec2:DeleteClientVpnRoute'
|
|
257
|
+
],
|
|
258
|
+
Resource: '*'
|
|
259
|
+
}]
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
PolicyName: 'logging',
|
|
264
|
+
PolicyDocument: {
|
|
265
|
+
Version: '2012-10-17',
|
|
266
|
+
Statement: [{
|
|
267
|
+
Effect: 'Allow',
|
|
268
|
+
Action: [
|
|
269
|
+
'logs:DescribeLogGroups',
|
|
270
|
+
'logs:CreateLogGroup',
|
|
271
|
+
'logs:CreateLogStream',
|
|
272
|
+
'logs:DescribeLogStreams',
|
|
273
|
+
'logs:PutLogEvents'
|
|
274
|
+
],
|
|
275
|
+
Resource: '*'
|
|
276
|
+
}]
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
])
|
|
280
|
+
Tags([
|
|
281
|
+
{ Key: 'Name', Value: "#{name}-cfnvpn-auto-route-populator-role" },
|
|
282
|
+
{ Key: 'Environment', Value: 'cfnvpn' }
|
|
283
|
+
])
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
s3_key = CfnVpn::Templates::Lambdas.package_lambda(name: name, bucket: bucket, func: 'auto_route_populator', files: ['app.py'])
|
|
287
|
+
|
|
288
|
+
Lambda_Function(:CfnVpnAutoRoutePopulator) {
|
|
289
|
+
Runtime 'python3.8'
|
|
290
|
+
Role FnGetAtt(:CfnVpnAutoRoutePopulatorRole, :Arn)
|
|
291
|
+
MemorySize '128'
|
|
292
|
+
Handler 'app.handler'
|
|
293
|
+
Timeout 60
|
|
294
|
+
Code({
|
|
295
|
+
S3Bucket: bucket,
|
|
296
|
+
S3Key: s3_key
|
|
297
|
+
})
|
|
298
|
+
Tags([
|
|
299
|
+
{ Key: 'Name', Value: "#{name}-cfnvpn-auto-route-populator" },
|
|
300
|
+
{ Key: 'Environment', Value: 'cfnvpn' }
|
|
301
|
+
])
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
Logs_LogGroup(:CfnVpnAutoRoutePopulatorLogGroup) {
|
|
305
|
+
LogGroupName FnSub("/aws/lambda/${CfnVpnAutoRoutePopulator}")
|
|
306
|
+
RetentionInDays 30
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
Lambda_Permission(:CfnVpnAutoRoutePopulatorFunctionPermissions) {
|
|
310
|
+
FunctionName Ref(:CfnVpnAutoRoutePopulator)
|
|
311
|
+
Action 'lambda:InvokeFunction'
|
|
312
|
+
Principal 'events.amazonaws.com'
|
|
313
|
+
}
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def scheduler(name, start, stop, bucket)
|
|
192
317
|
IAM_Role(:ClientVpnSchedulerRole) {
|
|
193
318
|
AssumeRolePolicyDocument({
|
|
194
319
|
Version: '2012-10-17',
|
|
@@ -258,46 +383,17 @@ module CfnVpn
|
|
|
258
383
|
])
|
|
259
384
|
}
|
|
260
385
|
|
|
386
|
+
s3_key = CfnVpn::Templates::Lambdas.package_lambda(name: name, bucket: bucket, func: 'scheduler', files: ['app.py'])
|
|
387
|
+
|
|
261
388
|
Lambda_Function(:ClientVpnSchedulerFunction) {
|
|
262
|
-
Runtime 'python3.
|
|
389
|
+
Runtime 'python3.8'
|
|
263
390
|
Role FnGetAtt(:ClientVpnSchedulerRole, :Arn)
|
|
264
391
|
MemorySize '128'
|
|
265
|
-
Handler '
|
|
392
|
+
Handler 'app.handler'
|
|
393
|
+
Timeout 60
|
|
266
394
|
Code({
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
def handler(event, context):
|
|
271
|
-
|
|
272
|
-
print(f"updating cfn-vpn stack {event['StackName']} parameter AssociateSubnets with value {event['AssociateSubnets']}")
|
|
273
|
-
|
|
274
|
-
if event['AssociateSubnets'] == 'false':
|
|
275
|
-
print(f"terminating current vpn sessions to {event['ClientVpnEndpointId']}")
|
|
276
|
-
ec2 = boto3.client('ec2')
|
|
277
|
-
resp = ec2.describe_client_vpn_connections(ClientVpnEndpointId=event['ClientVpnEndpointId'])
|
|
278
|
-
for conn in resp['Connections']:
|
|
279
|
-
if conn['Status']['Code'] == 'active':
|
|
280
|
-
ec2.terminate_client_vpn_connections(
|
|
281
|
-
ClientVpnEndpointId=event['ClientVpnEndpointId'],
|
|
282
|
-
ConnectionId=conn['ConnectionId']
|
|
283
|
-
)
|
|
284
|
-
print(f"terminated session {conn['ConnectionId']}")
|
|
285
|
-
|
|
286
|
-
client = boto3.client('cloudformation')
|
|
287
|
-
print(client.update_stack(
|
|
288
|
-
StackName=event['StackName'],
|
|
289
|
-
UsePreviousTemplate=True,
|
|
290
|
-
Capabilities=['CAPABILITY_IAM'],
|
|
291
|
-
Parameters=[
|
|
292
|
-
{
|
|
293
|
-
'ParameterKey': 'AssociateSubnets',
|
|
294
|
-
'ParameterValue': event['AssociateSubnets']
|
|
295
|
-
}
|
|
296
|
-
]
|
|
297
|
-
))
|
|
298
|
-
|
|
299
|
-
return 'OK'
|
|
300
|
-
EOS
|
|
395
|
+
S3Bucket: bucket,
|
|
396
|
+
S3Key: s3_key
|
|
301
397
|
})
|
|
302
398
|
Tags([
|
|
303
399
|
{ Key: 'Name', Value: "#{name}-cfnvpn-scheduler-function" },
|
data/lib/cfnvpn/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cfn-vpn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Guslington
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-
|
|
11
|
+
date: 2021-04-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|
|
@@ -78,6 +78,20 @@ dependencies:
|
|
|
78
78
|
- - '='
|
|
79
79
|
- !ruby/object:Gem::Version
|
|
80
80
|
version: 2.0.4
|
|
81
|
+
- !ruby/object:Gem::Dependency
|
|
82
|
+
name: rubyzip
|
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - "~>"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '2.3'
|
|
88
|
+
type: :runtime
|
|
89
|
+
prerelease: false
|
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "~>"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '2.3'
|
|
81
95
|
- !ruby/object:Gem::Dependency
|
|
82
96
|
name: aws-sdk-ec2
|
|
83
97
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -254,8 +268,12 @@ files:
|
|
|
254
268
|
- lib/cfnvpn/globals.rb
|
|
255
269
|
- lib/cfnvpn/log.rb
|
|
256
270
|
- lib/cfnvpn/s3.rb
|
|
271
|
+
- lib/cfnvpn/s3_bucket.rb
|
|
257
272
|
- lib/cfnvpn/string.rb
|
|
258
273
|
- lib/cfnvpn/templates/helper.rb
|
|
274
|
+
- lib/cfnvpn/templates/lambdas.rb
|
|
275
|
+
- lib/cfnvpn/templates/lambdas/auto_route_populator/app.py
|
|
276
|
+
- lib/cfnvpn/templates/lambdas/scheduler/app.py
|
|
259
277
|
- lib/cfnvpn/templates/vpn.rb
|
|
260
278
|
- lib/cfnvpn/version.rb
|
|
261
279
|
homepage: https://github.com/base2services/aws-client-vpn
|