-
Notifications
You must be signed in to change notification settings - Fork 1
/
template.yaml
344 lines (322 loc) · 13.2 KB
/
template.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: LM Okta log collector
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: !Ref FunctionTimeoutInSeconds
Mappings:
Constants:
LMOktaLogCollector:
Version: 0.0.1
Parameters:
LMCompanyName:
Type: String
Description: The LogicMonitor account name. If your domain is mycompany.logicmonitor.com, then it is mycompany
LMAccessId:
Type: String
NoEcho: true
Default: ""
Description: The LM API tokens access ID
LMAccessKey:
Type: String
NoEcho: true
Default: ""
Description: The LM API tokens access key
LMBearerToken:
Type: String
NoEcho: true
Default: ""
Description: The LM API Bearer token. (You must specify LMBearerToken if not providing LMAccessId, LMAccessKey. In case you provide all, LMAccessId and LMAccessKey will be used to authenticate with Logicmonitor. )
OktaDomain:
Type: String
AllowedPattern: ^(?!-)[A-Za-z0-9-]+([\-\.]{1}[a-z0-9]+)*\.[A-Za-z]{2,6}
Description: okta domain eg "company.okta.com".
OktaAPIKey:
Type: String
NoEcho: true
Description: Okta API key to fetch logs from okta.
LMResourceId:
Type: String
Description: "Ignored when LMLogsServiceName is specified. Is a json for resource mapping. if specified as {\"system.hostname\" : \"prod-node-us-west-1\"} all logs will be mapped against the device with property system.hostname = prod-node-us-west-1"
LMLogsServiceName:
Type: String
Default: "okta-system-logs"
Description: This will be used for anomaly detection.
FunctionName:
Type: String
Default: LM-Okta-Log-Collector
Description: The name for lambda function.
IncludeMetadataKeys:
Type: String
Default: 'severity,actor.displayName,actor.type,actor.alternateId,client.geographicalContext.city,displayMessage,eventType,target'
Description: comma separated keys to add as event metadata in a lm-log event. for nested json specify '.' eg - actor.displayname,actor.type
FunctionMemorySize:
Type: Number
Default: 2048
MinValue: 128
MaxValue: 4096
Description: The memory size for the OKTA Log Collector lambda function in MBs. CPU power assigned is proportional to the memory size. See https://docs.aws.amazon.com/lambda/latest/operatorguide/computing-power.html
FunctionTimeoutInSeconds:
Type: Number
Default: 110
MinValue: 20
Description: The timeout for the OKTA Log Collector lambda function in Seconds
ScheduleExpression:
Type: String
Default: "rate(2 minutes)"
Description: Cron expression for this lambda function. "rate(2 minutes)" means, function will be triggered every 2 minutes. See https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/ScheduledEvents.html for more details.
Conditions:
AccessIdEmpty: !Equals [!Ref LMAccessId, ""]
AccessKeyEmpty: !Equals [!Ref LMAccessKey, ""]
MustUseBearerTokenForAuth: !Or
- !Condition AccessIdEmpty
- !Condition AccessKeyEmpty
UseLMV1ForAuth: !Not [!Condition MustUseBearerTokenForAuth]
Rules:
NoAccessIdKeyProvided:
RuleCondition: !Or [!Equals [!Ref LMAccessId, ""], !Equals [!Ref LMAccessKey, ""]]
Assertions:
- AssertDescription: Please specify either LMAccessId, LMAccessKey both or LMBearerToken to authenticate with Logicmonitor.
Assert: !Not
- !Equals
- !Ref LMBearerToken
- ""
Resources:
LRTBucket:
Type: AWS::S3::Bucket
oktaLogCollector :
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
DependsOn: OktaLogCollectorZip
Properties:
FunctionName:
Ref: FunctionName
CodeUri:
Bucket: !Ref OktaLogCollectorZipsBucket
Key: main.zip
Handler: oktalogcollector.collector.lambda_handler
Runtime: python3.9
MemorySize: !Ref FunctionMemorySize
Policies:
- S3CrudPolicy:
BucketName: !Ref LRTBucket
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
- secretsmanager:DescribeSecret
Resource:
- Ref: OktaAPIKeySecret
- !If [UseLMV1ForAuth, !Ref AccessIdSecret, !Ref AWS::NoValue]
- !If [UseLMV1ForAuth, !Ref AccessKeySecret, !Ref AWS::NoValue]
- !If [MustUseBearerTokenForAuth, !Ref BearerTokenSecret, !Ref AWS::NoValue]
Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
Variables:
# last report time persistence config
LRT_S3_BUCKET : !Ref LRTBucket
# config for okta log collection.
OKTA_DOMAIN : !Ref OktaDomain
OKTA_API_KEY : !Ref OktaAPIKeySecret
# config for log-ingestion
COMPANY_NAME : !Ref LMCompanyName
LM_KEY_SERVICE : !Ref LMLogsServiceName
LM_RESOURCE_ID : !Ref LMResourceId
INCLUDE_METADATA_KEYS : !Ref IncludeMetadataKeys
LM_SERVICE_NAME_KEY : !Ref LMLogsServiceName
LM_ACCESS_ID : !If [UseLMV1ForAuth,!Ref AccessIdSecret, "" ]
LM_ACCESS_KEY : !If [UseLMV1ForAuth,!Ref AccessKeySecret, "" ]
LM_BEARER_TOKEN : !If [MustUseBearerTokenForAuth, !Ref BearerTokenSecret, ""]
Events:
oktaLogCollectionScheduleEvent:
Type: Schedule
Properties:
Schedule: !Ref ScheduleExpression
Enabled: True
version:
Type: AWS::Lambda::Version
Properties:
FunctionName: !Ref oktaLogCollector
lambdaRetryConfig:
Type: AWS::Lambda::EventInvokeConfig
Properties:
FunctionName: !Ref oktaLogCollector
MaximumRetryAttempts: 0 # Back-filling will be performed in next lamda invocation in case of lambda timeouts
Qualifier: !GetAtt version.Version
OktaLogCollectorZipsBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
OktaLogCollectorZip:
Type: Custom::OktaLogCollectorZip
Properties:
ServiceToken: !GetAtt "OktaLogCollectorZipCopier.Arn"
DestZipsBucket: !Ref "OktaLogCollectorZipsBucket"
LRTBucket: !Ref "LRTBucket"
SourceZipUrl:
Fn::Sub:
- "https://lm-logs-okta-collector.s3.amazonaws.com/stable/code/${LMOktaLogCollectorVersion}/lambda.zip"
- {
LMOktaLogCollectorVersion:
!FindInMap [ Constants, LMOktaLogCollector, Version ],
}
OktaLogCollectorZipCopier:
Type: AWS::Serverless::Function
Properties:
Description: Copies Logic Monitor okta-log-collector zip to the destination S3 bucket
Handler: index.handler
Runtime: python3.9
Timeout: 300
InlineCode: |
import json
import logging
import threading
import boto3
import urllib.request
import os
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def send_cfn_resp(event, context, response_status):
resp_body = json.dumps({
'Status': response_status,
'Reason': f'See reasons in CloudWatch Logs - group: {context.log_group_name}, stream:{context.log_stream_name}',
'PhysicalResourceId': context.log_stream_name,
'StackId': event['StackId'],
'RequestId': event['RequestId'],
'LogicalResourceId': event['LogicalResourceId'],
'Data': {}
}).encode('utf-8')
req = urllib.request.Request(url=event['ResponseURL'], data=resp_body, method='PUT')
with urllib.request.urlopen(req) as f:
logger.info(f'Sent response to CloudFormation: {f.status}, {f.reason}')
def delete_zips(bucket):
s3 = boto3.resource('s3')
bucket = s3.Bucket(bucket)
bucket.objects.all().delete()
def copy_zip(source_zip_url, dest_zips_bucket):
s3 = boto3.client('s3')
filename = "main.zip"
with urllib.request.urlopen(source_zip_url) as data:
s3.upload_fileobj(data, dest_zips_bucket, filename)
def clear_lrt_bucket(bucket):
s3 = boto3.resource('s3')
bucket = s3.Bucket(bucket)
bucket.objects.all().delete()
def timeout(event, context):
logger.error('Execution is about to time out, sending failure response to CloudFormation')
send_cfn_resp(event, context, 'FAILED')
def handler(event, context):
timer = threading.Timer((context.get_remaining_time_in_millis()
/ 1000.00) - 0.5, timeout, args=[event, context])
timer.start()
logger.info(f'Received event: {json.dumps(event)}')
try:
source_zip_url = event['ResourceProperties']['SourceZipUrl']
dest_zips_bucket = event['ResourceProperties']['DestZipsBucket']
LRTBucket = event['ResourceProperties']['LRTBucket']
if event['RequestType'] == 'Delete':
logger.info(f'Deleting : {dest_zips_bucket}')
delete_zips(dest_zips_bucket)
clear_lrt_bucket(LRTBucket)
else:
logger.info(f'Copying zip from : {source_zip_url} to {dest_zips_bucket}')
copy_zip(source_zip_url, dest_zips_bucket)
except Exception as e:
logger.exception(f'Exception when copying zip from {source_zip_url} to {dest_zips_bucket}')
send_cfn_resp(event, context, 'FAILED')
else:
send_cfn_resp(event, context, 'SUCCESS')
finally:
timer.cancel()
Policies:
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- s3:PutObject
- s3:DeleteObject
Resource:
- Fn::Join:
- "/"
- - Fn::GetAtt: "OktaLogCollectorZipsBucket.Arn"
- "*"
- Effect: Allow
Action:
- s3:DeleteObject
Resource:
- Fn::Join:
- "/"
- - Fn::GetAtt: "LRTBucket.Arn"
- "*"
- Effect: Allow
Action:
- s3:ListBucket
Resource:
- Fn::GetAtt: "OktaLogCollectorZipsBucket.Arn"
- Fn::GetAtt: "LRTBucket.Arn"
Environment:
Variables:
LM_OKTA_LOG_COLLECTOR_VERSION: !FindInMap [ Constants, LMOktaLogCollector, Version ]
AccessKeySecret:
Type: AWS::SecretsManager::Secret
Condition: UseLMV1ForAuth
Properties:
Description: Logic Monitor Access Key
SecretString:
Ref: LMAccessKey
AccessIdSecret:
Type: AWS::SecretsManager::Secret
Condition: UseLMV1ForAuth
Properties:
Description: Logic Monitor Access Id
SecretString:
Ref: LMAccessId
BearerTokenSecret:
Type: AWS::SecretsManager::Secret
Condition: MustUseBearerTokenForAuth
Properties:
Description: Logic Monitor Bearer Token
SecretString:
Ref: LMBearerToken
OktaAPIKeySecret:
Type: AWS::SecretsManager::Secret
Properties:
Description: Okta API key to fetch logs
SecretString:
Ref: OktaAPIKey
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Required
Parameters:
- FunctionName
- OktaDomain
- OktaAPIKey
- LMCompanyName
- Label:
default: Authenticate with Logicmonitor
Parameters:
- LMAccessId
- LMAccessKey
- LMBearerToken
- Label:
default: LM Logs (Optional)
Parameters:
- LMLogsServiceName
- LMResourceId
- IncludeMetadataKeys
- Label:
default: Lambda Function (Optional)
Parameters:
- ScheduleExpression
- FunctionMemorySize
- FunctionTimeoutInSeconds