Skip to content

Commit

Permalink
Add FIPS flag (open-telemetry#3315)
Browse files Browse the repository at this point in the history
* Add FIPS disabled components flag

Signed-off-by: Pavol Loffay <p.loffay@gmail.com>

* Fix

Signed-off-by: Pavol Loffay <p.loffay@gmail.com>

* Fix

Signed-off-by: Pavol Loffay <p.loffay@gmail.com>

* Fix

Signed-off-by: Pavol Loffay <p.loffay@gmail.com>

* Fix

Signed-off-by: Pavol Loffay <p.loffay@gmail.com>

* Fix

Signed-off-by: Pavol Loffay <p.loffay@gmail.com>

---------

Signed-off-by: Pavol Loffay <p.loffay@gmail.com>
  • Loading branch information
pavolloffay authored Oct 2, 2024
1 parent 218ff4a commit 05c901f
Show file tree
Hide file tree
Showing 13 changed files with 243 additions and 6 deletions.
19 changes: 19 additions & 0 deletions .chloggen/fips.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# 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: collector

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add flag to disable components when operator runs on FIPS enabled cluster.

# One or more tracking issues related to the change
issues: [3315]

# (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: |
Flag `--fips-disabled-components=receiver.otlp,exporter.otlp,processor.batch,extension.oidc` can be used to disable
components when operator runs on FIPS enabled cluster. The operator uses `/proc/sys/crypto/fips_enabled` to check
if FIPS is enabled.
15 changes: 13 additions & 2 deletions apis/v1beta1/collector_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

"github.com/open-telemetry/opentelemetry-operator/internal/config"
"github.com/open-telemetry/opentelemetry-operator/internal/fips"
ta "github.com/open-telemetry/opentelemetry-operator/internal/manifests/targetallocator/adapters"
"github.com/open-telemetry/opentelemetry-operator/internal/rbac"
)
Expand All @@ -48,6 +49,7 @@ type CollectorWebhook struct {
reviewer *rbac.Reviewer
metrics *Metrics
bv BuildValidator
fips fips.FIPSCheck
}

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

if c.fips != nil {
components := r.Spec.Config.GetEnabledComponents()
if notAllowedComponents := c.fips.DisabledComponents(components[KindReceiver], components[KindExporter], components[KindProcessor], components[KindExtension]); notAllowedComponents != nil {
return nil, fmt.Errorf("the collector configuration contains not FIPS compliant components: %s. Please remove it from the config", notAllowedComponents)
}
}

return warnings, nil
}

