Skip to content

Commit 018fb9a

Browse files
committed
Add FIPS disabled components flag
Signed-off-by: Pavol Loffay <[email protected]>
1 parent 94249dc commit 018fb9a

File tree

10 files changed

+221
-6
lines changed

10 files changed

+221
-6
lines changed

.chloggen/fips.yaml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
2+
change_type: enhancement
3+
4+
# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action)
5+
component: collector
6+
7+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
8+
note: Add flag to disable components when operator runs on FIPS enabled cluster.
9+
10+
# One or more tracking issues related to the change
11+
issues: [3315]
12+
13+
# (Optional) One or more lines of additional information to render under the primary note.
14+
# These lines will be padded with 2 spaces and then inserted directly into the document.
15+
# Use pipe (|) for multiline entries.
16+
subtext: |
17+
Flag `--fips-disabled-components=receiver.otlp,exporter.otlp,processor.batch,extension.oidc` can be used to disable
18+
components when operator runs on FIPS enabled cluster. The operator uses `/proc/sys/crypto/fips_enabled` to check
19+
if FIPS is enabled.

apis/v1beta1/collector_webhook.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
2828

2929
"github.com/open-telemetry/opentelemetry-operator/internal/config"
30+
"github.com/open-telemetry/opentelemetry-operator/internal/fips"
3031
ta "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters"
3132
"github.com/open-telemetry/opentelemetry-operator/internal/rbac"
3233
)
@@ -48,6 +49,7 @@ type CollectorWebhook struct {
4849
reviewer *rbac.Reviewer
4950
metrics *Metrics
5051
bv BuildValidator
52+
fips fips.FipsCheck
5153
}
5254

5355
func (c CollectorWebhook) Default(_ context.Context, obj runtime.Object) error {
@@ -290,6 +292,11 @@ func (c CollectorWebhook) Validate(ctx context.Context, r *OpenTelemetryCollecto
290292
return warnings, fmt.Errorf("the OpenTelemetry Collector mode is set to %s, which does not support the attribute 'deploymentUpdateStrategy'", r.Spec.Mode)
291293
}
292294

295+
components := r.Spec.Config.GetEnabledComponents()
296+
if notAllowedComponents := c.fips.Check(components[KindReceiver], components[KindExporter], components[KindProcessor], components[KindExtension]); notAllowedComponents != nil {
297+
return nil, fmt.Errorf("the collector configuration contains not FIPS compliant components: %s. Please remove it from the config", notAllowedComponents)
298+
}
299+
293300
return warnings, nil
294301
}
295302

