|
1 | 1 | package validation
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "crypto/tls" |
| 5 | + "crypto/x509" |
| 6 | + "encoding/pem" |
4 | 7 | "fmt"
|
5 | 8 | "strings"
|
6 | 9 |
|
@@ -76,6 +79,60 @@ func ValidateRouteStatusUpdate(route *routeapi.Route, older *routeapi.Route) fie
|
76 | 79 | return allErrs
|
77 | 80 | }
|
78 | 81 |
|
| 82 | +// ExtendedValidateRoute performs an extended validation on the route |
| 83 | +// including checking that the TLS config is valid. |
| 84 | +func ExtendedValidateRoute(route *routeapi.Route) field.ErrorList { |
| 85 | + tlsConfig := route.Spec.TLS |
| 86 | + result := field.ErrorList{} |
| 87 | + |
| 88 | + tlsFieldPath := field.NewPath("spec").Child("tls") |
| 89 | + if errs := validateTLS(route, tlsFieldPath); len(errs) != 0 { |
| 90 | + result = append(result, errs...) |
| 91 | + } |
| 92 | + |
| 93 | + // TODO: Check if we can be stricter with validating the certificate |
| 94 | + // is for the route hostname. Don't want existing routes to |
| 95 | + // break, so disable the hostname validation for now. |
| 96 | + // hostname := route.Spec.Host |
| 97 | + hostname := "" |
| 98 | + var certPool *x509.CertPool |
| 99 | + |
| 100 | + if len(tlsConfig.CACertificate) > 0 { |
| 101 | + certPool = x509.NewCertPool() |
| 102 | + if ok := certPool.AppendCertsFromPEM([]byte(tlsConfig.CACertificate)); !ok { |
| 103 | + result = append(result, field.Invalid(tlsFieldPath.Child("caCertificate"), tlsConfig.CACertificate, "failed to parse CA certificate")) |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + verifyOptions := &x509.VerifyOptions{ |
| 108 | + DNSName: hostname, |
| 109 | + Roots: certPool, |
| 110 | + } |
| 111 | + |
| 112 | + if len(tlsConfig.Certificate) > 0 { |
| 113 | + if _, err := validateCertificatePEM(tlsConfig.Certificate, verifyOptions); err != nil { |
| 114 | + result = append(result, field.Invalid(tlsFieldPath.Child("certificate"), tlsConfig.Certificate, err.Error())) |
| 115 | + } |
| 116 | + } |
| 117 | + |
| 118 | + if len(tlsConfig.Key) > 0 { |
| 119 | + if len(tlsConfig.Certificate) > 0 { |
| 120 | + if _, err := tls.X509KeyPair([]byte(tlsConfig.Certificate), []byte(tlsConfig.Key)); err != nil { |
| 121 | + result = append(result, field.Invalid(tlsFieldPath.Child("key"), tlsConfig.Key, err.Error())) |
| 122 | + } |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + if len(tlsConfig.DestinationCACertificate) > 0 { |
| 127 | + roots := x509.NewCertPool() |
| 128 | + if ok := roots.AppendCertsFromPEM([]byte(tlsConfig.DestinationCACertificate)); !ok { |
| 129 | + result = append(result, field.Invalid(tlsFieldPath.Child("destinationCACertificate"), tlsConfig.DestinationCACertificate, "failed to parse destination CA certificate")) |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + return result |
| 134 | +} |
| 135 | + |
79 | 136 | // validateTLS tests fields for different types of TLS combinations are set. Called
|
80 | 137 | // by ValidateRoute.
|
81 | 138 | func validateTLS(route *routeapi.Route, fldPath *field.Path) field.ErrorList {
|
@@ -179,3 +236,37 @@ func validateInsecureEdgeTerminationPolicy(tls *routeapi.TLSConfig, fldPath *fie
|
179 | 236 |
|
180 | 237 | return nil
|
181 | 238 | }
|
| 239 | + |
| 240 | +// validateCertificatePEM checks if a certificate PEM is valid and |
| 241 | +// optionally verifies the certificate using the options. |
| 242 | +func validateCertificatePEM(certPEM string, options *x509.VerifyOptions) (*x509.Certificate, error) { |
| 243 | + var data *pem.Block |
| 244 | + for remaining := []byte(certPEM); len(remaining) > 0; { |
| 245 | + block, rest := pem.Decode(remaining) |
| 246 | + if block == nil { |
| 247 | + return nil, fmt.Errorf("error decoding certificate data") |
| 248 | + } |
| 249 | + if block.Type == "CERTIFICATE" { |
| 250 | + data = block |
| 251 | + } |
| 252 | + remaining = rest |
| 253 | + } |
| 254 | + |
| 255 | + if data == nil || len(data.Bytes) < 1 { |
| 256 | + return nil, fmt.Errorf("invalid/empty certificate data") |
| 257 | + } |
| 258 | + |
| 259 | + cert, err := x509.ParseCertificate(data.Bytes) |
| 260 | + if err != nil { |
| 261 | + return nil, fmt.Errorf("error parsing certificate: %s", err.Error()) |
| 262 | + } |
| 263 | + |
| 264 | + if options != nil { |
| 265 | + _, err = cert.Verify(*options) |
| 266 | + if err != nil { |
| 267 | + return cert, fmt.Errorf("error verifying certificate: %s", err.Error()) |
| 268 | + } |
| 269 | + } |
| 270 | + |
| 271 | + return cert, nil |
| 272 | +} |
0 commit comments