1
0
Fork 0

Remove old global config and use new static config

This commit is contained in:
SALLEYRON Julien 2018-11-27 17:42:04 +01:00 committed by Traefiker Bot
parent c39d21c178
commit 5d91c7e15c
114 changed files with 2485 additions and 3646 deletions

244
old/tls/certificate.go Normal file
View file

@ -0,0 +1,244 @@
package tls
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"os"
"sort"
"strings"
"github.com/containous/traefik/log"
"github.com/containous/traefik/tls/generate"
)
var (
// MinVersion Map of allowed TLS minimum versions
MinVersion = map[string]uint16{
`VersionTLS10`: tls.VersionTLS10,
`VersionTLS11`: tls.VersionTLS11,
`VersionTLS12`: tls.VersionTLS12,
}
// CipherSuites Map of TLS CipherSuites from crypto/tls
// Available CipherSuites defined at https://golang.org/pkg/crypto/tls/#pkg-constants
CipherSuites = map[string]uint16{
`TLS_RSA_WITH_RC4_128_SHA`: tls.TLS_RSA_WITH_RC4_128_SHA,
`TLS_RSA_WITH_3DES_EDE_CBC_SHA`: tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
`TLS_RSA_WITH_AES_128_CBC_SHA`: tls.TLS_RSA_WITH_AES_128_CBC_SHA,
`TLS_RSA_WITH_AES_256_CBC_SHA`: tls.TLS_RSA_WITH_AES_256_CBC_SHA,
`TLS_RSA_WITH_AES_128_CBC_SHA256`: tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
`TLS_RSA_WITH_AES_128_GCM_SHA256`: tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
`TLS_RSA_WITH_AES_256_GCM_SHA384`: tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
`TLS_ECDHE_ECDSA_WITH_RC4_128_SHA`: tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA`: tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
`TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`: tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
`TLS_ECDHE_RSA_WITH_RC4_128_SHA`: tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
`TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
`TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256`: tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256`: tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
`TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`: tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
`TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`: tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
`TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`: tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
`TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`: tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
`TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305`: tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
`TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305`: tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}
)
// Certificate holds a SSL cert/key pair
// Certs and Key could be either a file path, or the file content itself
type Certificate struct {
CertFile FileOrContent
KeyFile FileOrContent
}
// Certificates defines traefik certificates type
// Certs and Keys could be either a file path, or the file content itself
type Certificates []Certificate
// FileOrContent hold a file path or content
type FileOrContent string
func (f FileOrContent) String() string {
return string(f)
}
// IsPath returns true if the FileOrContent is a file path, otherwise returns false
func (f FileOrContent) IsPath() bool {
_, err := os.Stat(f.String())
return err == nil
}
func (f FileOrContent) Read() ([]byte, error) {
var content []byte
if _, err := os.Stat(f.String()); err == nil {
content, err = ioutil.ReadFile(f.String())
if err != nil {
return nil, err
}
} else {
content = []byte(f)
}
return content, nil
}
// CreateTLSConfig creates a TLS config from Certificate structures
func (c *Certificates) CreateTLSConfig(entryPointName string) (*tls.Config, error) {
config := &tls.Config{}
domainsCertificates := make(map[string]map[string]*tls.Certificate)
if c.isEmpty() {
config.Certificates = []tls.Certificate{}
cert, err := generate.DefaultCertificate()
if err != nil {
return nil, err
}
config.Certificates = append(config.Certificates, *cert)
} else {
for _, certificate := range *c {
err := certificate.AppendCertificates(domainsCertificates, entryPointName)
if err != nil {
log.Errorf("Unable to add a certificate to the entryPoint %q : %v", entryPointName, err)
continue
}
for _, certDom := range domainsCertificates {
for _, cert := range certDom {
config.Certificates = append(config.Certificates, *cert)
}
}
}
}
return config, nil
}
// isEmpty checks if the certificates list is empty
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)
}
// AppendCertificates appends a Certificate to a certificates map sorted by entrypoints
func (c *Certificate) AppendCertificates(certs map[string]map[string]*tls.Certificate, ep string) error {
certContent, err := c.CertFile.Read()
if err != nil {
return fmt.Errorf("unable to read CertFile : %v", err)
}
keyContent, err := c.KeyFile.Read()
if err != nil {
return fmt.Errorf("unable to read KeyFile : %v", err)
}
tlsCert, err := tls.X509KeyPair(certContent, keyContent)
if err != nil {
return fmt.Errorf("unable to generate TLS certificate : %v", err)
}
parsedCert, _ := x509.ParseCertificate(tlsCert.Certificate[0])
var SANs []string
if parsedCert.Subject.CommonName != "" {
SANs = append(SANs, parsedCert.Subject.CommonName)
}
if parsedCert.DNSNames != nil {
sort.Strings(parsedCert.DNSNames)
for _, dnsName := range parsedCert.DNSNames {
if dnsName != parsedCert.Subject.CommonName {
SANs = append(SANs, dnsName)
}
}
}
if parsedCert.IPAddresses != nil {
for _, ip := range parsedCert.IPAddresses {
if ip.String() != parsedCert.Subject.CommonName {
SANs = append(SANs, ip.String())
}
}
}
certKey := strings.Join(SANs, ",")
certExists := false
if certs[ep] == nil {
certs[ep] = make(map[string]*tls.Certificate)
} else {
for domains := range certs[ep] {
if domains == certKey {
certExists = true
break
}
}
}
if certExists {
log.Warnf("Into EntryPoint %s, try to add certificate for domains which already have this certificate (%s). The new certificate will not be append to the EntryPoint.", ep, certKey)
} else {
log.Debugf("Add certificate for domains %s", certKey)
certs[ep][certKey] = &tlsCert
}
return err
}
func (c *Certificate) getTruncatedCertificateName() string {
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
}
// 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
}
// Type is type of the struct
func (c *Certificates) Type() string {
return "certificates"
}

View file

@ -0,0 +1,137 @@
package tls
import (
"crypto/tls"
"net"
"sort"
"strings"
"time"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/patrickmn/go-cache"
)
// CertificateStore store for dynamic and static certificates
type CertificateStore struct {
DynamicCerts *safe.Safe
StaticCerts *safe.Safe
DefaultCertificate *tls.Certificate
CertCache *cache.Cache
SniStrict bool
}
// NewCertificateStore create a store for dynamic and static certificates
func NewCertificateStore() *CertificateStore {
return &CertificateStore{
StaticCerts: &safe.Safe{},
DynamicCerts: &safe.Safe{},
CertCache: cache.New(1*time.Hour, 10*time.Minute),
}
}
// GetAllDomains return a slice with all the certificate domain
func (c CertificateStore) GetAllDomains() []string {
var allCerts []string
// Get static certificates
if c.StaticCerts != nil && c.StaticCerts.Get() != nil {
for domains := range c.StaticCerts.Get().(map[string]*tls.Certificate) {
allCerts = append(allCerts, domains)
}
}
// Get dynamic certificates
if c.DynamicCerts != nil && c.DynamicCerts.Get() != nil {
for domains := range c.DynamicCerts.Get().(map[string]*tls.Certificate) {
allCerts = append(allCerts, domains)
}
}
return allCerts
}
// GetBestCertificate returns the best match certificate, and caches the response
func (c CertificateStore) GetBestCertificate(clientHello *tls.ClientHelloInfo) *tls.Certificate {
domainToCheck := strings.ToLower(strings.TrimSpace(clientHello.ServerName))
if len(domainToCheck) == 0 {
// If no ServerName is provided, Check for local IP address matches
host, _, err := net.SplitHostPort(clientHello.Conn.LocalAddr().String())
if err != nil {
log.Debugf("Could not split host/port: %v", err)
}
domainToCheck = strings.TrimSpace(host)
}
if cert, ok := c.CertCache.Get(domainToCheck); ok {
return cert.(*tls.Certificate)
}
matchedCerts := map[string]*tls.Certificate{}
if c.DynamicCerts != nil && c.DynamicCerts.Get() != nil {
for domains, cert := range c.DynamicCerts.Get().(map[string]*tls.Certificate) {
for _, certDomain := range strings.Split(domains, ",") {
if MatchDomain(domainToCheck, certDomain) {
matchedCerts[certDomain] = cert
}
}
}
}
if c.StaticCerts != nil && c.StaticCerts.Get() != nil {
for domains, cert := range c.StaticCerts.Get().(map[string]*tls.Certificate) {
for _, certDomain := range strings.Split(domains, ",") {
if MatchDomain(domainToCheck, certDomain) {
matchedCerts[certDomain] = cert
}
}
}
}
if len(matchedCerts) > 0 {
// sort map by keys
keys := make([]string, 0, len(matchedCerts))
for k := range matchedCerts {
keys = append(keys, k)
}
sort.Strings(keys)
// cache best match
c.CertCache.SetDefault(domainToCheck, matchedCerts[keys[len(keys)-1]])
return matchedCerts[keys[len(keys)-1]]
}
return nil
}
// ContainsCertificates checks if there are any certs in the store
func (c CertificateStore) ContainsCertificates() bool {
return c.StaticCerts.Get() != nil || c.DynamicCerts.Get() != nil
}
// ResetCache clears the cache in the store
func (c CertificateStore) ResetCache() {
if c.CertCache != nil {
c.CertCache.Flush()
}
}
// MatchDomain return true if a domain match the cert domain
func MatchDomain(domain string, certDomain string) bool {
if domain == certDomain {
return true
}
for len(certDomain) > 0 && certDomain[len(certDomain)-1] == '.' {
certDomain = certDomain[:len(certDomain)-1]
}
labels := strings.Split(domain, ".")
for i := range labels {
labels[i] = "*"
candidate := strings.Join(labels, ".")
if certDomain == candidate {
return true
}
}
return false
}

View file

@ -0,0 +1,94 @@
package generate
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/pem"
"fmt"
"math/big"
"time"
)
// DefaultDomain Traefik domain for the default certificate
const DefaultDomain = "TRAEFIK DEFAULT CERT"
// DefaultCertificate generates random TLS certificates
func DefaultCertificate() (*tls.Certificate, error) {
randomBytes := make([]byte, 100)
_, err := rand.Read(randomBytes)
if err != nil {
return nil, err
}
zBytes := sha256.Sum256(randomBytes)
z := hex.EncodeToString(zBytes[:sha256.Size])
domain := fmt.Sprintf("%s.%s.traefik.default", z[:32], z[32:])
certPEM, keyPEM, err := KeyPair(domain, time.Time{})
if err != nil {
return nil, err
}
certificate, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
return nil, err
}
return &certificate, nil
}
// KeyPair generates cert and key files
func KeyPair(domain string, expiration time.Time) ([]byte, []byte, error) {
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)})
certPEM, err := PemCert(rsaPrivKey, domain, expiration)
if err != nil {
return nil, nil, err
}
return certPEM, keyPEM, nil
}
// PemCert generates PEM cert file
func PemCert(privKey *rsa.PrivateKey, domain string, expiration time.Time) ([]byte, error) {
derBytes, err := derCert(privKey, expiration, domain)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
}
func derCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, err
}
if expiration.IsZero() {
expiration = time.Now().Add(365 * (24 * time.Hour))
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: DefaultDomain,
},
NotBefore: time.Now(),
NotAfter: expiration,
KeyUsage: x509.KeyUsageKeyEncipherment,
BasicConstraintsValid: true,
DNSNames: []string{domain},
}
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
}

101
old/tls/tls.go Normal file
View file

@ -0,0 +1,101 @@
package tls
import (
"crypto/tls"
"fmt"
"strings"
"github.com/containous/traefik/log"
"github.com/sirupsen/logrus"
)
const (
certificateHeader = "-----BEGIN CERTIFICATE-----\n"
)
// ClientCA defines traefik CA files for a entryPoint
// and it indicates if they are mandatory or have just to be analyzed if provided
type ClientCA struct {
Files FilesOrContents
Optional bool
}
// TLS configures TLS for an entry point
type TLS struct {
MinVersion string `export:"true"`
CipherSuites []string
Certificates Certificates
ClientCA ClientCA
DefaultCertificate *Certificate
SniStrict bool `export:"true"`
}
// FilesOrContents hold the CA we want to have in root
type FilesOrContents []FileOrContent
// Configuration allows mapping a TLS certificate to a list of entrypoints
type Configuration struct {
EntryPoints []string
Certificate *Certificate
}
// 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 (r *FilesOrContents) String() string {
sliceOfString := make([]string, len([]FileOrContent(*r)))
for key, value := range *r {
sliceOfString[key] = value.String()
}
return strings.Join(sliceOfString, ",")
}
// 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 (r *FilesOrContents) Set(value string) error {
filesOrContents := strings.Split(value, ",")
if len(filesOrContents) == 0 {
return fmt.Errorf("bad FilesOrContents format: %s", value)
}
for _, fileOrContent := range filesOrContents {
*r = append(*r, FileOrContent(fileOrContent))
}
return nil
}
// Get return the FilesOrContents list
func (r *FilesOrContents) Get() interface{} {
return *r
}
// SetValue sets the FilesOrContents with val
func (r *FilesOrContents) SetValue(val interface{}) {
*r = val.(FilesOrContents)
}
// Type is type of the struct
func (r *FilesOrContents) Type() string {
return "filesorcontents"
}
// SortTLSPerEntryPoints converts TLS configuration sorted by Certificates into TLS configuration sorted by EntryPoints
func SortTLSPerEntryPoints(configurations []*Configuration, epConfiguration map[string]map[string]*tls.Certificate, defaultEntryPoints []string) {
if epConfiguration == nil {
epConfiguration = make(map[string]map[string]*tls.Certificate)
}
for _, conf := range configurations {
if conf.EntryPoints == nil || len(conf.EntryPoints) == 0 {
if log.GetLevel() >= logrus.DebugLevel {
log.Debugf("No entryPoint is defined to add the certificate %s, it will be added to the default entryPoints: %s",
conf.Certificate.getTruncatedCertificateName(),
strings.Join(defaultEntryPoints, ", "))
}
conf.EntryPoints = append(conf.EntryPoints, defaultEntryPoints...)
}
for _, ep := range conf.EntryPoints {
if err := conf.Certificate.AppendCertificates(epConfiguration, ep); err != nil {
log.Errorf("Unable to append certificate %s to entrypoint %s: %v", conf.Certificate.getTruncatedCertificateName(), ep, err)
}
}
}
}