Expand Down Expand Up @@ -423,6 +432,7 @@ func NewCollectorWebhook(
reviewer *rbac.Reviewer,
metrics *Metrics,
bv BuildValidator,
fips fips.FIPSCheck,
) *CollectorWebhook {
return &CollectorWebhook{
logger: logger,
Expand All @@ -431,11 +441,12 @@ func NewCollectorWebhook(
reviewer: reviewer,
metrics: metrics,
bv: bv,
fips: fips,
}
}

func SetupCollectorWebhook(mgr ctrl.Manager, cfg config.Config, reviewer *rbac.Reviewer, metrics *Metrics, bv BuildValidator) error {
cvw := NewCollectorWebhook(mgr.GetLogger().WithValues("handler", "CollectorWebhook", "version", "v1beta1"), mgr.GetScheme(), cfg, reviewer, metrics, bv)
func SetupCollectorWebhook(mgr ctrl.Manager, cfg config.Config, reviewer *rbac.Reviewer, metrics *Metrics, bv BuildValidator, fipsCheck fips.FIPSCheck) error {
cvw := NewCollectorWebhook(mgr.GetLogger().WithValues("handler", "CollectorWebhook", "version", "v1beta1"), mgr.GetScheme(), cfg, reviewer, metrics, bv, fipsCheck)
return ctrl.NewWebhookManagedBy(mgr).
For(&OpenTelemetryCollector{}).
WithValidator(cvw).
Expand Down
4 changes: 4 additions & 0 deletions apis/v1beta1/collector_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ func TestValidate(t *testing.T) {
getReviewer(test.shouldFailSar),
nil,
bv,
nil,
)
t.Run(tt.name, func(t *testing.T) {
tt := tt
Expand Down Expand Up @@ -494,6 +495,7 @@ func TestCollectorDefaultingWebhook(t *testing.T) {
getReviewer(test.shouldFailSar),
nil,
bv,
nil,
)
ctx := context.Background()
err := cvw.Default(ctx, &test.otelcol)
Expand Down Expand Up @@ -1285,6 +1287,7 @@ func TestOTELColValidatingWebhook(t *testing.T) {
getReviewer(test.shouldFailSar),
nil,
bv,
nil,
)
ctx := context.Background()
warnings, err := cvw.ValidateCreate(ctx, &test.otelcol)
Expand Down Expand Up @@ -1352,6 +1355,7 @@ func TestOTELColValidateUpdateWebhook(t *testing.T) {
getReviewer(test.shouldFailSar),
nil,
bv,
nil,
)
ctx := context.Background()
warnings, err := cvw.ValidateUpdate(ctx, &test.otelcolOld, &test.otelcolNew)
Expand Down
4 changes: 4 additions & 0 deletions apis/v1beta1/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ func (c *Config) GetEnabledComponents() map[ComponentKind]map[string]interface{}
KindExporter: {},
KindExtension: {},
}
for _, extension := range c.Service.Extensions {
toReturn[KindExtension][extension] = struct{}{}
}

for _, pipeline := range c.Service.Pipelines {
if pipeline == nil {
continue
Expand Down
6 changes: 5 additions & 1 deletion controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ type mockAutoDetect struct {
RBACPermissionsFunc func(ctx context.Context) (autoRBAC.Availability, error)
}

func (m *mockAutoDetect) FIPSEnabled(ctx context.Context) bool {
return false
}

func (m *mockAutoDetect) PrometheusCRsAvailability() (prometheus.Availability, error) {
if m.PrometheusCRsAvailabilityFunc != nil {
return m.PrometheusCRsAvailabilityFunc()
Expand Down Expand Up @@ -178,7 +182,7 @@ func TestMain(m *testing.M) {
}
reviewer := rbac.NewReviewer(clientset)

if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil); err != nil {
if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil, nil); err != nil {
fmt.Printf("failed to SetupWebhookWithManager: %v", err)
os.Exit(1)
}
Expand Down
39 changes: 39 additions & 0 deletions internal/autodetect/fips/fipsautodetect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fips

import (
"errors"
"os"
"strings"
)

const fipsFile = "/proc/sys/crypto/fips_enabled"

// IsFipsEnabled checks whether FIPS is enabled on the platform.
func IsFipsEnabled() bool {
// check if file exists
if _, err := os.Stat(fipsFile); errors.Is(err, os.ErrNotExist) {
return false
}
content, err := os.ReadFile(fipsFile)
if err != nil {
// file cannot be read, enable FIPS to avoid any violations
return true
}
contentStr := string(content)
contentStr = strings.TrimSpace(contentStr)
return contentStr == "1"
}
6 changes: 6 additions & 0 deletions internal/autodetect/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"

"github.com/open-telemetry/opentelemetry-operator/internal/autodetect/fips"
"github.com/open-telemetry/opentelemetry-operator/internal/autodetect/openshift"
"github.com/open-telemetry/opentelemetry-operator/internal/autodetect/prometheus"
autoRBAC "github.com/open-telemetry/opentelemetry-operator/internal/autodetect/rbac"
Expand All @@ -35,6 +36,7 @@ type AutoDetect interface {
OpenShiftRoutesAvailability() (openshift.RoutesAvailability, error)
PrometheusCRsAvailability() (prometheus.Availability, error)
RBACPermissions(ctx context.Context) (autoRBAC.Availability, error)
FIPSEnabled(ctx context.Context) bool
}

type autoDetect struct {
Expand Down Expand Up @@ -122,3 +124,7 @@ func (a *autoDetect) RBACPermissions(ctx context.Context) (autoRBAC.Availability

return autoRBAC.Available, nil
}

func (a *autoDetect) FIPSEnabled(_ context.Context) bool {
return fips.IsFipsEnabled()
}
4 changes: 4 additions & 0 deletions internal/config/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ type mockAutoDetect struct {
RBACPermissionsFunc func(ctx context.Context) (rbac.Availability, error)
}

func (m *mockAutoDetect) FIPSEnabled(_ context.Context) bool {
return false
}

func (m *mockAutoDetect) OpenShiftRoutesAvailability() (openshift.RoutesAvailability, error) {
if m.OpenShiftRoutesAvailabilityFunc != nil {
return m.OpenShiftRoutesAvailabilityFunc()
Expand Down
77 changes: 77 additions & 0 deletions internal/fips/fipscheck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fips

import (
"strings"
)

type FIPSCheck interface {
// DisabledComponents checks if a submitted components are denied or not.
DisabledComponents(receivers map[string]interface{}, exporters map[string]interface{}, processors map[string]interface{}, extensions map[string]interface{}) []string
}

// FipsCheck holds configuration for FIPS deny list.
type fipsCheck struct {
receivers map[string]bool
exporters map[string]bool
processors map[string]bool
extensions map[string]bool
}

// NewFipsCheck creates new FipsCheck.
func NewFipsCheck(receivers, exporters, processors, extensions []string) FIPSCheck {
return &fipsCheck{
receivers: listToMap(receivers),
exporters: listToMap(exporters),
processors: listToMap(processors),
extensions: listToMap(extensions),
}
}

func listToMap(list []string) map[string]bool {
m := map[string]bool{}
for _, v := range list {
m[v] = true
}
return m
}

func (fips fipsCheck) DisabledComponents(receivers map[string]interface{}, exporters map[string]interface{}, processors map[string]interface{}, extensions map[string]interface{}) []string {
var disabled []string
if comp := isDisabled(fips.receivers, receivers); comp != "" {
disabled = append(disabled, comp)
}
if comp := isDisabled(fips.exporters, exporters); comp != "" {
disabled = append(disabled, comp)
}
if comp := isDisabled(fips.processors, processors); comp != "" {
disabled = append(disabled, comp)
}
if comp := isDisabled(fips.extensions, extensions); comp != "" {
disabled = append(disabled, comp)
}
return disabled
}

func isDisabled(denyList map[string]bool, cfg map[string]interface{}) string {
for id := range cfg {
component := strings.Split(id, "/")[0]
if denyList[component] {
return component
}
}
return ""
}
32 changes: 32 additions & 0 deletions internal/fips/fipscheck_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fips

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestFipsCheck(t *testing.T) {
fipsCheck := NewFipsCheck([]string{"rec1", "rec2"}, []string{"exp1"}, []string{"processor"}, []string{"ext1"})
blocked := fipsCheck.DisabledComponents(
map[string]interface{}{"otlp": true, "rec1/my": true},
map[string]interface{}{"exp1": true},
map[string]interface{}{"processor": true},
map[string]interface{}{"ext1": true})

assert.Equal(t, []string{"rec1", "exp1", "processor", "ext1"}, blocked)
}
2 changes: 1 addition & 1 deletion internal/webhook/podmutation/webhookhandler_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func TestMain(m *testing.M) {
}
reviewer := rbac.NewReviewer(clientset)

if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil); err != nil {
if err = v1beta1.SetupCollectorWebhook(mgr, config.New(), reviewer, nil, nil, nil); err != nil {
fmt.Printf("failed to SetupWebhookWithManager: %v", err)
os.Exit(1)
}
Expand Down
Loading

0 comments on commit 05c901f

Please sign in to comment.