VYPR
High severity7.7NVD Advisory· Published Jun 10, 2026

OpenTelemetry Operator for Kubernetes's ServiceMonitor bearerTokenFile reads arbitrary local file and sends contents as bearer auth

CVE-2026-47701

Description

OpenTelemetry Operator's TargetAllocator allows tenants to exfiltrate the Collector's service account token to a controlled endpoint.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

OpenTelemetry Operator's TargetAllocator allows tenants to exfiltrate the Collector's service account token to a controlled endpoint.

Vulnerability

The cmd/otel-allocator component of the OpenTelemetry Operator, specifically the TargetAllocator, improperly handles ServiceMonitor resources. It preserves the bearerTokenFile field, which is then used by the OpenTelemetry Collector to send its mounted service account JWT to a scrape target controlled by a tenant. This vulnerability affects versions of the OpenTelemetry Operator where this functionality is present and not guarded.

Exploitation

An attacker who can create or update a ServiceMonitor resource, and has control over a scrape target, can configure bearerTokenFile to point to the Collector's service account token file (/var/run/secrets/kubernetes.io/serviceaccount/token). This requires the OpenTelemetry Collector to be deployed with targetAllocator.prometheusCR.enabled: true and matching serviceMonitorSelector settings, and the Collector pod must have its service account token mounted and be able to reach the attacker-controlled target.

Impact

Successful exploitation allows a tenant to impersonate the OpenTelemetry Collector's service account when interacting with the Kubernetes API. The extent of the impact depends on the permissions granted to the Collector's service account, but typically includes the ability to enumerate pods, nodes, endpoints, namespaces, and services within the cluster, potentially leading to further compromise.

Mitigation

This issue was addressed by adding a feature to disable the functionality that reads arbitrary files from ServiceMonitor endpoints. Users can enable this feature to prevent the vulnerability. The specific fixed version and release date are not detailed in the provided references, but the change is documented in pull request #5104 [4].

AI Insight generated on Jun 10, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

1
95a8c2a3dc64

