From 5c2374826b279c5b756dbaee38edecbacd00d06d Mon Sep 17 00:00:00 2001 From: Jefferson Ramos Date: Wed, 19 Aug 2026 18:37:44 -0300 Subject: [PATCH] pkg/agenticrun: derive nginx TLS config from cluster APIServer profile Read the cluster's APIServer TLS security profile and render the nginx ssl_protocols and ssl_ciphers directives dynamically. This ensures the console plugin respects cluster-wide TLS policy (Old/Intermediate/ Modern/Custom) and any admin overrides. Falls back to the Intermediate profile when the APIServer resource cannot be read. Co-Authored-By: Claude Opus 4.6 --- pkg/agenticrun/bindata/assets/configmap.yaml | 4 ++ pkg/agenticrun/consoleplugin.go | 59 +++++++++++++++++++- pkg/agenticrun/controller.go | 12 +++- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/pkg/agenticrun/bindata/assets/configmap.yaml b/pkg/agenticrun/bindata/assets/configmap.yaml index f54f33bce0..c708522664 100644 --- a/pkg/agenticrun/bindata/assets/configmap.yaml +++ b/pkg/agenticrun/bindata/assets/configmap.yaml @@ -19,6 +19,10 @@ data: listen [::]:9001 ssl; ssl_certificate /var/cert/tls.crt; ssl_certificate_key /var/cert/tls.key; + ssl_protocols ${SSL_PROTOCOLS}; + ssl_ciphers ${SSL_CIPHERS}; + ssl_prefer_server_ciphers on; + server_tokens off; root /usr/share/nginx/html; } } diff --git a/pkg/agenticrun/consoleplugin.go b/pkg/agenticrun/consoleplugin.go index 40d8ebe7ba..484023e102 100644 --- a/pkg/agenticrun/consoleplugin.go +++ b/pkg/agenticrun/consoleplugin.go @@ -16,12 +16,63 @@ import ( "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/klog/v2" + configv1 "github.com/openshift/api/config/v1" operatorv1 "github.com/openshift/api/operator/v1" "github.com/openshift/cluster-version-operator/pkg/agenticrun/bindata" i "github.com/openshift/cluster-version-operator/pkg/internal" ) +var tlsVersionToNginxProtocols = map[configv1.TLSProtocolVersion]string{ + configv1.VersionTLS10: "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3", + configv1.VersionTLS11: "TLSv1.1 TLSv1.2 TLSv1.3", + configv1.VersionTLS12: "TLSv1.2 TLSv1.3", + configv1.VersionTLS13: "TLSv1.3", +} + +func resolveTLSProfileSpec(tlsSecurityProfile *configv1.TLSSecurityProfile) *configv1.TLSProfileSpec { + if tlsSecurityProfile == nil { + return configv1.TLSProfiles[configv1.TLSProfileIntermediateType] + } + if tlsSecurityProfile.Type == configv1.TLSProfileCustomType && tlsSecurityProfile.Custom != nil { + return &tlsSecurityProfile.Custom.TLSProfileSpec + } + if spec, ok := configv1.TLSProfiles[tlsSecurityProfile.Type]; ok { + return spec + } + return configv1.TLSProfiles[configv1.TLSProfileIntermediateType] +} + +func nginxTLSDirectives(profile *configv1.TLSProfileSpec) (sslProtocols, sslCiphers string) { + sslProtocols = tlsVersionToNginxProtocols[profile.MinTLSVersion] + if sslProtocols == "" { + sslProtocols = tlsVersionToNginxProtocols[configv1.VersionTLS12] + } + + // TLS 1.3 ciphers (TLS_*) are not configurable via nginx ssl_ciphers — + // they are always enabled when TLS 1.3 is negotiated. + var ciphers []string + for _, c := range profile.Ciphers { + if !strings.HasPrefix(c, "TLS_") { + ciphers = append(ciphers, c) + } + } + + // Modern profile has only TLS 1.3 ciphers, which all get filtered above. + // Fall back to Intermediate ciphers to avoid an empty ssl_ciphers directive + // that would produce invalid nginx config. + if len(ciphers) == 0 { + for _, c := range configv1.TLSProfiles[configv1.TLSProfileIntermediateType].Ciphers { + if !strings.HasPrefix(c, "TLS_") { + ciphers = append(ciphers, c) + } + } + } + + sslCiphers = strings.Join(ciphers, ":") + return sslProtocols, sslCiphers +} + var consolePluginAssets = []string{ "assets/namespace.yaml", "assets/serviceaccount.yaml", @@ -33,13 +84,19 @@ var consolePluginAssets = []string{ "assets/consoleplugin.yaml", } -func applyConsolePluginManifests(ctx context.Context, client ctrlruntimeclient.Client, image string) error { +func applyConsolePluginManifests(ctx context.Context, client ctrlruntimeclient.Client, image string, tlsProfile *configv1.TLSProfileSpec) error { + sslProtocols, sslCiphers := nginxTLSDirectives(tlsProfile) + for _, asset := range consolePluginAssets { raw := bindata.MustAsset(asset) if asset == "assets/deployment.yaml" { raw = []byte(strings.ReplaceAll(string(raw), "${IMAGE}", image)) } + if asset == "assets/configmap.yaml" { + s := strings.ReplaceAll(string(raw), "${SSL_PROTOCOLS}", sslProtocols) + raw = []byte(strings.ReplaceAll(s, "${SSL_CIPHERS}", sslCiphers)) + } obj := &unstructured.Unstructured{} if err := yaml.NewYAMLOrJSONDecoder(strings.NewReader(string(raw)), len(raw)).Decode(obj); err != nil { diff --git a/pkg/agenticrun/controller.go b/pkg/agenticrun/controller.go index 6c293491f1..e7161afff1 100644 --- a/pkg/agenticrun/controller.go +++ b/pkg/agenticrun/controller.go @@ -186,7 +186,17 @@ func (c *Controller) ensureConsolePlugin(ctx context.Context) error { if c.consolePluginImage == "" { return fmt.Errorf("console plugin image not set") } - return applyConsolePluginManifests(ctx, c.client, c.consolePluginImage) + + apiServer := &configv1.APIServer{} + var tlsProfile *configv1.TLSProfileSpec + if err := c.client.Get(ctx, ctrlruntimeclient.ObjectKey{Name: "cluster"}, apiServer); err != nil { + klog.Warningf("Could not read APIServer config, using Intermediate TLS defaults: %v", err) + tlsProfile = configv1.TLSProfiles[configv1.TLSProfileIntermediateType] + } else { + tlsProfile = resolveTLSProfileSpec(apiServer.Spec.TLSSecurityProfile) + } + + return applyConsolePluginManifests(ctx, c.client, c.consolePluginImage, tlsProfile) } func (c *Controller) Sync(ctx context.Context, key string) error {