2017-11-09 12:16:03 +01:00
package tls
import (
"crypto/tls"
"crypto/x509"
2021-07-15 17:32:11 +05:30
"errors"
2017-11-09 12:16:03 +01:00
"fmt"
2021-07-15 17:32:11 +05:30
"net/url"
2017-11-09 12:16:03 +01:00
"os"
"sort"
"strings"
2020-09-16 15:46:04 +02:00
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/tls/generate"
2017-11-09 12:16:03 +01:00
)
var (
2020-05-11 12:06:07 +02:00
// MinVersion Map of allowed TLS minimum versions.
2017-11-09 12:16:03 +01:00
MinVersion = map [ string ] uint16 {
` VersionTLS10 ` : tls . VersionTLS10 ,
` VersionTLS11 ` : tls . VersionTLS11 ,
` VersionTLS12 ` : tls . VersionTLS12 ,
2019-03-01 11:48:04 +01:00
` VersionTLS13 ` : tls . VersionTLS13 ,
2017-11-09 12:16:03 +01:00
}
2020-05-11 12:06:07 +02:00
// MaxVersion Map of allowed TLS maximum versions.
2019-10-29 07:58:05 -04:00
MaxVersion = map [ string ] uint16 {
` VersionTLS10 ` : tls . VersionTLS10 ,
` VersionTLS11 ` : tls . VersionTLS11 ,
` VersionTLS12 ` : tls . VersionTLS12 ,
` VersionTLS13 ` : tls . VersionTLS13 ,
}
2019-11-03 15:54:04 +01:00
// CurveIDs is a Map of TLS elliptic curves from crypto/tls
// Available CurveIDs defined at https://godoc.org/crypto/tls#CurveID,
// also allowing rfc names defined at https://tools.ietf.org/html/rfc8446#section-4.2.7
CurveIDs = map [ string ] tls . CurveID {
` secp256r1 ` : tls . CurveP256 ,
` CurveP256 ` : tls . CurveP256 ,
` secp384r1 ` : tls . CurveP384 ,
` CurveP384 ` : tls . CurveP384 ,
` secp521r1 ` : tls . CurveP521 ,
` CurveP521 ` : tls . CurveP521 ,
` x25519 ` : tls . X25519 ,
` X25519 ` : tls . X25519 ,
}
2017-11-09 12:16:03 +01:00
)
// Certificate holds a SSL cert/key pair
2020-05-11 12:06:07 +02:00
// Certs and Key could be either a file path, or the file content itself.
2017-11-09 12:16:03 +01:00
type Certificate struct {
2019-07-01 11:30:05 +02:00
CertFile FileOrContent ` json:"certFile,omitempty" toml:"certFile,omitempty" yaml:"certFile,omitempty" `
2022-01-24 05:08:05 -05:00
KeyFile FileOrContent ` json:"keyFile,omitempty" toml:"keyFile,omitempty" yaml:"keyFile,omitempty" loggable:"false" `
2017-11-09 12:16:03 +01:00
}
// Certificates defines traefik certificates type
2020-05-11 12:06:07 +02:00
// Certs and Keys could be either a file path, or the file content itself.
2017-11-09 12:16:03 +01:00
type Certificates [ ] Certificate
2020-09-11 15:40:03 +02:00
// GetCertificates retrieves the certificates as slice of tls.Certificate.
func ( c Certificates ) GetCertificates ( ) [ ] tls . Certificate {
var certs [ ] tls . Certificate
for _ , certificate := range c {
cert , err := certificate . GetCertificate ( )
if err != nil {
log . WithoutContext ( ) . Debugf ( "Error while getting certificate: %v" , err )
continue
}
certs = append ( certs , cert )
}
return certs
}
2020-05-11 12:06:07 +02:00
// FileOrContent hold a file path or content.
2017-11-09 12:16:03 +01:00
type FileOrContent string
func ( f FileOrContent ) String ( ) string {
return string ( f )
}
2020-05-11 12:06:07 +02:00
// IsPath returns true if the FileOrContent is a file path, otherwise returns false.
2017-12-08 11:02:03 +01:00
func ( f FileOrContent ) IsPath ( ) bool {
_ , err := os . Stat ( f . String ( ) )
return err == nil
}
2017-11-09 12:16:03 +01:00
func ( f FileOrContent ) Read ( ) ( [ ] byte , error ) {
var content [ ] byte
2019-12-04 16:26:05 +01:00
if f . IsPath ( ) {
var err error
2021-03-04 20:08:03 +01:00
content , err = os . ReadFile ( f . String ( ) )
2017-11-09 12:16:03 +01:00
if err != nil {
return nil , err
}
} else {
content = [ ] byte ( f )
}
return content , nil
}
2020-05-11 12:06:07 +02:00
// CreateTLSConfig creates a TLS config from Certificate structures.
2018-03-05 20:54:04 +01:00
func ( c * Certificates ) CreateTLSConfig ( entryPointName string ) ( * tls . Config , error ) {
2017-11-09 12:16:03 +01:00
config := & tls . Config { }
2018-03-06 10:12:04 +01:00
domainsCertificates := make ( map [ string ] map [ string ] * tls . Certificate )
2018-04-23 10:54:03 +02:00
2017-11-09 12:16:03 +01:00
if c . isEmpty ( ) {
2018-03-05 20:54:04 +01:00
config . Certificates = [ ] tls . Certificate { }
2018-04-23 10:54:03 +02:00
2017-11-09 12:16:03 +01:00
cert , err := generate . DefaultCertificate ( )
if err != nil {
2018-03-05 20:54:04 +01:00
return nil , err
2017-11-09 12:16:03 +01:00
}
2018-04-23 10:54:03 +02:00
2017-11-09 12:16:03 +01:00
config . Certificates = append ( config . Certificates , * cert )
} else {
for _ , certificate := range * c {
2019-04-15 12:32:03 +02:00
err := certificate . AppendCertificate ( domainsCertificates , entryPointName )
2017-11-09 12:16:03 +01:00
if err != nil {
2018-02-22 14:38:04 +01:00
log . Errorf ( "Unable to add a certificate to the entryPoint %q : %v" , entryPointName , err )
continue
2017-11-09 12:16:03 +01:00
}
2018-04-23 10:54:03 +02:00
2017-11-09 12:16:03 +01:00
for _ , certDom := range domainsCertificates {
2018-04-23 10:54:03 +02:00
for _ , cert := range certDom {
2017-11-09 12:16:03 +01:00
config . Certificates = append ( config . Certificates , * cert )
}
}
}
}
2018-03-05 20:54:04 +01:00
return config , nil
2017-11-09 12:16:03 +01:00
}
2020-05-11 12:06:07 +02:00
// isEmpty checks if the certificates list is empty.
2017-11-09 12:16:03 +01:00
func ( c * Certificates ) isEmpty ( ) bool {
if len ( * c ) == 0 {
return true
}
var key int
for _ , cert := range * c {
if len ( cert . CertFile . String ( ) ) != 0 && len ( cert . KeyFile . String ( ) ) != 0 {
break
}
key ++
}
return key == len ( * c )
}
2019-04-15 12:32:03 +02:00
// AppendCertificate appends a Certificate to a certificates map keyed by entrypoint.
func ( c * Certificate ) AppendCertificate ( certs map [ string ] map [ string ] * tls . Certificate , ep string ) error {
2017-11-09 12:16:03 +01:00
certContent , err := c . CertFile . Read ( )
if err != nil {
2020-05-11 12:06:07 +02:00
return fmt . Errorf ( "unable to read CertFile : %w" , err )
2017-11-09 12:16:03 +01:00
}
keyContent , err := c . KeyFile . Read ( )
if err != nil {
2020-05-11 12:06:07 +02:00
return fmt . Errorf ( "unable to read KeyFile : %w" , err )
2017-11-09 12:16:03 +01:00
}
tlsCert , err := tls . X509KeyPair ( certContent , keyContent )
if err != nil {
2020-05-11 12:06:07 +02:00
return fmt . Errorf ( "unable to generate TLS certificate : %w" , err )
2017-11-09 12:16:03 +01:00
}
parsedCert , _ := x509 . ParseCertificate ( tlsCert . Certificate [ 0 ] )
2018-07-06 02:30:03 -06:00
var SANs [ ] string
if parsedCert . Subject . CommonName != "" {
2018-11-26 03:38:03 -06:00
SANs = append ( SANs , strings . ToLower ( parsedCert . Subject . CommonName ) )
2018-07-06 02:30:03 -06:00
}
2017-11-09 12:16:03 +01:00
if parsedCert . DNSNames != nil {
sort . Strings ( parsedCert . DNSNames )
2018-01-29 10:48:03 +01:00
for _ , dnsName := range parsedCert . DNSNames {
if dnsName != parsedCert . Subject . CommonName {
2018-11-26 03:38:03 -06:00
SANs = append ( SANs , strings . ToLower ( dnsName ) )
2018-07-06 02:30:03 -06:00
}
}
}
if parsedCert . IPAddresses != nil {
for _ , ip := range parsedCert . IPAddresses {
if ip . String ( ) != parsedCert . Subject . CommonName {
2018-11-26 03:38:03 -06:00
SANs = append ( SANs , strings . ToLower ( ip . String ( ) ) )
2018-01-29 10:48:03 +01:00
}
}
2017-11-09 12:16:03 +01:00
}
2018-07-06 02:30:03 -06:00
certKey := strings . Join ( SANs , "," )
2017-11-09 12:16:03 +01:00
certExists := false
if certs [ ep ] == nil {
2018-03-06 10:12:04 +01:00
certs [ ep ] = make ( map [ string ] * tls . Certificate )
2017-11-09 12:16:03 +01:00
} else {
2018-03-06 10:12:04 +01:00
for domains := range certs [ ep ] {
2017-11-09 12:16:03 +01:00
if domains == certKey {
certExists = true
break
}
}
}
if certExists {
2019-10-24 01:10:04 +02:00
log . Debugf ( "Skipping addition of certificate for domain(s) %q, to EntryPoint %s, as it already exists for this Entrypoint." , certKey , ep )
2017-11-09 12:16:03 +01:00
} else {
2019-04-15 12:32:03 +02:00
log . Debugf ( "Adding certificate for domain(s) %s" , certKey )
2018-03-06 10:12:04 +01:00
certs [ ep ] [ certKey ] = & tlsCert
2017-11-09 12:16:03 +01:00
}
return err
}
2020-09-11 15:40:03 +02:00
// GetCertificate retrieves Certificate as tls.Certificate.
func ( c * Certificate ) GetCertificate ( ) ( tls . Certificate , error ) {
certContent , err := c . CertFile . Read ( )
if err != nil {
return tls . Certificate { } , fmt . Errorf ( "unable to read CertFile : %w" , err )
}
keyContent , err := c . KeyFile . Read ( )
if err != nil {
return tls . Certificate { } , fmt . Errorf ( "unable to read KeyFile : %w" , err )
}
cert , err := tls . X509KeyPair ( certContent , keyContent )
if err != nil {
return tls . Certificate { } , fmt . Errorf ( "unable to generate TLS certificate : %w" , err )
}
return cert , nil
}
2020-05-11 12:06:07 +02:00
// GetTruncatedCertificateName truncates the certificate name.
2019-03-14 09:30:04 +01:00
func ( c * Certificate ) GetTruncatedCertificateName ( ) string {
2018-10-16 11:00:04 +02:00
certName := c . CertFile . String ( )
// Truncate certificate information only if it's a well formed certificate content with more than 50 characters
if ! c . CertFile . IsPath ( ) && strings . HasPrefix ( certName , certificateHeader ) && len ( certName ) > len ( certificateHeader ) + 50 {
certName = strings . TrimPrefix ( c . CertFile . String ( ) , certificateHeader ) [ : 50 ]
}
return certName
}
2017-11-09 12:16:03 +01:00
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func ( c * Certificates ) String ( ) string {
if len ( * c ) == 0 {
return ""
}
var result [ ] string
for _ , certificate := range * c {
result = append ( result , certificate . CertFile . String ( ) + "," + certificate . KeyFile . String ( ) )
}
return strings . Join ( result , ";" )
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a comma-separated list, so we split it.
func ( c * Certificates ) Set ( value string ) error {
certificates := strings . Split ( value , ";" )
for _ , certificate := range certificates {
files := strings . Split ( certificate , "," )
if len ( files ) != 2 {
return fmt . Errorf ( "bad certificates format: %s" , value )
}
* c = append ( * c , Certificate {
CertFile : FileOrContent ( files [ 0 ] ) ,
KeyFile : FileOrContent ( files [ 1 ] ) ,
} )
}
return nil
}
2020-05-11 12:06:07 +02:00
// Type is type of the struct.
2017-11-09 12:16:03 +01:00
func ( c * Certificates ) Type ( ) string {
return "certificates"
}
2021-07-15 17:32:11 +05:30
// VerifyPeerCertificate verifies the chain certificates and their URI.
func VerifyPeerCertificate ( uri string , cfg * tls . Config , rawCerts [ ] [ ] byte ) error {
// TODO: Refactor to avoid useless verifyChain (ex: when insecureskipverify is false)
cert , err := verifyChain ( cfg . RootCAs , rawCerts )
if err != nil {
return err
}
if len ( uri ) > 0 {
return verifyServerCertMatchesURI ( uri , cert )
}
return nil
}
// verifyServerCertMatchesURI is used on tls connections dialed to a server
// to ensure that the certificate it presented has the correct URI.
func verifyServerCertMatchesURI ( uri string , cert * x509 . Certificate ) error {
if cert == nil {
return errors . New ( "peer certificate mismatch: no peer certificate presented" )
}
// Our certs will only ever have a single URI for now so only check that
if len ( cert . URIs ) < 1 {
return errors . New ( "peer certificate mismatch: peer certificate invalid" )
}
gotURI := cert . URIs [ 0 ]
// Override the hostname since we rely on x509 constraints to limit ability to spoof the trust domain if needed
// (i.e. because a root is shared with other PKI or Consul clusters).
// This allows for seamless migrations between trust domains.
expectURI := & url . URL { }
id , err := url . Parse ( uri )
if err != nil {
return fmt . Errorf ( "%q is not a valid URI" , uri )
}
* expectURI = * id
expectURI . Host = gotURI . Host
if strings . EqualFold ( gotURI . String ( ) , expectURI . String ( ) ) {
return nil
}
return fmt . Errorf ( "peer certificate mismatch got %s, want %s" , gotURI , uri )
}
// verifyChain performs standard TLS verification without enforcing remote hostname matching.
func verifyChain ( rootCAs * x509 . CertPool , rawCerts [ ] [ ] byte ) ( * x509 . Certificate , error ) {
// Fetch leaf and intermediates. This is based on code form tls handshake.
if len ( rawCerts ) < 1 {
return nil , errors . New ( "tls: no certificates from peer" )
}
certs := make ( [ ] * x509 . Certificate , len ( rawCerts ) )
for i , asn1Data := range rawCerts {
cert , err := x509 . ParseCertificate ( asn1Data )
if err != nil {
return nil , fmt . Errorf ( "tls: failed to parse certificate from peer: %w" , err )
}
certs [ i ] = cert
}
opts := x509 . VerifyOptions {
Roots : rootCAs ,
Intermediates : x509 . NewCertPool ( ) ,
}
// All but the first cert are intermediates
for _ , cert := range certs [ 1 : ] {
opts . Intermediates . AddCert ( cert )
}
_ , err := certs [ 0 ] . Verify ( opts )
if err != nil {
return nil , err
}
return certs [ 0 ] , nil
}