cfn-vpn 1.3.4 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,26 +1,41 @@
1
+ import os
1
2
  import socket
2
3
  import boto3
3
4
  from botocore.exceptions import ClientError
5
+ from lib.slack import Slack
6
+ from states import *
4
7
  import logging
8
+ from quotas import increase_quota, AUTH_RULE_TABLE_QUOTA_CODE, ROUTE_TABLE_QUOTA_CODE
5
9
 
6
- logger = logging.getLogger()
10
+ logger = logging.getLogger(__name__)
7
11
  logger.setLevel(logging.INFO)
8
12
 
13
+ SLACK_USERNAME = 'CfnVpn Route Table Event'
9
14
 
10
15
  def delete_route(client, vpn_endpoint, subnet, cidr):
16
+ try:
11
17
  client.delete_client_vpn_route(
12
18
  ClientVpnEndpointId=vpn_endpoint,
13
19
  TargetVpcSubnetId=subnet,
14
20
  DestinationCidrBlock=cidr,
15
21
  )
22
+ except ClientError as e:
23
+ if e.response['Error']['Code'] == 'InvalidClientVpnEndpointAuthorizationRuleNotFound':
24
+ logger.info(f"route not found when deleting", exc_info=True)
25
+ else:
26
+ raise e
16
27
 
17
28
 
18
- def create_route(client, event, cidr):
29
+ def create_route(client, event, cidr, target_subnet):
30
+ description = f"cfnvpn auto generated route for endpoint {event['Record']}."
31
+ if event['Description']:
32
+ description += f" {event['Description']}"
33
+
19
34
  client.create_client_vpn_route(
20
35
  ClientVpnEndpointId=event['ClientVpnEndpointId'],
21
36
  DestinationCidrBlock=cidr,
22
- TargetVpcSubnetId=event['TargetSubnet'],
23
- Description=f"cfnvpn auto generated route for endpoint {event['Record']}. {event['Description']}"
37
+ TargetVpcSubnetId=target_subnet,
38
+ Description=description
24
39
  )
25
40
 
26
41
 
@@ -34,15 +49,27 @@ def revoke_route_auth(client, event, cidr, group = None):
34
49
  args['RevokeAllGroups'] = True
35
50
  else:
36
51
  args['AccessGroupId'] = group
37
-
38
- client.revoke_client_vpn_ingress(**args)
52
+
53
+ try:
54
+ client.revoke_client_vpn_ingress(**args)
55
+ except ClientError as e:
56
+ if e.response['Error']['Code'] == 'ConcurrentMutationLimitExceeded':
57
+ logger.warn(f"revoking auth is being rate limited", exc_info=True)
58
+ elif e.response['Error']['Code'] == 'InvalidClientVpnEndpointAuthorizationRuleNotFound':
59
+ logger.info(f"rule not found when revoking", exc_info=True)
60
+ else:
61
+ raise e
39
62
 
40
63
 
41
64
  def authorize_route(client, event, cidr, group = None):
65
+ description = f"cfnvpn auto generated authorization for endpoint {event['Record']}."
66
+ if event['Description']:
67
+ description += f" {event['Description']}"
68
+
42
69
  args = {
43
70
  'ClientVpnEndpointId': event['ClientVpnEndpointId'],
44
71
  'TargetNetworkCidr': cidr,
45
- 'Description': f"cfnvpn auto generated authorization for endpoint {event['Record']}. {event['Description']}"
72
+ 'Description': description
46
73
  }
47
74
 
48
75
  if group is None:
@@ -54,7 +81,8 @@ def authorize_route(client, event, cidr, group = None):
54
81
 
55
82
 
56
83
  def get_routes(client, event):
57
- response = client.describe_client_vpn_routes(
84
+ paginator = client.get_paginator('describe_client_vpn_routes')
85
+ response_iterator = paginator.paginate(
58
86
  ClientVpnEndpointId=event['ClientVpnEndpointId'],
59
87
  Filters=[
60
88
  {
@@ -63,113 +91,170 @@ def get_routes(client, event):
63
91
  }
64
92
  ]
65
93
  )
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
94
+
95
+ return [route for page in response_iterator
96
+ for route in page['Routes']
97
+ if event['Record'] in route['Description']]
70
98
 
71
99
 
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
- ]
100
+ def get_auth_rules(client, event):
101
+ paginator = client.get_paginator('describe_client_vpn_authorization_rules')
102
+ response_iterator = paginator.paginate(
103
+ ClientVpnEndpointId=event['ClientVpnEndpointId']
81
104
  )
