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
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ rules:
- infrastructures
verbs:
- get
# TLS security profile access
- apiGroups:
- config.openshift.io
resources:
- apiservers
verbs:
- get
- list
- watch
- apiGroups:
- events.k8s.io
resources:
Expand Down
11 changes: 10 additions & 1 deletion bundle-ocp/manifests/das-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ metadata:
features.operators.openshift.io/disconnected: "false"
features.operators.openshift.io/fips-compliant: "false"
features.operators.openshift.io/proxy-aware: "false"
features.operators.openshift.io/tls-profiles: "false"
features.operators.openshift.io/tls-profiles: "true"
features.operators.openshift.io/token-auth-aws: "false"
features.operators.openshift.io/token-auth-azure: "false"
features.operators.openshift.io/token-auth-gcp: "false"
Expand Down Expand Up @@ -248,6 +248,15 @@ spec:
- infrastructures
verbs:
- get
# TLS security profile access
- apiGroups:
- config.openshift.io
resources:
- apiservers
verbs:
- get
- list
- watch
- apiGroups:
- cert-manager.io
resources:
Expand Down
9 changes: 9 additions & 0 deletions deploy/02_operator_rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ rules:
- infrastructures
verbs:
- get
# TLS security profile access
- apiGroups:
- config.openshift.io
resources:
- apiservers
verbs:
- get
- list
- watch
- apiGroups:
- cert-manager.io
resources:
Expand Down
2 changes: 1 addition & 1 deletion deploy/base-csv/das-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ metadata:
features.operators.openshift.io/disconnected: "false"
features.operators.openshift.io/fips-compliant: "false"
features.operators.openshift.io/proxy-aware: "false"
features.operators.openshift.io/tls-profiles: "false"
features.operators.openshift.io/tls-profiles: "true"
features.operators.openshift.io/token-auth-aws: "false"
features.operators.openshift.io/token-auth-azure: "false"
features.operators.openshift.io/token-auth-gcp: "false"
Expand Down
79 changes: 77 additions & 2 deletions pkg/cmd/daemonset/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,25 @@ package daemonset

import (
"context"
"encoding/json"
"os"
"path/filepath"

"github.com/spf13/cobra"
"k8s.io/utils/clock"

configv1 "github.com/openshift/api/config/v1"
operatorv1alpha1 "github.com/openshift/api/operator/v1alpha1"
"github.com/openshift/instaslice-operator/pkg/daemonset"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/openshift/instaslice-operator/pkg/version"

"github.com/openshift/library-go/pkg/controller/controllercmd"
"k8s.io/klog/v2"
"k8s.io/utils/clock"
)

var (
tlsMinVersion string
tlsCipherSuites []string
)

