AgentSkillsCN

OpenShift TLS Security Profile Configuration

利用此技能,为 OpenShift 上的运维人员与工作负载实施 TLS 安全配置文件。提供从 APIServer CR 中读取 TLS 配置,并将其应用于 webhook/metrics 服务器、HTTP 与 gRPC 端点的指导。

SKILL.md
--- frontmatter
name: OpenShift TLS Security Profile Configuration
description: Use this skill to implement TLS security profiles for operators and workloads on OpenShift. Provides guidance on reading TLS config from APIServer CR and applying it to webhook/metrics servers, HTTP, and gRPC endpoints.

OpenShift TLS Security Profile Configuration

This skill helps implement TLS security profiles for operators and workloads running on OpenShift. It provides complete guidance on reading TLS configuration from the APIServer custom resource and applying it consistently across all secured endpoints.

When to Use This Skill

Use this skill when:

  • Implementing TLS security profiles in a Kubernetes operator running on OpenShift
  • Configuring webhook servers and metrics endpoints with cluster-wide TLS settings
  • Setting up HTTP or gRPC clients/servers that need to comply with OpenShift TLS policies
  • Converting OpenShift TLS profile types to Go crypto/tls configuration
  • Troubleshooting TLS-related connection issues in OpenShift clusters

Requirements

Operators implementing TLS security profiles must satisfy these requirements:

  1. Read TLS profile from APIServer CR: Fetch configuration from apiservers.config.openshift.io/cluster
  2. Apply to all TLS endpoints: Webhook server, metrics server, and any HTTP/gRPC clients or servers
  3. Respond to profile changes: If the TLS profile is updated in the cluster, the component should pick up the changes (may require restart depending on implementation)

Handling Profile Changes

There are two approaches to respond to TLS profile changes:

Option A: Watch from Existing Controller (Recommended)

If your operator manages operands that need TLS configuration, watch the APIServer resource from your existing controller. This triggers operand reconciliation when the TLS profile changes, allowing you to update operand deployments with the new TLS settings:

go
package controller

import (
	"context"
	"reflect"

	configv1 "github.com/openshift/api/config/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/builder"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/event"
	"sigs.k8s.io/controller-runtime/pkg/handler"
	"sigs.k8s.io/controller-runtime/pkg/predicate"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"

	myv1 "myoperator/api/v1"
)

type MyOperandReconciler struct {
	client.Client
	Scheme *runtime.Scheme
}

func (r *MyOperandReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	// Fetch operand
	operand := &myv1.MyOperand{}
	if err := r.Get(ctx, req.NamespacedName, operand); err != nil {
		return ctrl.Result{}, client.IgnoreNotFound(err)
	}

	// Fetch current TLS profile
	profile, err := GetTLSSecurityProfile(ctx, r.Client)
	if err != nil {
		return ctrl.Result{}, err
	}

	// Apply TLS configuration to operand's deployment/pods
	// This could involve updating a ConfigMap, Secret, or Deployment annotation
	// to trigger a rolling restart of operand pods with new TLS settings
	if err := r.reconcileOperandTLS(ctx, operand, profile); err != nil {
		return ctrl.Result{}, err
	}

	return ctrl.Result{}, nil
}

func (r *MyOperandReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&myv1.MyOperand{}).
		// Watch APIServer and trigger reconcile for all operands when TLS profile changes
		Watches(
			&configv1.APIServer{},
			handler.EnqueueRequestsFromMapFunc(r.mapAPIServerToOperands),
			builder.WithPredicates(tlsProfileChangedPredicate()),
		).
		Complete(r)
}

// mapAPIServerToOperands returns reconcile requests for all operands when APIServer changes
func (r *MyOperandReconciler) mapAPIServerToOperands(ctx context.Context, obj client.Object) []reconcile.Request {
	// Only react to the "cluster" APIServer
	if obj.GetName() != "cluster" {
		return nil
	}

	// List all operands and trigger reconcile for each
	var operands myv1.MyOperandList
	if err := r.List(ctx, &operands); err != nil {
		return nil
	}

	requests := make([]reconcile.Request, len(operands.Items))
	for i, op := range operands.Items {
		requests[i] = reconcile.Request{
			NamespacedName: types.NamespacedName{
				Name:      op.Name,
				Namespace: op.Namespace,
			},
		}
	}
	return requests
}

