Skip to content

Commit 1edd172

Browse files
authored
feat(internal/appsec): activation metric and config key (#3959)
This PR adds the `DD_APPSEC_ENABLED` configuration that was supposed to already be there but was removed by stableconfig APM work. It also adds the `appsec.enabled` telemetyr metric to also report the value of appsec being enabled or not. This PR ALSO fixes a bug that stableconfig introduced by returning an error even when the env var is not set which made us branch out of appsec.Start too early. And since telemetry logs are dropped backend side we could not have find out earlier. Signed-off-by: Eliott Bouhana <[email protected]>
1 parent 94e5988 commit 1edd172

File tree

4 files changed

+87
-7
lines changed

4 files changed

+87
-7
lines changed

internal/appsec/appsec.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ func Start(opts ...config.StartOption) {
5555
// and enforces to have AppSec disabled.
5656
mode, modeOrigin, err := startConfig.EnablementMode()
5757
if err != nil {
58-
logUnexpectedStartError(err)
59-
return
58+
// Even when DD_APPSEC_ENABLED is an empty string or unset, an error can be returned here
59+
log.Debug("appsec: could not determine the AppSec enablement mode from DD_APPSEC_ENABLED: %s", err.Error())
6060
}
6161

62+
defer registerAppsecStartTelemetry(mode, modeOrigin)
63+
6264
if mode == config.ForcedOff {
6365
log.Debug("appsec: disabled by the configuration: set the environment variable DD_APPSEC_ENABLED to true to enable it")
6466
return
@@ -111,7 +113,6 @@ func Start(opts ...config.StartOption) {
111113
return
112114
}
113115

114-
registerAppsecStartTelemetry(mode, modeOrigin)
115116
setActiveAppSec(appsec)
116117
}
117118

internal/appsec/appsec_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"github.com/DataDog/dd-trace-go/v2/instrumentation/testutils"
1515
"github.com/DataDog/dd-trace-go/v2/internal/appsec"
1616
"github.com/DataDog/dd-trace-go/v2/internal/appsec/config"
17+
"github.com/DataDog/dd-trace-go/v2/internal/telemetry"
18+
"github.com/DataDog/dd-trace-go/v2/internal/telemetry/telemetrytest"
1719
"github.com/DataDog/go-libddwaf/v4"
1820
"github.com/stretchr/testify/assert"
1921
"github.com/stretchr/testify/require"
@@ -39,3 +41,62 @@ func TestStartStop(t *testing.T) {
3941
testutils.StartAppSec(t)
4042
appsec.Stop()
4143
}
44+
45+
func TestAppsecEnabledTelemetry(t *testing.T) {
46+
t.Run("default", func(t *testing.T) {
47+
var telemetryClient telemetrytest.RecordClient
48+
defer telemetry.MockClient(&telemetryClient)()
49+
t.Setenv(config.EnvEnabled, "")
50+
51+
appsec.Start()
52+
defer appsec.Stop()
53+
54+
assert.Contains(t, telemetryClient.Configuration, telemetry.Configuration{Name: config.EnvEnabled, Value: false, Origin: telemetry.OriginDefault})
55+
})
56+
57+
t.Run("env_enabled", func(t *testing.T) {
58+
var telemetryClient telemetrytest.RecordClient
59+
defer telemetry.MockClient(&telemetryClient)()
60+
t.Setenv(config.EnvEnabled, "true")
61+
62+
appsec.Start()
63+
defer appsec.Stop()
64+
65+
shouldBeEnabled, _ := libddwaf.Usable()
66+
assert.Contains(t, telemetryClient.Configuration, telemetry.Configuration{Name: config.EnvEnabled, Value: shouldBeEnabled, Origin: telemetry.OriginEnvVar})
67+
})
68+
69+
t.Run("env_disable", func(t *testing.T) {
70+
var telemetryClient telemetrytest.RecordClient
71+
defer telemetry.MockClient(&telemetryClient)()
72+
t.Setenv(config.EnvEnabled, "false")
73+
74+
appsec.Start()
75+
defer appsec.Stop()
76+
77+
assert.Contains(t, telemetryClient.Configuration, telemetry.Configuration{Name: config.EnvEnabled, Value: false, Origin: telemetry.OriginEnvVar})
78+
})
79+
80+
t.Run("code_enabled", func(t *testing.T) {
81+
var telemetryClient telemetrytest.RecordClient
82+
defer telemetry.MockClient(&telemetryClient)()
83+
t.Setenv(config.EnvEnabled, "")
84+
85+
appsec.Start(config.WithEnablementMode(config.ForcedOn))
86+
defer appsec.Stop()
87+
88+
shouldBeEnabled, _ := libddwaf.Usable()
89+
assert.Contains(t, telemetryClient.Configuration, telemetry.Configuration{Name: config.EnvEnabled, Value: shouldBeEnabled, Origin: telemetry.OriginCode})
90+
})
91+
92+
t.Run("code_enabled", func(t *testing.T) {
93+
var telemetryClient telemetrytest.RecordClient
94+
defer telemetry.MockClient(&telemetryClient)()
95+
t.Setenv(config.EnvEnabled, "")
96+
97+
appsec.Start(config.WithEnablementMode(config.ForcedOff))
98+
defer appsec.Stop()
99+
100+
assert.Contains(t, telemetryClient.Configuration, telemetry.Configuration{Name: config.EnvEnabled, Value: false, Origin: telemetry.OriginCode})
101+
})
102+
}

internal/appsec/remoteconfig.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,14 @@ func (a *appsec) handleASMFeatures(u remoteconfig.ProductUpdate) map[string]stat
250250
log.Error("appsec: remote config: error while processing %q. Configuration won't be applied: %s", path, err.Error())
251251
return map[string]state.ApplyStatus{path: {State: state.ApplyStateError, Error: err.Error()}}
252252
}
253+
registerAppsecStartTelemetry(config.RCStandby, telemetry.OriginRemoteConfig)
253254
}
254255

