Skip to content

Commit b354742

Browse files
committed
Add centrally managed TLS configuration for console-plugin nginx
1 parent e4eaabd commit b354742

3 files changed

Lines changed: 585 additions & 0 deletions

File tree

pkg/reconciler/openshift/tektonconfig/console_plugin_reconciler.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)