Skip to content

Commit 76de4d8

Browse files
[8.19] (backport #9560) Fix/5871 redact secrets in diagnostics (#9599)
* 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 7ff188b commit 76de4d8

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
@@ -343,6 +343,9 @@ type Coordinator struct {
343343
// run a ticker that checks to see if we have a new PID.
344344
componentPIDTicker *time.Ticker
345345
componentPidRequiresUpdate *atomic.Bool
346+
347+
// Abstraction for diagnostics AddSecretMarkers function for testability
348+
secretMarkerFunc func(*logger.Logger, *config.Config) error
346349
}
347350

348351
// The channels Coordinator reads to receive updates from the various managers.
@@ -464,7 +467,8 @@ func New(
464467
componentPIDTicker: time.NewTicker(time.Second * 30),
465468
componentPidRequiresUpdate: &atomic.Bool{},
466469

467-
fleetAcker: fleetAcker,
470+
fleetAcker: fleetAcker,
471+
secretMarkerFunc: diagnostics.AddSecretMarkers,
468472
}
469473
// Setup communication channels for any non-nil components. This pattern
470474
// lets us transparently accept nil managers / simulated events during
@@ -1361,6 +1365,10 @@ func (c *Coordinator) processConfigAgent(ctx context.Context, cfg *config.Config
13611365
span.End()
13621366
}()
13631367

1368+
if err = c.secretMarkerFunc(c.logger, cfg); err != nil {
1369+
c.logger.Errorf("failed to add secret markers: %v", err)
1370+
}
1371+
13641372
err = c.generateAST(cfg)
13651373
c.setConfigError(err)
13661374
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
@@ -39,10 +39,16 @@ import (
3939
"github.com/elastic/elastic-agent/pkg/component"
4040
"github.com/elastic/elastic-agent/pkg/component/runtime"
4141
agentclient "github.com/elastic/elastic-agent/pkg/control/v2/client"
42+
"github.com/elastic/elastic-agent/pkg/core/logger"
4243
"github.com/elastic/elastic-agent/pkg/core/logger/loggertest"
4344
"github.com/elastic/elastic-agent/pkg/utils/broadcaster"
4445
)
4546

47+
var testSecretMarkerFunc = func(*logger.Logger, *config.Config) error {
48+
// no-op secret marker function for testing
49+
return nil
50+
}
51+
4652
func TestVarsManagerError(t *testing.T) {
4753
// Set a one-second timeout -- nothing here should block, but if it
4854
// does let's report a failure instead of timing out the test runner.
@@ -473,6 +479,7 @@ func TestCoordinatorReportsInvalidPolicy(t *testing.T) {
473479
vars: emptyVars(t),
474480
ast: emptyAST(t),
475481
componentPIDTicker: time.NewTicker(time.Second * 30),
482+
secretMarkerFunc: testSecretMarkerFunc,
476483
}
477484

478485
// Send an invalid config update and confirm that Coordinator reports
@@ -589,6 +596,7 @@ func TestCoordinatorReportsComponentModelError(t *testing.T) {
589596
vars: emptyVars(t),
590597
ast: emptyAST(t),
591598
componentPIDTicker: time.NewTicker(time.Second * 30),
599+
secretMarkerFunc: testSecretMarkerFunc,
592600
}
593601

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

662670
configChan := make(chan ConfigChange, 1)
663671

@@ -672,10 +680,16 @@ func TestCoordinatorPolicyChangeUpdatesMonitorReloader(t *testing.T) {
672680
newServerFn := func(*monitoringCfg.MonitoringConfig) (reload.ServerController, error) {
673681
return monitoringServer, nil
674682
}
675-
monitoringReloader := reload.NewServerReloader(newServerFn, logger, monitoringCfg.DefaultConfig())
683+
monitoringReloader := reload.NewServerReloader(newServerFn, log, monitoringCfg.DefaultConfig())
684+
685+
secretMarkerCalled := false
686+
mockSecretMarkerFunc := func(*logger.Logger, *config.Config) error {
687+
secretMarkerCalled = true
688+
return nil
689+
}
676690

677691
coord := &Coordinator{
678-
logger: logger,
692+
logger: log,
679693
agentInfo: &info.AgentInfo{},
680694
stateBroadcaster: broadcaster.New(State{}, 0, 0),
681695
managerChans: managerChans{
@@ -685,6 +699,7 @@ func TestCoordinatorPolicyChangeUpdatesMonitorReloader(t *testing.T) {
685699
otelMgr: &fakeOTelManager{},
686700
vars: emptyVars(t),
687701
componentPIDTicker: time.NewTicker(time.Second * 30),
702+
secretMarkerFunc: mockSecretMarkerFunc,
688703
}
689704
coord.RegisterMonitoringServer(monitoringReloader)
690705

@@ -705,6 +720,8 @@ inputs:
705720
coord.runLoopIteration(ctx)
706721
assert.True(t, cfgChange.acked, "Coordinator should ACK a successful policy change")
707722

723+
assert.True(t, secretMarkerCalled, "secret marker should be called")
724+
708725
// server is started by default
709726
assert.True(t, monitoringServer.startTriggered)
710727
assert.True(t, monitoringServer.isRunning)
@@ -824,6 +841,7 @@ func TestCoordinatorPolicyChangeUpdatesRuntimeAndOTelManager(t *testing.T) {
824841
otelMgr: otelManager,
825842
vars: emptyVars(t),
826843
componentPIDTicker: time.NewTicker(time.Second * 30),
844+
secretMarkerFunc: testSecretMarkerFunc,
827845
}
828846

829847
// Create a policy with one input and one output (no otel configuration)
@@ -996,6 +1014,7 @@ func TestCoordinatorPolicyChangeUpdatesRuntimeAndOTelManagerWithOtelComponents(t
9961014
specs: specs,
9971015
vars: emptyVars(t),
9981016
componentPIDTicker: time.NewTicker(time.Second * 30),
1017+
secretMarkerFunc: testSecretMarkerFunc,
9991018
}
10001019

10011020
// Create a policy with one input and one output (no otel configuration)
@@ -1094,6 +1113,7 @@ func TestCoordinatorReportsRuntimeManagerUpdateFailure(t *testing.T) {
10941113

10951114
vars: emptyVars(t),
10961115
componentPIDTicker: time.NewTicker(time.Second * 30),
1116+
secretMarkerFunc: testSecretMarkerFunc,
10971117
}
10981118

10991119
// Send an empty policy which should forward an empty component model to
@@ -1155,6 +1175,7 @@ func TestCoordinatorReportsOTelManagerUpdateFailure(t *testing.T) {
11551175
otelMgr: otelManager,
11561176
vars: emptyVars(t),
11571177
componentPIDTicker: time.NewTicker(time.Second * 30),
1178+
secretMarkerFunc: testSecretMarkerFunc,
11581179
}
11591180

11601181
// Send an empty policy which should forward an empty component model to
@@ -1219,6 +1240,7 @@ func TestCoordinatorAppliesVarsToPolicy(t *testing.T) {
12191240
otelMgr: &fakeOTelManager{},
12201241
vars: emptyVars(t),
12211242
componentPIDTicker: time.NewTicker(time.Second * 30),
1243+
secretMarkerFunc: testSecretMarkerFunc,
12221244
}
12231245

12241246
// 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)