func NewDaemonset(ctx context.Context) *cobra.Command {
Expand All @@ -20,5 +31,69 @@ func NewDaemonset(ctx context.Context) *cobra.Command {
cmd.Use = "daemonset"
cmd.Short = "Start the Cluster Instaslice Operator"

// TLS security profile flags - values injected by operator per OCPSTRAT-2611
cmd.Flags().StringVar(&tlsMinVersion, "tls-min-version", "VersionTLS12",
"Minimum TLS version (e.g., VersionTLS12, VersionTLS13)")
cmd.Flags().StringSliceVar(&tlsCipherSuites, "tls-cipher-suites", nil,
"Comma-separated list of TLS cipher suites (IANA names)")

// Wrap the Run function to inject TLS config
originalRun := cmd.Run
cmd.Run = func(cmd *cobra.Command, args []string) {
// Only inject config if --config flag is not already set
configFlag := cmd.Flag("config")
if configFlag == nil || configFlag.Value.String() == "" {
configFile, err := createTLSConfigFileFromFlags()
if err != nil {
klog.V(4).InfoS("Could not create TLS config file, using defaults", "error", err)
} else if configFile != "" {
if err := cmd.Flags().Set("config", configFile); err != nil {
klog.V(4).InfoS("Could not set config flag", "error", err)
} else {
klog.InfoS("Using cluster TLS profile for metrics server", "configFile", configFile)
}
}
}
originalRun(cmd, args)
}

return cmd
}

// createTLSConfigFileFromFlags creates a config file from CLI flags injected by operator.
// The operator serializes TLS config into --tls-min-version and --tls-cipher-suites
// per OCPSTRAT-2611 (components don't read from the API directly).
func createTLSConfigFileFromFlags() (string, error) {
klog.InfoS("Daemonset metrics server TLS configuration from operator",
"minVersion", tlsMinVersion,
"cipherSuites", tlsCipherSuites)

// Create GenericOperatorConfig with TLS settings from CLI flags
config := &operatorv1alpha1.GenericOperatorConfig{
TypeMeta: metav1.TypeMeta{
APIVersion: "operator.openshift.io/v1alpha1",
Kind: "GenericOperatorConfig",
},
ServingInfo: configv1.HTTPServingInfo{
ServingInfo: configv1.ServingInfo{
MinTLSVersion: tlsMinVersion,
CipherSuites: tlsCipherSuites,
},
},
}

// Write to temp file
tmpDir := os.TempDir()
configFile := filepath.Join(tmpDir, "daemonset-config.json")

data, err := json.Marshal(config)
if err != nil {
return "", err
}

if err := os.WriteFile(configFile, data, 0600); err != nil {
return "", err
}

return configFile, nil
}
84 changes: 79 additions & 5 deletions pkg/cmd/operator/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,95 @@ package operator

import (
"context"
"encoding/json"
"os"
"path/filepath"

"github.com/spf13/cobra"

"k8s.io/utils/clock"

configv1 "github.com/openshift/api/config/v1"
operatorv1alpha1 "github.com/openshift/api/operator/v1alpha1"
"github.com/openshift/instaslice-operator/pkg/operator"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/openshift/instaslice-operator/pkg/tlsconfig"
"github.com/openshift/instaslice-operator/pkg/version"
"github.com/openshift/library-go/pkg/controller/controllercmd"
"k8s.io/client-go/rest"
"k8s.io/klog/v2"
"k8s.io/utils/clock"
)

func NewOperator(ctx context.Context) *cobra.Command {
cmd := controllercmd.
NewControllerCommandConfig("instaslice-operator", version.Get(), operator.RunOperator, clock.RealClock{}).
NewCommandWithContext(ctx)
cfg := controllercmd.NewControllerCommandConfig("instaslice-operator", version.Get(), operator.RunOperator, clock.RealClock{})

cmd := cfg.NewCommandWithContext(ctx)
cmd.Use = "operator"
cmd.Short = "Start the Cluster Instaslice Operator"

// Wrap the Run function to inject TLS config
originalRun := cmd.Run
cmd.Run = func(cmd *cobra.Command, args []string) {
// Only inject config if --config flag is not already set
configFlag := cmd.Flag("config")
if configFlag == nil || configFlag.Value.String() == "" {
configFile, err := createTLSConfigFile(ctx)
if err != nil {
klog.V(4).InfoS("Could not create TLS config file, using defaults", "error", err)
} else if configFile != "" {
if err := cmd.Flags().Set("config", configFile); err != nil {
klog.V(4).InfoS("Could not set config flag", "error", err)
} else {
klog.InfoS("Using cluster TLS profile for metrics server", "configFile", configFile)
}
}
}
originalRun(cmd, args)
}

return cmd
}

// createTLSConfigFile fetches the cluster TLS profile and creates a temp config file
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.

sorta confused why we need a config file? The command line arguments should be passed to the operator.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The library-go controllercmd.ControllerCommandConfig framework reads TLS serving info (ServingInfo with MinTLSVersion and CipherSuites) from a GenericOperatorConfig loaded via the --config flag. There are no direct CLI flags exposed by controllercmd for setting TLS on the operator metrics server. The webhook and daemonset binaries do use direct CLI args (--tls-min-version, --tls-cipher-suites), but for the operator controller the config file is the only supported mechanism.

func createTLSConfigFile(ctx context.Context) (string, error) {
cfg, err := rest.InClusterConfig()
if err != nil {
return "", err
}

clusterProfile := tlsconfig.GetClusterTLSProfile(ctx, cfg)
tlsCfg := tlsconfig.ResolveTLSConfig(clusterProfile)

klog.InfoS("Operator metrics server TLS profile",
"profile", tlsCfg.ProfileDescription(),
"minVersion", tlsCfg.MinTLSVersion,
"cipherSuites", tlsCfg.CipherSuitesIANA)

// Create GenericOperatorConfig with TLS settings
config := &operatorv1alpha1.GenericOperatorConfig{
TypeMeta: metav1.TypeMeta{
APIVersion: "operator.openshift.io/v1alpha1",
Kind: "GenericOperatorConfig",
},
ServingInfo: configv1.HTTPServingInfo{
ServingInfo: configv1.ServingInfo{
MinTLSVersion: tlsCfg.MinTLSVersion,
CipherSuites: tlsCfg.CipherSuitesIANA,
},
},
}

// Write to temp file
tmpDir := os.TempDir()
configFile := filepath.Join(tmpDir, "operator-config.json")

data, err := json.Marshal(config)
if err != nil {
return "", err
}

if err := os.WriteFile(configFile, data, 0600); err != nil {
return "", err
}

return configFile, nil
}
64 changes: 54 additions & 10 deletions pkg/cmd/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
"net/http"
"os"
"strconv"
"strings"

"github.com/openshift/instaslice-operator/pkg/version"
"github.com/openshift/instaslice-operator/pkg/webhook"
"github.com/spf13/cobra"
k8sapiflag "k8s.io/component-base/cli/flag"
"k8s.io/klog/v2"
)