82
- return response['AuthorizationRules']
105
+
106
+ return [rule for page in response_iterator
107
+ for rule in page['AuthorizationRules']
108
+ if event['Record'] in rule['Description']]
109
+
110
+
111
+ def expired_auth_rules(auth_rules, cidrs, groups):
112
+ for rule in auth_rules:
113
+ # if there is a rule for the record with an old cidr
114
+ if rule['DestinationCidr'] not in cidrs:
115
+ yield rule
116
+ # if there is a rule for a group that is no longer in the event
117
+ if groups and rule['GroupId'] not in groups:
118
+ yield rule
119
+ # if there is a rule for allow all but groups are in the event
120
+ if groups and rule['AccessAll']:
121
+ yield rule
122
+
123
+
124
+ def expired_routes(routes, cidrs):
125
+ for route in routes:
126
+ if route['DestinationCidr'] not in cidrs:
127
+ yield route
83
128
 
84
129
 
85
130
  def handler(event,context):
131
+
132
+ logger.info(f"auto route populator triggered with event : {event}")
133
+ slack = Slack(username=SLACK_USERNAME)
86
134
 
87
135
  # DNS lookup on the dns record and return all IPS for the endpoint
88
136
  try:
89
137
  cidrs = [ ip + "/32" for ip in socket.gethostbyname_ex(event['Record'])[-1]]
90
138
  logger.info(f"resolved endpoint {event['Record']} to {cidrs}")
91
139
  except socket.gaierror as e:
92
- logger.exception(f"failed to resolve record {event['Record']}")
140
+ logger.error(f"failed to resolve record {event['Record']}", exc_info=True)
141
+ slack.post_event(message=f"failed to resolve record {event['Record']}", state=RESOLVE_FAILED, error=e)
93
142
  return 'KO'
94
143
 
95
144
  client = boto3.client('ec2')
145
+
146
+ # describe vpn and check if subnets are associated with the vpn
147
+ response = client.describe_client_vpn_endpoints(
148
+ ClientVpnEndpointIds=[event['ClientVpnEndpointId']]
149
+ )
150
+
151
+ if not response['ClientVpnEndpoints']:
152
+ logger.error(f"endpoint not found")
153
+ slack.post_event(message=f"failed create routes for {event['Record']}", state=FAILED, error="endpoint not found")
154
+ return 'KO'
155
+
156
+ endpoint = response['ClientVpnEndpoints'][0]
157
+ if endpoint['Status'] == 'pending-associate':
158
+ logger.error(f"no subnets associated with endpoint")
159
+ slack.post_event(message=f"failed create routes for {event['Record']}", state=FAILED, error="vpn is in a stopped state")
160
+ return 'KO'
161
+
96
162
  routes = get_routes(client, event)
163
+ auth_rules = get_auth_rules(client, event)
164
+
165
+ auto_limit_increase = os.environ.get('AUTO_LIMIT_INCREASE')
166
+ route_limit_increase_required = False
167
+ auth_rules_limit_increase_required = False
97
168
 
98
169
  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)
170
+ # create route if doesn't exist
171
+ for subnet in event['TargetSubnets']:
172
+ if not any(route['DestinationCidr'] == cidr and route['TargetSubnet'] == subnet for route in routes):
173
+ try:
174
+ create_route(client, event, cidr, subnet)
175
+ except ClientError as e:
176
+ if e.response['Error']['Code'] == 'ClientVpnRouteLimitExceeded':
177
+ route_limit_increase_required = True
178
+ logger.error("vpn route table has reached the route limit", exc_info=True)
179
+ slack.post_event(
180
+ message=f"unable to create route {cidr} from {event['Record']}",
181
+ state=ROUTE_LIMIT_EXCEEDED,
182
+ error="vpn route table has reached the route limit"
183
+ )
184
+ elif e.response['Error']['Code'] == 'InvalidClientVpnActiveAssociationNotFound':
185
+ logger.warn("no subnets are associated with the vpn", exc_info=True)
186
+ slack.post_event(
187
+ message=f"unable to create the route {cidr} from {event['Record']}",
188
+ state=SUBNET_NOT_ASSOCIATED,
189
+ error="no subnets are associated with the vpn"
190
+ )
191
+ else:
192
+ logger.error("encountered a unexpected client error when creating a route", exc_info=True)
108
193
  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")
