Skip to content

Commit 7abb9ca

Browse files
committed
Process Invalid API key events from Remote Agents
Add a hook to trigger API key refresh if Remote Agents inform us that they get Invalid API Key errors. When users enable secrets to handle their API keys, Remote Agents need a mechanism to ask Agent to refresh those keys when endpoints return 403s on submissions, because in this case a 403 is not a hard fault, but rather a signal that the key has become stale and we need a new one. Here we add this trigger to the existing RAR event mechanism. Signed-off-by: Mark Kirichenko <mark.kirichenko@datadoghq.com>
1 parent 5eaeaa7 commit 7abb9ca

4 files changed

Lines changed: 59 additions & 1 deletion

File tree

comp/core/remoteagentregistry/impl/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ go_library(
1818
"//comp/core/remoteagentregistry/def",
1919
"//comp/core/remoteagentregistry/status",
2020
"//comp/core/remoteagentregistry/util",
21+
"//comp/core/secrets/def",
2122
"//comp/core/status",
2223
"//comp/core/telemetry/def",
2324
"//comp/def",
@@ -51,6 +52,7 @@ dd_agent_go_test(
5152
"//comp/core/ipc/def",
5253
"//comp/core/ipc/mock",
5354
"//comp/core/remoteagentregistry/def",
55+
"//comp/core/secrets/mock",
5456
"//comp/core/telemetry/def",
5557
"//comp/core/telemetry/impl",
5658
"//comp/def",

comp/core/remoteagentregistry/impl/registry.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
ipc "github.com/DataDog/datadog-agent/comp/core/ipc/def"
2222
remoteagentregistry "github.com/DataDog/datadog-agent/comp/core/remoteagentregistry/def"
2323
remoteagentregistryStatus "github.com/DataDog/datadog-agent/comp/core/remoteagentregistry/status"
24+
secrets "github.com/DataDog/datadog-agent/comp/core/secrets/def"
2425
"github.com/DataDog/datadog-agent/comp/core/status"
2526
telemetry "github.com/DataDog/datadog-agent/comp/core/telemetry/def"
2627
compdef "github.com/DataDog/datadog-agent/comp/def"
@@ -33,6 +34,7 @@ type Requires struct {
3334
Ipc ipc.Component
3435
Lifecycle compdef.Lifecycle
3536
Telemetry telemetry.Component
37+
Secrets secrets.Component
3638
EventSubscribers []*remoteagentregistry.EventSubscriber `group:"remoteAgentEventSubscriber"`
3739
}
3840

@@ -61,6 +63,8 @@ func NewComponent(reqs Requires) Provides {
6163

6264
func newRegistry(reqs Requires) *remoteAgentRegistry {
6365
shutdownChan := make(chan struct{})
66+
eventSubscribers := append([]*remoteagentregistry.EventSubscriber{}, reqs.EventSubscribers...)
67+
eventSubscribers = append(eventSubscribers, newSecretsRefreshEventSubscriber(reqs.Secrets))
6468
registry := &remoteAgentRegistry{
6569
conf: reqs.Config,
6670
ipc: reqs.Ipc,
@@ -74,7 +78,7 @@ func newRegistry(reqs Requires) *remoteAgentRegistry {
7478
FlareServiceName: {},
7579
TelemetryServiceName: {},
7680
},
77-
eventSubscribers: reqs.EventSubscribers,
81+
eventSubscribers: eventSubscribers,
7882
}
7983

8084
reqs.Lifecycle.Append(compdef.Hook{
@@ -91,6 +95,23 @@ func newRegistry(reqs Requires) *remoteAgentRegistry {
9195
return registry
9296
}
9397

98+
// newSecretsRefreshEventSubscriber creates the subscriber that asks the secrets component to refresh when a remote
99+
// agent reports that its API key was rejected. Refresh is asynchronous and applies its own configured throttle.
100+
func newSecretsRefreshEventSubscriber(resolver secrets.Component) *remoteagentregistry.EventSubscriber {
101+
return &remoteagentregistry.EventSubscriber{
102+
Name: "secrets-refresh",
103+
Callback: func(_ remoteagentregistry.RegisteredAgent, events []remoteagentregistry.RemoteAgentEvent) {
104+
for _, event := range events {
105+
if _, ok := event.Details.(*remoteagentregistry.InvalidAPIKey); ok {
106+
// One refresh is sufficient for the whole report; the resolver coalesces and throttles requests.
107+
resolver.Refresh()
108+
return
109+
}
110+
}
111+
},
112+
}
113+
}
114+
94115
type telemetryStore struct {
95116
// remoteAgentRegistered tracks how many remote agents are registered.
96117
remoteAgentRegistered telemetry.Gauge

comp/core/remoteagentregistry/impl/registry_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
ipc "github.com/DataDog/datadog-agent/comp/core/ipc/def"
2929
ipcmock "github.com/DataDog/datadog-agent/comp/core/ipc/mock"
3030
remoteagent "github.com/DataDog/datadog-agent/comp/core/remoteagentregistry/def"
31+
secretsmock "github.com/DataDog/datadog-agent/comp/core/secrets/mock"
3132
"github.com/DataDog/datadog-agent/comp/core/telemetry/def"
3233
"github.com/DataDog/datadog-agent/comp/core/telemetry/impl"
3334
compdef "github.com/DataDog/datadog-agent/comp/def"
@@ -89,6 +90,34 @@ func TestReportRemoteAgentEvent(t *testing.T) {
8990
require.Error(t, component.ReportRemoteAgentEvent("does-not-exist", events))
9091
}
9192

93+
func TestReportRemoteAgentEventRefreshesSecrets(t *testing.T) {
94+
cfg := configmock.New(t)
95+
cfg.SetInTest("remote_agent.registry.enabled", true)
96+
ipcComp := ipcmock.New(t)
97+
resolver := secretsmock.New(t)
98+
refreshes := 0
99+
resolver.SetRefreshHook(func() bool {
100+
refreshes++
101+
return true
102+
})
103+
104+
component := NewComponent(Requires{
105+
Config: cfg,
106+
Ipc: ipcComp,
107+
Lifecycle: compdef.NewTestLifecycle(t),
108+
Telemetry: telemetryimpl.NewMock(t),
109+
Secrets: resolver,
110+
}).Comp.(*remoteAgentRegistry)
111+
remoteAgent := buildAndRegisterRemoteAgent(t, ipcComp, component, "test-agent", "Test Agent", "1234")
112+
113+
require.NoError(t, component.ReportRemoteAgentEvent(remoteAgent.registeredSessionID, []remoteagent.RemoteAgentEvent{
114+
{Message: "ordinary event"},
115+
{Message: "API key rejected", Details: &remoteagent.InvalidAPIKey{}},
116+
{Message: "duplicate API key rejection", Details: &remoteagent.InvalidAPIKey{}},
117+
}))
118+
require.Equal(t, 1, refreshes)
119+
}
120+
92121
func TestReportRemoteAgentEventBroadcast(t *testing.T) {
93122
cfg := configmock.New(t)
94123
cfg.SetInTest("remote_agent.registry.enabled", true)
@@ -124,6 +153,7 @@ func TestReportRemoteAgentEventBroadcast(t *testing.T) {
124153
Ipc: ipcComp,
125154
Lifecycle: lc,
126155
Telemetry: tel,
156+
Secrets: secretsmock.New(t),
127157
EventSubscribers: []*remoteagent.EventSubscriber{panicker, nil, recorder},
128158
}
129159
component := NewComponent(reqs).Comp.(*remoteAgentRegistry)
@@ -262,6 +292,7 @@ func buildComponentWithConfig(t *testing.T, config configmodel.Config) (Provides
262292
Ipc: ipc,
263293
Lifecycle: lc,
264294
Telemetry: telemetry,
295+
Secrets: secretsmock.New(t),
265296
}
266297

267298
return NewComponent(reqs), lc, telemetry, ipc
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
enhancements:
3+
- |
4+
The Agent can now refresh secrets-managed API keys when a Remote Agent reports an Invalid API Kay event.

0 commit comments

Comments
 (0)