// tlsProfileChangedPredicate filters events to only TLS profile changes
func tlsProfileChangedPredicate() predicate.Predicate {
	return predicate.Funcs{
		CreateFunc: func(e event.CreateEvent) bool {
			return e.Object.GetName() == "cluster"
		},
		UpdateFunc: func(e event.UpdateEvent) bool {
			if e.ObjectNew.GetName() != "cluster" {
				return false
			}
			oldAPI, ok := e.ObjectOld.(*configv1.APIServer)
			if !ok {
				return false
			}
			newAPI, ok := e.ObjectNew.(*configv1.APIServer)
			if !ok {
				return false
			}
			// Only reconcile if TLS profile actually changed
			return !reflect.DeepEqual(
				oldAPI.Spec.TLSSecurityProfile,
				newAPI.Spec.TLSSecurityProfile,
			)
		},
		DeleteFunc: func(e event.DeleteEvent) bool {
			return false
		},
		GenericFunc: func(e event.GenericEvent) bool {
			return false
		},
	}
}

func (r *MyOperandReconciler) reconcileOperandTLS(
	ctx context.Context,
	operand *myv1.MyOperand,
	profile *configv1.TLSSecurityProfile,
) error {
	// Update operand deployment with new TLS settings
	// For example, update an annotation to trigger rolling restart:
	//
	// deployment.Spec.Template.Annotations["tls-profile-hash"] = hashTLSProfile(profile)
	//
	// Or update a ConfigMap/Secret that the operand mounts
	return nil
}

This approach is efficient because:

  • Uses predicates to filter only TLS profile changes (ignores other APIServer updates)
  • Integrates with existing controller logic
  • Automatically reconciles all operands when the profile changes
  • Follows standard controller-runtime patterns

Option B: Dynamic TLS Config Update

For servers that support dynamic TLS config, use GetConfigForClient:

go
package tlsprofile

