Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FIPS flag #3315

Merged
merged 6 commits into from
Oct 2, 2024
Merged
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
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)
}
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
Loading