@@ -29,6 +29,7 @@ import (
2929 "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
3030 "github.com/tektoncd/operator/pkg/client/clientset/versioned"
3131 "github.com/tektoncd/operator/pkg/reconciler/common"
32+ occommon "github.com/tektoncd/operator/pkg/reconciler/openshift/common"
3233 "github.com/tektoncd/operator/pkg/reconciler/shared/hash"
3334 "go.uber.org/zap"
3435 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -64,6 +65,15 @@ type consolePluginReconciler struct {
6465 operatorVersion string
6566 pipelinesConsolePluginImage string
6667 manifest mf.Manifest
68+ // tlsConfig holds the centrally resolved TLS profile (set on every reconcile).
69+ // nil means central TLS is disabled; the nginx.conf is left unmodified.
70+ tlsConfig * occommon.TLSEnvVars
71+ }
72+
73+ // SetTLSConfig stores the resolved central TLS configuration for use during the
74+ // next reconcile cycle. Call this before reconcile() on every reconcile loop.
75+ func (cpr * consolePluginReconciler ) SetTLSConfig (tlsEnvVars * occommon.TLSEnvVars ) {
76+ cpr .tlsConfig = tlsEnvVars
6777}
6878
6979// reconcile steps
@@ -169,6 +179,7 @@ func (cpr *consolePluginReconciler) updateOnce(ctx context.Context) {
169179 "environmentVariable" , PipelinesConsolePluginImageEnvironmentKey ,
170180 )
171181 }
182+
172183 })
173184}
174185
@@ -206,6 +217,8 @@ func (cpr *consolePluginReconciler) transform(ctx context.Context, manifest *mf.
206217 // updates "metadata.namespace" to targetNamespace
207218 common .ReplaceNamespace (tektonConfigCR .Spec .TargetNamespace ),
208219 cpr .transformerConsolePlugin (tektonConfigCR .Spec .TargetNamespace ),
220+ // Add nginx TLS configuration transformer
221+ cpr .transformerNginxTLS (),
209222 common .AddConfiguration (tektonConfigCR .Spec .Config ),
210223 }
211224
@@ -234,3 +247,116 @@ func (cpr *consolePluginReconciler) transformerConsolePlugin(targetNamespace str
234247 return unstructured .SetNestedField (u .Object , targetNamespace , "spec" , "backend" , "service" , "namespace" )
235248 }
236249}
250+
251+ // transformerNginxTLS updates the nginx.conf ConfigMap with TLS directives
252+ func (cpr * consolePluginReconciler ) transformerNginxTLS () mf.Transformer {
253+ return func (u * unstructured.Unstructured ) error {
254+ if u .GetKind () != "ConfigMap" || u .GetName () != "pipelines-console-plugin" {
255+ return nil
256+ }
257+
258+ // Get the current nginx.conf
259+ data , found , err := unstructured .NestedString (u .Object , "data" , "nginx.conf" )
260+ if err != nil || ! found {
261+ return err
262+ }
263+
264+ // Generate the updated nginx.conf with TLS directives
265+ updatedConf := cpr .generateNginxConfWithTLS (data )
266+
267+ // Set the updated nginx.conf back
268+ return unstructured .SetNestedField (u .Object , updatedConf , "data" , "nginx.conf" )
269+ }
270+ }
271+
272+ // generateNginxConfWithTLS injects TLS directives into nginx configuration
273+ func (cpr * consolePluginReconciler ) generateNginxConfWithTLS (baseConf string ) string {
274+ // Build TLS directives
275+ tlsDirectives := cpr .buildNginxTLSDirectives ()
276+
277+ // If no TLS directives to add, return original
278+ if tlsDirectives == "" {
279+ return baseConf
280+ }
281+
282+ // Inject TLS directives into the server block
283+ // Find "server {" and inject after it
284+ lines := strings .Split (baseConf , "\n " )
285+ var result strings.Builder
286+
287+ for _ , line := range lines {
288+ result .WriteString (line )
289+ result .WriteString ("\n " )
290+
291+ // After "server {", inject TLS directives
292+ if strings .Contains (line , "server {" ) {
293+ // Add TLS directives with proper indentation
294+ result .WriteString (tlsDirectives )
295+ }
296+ }
297+
298+ return result .String ()
299+ }
300+
301+ // buildNginxTLSDirectives generates nginx TLS directives from the centrally resolved
302+ // TLS profile. Returns an empty string when no TLS config is available.
303+ func (cpr * consolePluginReconciler ) buildNginxTLSDirectives () string {
304+ if cpr .tlsConfig == nil {
305+ return ""
306+ }
307+
308+ var directives strings.Builder
309+
310+ // ssl_protocols – derived from the minimum TLS version in the APIServer profile.
311+ // TLSEnvVars.MinVersion is in Go crypto/tls format: "1.2" or "1.3".
312+ // We always include TLSv1.3 so ML-KEM hybrid groups are available.
313+ if cpr .tlsConfig .MinVersion != "" {
314+ protocols := convertTLSVersionToNginx (cpr .tlsConfig .MinVersion )
315+ directives .WriteString (fmt .Sprintf (" ssl_protocols %s;\n " , protocols ))
316+
317+ // Enable ML-KEM (X25519MLKEM768) hybrid key exchange for PQC readiness.
318+ // ssl_conf_command passes OpenSSL configuration directly and is the only
319+ // nginx mechanism that supports the post-quantum hybrid groups introduced
320+ // in OpenSSL 3.x; ssl_ecdh_curve does not cover these groups.
321+ // X25519MLKEM768 is tried first (PQC); X25519 is the classical fallback.
322+ directives .WriteString (" ssl_conf_command Groups X25519MLKEM768:X25519;\n " )
323+ }
324+
325+ // NOTE: IANA cipher suite names (TLS_ECDHE_RSA_…) cannot be used directly in
326+ // nginx's ssl_ciphers directive (which uses OpenSSL names) or ssl_conf_command
327+ // (which uses a different format). Relying on nginx's own TLS 1.3 defaults is
328+ // simpler and equally secure; we intentionally skip cipher configuration here.
329+ if cpr .tlsConfig .CipherSuites != "" {
330+ cpr .logger .Debugw ("TLS cipher suites provided but not applied to nginx (using nginx defaults)" ,
331+ "reason" , "IANA names are not directly usable in nginx ssl_ciphers" ,
332+ )
333+ }
334+
335+ // ssl_ecdh_curve – comma-separated curve names become colon-separated for nginx.
336+ // This covers TLS 1.2 classical curves; ML-KEM hybrid groups are handled above
337+ // via ssl_conf_command.
338+ if cpr .tlsConfig .CurvePreferences != "" {
339+ curves := strings .ReplaceAll (cpr .tlsConfig .CurvePreferences , "," , ":" )
340+ directives .WriteString (fmt .Sprintf (" ssl_ecdh_curve %s;\n " , curves ))
341+ }
342+
343+ return directives .String ()
344+ }
345+
346+ // convertTLSVersionToNginx converts the Go crypto/tls minimum version string
347+ // ("1.2" or "1.3", as stored in TLSEnvVars.MinVersion) to the corresponding
348+ // nginx ssl_protocols value.
349+ func convertTLSVersionToNginx (minVersion string ) string {
350+ switch minVersion {
351+ case "1.3" :
352+ return "TLSv1.3"
353+ case "1.2" :
354+ return "TLSv1.2 TLSv1.3"
355+ case "1.1" :
356+ return "TLSv1.1 TLSv1.2 TLSv1.3"
357+ case "1.0" :
358+ return "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3"
359+ default :
360+ return "TLSv1.2 TLSv1.3"
361+ }
362+ }
0 commit comments