@@ -65,6 +65,15 @@ type consolePluginReconciler struct {
6565 operatorVersion string
6666 pipelinesConsolePluginImage string
6767 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
6877}
6978
7079// reconcile steps
@@ -184,6 +193,7 @@ func (cpr *consolePluginReconciler) updateOnce(ctx context.Context) {
184193 "environmentVariable" , envKey ,
185194 )
186195 }
196+
187197 })
188198}
189199
@@ -221,6 +231,8 @@ func (cpr *consolePluginReconciler) transform(ctx context.Context, manifest *mf.
221231 // updates "metadata.namespace" to targetNamespace
222232 common .ReplaceNamespace (tektonConfigCR .Spec .TargetNamespace ),
223233 cpr .transformerConsolePlugin (tektonConfigCR .Spec .TargetNamespace ),
234+ // Add nginx TLS configuration transformer
235+ cpr .transformerNginxTLS (),
224236 common .AddConfiguration (tektonConfigCR .Spec .Config ),
225237 }
226238
@@ -249,3 +261,113 @@ func (cpr *consolePluginReconciler) transformerConsolePlugin(targetNamespace str
249261 return unstructured .SetNestedField (u .Object , targetNamespace , "spec" , "backend" , "service" , "namespace" )
250262 }
251263}
264+
265+ // transformerNginxTLS updates the nginx.conf ConfigMap with TLS directives
266+ func (cpr * consolePluginReconciler ) transformerNginxTLS () mf.Transformer {
267+ return func (u * unstructured.Unstructured ) error {
268+ if u .GetKind () != "ConfigMap" || u .GetName () != "pipelines-console-plugin" {
269+ return nil
270+ }
271+
272+ // Get the current nginx.conf
273+ data , found , err := unstructured .NestedString (u .Object , "data" , "nginx.conf" )
274+ if err != nil || ! found {
275+ return err
276+ }
277+
278+ // Generate the updated nginx.conf with TLS directives
279+ updatedConf := cpr .generateNginxConfWithTLS (data )
280+
281+ // Set the updated nginx.conf back
282+ return unstructured .SetNestedField (u .Object , updatedConf , "data" , "nginx.conf" )
283+ }
284+ }
285+
286+ // generateNginxConfWithTLS injects TLS directives into nginx configuration.
287+ // Directives are always produced (ssl_protocols + ML-KEM ssl_conf_command) so
288+ // this function never returns the unmodified base configuration.
289+ func (cpr * consolePluginReconciler ) generateNginxConfWithTLS (baseConf string ) string {
290+ tlsDirectives := cpr .buildNginxTLSDirectives ()
291+
292+ // Inject TLS directives into the server block
293+ // Find "server {" and inject after it
294+ lines := strings .Split (baseConf , "\n " )
295+ var result strings.Builder
296+
297+ for _ , line := range lines {
298+ result .WriteString (line )
299+ result .WriteString ("\n " )
300+
301+ // After "server {", inject TLS directives
302+ if strings .Contains (line , "server {" ) {
303+ // Add TLS directives with proper indentation
304+ result .WriteString (tlsDirectives )
305+ }
306+ }
307+
308+ return result .String ()
309+ }
310+
311+ // buildNginxTLSDirectives generates nginx TLS directives from the centrally resolved
312+ // TLS profile. When no explicit profile is configured (cluster uses the "Default"
313+ // profile), secure Intermediate-equivalent defaults are applied so that PQC
314+ // directives are always present regardless of cluster configuration.
315+ func (cpr * consolePluginReconciler ) buildNginxTLSDirectives () string {
316+ var directives strings.Builder
317+
318+ // ssl_protocols – derived from the minimum TLS version in the APIServer profile.
319+ // Fall back to "1.2" (Intermediate) when no central TLS config is present, which
320+ // is the OpenShift default for clusters without an explicit tlsSecurityProfile.
321+ minVersion := "1.2"
322+ if cpr .tlsConfig != nil && cpr .tlsConfig .MinVersion != "" {
323+ minVersion = cpr .tlsConfig .MinVersion
324+ }
325+ protocols := convertTLSVersionToNginx (minVersion )
326+ directives .WriteString (fmt .Sprintf (" ssl_protocols %s;\n " , protocols ))
327+
328+ // Always enable ML-KEM (X25519MLKEM768) hybrid key exchange for PQC readiness.
329+ // ssl_conf_command passes OpenSSL configuration directly and is the only nginx
330+ // mechanism that supports the post-quantum hybrid groups introduced in OpenSSL 3.x;
331+ // ssl_ecdh_curve does not cover these groups.
332+ // X25519MLKEM768 is tried first (PQC); X25519 is the classical fallback for
333+ // clients that do not yet support ML-KEM.
334+ directives .WriteString (" ssl_conf_command Groups X25519MLKEM768:X25519;\n " )
335+
336+ // NOTE: IANA cipher suite names (TLS_ECDHE_RSA_…) cannot be used directly in
337+ // nginx's ssl_ciphers directive (which uses OpenSSL names) or ssl_conf_command
338+ // (which uses a different format). Relying on nginx's own TLS 1.3 defaults is
339+ // simpler and equally secure; we intentionally skip cipher configuration here.
340+ if cpr .tlsConfig != nil && cpr .tlsConfig .CipherSuites != "" {
341+ cpr .logger .Debugw ("TLS cipher suites provided but not applied to nginx (using nginx defaults)" ,
342+ "reason" , "IANA names are not directly usable in nginx ssl_ciphers" ,
343+ )
344+ }
345+
346+ // ssl_ecdh_curve – comma-separated curve names become colon-separated for nginx.
347+ // This covers TLS 1.2 classical curves; ML-KEM hybrid groups are handled above
348+ // via ssl_conf_command Groups.
349+ if cpr .tlsConfig != nil && cpr .tlsConfig .CurvePreferences != "" {
350+ curves := strings .ReplaceAll (cpr .tlsConfig .CurvePreferences , "," , ":" )
351+ directives .WriteString (fmt .Sprintf (" ssl_ecdh_curve %s;\n " , curves ))
352+ }
353+
354+ return directives .String ()
355+ }
356+
357+ // convertTLSVersionToNginx converts the Go crypto/tls minimum version string
358+ // ("1.2" or "1.3", as stored in TLSEnvVars.MinVersion) to the corresponding
359+ // nginx ssl_protocols value.
360+ func convertTLSVersionToNginx (minVersion string ) string {
361+ switch minVersion {
362+ case "1.3" :
363+ return "TLSv1.3"
364+ case "1.2" :
365+ return "TLSv1.2 TLSv1.3"
366+ case "1.1" :
367+ return "TLSv1.1 TLSv1.2 TLSv1.3"
368+ case "1.0" :
369+ return "TLSv1 TLSv1.1 TLSv1.2 TLSv1.3"
370+ default :
371+ return "TLSv1.2 TLSv1.3"
372+ }
373+ }
0 commit comments