194
+ slack.post_event(
195
+ message=f"created new route {cidr} ({event['Record']}) to target subnet {subnet}",
196
+ state=NEW_ROUTE
197
+ )
198
+
199
+ # remove route if target subnet has changed
200
+ for route in routes:
201
+ if route['DestinationCidr'] == cidr and route['TargetSubnet'] not in event['TargetSubnets']:
124
202
  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]
203
+
204
+ # collect all rules that matches the current cidr
205
+ cidr_auth_rules = [rule for rule in auth_rules if rule['DestinationCidr'] == cidr]
206
+
207
+ try:
208
+ # create rules for newly added groups
132
209
  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}")
210
+ existing_groups = list(set(rule['GroupId'] for rule in cidr_auth_rules))
211
+ new_groups = [group for group in event['Groups'] if group not in existing_groups]
212
+
213
+ for group in new_groups:
142
214
  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
215
 
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'])
216
+ # create an allow all rule
217
+ elif 'Groups' not in event and not cidr_auth_rules:
218
+ authorize_route(client, event, cidr)
219
+
169
220
  except ClientError as e:
170
- if e.response['Error']['Code'] == 'InvalidClientVpnRouteNotFound':
171
- pass
172
- else:
173
- raise e
221
+ if e.response['Error']['Code'] == 'ClientVpnAuthorizationRuleLimitExceeded':
222
+ auth_rules_limit_increase_required = True
223
+ logger.error("vpn has reached the authorization rule limit", exc_info=True)
224
+ slack.post_event(
225
+ message=f"unable add to authorization rule for route {cidr} from {event['Record']}",
226
+ state=AUTH_RULE_LIMIT_EXCEEDED,
227
+ error="vpn has reached the authorization rule limit"
228
+ )
229
+ continue
230
+ else:
231
+ logger.error("encountered a unexpected client error when creating an auth rule", exc_info=True)
232
+
233
+ # request route limit increase
234
+ if route_limit_increase_required and auto_limit_increase:
235
+ case_id = increase_quota(10, ROUTE_TABLE_QUOTA_CODE, event['ClientVpnEndpointId'])
236
+ if case_id is not None:
237
+ slack.post_event(message=f"requested an increase for the routes per vpn service quota", state=QUOTA_INCREASE_REQUEST, support_case=case_id)
238
+ else:
239
+ logger.info(f"routes per vpn service quota increase request pending")
240
+
241
+ # request auth rule limit increase
242
+ if auth_rules_limit_increase_required and auto_limit_increase:
243
+ case_id = increase_quota(20, AUTH_RULE_TABLE_QUOTA_CODE, event['ClientVpnEndpointId'])
244
+ if case_id is not None:
245
+ slack.post_event(message=f"requested an increase for the authorization rules per vpn service quota", state=QUOTA_INCREASE_REQUEST, support_case=case_id)
246
+ else:
247
+ logger.info(f"authorization rules per vpn service quota increase request pending")
248
+
249
+ # remove expired auth rules
250
+ for rule in expired_auth_rules(auth_rules, cidrs, event.get('Groups', [])):
251
+ logger.info(f"removing expired auth rule {rule['DestinationCidr']} for endpoint {event['Record']}")
252
+ revoke_route_auth(client, event, route['DestinationCidr'])
253
+
254
+ # remove expired routes
255
+ for route in expired_routes(routes, cidrs):
256
+ logger.info(f"removing expired route {route['DestinationCidr']} for endpoint {event['Record']}")
257
+ delete_route(client, event['ClientVpnEndpointId'], route['TargetSubnet'], route['DestinationCidr'])
258
+ slack.post_event(message=f"removed expired route {route['DestinationCidr']} for endpoint {event['Record']}", state=EXPIRED_ROUTE)
174
259
 
175
260
  return 'OK'
