diff --git a/argocd-operator/pkg/tlsprofile/profile.go b/argocd-operator/pkg/tlsprofile/profile.go index 6b83c09b2fe..71d50b676a7 100644 --- a/argocd-operator/pkg/tlsprofile/profile.go +++ b/argocd-operator/pkg/tlsprofile/profile.go @@ -8,4 +8,6 @@ type TLSConfigProfile struct { MinVersion configv1.TLSProtocolVersion // Ciphers specifies the list of supported TLS cipher suites in cluster. Ciphers []string + // CurvePreferences specifies the list of supported TLS curve preferences in cluster. + CurvePreferences []string } diff --git a/cmd/main.go b/cmd/main.go index a4a8a72bdce..a9763514c2e 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -371,6 +371,11 @@ func main() { argocdprovisioner.Register(openshift.ReconcilerHook, openshift.BuilderHook) + curvePreferences := make([]string, len(profile.Groups)) + for i, group := range profile.Groups { + curvePreferences[i] = string(group) + } + if err = (&argocdprovisioner.ReconcileArgoCD{ Client: client, Scheme: mgr.GetScheme(), @@ -382,6 +387,7 @@ func main() { DisableClusterTLSProfile: disableClusterTLSProfile, MinVersion: profile.MinTLSVersion, Ciphers: profile.Ciphers, + CurvePreferences: curvePreferences, }, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Argo CD") diff --git a/controllers/consoleplugin.go b/controllers/consoleplugin.go index aab97f443a0..bac312c146b 100644 --- a/controllers/consoleplugin.go +++ b/controllers/consoleplugin.go @@ -13,6 +13,7 @@ import ( argocommon "github.com/argoproj-labs/gitops-operator/argocd-operator/common" argocdutil "github.com/argoproj-labs/gitops-operator/argocd-operator/controllers/argoutil" + configv1 "github.com/openshift/api/config/v1" consolev1 "github.com/openshift/api/console/v1" pipelinesv1alpha1 "github.com/redhat-developer/gitops-operator/api/v1alpha1" "github.com/redhat-developer/gitops-operator/controllers/util" @@ -250,6 +251,34 @@ func securityContextForPlugin() *corev1.SecurityContext { } } +// allowedHttpdGroups lists TLS groups supported by OpenSSL via SSLOpenSSLConfCmd Groups. +// OpenShift TLSGroup string values are directly usable as OpenSSL group names (OpenSSL >= 3.5). +var allowedHttpdGroups = map[configv1.TLSGroup]struct{}{ + configv1.TLSGroupX25519: {}, + configv1.TLSGroupSecP256r1: {}, + configv1.TLSGroupSecP384r1: {}, + configv1.TLSGroupSecP521r1: {}, + configv1.TLSGroupX25519MLKEM768: {}, + configv1.TLSGroupSecP256r1MLKEM768: {}, + configv1.TLSGroupSecP384r1MLKEM1024: {}, +} + +// tlsGroupsForHttpd converts OpenShift TLS groups to a colon-separated OpenSSL +// Groups string for Apache's SSLOpenSSLConfCmd directive. Unknown groups are +// filtered out so forward-compatible profile values do not break httpd. +func tlsGroupsForHttpd(groups []configv1.TLSGroup) string { + if len(groups) == 0 { + return "" + } + names := make([]string, 0, len(groups)) + for _, g := range groups { + if _, ok := allowedHttpdGroups[g]; ok { + names = append(names, string(g)) + } + } + return strings.Join(names, ":") +} + // buildHttpdConfig generates httpd.conf with dynamic TLS settings func (r *ReconcileGitopsService) buildHttpdConfig() string { minVersionTLS := string(r.CentralTLSProfile.MinTLSVersion) @@ -275,6 +304,9 @@ ServerRoot "/etc/httpd" if minVersionTLS != "VersionTLS13" && strings.Join(r.CentralTLSProfile.Ciphers, ":") != "" { httpdConfigBase += fmt.Sprintf("\n\tSSLCipherSuite %s", strings.Join(r.CentralTLSProfile.Ciphers, ":")) } + if groups := tlsGroupsForHttpd(r.CentralTLSProfile.Groups); groups != "" { + httpdConfigBase += fmt.Sprintf("\n\tSSLOpenSSLConfCmd Groups %s", groups) + } // Close VirtualHost httpdConfigBase += "\n" return httpdConfigBase diff --git a/controllers/consoleplugin_test.go b/controllers/consoleplugin_test.go index f03a553316f..611414a2252 100644 --- a/controllers/consoleplugin_test.go +++ b/controllers/consoleplugin_test.go @@ -1869,8 +1869,10 @@ func TestBuildHttpdConfig(t *testing.T) { CentralTLSProfile configv1.TLSProfileSpec wantProtocol bool wantCipherSuite bool + wantGroups bool protocol string cipherSuite string + groups string }{ { name: "no TLS version no ciphers", @@ -1880,6 +1882,7 @@ func TestBuildHttpdConfig(t *testing.T) { }, wantProtocol: false, wantCipherSuite: false, + wantGroups: false, }, { name: "TLS 1.2 with cipher suites", @@ -1889,6 +1892,7 @@ func TestBuildHttpdConfig(t *testing.T) { }, wantProtocol: true, wantCipherSuite: true, + wantGroups: false, protocol: "-all +TLSv1.2 +TLSv1.3", cipherSuite: "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256", }, @@ -1900,6 +1904,7 @@ func TestBuildHttpdConfig(t *testing.T) { }, wantProtocol: true, wantCipherSuite: false, + wantGroups: false, protocol: "-all +TLSv1.3", }, { @@ -1910,6 +1915,7 @@ func TestBuildHttpdConfig(t *testing.T) { }, wantProtocol: true, wantCipherSuite: false, + wantGroups: false, protocol: "-all +TLSv1.2 +TLSv1.3", }, { @@ -1920,8 +1926,70 @@ func TestBuildHttpdConfig(t *testing.T) { }, wantProtocol: false, wantCipherSuite: true, + wantGroups: false, cipherSuite: "ECDHE-RSA-AES256-GCM-SHA384", }, + { + name: "TLS groups configure SSLOpenSSLConfCmd Groups", + CentralTLSProfile: configv1.TLSProfileSpec{ + MinTLSVersion: "VersionTLS12", + Groups: []configv1.TLSGroup{ + configv1.TLSGroupX25519MLKEM768, + configv1.TLSGroupX25519, + configv1.TLSGroupSecP256r1, + configv1.TLSGroupSecP384r1, + }, + }, + wantProtocol: true, + wantCipherSuite: false, + wantGroups: true, + protocol: "-all +TLSv1.2 +TLSv1.3", + groups: "X25519MLKEM768:X25519:secp256r1:secp384r1", + }, + { + name: "TLS 1.3 with groups", + CentralTLSProfile: configv1.TLSProfileSpec{ + MinTLSVersion: "VersionTLS13", + Groups: []configv1.TLSGroup{ + configv1.TLSGroupX25519, + configv1.TLSGroupSecP256r1, + }, + }, + wantProtocol: true, + wantCipherSuite: false, + wantGroups: true, + protocol: "-all +TLSv1.3", + groups: "X25519:secp256r1", + }, + { + name: "unknown groups are filtered out", + CentralTLSProfile: configv1.TLSProfileSpec{ + MinTLSVersion: "VersionTLS12", + Groups: []configv1.TLSGroup{ + configv1.TLSGroupX25519, + configv1.TLSGroup("UnknownGroup"), + configv1.TLSGroupSecP384r1, + }, + }, + wantProtocol: true, + wantCipherSuite: false, + wantGroups: true, + protocol: "-all +TLSv1.2 +TLSv1.3", + groups: "X25519:secp384r1", + }, + { + name: "only unknown groups omits SSLOpenSSLConfCmd", + CentralTLSProfile: configv1.TLSProfileSpec{ + MinTLSVersion: "VersionTLS12", + Groups: []configv1.TLSGroup{ + configv1.TLSGroup("UnknownGroup"), + }, + }, + wantProtocol: true, + wantCipherSuite: false, + wantGroups: false, + protocol: "-all +TLSv1.2 +TLSv1.3", + }, } for _, tt := range tests { @@ -1953,6 +2021,13 @@ func TestBuildHttpdConfig(t *testing.T) { } else { assert.Assert(t, !strings.Contains(cfg, "SSLCipherSuite")) } + + // SSLOpenSSLConfCmd Groups + if tt.wantGroups { + assert.Assert(t, cmp.Contains(cfg, "SSLOpenSSLConfCmd Groups "+tt.groups)) + } else { + assert.Assert(t, !strings.Contains(cfg, "SSLOpenSSLConfCmd Groups")) + } }) } }