21
21
import static datadog .remoteconfig .Capabilities .CAPABILITY_ASM_TRUSTED_IPS ;
22
22
import static datadog .remoteconfig .Capabilities .CAPABILITY_ASM_USER_BLOCKING ;
23
23
import static datadog .remoteconfig .Capabilities .CAPABILITY_ENDPOINT_FINGERPRINT ;
24
+ import static datadog .trace .api .ConfigOrigin .DEFAULT ;
25
+ import static datadog .trace .api .ConfigOrigin .LOCAL_STABLE_CONFIG ;
24
26
25
27
import com .datadog .appsec .AppSecModule ;
26
28
import com .datadog .appsec .AppSecSystem ;
45
47
import datadog .remoteconfig .state .ConfigKey ;
46
48
import datadog .remoteconfig .state .ProductListener ;
47
49
import datadog .trace .api .Config ;
50
+ import datadog .trace .api .ConfigOrigin ;
48
51
import datadog .trace .api .ProductActivation ;
49
52
import datadog .trace .api .UserIdCollectionMode ;
50
53
import datadog .trace .api .telemetry .LogCollector ;
51
54
import datadog .trace .api .telemetry .WafMetricCollector ;
52
55
import java .io .ByteArrayInputStream ;
56
+ import java .io .File ;
53
57
import java .io .FileInputStream ;
54
58
import java .io .FileNotFoundException ;
55
59
import java .io .IOException ;
56
60
import java .io .InputStream ;
61
+ import java .nio .file .Paths ;
57
62
import java .util .ArrayList ;
58
63
import java .util .Collections ;
59
64
import java .util .HashMap ;
60
- import java .util .HashSet ;
61
65
import java .util .List ;
62
66
import java .util .Map ;
63
67
import java .util .Set ;
@@ -71,12 +75,12 @@ public class AppSecConfigServiceImpl implements AppSecConfigService {
71
75
private static final Logger log = LoggerFactory .getLogger (AppSecConfigServiceImpl .class );
72
76
73
77
private static final String DEFAULT_CONFIG_LOCATION = "default_config.json" ;
78
+ private static final String DEFAULT_WAF_CONFIG_RULE = "DEFAULT_WAF_CONFIG" ;
74
79
75
80
private final ConfigurationPoller configurationPoller ;
76
81
private WafBuilder wafBuilder ;
77
82
78
- private MergedAsmFeatures mergedAsmFeatures ;
79
- private volatile boolean initialized ;
83
+ private final MergedAsmFeatures mergedAsmFeatures = new MergedAsmFeatures ();
80
84
81
85
private final ConcurrentHashMap <String , SubconfigListener > subconfigListeners =
82
86
new ConcurrentHashMap <>();
@@ -95,10 +99,9 @@ public class AppSecConfigServiceImpl implements AppSecConfigService {
95
99
.build ()
96
100
.adapter (Types .newParameterizedType (Map .class , String .class , Object .class ));
97
101
98
- private boolean hasUserWafConfig ;
99
- private boolean defaultConfigActivated ;
100
- private final Set <String > usedDDWafConfigKeys = new HashSet <>();
101
- private final String DEFAULT_WAF_CONFIG_RULE = "DEFAULT_WAF_CONFIG" ;
102
+ private ConfigOrigin wafConfigOrigin ;
103
+ private final Set <String > usedDDWafConfigKeys =
104
+ Collections .newSetFromMap (new ConcurrentHashMap <>());
102
105
private String currentRuleVersion ;
103
106
private List <AppSecModule > modulesToUpdateVersionIn ;
104
107
@@ -113,13 +116,14 @@ public AppSecConfigServiceImpl(
113
116
if (tracerConfig .isAppSecWafMetrics ()) {
114
117
traceSegmentPostProcessors .add (statsReporter );
115
118
}
119
+ wafConfigOrigin = hasUserWafConfig (tracerConfig ) ? LOCAL_STABLE_CONFIG : DEFAULT ;
116
120
}
117
121
118
122
private void subscribeConfigurationPoller () {
119
123
// see also close() method
120
124
subscribeAsmFeatures ();
121
125
122
- if (! hasUserWafConfig ) {
126
+ if (wafConfigOrigin != LOCAL_STABLE_CONFIG ) {
123
127
subscribeRulesAndData ();
124
128
} else {
125
129
log .debug ("Will not subscribe to ASM, ASM_DD and ASM_DATA (AppSec custom rules in use)" );
@@ -173,16 +177,10 @@ private class AppSecConfigChangesListener implements ProductListener {
173
177
@ Override
174
178
public void accept (ConfigKey configKey , byte [] content , PollingRateHinter pollingRateHinter )
175
179
throws IOException {
176
- if (!initialized ) {
177
- throw new IllegalStateException ();
178
- }
180
+ maybeApplyDefaultConfig ();
179
181
180
182
if (content == null ) {
181
- try {
182
- wafBuilder .removeConfig (configKey .toString ());
183
- } catch (UnclassifiedWafException e ) {
184
- throw new RuntimeException (e );
185
- }
183
+ remove (configKey , pollingRateHinter );
186
184
} else {
187
185
Map <String , Object > contentMap =
188
186
ADAPTER .fromJson (Okio .buffer (Okio .source (new ByteArrayInputStream (content ))));
@@ -197,7 +195,11 @@ public void accept(ConfigKey configKey, byte[] content, PollingRateHinter pollin
197
195
@ Override
198
196
public void remove (ConfigKey configKey , PollingRateHinter pollingRateHinter )
199
197
throws IOException {
200
- accept (configKey , null , pollingRateHinter );
198
+ try {
199
+ wafBuilder .removeConfig (configKey .toString ());
200
+ } catch (UnclassifiedWafException e ) {
201
+ throw new RuntimeException (e );
202
+ }
201
203
}
202
204
203
205
@ Override
@@ -206,28 +208,48 @@ public void commit(PollingRateHinter pollingRateHinter) {
206
208
}
207
209
}
208
210
209
- private class AppSecConfigChangesDDListener extends AppSecConfigChangesListener {
211
+ private class AppSecConfigChangesDDListener implements ProductListener {
210
212
@ Override
211
213
public void accept (ConfigKey configKey , byte [] content , PollingRateHinter pollingRateHinter )
212
214
throws IOException {
213
- if (defaultConfigActivated ) { // if we get any config, remove the default one
214
- log .debug ("Removing default config" );
215
+ if (content == null ) {
216
+ remove (configKey , pollingRateHinter );
217
+ } else {
218
+ if (usedDDWafConfigKeys .remove (DEFAULT_WAF_CONFIG_RULE )) {
219
+ log .debug ("Removing default config" );
220
+ try {
221
+ wafBuilder .removeConfig (DEFAULT_WAF_CONFIG_RULE );
222
+ } catch (UnclassifiedWafException e ) {
223
+ throw new RuntimeException ("Error removing default WAF config" , e );
224
+ }
225
+ }
226
+ Map <String , Object > contentMap =
227
+ ADAPTER .fromJson (Okio .buffer (Okio .source (new ByteArrayInputStream (content ))));
215
228
try {
216
- wafBuilder .removeConfig (DEFAULT_WAF_CONFIG_RULE );
217
- } catch (UnclassifiedWafException e ) {
229
+ final String key = configKey .toString ();
230
+ handleWafUpdateResultReport (key , contentMap );
231
+ usedDDWafConfigKeys .add (key );
232
+ } catch (AppSecModule .AppSecModuleActivationException e ) {
218
233
throw new RuntimeException (e );
219
234
}
220
- defaultConfigActivated = false ;
221
235
}
222
- super .accept (configKey , content , pollingRateHinter );
223
- usedDDWafConfigKeys .add (configKey .toString ());
224
236
}
225
237
226
238
@ Override
227
239
public void remove (ConfigKey configKey , PollingRateHinter pollingRateHinter )
228
240
throws IOException {
229
- super .remove (configKey , pollingRateHinter );
230
- usedDDWafConfigKeys .remove (configKey .toString ());
241
+ try {
242
+ final String key = configKey .toString ();
243
+ wafBuilder .removeConfig (key );
244
+ usedDDWafConfigKeys .remove (key );
245
+ } catch (UnclassifiedWafException e ) {
246
+ throw new RuntimeException (e );
247
+ }
248
+ }
249
+
250
+ @ Override
251
+ public void commit (PollingRateHinter pollingRateHinter ) {
252
+ // no action needed
231
253
}
232
254
}
233
255
@@ -252,7 +274,8 @@ private void handleWafUpdateResultReport(String configKey, Map<String, Object> r
252
274
if (wafDiagnostics .rulesetVersion != null
253
275
&& !wafDiagnostics .rulesetVersion .isEmpty ()
254
276
&& !wafDiagnostics .rules .getLoaded ().isEmpty ()
255
- && (!defaultConfigActivated || currentRuleVersion == null )) {
277
+ && (!usedDDWafConfigKeys .contains (DEFAULT_CONFIG_LOCATION )
278
+ || currentRuleVersion == null )) {
256
279
currentRuleVersion = wafDiagnostics .rulesetVersion ;
257
280
statsReporter .setRulesVersion (currentRuleVersion );
258
281
if (modulesToUpdateVersionIn != null ) {
@@ -282,13 +305,7 @@ private void subscribeAsmFeatures() {
282
305
Product .ASM_FEATURES ,
283
306
AppSecFeaturesDeserializer .INSTANCE ,
284
307
(configKey , newConfig , hinter ) -> {
285
- if (!hasUserWafConfig && !defaultConfigActivated ) {
286
- // features activated in runtime
287
- init ();
288
- }
289
- if (!initialized ) {
290
- throw new IllegalStateException ();
291
- }
308
+ maybeApplyDefaultConfig ();
292
309
if (newConfig == null ) {
293
310
mergedAsmFeatures .removeConfig (configKey );
294
311
} else {
@@ -305,10 +322,7 @@ private void subscribeAsmFeatures() {
305
322
306
323
private void distributeSubConfigurations (
307
324
String key , AppSecModuleConfigurer .Reconfiguration reconfiguration ) {
308
- if (usedDDWafConfigKeys .isEmpty () && !defaultConfigActivated && !hasUserWafConfig ) {
309
- // no config left in the WAF builder, add the default config
310
- init ();
311
- }
325
+ maybeApplyDefaultConfig ();
312
326
for (Map .Entry <String , SubconfigListener > entry : subconfigListeners .entrySet ()) {
313
327
SubconfigListener listener = entry .getValue ();
314
328
try {
@@ -320,43 +334,56 @@ private void distributeSubConfigurations(
320
334
}
321
335
}
322
336
337
+ private void maybeApplyDefaultConfig () {
338
+ if (!usedDDWafConfigKeys .isEmpty ()) {
339
+ return ;
340
+ }
341
+ // any error here means that we are not able to fetch the default/user config so we print a
342
+ // message and stop receiving RC messages as the installation is broken
343
+ try {
344
+ init ();
345
+ } catch (Exception e ) {
346
+ log .error ("Error applying default AppSec configuration; AppSec won´t work properly" , e );
347
+ close ();
348
+ }
349
+ }
350
+
323
351
@ Override
324
352
public void init () {
325
353
Map <String , Object > wafConfig ;
326
- hasUserWafConfig = false ;
327
354
try {
328
355
wafConfig = loadUserWafConfig (tracerConfig );
356
+ wafConfigOrigin = LOCAL_STABLE_CONFIG ;
329
357
} catch (Exception e ) {
330
358
log .error ("Error loading user-provided config" , e );
331
359
throw new AbortStartupException ("Error loading user-provided config" , e );
332
360
}
333
361
if (wafConfig == null ) {
334
362
try {
335
363
wafConfig = loadDefaultWafConfig ();
336
- defaultConfigActivated = true ;
364
+ wafConfigOrigin = DEFAULT ;
337
365
} catch (IOException e ) {
338
366
log .error ("Error loading default config" , e );
339
367
throw new AbortStartupException ("Error loading default config" , e );
340
368
}
341
- } else {
342
- hasUserWafConfig = true ;
343
369
}
344
- this . mergedAsmFeatures = new MergedAsmFeatures ();
345
- this . initialized = true ;
370
+ mergedAsmFeatures . clear ();
371
+ usedDDWafConfigKeys . clear () ;
346
372
347
373
if (wafConfig .isEmpty ()) {
348
374
throw new IllegalStateException ("Expected default waf config to be available" );
349
375
}
350
376
try {
351
377
handleWafUpdateResultReport (DEFAULT_WAF_CONFIG_RULE , wafConfig );
378
+ usedDDWafConfigKeys .add (DEFAULT_WAF_CONFIG_RULE );
352
379
} catch (AppSecModule .AppSecModuleActivationException e ) {
353
380
throw new RuntimeException (e );
354
381
}
355
382
}
356
383
357
384
public void maybeSubscribeConfigPolling () {
358
385
if (this .configurationPoller != null ) {
359
- if (hasUserWafConfig
386
+ if (wafConfigOrigin == LOCAL_STABLE_CONFIG
360
387
&& tracerConfig .getAppSecActivation () == ProductActivation .FULLY_ENABLED ) {
361
388
log .info (
362
389
"AppSec will not use remote config because "
@@ -437,6 +464,15 @@ private static Map<String, Object> loadDefaultWafConfig() throws IOException {
437
464
}
438
465
}
439
466
467
+ private static boolean hasUserWafConfig (Config tracerConfig ) {
468
+ String filename = tracerConfig .getAppSecRulesFile ();
469
+ if (filename == null ) {
470
+ return false ;
471
+ }
472
+ File file = Paths .get (filename ).toFile ();
473
+ return file .exists () && file .isFile () && file .canRead ();
474
+ }
475
+
440
476
private static Map <String , Object > loadUserWafConfig (Config tracerConfig ) throws IOException {
441
477
log .debug ("Loading user waf config" );
442
478
String filename = tracerConfig .getAppSecRulesFile ();
0 commit comments