1
1
import assert = require( 'assert' ) ;
2
2
3
3
import { Construct } from 'constructs' ;
4
- import { Stack , StackProps } from 'aws-cdk-lib' ;
4
+ import { Duration , Stack , StackProps } from 'aws-cdk-lib' ;
5
5
import * as ec2 from 'aws-cdk-lib/aws-ec2' ;
6
+ import * as events from 'aws-cdk-lib/aws-events' ;
7
+ import * as targets from 'aws-cdk-lib/aws-events-targets' ;
8
+ import * as iam from 'aws-cdk-lib/aws-iam' ;
9
+ import * as lambda from 'aws-cdk-lib/aws-lambda' ;
6
10
import * as ssm from 'aws-cdk-lib/aws-ssm' ;
11
+ import * as path from 'path'
7
12
8
13
import { SubnetSsmValue , VpcSsmValue } from '../core/ssm-wrangling'
14
+ import * as constants from '../core/constants'
9
15
10
16
export interface VpcMirrorStackProps extends StackProps {
17
+ readonly eventBusArn : string ;
11
18
readonly subnetIds : string [ ] ;
12
19
readonly subnetSsmParamNames : string [ ] ;
13
20
readonly vpcId : string ;
@@ -68,7 +75,7 @@ export class VpcMirrorStack extends Stack {
68
75
// See: https://docs.aws.amazon.com/vpc/latest/mirroring/tm-example-non-vpc.html
69
76
const filter = new ec2 . CfnTrafficMirrorFilter ( this , `Filter` , {
70
77
description : 'Mirror non-local VPC traffic' ,
71
- tags : [ { key : " Name" , value : props . vpcId } ]
78
+ tags : [ { key : ' Name' , value : props . vpcId } ]
72
79
} ) ;
73
80
new ec2 . CfnTrafficMirrorFilterRule ( this , `FRule-RejectLocalOutbound` , {
74
81
destinationCidrBlock : '10.0.0.0/16' , // TODO: Need to figure this out instead of hardcode
@@ -118,5 +125,147 @@ export class VpcMirrorStack extends Stack {
118
125
tier : ssm . ParameterTier . STANDARD ,
119
126
} ) ;
120
127
vpcParam . node . addDependency ( filter ) ;
128
+
129
+ /**
130
+ * Configure the resources required for event-based mirroring configuration
131
+ */
132
+ // Get a handle to the cluster event bus
133
+ const clusterBus = events . EventBus . fromEventBusArn ( this , 'ClusterBus' , props . eventBusArn ) ;
134
+
135
+ // Archive Arkime events related to this User VPC to enable replay, with a focus on shorter-term debugging
136
+ clusterBus . archive ( 'Archive' , {
137
+ archiveName : `Arkime-${ props . vpcId } ` ,
138
+ description : `Archive of Arkime events for VPC ${ props . vpcId } ` ,
139
+ eventPattern : {
140
+ source : [ constants . EVENT_SOURCE ] ,
141
+ detail : {
142
+ 'vpc_id' : events . Match . exactString ( props . vpcId )
143
+ }
144
+ } ,
145
+ retention : Duration . days ( 30 ) ,
146
+ } ) ;
147
+
148
+ // Create the Lambda that will set up the traffic mirroring for ENIs in our VPC
149
+ const createLambda = new lambda . Function ( this , 'CreateEniMirrorLambda' , {
150
+ functionName : `CreateEniMirror-${ props . vpcId } ` ,
151
+ runtime : lambda . Runtime . PYTHON_3_9 ,
152
+ code : lambda . Code . fromAsset ( path . resolve ( __dirname , '..' , '..' , 'manage_arkime' ) ) ,
153
+ handler : 'lambda_handlers.create_eni_mirror_handler' ,
154
+ timeout : Duration . seconds ( 30 ) , // Something has gone very wrong if this is exceeded
155
+ } ) ;
156
+ createLambda . addToRolePolicy (
157
+ new iam . PolicyStatement ( {
158
+ effect : iam . Effect . ALLOW ,
159
+ actions : [
160
+ // TODO: Should scope this down.
161
+ // We need ec2:CreateTrafficMirrorSession in order to set up our session, but whenever I add *just*
162
+ // that, I get an UnauthorizedOperation. The docs say that's all that should be required, but the
163
+ // documentation appears wrong or there's something extra mysterious going on here. Even CloudTrail
164
+ // indicates the only call being made is CreateTrafficMirrorSession, but it's still failing. The
165
+ // exception also doesn't indicate otherwise.
166
+ // See: https://docs.aws.amazon.com/vpc/latest/mirroring/traffic-mirroring-security.html
167
+ 'ec2:*' ,
168
+ ] ,
169
+ resources : [
170
+ `arn:aws:ec2:${ this . region } :${ this . account } :*`
171
+ ]
172
+ } )
173
+ ) ;
174
+ createLambda . addToRolePolicy (
175
+ new iam . PolicyStatement ( {
176
+ effect : iam . Effect . ALLOW ,
177
+ actions : [
178
+ 'ssm:GetParameter' ,
179
+ 'ssm:PutParameter' ,
180
+ ] ,
181
+ resources : [
182
+ `arn:aws:ssm:${ this . region } :${ this . account } :*`
183
+ ]
184
+ } )
185
+ ) ;
186
+ createLambda . addToRolePolicy (
187
+ new iam . PolicyStatement ( {
188
+ effect : iam . Effect . ALLOW ,
189
+ actions : [
190
+ 'cloudwatch:PutMetricData' ,
191
+ ] ,
192
+ resources : [
193
+ "*"
194
+ ]
195
+ } )
196
+ ) ;
197
+
198
+ // Create a rule to funnel appropriate events to our setup lambda
199
+ const createRule = new events . Rule ( this , 'RuleCreateEniMirror' , {
200
+ eventBus : clusterBus ,
201
+ eventPattern : {
202
+ source : [ constants . EVENT_SOURCE ] ,
203
+ detailType : [ constants . EVENT_DETAIL_TYPE_CREATE_ENI_MIRROR ] ,
204
+ detail : {
205
+ 'vpc_id' : events . Match . exactString ( props . vpcId )
206
+ }
207
+ } ,
208
+ targets : [ new targets . LambdaFunction ( createLambda ) ]
209
+ } ) ;
210
+ createRule . node . addDependency ( clusterBus ) ;
211
+
212
+ // Create the Lambda that will tear down the traffic mirroring for ENIs in our VPC
213
+ const destroyLambda = new lambda . Function ( this , 'DestroyEniMirrorLambda' , {
214
+ functionName : `DestroyEniMirror-${ props . vpcId } ` ,
215
+ runtime : lambda . Runtime . PYTHON_3_9 ,
216
+ code : lambda . Code . fromAsset ( path . resolve ( __dirname , '..' , '..' , 'manage_arkime' ) ) ,
217
+ handler : 'lambda_handlers.destroy_eni_mirror_handler' ,
218
+ timeout : Duration . seconds ( 30 ) , // Something has gone very wrong if this is exceeded
219
+ } ) ;
220
+ destroyLambda . addToRolePolicy (
221
+ new iam . PolicyStatement ( {
222
+ effect : iam . Effect . ALLOW ,
223
+ actions : [
224
+ // TODO: Should scope this down.
225
+ // Just need ec2:DeleteTrafficMirroringSession, but failing similar to the Create Lambda
226
+ 'ec2:*' ,
227
+ ] ,
228
+ resources : [
229
+ `arn:aws:ec2:${ this . region } :${ this . account } :*`
230
+ ]
231
+ } )
232
+ ) ;
233
+ destroyLambda . addToRolePolicy (
234
+ new iam . PolicyStatement ( {
235
+ effect : iam . Effect . ALLOW ,
236
+ actions : [
237
+ 'ssm:GetParameter' ,
238
+ 'ssm:DeleteParameter' ,
239
+ ] ,
240
+ resources : [
241
+ `arn:aws:ssm:${ this . region } :${ this . account } :*`
242
+ ]
243
+ } )
244
+ ) ;
245
+ destroyLambda . addToRolePolicy (
246
+ new iam . PolicyStatement ( {
247
+ effect : iam . Effect . ALLOW ,
248
+ actions : [
249
+ 'cloudwatch:PutMetricData' ,
250
+ ] ,
251
+ resources : [
252
+ "*"
253
+ ]
254
+ } )
255
+ ) ;
256
+
257
+ // Create a rule to funnel appropriate events to our teardwon lambda
258
+ const destroyRule = new events . Rule ( this , 'RuleDestroyEniMirror' , {
259
+ eventBus : clusterBus ,
260
+ eventPattern : {
261
+ source : [ constants . EVENT_SOURCE ] ,
262
+ detailType : [ constants . EVENT_DETAIL_TYPE_DESTROY_ENI_MIRROR ] ,
263
+ detail : {
264
+ 'vpc_id' : events . Match . exactString ( props . vpcId )
265
+ }
266
+ } ,
267
+ targets : [ new targets . LambdaFunction ( destroyLambda ) ]
268
+ } ) ;
269
+ destroyRule . node . addDependency ( clusterBus ) ;
121
270
}
122
271
}
0 commit comments