Skip to content
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
73 changes: 71 additions & 2 deletions cmd/local-storage-operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ limitations under the License.
package main

import (
"context"
"crypto/tls"
"flag"
"os"
"runtime"
"time"

// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
Expand All @@ -28,13 +31,16 @@ import (
"k8s.io/klog/v2"

configv1 "github.com/openshift/api/config/v1"
crcommon "github.com/openshift/controller-runtime-common/pkg/tls"
libcrypto "github.com/openshift/library-go/pkg/crypto"
localv1 "github.com/openshift/local-storage-operator/api/v1"
localv1alpha1 "github.com/openshift/local-storage-operator/api/v1alpha1"
"github.com/openshift/local-storage-operator/pkg/common"
lvcontroller "github.com/openshift/local-storage-operator/pkg/controllers/localvolume"
lvdcontroller "github.com/openshift/local-storage-operator/pkg/controllers/localvolumediscovery"
lvscontroller "github.com/openshift/local-storage-operator/pkg/controllers/localvolumeset"
nodedaemoncontroller "github.com/openshift/local-storage-operator/pkg/controllers/nodedaemon"
lsotls "github.com/openshift/local-storage-operator/pkg/tls"
"github.com/openshift/local-storage-operator/pkg/utils"
monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
zaplog "go.uber.org/zap"
Expand All @@ -44,6 +50,7 @@ import (
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
Expand Down Expand Up @@ -102,12 +109,62 @@ func main() {
restConfig := ctrl.GetConfigOrDie()
le := utils.GetLeaderElectionConfig(restConfig, enableLeaderElection)

// Fetch TLS profile and adherence once at startup to ensure consistency
// Use a timeout to prevent indefinite stalls on API server outages
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()

configClient, err := client.New(restConfig, client.Options{Scheme: scheme})
if err != nil {
klog.ErrorS(err, "unable to create config client for TLS profile")
os.Exit(1)
}

adherence, err := crcommon.FetchAPIServerTLSAdherencePolicy(ctx, configClient)
if err != nil {
klog.ErrorS(err, "failed to fetch TLS adherence policy")
os.Exit(1)
}
klog.Infof("TLS adherence policy: %s", adherence)

tlsProfile, err := lsotls.FetchAPIServerTLSProfile(ctx, configClient)
if err != nil {
klog.ErrorS(err, "failed to fetch TLS profile")
os.Exit(1)
}

var tlsConfigFn func(*tls.Config)
if libcrypto.ShouldHonorClusterTLSProfile(adherence) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We have the check now, but we miss the defaulting it seems. The requirement is to never use default Go TLS, so there's usually a fallback to this default: https://github.com/openshift/cluster-machine-approver/blob/ed5f7b438136c46207bf443bb89e6610872292ec/pkg/tls/tls.go#L135

If we leave tlsConfigFn nil controller-runtime will use Go TLS defaults - so this should never happen I think.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

(got access to this branch - resolving)

var unsupportedCiphers []string
tlsConfigFn, unsupportedCiphers = lsotls.GetTLSConfigFromProfile(tlsProfile)
if len(unsupportedCiphers) > 0 {
klog.Warningf("TLS profile contains %d unsupported cipher suites: %v",
len(unsupportedCiphers), unsupportedCiphers)
}
klog.Infof("TLS adherence policy %q: using cluster TLS profile", adherence)
} else {
defaultProfile := *configv1.TLSProfiles[libcrypto.DefaultTLSProfileType]
var unsupportedCiphers []string
tlsConfigFn, unsupportedCiphers = lsotls.GetTLSConfigFromProfile(defaultProfile)
if len(unsupportedCiphers) > 0 {
klog.Warningf("Default TLS profile contains %d unsupported cipher suites: %v",
len(unsupportedCiphers), unsupportedCiphers)
}
klog.Infof("TLS adherence policy %q: using default Intermediate profile", adherence)
}

metricsOpts := metricsserver.Options{
BindAddress: metricsAddr,
SecureServing: true,
TLSOpts: []func(*tls.Config){tlsConfigFn},
}

mgr, err := ctrl.NewManager(restConfig, ctrl.Options{
Cache: cache.Options{
DefaultNamespaces: map[string]cache.Config{namespace: {}},
},
Scheme: scheme,
Metrics: metricsserver.Options{BindAddress: metricsAddr},
Metrics: metricsOpts,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
RenewDeadline: &le.RenewDeadline.Duration,
Expand Down Expand Up @@ -151,6 +208,7 @@ func main() {
klog.ErrorS(err, "unable to create NodeDaemon controller")
os.Exit(1)
}

//+kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Expand All @@ -162,8 +220,19 @@ func main() {
os.Exit(1)
}

mgrCtx, cancel := context.WithCancel(ctrl.SetupSignalHandler())
defer cancel()

// Setup TLS security profile watcher to trigger graceful shutdown on changes
tlsWatcher := lsotls.NewSecurityProfileWatcher(tlsProfile, adherence, cancel)
tlsWatcher.Client = mgr.GetClient()
if err = tlsWatcher.SetupWithManager(mgr); err != nil {
klog.ErrorS(err, "unable to create TLS security profile watcher")
os.Exit(1)
}

klog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
if err := mgr.Start(mgrCtx); err != nil {
klog.ErrorS(err, "problem running manager")
os.Exit(1)
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ require (
github.com/aws/aws-sdk-go-v2/credentials v1.19.29
github.com/aws/aws-sdk-go-v2/service/ec2 v1.316.1
github.com/onsi/ginkgo/v2 v2.32.0
github.com/openshift/controller-runtime-common v0.0.0-20260722095319-fea68df23430
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ github.com/openshift/build-machinery-go v0.0.0-20250602125535-1b6d00b8c37c h1:gJ
github.com/openshift/build-machinery-go v0.0.0-20250602125535-1b6d00b8c37c/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE=
github.com/openshift/client-go v0.0.0-20260721124015-35d8f3c0e847 h1:i96ZYwZrCbqxsSrtO/qLJ7Olvu5M9xBU/yVz4Xg8LK4=
github.com/openshift/client-go v0.0.0-20260721124015-35d8f3c0e847/go.mod h1:il93009qOuWG58FeXsVnXD4Yurtm+RMtxIpJv231JdU=
github.com/openshift/controller-runtime-common v0.0.0-20260722095319-fea68df23430 h1:c068rCmMU/tUklmy5v3lH+hIAVIn2N56jDhiY5OX4rY=
github.com/openshift/controller-runtime-common v0.0.0-20260722095319-fea68df23430/go.mod h1:YVDrbC4muEYMejrIZkaQHhlH0bcBusL1UUBkPBxeVrI=
github.com/openshift/library-go v0.0.0-20260721103755-0c9fbc9f043a h1:86Mj1eP0RtYtwPRpdkGbzf6h1Tn+uuYiR6XKZXsWWn8=
github.com/openshift/library-go v0.0.0-20260721103755-0c9fbc9f043a/go.mod h1:iWcB6wgeOhsByZAZGhmzBtEnrLQzABL0s3aeou8AmSI=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
Expand Down
16 changes: 16 additions & 0 deletions pkg/tls/tlsprofile.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package tls

import (
"context"
"crypto/tls"
"fmt"
"strings"

configv1 "github.com/openshift/api/config/v1"
configlistersv1 "github.com/openshift/client-go/config/listers/config/v1"
crcommon "github.com/openshift/controller-runtime-common/pkg/tls"
libapiserver "github.com/openshift/library-go/pkg/operator/configobserver/apiserver"
libevents "github.com/openshift/library-go/pkg/operator/events"
"github.com/openshift/library-go/pkg/operator/resourcesynccontroller"
Expand Down Expand Up @@ -93,3 +95,17 @@ func (a *apiServerListers) ResourceSyncer() resourcesynccontroller.ResourceSynce
func (a *apiServerListers) PreRunHasSynced() []cache.InformerSynced {
return nil
}

// GetTLSConfigFromProfile returns a function that configures a tls.Config based on
// the provided TLS profile spec. The returned function is suitable for use with
// controller-runtime's metricsserver.Options TLSOpts field.
func GetTLSConfigFromProfile(profileSpec configv1.TLSProfileSpec) (func(*tls.Config), []string) {
configFn, unsupportedCiphers := crcommon.NewTLSConfigFromProfile(profileSpec)
return configFn, unsupportedCiphers
}

// FetchAPIServerTLSProfile fetches the TLS profile spec from the cluster APIServer CR.
// This is a convenience wrapper around controller-runtime-common's function.
func FetchAPIServerTLSProfile(ctx context.Context, c client.Client) (configv1.TLSProfileSpec, error) {
return crcommon.FetchAPIServerTLSProfile(ctx, c)
}
50 changes: 50 additions & 0 deletions pkg/tls/watcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Copyright 2021 The Local Storage Operator 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 tls

import (
"context"

configv1 "github.com/openshift/api/config/v1"
crcommon "github.com/openshift/controller-runtime-common/pkg/tls"
"k8s.io/klog/v2"
)

// NewSecurityProfileWatcher creates a SecurityProfileWatcher that triggers graceful
// shutdown when TLS profile or adherence policy changes.
// The initial TLS profile spec and adherence policy are passed in so the watcher
// knows the baseline configuration from operator startup.
func NewSecurityProfileWatcher(
initialProfile configv1.TLSProfileSpec,
initialAdherence configv1.TLSAdherencePolicy,
cancel context.CancelFunc,
) *crcommon.SecurityProfileWatcher {
return &crcommon.SecurityProfileWatcher{
InitialTLSProfileSpec: initialProfile,
InitialTLSAdherencePolicy: initialAdherence,
OnProfileChange: func(ctx context.Context, oldProfile, newProfile configv1.TLSProfileSpec) {
klog.Infof("TLS profile changed, restarting operator to apply new configuration")
klog.V(2).Infof("Old profile: %+v, New profile: %+v", oldProfile, newProfile)
cancel()
},
OnAdherencePolicyChange: func(ctx context.Context, oldPolicy, newPolicy configv1.TLSAdherencePolicy) {
klog.Infof("TLS adherence policy changed from %s to %s, restarting operator",
oldPolicy, newPolicy)
cancel()
},
}
}
Loading