@@ -0,0 +1,37 @@
1
+ import boto3
2
+
3
+ ROUTE_TABLE_QUOTA_CODE = 'L-401D78F7'
4
+ AUTH_RULE_TABLE_QUOTA_CODE = 'L-9A1BC94B'
5
+ EC2_SERVICE_CODE = 'ec2'
6
+ IN_PROGRESS = ['PENDING', 'CASE_OPENED']
7
+
8
+ def get_route_count(endpoint) -> int:
9
+ client = boto3.client('ec2')
10
+ response = client.describe_client_vpn_routes(
11
+ ClientVpnEndpointId=endpoint,
12
+ )
13
+ return len(response['Routes'])
14
+
15
+ def quota_request_open(quota_code) -> bool:
16
+ client = boto3.client('service-quotas')
17
+ response = client.list_requested_service_quota_change_history_by_quota(
18
+ ServiceCode=EC2_SERVICE_CODE,
19
+ QuotaCode=quota_code
20
+ )
21
+ # Status='PENDING'|'CASE_OPENED'|'APPROVED'|'DENIED'|'CASE_CLOSED'
22
+ return any(req['status'] in IN_PROGRESS for req in response['RequestedQuotas'])
23
+
24
+ def increase_quota(increase_value, quota_code, endpoint) -> str:
25
+ if quota_request_open(quota_code):
26
+ return None
27
+
28
+ current_route_count = get_route_count(endpoint)
29
+ desired_value = current_route_count + increase_value
30
+
31
+ client = boto3.client('service-quotas')
32
+ response = client.request_service_quota_increase(
33
+ ServiceCode=EC2_SERVICE_CODE,
34
+ QuotaCode=quota_code,
35
+ DesiredValue=desired_value
36
+ )
37
+ return response['CaseId']
@@ -0,0 +1,21 @@
1
+ """
2
+ states:
3
+
4
+ FAILED: general failure
5
+ NEW_ROUTE: new route added to route table
6
+ EXPIRED_ROUTE: cidr is no longer associated with DNS entry and is removed from the route table
7
+ ROUTE_LIMIT_EXCEEDED: no new routes can be added to the route table due to aws route table limit
8
+ AUTH_RULE_LIMIT_EXCEEDED: no new authorization rules can be added to the rule list due to aws auth rule limit
9
+ RESOLVE_FAILED: failed to resolve the provided dns entry
10
+ SUBNET_NOT_ASSOCIATED: no subnets are associated with the client vpn
11
+ QUOTA_INCREASE_REQUEST: automatic quota increase made
12
+ """
13
+
14
+ FAILED = 'FAILED'
15
+ NEW_ROUTE = 'NEW_ROUTE'
16
+ EXPIRED_ROUTE = 'EXPIRED_ROUTE'
17
+ ROUTE_LIMIT_EXCEEDED = 'ROUTE_LIMIT_EXCEEDED'
18
+ AUTH_RULE_LIMIT_EXCEEDED = 'AUTH_RULE_LIMIT_EXCEEDED'
19
+ RESOLVE_FAILED = 'RESOLVE_FAILED'
20
+ SUBNET_NOT_ASSOCIATED = 'SUBNET_NOT_ASSOCIATED'
21
+ QUOTA_INCREASE_REQUEST = 'QUOTA_INCREASE_REQUEST'
@@ -0,0 +1,66 @@
1
+ import os
2
+ import json
3
+ import logging
4
+ import urllib
5
+
6
+ logger = logging.getLogger(__name__)
7
+ logger.setLevel(logging.INFO)
8
+
9
+ class Slack:
10
+
11
+ def __init__(self, username):
12
+ self.username = username
13
+ self.slack_url = os.environ.get('SLACK_URL')
14
+
15
+ def post_event(self, message, state, error=None, support_case=None):
16
+ """Posts event to slack using an incoming webhook
17
+ Parameters
18
+ ----------
19
+ message: str
20
+ message to post to slack
21
+ state: str
22
+ the state of the event
23
+ error: str
24
+ error message to add to the message
25
+ support_case: str
26
+ displays a aws console link to the support case in the message
27
+ """
28
+
29
+ if not self.slack_url.startswith('https://hooks.slack.com'):
30
+ return
31
+
32
+ if 'FAILED' in state or 'LIMIT_EXCEEDED' in state:
33
+ colour = '#ad0614'
34
+ elif 'NOT_ASSOCIATED' in state:
35
+ colour = '#d4b126'
36
+ else:
37
+ colour = '#3ead3e'
38
+
39
+ text = f'Message: {message}\nState: {state}'
40
+
41
+ if error:
42
+ text += f'\nError: {error}'
43
+
44
+ if support_case:
45
+ text += f'\nSupport Case: <https://console.aws.amazon.com/support/cases#/{support_case}|{support_case}>'
46
+
47
+ payload = {
48
+ 'username': self.username,
49
+ 'attachments': [
50
+ {
51
+ 'color': colour,
52
+ 'text': text,
53
+ 'mrkdwn_in': ['text','pretext']
54
+ }
55
+ ]
56
+ }
57
+
58
+ try:
59
+ urllib.request.urlopen(urllib.request.Request(
60
+ self.slack_url,
61
+ headers={'Content-Type': 'application/json'},
62
+ data=json.dumps(payload).encode('utf-8')
63
+ ))
64
+ except urllib.error.HTTPError as e:
65
+ logger.error(f"failed to post slack notification. REASON: {e.reason} CODE: {e.code}", exc_info=True)
66
+
@@ -1,36 +1,54 @@
1
1
  import boto3
