-
Notifications
You must be signed in to change notification settings - Fork 279
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add standalone ASM billing support (#7040)
What Does This Do Add new boolean environment variable DD_EXPERIMENTAL_APPSEC_STANDALONE_ENABLED, when it's enabled: Libraries must add the numeric tag _dd.apm.enabled:0 to the metrics map of the service entry spans. _dd.apm.enabled is assumed to be 1 when absent, so it is backward compatible. Disable the generation of APM trace metrics by disabling the computation of the APM trace metrics and the computation agent-side of the APM trace metrics by pretending it was already done by the library (the trace payload sent to the agent must contain the HTTP header Datadog-Client-Computed-Stats: yes) Introduce a new propagated span tag _dd.p.appsec: 1 providing the knowledge to downstream services that the current distributed trace is containing at least one ASM event and must inherit from the given force-keep priority indeed. Ignore the force-keep priority in the absence of this propagated _dd.p.appsec span tag Use a new TimeSampler to only allow 1 APM trace per minute as standalone ASM is only interested in the traces containing ASM events. But the service catalog and the billing need a continuous ingestion of at least at 1 trace per minute to consider a service as being live and billable. In the absence of ASM events, no APM traces must be sent, so we need to let some regular APM traces go through, even in the absence of ASM events. If ASM standalone billing is enabled and here is no ASM events (No _dd.p.appsec) propagation should be stopped to downstream services Motivation ASM is a natural continuation of APM, leveraging concepts such as traces to build threat monitoring protection capabilities, or on telemetry to build vulnerability management. Though, some customers (primarily infrastructure-monitoring-only customers) only want ASM. We want to make this possible, still internally leveraging APM and provide the same service to ASM customers, while allowing them to not use APM
- Loading branch information
Showing
36 changed files
with
1,179 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
plugins { | ||
id 'java' | ||
id 'org.springframework.boot' version '2.7.15' | ||
id 'io.spring.dependency-management' version '1.0.15.RELEASE' | ||
id 'java-test-fixtures' | ||
} | ||
|
||
apply from: "$rootDir/gradle/java.gradle" | ||
description = 'ASM Standalone Billing Tests.' | ||
|
||
java { | ||
sourceCompatibility = '1.8' | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
implementation 'org.springframework.boot:spring-boot-starter-web' | ||
implementation group: 'io.opentracing', name: 'opentracing-api', version: '0.32.0' | ||
implementation group: 'io.opentracing', name: 'opentracing-util', version: '0.32.0' | ||
implementation project(':dd-trace-api') | ||
testImplementation project(':dd-smoke-tests') | ||
testImplementation(testFixtures(project(":dd-smoke-tests:iast-util"))) | ||
} | ||
|
||
tasks.withType(Test).configureEach { | ||
dependsOn "bootJar" | ||
jvmArgs "-Ddatadog.smoketest.springboot.shadowJar.path=${tasks.bootJar.archiveFile.get()}" | ||
} |
25 changes: 25 additions & 0 deletions
25
...sm-standalone-billing/src/main/java/datadog/smoketest/asmstandalonebilling/AppConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package datadog.smoketest.asmstandalonebilling; | ||
|
||
import java.util.EnumSet; | ||
import javax.servlet.ServletContext; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.SessionTrackingMode; | ||
import org.springframework.boot.web.servlet.ServletContextInitializer; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
public class AppConfig { | ||
@Bean | ||
public ServletContextInitializer servletContextInitializer() { | ||
return new SessionTrackingConfig(); | ||
} | ||
|
||
private class SessionTrackingConfig implements ServletContextInitializer { | ||
@Override | ||
public void onStartup(ServletContext servletContext) throws ServletException { | ||
EnumSet<SessionTrackingMode> sessionTrackingModes = EnumSet.of(SessionTrackingMode.COOKIE); | ||
servletContext.setSessionTrackingModes(sessionTrackingModes); | ||
} | ||
} | ||
} |
84 changes: 84 additions & 0 deletions
84
...m-standalone-billing/src/main/java/datadog/smoketest/asmstandalonebilling/Controller.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package datadog.smoketest.asmstandalonebilling; | ||
|
||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; | ||
import io.opentracing.Span; | ||
import io.opentracing.util.GlobalTracer; | ||
import java.io.IOException; | ||
import java.util.Map; | ||
import javax.servlet.http.HttpServletResponse; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.*; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
@RestController | ||
@RequestMapping("/rest-api") | ||
public class Controller { | ||
|
||
@GetMapping("/greetings") | ||
public String greetings( | ||
@RequestParam(name = "url", required = false) String url, | ||
@RequestParam(name = "forceKeep", required = false) boolean forceKeep) { | ||
if (forceKeep) { | ||
forceKeepSpan(); | ||
} | ||
if (url != null) { | ||
RestTemplate restTemplate = new RestTemplate(); | ||
return restTemplate.getForObject(url, String.class); | ||
} | ||
return "Hello I'm service " + System.getProperty("dd.service.name"); | ||
} | ||
|
||
@GetMapping(value = "/returnheaders", produces = MediaType.APPLICATION_JSON_VALUE) | ||
public ResponseEntity<Map<String, String>> returnheaders( | ||
@RequestHeader Map<String, String> headers) { | ||
return ResponseEntity.ok(headers); | ||
} | ||
|
||
@GetMapping("/appsec/{id}") | ||
public String pathParam( | ||
@PathVariable("id") String id, | ||
@RequestParam(name = "url", required = false) String url, | ||
@RequestParam(name = "forceKeep", required = false) boolean forceKeep) { | ||
if (forceKeep) { | ||
forceKeepSpan(); | ||
} | ||
if (url != null) { | ||
RestTemplate restTemplate = new RestTemplate(); | ||
return restTemplate.getForObject(url, String.class); | ||
} | ||
return id; | ||
} | ||
|
||
@GetMapping("/iast") | ||
@SuppressFBWarnings | ||
public void write( | ||
@RequestParam(name = "injection", required = false) String injection, | ||
@RequestParam(name = "url", required = false) String url, | ||
@RequestParam(name = "forceKeep", required = false) boolean forceKeep, | ||
final HttpServletResponse response) { | ||
if (forceKeep) { | ||
forceKeepSpan(); | ||
} | ||
if (injection != null) { | ||
try { | ||
response.getWriter().write(injection); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
if (url != null) { | ||
RestTemplate restTemplate = new RestTemplate(); | ||
restTemplate.getForObject(url, String.class); | ||
} | ||
} | ||
|
||
private String forceKeepSpan() { | ||
final Span span = GlobalTracer.get().activeSpan(); | ||
if (span != null) { | ||
span.setTag("manual.keep", true); | ||
return span.context().toSpanId(); | ||
} | ||
return null; | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
...e-billing/src/main/java/datadog/smoketest/asmstandalonebilling/SpringbootApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package datadog.smoketest.asmstandalonebilling; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
@SpringBootApplication | ||
public class SpringbootApplication { | ||
public static void main(String[] args) { | ||
SpringApplication.run(SpringbootApplication.class, args); | ||
} | ||
} |
80 changes: 80 additions & 0 deletions
80
...roovy/datadog/smoketest/asmstandalonebilling/AbstractAsmStandaloneBillingSmokeTest.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package datadog.smoketest.asmstandalonebilling | ||
|
||
import datadog.smoketest.AbstractServerSmokeTest | ||
import datadog.trace.api.sampling.PrioritySampling | ||
import datadog.trace.test.agent.decoder.DecodedTrace | ||
|
||
abstract class AbstractAsmStandaloneBillingSmokeTest extends AbstractServerSmokeTest { | ||
|
||
@Override | ||
File createTemporaryFile(int processIndex) { | ||
return null | ||
} | ||
|
||
@Override | ||
String logLevel() { | ||
return 'debug' | ||
} | ||
|
||
@Override | ||
Closure decodedTracesCallback() { | ||
return {} // force traces decoding | ||
} | ||
|
||
protected ProcessBuilder createProcess(String[] properties){ | ||
createProcess(-1, properties) | ||
} | ||
|
||
|
||
protected ProcessBuilder createProcess(int processIndex, String[] properties){ | ||
def port = processIndex == -1 ? httpPort : httpPorts[processIndex] | ||
String springBootShadowJar = System.getProperty("datadog.smoketest.springboot.shadowJar.path") | ||
List<String> command = [] | ||
command.add(javaPath()) | ||
command.addAll(defaultJavaProperties) | ||
command.addAll(properties) | ||
command.addAll((String[]) ['-jar', springBootShadowJar, "--server.port=${port}"]) | ||
ProcessBuilder processBuilder = new ProcessBuilder(command) | ||
processBuilder.directory(new File(buildDirectory)) | ||
// Spring will print all environment variables to the log, which may pollute it and affect log assertions. | ||
processBuilder.environment().clear() | ||
return processBuilder | ||
} | ||
|
||
protected DecodedTrace getServiceTrace(String serviceName) { | ||
return traces.find { trace -> | ||
trace.spans.find { span -> | ||
span.service == serviceName | ||
} | ||
} | ||
} | ||
|
||
protected DecodedTrace getServiceTraceFromUrl(String url) { | ||
return traces.find { trace -> | ||
trace.spans.find { span -> | ||
span.meta["http.url"] == url | ||
} | ||
} | ||
} | ||
|
||
protected checkRootSpanPrioritySampling(DecodedTrace trace, byte priority) { | ||
return trace.spans[0].metrics['_sampling_priority_v1'] == priority | ||
} | ||
|
||
protected isSampledBySampler(DecodedTrace trace) { | ||
def samplingPriority = trace.spans[0].metrics['_sampling_priority_v1'] | ||
return samplingPriority == PrioritySampling.SAMPLER_KEEP || samplingPriority == PrioritySampling.SAMPLER_DROP | ||
} | ||
|
||
protected hasAppsecPropagationTag(DecodedTrace trace) { | ||
return trace.spans[0].meta['_dd.p.appsec'] == "1" | ||
} | ||
|
||
protected hasApmDisabledTag(DecodedTrace trace) { | ||
return trace.spans[0].metrics['_dd.apm.enabled'] == 0 | ||
} | ||
|
||
protected hasASMEvents(DecodedTrace trace){ | ||
return trace.spans[0].meta['_dd.iast.json'] != null || trace.spans[0].meta['_dd.appsec.json'] != null | ||
} | ||
} |
Oops, something went wrong.