import (
	"context"
	"crypto/tls"
	"sync"
	"time"

	configv1 "github.com/openshift/api/config/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

// DynamicTLSConfig provides a TLS config that updates when the profile changes
type DynamicTLSConfig struct {
	client    client.Client
	mu        sync.RWMutex
	current   *tls.Config
	lastCheck time.Time
	cacheTTL  time.Duration
}

func NewDynamicTLSConfig(c client.Client) *DynamicTLSConfig {
	return &DynamicTLSConfig{
		client:   c,
		cacheTTL: 30 * time.Second,
	}
}

// GetConfigForClient returns a TLS config callback for dynamic updates
func (d *DynamicTLSConfig) GetConfigForClient(hello *tls.ClientHelloInfo) (*tls.Config, error) {
	d.mu.RLock()
	if d.current != nil && time.Since(d.lastCheck) < d.cacheTTL {
		defer d.mu.RUnlock()
		return d.current, nil
	}
	d.mu.RUnlock()

	// Refresh TLS config from cluster
	d.mu.Lock()
	defer d.mu.Unlock()

	profile, err := GetTLSSecurityProfile(context.Background(), d.client)
	if err != nil {
		if d.current != nil {
			return d.current, nil // Use cached config on error
		}
		return nil, err
	}

	d.current, err = TLSConfigFromProfile(profile)
	if err != nil {
		return nil, err
	}
	d.lastCheck = time.Now()

	return d.current, nil
}

// BaseTLSConfig returns a tls.Config that uses dynamic configuration
func (d *DynamicTLSConfig) BaseTLSConfig() *tls.Config {
	return &tls.Config{
		GetConfigForClient: d.GetConfigForClient,
	}
}

Note: Option A (watch and reconcile) is recommended for most operators because:

  • TLS configuration is typically set at server startup
  • Webhook and metrics servers in controller-runtime don't support hot-reload of TLS settings
  • Triggering operand restarts ensures clean application of new settings

TLS Profile Types

OpenShift supports four TLS profile types based on Mozilla's Server Side TLS recommendations:

ProfileMin TLS VersionDescription
OldTLS 1.0Legacy compatibility, not recommended for production
IntermediateTLS 1.2Recommended for general use, balances security and compatibility
ModernTLS 1.3Highest security, may not work with older clients
CustomConfigurableUser-defined ciphers and minimum TLS version

Note: In Go, cipher suites are not configurable for TLS 1.3 - they are automatically selected by the runtime.

APIServer Custom Resource

The TLS profile is configured in the APIServer custom resource named cluster:

yaml
apiVersion: config.openshift.io/v1
kind: APIServer
metadata:
  name: cluster
spec:
  audit:
    profile: Default
  tlsSecurityProfile:
    # type can be: Old, Intermediate, Modern, or Custom
    type: Intermediate
    # Only one of the following should be set based on type:
    old: {}
    intermediate: {}
    modern: {}
    custom:
      ciphers:
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
      minTLSVersion: VersionTLS12

Reference:

Query Commands

Check the current TLS security profile in your cluster:

bash
# Get the full APIServer configuration
oc get apiserver cluster -o yaml

# Get just the TLS security profile
oc get apiserver cluster -o jsonpath='{.spec.tlsSecurityProfile}' | jq .

# Check the effective TLS profile type
oc get apiserver cluster -o jsonpath='{.spec.tlsSecurityProfile.type}'

Implementation Steps

Step 1: Fetch TLS Profile from APIServer CR

Create a function to retrieve the TLS security profile from the cluster:

go
package tlsprofile

import (
	"context"
	"fmt"

	configv1 "github.com/openshift/api/config/v1"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

// GetTLSSecurityProfile fetches the TLS security profile from the APIServer CR
func GetTLSSecurityProfile(ctx context.Context, c client.Client) (*configv1.TLSSecurityProfile, error) {
	apiServer := &configv1.APIServer{}
	if err := c.Get(ctx, types.NamespacedName{Name: "cluster"}, apiServer); err != nil {
		return nil, fmt.Errorf("failed to get APIServer cluster: %w", err)
	}

	if apiServer.Spec.TLSSecurityProfile == nil {
		// Return default Intermediate profile if not set
		return &configv1.TLSSecurityProfile{
			Type:         configv1.TLSProfileIntermediateType,
			Intermediate: &configv1.IntermediateTLSProfile{},
		}, nil
	}

	return apiServer.Spec.TLSSecurityProfile, nil
}

Step 2: Convert TLS Profile to Go crypto/tls Configuration

Create a conversion function that transforms the OpenShift TLS profile into Go's tls.Config:

go
package tlsprofile

import (
	"crypto/tls"
	"fmt"

	configv1 "github.com/openshift/api/config/v1"
	"github.com/openshift/library-go/pkg/crypto"
)

// TLSConfigFromProfile converts an OpenShift TLS security profile to a tls.Config
func TLSConfigFromProfile(profile *configv1.TLSSecurityProfile) (*tls.Config, error) {
	if profile == nil {
		profile = &configv1.TLSSecurityProfile{
			Type:         configv1.TLSProfileIntermediateType,
			Intermediate: &configv1.IntermediateTLSProfile{},
		}
	}

	var minVersion configv1.TLSProtocolVersion
	var ciphers []string

	switch profile.Type {
	case configv1.TLSProfileOldType:
		minVersion = configv1.TLSProfiles[configv1.TLSProfileOldType].MinTLSVersion
		ciphers = configv1.TLSProfiles[configv1.TLSProfileOldType].Ciphers
	case configv1.TLSProfileIntermediateType:
		minVersion = configv1.TLSProfiles[configv1.TLSProfileIntermediateType].MinTLSVersion
		ciphers = configv1.TLSProfiles[configv1.TLSProfileIntermediateType].Ciphers
	case configv1.TLSProfileModernType:
		minVersion = configv1.TLSProfiles[configv1.TLSProfileModernType].MinTLSVersion
		ciphers = configv1.TLSProfiles[configv1.TLSProfileModernType].Ciphers
	case configv1.TLSProfileCustomType:
		if profile.Custom == nil {
			return nil, fmt.Errorf("custom TLS profile specified but no custom configuration provided")
		}
		minVersion = profile.Custom.MinTLSVersion
		ciphers = profile.Custom.Ciphers
	default:
		return nil, fmt.Errorf("unknown TLS profile type: %s", profile.Type)
	}

	tlsMinVersion, err := tlsVersionToUint16(minVersion)
	if err != nil {
		return nil, err
	}

	// Convert OpenSSL cipher names to IANA names, then to Go constants
	ianaCiphers := crypto.OpenSSLToIANACipherSuites(ciphers)
	cipherSuites := crypto.CipherSuitesOrDie(ianaCiphers)

	return &tls.Config{
		MinVersion:   tlsMinVersion,
		CipherSuites: cipherSuites,
	}, nil
}

// tlsVersionToUint16 converts OpenShift TLS version string to Go tls constant
func tlsVersionToUint16(version configv1.TLSProtocolVersion) (uint16, error) {
	switch version {
	case configv1.VersionTLS10:
		return tls.VersionTLS10, nil
	case configv1.VersionTLS11:
		return tls.VersionTLS11, nil
	case configv1.VersionTLS12:
		return tls.VersionTLS12, nil
	case configv1.VersionTLS13:
		return tls.VersionTLS13, nil
	default:
		return 0, fmt.Errorf("unknown TLS version: %s", version)
	}
}

Step 3: Apply to Controller-Runtime Webhook and Metrics Servers

Configure the controller-runtime manager with the TLS settings:

go
package main

import (
	"context"
	"crypto/tls"
	"os"

	configv1 "github.com/openshift/api/config/v1"
	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
	"sigs.k8s.io/controller-runtime/pkg/webhook"

	"myoperator/pkg/tlsprofile"
)

var scheme = runtime.NewScheme()

func init() {
	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
	utilruntime.Must(configv1.AddToScheme(scheme))
}

func main() {
	ctx := context.Background()

	// Create a temporary client to fetch TLS profile
	cfg := ctrl.GetConfigOrDie()
	tempClient, err := client.New(cfg, client.Options{Scheme: scheme})
	if err != nil {
		os.Exit(1)
	}

	// Fetch and convert TLS profile
	profile, err := tlsprofile.GetTLSSecurityProfile(ctx, tempClient)
	if err != nil {
		os.Exit(1)
	}

	tlsConfig, err := tlsprofile.TLSConfigFromProfile(profile)
	if err != nil {
		os.Exit(1)
	}

	// Create TLS options function
	tlsOpts := func(config *tls.Config) {
		config.MinVersion = tlsConfig.MinVersion
		config.CipherSuites = tlsConfig.CipherSuites
	}

	mgr, err := ctrl.NewManager(cfg, ctrl.Options{
		Scheme: scheme,
		Metrics: metricsserver.Options{
			BindAddress: ":8443",
			SecureServing: true,
			TLSOpts:     []func(*tls.Config){tlsOpts},
		},
		WebhookServer: webhook.NewServer(webhook.Options{
			Port:    9443,
			TLSOpts: []func(*tls.Config){tlsOpts},
		}),
	})
	if err != nil {
		os.Exit(1)
	}

	if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
		os.Exit(1)
	}
}

Step 4: Apply to HTTP Client

Configure an HTTP client with the TLS profile:

go
package httpclient

import (
	"crypto/tls"
	"crypto/x509"
	"net/http"
	"os"
	"time"

	configv1 "github.com/openshift/api/config/v1"
	"myoperator/pkg/tlsprofile"
)

// NewHTTPClientWithTLSProfile creates an HTTP client configured with the TLS profile
func NewHTTPClientWithTLSProfile(profile *configv1.TLSSecurityProfile, caCertPath string) (*http.Client, error) {
	tlsConfig, err := tlsprofile.TLSConfigFromProfile(profile)
	if err != nil {
		return nil, err
	}

	// Load CA certificate if provided
	if caCertPath != "" {
		caCert, err := os.ReadFile(caCertPath)
		if err != nil {
			return nil, err
		}
		caCertPool := x509.NewCertPool()
		caCertPool.AppendCertsFromPEM(caCert)
		tlsConfig.RootCAs = caCertPool
	}

	return &http.Client{
		Timeout: 30 * time.Second,
		Transport: &http.Transport{
			TLSClientConfig: tlsConfig,
		},
	}, nil
}

Step 5: Apply to HTTP Server

Configure an HTTP server with the TLS profile:

go
package httpserver

import (
	"crypto/tls"
	"net/http"

	configv1 "github.com/openshift/api/config/v1"
	"myoperator/pkg/tlsprofile"
)

// NewHTTPServerWithTLSProfile creates an HTTP server configured with the TLS profile
func NewHTTPServerWithTLSProfile(
	addr string,
	handler http.Handler,
	profile *configv1.TLSSecurityProfile,
	certFile, keyFile string,
) (*http.Server, error) {
	tlsConfig, err := tlsprofile.TLSConfigFromProfile(profile)
	if err != nil {
		return nil, err
	}

	// Add recommended server settings
	// Note: PreferServerCipherSuites is deprecated in Go 1.18+ and ignored
	tlsConfig.CurvePreferences = []tls.CurveID{tls.CurveP256, tls.X25519}

	server := &http.Server{
		Addr:      addr,
		Handler:   handler,
		TLSConfig: tlsConfig,
		// Disable HTTP/2 if not needed
		TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
	}

	return server, nil
}

// Example usage:
// server, err := NewHTTPServerWithTLSProfile(":8443", mux, profile, "server.crt", "server.key")
// if err != nil { ... }
// err = server.ListenAndServeTLS("server.crt", "server.key")

Step 6: Apply to gRPC Client

Configure a gRPC client with the TLS profile:

go
package grpcclient

import (
	"crypto/tls"
	"crypto/x509"
	"os"

	configv1 "github.com/openshift/api/config/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"myoperator/pkg/tlsprofile"
)

// NewGRPCClientConnWithTLSProfile creates a gRPC client connection with the TLS profile
func NewGRPCClientConnWithTLSProfile(
	target string,
	profile *configv1.TLSSecurityProfile,
	caCertPath string,
) (*grpc.ClientConn, error) {
	tlsConfig, err := tlsprofile.TLSConfigFromProfile(profile)
	if err != nil {
		return nil, err
	}

	// Load CA certificate
	caCert, err := os.ReadFile(caCertPath)
	if err != nil {
		return nil, err
	}
	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)
	tlsConfig.RootCAs = caCertPool

	creds := credentials.NewTLS(tlsConfig)
	conn, err := grpc.Dial(target, grpc.WithTransportCredentials(creds))
	if err != nil {
		return nil, err
	}

	return conn, nil
}