@@ -423,6 +430,7 @@ func NewCollectorWebhook(
423430
reviewer *rbac.Reviewer,
424431
metrics *Metrics,
425432
bv BuildValidator,
433+
fips fips.FipsCheck,
426434
) *CollectorWebhook {
427435
return &CollectorWebhook{
428436
logger: logger,
@@ -431,11 +439,12 @@ func NewCollectorWebhook(
431439
reviewer: reviewer,
432440
metrics: metrics,
433441
bv: bv,
442+
fips: fips,
434443
}
435444
}
436445

437-
func SetupCollectorWebhook(mgr ctrl.Manager, cfg config.Config, reviewer *rbac.Reviewer, metrics *Metrics, bv BuildValidator) error {
438-
cvw := NewCollectorWebhook(mgr.GetLogger().WithValues("handler", "CollectorWebhook", "version", "v1beta1"), mgr.GetScheme(), cfg, reviewer, metrics, bv)
446+
func SetupCollectorWebhook(mgr ctrl.Manager, cfg config.Config, reviewer *rbac.Reviewer, metrics *Metrics, bv BuildValidator, fipsCheck fips.FipsCheck) error {
447+
cvw := NewCollectorWebhook(mgr.GetLogger().WithValues("handler", "CollectorWebhook", "version", "v1beta1"), mgr.GetScheme(), cfg, reviewer, metrics, bv, fipsCheck)
439448
return ctrl.NewWebhookManagedBy(mgr).
440449
For(&OpenTelemetryCollector{}).
441450
WithValidator(cvw).

apis/v1beta1/collector_webhook_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939

4040
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
4141
"github.com/open-telemetry/opentelemetry-operator/internal/config"
42+
"github.com/open-telemetry/opentelemetry-operator/internal/fips"
4243
"github.com/open-telemetry/opentelemetry-operator/internal/manifests"
4344
collectorManifests "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector"
4445
"github.com/open-telemetry/opentelemetry-operator/internal/rbac"
@@ -113,6 +114,7 @@ func TestValidate(t *testing.T) {
113114
getReviewer(test.shouldFailSar),
114115
nil,
115116
bv,
117+
fips.NewFipsCheck(nil, nil, nil, nil),
116118
)
117119
t.Run(tt.name, func(t *testing.T) {
118120
tt := tt
@@ -494,6 +496,7 @@ func TestCollectorDefaultingWebhook(t *testing.T) {
494496
getReviewer(test.shouldFailSar),
495497
nil,
496498
bv,
499+
fips.NewFipsCheck(nil, nil, nil, nil),
497500
)
498501
ctx := context.Background()
499502
err := cvw.Default(ctx, &test.otelcol)
@@ -1285,6 +1288,7 @@ func TestOTELColValidatingWebhook(t *testing.T) {
12851288
getReviewer(test.shouldFailSar),
12861289
nil,
12871290
bv,
1291+
fips.NewFipsCheck(nil, nil, nil, nil),
12881292
)
12891293
ctx := context.Background()
12901294
warnings, err := cvw.ValidateCreate(ctx, &test.otelcol)
@@ -1352,6 +1356,7 @@ func TestOTELColValidateUpdateWebhook(t *testing.T) {
13521356
getReviewer(test.shouldFailSar),
13531357
nil,
13541358
bv,
1359+
fips.NewFipsCheck(nil, nil, nil, nil),
13551360
)
13561361
ctx := context.Background()
13571362
warnings, err := cvw.ValidateUpdate(ctx, &test.otelcolOld, &test.otelcolNew)

apis/v1beta1/config.go

+4
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ func (c *Config) GetEnabledComponents() map[ComponentKind]map[string]interface{}
112112
KindExporter: {},
113113
KindExtension: {},
114114
}
115+
for _, extension := range c.Service.Extensions {
116+
toReturn[KindExtension][extension] = struct{}{}
117+
}
118+
115119
for _, pipeline := range c.Service.Pipelines {
116120
if pipeline == nil {
117121
continue

controllers/suite_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import (
5959
"github.com/open-telemetry/opentelemetry-operator/internal/autodetect/prometheus"
6060
autoRBAC "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/rbac"
6161
"github.com/open-telemetry/opentelemetry-operator/internal/config"
62+
"github.com/open-telemetry/opentelemetry-operator/internal/fips"
6263
"github.com/open-telemetry/opentelemetry-operator/internal/manifests"
6364
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector/testdata"
6465
"github.com/open-telemetry/opentelemetry-operator/internal/manifests/manifestutils"
@@ -178,7 +179,7 @@ func TestMain(m *testing.M) {
178179
}
179180
reviewer := rbac.NewReviewer(clientset)
180181

181-
if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil); err != nil {
182+
if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil, fips.NewFipsCheck(nil, nil, nil, nil)); err != nil {
182183
fmt.Printf("failed to SetupWebhookWithManager: %v", err)
183184
os.Exit(1)
184185
}

internal/fips/check.go

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fips
16+
17+
import (
18+
"errors"
19+
"fmt"
20+
"os"
21+
"strings"
22+
)
23+
24+
const fipsFile = "/proc/sys/crypto/fips_enabled"
25+
26+
// FipsCheck holds configuration for FIPS black list.
27+
type FipsCheck struct {
28+
isFIPSEnabled bool
29+
30+
receivers map[string]bool
31+
exporters map[string]bool
32+
processors map[string]bool
33+
extensions map[string]bool
34+
}
35+
36+
// NewFipsCheck creates new FipsCheck.
37+
// It checks if FIPS is enabled on the platform in /proc/sys/crypto/fips_enabled.
38+
func NewFipsCheck(receivers, exporters, processors, extensions []string) FipsCheck {
39+
return FipsCheck{
40+
isFIPSEnabled: isFipsEnabled(),
41+
receivers: listToMap(receivers),
42+
exporters: listToMap(exporters),
43+
processors: listToMap(processors),
44+
extensions: listToMap(extensions),
45+
}
46+
}
47+
48+
func listToMap(list []string) map[string]bool {
49+
m := map[string]bool{}
50+
for _, v := range list {
51+
m[v] = true
52+
}
53+
return m
54+
}
55+
56+
// Check checks if a submitted components are back lister or not.
57+
func (fips FipsCheck) Check(receivers map[string]interface{}, exporters map[string]interface{}, processors map[string]interface{}, extensions map[string]interface{}) []string {
58+
if !fips.isFIPSEnabled {
59+
return nil
60+
}
61+
var disabled []string
62+
if comp := isBlackListed(fips.receivers, receivers); comp != "" {
63+
disabled = append(disabled, comp)
64+
}
65+
if comp := isBlackListed(fips.exporters, exporters); comp != "" {
66+
disabled = append(disabled, comp)
67+
}
68+
if comp := isBlackListed(fips.processors, processors); comp != "" {
69+
disabled = append(disabled, comp)
70+
}
71+
if comp := isBlackListed(fips.extensions, extensions); comp != "" {
72+
disabled = append(disabled, comp)
73+
}
74+
return disabled
75+
}
76+
77+
func isBlackListed(blackListed map[string]bool, cfg map[string]interface{}) string {
78+
for id := range cfg {
79+
component := strings.Split(id, "/")[0]
80+
if blackListed[component] {
81+
return component
82+
}
83+
}
84+
return ""
85+
}
86+
87+
func isFipsEnabled() bool {
88+
// check if file exists
89+
if _, err := os.Stat(fipsFile); errors.Is(err, os.ErrNotExist) {
90+
fmt.Println("fips file doesn't exist")
91+
return false
92+
}
93+
content, err := os.ReadFile(fipsFile)
94+
if err != nil {
95+
// file cannot be read, enable FIPS to avoid any violations
96+
fmt.Println("cannot read fips file")
97+
return true
98+
}
99+
contentStr := string(content)
100+
contentStr = strings.TrimSpace(contentStr)
101+
return contentStr == "1"
102+
}

internal/fips/check_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fips
16+
17+
import (
18+
"testing"
19+
20+
"github.com/stretchr/testify/assert"
21+
)
22+
23+
func TestFipsCheck(t *testing.T) {
24+
fipsCheck := NewFipsCheck([]string{"rec1", "rec2"}, []string{"exp1"}, []string{"processor"}, []string{"ext1"})
25+
assert.Equal(t, map[string]bool{"rec1": true, "rec2": true}, fipsCheck.receivers)
26+
assert.Equal(t, map[string]bool{"exp1": true}, fipsCheck.exporters)
27+
assert.Equal(t, map[string]bool{"processor": true}, fipsCheck.processors)
28+
assert.Equal(t, map[string]bool{"ext1": true}, fipsCheck.extensions)
29+
30+
// test machine probably does not have this enabled
31+
fipsCheck.isFIPSEnabled = true
32+
blocked := fipsCheck.Check(
33+
map[string]interface{}{"otlp": true, "rec1/my": true},
34+
map[string]interface{}{"exp1": true},
35+
map[string]interface{}{"processor": true},
36+
map[string]interface{}{"ext1": true})
37+
38+
assert.Equal(t, []string{"rec1", "exp1", "processor", "ext1"}, blocked)
39+
}

internal/webhook/podmutation/webhookhandler_suite_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"crypto/tls"
2020
"fmt"
21+
"github.com/open-telemetry/opentelemetry-operator/internal/fips"
2122
"net"
2223
"os"
2324
"path/filepath"
@@ -105,7 +106,7 @@ func TestMain(m *testing.M) {
105106
}
106107
reviewer := rbac.NewReviewer(clientset)
107108

108-
if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil); err != nil {
109+
if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil, fips.NewFipsCheck(nil, nil, nil, nil)); err != nil {
109110
fmt.Printf("failed to SetupWebhookWithManager: %v", err)
110111
os.Exit(1)
111112
}

main.go

+35-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import (
5353
"github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift"
5454
"github.com/open-telemetry/opentelemetry-operator/internal/autodetect/prometheus"
5555
"github.com/open-telemetry/opentelemetry-operator/internal/config"
56+
"github.com/open-telemetry/opentelemetry-operator/internal/fips"
5657
collectorManifests "github.com/open-telemetry/opentelemetry-operator/internal/manifests/collector"
5758
openshiftDashboards "github.com/open-telemetry/opentelemetry-operator/internal/openshift/dashboards"
5859
"github.com/open-telemetry/opentelemetry-operator/internal/rbac"
@@ -141,6 +142,7 @@ func main() {
141142
encodeLevelKey string
142143
encodeTimeKey string
143144
encodeLevelFormat string
145+
fipsDisabledComponents string
144146
)
145147

146148
pflag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
@@ -180,6 +182,7 @@ func main() {
180182
pflag.StringVar(&encodeLevelKey, "zap-level-key", "level", "The level key to be used in the customized Log Encoder")
181183
pflag.StringVar(&encodeTimeKey, "zap-time-key", "timestamp", "The time key to be used in the customized Log Encoder")
182184
pflag.StringVar(&encodeLevelFormat, "zap-level-format", "uppercase", "The level format to be used in the customized Log Encoder")
185+
pflag.StringVar(&fipsDisabledComponents, "fips-disabled-components", "uppercase", "Disabled collector components when operator runs on FIPS enabled platform. Example flag value =receiver.foo,receiver.bar,exporter.baz")
183186
pflag.IntVar(&webhookPort, "webhook-port", 9443, "The port the webhook endpoint binds to.")
184187
pflag.Parse()
185188

@@ -438,7 +441,10 @@ func main() {
438441
return warnings
439442
}
440443

441-
if err = otelv1beta1.SetupCollectorWebhook(mgr, cfg, reviewer, crdMetrics, bv); err != nil {
444+
receivers, exporters, processors, extensions := parseFipsFlag(fipsDisabledComponents)
445+
fipsCheck := fips.NewFipsCheck(receivers, exporters, processors, extensions)
446+
logger.Info("Fips disabled components", "receivers", receivers, "exporters", exporters, "processors", processors, "extensions", extensions)
447+
if err = otelv1beta1.SetupCollectorWebhook(mgr, cfg, reviewer, crdMetrics, bv, fipsCheck); err != nil {
442448
setupLog.Error(err, "unable to create webhook", "webhook", "OpenTelemetryCollector")
443449
os.Exit(1)
444450
}
@@ -535,3 +541,31 @@ func tlsConfigSetting(cfg *tls.Config, tlsOpt tlsConfig) {
535541
}
536542
cfg.CipherSuites = cipherSuiteIDs
537543
}
544+
545+
func parseFipsFlag(fipsFlag string) ([]string, []string, []string, []string) {
546+
split := strings.Split(fipsFlag, ",")
547+
var receivers []string
548+
var exporters []string
549+
var processors []string
550+
var extensions []string
551+
for _, val := range split {
552+
val = strings.TrimSpace(val)
553+
typeAndName := strings.Split(val, ".")
554+
if len(typeAndName) == 2 {
555+
componentType := typeAndName[0]
556+
name := typeAndName[1]
557+
558+
switch componentType {
559+
case "receiver":
560+
receivers = append(receivers, name)
561+
case "exporter":
562+
exporters = append(exporters, name)
563+
case "processor":
564+
processors = append(processors, name)
565+
case "extension":
566+
extensions = append(extensions, name)
567+
}
568+
}
569+
}
570+
return receivers, exporters, processors, extensions
571+
}

pkg/collector/upgrade/suite_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1"
4242
"github.com/open-telemetry/opentelemetry-operator/apis/v1beta1"
4343
"github.com/open-telemetry/opentelemetry-operator/internal/config"
44+
"github.com/open-telemetry/opentelemetry-operator/internal/fips"
4445
"github.com/open-telemetry/opentelemetry-operator/internal/rbac"
4546
)
4647

@@ -105,7 +106,7 @@ func TestMain(m *testing.M) {
105106
}
106107
reviewer := rbac.NewReviewer(clientset)
107108

108-
if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil); err != nil {
109+
if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil, fips.NewFipsCheck(nil, nil, nil, nil)); err != nil {
109110
fmt.Printf("failed to SetupWebhookWithManager: %v", err)
110111
os.Exit(1)
111112
}

0 commit comments

Comments
 (0)