255256
// RC triggers desactivation of ASM; ASM is started... Stopping it!
256257
if !parsed.ASM.Enabled && a.started {
257258
log.Debug("appsec: remote config: Stopping AppSec")
258259
a.stop()
260+
registerAppsecStartTelemetry(config.ForcedOff, telemetry.OriginRemoteConfig)
259261
return map[string]state.ApplyStatus{path: {State: state.ApplyStateAcknowledged}}
260262
}
261263

internal/appsec/telemetry.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"os/exec"
1414
"runtime"
1515
"sync"
16+
"sync/atomic"
1617

1718
"github.com/DataDog/dd-trace-go/v2/internal/appsec/config"
1819
"github.com/DataDog/dd-trace-go/v2/internal/log"
@@ -33,15 +34,33 @@ var (
3334
{Name: "waf_supports_target", Value: wafSupported, Origin: telemetry.OriginCode},
3435
{Name: "waf_healthy", Value: wafUsable, Origin: telemetry.OriginCode},
3536
}
37+
appsecEnabledOrigin atomic.Pointer[telemetry.Origin]
3638
)
3739

3840
// init sends the static telemetry for AppSec.
3941
func init() {
4042
telemetry.RegisterAppConfigs(staticConfigs...)
43+
telemetry.AddFlushTicker(func(client telemetry.Client) {
44+
var val float64
45+
if Enabled() {
46+
val = 1.0
47+
}
48+
49+
origin := telemetry.OriginDefault
50+
if o := appsecEnabledOrigin.Load(); o != nil {
51+
origin = *o
52+
}
53+
54+
client.Gauge(telemetry.NamespaceAppSec, "enabled", []string{"origin:" + string(origin)}).Submit(val)
55+
})
4156
}
4257

4358
func registerAppsecStartTelemetry(mode config.EnablementMode, origin telemetry.Origin) {
44-
if mode == config.RCStandby {
59+
telemetry.RegisterAppConfig(config.EnvEnabled, Enabled(), origin)
60+
appsecEnabledOrigin.Store(&origin)
61+
detectLibDLOnce.Do(detectLibDL)
62+
63+
if !Enabled() {
4564
return
4665
}
4766

@@ -50,9 +69,6 @@ func registerAppsecStartTelemetry(mode config.EnablementMode, origin telemetry.O
5069
}
5170

5271
telemetry.ProductStarted(telemetry.NamespaceAppSec)
53-
// TODO: add appsec.enabled metric once this metric is enabled backend-side
54-
55-
detectLibDLOnce.Do(detectLibDL)
5672
}
5773

5874
func detectLibDL() {

0 commit comments

Comments
 (0)