// validateCertificatePEM checks if a certificate PEM is valid and // optionally verifies the certificate using the options. func validateCertificatePEM(certPEM string, options *x509.VerifyOptions) ([]*x509.Certificate, error) { certs, err := cmdutil.CertificatesFromPEM([]byte(certPEM)) if err != nil { return nil, err } if len(certs) < 1 { return nil, fmt.Errorf("invalid/empty certificate data") } if options != nil { // Ensure we don't report errors for expired certs or if // the validity is in the future. // Not that this can be for the actual certificate or any // intermediates in the CA chain. This allows the router to // still serve an expired/valid-in-the-future certificate // and lets the client to control if it can tolerate that // (just like for self-signed certs). _, err = certs[0].Verify(*options) if err != nil { if invalidErr, ok := err.(x509.CertificateInvalidError); !ok || invalidErr.Reason != x509.Expired { return certs, fmt.Errorf("error verifying certificate: %s", err.Error()) } } } return certs, nil }
func GetTLSCertificateConfig(certFile, keyFile string) (*TLSCertificateConfig, error) { if len(certFile) == 0 { return nil, errors.New("certFile missing") } if len(keyFile) == 0 { return nil, errors.New("keyFile missing") } certPEMBlock, err := ioutil.ReadFile(certFile) if err != nil { return nil, err } certs, err := cmdutil.CertificatesFromPEM(certPEMBlock) if err != nil { return nil, fmt.Errorf("Error reading %s: %s", certFile, err) } keyPEMBlock, err := ioutil.ReadFile(keyFile) if err != nil { return nil, err } keyPairCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) if err != nil { return nil, err } key := keyPairCert.PrivateKey return &TLSCertificateConfig{certs, key}, nil }
func GetTLSCARoots(caFile string) (*TLSCARoots, error) { if len(caFile) == 0 { return nil, errors.New("caFile missing") } caPEMBlock, err := ioutil.ReadFile(caFile) if err != nil { return nil, err } roots, err := cmdutil.CertificatesFromPEM(caPEMBlock) if err != nil { return nil, fmt.Errorf("Error reading %s: %s", caFile, err) } return &TLSCARoots{roots}, nil }
// ExtendedValidateRoute performs an extended validation on the route // including checking that the TLS config is valid. func ExtendedValidateRoute(route *routeapi.Route) field.ErrorList { tlsConfig := route.Spec.TLS result := field.ErrorList{} if tlsConfig == nil { return result } tlsFieldPath := field.NewPath("spec").Child("tls") if errs := validateTLS(route, tlsFieldPath); len(errs) != 0 { result = append(result, errs...) } // TODO: Check if we can be stricter with validating the certificate // is for the route hostname. Don't want existing routes to // break, so disable the hostname validation for now. // hostname := route.Spec.Host hostname := "" var verifyOptions *x509.VerifyOptions if len(tlsConfig.CACertificate) > 0 { certPool := x509.NewCertPool() if certs, err := cmdutil.CertificatesFromPEM([]byte(tlsConfig.CACertificate)); err != nil { errmsg := fmt.Sprintf("failed to parse CA certificate: %v", err) result = append(result, field.Invalid(tlsFieldPath.Child("caCertificate"), "<ca certificate data>", errmsg)) } else { for _, cert := range certs { certPool.AddCert(cert) } } verifyOptions = &x509.VerifyOptions{ DNSName: hostname, Intermediates: certPool, Roots: certPool, } } if len(tlsConfig.Certificate) > 0 { if _, err := validateCertificatePEM(tlsConfig.Certificate, verifyOptions); err != nil { result = append(result, field.Invalid(tlsFieldPath.Child("certificate"), "<certificate data>", err.Error())) } certKeyBytes := []byte{} certKeyBytes = append(certKeyBytes, []byte(tlsConfig.Certificate)...) if len(tlsConfig.Key) > 0 { certKeyBytes = append(certKeyBytes, byte('\n')) certKeyBytes = append(certKeyBytes, []byte(tlsConfig.Key)...) } if _, err := tls.X509KeyPair(certKeyBytes, certKeyBytes); err != nil { result = append(result, field.Invalid(tlsFieldPath.Child("key"), "<key data>", err.Error())) } } if len(tlsConfig.DestinationCACertificate) > 0 { if _, err := cmdutil.CertificatesFromPEM([]byte(tlsConfig.DestinationCACertificate)); err != nil { errmsg := fmt.Sprintf("failed to parse destination CA certificate: %v", err) result = append(result, field.Invalid(tlsFieldPath.Child("destinationCACertificate"), "<destination ca certificate data>", errmsg)) } } return result }