Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
53091fb
feat(tls): configure TLS curve preferences for gitops-plugin
akhilnittala Oct 8, 2026
3cbbe79
feat(tls): configure TLS curve preferences for gitops-plugin
akhilnittala Oct 8, 2026
c2e436f
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 9, 2026
c51318b
feat(tls): Configure TLS Curve Preferences
akhilnittala Oct 9, 2026
e288d20
feat(tls): Configure TLS Curve Preferences
akhilnittala Oct 9, 2026
f1bdb0b
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 10, 2026
47abcba
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 11, 2026
b015843
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 12, 2026
d1f19ce
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 13, 2026
9fbe7f4
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 13, 2026
39e168f
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 13, 2026
9f43fad
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 13, 2026
91188d8
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 13, 2026
43d4880
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 14, 2026
34af450
Merge branch 'master' into usr/akhil/configure_gitops_plugin_curve_pr…
akhilnittala Oct 14, 2026
c423132
feat(tls): configure TLS Curve preferences for PQC
akhilnittala Oct 14, 2026
9dc447b
feat(tls): configure TLS Curve preferences for PQC
akhilnittala Oct 14, 2026
796f53d
feat(tls): configure TLS Curve preferences for PQC
akhilnittala Oct 14, 2026
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
2 changes: 2 additions & 0 deletions argocd-operator/pkg/tlsprofile/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 []configv1.TLSGroup
}
27 changes: 15 additions & 12 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,19 @@ func main() {
os.Exit(1)
}

centralTLSConfigProfile := tlsprofile.TLSConfigProfile{
DisableClusterTLSProfile: disableClusterTLSProfile,
MinVersion: profile.MinTLSVersion,
Ciphers: profile.Ciphers,
CurvePreferences: profile.Groups,
}

