Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .chloggen/44341.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. receiver/filelog)
component: extension/opamp

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add an opt-in `reports_raw_config` setting to additionally report the raw, unexpanded configuration alongside the effective configuration

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [44341]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext: |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also mention here that configuration fields that are using types meant for opaque information (like sqlreceiver.password and many others) are redacted. Users should consult the documentation/source of their components to verify what's automatically redacted or not.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@douglascamata Added in 8e5a144, the chloggen subtext and README now mention that fields using opaque types (like password fields) are redacted, and that users should check their components' docs/source to see what's automatically redacted.

When `reports_raw_config` is enabled (default: false, and requires the
`reports_effective_config` capability), the extension reports the raw
configuration as authored, before environment variable and other provider
references are expanded, under the `raw` key of the effective config map. The
fully expanded effective configuration is unchanged and remains under the
`""` (empty) key. This is disabled by default because raw configuration files
may contain secrets written directly into them. Values sourced from provider
references such as `${env:TOKEN}` retain their unexpanded form in the raw
configuration, so they are not exposed, and fields using types meant for
opaque information (such as `configopaque.String`, commonly used for password
fields) are redacted; consult your components' documentation or source to
verify which fields are automatically redacted.

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
4 changes: 4 additions & 0 deletions extension/opampextension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ The following settings are optional for both transports:
- `reports_health`: Whether to enable the OpAMP ReportsHealth capability. Default is `true`.
- `reports_available_components`: Whether to enable the OpAMP ReportsAvailableComponents capability. Default is `true`.
- `accepts_restart_command`: Whether to enable the OpAMP AcceptsRestartCommand capability. Default is `false`. The extension sends a `SIGHUP` signal to the collector to initiate a restart (however, SIGHUP isn't supported on windows systems, so it will be ignored). This functionality is also behind a feature gate (alpha) called `extension.opampextension.RemoteRestarts`
- `reports_raw_config`: Whether to additionally report the raw, unexpanded configuration alongside the effective configuration. Default is `false`. Requires the `reports_effective_config` capability to be enabled. When enabled, the raw configuration (as authored, before environment variable and other provider references are expanded) is reported under the `raw` key of the effective config map, while the fully expanded effective configuration remains under the `""` (empty) key. This lets a server distinguish values that were explicitly authored from component defaults populated during expansion.

> [!WARNING]
> The raw configuration may expose secrets that are written directly in the configuration files. Values sourced from provider references (for example `${env:TOKEN}`) retain their unexpanded form in the raw configuration, so they are not exposed. Configuration fields using types meant for opaque information (such as `configopaque.String`, commonly used for password fields) are redacted; consult your components' documentation or source to verify which fields are automatically redacted. This setting is disabled by default for this reason; only enable it if you trust the OpAMP server with the full contents of your configuration files.
- `agent_description`: Setting that modifies the agent description reported to the OpAMP server.
- `include_resource_attributes`: Copy the Collector's resource attributes into the set of non-identifying attributes in the agent description.
- `non_identifying_attributes`: A map of key value pairs that will be added to the [non-identifying attributes](https://github.com/open-telemetry/opamp-spec/blob/main/specification.md#agentdescriptionnon_identifying_attributes) reported to the OpAMP server. If an attribute collides with the default non-identifying attributes that are automatically added, the ones specified here take precedence.
Expand Down
25 changes: 25 additions & 0 deletions extension/opampextension/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,27 @@ type Config struct {
// Capabilities contains options to enable a particular OpAMP capability
Capabilities Capabilities `mapstructure:"capabilities"`

// ReportsRawConfig additionally reports the raw, unexpanded Collector
// configuration (before environment variable and other provider references
// are expanded) alongside the effective configuration. (default: false)
//
// The raw configuration is reported under the "raw" key of the OpAMP
// effective config map; the fully expanded effective configuration is left
// unchanged under the "" (empty) key. Requires the
// reports_effective_config capability.
//
// WARNING: the raw configuration can expose secrets that are written
// directly in the configuration files. Values sourced from provider
// references (for example ${env:TOKEN}) retain their unexpanded form in
// the raw configuration, so they are not exposed. Configuration fields
// using types meant for opaque information (such as configopaque.String,
// commonly used for password fields) are redacted; consult your
// components' documentation or source to verify which fields are
// automatically redacted. Only enable this if you trust the OpAMP server
// with the full contents of your configuration files. It is disabled by
// default for this reason.
ReportsRawConfig bool `mapstructure:"reports_raw_config"`

// Agent descriptions contains options to modify the AgentDescription message
AgentDescription AgentDescription `mapstructure:"agent_description"`

Expand Down Expand Up @@ -228,6 +249,10 @@ func (cfg *Config) Validate() error {
}
}

if cfg.ReportsRawConfig && !cfg.Capabilities.ReportsEffectiveConfig {
return errors.New("reports_raw_config requires reports_effective_config to be enabled")
}

if cfg.InstanceUID != "" {
_, err := parseInstanceIDString(cfg.InstanceUID)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions extension/opampextension/config.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ properties:
description: PPIDPollInterval is the time between polling for whether PPID is running.
type: string
format: duration
reports_raw_config:
description: 'ReportsRawConfig additionally reports the raw, unexpanded Collector configuration (before environment variable and other provider references are expanded) alongside the effective configuration. (default: false) The raw configuration is reported under the "raw" key of the OpAMP effective config map; the fully expanded effective configuration is left unchanged under the "" (empty) key. Requires the reports_effective_config capability. WARNING: the raw configuration can expose secrets that are written directly in the configuration files. Values sourced from provider references (for example ${env:TOKEN}) retain their unexpanded form in the raw configuration, so they are not exposed. Configuration fields using types meant for opaque information (such as configopaque.String, commonly used for password fields) are redacted; consult your components'' documentation or source to verify which fields are automatically redacted. Only enable this if you trust the OpAMP server with the full contents of your configuration files. It is disabled by default for this reason.'
type: boolean
server:
x-pointer: true
$ref: op_amp_server
52 changes: 46 additions & 6 deletions extension/opampextension/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,10 @@ func TestOpAMPServer_GetTLSConfig(t *testing.T) {

func TestConfig_Validate(t *testing.T) {
type fields struct {
Server *OpAMPServer
InstanceUID string
Capabilities Capabilities
Server *OpAMPServer
InstanceUID string
Capabilities Capabilities
ReportsRawConfig bool
}
tests := []struct {
name string
Expand Down Expand Up @@ -358,13 +359,52 @@ func TestConfig_Validate(t *testing.T) {
return assert.Equal(t, "extension.opampextension.RemoteRestarts feature gate must be enabled to use the accepts_restart_command capability", err.Error())
},
},
{
name: "reports_raw_config without reports_effective_config",
fields: fields{
ReportsRawConfig: true,
Capabilities: Capabilities{
ReportsEffectiveConfig: false,
},
Server: &OpAMPServer{
HTTP: &httpFields{
commonFields: commonFields{
Endpoint: "https://127.0.0.1:4320/v1/opamp",
},
},
},
InstanceUID: "01BX5ZZKBKACTAV9WEVGEMMVRZ",
},
wantErr: func(t assert.TestingT, err error, _ ...any) bool {
return assert.Equal(t, "reports_raw_config requires reports_effective_config to be enabled", err.Error())
},
},
{
name: "reports_raw_config with reports_effective_config",
fields: fields{
ReportsRawConfig: true,
Capabilities: Capabilities{
ReportsEffectiveConfig: true,
},
Server: &OpAMPServer{
HTTP: &httpFields{
commonFields: commonFields{
Endpoint: "https://127.0.0.1:4320/v1/opamp",
},
},
},
InstanceUID: "01BX5ZZKBKACTAV9WEVGEMMVRZ",
},
wantErr: assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &Config{
Server: tt.fields.Server,
InstanceUID: tt.fields.InstanceUID,
Capabilities: tt.fields.Capabilities,
Server: tt.fields.Server,
InstanceUID: tt.fields.InstanceUID,
Capabilities: tt.fields.Capabilities,
ReportsRawConfig: tt.fields.ReportsRawConfig,
}
tt.wantErr(t, cfg.Validate())
})
Expand Down
73 changes: 56 additions & 17 deletions extension/opampextension/opamp_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ import (
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/status"
)

// rawConfigMapKey is the key under which the raw (unexpanded) configuration is
// reported in the OpAMP effective config map when reports_raw_config is enabled.
// The fully expanded effective configuration continues to be reported under the
// "" (empty) key for backward compatibility.
const rawConfigMapKey = "raw"

type statusAggregator interface {
Subscribe(scope status.Scope, verbosity status.Verbosity) (<-chan *status.AggregateStatus, status.UnsubscribeFunc)
RecordStatus(source *componentstatus.InstanceID, event *componentstatus.Event)
Expand All @@ -65,6 +71,12 @@ type opampAgent struct {

eclk sync.RWMutex
effectiveConfig *confmap.Conf
// rawConfig holds the unexpanded configuration reported alongside the
// effective config when reportsRawConfig is enabled. It is nil when raw
// config reporting is disabled or unavailable.
rawConfig *confmap.Conf
// reportsRawConfig mirrors Config.ReportsRawConfig.
reportsRawConfig bool

// lifetimeCtx is canceled on Stop of the component
lifetimeCtx context.Context
Expand Down Expand Up @@ -92,7 +104,7 @@ type opampAgent struct {
var (
_ opampcustommessages.CustomCapabilityRegistry = (*opampAgent)(nil)
_ extensioncapabilities.Dependent = (*opampAgent)(nil)
_ extensioncapabilities.ConfigWatcher = (*opampAgent)(nil)
_ extensioncapabilities.ConfigSnapshotWatcher = (*opampAgent)(nil)
_ extensioncapabilities.PipelineWatcher = (*opampAgent)(nil)
_ componentstatus.Watcher = (*opampAgent)(nil)

Expand Down Expand Up @@ -231,12 +243,21 @@ func (o *opampAgent) Dependencies() []component.ID {
return []component.ID{authID}
}

func (o *opampAgent) NotifyConfig(ctx context.Context, conf *confmap.Conf) error {
if o.capabilities.ReportsEffectiveConfig {
o.updateEffectiveConfig(conf)
return o.opampClient.UpdateEffectiveConfig(ctx)
// NotifyConfigSnapshot implements the extensioncapabilities.ConfigSnapshotWatcher
// interface. It records the Collector's effective configuration and, when
// reports_raw_config is enabled, the raw (unexpanded) configuration, then pushes
// the effective config update to the OpAMP server.
func (o *opampAgent) NotifyConfigSnapshot(ctx context.Context, configSnapshot extensioncapabilities.ConfigSnapshot) error {
if !o.capabilities.ReportsEffectiveConfig {
return nil
}
return nil

var rawConfig *confmap.Conf
if o.reportsRawConfig {
rawConfig = configSnapshot.Unexpanded()
}
o.updateEffectiveConfig(configSnapshot.Effective(), rawConfig)
return o.opampClient.UpdateEffectiveConfig(ctx)
}

func (o *opampAgent) Register(capability string, opts ...opampcustommessages.CustomCapabilityRegisterOption) (opampcustommessages.CustomCapabilityHandler, error) {
Expand Down Expand Up @@ -275,11 +296,12 @@ func (o *opampAgent) ComponentStatusChanged(
o.componentStatusCh <- &eventSourcePair{source: source, event: event}
}

func (o *opampAgent) updateEffectiveConfig(conf *confmap.Conf) {
func (o *opampAgent) updateEffectiveConfig(effective, raw *confmap.Conf) {
o.eclk.Lock()
defer o.eclk.Unlock()

o.effectiveConfig = conf
o.effectiveConfig = effective
o.rawConfig = raw
}

func newOpampAgent(cfg *Config, set extension.Settings) (*opampAgent, error) {
Expand Down Expand Up @@ -333,6 +355,7 @@ func newOpampAgent(cfg *Config, set extension.Settings) (*opampAgent, error) {
serviceInstanceID: serviceInstanceID,
instanceUID: uid,
capabilities: cfg.Capabilities,
reportsRawConfig: cfg.ReportsRawConfig,
opampClient: opampClient,
resourceAttrs: resourceAttrs,
statusSubscriptionWg: &sync.WaitGroup{},
Expand Down Expand Up @@ -437,21 +460,37 @@ func (o *opampAgent) composeEffectiveConfig() *protobufs.EffectiveConfig {
return nil
}

m := o.effectiveConfig.ToStringMap()
conf, err := yaml.Marshal(m)
conf, err := yaml.Marshal(o.effectiveConfig.ToStringMap())
if err != nil {
o.logger.Error("cannot unmarshal effectiveConfig", zap.Any("conf", o.effectiveConfig), zap.Error(err))
o.logger.Error("cannot marshal effective config", zap.Any("conf", o.effectiveConfig), zap.Error(err))
return nil
}

configMap := map[string]*protobufs.AgentConfigFile{
"": {
Body: conf,
ContentType: "text/yaml",
},
}

// When enabled, additionally report the raw (unexpanded) config under a
// distinct key. A failure to marshal the raw config must not drop the
// effective config reported under the "" key.
if o.reportsRawConfig && o.rawConfig != nil {
rawConf, err := yaml.Marshal(o.rawConfig.ToStringMap())
if err != nil {
o.logger.Error("cannot marshal raw config", zap.Any("conf", o.rawConfig), zap.Error(err))
} else {
configMap[rawConfigMapKey] = &protobufs.AgentConfigFile{
Body: rawConf,
ContentType: "text/yaml",
}
}
}

return &protobufs.EffectiveConfig{
ConfigMap: &protobufs.AgentConfigMap{
ConfigMap: map[string]*protobufs.AgentConfigFile{
"": {
Body: conf,
ContentType: "text/yaml",
},
},
ConfigMap: configMap,
},
}
}
Expand Down
Loading
Loading