Skip to content

Commit f658f2e

Browse files
[9.1] (backport #9560) Fix/5871 redact secrets in diagnostics (#9601)
* Fix/5871 redact secrets in diagnostics (#9560) * fix(5871): added secret marker func * fix(5871): added redaction marker test * fix(5871): added secret marker in coordinator * fix(5871): added secret marker func in coordinator unit tests * fix(5871): update redactMap to handle redaction markers * fix(5871): update redactmap tests * fix(5871): update diagnostics integration test * fix(5871): udpate inspect command to inject redaction markers * fix(5871): add changelog fragment (cherry picked from commit a44e87a) # Conflicts: # internal/pkg/agent/application/coordinator/coordinator.go * resolved merge conflicts --------- Co-authored-by: Kaan Yalti <[email protected]>
1 parent 73d470b commit f658f2e

File tree

7 files changed

+622
-364
lines changed

7 files changed

+622
-364
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: security
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: redact secrets from pre-config, computed-config, components-expected, and components-actual files in diagnostics archive
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
#description:
20+
21+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
22+
component: elastic-agent
23+
24+
# PR URL; optional; the PR number that added the changeset.
25+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
26+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
27+
# Please provide it if you are adding a fragment for a different PR.
28+
pr: https://github.com/elastic/elastic-agent/pull/9560
29+
30+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
31+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
32+
#issue: https://github.com/owner/repo/1234

internal/pkg/agent/application/coordinator/coordinator.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ type Coordinator struct {
332332
// run a ticker that checks to see if we have a new PID.
333333
componentPIDTicker *time.Ticker
334334
componentPidRequiresUpdate *atomic.Bool
335+
336+
// Abstraction for diagnostics AddSecretMarkers function for testability
337+
secretMarkerFunc func(*logger.Logger, *config.Config) error
335338
}
336339

337340
// The channels Coordinator reads to receive updates from the various managers.
@@ -452,7 +455,8 @@ func New(
452455
componentPIDTicker: time.NewTicker(time.Second * 30),
453456
componentPidRequiresUpdate: &atomic.Bool{},
454457

455-
fleetAcker: fleetAcker,
458+
fleetAcker: fleetAcker,
459+
secretMarkerFunc: diagnostics.AddSecretMarkers,
456460
}
457461
// Setup communication channels for any non-nil components. This pattern
458462
// lets us transparently accept nil managers / simulated events during
@@ -1371,6 +1375,10 @@ func (c *Coordinator) processConfigAgent(ctx context.Context, cfg *config.Config
13711375
span.End()
13721376
}()
13731377

1378+
if err = c.secretMarkerFunc(c.logger, cfg); err != nil {
1379+
c.logger.Errorf("failed to add secret markers: %v", err)
1380+
}
1381+
13741382
err = c.generateAST(cfg)
13751383
c.setConfigError(err)
13761384
if err != nil {

internal/pkg/agent/application/coordinator/coordinator_unit_test.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,16 @@ import (
4141
"github.com/elastic/elastic-agent/pkg/component"
4242
"github.com/elastic/elastic-agent/pkg/component/runtime"
4343
agentclient "github.com/elastic/elastic-agent/pkg/control/v2/client"
44+
"github.com/elastic/elastic-agent/pkg/core/logger"
4445
"github.com/elastic/elastic-agent/pkg/core/logger/loggertest"
4546
"github.com/elastic/elastic-agent/pkg/utils/broadcaster"
4647
)
4748

49+
var testSecretMarkerFunc = func(*logger.Logger, *config.Config) error {
50+
// no-op secret marker function for testing
51+
return nil
52+
}
53+
4854
func TestVarsManagerError(t *testing.T) {
4955
// Set a one-second timeout -- nothing here should block, but if it
5056
// does let's report a failure instead of timing out the test runner.
@@ -475,6 +481,7 @@ func TestCoordinatorReportsInvalidPolicy(t *testing.T) {
475481
vars: emptyVars(t),
476482
ast: emptyAST(t),
477483
componentPIDTicker: time.NewTicker(time.Second * 30),
484+
secretMarkerFunc: testSecretMarkerFunc,
478485
}
479486

480487
// Send an invalid config update and confirm that Coordinator reports
@@ -591,6 +598,7 @@ func TestCoordinatorReportsComponentModelError(t *testing.T) {
591598
vars: emptyVars(t),
592599
ast: emptyAST(t),
593600
componentPIDTicker: time.NewTicker(time.Second * 30),
601+
secretMarkerFunc: testSecretMarkerFunc,
594602
}
595603

596604
// This configuration produces a valid AST but its EQL condition is
@@ -659,7 +667,7 @@ func TestCoordinatorPolicyChangeUpdatesMonitorReloader(t *testing.T) {
659667
// does let's report a failure instead of timing out the test runner.
660668
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
661669
defer cancel()
662-
logger := logp.NewLogger("testing")
670+
log := logp.NewLogger("testing")
663671

664672
configChan := make(chan ConfigChange, 1)
665673

@@ -674,10 +682,16 @@ func TestCoordinatorPolicyChangeUpdatesMonitorReloader(t *testing.T) {
674682
newServerFn := func(*monitoringCfg.MonitoringConfig) (reload.ServerController, error) {
675683
return monitoringServer, nil
676684
}
677-
monitoringReloader := reload.NewServerReloader(newServerFn, logger, monitoringCfg.DefaultConfig())
685+
monitoringReloader := reload.NewServerReloader(newServerFn, log, monitoringCfg.DefaultConfig())
686+
687+
secretMarkerCalled := false
688+
mockSecretMarkerFunc := func(*logger.Logger, *config.Config) error {
689+
secretMarkerCalled = true
690+
return nil
691+
}
678692

679693
coord := &Coordinator{
680-
logger: logger,
694+
logger: log,
681695
agentInfo: &info.AgentInfo{},
682696
stateBroadcaster: broadcaster.New(State{}, 0, 0),
683697
managerChans: managerChans{
@@ -687,6 +701,7 @@ func TestCoordinatorPolicyChangeUpdatesMonitorReloader(t *testing.T) {
687701
otelMgr: &fakeOTelManager{},
688702
vars: emptyVars(t),
689703
componentPIDTicker: time.NewTicker(time.Second * 30),
704+
secretMarkerFunc: mockSecretMarkerFunc,
690705
}
691706
coord.RegisterMonitoringServer(monitoringReloader)
692707

@@ -707,6 +722,8 @@ inputs:
707722
coord.runLoopIteration(ctx)
708723
assert.True(t, cfgChange.acked, "Coordinator should ACK a successful policy change")
709724

725+
assert.True(t, secretMarkerCalled, "secret marker should be called")
726+
710727
// server is started by default
711728
assert.True(t, monitoringServer.startTriggered)
712729
assert.True(t, monitoringServer.isRunning)
@@ -826,6 +843,7 @@ func TestCoordinatorPolicyChangeUpdatesRuntimeAndOTelManager(t *testing.T) {
826843
otelMgr: otelManager,
827844
vars: emptyVars(t),
828845
componentPIDTicker: time.NewTicker(time.Second * 30),
846+
secretMarkerFunc: testSecretMarkerFunc,
829847
}
830848

831849
// Create a policy with one input and one output (no otel configuration)
@@ -998,6 +1016,7 @@ func TestCoordinatorPolicyChangeUpdatesRuntimeAndOTelManagerWithOtelComponents(t
9981016
specs: specs,
9991017
vars: emptyVars(t),
10001018
componentPIDTicker: time.NewTicker(time.Second * 30),
1019+
secretMarkerFunc: testSecretMarkerFunc,
10011020
}
10021021

10031022
// Create a policy with one input and one output (no otel configuration)
@@ -1096,6 +1115,7 @@ func TestCoordinatorReportsRuntimeManagerUpdateFailure(t *testing.T) {
10961115

10971116
vars: emptyVars(t),
10981117
componentPIDTicker: time.NewTicker(time.Second * 30),
1118+
secretMarkerFunc: testSecretMarkerFunc,
10991119
}
11001120

11011121
// Send an empty policy which should forward an empty component model to
@@ -1157,6 +1177,7 @@ func TestCoordinatorReportsOTelManagerUpdateFailure(t *testing.T) {
11571177
otelMgr: otelManager,
11581178
vars: emptyVars(t),
11591179
componentPIDTicker: time.NewTicker(time.Second * 30),
1180+
secretMarkerFunc: testSecretMarkerFunc,
11601181
}
11611182

11621183
// Send an empty policy which should forward an empty component model to
@@ -1221,6 +1242,7 @@ func TestCoordinatorAppliesVarsToPolicy(t *testing.T) {
12211242
otelMgr: &fakeOTelManager{},
12221243
vars: emptyVars(t),
12231244
componentPIDTicker: time.NewTicker(time.Second * 30),
1245+
secretMarkerFunc: testSecretMarkerFunc,
12241246
}
12251247

12261248
// Create a policy with one input and one output

internal/pkg/agent/cmd/inspect.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ func inspectConfig(ctx context.Context, cfgPath string, opts inspectConfigOpts,
145145
if err != nil {
146146
return fmt.Errorf("error loading agent config: %w", err)
147147
}
148+
// Ensure secret markers are injected based on secret_paths before redaction.
149+
if err := diagnostics.AddSecretMarkers(l, fullCfg); err != nil {
150+
fmt.Fprintf(streams.Err, "failed to add secret markers: %v\n", err)
151+
}
152+
148153
err = printConfig(fullCfg, streams)
149154
if err != nil {
150155
return fmt.Errorf("error printing config: %w", err)
@@ -228,6 +233,16 @@ func inspectConfig(ctx context.Context, cfgPath string, opts inspectConfigOpts,
228233

229234
}
230235

236+
// Ensure secret markers are injected based on secret_paths before redaction.
237+
rawCfg := config.MustNewConfigFrom(cfg)
238+
if err := diagnostics.AddSecretMarkers(l, rawCfg); err != nil {
239+
fmt.Fprintf(streams.Err, "failed to add secret markers: %v\n", err)
240+
}
241+
cfg, err = rawCfg.ToMapStr()
242+
if err != nil {
243+
return fmt.Errorf("failed to convert config with secret markers: %w", err)
244+
}
245+
231246
return printMapStringConfig(cfg, streams)
232247
}
233248

0 commit comments

Comments
 (0)