2
2
  import logging
3
+ from lib.slack import Slack
4
+ from states import *
3
5
 
4
6
  logger = logging.getLogger()
5
7
  logger.setLevel(logging.INFO)
6
8
 
9
+ SLACK_USERNAME = 'CfnVpn Scheduler'
10
+
7
11
  def handler(event, context):
8
12
 
9
13
  logger.info(f"updating cfn-vpn stack {event['StackName']} parameter AssociateSubnets with value {event['AssociateSubnets']}")
14
+ slack = Slack(username=SLACK_USERNAME)
15
+
16
+ try:
17
+ if event['AssociateSubnets'] == 'false':
18
+ logger.info(f"terminating current vpn sessions to {event['ClientVpnEndpointId']}")
19
+ ec2 = boto3.client('ec2')
20
+ resp = ec2.describe_client_vpn_connections(ClientVpnEndpointId=event['ClientVpnEndpointId'])
21
+ for conn in resp['Connections']:
22
+ if conn['Status']['Code'] == 'active':
23
+ ec2.terminate_client_vpn_connections(
24
+ ClientVpnEndpointId=event['ClientVpnEndpointId'],
25
+ ConnectionId=conn['ConnectionId']
26
+ )
27
+ logger.info(f"terminated session {conn['ConnectionId']}")
28
+
29
+ client = boto3.client('cloudformation')
30
+ logger.info(client.update_stack(
31
+ StackName=event['StackName'],
32
+ UsePreviousTemplate=True,
33
+ Capabilities=['CAPABILITY_IAM'],
34
+ Parameters=[
35
+ {
36
+ 'ParameterKey': 'AssociateSubnets',
37
+ 'ParameterValue': event['AssociateSubnets']
38
+ }
39
+ ]
40
+ ))
41
+ except Exception as ex:
42
+ logger.error(f"failed to start/stop client vpn", exc_info=True)
43
+ if event['AssociateSubnets'] == 'true':
44
+ slack.post_event(message=f"failed to associate subnets with the client vpn", state=START_FAILED, error=ex)
45
+ else:
46
+ slack.post_event(message=f"failed to disassociate subnets with the client vpn", state=STOP_FAILED, error=ex)
47
+ return 'KO'
10
48
 
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
- ))
49
+ if event['AssociateSubnets'] == 'true':
50
+ slack.post_event(message=f"successfully associated subnets with the client vpn", state=START_IN_PROGRESS)
51
+ else:
52
+ slack.post_event(message=f"successfully disassociated subnets with the client vpn", state=STOP_IN_PROGRESS)
35
53
 
36
54
  return 'OK'
@@ -0,0 +1,13 @@
1
+ """
2
+ states:
3
+
4
+ START_IN_PROGRESS: associating subnets with the Client VPN
5
+ STOP_IN_PROGRESS: disassociating subnets with the Client VPN
6
+ START_FAILED: failed to associated subnets with the Client VPN
7
+ STOP_FAILED: failed to disassociated subnets with the Client VPN
8
+ """
9
+
10
+ START_IN_PROGRESS = 'START_IN_PROGRESS'
11
+ STOP_IN_PROGRESS = 'STOP_IN_PROGRESS'
12
+ START_FAILED = 'START_FAILED'
13
+ STOP_FAILED = 'STOP_FAILED'
@@ -17,7 +17,16 @@ module CfnVpn
17
17
  FileUtils.mkdir_p(zipfile_path)
18
18
  Zip::File.open("#{zipfile_path}/#{zipfile_name}", Zip::File::CREATE) do |zipfile|
19
19
  files.each do |file|
20
- zipfile.add(file, File.join("#{lambdas_dir}/#{func}", file))
20
+ file_path = lambdas_dir
21
+
22
+ # this is to allow for shared library methods in a different lambda directory
23
+ # so the files in the function lambda is in the root of the zip
24
+ if file.include? func
25
+ file_path = "#{file_path}/#{func}"
26
+ file.gsub!("#{func}/", "")
27
+ end
28
+
29
+ zipfile.add(file, File.join(file_path, file))
21
30
  end
22
31
  end
23
32