cfn-vpn 1.1.1 → 1.3.3

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.
@@ -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
@@ -32,10 +33,17 @@ module CfnVpn
32
33
  {
33
34
  FederatedAuthentication: {
34
35
  SAMLProviderArn: config[:saml_arn],
35
- SelfServiceSAMLProviderArn: config[:saml_arn]
36
+ SelfServiceSAMLProviderArn: config[:saml_self_service_arn].nil? ? config[:saml_arn] : config[:saml_self_service_arn]
36
37
  },
37
38
  Type: 'federated-authentication'
38
39
  }
40
+ elsif config[:type] == 'active-directory'
41
+ {
42
+ ActiveDirectory: {
43
+ DirectoryId: config[:directory_id]
44
+ },
45
+ Type: 'directory-service-authentication'
46
+ }
39
47
  else
40
48
  {
41
49
  MutualAuthentication: {
@@ -62,6 +70,7 @@ module CfnVpn
62
70
  SplitTunnel config[:split_tunnel]
63
71
  }
64
72
 
73
+ network_assoc_dependson = []
65
74
  config[:subnet_ids].each_with_index do |subnet, index|
66
75
  suffix = index == 0 ? "" : "For#{subnet.resource_safe}"
67
76
 
@@ -71,74 +80,112 @@ module CfnVpn
71
80
  SubnetId subnet
72
81
  }
73
82
 
74
- if config[:default_groups].any?
75
- config[:default_groups].each do |group|
76
- EC2_ClientVpnAuthorizationRule(:"TargetNetworkAuthorizationRule#{suffix}#{group.resource_safe}"[0..255]) {
77
- Condition(:EnableSubnetAssociation)
78
- DependsOn "ClientVpnTargetNetworkAssociation#{suffix}"
79
- Description FnSub("#{name} client-vpn auth rule for subnet association")
80
- AccessGroupId group
81
- ClientVpnEndpointId Ref(:ClientVpnEndpoint)
82
- TargetNetworkCidr CfnVpn::Templates::Helper.get_auth_cidr(config[:region], subnet)
83
- }
84
- end
85
- else
86
- 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]) {
87
89
  Condition(:EnableSubnetAssociation)
88
- DependsOn "ClientVpnTargetNetworkAssociation#{suffix}"
90
+ DependsOn network_assoc_dependson if network_assoc_dependson.any?
89
91
  Description FnSub("#{name} client-vpn auth rule for subnet association")
90
- AuthorizeAllGroups true
92
+ AccessGroupId group
91
93
  ClientVpnEndpointId Ref(:ClientVpnEndpoint)
92
- TargetNetworkCidr CfnVpn::Templates::Helper.get_auth_cidr(config[:region], subnet)
94
+ TargetNetworkCidr CfnVpn::Templates::Helper.get_auth_cidr(config[:region], config[:subnet_ids].first)
93
95
  }
94
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
95
107
 
96
- if subnet == config[:internet_route]
97
- EC2_ClientVpnRoute(:RouteToInternet) {
98
- Condition(:EnableSubnetAssociation)
99
- DependsOn "ClientVpnTargetNetworkAssociation#{suffix}"
100
- Description 'Route to the internet'
101
- ClientVpnEndpointId Ref(:ClientVpnEndpoint)
102
- DestinationCidrBlock '0.0.0.0/0'
103
- TargetVpcSubnetId config[:internet_route]
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]
104
142
  }
105
-
106
- EC2_ClientVpnAuthorizationRule(:RouteToInternetAuthorizationRule) {
107
- Condition(:EnableSubnetAssociation)
108
- DependsOn "ClientVpnTargetNetworkAssociation#{suffix}"
109
- Description 'Route to the internet'
110
- AuthorizeAllGroups true
111
- ClientVpnEndpointId Ref(:ClientVpnEndpoint)
112
- TargetNetworkCidr '0.0.0.0/0'
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
+ ])
113
159
  }
114
-
115
- output(:InternetRoute, config[:internet_route])
116
160
  end
117
161
  end
118
162
 
119
- config[:routes].each do |route|
120
- EC2_ClientVpnRoute(:"#{route[:cidr].resource_safe}VpnRoute") {
121
- Description route[:desc]
122
- ClientVpnEndpointId Ref(:ClientVpnEndpoint)
123
- DestinationCidrBlock route[:cidr]
124
- TargetVpcSubnetId route[:subnet]
125
- }
126
- if route[:groups].any?
127
- route[:groups].each do |group|
128
- EC2_ClientVpnAuthorizationRule(:"#{route[:cidr].resource_safe}AuthorizationRule#{group.resource_safe}"[0..255]) {
129
- Description route[:desc]
130
- AccessGroupId group
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]}".strip
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]}".strip
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]}".strip
184
+ AuthorizeAllGroups true
131
185
  ClientVpnEndpointId Ref(:ClientVpnEndpoint)
132
186
  TargetNetworkCidr route[:cidr]
133
187
  }
