8484 "TLS_AES_128_CCM_SHA256" ,
8585 "TLS_AES_128_CCM_8_SHA256" ,
8686 )
87+
88+ // fipsApprovedTLS13Ciphers is the set of TLS 1.3 cipher suites that are
89+ // FIPS-approved. These are kept in ROUTER_CIPHERSUITES when the operator
90+ // is running in FIPS mode, while others are filtered out.
91+ fipsApprovedTLS13Ciphers = sets .NewString (
92+ "TLS_AES_128_GCM_SHA256" ,
93+ "TLS_AES_256_GCM_SHA384" ,
94+ )
8795)
8896
8997// New creates the ingress controller from configuration. This is the controller
@@ -356,7 +364,7 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
356364 // successful, immediately re-queue to refresh state.
357365 alreadyAdmitted := ingresscontroller .IsAdmitted (ingress )
358366 if ! alreadyAdmitted || needsReadmission (ingress ) {
359- if err := r .admit (ingress , ingressConfig , platformStatus , dnsConfig , alreadyAdmitted ); err != nil {
367+ if err := r .admit (ingress , ingressConfig , platformStatus , dnsConfig , apiConfig , alreadyAdmitted ); err != nil {
360368 switch err := err .(type ) {
361369 case * admissionRejection :
362370 log .Info ("IngressController rejected" , "namespace" , ingress .Namespace , "name" , ingress .Name , "reason" , err .Reason )
@@ -389,7 +397,7 @@ func (r *reconciler) Reconcile(ctx context.Context, request reconcile.Request) (
389397// fields. Returns an error value, which will have a non-nil value of type
390398// admissionRejection if the ingresscontroller was rejected, or a non-nil
391399// value of a different type if the ingresscontroller could not be processed.
392- func (r * reconciler ) admit (current * operatorv1.IngressController , ingressConfig * configv1.Ingress , platformStatus * configv1.PlatformStatus , dnsConfig * configv1.DNS , alreadyAdmitted bool ) error {
400+ func (r * reconciler ) admit (current * operatorv1.IngressController , ingressConfig * configv1.Ingress , platformStatus * configv1.PlatformStatus , dnsConfig * configv1.DNS , apiConfig * configv1. APIServer , alreadyAdmitted bool ) error {
393401 updated := current .DeepCopy ()
394402
395403 setDefaultDomain (updated , ingressConfig )
@@ -404,7 +412,7 @@ func (r *reconciler) admit(current *operatorv1.IngressController, ingressConfig
404412 // get the default from the APIServer config (which is assumed to be
405413 // valid).
406414
407- if err := r .validate (updated ); err != nil {
415+ if err := r .validate (updated , apiConfig ); err != nil {
408416 switch err := err .(type ) {
409417 case * admissionRejection :
410418 updated .Status .Conditions = MergeConditions (updated .Status .Conditions , operatorv1.OperatorCondition {
@@ -905,7 +913,7 @@ func hasTLSSecurityProfile(ic *operatorv1.IngressController) bool {
905913// returns an error value, which will have a non-nil value of type
906914// admissionRejection if the ingresscontroller is invalid, or a non-nil value of
907915// a different type if validation could not be completed.
908- func (r * reconciler ) validate (ic * operatorv1.IngressController ) error {
916+ func (r * reconciler ) validate (ic * operatorv1.IngressController , apiConfig * configv1. APIServer ) error {
909917 var errors []error
910918
911919 ingresses := & operatorv1.IngressControllerList {}
@@ -919,7 +927,7 @@ func (r *reconciler) validate(ic *operatorv1.IngressController) error {
919927 if err := validateDomainUniqueness (ic , ingresses .Items ); err != nil {
920928 errors = append (errors , err )
921929 }
922- if err := validateTLSSecurityProfile (ic ); err != nil {
930+ if err := validateTLSSecurityProfile (ic , apiConfig ); err != nil {
923931 errors = append (errors , err )
924932 }
925933 if err := validateHTTPHeaderBufferValues (ic ); err != nil {
@@ -972,49 +980,76 @@ var (
972980)
973981
974982// validateTLSSecurityProfile validates the given ingresscontroller's TLS
975- // security profile, if it specifies one.
976- func validateTLSSecurityProfile (ic * operatorv1.IngressController ) error {
977- if ! hasTLSSecurityProfile (ic ) {
978- return nil
979- }
983+ // security profile. It resolves the effective profile (which may be inherited
984+ // from the APIServer config) and ensures it is properly configured and FIPS-compliant.
985+ func validateTLSSecurityProfile (ic * operatorv1.IngressController , apiConfig * configv1.APIServer ) error {
986+ var errs []error
980987
981- if ic . Spec . TLSSecurityProfile . Type != configv1 . TLSProfileCustomType {
982- return nil
988+ if apiConfig == nil {
989+ apiConfig = & configv1. APIServer {}
983990 }
984991
985- spec := ic .Spec .TLSSecurityProfile .Custom
986- if spec == nil {
987- return fmt .Errorf ("security profile is not defined" )
992+ // 1. Figure out which profile is in effect.
993+ var effectiveProfile * configv1.TLSSecurityProfile
994+ if hasTLSSecurityProfile (ic ) {
995+ effectiveProfile = ic .Spec .TLSSecurityProfile
996+ } else {
997+ effectiveProfile = apiConfig .Spec .TLSSecurityProfile
988998 }
989999
990- var errs []error
1000+ // 2. Validate the effective profile.
1001+ if effectiveProfile != nil && effectiveProfile .Type == configv1 .TLSProfileCustomType {
1002+ spec := effectiveProfile .Custom
1003+ if spec == nil {
1004+ return fmt .Errorf ("security profile is not defined" )
1005+ }
9911006
992- if len (spec .Ciphers ) == 0 {
993- errs = append (errs , fmt .Errorf ("security profile has an empty ciphers list" ))
994- } else {
995- invalidCiphers := []string {}
996- for _ , cipher := range spec .Ciphers {
997- if ! isValidCipher (strings .TrimPrefix (cipher , "!" )) {
998- invalidCiphers = append (invalidCiphers , cipher )
1007+ if len (spec .Ciphers ) == 0 {
1008+ errs = append (errs , fmt .Errorf ("security profile has an empty ciphers list" ))
1009+ } else {
1010+ invalidCiphers := []string {}
1011+ for _ , cipher := range spec .Ciphers {
1012+ if ! isValidCipher (strings .TrimPrefix (cipher , "!" )) {
1013+ invalidCiphers = append (invalidCiphers , cipher )
1014+ }
9991015 }
1000- }
1001- if len (invalidCiphers ) != 0 {
1002- errs = append (errs , fmt .Errorf ("security profile has invalid ciphers: %s" , strings .Join (invalidCiphers , ", " )))
1003- }
1004- switch spec .MinTLSVersion {
1005- case configv1 .VersionTLS10 , configv1 .VersionTLS11 , configv1 .VersionTLS12 :
1006- if tlsVersion13Ciphers .HasAll (spec .Ciphers ... ) {
1007- errs = append (errs , fmt .Errorf ("security profile specifies minTLSVersion: %s and contains only TLSv1.3 cipher suites" , spec .MinTLSVersion ))
1016+ if len (invalidCiphers ) != 0 {
1017+ errs = append (errs , fmt .Errorf ("security profile has invalid ciphers: %s" , strings .Join (invalidCiphers , ", " )))
10081018 }
1009- case configv1 .VersionTLS13 :
1010- if ! tlsVersion13Ciphers .HasAny (spec .Ciphers ... ) {
1011- errs = append (errs , fmt .Errorf ("security profile specifies minTLSVersion: %s and contains no TLSv1.3 cipher suites" , spec .MinTLSVersion ))
1019+ switch spec .MinTLSVersion {
1020+ case configv1 .VersionTLS10 , configv1 .VersionTLS11 , configv1 .VersionTLS12 :
1021+ if tlsVersion13Ciphers .HasAll (spec .Ciphers ... ) {
1022+ errs = append (errs , fmt .Errorf ("security profile specifies minTLSVersion: %s and contains only TLSv1.3 cipher suites" , spec .MinTLSVersion ))
1023+ }
1024+ case configv1 .VersionTLS13 :
1025+ if ! tlsVersion13Ciphers .HasAny (spec .Ciphers ... ) {
1026+ errs = append (errs , fmt .Errorf ("security profile specifies minTLSVersion: %s and contains no TLSv1.3 cipher suites" , spec .MinTLSVersion ))
1027+ }
10121028 }
10131029 }
1030+
1031+ if _ , ok := validTLSVersions [spec .MinTLSVersion ]; ! ok {
1032+ errs = append (errs , fmt .Errorf ("security profile has invalid minimum security protocol version: %q" , spec .MinTLSVersion ))
1033+ }
10141034 }
10151035
1016- if _ , ok := validTLSVersions [spec .MinTLSVersion ]; ! ok {
1017- errs = append (errs , fmt .Errorf ("security profile has invalid minimum security protocol version: %q" , spec .MinTLSVersion ))
1036+ // On FIPS-enabled clusters, non-FIPS TLS 1.3 ciphers are filtered from
1037+ // ROUTER_CIPHERSUITES. If the effective profile's only TLS 1.3 cipher suites
1038+ // are non-FIPS, they would all be removed, leaving no TLS 1.3 ciphers
1039+ // configured. Reject such profiles with a clear error message.
1040+ if isFIPSEnabled {
1041+ resolvedSpec := tlsProfileSpecForIngressController (ic , apiConfig )
1042+ tls13InProfile := tlsVersion13Ciphers .Intersection (sets .NewString (resolvedSpec .Ciphers ... ))
1043+ if tls13InProfile .Len () > 0 && ! tls13InProfile .HasAny (fipsApprovedTLS13Ciphers .UnsortedList ()... ) {
1044+ errs = append (errs , fmt .Errorf (
1045+ "security profile's TLS 1.3 cipher suites (%s) are not FIPS-compliant" +
1046+ " and will be removed on this FIPS-enabled cluster," +
1047+ " leaving no TLS 1.3 ciphers configured;" +
1048+ " specify at least one FIPS-compliant TLS 1.3 cipher suite" +
1049+ " (e.g. TLS_AES_128_GCM_SHA256 or TLS_AES_256_GCM_SHA384)" +
1050+ " or remove the non-FIPS cipher suites from the profile" ,
1051+ strings .Join (tls13InProfile .List (), ", " )))
1052+ }
10181053 }
10191054
10201055 return utilerrors .NewAggregate (errs )
0 commit comments