Expand All @@ -21,13 +23,15 @@ const (
)

var (
useTLS bool
tlsCert string
tlsKey string
caCert string
listenAddress string
listenPort int
testHooks bool
useTLS bool
tlsCert string
tlsKey string
caCert string
listenAddress string
listenPort int
testHooks bool
tlsMinVersion string
tlsCipherSuites []string
)

func NewWebhook(ctx context.Context) *cobra.Command {
Expand All @@ -46,6 +50,13 @@ func NewWebhook(ctx context.Context) *cobra.Command {
cmd.Flags().StringVar(&listenAddress, "listen", "0.0.0.0", "Listen address")
cmd.Flags().IntVar(&listenPort, "port", 8443, "Secure port that the webhook listens on")
cmd.Flags().BoolVar(&testHooks, "testHooks", false, "Test webhook URI uniqueness and quit")

// TLS security profile flags - values injected by operator per OCPSTRAT-2611
cmd.Flags().StringVar(&tlsMinVersion, "tls-min-version", "VersionTLS12",
"Minimum TLS version (e.g., VersionTLS12, VersionTLS13)")
cmd.Flags().StringSliceVar(&tlsCipherSuites, "tls-cipher-suites", nil,
"Comma-separated list of TLS cipher suites (IANA names)")

return cmd
}

Expand Down Expand Up @@ -77,9 +88,11 @@ func startServer() {
certpool := x509.NewCertPool()
certpool.AppendCertsFromPEM(cafile)

server.TLSConfig = &tls.Config{
RootCAs: certpool,
}
// Build TLS config from CLI flags (injected by operator per OCPSTRAT-2611)
tlsCfg := buildTLSConfigFromFlags()
tlsCfg.RootCAs = certpool

server.TLSConfig = tlsCfg
err = server.ListenAndServeTLS(tlsCert, tlsKey)
} else {
err = server.ListenAndServe()
Expand All @@ -89,3 +102,34 @@ func startServer() {
os.Exit(1)
}
}

// buildTLSConfigFromFlags builds a tls.Config from CLI flags.
// The operator injects --tls-min-version and --tls-cipher-suites based on
// the cluster's APIServer.spec.tlsSecurityProfile (OCPSTRAT-2611).
func buildTLSConfigFromFlags() *tls.Config {
// Parse min TLS version
minVersionID, err := k8sapiflag.TLSVersion(tlsMinVersion)
if err != nil {
klog.Warningf("Invalid TLS min version %q, using TLS 1.2: %v", tlsMinVersion, err)
minVersionID = tls.VersionTLS12
}

// Parse cipher suites
var cipherSuiteIDs []uint16
if len(tlsCipherSuites) > 0 {
cipherSuiteIDs, err = k8sapiflag.TLSCipherSuites(tlsCipherSuites)
if err != nil {
klog.Warningf("Invalid cipher suites, using Go defaults: %v", err)
cipherSuiteIDs = nil
}
}

klog.InfoS("TLS configuration from operator",
"minVersion", tlsMinVersion,
"cipherSuites", strings.Join(tlsCipherSuites, ","))

return &tls.Config{
MinVersion: minVersionID,
CipherSuites: cipherSuiteIDs,
}
}
Loading