134
188
  end
135
- else
136
- EC2_ClientVpnAuthorizationRule(:"#{route[:cidr].resource_safe}AllowAllAuthorizationRule") {
137
- Description route[:desc]
138
- AuthorizeAllGroups true
139
- ClientVpnEndpointId Ref(:ClientVpnEndpoint)
140
- TargetNetworkCidr route[:cidr]
141
- }
142
189
  end
143
190
  end
144
191
 
@@ -149,13 +196,13 @@ module CfnVpn
149
196
  Type 'String'
150
197
  Value config.to_json
151
198
  Tags({
152
- Name: "#{name}-cfnvpn-config",
199
+ Name: "#{name}-cfnvpn-config",
153
200
  Environment: 'cfnvpn'
154
201
  })
155
202
  }
156
203
 
157
204
  if config[:start] || config[:stop]
158
- scheduler(name, config[:start], config[:stop])
205
+ scheduler(name, config[:start], config[:stop], config[:bucket])
159
206
  output(:Start, config[:start]) if config[:start]
160
207
  output(:Stop, config[:stop]) if config[:stop]
161
208
  end
@@ -170,6 +217,8 @@ module CfnVpn
170
217
 
171
218
  if config[:type] == 'federated'
172
219
  output(:SamlArn, config[:saml_arn])
220
+ elsif config[:type] == 'active-directory'
221
+ output(:DirectoryId, config[:directory_id])
173
222
  else
174
223
  output(:ClientCertArn, config[:client_cert_arn])
175
224
  end
@@ -179,7 +228,92 @@ module CfnVpn
179
228
  Output(name) { Value value }
180
229
  end
181
230
 
182
- def scheduler(name, start, stop)
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)
183
317
  IAM_Role(:ClientVpnSchedulerRole) {
184
318
  AssumeRolePolicyDocument({
185
319
  Version: '2012-10-17',
@@ -249,46 +383,17 @@ module CfnVpn
249
383
  ])
250
384
  }
251
385
 
386
+ s3_key = CfnVpn::Templates::Lambdas.package_lambda(name: name, bucket: bucket, func: 'scheduler', files: ['app.py'])
387
+
252
388
  Lambda_Function(:ClientVpnSchedulerFunction) {
253
- Runtime 'python3.7'
389
+ Runtime 'python3.8'
254
390
  Role FnGetAtt(:ClientVpnSchedulerRole, :Arn)
255
391
  MemorySize '128'
256
- Handler 'index.handler'
392
+ Handler 'app.handler'
393
+ Timeout 60
257
394
  Code({
258
- ZipFile: <<~EOS
259
- import boto3
260
-
261
- def handler(event, context):
262
-
263
- print(f"updating cfn-vpn stack {event['StackName']} parameter AssociateSubnets with value {event['AssociateSubnets']}")
264
-
265
- if event['AssociateSubnets'] == 'false':
266
- print(f"terminating current vpn sessions to {event['ClientVpnEndpointId']}")
267
- ec2 = boto3.client('ec2')
268
- resp = ec2.describe_client_vpn_connections(ClientVpnEndpointId=event['ClientVpnEndpointId'])
269
- for conn in resp['Connections']:
270
- if conn['Status']['Code'] == 'active':
271
- ec2.terminate_client_vpn_connections(
272
- ClientVpnEndpointId=event['ClientVpnEndpointId'],
273
- ConnectionId=conn['ConnectionId']
274
- )
275
- print(f"terminated session {conn['ConnectionId']}")
276
-
277
- client = boto3.client('cloudformation')
278
- print(client.update_stack(
279
- StackName=event['StackName'],
280
- UsePreviousTemplate=True,
281
- Capabilities=['CAPABILITY_IAM'],
282
- Parameters=[
283
- {
284
- 'ParameterKey': 'AssociateSubnets',
285
- 'ParameterValue': event['AssociateSubnets']
286
- }
287
- ]
288
- ))
289
-
290
- return 'OK'
291
- EOS
395
+ S3Bucket: bucket,
396
+ S3Key: s3_key
292
397
  })
293
398
  Tags([
294
399
  { Key: 'Name', Value: "#{name}-cfnvpn-scheduler-function" },
@@ -1,4 +1,4 @@
1
1
  module CfnVpn
2
- VERSION = "1.1.1".freeze
2
+ VERSION = "1.3.3".freeze
3
3
  CHANGE_SET_VERSION = VERSION.gsub('.', '-').freeze
4
4
  end
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.1.1
4
+ version: 1.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guslington
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-15 00:00:00.000000000 Z
11
+ date: 2021-11-04 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
@@ -279,7 +297,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
279
297
  - !ruby/object:Gem::Version
280
298
  version: '0'
281
299
  requirements: []
282
- rubygems_version: 3.1.4
300
+ rubygems_version: 3.1.6
283
301
  signing_key:
284
302
  specification_version: 4
285
303
  summary: creates and manages resources for the aws client vpn