feat: add support for arb sm toggle (#5104)

https://github.com/open-telemetry/opentelemetry-operatorJacob AronoffMay 20, 2026via body-scan-shorthand
23 files changed · +658 2
  • apis/v1alpha1/opentelemetrycollector_types.go+10 0 modified
    @@ -411,6 +411,16 @@ type OpenTelemetryTargetAllocatorPrometheusCR struct {
     	// Empty or nil map matches all service monitors.
     	// +optional
     	ServiceMonitorSelector map[string]string `json:"serviceMonitorSelector,omitempty"`
    +	// DenyFSAccessThroughSMs causes the Target Allocator to drop ServiceMonitor and
    +	// PodMonitor endpoints that reference arbitrary files on the file system. When
    +	// enabled, endpoints with bearerTokenFile, tlsConfig.caFile, tlsConfig.certFile,
    +	// or tlsConfig.keyFile are dropped from the produced scrape configuration while
    +	// the remaining endpoints are kept. This prevents tenants from stealing the
    +	// Collector's service account token via ServiceMonitor bearerTokenFile
    +	// references. This is the equivalent of ArbitraryFSAccessThroughSMs.Deny from
    +	// the Prometheus Operator.
    +	// +optional
    +	DenyFSAccessThroughSMs bool `json:"denyFSAccessThroughSMs,omitempty"`
     }
     
     // ScaleSubresourceStatus defines the observed state of the OpenTelemetryCollector's
    
  • apis/v1beta1/targetallocator_types.go+10 0 modified
    @@ -22,6 +22,16 @@ type TargetAllocatorPrometheusCR struct {
     	// If not configured, defaults to the target allocator's own namespace.
     	// +optional
     	SecretNamespaces []string `json:"secretNamespaces,omitempty"`
    +	// DenyFSAccessThroughSMs causes the Target Allocator to drop ServiceMonitor and
    +	// PodMonitor endpoints that reference arbitrary files on the file system. When
    +	// enabled, endpoints with bearerTokenFile, tlsConfig.caFile, tlsConfig.certFile,
    +	// or tlsConfig.keyFile are dropped from the produced scrape configuration while
    +	// the remaining endpoints are kept. This prevents tenants from stealing the
    +	// Collector's service account token via ServiceMonitor bearerTokenFile
    +	// references. This is the equivalent of ArbitraryFSAccessThroughSMs.Deny from
    +	// the Prometheus Operator.
    +	// +optional
    +	DenyFSAccessThroughSMs bool `json:"denyFSAccessThroughSMs,omitempty"`
     	// Default interval between consecutive scrapes. Intervals set in ServiceMonitors and PodMonitors override it.
     	//
     	// Default: "30s"
    
  • bundle/community/manifests/opentelemetry.io_opentelemetrycollectors.yaml+4 0 modified
    @@ -3363,6 +3363,8 @@ spec:
                         type: object
                       prometheusCR:
                         properties:
    +                      denyFSAccessThroughSMs:
    +                        type: boolean
                           enabled:
                             type: boolean
                           podMonitorSelector:
    @@ -8302,6 +8304,8 @@ spec:
                             items:
                               type: string
                             type: array
    +                      denyFSAccessThroughSMs:
    +                        type: boolean
                           denyNamespaces:
                             items:
                               type: string
    
  • bundle/community/manifests/opentelemetry.io_targetallocators.yaml+2 0 modified
    @@ -2480,6 +2480,8 @@ spec:
                         items:
                           type: string
                         type: array
    +                  denyFSAccessThroughSMs:
    +                    type: boolean
                       denyNamespaces:
                         items:
                           type: string
    
  • bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml+1 1 modified
    @@ -99,7 +99,7 @@ metadata:
         categories: Logging & Tracing,Monitoring,Observability
         certified: "false"
         containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator
    -    createdAt: "2026-05-08T09:28:09Z"
    +    createdAt: "2026-05-19T18:40:17Z"
         description: Provides the OpenTelemetry components, including the Collector
         operators.operatorframework.io/builder: operator-sdk-v1.29.0
         operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
    
  • bundle/openshift/manifests/opentelemetry.io_opentelemetrycollectors.yaml+4 0 modified
    @@ -3362,6 +3362,8 @@ spec:
                         type: object
                       prometheusCR:
                         properties:
    +                      denyFSAccessThroughSMs:
    +                        type: boolean
                           enabled:
                             type: boolean
                           podMonitorSelector:
    @@ -8301,6 +8303,8 @@ spec:
                             items:
                               type: string
                             type: array
    +                      denyFSAccessThroughSMs:
    +                        type: boolean
                           denyNamespaces:
                             items:
                               type: string
    
  • bundle/openshift/manifests/opentelemetry.io_targetallocators.yaml+2 0 modified
    @@ -2480,6 +2480,8 @@ spec:
                         items:
                           type: string
                         type: array
    +                  denyFSAccessThroughSMs:
    +                    type: boolean
                       denyNamespaces:
                         items:
                           type: string
    
  • bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml+1 1 modified
    @@ -99,7 +99,7 @@ metadata:
         categories: Logging & Tracing,Monitoring,Observability
         certified: "false"
         containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator
    -    createdAt: "2026-05-08T09:28:09Z"
    +    createdAt: "2026-05-19T18:40:17Z"
         description: Provides the OpenTelemetry components, including the Collector
         operators.operatorframework.io/builder: operator-sdk-v1.29.0
         operators.operatorframework.io/project_layout: go.kubebuilder.io/v3
    
  • .chloggen/add-support-for-arb-sm-toggle.yaml+16 0 added
    @@ -0,0 +1,16 @@
    +# 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. collector, target allocator, auto-instrumentation, opamp, github action)
    +component: target allocator
    +
    +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
    +note: "Add support for dropping ServiceMonitor/PodMonitor endpoints that reference arbitrary files"
    +
    +# One or more tracking issues related to the change
    +issues: [5104]
    +
    +# (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:
    
  • cmd/otel-allocator/internal/config/config.go+10 0 modified
    @@ -92,6 +92,16 @@ type PrometheusCRConfig struct {
     	EvaluationInterval              model.Duration                `yaml:"evaluation_interval,omitempty"`
     	ScrapeProtocols                 []monitoringv1.ScrapeProtocol `yaml:"scrape_protocols,omitempty"`
     	ScrapeClasses                   []monitoringv1.ScrapeClass    `yaml:"scrape_classes,omitempty"`
    +	// DenyFSAccessThroughSMs causes the Target Allocator to drop ServiceMonitor and
    +	// PodMonitor endpoints that reference arbitrary files on the file system. When
    +	// true, endpoints with bearerTokenFile, tlsConfig.caFile, tlsConfig.certFile, or
    +	// tlsConfig.keyFile referencing paths outside an operator-owned mount are
    +	// dropped from the produced scrape configuration while the remaining endpoints
    +	// are kept. This prevents tenants from stealing the Collector's service account
    +	// token. This is the equivalent of ArbitraryFSAccessThroughSMs.Deny from the
    +	// Prometheus Operator.
    +	// +optional
    +	DenyFSAccessThroughSMs bool `yaml:"deny_fs_access_through_sms,omitempty"`
     }
     
     type HTTPSServerConfig struct {
    
  • cmd/otel-allocator/internal/watcher/promOperator_denyfs_test.go+195 0 added
    @@ -0,0 +1,195 @@
    +// Copyright The OpenTelemetry Authors
    +// SPDX-License-Identifier: Apache-2.0
    +
    +package watcher
    +
    +import (
    +	"log/slog"
    +	"testing"
    +	"time"
    +
    +	"github.com/prometheus/common/config"
    +	"github.com/prometheus/common/model"
    +	promconfig "github.com/prometheus/prometheus/config"
    +	"github.com/stretchr/testify/assert"
    +)
    +
    +func newDenyFSWatcher() *PrometheusCRWatcher {
    +	return &PrometheusCRWatcher{
    +		denyFSAccessThroughSMs: true,
    +		logger:                 slog.Default(),
    +	}
    +}
    +
    +// TestFilterScrapeConfigsDropsCredentialsFile verifies that a scrape config
    +// with authorization.credentials_file is dropped.
    +func TestFilterScrapeConfigsDropsCredentialsFile(t *testing.T) {
    +	tw := newDenyFSWatcher()
    +
    +	cfg := &promconfig.Config{
    +		ScrapeConfigs: []*promconfig.ScrapeConfig{
    +			{
    +				JobName: "unsafe",
    +				HTTPClientConfig: config.HTTPClientConfig{
    +					Authorization: &config.Authorization{
    +						Type:            "Bearer",
    +						CredentialsFile: "/var/run/secrets/kubernetes.io/serviceaccount/token",
    +					},
    +				},
    +			},
    +		},
    +	}
    +
    +	tw.filterScrapeConfigs(cfg)
    +	assert.Empty(t, cfg.ScrapeConfigs)
    +}
    +
    +// TestFilterScrapeConfigsDropsTLSFiles verifies the guard drops scrape configs
    +// that set tlsConfig.caFile / certFile / keyFile.
    +func TestFilterScrapeConfigsDropsTLSFiles(t *testing.T) {
    +	for _, tc := range []struct {
    +		name string
    +		tls  config.TLSConfig
    +	}{
    +		{name: "ca_file", tls: config.TLSConfig{CAFile: "/path/to/ca.crt"}},
    +		{name: "cert_file", tls: config.TLSConfig{CertFile: "/path/to/cert.crt"}},
    +		{name: "key_file", tls: config.TLSConfig{KeyFile: "/path/to/key.key"}},
    +	} {
    +		t.Run(tc.name, func(t *testing.T) {
    +			tw := newDenyFSWatcher()
    +
    +			cfg := &promconfig.Config{
    +				ScrapeConfigs: []*promconfig.ScrapeConfig{
    +					{
    +						JobName: "tls-" + tc.name,
    +						HTTPClientConfig: config.HTTPClientConfig{
    +							TLSConfig: tc.tls,
    +						},
    +					},
    +				},
    +			}
    +
    +			tw.filterScrapeConfigs(cfg)
    +			assert.Empty(t, cfg.ScrapeConfigs)
    +		})
    +	}
    +}
    +
    +// TestFilterScrapeConfigsEmpty verifies that an empty scrape config list is a
    +// no-op.
    +func TestFilterScrapeConfigsEmpty(t *testing.T) {
    +	tw := newDenyFSWatcher()
    +
    +	cfg := &promconfig.Config{
    +		ScrapeConfigs: []*promconfig.ScrapeConfig{},
    +	}
    +
    +	tw.filterScrapeConfigs(cfg)
    +	assert.Empty(t, cfg.ScrapeConfigs)
    +}
    +
    +// TestFilterScrapeConfigsKeepsSafeConfigs verifies that scrape configs without
    +// file references are kept.
    +func TestFilterScrapeConfigsKeepsSafeConfigs(t *testing.T) {
    +	tw := newDenyFSWatcher()
    +
    +	cfg := &promconfig.Config{
    +		ScrapeConfigs: []*promconfig.ScrapeConfig{
    +			{
    +				JobName:           "safe",
    +				MetricsPath:       "/metrics",
    +				ScrapeInterval:    model.Duration(30 * time.Second),
    +				EnableCompression: true,
    +				HTTPClientConfig: config.HTTPClientConfig{
    +					BasicAuth: &config.BasicAuth{
    +						Username: "user",
    +						Password: "password",
    +					},
    +				},
    +			},
    +		},
    +	}
    +
    +	tw.filterScrapeConfigs(cfg)
    +	assert.Len(t, cfg.ScrapeConfigs, 1)
    +	assert.Equal(t, "safe", cfg.ScrapeConfigs[0].JobName)
    +}
    +
    +// TestFilterScrapeConfigsKeepsSafeDropsUnsafe verifies that in a mixed list,
    +// only the unsafe scrape configs are dropped.
    +func TestFilterScrapeConfigsKeepsSafeDropsUnsafe(t *testing.T) {
    +	tw := newDenyFSWatcher()
    +
    +	cfg := &promconfig.Config{
    +		ScrapeConfigs: []*promconfig.ScrapeConfig{
    +			{
    +				JobName: "safe",
    +				HTTPClientConfig: config.HTTPClientConfig{
    +					BasicAuth: &config.BasicAuth{
    +						Username: "user",
    +						Password: "pass",
    +					},
    +				},
    +			},
    +			{
    +				JobName: "unsafe-credentials",
    +				HTTPClientConfig: config.HTTPClientConfig{
    +					Authorization: &config.Authorization{
    +						Type:            "Bearer",
    +						CredentialsFile: "/var/run/secrets/kubernetes.io/serviceaccount/token",
    +					},
    +				},
    +			},
    +			{
    +				JobName: "unsafe-tls",
    +				HTTPClientConfig: config.HTTPClientConfig{
    +					TLSConfig: config.TLSConfig{KeyFile: "/path/to/key.key"},
    +				},
    +			},
    +		},
    +	}
    +
    +	tw.filterScrapeConfigs(cfg)
    +	assert.Len(t, cfg.ScrapeConfigs, 1)
    +	assert.Equal(t, "safe", cfg.ScrapeConfigs[0].JobName)
    +}
    +
    +// TestFilterScrapeConfigsNilAuthorization verifies nil Authorization fields do
    +// not cause panics or unintended drops.
    +func TestFilterScrapeConfigsNilAuthorization(t *testing.T) {
    +	tw := newDenyFSWatcher()
    +
    +	cfg := &promconfig.Config{
    +		ScrapeConfigs: []*promconfig.ScrapeConfig{
    +			{
    +				JobName: "no-auth",
    +				HTTPClientConfig: config.HTTPClientConfig{
    +					Authorization: nil,
    +				},
    +			},
    +		},
    +	}
    +
    +	tw.filterScrapeConfigs(cfg)
    +	assert.Len(t, cfg.ScrapeConfigs, 1)
    +}
    +
    +// TestFilterScrapeConfigsEmptyTLSConfig verifies an empty TLSConfig keeps the
    +// scrape config.
    +func TestFilterScrapeConfigsEmptyTLSConfig(t *testing.T) {
    +	tw := newDenyFSWatcher()
    +
    +	cfg := &promconfig.Config{
    +		ScrapeConfigs: []*promconfig.ScrapeConfig{
    +			{
    +				JobName: "empty-tls",
    +				HTTPClientConfig: config.HTTPClientConfig{
    +					TLSConfig: config.TLSConfig{},
    +				},
    +			},
    +		},
    +	}
    +
    +	tw.filterScrapeConfigs(cfg)
    +	assert.Len(t, cfg.ScrapeConfigs, 1)
    +}
    
  • cmd/otel-allocator/internal/watcher/promOperator.go+47 0 modified
    @@ -150,6 +150,7 @@ func NewPrometheusCRWatcher(
     		resourceSelector:                resourceSelector,
     		store:                           store,
     		prometheusCR:                    prom,
    +		denyFSAccessThroughSMs:          cfg.PrometheusCR.DenyFSAccessThroughSMs,
     	}, nil
     }
     
    @@ -170,6 +171,7 @@ type PrometheusCRWatcher struct {
     	resourceSelector                *prometheus.ResourceSelector
     	store                           *assets.StoreBuilder
     	prometheusCR                    *monitoringv1.Prometheus
    +	denyFSAccessThroughSMs          bool
     }
     
     func getNamespaceInformer(ctx context.Context, allowList, denyList map[string]struct{}, promOperatorLogger *slog.Logger, clientset kubernetes.Interface, operatorMetrics *operator.Metrics) (cache.SharedIndexInformer, error) {
    @@ -649,6 +651,13 @@ func (w *PrometheusCRWatcher) LoadConfig(ctx context.Context) (*promconfig.Confi
     			return nil, unmarshalErr
     		}
     
    +		// If denyFSAccessThroughSMs is enabled, drop scrape configs that reference
    +		// arbitrary files on the file system. This prevents tenants from stealing
    +		// the Collector's service account token.
    +		if w.denyFSAccessThroughSMs {
    +			w.filterScrapeConfigs(promCfg)
    +		}
    +
     		// set kubeconfig path to service discovery configs, else kubernetes_sd will always attempt in-cluster
     		// authentication even if running with a detected kubeconfig
     		for _, scrapeConfig := range promCfg.ScrapeConfigs {
    @@ -665,6 +674,44 @@ func (w *PrometheusCRWatcher) LoadConfig(ctx context.Context) (*promconfig.Confi
     	return promCfg, nil
     }
     
    +// filterScrapeConfigs drops scrape configs that reference arbitrary files on
    +// the file system. This prevents tenants from stealing the Collector's service
    +// account token via ServiceMonitor bearerTokenFile (via
    +// authorization.credentials_file) or tlsConfig file references (caFile,
    +// certFile, keyFile). This is the equivalent guard from
    +// ArbitraryFSAccessThroughSMs.Deny in the Prometheus Operator.
    +func (w *PrometheusCRWatcher) filterScrapeConfigs(promCfg *promconfig.Config) {
    +	filtered := promCfg.ScrapeConfigs[:0]
    +	for _, sc := range promCfg.ScrapeConfigs {
    +		if reason := deniedFSAccessReason(sc); reason != "" {
    +			w.logger.Warn("dropping scrape config that references arbitrary file path", "job", sc.JobName, "reason", reason)
    +			continue
    +		}
    +		filtered = append(filtered, sc)
    +	}
    +	promCfg.ScrapeConfigs = filtered
    +}
    +
    +// deniedFSAccessReason returns a non-empty reason if the scrape config
    +// references arbitrary files via authorization or TLS config, or "" if the
    +// config is allowed.
    +func deniedFSAccessReason(sc *promconfig.ScrapeConfig) string {
    +	if auth := sc.HTTPClientConfig.Authorization; auth != nil && auth.CredentialsFile != "" {
    +		return fmt.Sprintf("authorization.credentials_file: %s", auth.CredentialsFile)
    +	}
    +	tls := &sc.HTTPClientConfig.TLSConfig
    +	if tls.CAFile != "" {
    +		return fmt.Sprintf("tls_config.ca_file: %s", tls.CAFile)
    +	}
    +	if tls.CertFile != "" {
    +		return fmt.Sprintf("tls_config.cert_file: %s", tls.CertFile)
    +	}
    +	if tls.KeyFile != "" {
    +		return fmt.Sprintf("tls_config.key_file: %s", tls.KeyFile)
    +	}
    +	return ""
    +}
    +
     // WaitForNamedCacheSync adds a timeout to the informer's wait for the cache to be ready.
     // If the PrometheusCRWatcher is unable to load an informer within 15 seconds, the method is
     // cancelled and returns false. A successful informer load will return true. This method also
    
  • config/crd/bases/opentelemetry.io_opentelemetrycollectors.yaml+4 0 modified
    @@ -3349,6 +3349,8 @@ spec:
                         type: object
                       prometheusCR:
                         properties:
    +                      denyFSAccessThroughSMs:
    +                        type: boolean
                           enabled:
                             type: boolean
                           podMonitorSelector:
    @@ -8288,6 +8290,8 @@ spec:
                             items:
                               type: string
                             type: array
    +                      denyFSAccessThroughSMs:
    +                        type: boolean
                           denyNamespaces:
                             items:
                               type: string
    
  • config/crd/bases/opentelemetry.io_targetallocators.yaml+2 0 modified
    @@ -2478,6 +2478,8 @@ spec:
                         items:
                           type: string
                         type: array
    +                  denyFSAccessThroughSMs:
    +                    type: boolean
                       denyNamespaces:
                         items:
                           type: string
    
  • docs/api/opentelemetrycollectors.md+28 0 modified
    @@ -13979,6 +13979,20 @@ All CR instances which the ServiceAccount has access to will be retrieved. This
             </tr>
         </thead>
         <tbody><tr>
    +        <td><b>denyFSAccessThroughSMs</b></td>
    +        <td>boolean</td>
    +        <td>
    +          DenyFSAccessThroughSMs causes the Target Allocator to drop ServiceMonitor and
    +PodMonitor endpoints that reference arbitrary files on the file system. When
    +enabled, endpoints with bearerTokenFile, tlsConfig.caFile, tlsConfig.certFile,
    +or tlsConfig.keyFile are dropped from the produced scrape configuration while
    +the remaining endpoints are kept. This prevents tenants from stealing the
    +Collector's service account token via ServiceMonitor bearerTokenFile
    +references. This is the equivalent of ArbitraryFSAccessThroughSMs.Deny from
    +the Prometheus Operator.<br/>
    +        </td>
    +        <td>false</td>
    +      </tr><tr>
             <td><b>enabled</b></td>
             <td>boolean</td>
             <td>
    @@ -34859,6 +34873,20 @@ All CR instances which the ServiceAccount has access to will be retrieved. This
               AllowNamespaces Namespaces to scope the interaction of the Target Allocator and the apiserver (allow list). This is mutually exclusive with DenyNamespaces.<br/>
             </td>
             <td>false</td>
    +      </tr><tr>
    +        <td><b>denyFSAccessThroughSMs</b></td>
    +        <td>boolean</td>
    +        <td>
    +          DenyFSAccessThroughSMs causes the Target Allocator to drop ServiceMonitor and
    +PodMonitor endpoints that reference arbitrary files on the file system. When
    +enabled, endpoints with bearerTokenFile, tlsConfig.caFile, tlsConfig.certFile,
    +or tlsConfig.keyFile are dropped from the produced scrape configuration while
    +the remaining endpoints are kept. This prevents tenants from stealing the
    +Collector's service account token via ServiceMonitor bearerTokenFile
    +references. This is the equivalent of ArbitraryFSAccessThroughSMs.Deny from
    +the Prometheus Operator.<br/>
    +        </td>
    +        <td>false</td>
           </tr><tr>
             <td><b>denyNamespaces</b></td>
             <td>[]string</td>
    
  • docs/api/targetallocators.md+14 0 modified
    @@ -10364,6 +10364,20 @@ PrometheusCR defines the configuration for the retrieval of PrometheusOperator C
               AllowNamespaces Namespaces to scope the interaction of the Target Allocator and the apiserver (allow list). This is mutually exclusive with DenyNamespaces.<br/>
             </td>
             <td>false</td>
    +      </tr><tr>
    +        <td><b>denyFSAccessThroughSMs</b></td>
    +        <td>boolean</td>
    +        <td>
    +          DenyFSAccessThroughSMs causes the Target Allocator to drop ServiceMonitor and
    +PodMonitor endpoints that reference arbitrary files on the file system. When
    +enabled, endpoints with bearerTokenFile, tlsConfig.caFile, tlsConfig.certFile,
    +or tlsConfig.keyFile are dropped from the produced scrape configuration while
    +the remaining endpoints are kept. This prevents tenants from stealing the
    +Collector's service account token via ServiceMonitor bearerTokenFile
    +references. This is the equivalent of ArbitraryFSAccessThroughSMs.Deny from
    +the Prometheus Operator.<br/>
    +        </td>
    +        <td>false</td>
           </tr><tr>
             <td><b>denyNamespaces</b></td>
             <td>[]string</td>
    
  • internal/manifests/targetallocator/configmap.go+4 0 modified
    @@ -117,6 +117,10 @@ func ConfigMap(params Params) (*corev1.ConfigMap, error) {
     			prometheusCRConfig["secret_namespaces"] = taSpec.PrometheusCR.SecretNamespaces
     		}
     
    +		if taSpec.PrometheusCR.DenyFSAccessThroughSMs {
    +			prometheusCRConfig["deny_fs_access_through_sms"] = true
    +		}
    +
     		prometheusCRConfig["service_monitor_namespace_selector"] = taSpec.PrometheusCR.ServiceMonitorNamespaceSelector
     		prometheusCRConfig["service_monitor_selector"] = taSpec.PrometheusCR.ServiceMonitorSelector
     
    
  • internal/manifests/targetallocator/configmap_test.go+55 0 modified
    @@ -923,3 +923,58 @@ filter_strategy: relabel-config
     		assert.Equal(t, expectedData[targetAllocatorFilename], actual.Data[targetAllocatorFilename])
     	})
     }
    +
    +func TestDesiredConfigMapWithDenyFSAccessThroughSMs(t *testing.T) {
    +	t.Run("should return expected target allocator config map with denyFSAccessThroughSMs", func(t *testing.T) {
    +		require.NoError(t, colfg.GlobalRegistry().Set("operator.targetallocator.fallbackstrategy", true))
    +		t.Cleanup(func() {
    +			require.NoError(t, colfg.GlobalRegistry().Set("operator.targetallocator.fallbackstrategy", false))
    +		})
    +
    +		expectedData := map[string]string{
    +			targetAllocatorFilename: `allocation_fallback_strategy: consistent-hashing
    +allocation_strategy: consistent-hashing
    +collector_selector:
    +  matchlabels:
    +    app.kubernetes.io/component: opentelemetry-collector
    +    app.kubernetes.io/instance: default.my-instance
    +    app.kubernetes.io/managed-by: opentelemetry-operator
    +    app.kubernetes.io/part-of: opentelemetry
    +  matchexpressions: []
    +config:
    +  scrape_configs:
    +  - job_name: otel-collector
    +    scrape_interval: 10s
    +    static_configs:
    +    - targets:
    +      - 0.0.0.0:8888
    +      - 0.0.0.0:9999
    +filter_strategy: relabel-config
    +prometheus_cr:
    +  deny_fs_access_through_sms: true
    +  enabled: true
    +  pod_monitor_namespace_selector: null
    +  pod_monitor_selector: null
    +  probe_namespace_selector: null
    +  probe_selector: null
    +  scrape_config_namespace_selector: null
    +  scrape_config_selector: null
    +  service_monitor_namespace_selector: null
    +  service_monitor_selector: null
    +`,
    +		}
    +
    +		targetAllocator := targetAllocatorInstance()
    +		targetAllocator.Spec.PrometheusCR.Enabled = true
    +		targetAllocator.Spec.PrometheusCR.DenyFSAccessThroughSMs = true
    +		testParams := Params{
    +			Collector:       collectorInstance(),
    +			TargetAllocator: targetAllocator,
    +		}
    +		actual, err := ConfigMap(testParams)
    +		assert.NoError(t, err)
    +
    +		assert.Equal(t, "my-instance-targetallocator", actual.Name)
    +		assert.Equal(t, expectedData, actual.Data)
    +	})
    +}
    
  • tests/e2e-targetallocator/targetallocator-prometheuscr-denyfs/00-assert.yaml+20 0 added
    @@ -0,0 +1,20 @@
    +apiVersion: apps/v1
    +kind: StatefulSet
    +metadata:
    +  name: prometheus-cr-denyfs-collector
    +status:
    +  readyReplicas: 1
    +  replicas: 1
    +---
    +apiVersion: apps/v1
    +kind: Deployment
    +metadata:
    +  name: prometheus-cr-denyfs-targetallocator
    +status:
    +  readyReplicas: 1
    +  replicas: 1
    +---
    +apiVersion: v1
    +kind: ConfigMap
    +metadata:
    +  name: prometheus-cr-denyfs-targetallocator
    
  • tests/e2e-targetallocator/targetallocator-prometheuscr-denyfs/00-install.yaml+72 0 added
    @@ -0,0 +1,72 @@
    +apiVersion: v1
    +automountServiceAccountToken: true
    +kind: ServiceAccount
    +metadata:
    +  name: collector
    +---
    +apiVersion: rbac.authorization.k8s.io/v1
    +kind: ClusterRole
    +metadata:
    +  name: collector-prometheuscr-denyfs
    +rules:
    +- apiGroups:
    +  - ""
    +  resources:
    +  - pods
    +  - nodes
    +  - nodes/metrics
    +  - services
    +  - endpoints
    +  - namespaces
    +  verbs:
    +  - get
    +  - watch
    +  - list
    +- nonResourceURLs:
    +  - /metrics
    +  - /metrics/cadvisor
    +  verbs:
    +  - get
    +---
    +apiVersion: rbac.authorization.k8s.io/v1
    +kind: ClusterRoleBinding
    +metadata:
    +  name: (join('-', ['collector', $namespace]))
    +roleRef:
    +  apiGroup: rbac.authorization.k8s.io
    +  kind: ClusterRole
    +  name: collector-prometheuscr-denyfs
    +subjects:
    +- kind: ServiceAccount
    +  name: collector
    +  namespace: ($namespace)
    +---
    +apiVersion: opentelemetry.io/v1beta1
    +kind: OpenTelemetryCollector
    +metadata:
    +  name: prometheus-cr-denyfs
    +spec:
    +  config:
    +    receivers:
    +      prometheus:
    +        config:
    +          scrape_configs: []
    +    exporters:
    +      debug:
    +        verbosity: detailed
    +    service:
    +      pipelines:
    +        metrics:
    +          receivers: [prometheus]
    +          exporters: [debug]
    +  mode: statefulset
    +  serviceAccount: collector
    +  targetAllocator:
    +    enabled: true
    +    prometheusCR:
    +      enabled: true
    +      scrapeInterval: 1s
    +      denyFSAccessThroughSMs: true
    +      serviceMonitorSelector: {}
    +      podMonitorSelector: {}
    +    serviceAccount: ta
    
  • tests/e2e-targetallocator/targetallocator-prometheuscr-denyfs/01-assert.yaml+18 0 added
    @@ -0,0 +1,18 @@
    +apiVersion: apps/v1
    +kind: Deployment
    +metadata:
    +  name: metrics-app
    +status:
    +  availableReplicas: 1
    +---
    +apiVersion: monitoring.coreos.com/v1
    +kind: ServiceMonitor
    +metadata:
    +  name: metrics-servicemonitor
    +---
    +apiVersion: batch/v1
    +kind: Job
    +metadata:
    +  name: check-denyfs-filtering
    +status:
    +  succeeded: 1
    
  • tests/e2e-targetallocator/targetallocator-prometheuscr-denyfs/01-install.yaml+103 0 added
    @@ -0,0 +1,103 @@
    +---
    +apiVersion: apps/v1
    +kind: Deployment
    +metadata:
    +  name: metrics-app
    +  labels:
    +    app: metrics-app
    +spec:
    +  replicas: 1
    +  selector:
    +    matchLabels:
    +      app: metrics-app
    +  template:
    +    metadata:
    +      labels:
    +        app: metrics-app
    +    spec:
    +      containers:
    +      - name: metrics-app
    +        image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-metrics-basic-auth:main
    +        ports:
    +        - containerPort: 9123
    +        env:
    +        - name: BASIC_AUTH_USERNAME
    +          value: user
    +        - name: BASIC_AUTH_PASSWORD
    +          value: pass
    +---
    +apiVersion: v1
    +kind: Service
    +metadata:
    +  name: metrics-service
    +  labels:
    +    app: metrics-app
    +spec:
    +  ports:
    +  - name: metrics
    +    port: 9123
    +    targetPort: 9123
    +    protocol: TCP
    +  - name: safe-metrics
    +    port: 9124
    +    targetPort: 9123
    +    protocol: TCP
    +  selector:
    +    app: metrics-app
    +  type: ClusterIP
    +---
    +# A ServiceMonitor with one unsafe endpoint (bearerTokenFile, references the
    +# Collector's mounted service account token) and one safe endpoint (no file
    +# references). With denyFSAccessThroughSMs enabled, only the safe endpoint
    +# should appear in the Target Allocator's scrape configs.
    +apiVersion: monitoring.coreos.com/v1
    +kind: ServiceMonitor
    +metadata:
    +  name: metrics-servicemonitor
    +  labels:
    +    app: metrics-app
    +spec:
    +  selector:
    +    matchLabels:
    +      app: metrics-app
    +  endpoints:
    +  - port: metrics
    +    interval: 10s
    +    bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
    +  - port: safe-metrics
    +    interval: 10s
    +---
    +apiVersion: batch/v1
    +kind: Job
    +metadata:
    +  name: check-denyfs-filtering
    +spec:
    +  backoffLimit: 10
    +  template:
    +    metadata:
    +      labels:
    +        checker: "true"
    +    spec:
    +      restartPolicy: OnFailure
    +      containers:
    +        - name: check-config
    +          image: mirror.gcr.io/curlimages/curl:latest
    +          args:
    +            - /bin/sh
    +            - -c
    +            - |
    +              set -e
    +              CONFIG=$(curl -fs http://prometheus-cr-denyfs-targetallocator/scrape_configs)
    +              echo "$CONFIG"
    +              # The safe endpoint (port safe-metrics) must remain.
    +              echo "$CONFIG" | grep -q "safe-metrics"
    +              # The unsafe endpoint (bearerTokenFile) must be dropped: neither
    +              # the credentials_file path nor the unsafe port should be present.
    +              if echo "$CONFIG" | grep -q "credentials_file"; then
    +                echo "credentials_file should have been dropped"
    +                exit 1
    +              fi
    +              if echo "$CONFIG" | grep -q "/var/run/secrets/kubernetes.io/serviceaccount/token"; then
    +                echo "service account token path should have been dropped"
    +                exit 1
    +              fi
    
  • tests/e2e-targetallocator/targetallocator-prometheuscr-denyfs/chainsaw-test.yaml+36 0 added
    @@ -0,0 +1,36 @@
    +# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json
    +apiVersion: chainsaw.kyverno.io/v1alpha1
    +kind: Test
    +metadata:
    +  creationTimestamp: null
    +  name: targetallocator-prometheuscr-denyfs
    +spec:
    +  steps:
    +  - name: Setup Target Allocator RBAC
    +    use:
    +      template: ../../step-templates/target-allocator-rbac-comprehensive.yaml
    +      with:
    +        bindings:
    +        - name: clusterRoleName
    +          value: targetallocator-prometheuscr-denyfs
    +        - name: clusterRoleBindingName
    +          value: (join('-', ['ta', $namespace]))
    +  - name: step-00-install-collector-with-deny
    +    try:
    +    - apply:
    +        template: true
    +        file: 00-install.yaml
    +    - assert:
    +        file: 00-assert.yaml
    +    catch:
    +    - podLogs:
    +        selector: app.kubernetes.io/managed-by=opentelemetry-operator
    +  - name: step-01-verify-unsafe-dropped-safe-kept
    +    try:
    +    - apply:
    +        file: 01-install.yaml
    +    - assert:
    +        file: 01-assert.yaml
    +    catch:
    +    - podLogs:
    +        selector: app.kubernetes.io/component=opentelemetry-targetallocator
    

Vulnerability mechanics

Synthesis attempt was rejected by the grounding validator. Re-run pending.

References

3

News mentions

0

No linked articles in our index yet.