if util.IsOpenShiftCluster() {
if err = (&controllers.ReconcileGitopsService{
Client: client,
Scheme: mgr.GetScheme(),
DisableDefaultInstall: strings.ToLower(os.Getenv(common.DisableDefaultInstallEnvVar)) == "true",
CentralTLSProfile: profile,
CentralTLSProfile: centralTLSConfigProfile,
PluginNamespace: pluginNamespace,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "GitopsService")
Expand Down Expand Up @@ -372,17 +379,13 @@ func main() {
argocdprovisioner.Register(openshift.ReconcilerHook, openshift.BuilderHook)

if err = (&argocdprovisioner.ReconcileArgoCD{
Client: client,
Scheme: mgr.GetScheme(),
LabelSelector: labelSelectorFlag,
K8sClient: k8sClient,
LocalUsers: argocdprovisioner.NewLocalUsersInfo(),
FipsConfigChecker: argoutil.NewLinuxFipsConfigChecker(),
CentralTLSConfigProfile: tlsprofile.TLSConfigProfile{
DisableClusterTLSProfile: disableClusterTLSProfile,
MinVersion: profile.MinTLSVersion,
Ciphers: profile.Ciphers,
},
Client: client,
Scheme: mgr.GetScheme(),
LabelSelector: labelSelectorFlag,
K8sClient: k8sClient,
LocalUsers: argocdprovisioner.NewLocalUsersInfo(),
FipsConfigChecker: argoutil.NewLinuxFipsConfigChecker(),
CentralTLSConfigProfile: centralTLSConfigProfile,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Argo CD")
os.Exit(1)
Expand Down
40 changes: 39 additions & 1 deletion controllers/consoleplugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -250,9 +251,43 @@ 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))
skipped := make([]configv1.TLSGroup, 0)
for _, g := range groups {
if _, ok := allowedHttpdGroups[g]; ok {
names = append(names, string(g))
continue
}
skipped = append(skipped, g)
}
if len(skipped) > 0 {
logs.Info("skipping unsupported TLS curve preference groups for gitops-plugin httpd", "groups", skipped)
}
return strings.Join(names, ":")
}
Comment thread
akhilnittala marked this conversation as resolved.

// buildHttpdConfig generates httpd.conf with dynamic TLS settings
func (r *ReconcileGitopsService) buildHttpdConfig() string {
minVersionTLS := string(r.CentralTLSProfile.MinTLSVersion)
minVersionTLS := string(r.CentralTLSProfile.MinVersion)
httpdConfigBase := fmt.Sprintf(`LoadModule ssl_module modules/mod_ssl.so
Listen %d https
ServerRoot "/etc/httpd"
Expand All @@ -275,6 +310,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.CurvePreferences); groups != "" {
httpdConfigBase += fmt.Sprintf("\n\tSSLOpenSSLConfCmd Groups %s", groups)
}
// Close VirtualHost
httpdConfigBase += "\n</VirtualHost>"
return httpdConfigBase
Expand Down
108 changes: 92 additions & 16 deletions controllers/consoleplugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

argocommon "github.com/argoproj-labs/gitops-operator/argocd-operator/common"
"github.com/argoproj-labs/gitops-operator/argocd-operator/pkg/tlsprofile"
configv1 "github.com/openshift/api/config/v1"
consolev1 "github.com/openshift/api/console/v1"
pipelinesv1alpha1 "github.com/redhat-developer/gitops-operator/api/v1alpha1"
Expand Down Expand Up @@ -1866,62 +1867,130 @@ func TestReconcileDeployment_UpdateWhenActualChange(t *testing.T) {
func TestBuildHttpdConfig(t *testing.T) {
tests := []struct {
name string
CentralTLSProfile configv1.TLSProfileSpec
CentralTLSProfile tlsprofile.TLSConfigProfile
wantProtocol bool
wantCipherSuite bool
wantGroups bool
protocol string
cipherSuite string
groups string
}{
{
name: "no TLS version no ciphers",
CentralTLSProfile: configv1.TLSProfileSpec{
MinTLSVersion: "",
Ciphers: nil,
CentralTLSProfile: tlsprofile.TLSConfigProfile{
MinVersion: "",
Ciphers: nil,
},
wantProtocol: false,
wantCipherSuite: false,
wantGroups: false,
},
{
name: "TLS 1.2 with cipher suites",
CentralTLSProfile: configv1.TLSProfileSpec{
MinTLSVersion: "VersionTLS12",
Ciphers: []string{"ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-RSA-AES128-GCM-SHA256"},
CentralTLSProfile: tlsprofile.TLSConfigProfile{
MinVersion: "VersionTLS12",
Ciphers: []string{"ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-RSA-AES128-GCM-SHA256"},
},
wantProtocol: true,
wantCipherSuite: true,
wantGroups: false,
protocol: "-all +TLSv1.2 +TLSv1.3",
cipherSuite: "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256",
},
{
name: "TLS 1.3 ignores cipher suites",
CentralTLSProfile: configv1.TLSProfileSpec{
MinTLSVersion: "VersionTLS13",
Ciphers: []string{"TLS_AES_128_GCM_SHA256"},
CentralTLSProfile: tlsprofile.TLSConfigProfile{
MinVersion: "VersionTLS13",
Ciphers: []string{"TLS_AES_128_GCM_SHA256"},
},
wantProtocol: true,
wantCipherSuite: false,
wantGroups: false,
protocol: "-all +TLSv1.3",
},
{
name: "TLS 1.2 without cipher suites",
CentralTLSProfile: configv1.TLSProfileSpec{
MinTLSVersion: "VersionTLS12",
Ciphers: nil,
CentralTLSProfile: tlsprofile.TLSConfigProfile{
MinVersion: "VersionTLS12",
Ciphers: nil,
},
wantProtocol: true,
wantCipherSuite: false,
wantGroups: false,
protocol: "-all +TLSv1.2 +TLSv1.3",
},
{
name: "invalid TLS version with cipher suites",
CentralTLSProfile: configv1.TLSProfileSpec{
MinTLSVersion: "invalid",
Ciphers: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
CentralTLSProfile: tlsprofile.TLSConfigProfile{
MinVersion: "invalid",
Ciphers: []string{"ECDHE-RSA-AES256-GCM-SHA384"},
},
wantProtocol: false,
wantCipherSuite: true,
wantGroups: false,
cipherSuite: "ECDHE-RSA-AES256-GCM-SHA384",
},
{
name: "TLS groups configure SSLOpenSSLConfCmd Groups",
CentralTLSProfile: tlsprofile.TLSConfigProfile{
MinVersion: "VersionTLS12",
CurvePreferences: []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: tlsprofile.TLSConfigProfile{
MinVersion: "VersionTLS13",
CurvePreferences: []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: tlsprofile.TLSConfigProfile{
MinVersion: "VersionTLS12",
CurvePreferences: []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: tlsprofile.TLSConfigProfile{
MinVersion: "VersionTLS12",
CurvePreferences: []configv1.TLSGroup{
configv1.TLSGroup("UnknownGroup"),
},
},
wantProtocol: true,
wantCipherSuite: false,
wantGroups: false,
protocol: "-all +TLSv1.2 +TLSv1.3",
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -1953,6 +2022,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"))
}
})
}
}
Expand Down
45 changes: 33 additions & 12 deletions controllers/gitopsservice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ import (
argocommon "github.com/argoproj-labs/gitops-operator/argocd-operator/common"
argocdcontroller "github.com/argoproj-labs/gitops-operator/argocd-operator/controllers/argocd"
argocdutil "github.com/argoproj-labs/gitops-operator/argocd-operator/controllers/argoutil"
"github.com/argoproj-labs/gitops-operator/argocd-operator/pkg/tlsprofile"
"github.com/go-logr/logr"
version "github.com/hashicorp/go-version"
configv1 "github.com/openshift/api/config/v1"
routev1 "github.com/openshift/api/route/v1"
pipelinesv1alpha1 "github.com/redhat-developer/gitops-operator/api/v1alpha1"
"github.com/redhat-developer/gitops-operator/common"
Expand All @@ -54,8 +56,6 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

configv1 "github.com/openshift/api/config/v1"
)

var logs = logf.Log.WithName("controller_gitopsservice")
Expand Down Expand Up @@ -148,8 +148,8 @@ type ReconcileGitopsService struct {

// disableDefaultInstall, if true, will ensure that the default ArgoCD instance is not instantiated in the openshift-gitops namespace.
DisableDefaultInstall bool
//CentralTLSProfile contains MinVersion and CipherSuites
CentralTLSProfile configv1.TLSProfileSpec
// CentralTLSProfile contains cluster TLS settings (min version, ciphers, curve preferences).
CentralTLSProfile tlsprofile.TLSConfigProfile

// PluginNamespace is the namespace where console plugin resources (Deployment, Service, ConfigMap) are deployed.
// This is typically the operator's own namespace (e.g. "openshift-gitops-operator").
Expand Down Expand Up @@ -834,7 +834,7 @@ func objectMeta(resourceName string, namespace string, opts ...func(*metav1.Obje
return objectMeta
}

func newBackendDeployment(ns types.NamespacedName, crImagePullPolicy corev1.PullPolicy, CentralTLSProfile configv1.TLSProfileSpec) *appsv1.Deployment {
func newBackendDeployment(ns types.NamespacedName, crImagePullPolicy corev1.PullPolicy, CentralTLSProfile tlsprofile.TLSConfigProfile) *appsv1.Deployment {
image := os.Getenv(backendImageEnvName)
if image == "" {
image = backendImage
Expand All @@ -845,18 +845,39 @@ func newBackendDeployment(ns types.NamespacedName, crImagePullPolicy corev1.Pull
Value: insecureEnvVarValue,
},
}
if argocdutil.TLSProtocolVersionString(CentralTLSProfile.MinTLSVersion) != "" {
if argocdutil.TLSProtocolVersionString(CentralTLSProfile.MinVersion) != "" {
env = append(env, corev1.EnvVar{
Name: "TLS_MIN_VERSION",
Value: argocdutil.TLSProtocolVersionString(CentralTLSProfile.MinTLSVersion),
Value: argocdutil.TLSProtocolVersionString(CentralTLSProfile.MinVersion),
})
}

if len(CentralTLSProfile.Ciphers) > 0 {
env = append(env, corev1.EnvVar{
Name: "TLS_CIPHER_SUITES",
Value: strings.Join(CentralTLSProfile.Ciphers, ":"),
})
// gitops-backend is Go-based and accepts IANA cipher names only (e.g.
// TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), not OpenSSL names from the
// OpenShift TLSSecurityProfile (e.g. ECDHE-ECDSA-AES128-GCM-SHA256).
if ciphers := argocdutil.MapCipherSuites(CentralTLSProfile.Ciphers); len(ciphers) > 0 {
// Go does not allow configuring TLS 1.3 cipher suites; gitops-backend
// also rejects TLS 1.3 suites when the configured minimum is below 1.3.
if CentralTLSProfile.MinVersion != configv1.VersionTLS13 {
tls13Ciphers := map[string]bool{
"TLS_AES_128_GCM_SHA256": true,
"TLS_AES_256_GCM_SHA384": true,
"TLS_CHACHA20_POLY1305_SHA256": true,
}
filtered := make([]string, 0, len(ciphers))
for _, cipher := range ciphers {
if !tls13Ciphers[cipher] {
filtered = append(filtered, cipher)
}
}
ciphers = filtered
}
if len(ciphers) > 0 {
env = append(env, corev1.EnvVar{
Name: "TLS_CIPHER_SUITES",
Value: strings.Join(ciphers, ":"),
})
}
}
podSpec := corev1.PodSpec{
Containers: []corev1.Container{
Expand Down
Loading
Loading