Step 7: Apply to gRPC Server

Configure a gRPC server with the TLS profile:

go
package grpcserver

import (
	"crypto/tls"

	configv1 "github.com/openshift/api/config/v1"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"

	"myoperator/pkg/tlsprofile"
)

// NewGRPCServerWithTLSProfile creates a gRPC server configured with the TLS profile
func NewGRPCServerWithTLSProfile(
	profile *configv1.TLSSecurityProfile,
	certFile, keyFile string,
) (*grpc.Server, error) {
	tlsConfig, err := tlsprofile.TLSConfigFromProfile(profile)
	if err != nil {
		return nil, err
	}

	// Load server certificate
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	}
	tlsConfig.Certificates = []tls.Certificate{cert}

	creds := credentials.NewTLS(tlsConfig)
	server := grpc.NewServer(grpc.Creds(creds))

	return server, nil
}

OpenShift library-go Crypto Utilities

The github.com/openshift/library-go/pkg/crypto package provides essential utilities for working with TLS in OpenShift. These functions simplify converting between OpenShift TLS profile configurations and Go's crypto/tls types.

Why Use library-go/pkg/crypto

  • Cipher suite name conversion: OpenShift's configv1.TLSProfiles uses OpenSSL-format cipher names, not Go constants
  • Validated defaults: Default ciphers align with Mozilla's Intermediate TLS profile
  • Consistent behavior: Same utilities used by OpenShift core components

