Skip to content

Commit 4d6ed93

Browse files
[9.0] (backport #9560) Fix/5871 redact secrets in diagnostics (#9600)
* 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 df4d00d commit 4d6ed93

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
@@ -1288,6 +1292,10 @@ func (c *Coordinator) processConfigAgent(ctx context.Context, cfg *config.Config
12881292
span.End()
12891293
}()
12901294

1295+
if err = c.secretMarkerFunc(c.logger, cfg); err != nil {
1296+
c.logger.Errorf("failed to add secret markers: %v", err)
1297+
}
1298+
12911299
err = c.generateAST(cfg)
12921300
c.setConfigError(err)
12931301
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
@@ -37,10 +37,16 @@ import (
3737
"github.com/elastic/elastic-agent/pkg/component"
3838
"github.com/elastic/elastic-agent/pkg/component/runtime"
3939
agentclient "github.com/elastic/elastic-agent/pkg/control/v2/client"
40+
"github.com/elastic/elastic-agent/pkg/core/logger"
4041
"github.com/elastic/elastic-agent/pkg/core/logger/loggertest"
4142
"github.com/elastic/elastic-agent/pkg/utils/broadcaster"
4243
)
4344

45+
var testSecretMarkerFunc = func(*logger.Logger, *config.Config) error {
46+
// no-op secret marker function for testing
47+
return nil
48+
}
49+
4450
func TestVarsManagerError(t *testing.T) {
4551
// Set a one-second timeout -- nothing here should block, but if it
4652
// does let's report a failure instead of timing out the test runner.
@@ -471,6 +477,7 @@ func TestCoordinatorReportsInvalidPolicy(t *testing.T) {
471477
vars: emptyVars(t),
472478
ast: emptyAST(t),
473479
componentPIDTicker: time.NewTicker(time.Second * 30),
480+
secretMarkerFunc: testSecretMarkerFunc,
474481
}
475482

476483
// Send an invalid config update and confirm that Coordinator reports
@@ -587,6 +594,7 @@ func TestCoordinatorReportsComponentModelError(t *testing.T) {
587594
vars: emptyVars(t),
588595
ast: emptyAST(t),
589596
componentPIDTicker: time.NewTicker(time.Second * 30),
597+
secretMarkerFunc: testSecretMarkerFunc,
590598
}
591599

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

660668
configChan := make(chan ConfigChange, 1)
661669

@@ -670,10 +678,16 @@ func TestCoordinatorPolicyChangeUpdatesMonitorReloader(t *testing.T) {
670678
newServerFn := func(*monitoringCfg.MonitoringConfig) (reload.ServerController, error) {
671679
return monitoringServer, nil
672680
}
673-
monitoringReloader := reload.NewServerReloader(newServerFn, logger, monitoringCfg.DefaultConfig())
681+
monitoringReloader := reload.NewServerReloader(newServerFn, log, monitoringCfg.DefaultConfig())
682+
683+
secretMarkerCalled := false
684+
mockSecretMarkerFunc := func(*logger.Logger, *config.Config) error {
685+
secretMarkerCalled = true
686+
return nil
687+
}
674688

675689
coord := &Coordinator{
676-
logger: logger,
690+
logger: log,
677691
agentInfo: &info.AgentInfo{},
678692
stateBroadcaster: broadcaster.New(State{}, 0, 0),
679693
managerChans: managerChans{
@@ -683,6 +697,7 @@ func TestCoordinatorPolicyChangeUpdatesMonitorReloader(t *testing.T) {
683697
otelMgr: &fakeOTelManager{},
684698
vars: emptyVars(t),
685699
componentPIDTicker: time.NewTicker(time.Second * 30),
700+
secretMarkerFunc: mockSecretMarkerFunc,
686701
}
687702
coord.RegisterMonitoringServer(monitoringReloader)
688703

@@ -703,6 +718,8 @@ inputs:
703718
coord.runLoopIteration(ctx)
704719
assert.True(t, cfgChange.acked, "Coordinator should ACK a successful policy change")
705720

721+
assert.True(t, secretMarkerCalled, "secret marker should be called")
722+
706723
// server is started by default
707724
assert.True(t, monitoringServer.startTriggered)
708725
assert.True(t, monitoringServer.isRunning)
@@ -822,6 +839,7 @@ func TestCoordinatorPolicyChangeUpdatesRuntimeAndOTelManager(t *testing.T) {
822839
otelMgr: otelManager,
823840
vars: emptyVars(t),
824841
componentPIDTicker: time.NewTicker(time.Second * 30),
842+
secretMarkerFunc: testSecretMarkerFunc,
825843
}
826844

827845
// Create a policy with one input and one output (no otel configuration)
@@ -994,6 +1012,7 @@ func TestCoordinatorPolicyChangeUpdatesRuntimeAndOTelManagerWithOtelComponents(t
9941012
specs: specs,
9951013
vars: emptyVars(t),
9961014
componentPIDTicker: time.NewTicker(time.Second * 30),
1015+
secretMarkerFunc: testSecretMarkerFunc,
9971016
}
9981017

9991018
// Create a policy with one input and one output (no otel configuration)
@@ -1092,6 +1111,7 @@ func TestCoordinatorReportsRuntimeManagerUpdateFailure(t *testing.T) {
10921111

10931112
vars: emptyVars(t),
10941113
componentPIDTicker: time.NewTicker(time.Second * 30),
1114+
secretMarkerFunc: testSecretMarkerFunc,
10951115
}
10961116

10971117
// Send an empty policy which should forward an empty component model to
@@ -1153,6 +1173,7 @@ func TestCoordinatorReportsOTelManagerUpdateFailure(t *testing.T) {
11531173
otelMgr: otelManager,
11541174
vars: emptyVars(t),
11551175
componentPIDTicker: time.NewTicker(time.Second * 30),
1176+
secretMarkerFunc: testSecretMarkerFunc,
11561177
}
11571178

11581179
// Send an empty policy which should forward an empty component model to
@@ -1217,6 +1238,7 @@ func TestCoordinatorAppliesVarsToPolicy(t *testing.T) {
12171238
otelMgr: &fakeOTelManager{},
12181239
vars: emptyVars(t),
12191240
componentPIDTicker: time.NewTicker(time.Second * 30),
1241+
secretMarkerFunc: testSecretMarkerFunc,
12201242
}
12211243

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