Key Functions

FunctionPurpose
TLSVersion(name string) (uint16, error)Convert TLS version name (e.g., "VersionTLS12") to Go constant
TLSVersionOrDie(name string) uint16Same as above, panics on error
DefaultTLSVersion() uint16Returns default TLS version (TLS 1.2)
CipherSuite(name string) (uint16, error)Convert cipher suite name to Go constant
CipherSuitesOrDie(names []string) []uint16Convert multiple cipher names, panics on error
OpenSSLToIANACipherSuites(ciphers []string) []stringMap OpenSSL cipher names to IANA names
SecureTLSConfig(config *tls.Config) *tls.ConfigApply secure defaults to a TLS config
DefaultCiphers() []uint16Get default cipher suites for Intermediate profile

Example: Using library-go for Cipher Conversion

The configv1.TLSProfiles map contains cipher names in OpenSSL format. Use OpenSSLToIANACipherSuites to convert them:

go
package tlsprofile

import (
	"crypto/tls"
	"fmt"

	configv1 "github.com/openshift/api/config/v1"
	"github.com/openshift/library-go/pkg/crypto"
)

// TLSConfigFromProfileWithLibraryGo uses library-go utilities for conversion
func TLSConfigFromProfileWithLibraryGo(profile *configv1.TLSSecurityProfile) (*tls.Config, error) {
	if profile == nil {
		// Use library-go defaults
		return &tls.Config{
			MinVersion:   crypto.DefaultTLSVersion(),
			CipherSuites: crypto.DefaultCiphers(),
		}, nil
	}

	var minVersionStr string
	var openSSLCiphers []string

	switch profile.Type {
	case configv1.TLSProfileOldType:
		spec := configv1.TLSProfiles[configv1.TLSProfileOldType]
		minVersionStr = string(spec.MinTLSVersion)
		openSSLCiphers = spec.Ciphers
	case configv1.TLSProfileIntermediateType:
		spec := configv1.TLSProfiles[configv1.TLSProfileIntermediateType]
		minVersionStr = string(spec.MinTLSVersion)
		openSSLCiphers = spec.Ciphers
	case configv1.TLSProfileModernType:
		spec := configv1.TLSProfiles[configv1.TLSProfileModernType]
		minVersionStr = string(spec.MinTLSVersion)
		openSSLCiphers = spec.Ciphers
	case configv1.TLSProfileCustomType:
		if profile.Custom == nil {
			return nil, fmt.Errorf("custom profile without configuration")
		}
		minVersionStr = string(profile.Custom.MinTLSVersion)
		openSSLCiphers = profile.Custom.Ciphers
	default:
		return nil, fmt.Errorf("unknown profile type: %s", profile.Type)
	}

	// Use library-go to convert TLS version
	minVersion, err := crypto.TLSVersion(minVersionStr)
	if err != nil {
		return nil, fmt.Errorf("invalid TLS version %s: %w", minVersionStr, err)
	}

	// Convert OpenSSL cipher names to IANA names, then to Go constants
	ianaCiphers := crypto.OpenSSLToIANACipherSuites(openSSLCiphers)
	cipherSuites := crypto.CipherSuitesOrDie(ianaCiphers)

	return &tls.Config{
		MinVersion:   minVersion,
		CipherSuites: cipherSuites,
	}, nil
}

Using SecureTLSConfig for Additional Hardening

Apply SecureTLSConfig to enforce secure defaults on any TLS configuration:

go
package main

import (
	"crypto/tls"

	"github.com/openshift/library-go/pkg/crypto"
)

func createSecureTLSConfig() *tls.Config {
	// Start with custom or empty config
	config := &tls.Config{
		// Your settings here
	}

	// Apply library-go secure defaults
	// This enforces minimum security settings used by OpenShift
	return crypto.SecureTLSConfig(config)
}

Additional Resources