Challenge certs PEM encoding

Signed-off-by: Emile Vauge <emile@vauge.com>
This commit is contained in:
Emile Vauge 2016-09-23 18:27:01 +02:00
parent a42845502e
commit e72e65858f
No known key found for this signature in database
GPG key ID: D808B4C167352E59
25 changed files with 490 additions and 107 deletions

1
.gitignore vendored
View file

@ -2,7 +2,6 @@
gen.go
.idea
.intellij
log
*.iml
traefik
traefik.toml

View file

@ -20,7 +20,14 @@ type Account struct {
Registration *acme.RegistrationResource
PrivateKey []byte
DomainsCertificate DomainsCertificates
ChallengeCerts map[string][]byte
ChallengeCerts map[string]*ChallengeCert
}
// ChallengeCert stores a challenge certificate
type ChallengeCert struct {
Certificate []byte
PrivateKey []byte
certificate *tls.Certificate
}
// Init inits acccount struct
@ -29,9 +36,27 @@ func (a *Account) Init() error {
if err != nil {
return err
}
for _, cert := range a.ChallengeCerts {
if cert.certificate == nil {
certificate, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey)
if err != nil {
return err
}
cert.certificate = &certificate
}
if cert.certificate.Leaf == nil {
leaf, err := x509.ParseCertificate(cert.certificate.Certificate[0])
if err != nil {
return err
}
cert.certificate.Leaf = leaf
}
}
return nil
}
// NewAccount creates an account
func NewAccount(email string) (*Account, error) {
// Create a user. New accounts need an email and private key to start
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
@ -43,22 +68,22 @@ func NewAccount(email string) (*Account, error) {
return &Account{
Email: email,
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
DomainsCertificate: domainsCerts,
ChallengeCerts: map[string][]byte{}}, nil
DomainsCertificate: DomainsCertificates{Certs: domainsCerts.Certs},
ChallengeCerts: map[string]*ChallengeCert{}}, nil
}
// GetEmail returns email
func (a Account) GetEmail() string {
func (a *Account) GetEmail() string {
return a.Email
}
// GetRegistration returns lets encrypt registration resource
func (a Account) GetRegistration() *acme.RegistrationResource {
func (a *Account) GetRegistration() *acme.RegistrationResource {
return a.Registration
}
// GetPrivateKey returns private key
func (a Account) GetPrivateKey() crypto.PrivateKey {
func (a *Account) GetPrivateKey() crypto.PrivateKey {
if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil {
return privateKey
}
@ -81,6 +106,7 @@ type DomainsCertificates struct {
lock sync.RWMutex
}
// Init inits DomainsCertificates
func (dc *DomainsCertificates) Init() error {
dc.lock.Lock()
defer dc.lock.Unlock()
@ -167,7 +193,7 @@ func (dc *DomainsCertificate) needRenew() bool {
return true
}
// <= 7 days left, renew certificate
if crt.NotAfter.Before(time.Now().Add(time.Duration(24 * 7 * time.Hour))) {
if crt.NotAfter.Before(time.Now().Add(time.Duration(24 * 30 * time.Hour))) {
return true
}
}

View file

@ -4,6 +4,7 @@ import (
"crypto/tls"
"errors"
"fmt"
"github.com/cenk/backoff"
"github.com/containous/staert"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
@ -22,6 +23,7 @@ type ACME struct {
Email string `description:"Email address used for registration"`
Domains []Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"`
Storage string `description:"File or key used for certificates storage."`
StorageFile string // deprecated
OnDemand bool `description:"Enable on demand certificate. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."`
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
CAServer string `description:"CA server to use."`
@ -82,6 +84,11 @@ func (a *ACME) init() error {
return err
}
a.defaultCertificate = cert
// TODO: to remove in the futurs
if len(a.StorageFile) > 0 && len(a.Storage) == 0 {
log.Warnf("ACME.StorageFile is deprecated, use ACME.Storage instead")
a.Storage = a.StorageFile
}
return nil
}
@ -160,7 +167,6 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
if err != nil {
return err
}
log.Debugf("buildACMEClient...")
a.client, err = a.buildACMEClient(account)
if err != nil {
return err
@ -179,7 +185,16 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
log.Debugf("AgreeToTOS...")
err = a.client.AgreeToTOS()
if err != nil {
return err
// Let's Encrypt Subscriber Agreement renew ?
reg, err := a.client.QueryRegistration()
if err != nil {
return err
}
account.Registration = reg
err = a.client.AgreeToTOS()
if err != nil {
log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error())
}
}
err = transaction.Commit(account)
if err != nil {
@ -302,7 +317,7 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat
return challengeCert, nil
}
if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(clientHello.ServerName); ok {
log.Debugf("ACME got domaincert %s", clientHello.ServerName)
log.Debugf("ACME got domain cert %s", clientHello.ServerName)
return domainCert.tlsCert, nil
}
if a.OnDemand {
@ -442,6 +457,22 @@ func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.C
// LoadCertificateForDomains loads certificates from ACME for given domains
func (a *ACME) LoadCertificateForDomains(domains []string) {
safe.Go(func() {
operation := func() error {
if a.client == nil {
return fmt.Errorf("ACME client still not built")
}
return nil
}
notify := func(err error, time time.Duration) {
log.Errorf("Error getting ACME client: %v, retrying in %s", err, time)
}
ebo := backoff.NewExponentialBackOff()
ebo.MaxElapsedTime = 30 * time.Second
err := backoff.RetryNotify(operation, ebo, notify)
if err != nil {
log.Errorf("Error getting ACME client: %v", err)
return
}
account := a.store.Get().(*Account)
var domain Domain
if len(domains) == 0 {

View file

@ -63,7 +63,7 @@ func TestDomainsSetAppend(t *testing.T) {
func TestCertificatesRenew(t *testing.T) {
domainsCertificates := DomainsCertificates{
lock: &sync.RWMutex{},
lock: sync.RWMutex{},
Certs: []*DomainsCertificate{
{
Domains: Domain{

View file

@ -2,23 +2,17 @@ package acme
import (
"crypto/tls"
"strings"
"sync"
"bytes"
"crypto/rsa"
"crypto/x509"
"encoding/gob"
"fmt"
"github.com/cenk/backoff"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
"github.com/xenolf/lego/acme"
"time"
)
func init() {
gob.Register(rsa.PrivateKey{})
gob.Register(rsa.PublicKey{})
}
var _ acme.ChallengeProviderTimeout = (*challengeProvider)(nil)
type challengeProvider struct {
@ -34,34 +28,44 @@ func newMemoryChallengeProvider(store cluster.Store) *challengeProvider {
func (c *challengeProvider) getCertificate(domain string) (cert *tls.Certificate, exists bool) {
log.Debugf("Challenge GetCertificate %s", domain)
if !strings.HasSuffix(domain, ".acme.invalid") {
return nil, false
}
c.lock.RLock()
defer c.lock.RUnlock()
account := c.store.Get().(*Account)
if account.ChallengeCerts == nil {
return nil, false
}
if certBinary, ok := account.ChallengeCerts[domain]; ok {
cert := &tls.Certificate{}
var buffer bytes.Buffer
buffer.Write(certBinary)
dec := gob.NewDecoder(&buffer)
err := dec.Decode(cert)
if err != nil {
log.Errorf("Error unmarshaling challenge cert %s", err.Error())
return nil, false
account.Init()
var result *tls.Certificate
operation := func() error {
for _, cert := range account.ChallengeCerts {
for _, dns := range cert.certificate.Leaf.DNSNames {
if domain == dns {
result = cert.certificate
return nil
}
}
}
return cert, true
return fmt.Errorf("Cannot find challenge cert for domain %s", domain)
}
return nil, false
notify := func(err error, time time.Duration) {
log.Errorf("Error getting cert: %v, retrying in %s", err, time)
}
ebo := backoff.NewExponentialBackOff()
ebo.MaxElapsedTime = 60 * time.Second
err := backoff.RetryNotify(operation, ebo, notify)
if err != nil {
log.Errorf("Error getting cert: %v", err)
return nil, false
}
return result, true
}
func (c *challengeProvider) Present(domain, token, keyAuth string) error {
log.Debugf("Challenge Present %s", domain)
cert, _, err := acme.TLSSNI01ChallengeCert(keyAuth)
if err != nil {
return err
}
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
cert, _, err := TLSSNI01ChallengeCert(keyAuth)
if err != nil {
return err
}
@ -74,18 +78,9 @@ func (c *challengeProvider) Present(domain, token, keyAuth string) error {
}
account := object.(*Account)
if account.ChallengeCerts == nil {
account.ChallengeCerts = map[string][]byte{}
}
for i := range cert.Leaf.DNSNames {
var buffer bytes.Buffer
enc := gob.NewEncoder(&buffer)
err := enc.Encode(cert)
if err != nil {
return err
}
account.ChallengeCerts[cert.Leaf.DNSNames[i]] = buffer.Bytes()
log.Debugf("Challenge Present cert: %s", cert.Leaf.DNSNames[i])
account.ChallengeCerts = map[string]*ChallengeCert{}
}
account.ChallengeCerts[domain] = &cert
return transaction.Commit(account)
}

View file

@ -1,6 +1,8 @@
package acme
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
@ -76,3 +78,48 @@ func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain strin
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
}
// TLSSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge
func TLSSNI01ChallengeCert(keyAuth string) (ChallengeCert, string, error) {
// generate a new RSA key for the certificates
var tempPrivKey crypto.PrivateKey
tempPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return ChallengeCert{}, "", err
}
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
rsaPrivPEM := pemEncode(rsaPrivKey)
zBytes := sha256.Sum256([]byte(keyAuth))
z := hex.EncodeToString(zBytes[:sha256.Size])
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
tempCertPEM, err := generatePemCert(rsaPrivKey, domain)
if err != nil {
return ChallengeCert{}, "", err
}
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
if err != nil {
return ChallengeCert{}, "", err
}
return ChallengeCert{Certificate: tempCertPEM, PrivateKey: rsaPrivPEM, certificate: &certificate}, domain, nil
}
func pemEncode(data interface{}) []byte {
var pemBlock *pem.Block
switch key := data.(type) {
case *ecdsa.PrivateKey:
keyBytes, _ := x509.MarshalECPrivateKey(key)
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
case *rsa.PrivateKey:
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
break
case *x509.CertificateRequest:
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
break
case []byte:
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))}
}
return pem.EncodeToMemory(pemBlock)
}

View file

@ -51,17 +51,6 @@ func (s *LocalStore) Load() (cluster.Object, error) {
return account, nil
}
// func (s *LocalStore) saveAccount(account *Account) error {
// s.storageLock.Lock()
// defer s.storageLock.Unlock()
// // write account to file
// data, err := json.MarshalIndent(account, "", " ")
// if err != nil {
// return err
// }
// return ioutil.WriteFile(s.file, data, 0644)
// }
// Begin creates a transaction with the KV store.
func (s *LocalStore) Begin() (cluster.Transaction, cluster.Object, error) {
s.storageLock.Lock()
@ -88,7 +77,7 @@ func (t *localTransaction) Commit(object cluster.Object) error {
if err != nil {
return err
}
return ioutil.WriteFile(t.file, data, 0644)
err = ioutil.WriteFile(t.file, data, 0644)
if err != nil {
return err
}

View file

@ -3,10 +3,11 @@ package cluster
import (
"encoding/json"
"fmt"
"github.com/cenk/backoff"
"github.com/containous/staert"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/docker/libkv/store"
"github.com/emilevauge/backoff"
"github.com/satori/go.uuid"
"golang.org/x/net/context"
"sync"
@ -84,19 +85,11 @@ func (d *Datastore) watchChanges() error {
cancel()
return err
}
d.localLock.Lock()
err := d.kv.LoadConfig(d.meta)
err = d.reload()
if err != nil {
d.localLock.Unlock()
return err
}
err = d.meta.unmarshall()
if err != nil {
d.localLock.Unlock()
return err
}
d.localLock.Unlock()
// log.Debugf("Datastore object change received: %+v", d.object)
// log.Debugf("Datastore object change received: %+v", d.meta)
if d.listener != nil {
err := d.listener(d.meta.object)
if err != nil {
@ -109,7 +102,7 @@ func (d *Datastore) watchChanges() error {
notify := func(err error, time time.Duration) {
log.Errorf("Error in watch datastore: %+v, retrying in %s", err, time)
}
err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
err := backoff.RetryNotify(operation, job.NewBackOff(backoff.NewExponentialBackOff()), notify)
if err != nil {
log.Errorf("Error in watch datastore: %v", err)
}
@ -117,6 +110,23 @@ func (d *Datastore) watchChanges() error {
return nil
}
func (d *Datastore) reload() error {
log.Debugf("Datastore reload")
d.localLock.Lock()
err := d.kv.LoadConfig(d.meta)
if err != nil {
d.localLock.Unlock()
return err
}
err = d.meta.unmarshall()
if err != nil {
d.localLock.Unlock()
return err
}
d.localLock.Unlock()
return nil
}
// Begin creates a transaction with the KV store.
func (d *Datastore) Begin() (Transaction, Object, error) {
id := uuid.NewV4().String()
@ -152,6 +162,10 @@ func (d *Datastore) Begin() (Transaction, Object, error) {
}
notify := func(err error, time time.Duration) {
log.Errorf("Datastore sync error: %v, retrying in %s", err, time)
err = d.reload()
if err != nil {
log.Errorf("Error reloading: %+v", err)
}
}
ebo := backoff.NewExponentialBackOff()
ebo.MaxElapsedTime = 60 * time.Second
@ -228,7 +242,6 @@ func (s *datastoreTransaction) Commit(object Object) error {
}
s.dirty = true
// log.Debugf("Datastore object saved: %+v", s.object)
log.Debugf("Transaction commited %s", s.id)
return nil
}

View file

@ -1,11 +1,11 @@
package cluster
import (
"github.com/cenk/backoff"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/docker/leadership"
"github.com/emilevauge/backoff"
"golang.org/x/net/context"
"time"
)

100
glide.lock generated
View file

@ -1,3 +1,4 @@
<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829
<<<<<<< 2fbcca003e6454c848801c859d8563da94ea8aaf
<<<<<<< a13549cc28273ba5c15a739fa4aaeb3e0f7216a4
hash: c0ac205a859d78847e21d3cd63f427ffba985755c6ae84373e4a20364ba39b05
@ -16,7 +17,14 @@ updated: 2016-09-20T14:50:04.029710103+02:00
hash: 49c7bd0e32b2764248183bda52f168fe22d69e2db5e17c1dbeebbe71be9929b1
updated: 2016-08-11T14:33:42.826534934+02:00
>>>>>>> Add ACME store
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
>>>>>>> Add ACME store
=======
=======
hash: af34f34bc4f9f3cc6c988caa4cc273a7f32f91b24c77bdf5cadd9bcb48883a53
updated: 2016-09-28T11:40:41.311876377+02:00
>>>>>>> Challenge certs PEM encoding
>>>>>>> Challenge certs PEM encoding
imports:
- name: github.com/abbot/go-http-auth
version: cb4372376e1e00e9f6ab9ec142e029302c9e7140
@ -41,7 +49,7 @@ imports:
- name: github.com/containous/mux
version: a819b77bba13f0c0cbe36e437bc2e948411b3996
- name: github.com/containous/staert
version: 56058c7d4152831a641764d10ec91132adf061ea
version: 92329254783dc01174f03302d51d7cf2c9ff84cf
- name: github.com/coreos/etcd
version: 1c9e0a0e33051fed6c05c141e6fcbfe5c7f2a899
subpackages:
@ -53,15 +61,25 @@ imports:
subpackages:
- spew
- name: github.com/docker/distribution
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
<<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab
version: 87917f30529e6a7fca8eaff2932424915fb11225
=======
=======
<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829
>>>>>>> Challenge certs PEM encoding
<<<<<<< a13549cc28273ba5c15a739fa4aaeb3e0f7216a4
version: 99cb7c0946d2f5a38015443e515dc916295064d7
=======
version: 857d0f15c0a4d8037175642e0ca3660829551cb5
>>>>>>> Add KV datastore
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
>>>>>>> Add KV datastore
=======
=======
version: 87917f30529e6a7fca8eaff2932424915fb11225
>>>>>>> Challenge certs PEM encoding
>>>>>>> Challenge certs PEM encoding
subpackages:
- context
- digest
@ -147,10 +165,14 @@ imports:
- sockets
- tlsconfig
- name: github.com/docker/go-units
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
<<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab
version: f2d77a61e3c169b43402a0a1e84f06daf29b8190
=======
version: f2145db703495b2e525c59662db69a7344b00bb8
=======
version: f2d77a61e3c169b43402a0a1e84f06daf29b8190
>>>>>>> Challenge certs PEM encoding
- name: github.com/docker/leadership
version: bfc7753dd48af19513b29deec23c364bf0f274eb
>>>>>>> Add KV datastore
@ -172,11 +194,15 @@ imports:
- version
- yaml
- name: github.com/docker/libkv
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
<<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab
version: 35d3e2084c650109e7bcc7282655b1bc8ba924ff
=======
version: aabc039ad04deb721e234f99cd1b4aa28ac71a40
>>>>>>> Add KV datastore
=======
version: 35d3e2084c650109e7bcc7282655b1bc8ba924ff
>>>>>>> Challenge certs PEM encoding
subpackages:
- store
- store/boltdb
@ -192,15 +218,25 @@ imports:
- name: github.com/go-check/check
version: 4f90aeace3a26ad7021961c297b22c42160c7b25
- name: github.com/gogo/protobuf
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
<<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab
version: e33835a643a970c11ac74f6333f5f6866387a101
=======
=======
<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829
>>>>>>> Challenge certs PEM encoding
<<<<<<< a13549cc28273ba5c15a739fa4aaeb3e0f7216a4
version: 89f1976ff373a3e549675d2f212c10f98b6c6316
=======
version: e57a569e1882958f6b188cb42231d6db87701f2a
>>>>>>> Add KV datastore
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
>>>>>>> Add KV datastore
=======
=======
version: e33835a643a970c11ac74f6333f5f6866387a101
>>>>>>> Challenge certs PEM encoding
>>>>>>> Challenge certs PEM encoding
subpackages:
- proto
- name: github.com/golang/glog
@ -212,13 +248,29 @@ imports:
- name: github.com/gorilla/context
version: aed02d124ae4a0e94fea4541c8effd05bf0c8296
- name: github.com/hashicorp/consul
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
version: fce7d75609a04eeb9d4bf41c8dc592aac18fc97d
=======
<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829
version: d5b7530ec593f1ec2a8f8a7c145bcadafa88b572
=======
version: fce7d75609a04eeb9d4bf41c8dc592aac18fc97d
>>>>>>> Challenge certs PEM encoding
>>>>>>> Challenge certs PEM encoding
subpackages:
- api
- name: github.com/hashicorp/go-cleanhttp
version: 875fb671b3ddc66f8e2f0acc33829c8cb989a38d
- name: github.com/hashicorp/serf
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
version: 6c4672d66fc6312ddde18399262943e21175d831
=======
<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829
version: b7a120a5fc494f6dd5e858f42fd0fd4022d6320f
=======
version: 6c4672d66fc6312ddde18399262943e21175d831
>>>>>>> Challenge certs PEM encoding
>>>>>>> Challenge certs PEM encoding
subpackages:
- coordinate
- serf
@ -263,11 +315,15 @@ imports:
- name: github.com/miekg/dns
version: 5d001d020961ae1c184f9f8152fdc73810481677
- name: github.com/mitchellh/mapstructure
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
<<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab
version: d2dd0262208475919e1a362f675cfc0e7c10e905
=======
version: 21a35fb16463dfb7c8eee579c65d995d95e64d1e
>>>>>>> Add KV datastore
=======
version: d2dd0262208475919e1a362f675cfc0e7c10e905
>>>>>>> Challenge certs PEM encoding
- name: github.com/moul/http2curl
version: b1479103caacaa39319f75e7f57fc545287fca0d
- name: github.com/NYTimes/gziphandler
@ -275,15 +331,25 @@ imports:
- name: github.com/ogier/pflag
version: 45c278ab3607870051a2ea9040bb85fcb8557481
- name: github.com/opencontainers/runc
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
<<<<<<< 38b62d4ae311e2d5247065cbc2c09421a2bb81ab
version: 1a81e9ab1f138c091fe5c86d0883f87716088527
=======
=======
<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829
>>>>>>> Challenge certs PEM encoding
<<<<<<< a13549cc28273ba5c15a739fa4aaeb3e0f7216a4
version: d9fec4c63b089ddfc267194ecb6cda58a13f072c
=======
version: ff88baa42fa5b2a1568a3a14665142fb4bdb3a2a
>>>>>>> Add KV datastore
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
>>>>>>> Add KV datastore
=======
=======
version: 1a81e9ab1f138c091fe5c86d0883f87716088527
>>>>>>> Challenge certs PEM encoding
>>>>>>> Challenge certs PEM encoding
subpackages:
- libcontainer/user
- name: github.com/parnurzeal/gorequest
@ -342,7 +408,15 @@ imports:
- name: github.com/vulcand/route
version: cb89d787ddbb1c5849a7ac9f79004c1fd12a4a32
- name: github.com/vulcand/vulcand
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
version: 28a4e5c0892167589737b95ceecbcef00295be50
=======
<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829
version: 643ca8acff8386e3b276f6feb8ba9b5893dbc4a2
=======
version: 28a4e5c0892167589737b95ceecbcef00295be50
>>>>>>> Challenge certs PEM encoding
>>>>>>> Challenge certs PEM encoding
subpackages:
- conntracker
- plugin
@ -372,11 +446,27 @@ imports:
- name: gopkg.in/fsnotify.v1
version: a8a77c9133d2d6fd8334f3260d06f60e8d80a5fb
- name: gopkg.in/mgo.v2
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
version: 29cc868a5ca65f401ff318143f9408d02f4799cc
subpackages:
- bson
- name: gopkg.in/square/go-jose.v1
version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc
=======
<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829
version: 22287bab4379e1fbf6002fb4eb769888f3fb224c
subpackages:
- bson
- name: gopkg.in/square/go-jose.v1
version: aa2e30fdd1fe9dd3394119af66451ae790d50e0d
=======
version: 29cc868a5ca65f401ff318143f9408d02f4799cc
subpackages:
- bson
- name: gopkg.in/square/go-jose.v1
version: e3f973b66b91445ec816dd7411ad1b6495a5a2fc
>>>>>>> Challenge certs PEM encoding
>>>>>>> Challenge certs PEM encoding
subpackages:
- cipher
- json
@ -394,7 +484,15 @@ testImports:
- name: github.com/libkermit/docker-check
version: cbe0ef03b3d23070eac4d00ba8828f2cc7f7e5a3
- name: github.com/spf13/pflag
<<<<<<< a42845502e9b6e3b9985c56ad99d28c1357287b2
version: 5644820622454e71517561946e3d94b9f9db6842
=======
<<<<<<< 9fb29a2d5ae0ade0aa8cb65df5c726a944e4a829
version: 08b1a584251b5b62f458943640fc8ebd4d50aaa5
=======
version: 5644820622454e71517561946e3d94b9f9db6842
>>>>>>> Challenge certs PEM encoding
>>>>>>> Challenge certs PEM encoding
- name: github.com/vbatts/tar-split
version: 6810cedb21b2c3d0b9bb8f9af12ff2dc7a2f14df
subpackages:

View file

@ -21,7 +21,7 @@ import:
- stream
- utils
- package: github.com/containous/staert
version: 56058c7d4152831a641764d10ec91132adf061ea
version: 92329254783dc01174f03302d51d7cf2c9ff84cf
- package: github.com/docker/engine-api
version: 62043eb79d581a32ea849645277023c550732e52
subpackages:

View file

@ -446,12 +446,12 @@ func (s *ConsulSuite) TestDatastore(c *check.C) {
c.Assert(err, checker.IsNil)
ctx := context.Background()
datastore1, err := cluster.NewDataStore(kvSource, ctx, &TestStruct{})
datastore1, err := cluster.NewDataStore(*kvSource, ctx, &TestStruct{}, nil)
c.Assert(err, checker.IsNil)
datastore2, err := cluster.NewDataStore(kvSource, ctx, &TestStruct{})
datastore2, err := cluster.NewDataStore(*kvSource, ctx, &TestStruct{}, nil)
c.Assert(err, checker.IsNil)
setter1, err := datastore1.Begin()
setter1, _, err := datastore1.Begin()
c.Assert(err, checker.IsNil)
err = setter1.Commit(&TestStruct{
String: "foo",
@ -465,7 +465,7 @@ func (s *ConsulSuite) TestDatastore(c *check.C) {
test2 := datastore2.Get().(*TestStruct)
c.Assert(test2.String, checker.Equals, "foo")
setter2, err := datastore2.Begin()
setter2, _, err := datastore2.Begin()
c.Assert(err, checker.IsNil)
err = setter2.Commit(&TestStruct{
String: "bar",
@ -483,7 +483,7 @@ func (s *ConsulSuite) TestDatastore(c *check.C) {
wg.Add(4)
go func() {
for i := 0; i < 100; i++ {
setter1, err := datastore1.Begin()
setter1, _, err := datastore1.Begin()
c.Assert(err, checker.IsNil)
err = setter1.Commit(&TestStruct{
String: "datastore1",
@ -495,7 +495,7 @@ func (s *ConsulSuite) TestDatastore(c *check.C) {
}()
go func() {
for i := 0; i < 100; i++ {
setter2, err := datastore2.Begin()
setter2, _, err := datastore2.Begin()
c.Assert(err, checker.IsNil)
err = setter2.Commit(&TestStruct{
String: "datastore2",

188
log/logger.go Normal file
View file

@ -0,0 +1,188 @@
package log
import (
"github.com/Sirupsen/logrus"
"io"
)
var (
logger *logrus.Entry
)
func init() {
logger = logrus.StandardLogger().WithFields(logrus.Fields{})
}
// Context sets the Context of the logger
func Context(context interface{}) *logrus.Entry {
return logger.WithField("context", context)
}
// SetOutput sets the standard logger output.
func SetOutput(out io.Writer) {
logrus.SetOutput(out)
}
// SetFormatter sets the standard logger formatter.
func SetFormatter(formatter logrus.Formatter) {
logrus.SetFormatter(formatter)
}
// SetLevel sets the standard logger level.
func SetLevel(level logrus.Level) {
logrus.SetLevel(level)
}
// GetLevel returns the standard logger level.
func GetLevel() logrus.Level {
return logrus.GetLevel()
}
// AddHook adds a hook to the standard logger hooks.
func AddHook(hook logrus.Hook) {
logrus.AddHook(hook)
}
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
func WithError(err error) *logrus.Entry {
return logger.WithError(err)
}
// WithField creates an entry from the standard logger and adds a field to
// it. If you want multiple fields, use `WithFields`.
//
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
// or Panic on the Entry it returns.
func WithField(key string, value interface{}) *logrus.Entry {
return logger.WithField(key, value)
}
// WithFields creates an entry from the standard logger and adds multiple
// fields to it. This is simply a helper for `WithField`, invoking it
// once for each field.
//
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
// or Panic on the Entry it returns.
func WithFields(fields logrus.Fields) *logrus.Entry {
return logger.WithFields(fields)
}
// Debug logs a message at level Debug on the standard logger.
func Debug(args ...interface{}) {
logger.Debug(args...)
}
// Print logs a message at level Info on the standard logger.
func Print(args ...interface{}) {
logger.Print(args...)
}
// Info logs a message at level Info on the standard logger.
func Info(args ...interface{}) {
logger.Info(args...)
}
// Warn logs a message at level Warn on the standard logger.
func Warn(args ...interface{}) {
logger.Warn(args...)
}
// Warning logs a message at level Warn on the standard logger.
func Warning(args ...interface{}) {
logger.Warning(args...)
}
// Error logs a message at level Error on the standard logger.
func Error(args ...interface{}) {
logger.Error(args...)
}
// Panic logs a message at level Panic on the standard logger.
func Panic(args ...interface{}) {
logger.Panic(args...)
}
// Fatal logs a message at level Fatal on the standard logger.
func Fatal(args ...interface{}) {
logger.Fatal(args...)
}
// Debugf logs a message at level Debug on the standard logger.
func Debugf(format string, args ...interface{}) {
logger.Debugf(format, args...)
}
// Printf logs a message at level Info on the standard logger.
func Printf(format string, args ...interface{}) {
logger.Printf(format, args...)
}
// Infof logs a message at level Info on the standard logger.
func Infof(format string, args ...interface{}) {
logger.Infof(format, args...)
}
// Warnf logs a message at level Warn on the standard logger.
func Warnf(format string, args ...interface{}) {
logger.Warnf(format, args...)
}
// Warningf logs a message at level Warn on the standard logger.
func Warningf(format string, args ...interface{}) {
logger.Warningf(format, args...)
}
// Errorf logs a message at level Error on the standard logger.
func Errorf(format string, args ...interface{}) {
logger.Errorf(format, args...)
}
// Panicf logs a message at level Panic on the standard logger.
func Panicf(format string, args ...interface{}) {
logger.Panicf(format, args...)
}
// Fatalf logs a message at level Fatal on the standard logger.
func Fatalf(format string, args ...interface{}) {
logger.Fatalf(format, args...)
}
// Debugln logs a message at level Debug on the standard logger.
func Debugln(args ...interface{}) {
logger.Debugln(args...)
}
// Println logs a message at level Info on the standard logger.
func Println(args ...interface{}) {
logger.Println(args...)
}
// Infoln logs a message at level Info on the standard logger.
func Infoln(args ...interface{}) {
logger.Infoln(args...)
}
// Warnln logs a message at level Warn on the standard logger.
func Warnln(args ...interface{}) {
logger.Warnln(args...)
}
// Warningln logs a message at level Warn on the standard logger.
func Warningln(args ...interface{}) {
logger.Warningln(args...)
}
// Errorln logs a message at level Error on the standard logger.
func Errorln(args ...interface{}) {
logger.Errorln(args...)
}
// Panicln logs a message at level Panic on the standard logger.
func Panicln(args ...interface{}) {
logger.Panicln(args...)
}
// Fatalln logs a message at level Fatal on the standard logger.
func Fatalln(args ...interface{}) {
logger.Fatalln(args...)
}

View file

@ -2,9 +2,9 @@ package middlewares
import (
"fmt"
"github.com/containous/traefik/log"
"github.com/abbot/go-http-auth"
"github.com/codegangsta/negroni"
"github.com/containous/traefik/log"
"github.com/containous/traefik/types"
"net/http"
"strings"

View file

@ -10,9 +10,9 @@ import (
"github.com/BurntSushi/ty/fun"
"github.com/Sirupsen/logrus"
"github.com/containous/traefik/log"
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/hashicorp/consul/api"

View file

@ -13,9 +13,9 @@ import (
"golang.org/x/net/context"
"github.com/BurntSushi/ty/fun"
"github.com/containous/traefik/log"
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/containous/traefik/version"

View file

@ -16,9 +16,6 @@ import (
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
"github.com/containous/traefik/provider/k8s"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
)
const (

View file

@ -9,9 +9,9 @@ import (
"errors"
"github.com/BurntSushi/ty/fun"
"github.com/containous/traefik/log"
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/docker/libkv"
@ -148,7 +148,7 @@ func (provider *Kv) list(keys ...string) []string {
joinedKeys := strings.Join(keys, "")
keysPairs, err := provider.kvclient.List(joinedKeys)
if err != nil {
log.Warnf("Cannot get keys %s %s ", joinedKeys, err)
log.Debugf("Cannot get keys %s %s ", joinedKeys, err)
return nil
}
directoryKeys := make(map[string]string)
@ -170,10 +170,10 @@ func (provider *Kv) get(defaultValue string, keys ...string) string {
joinedKeys := strings.Join(keys, "")
keyPair, err := provider.kvclient.Get(strings.TrimPrefix(joinedKeys, "/"))
if err != nil {
log.Warnf("Cannot get key %s %s, setting default %s", joinedKeys, err, defaultValue)
log.Debugf("Cannot get key %s %s, setting default %s", joinedKeys, err, defaultValue)
return defaultValue
} else if keyPair == nil {
log.Warnf("Cannot get key %s, setting default %s", joinedKeys, defaultValue)
log.Debugf("Cannot get key %s, setting default %s", joinedKeys, defaultValue)
return defaultValue
}
return string(keyPair.Value)
@ -183,10 +183,10 @@ func (provider *Kv) splitGet(keys ...string) []string {
joinedKeys := strings.Join(keys, "")
keyPair, err := provider.kvclient.Get(joinedKeys)
if err != nil {
log.Warnf("Cannot get key %s %s, setting default empty", joinedKeys, err)
log.Debugf("Cannot get key %s %s, setting default empty", joinedKeys, err)
return []string{}
} else if keyPair == nil {
log.Warnf("Cannot get key %s, setting default %empty", joinedKeys)
log.Debugf("Cannot get key %s, setting default %empty", joinedKeys)
return []string{}
}
return strings.Split(string(keyPair.Value), ",")

View file

@ -13,9 +13,9 @@ import (
"time"
"github.com/BurntSushi/ty/fun"
"github.com/containous/traefik/log"
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/gambol99/go-marathon"

View file

@ -8,9 +8,9 @@ import (
"fmt"
"github.com/BurntSushi/ty/fun"
"github.com/containous/traefik/log"
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/mesos/mesos-go/detector"

View file

@ -13,8 +13,8 @@ import (
"os"
"github.com/BurntSushi/toml"
"github.com/containous/traefik/log"
"github.com/containous/traefik/autogen"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
)

View file

@ -149,7 +149,7 @@ func (r *Rules) parseRules(expression string, onRule func(functionName string, f
err := onRule(functionName, parsedFunction, parsedArgs)
if err != nil {
return fmt.Errorf("Parsing error on rule:", err)
return fmt.Errorf("Parsing error on rule: %v", err)
}
}
return nil
@ -180,7 +180,7 @@ func (r *Rules) Parse(expression string) (*mux.Route, error) {
return nil
})
if err != nil {
return nil, fmt.Errorf("Error parsing rule:", err)
return nil, fmt.Errorf("Error parsing rule: %v", err)
}
return resultRoute, nil
}
@ -195,7 +195,7 @@ func (r *Rules) ParseDomains(expression string) ([]string, error) {
return nil
})
if err != nil {
return nil, fmt.Errorf("Error parsing domains:", err)
return nil, fmt.Errorf("Error parsing domains: %v", err)
}
return domains, nil
}

View file

@ -243,7 +243,7 @@ func (server *Server) defaultConfigurationValues(configuration *types.Configurat
for backendName, backend := range configuration.Backends {
_, err := types.NewLoadBalancerMethod(backend.LoadBalancer)
if err != nil {
log.Debugf("Error loading load balancer method '%+v' for backend %s: %v. Using default wrr.", backend.LoadBalancer, backendName, err)
log.Debugf("Load balancer method '%+v' for backend %s: %v. Using default wrr.", backend.LoadBalancer, backendName, err)
backend.LoadBalancer = &types.LoadBalancer{Method: "wrr"}
}
}

View file

@ -129,7 +129,7 @@ Complete documentation is available at https://traefik.io`,
}
if _, err := f.Parse(usedCmd); err != nil {
fmtlog.Println(err)
fmtlog.Printf("Error parsing command: %s\n", err)
os.Exit(-1)
}
@ -150,7 +150,7 @@ Complete documentation is available at https://traefik.io`,
kv, err = CreateKvSource(traefikConfiguration)
if err != nil {
fmtlog.Println(err)
fmtlog.Printf("Error creating kv store: %s\n", err)
os.Exit(-1)
}
@ -164,13 +164,13 @@ Complete documentation is available at https://traefik.io`,
}
s.AddSource(kv)
if _, err := s.LoadConfig(); err != nil {
fmtlog.Println(err)
fmtlog.Printf("Error loading configuration: %s\n", err)
os.Exit(-1)
}
}
if err := s.Run(); err != nil {
fmtlog.Println(err)
fmtlog.Printf("Error running traefik: %s\n", err)
os.Exit(-1)
}

4
web.go
View file

@ -8,10 +8,10 @@ import (
"net/http"
"runtime"
"github.com/containous/traefik/log"
"github.com/codegangsta/negroni"
"github.com/containous/mux"
"github.com/containous/traefik/autogen"
"github.com/containous/traefik/log"
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
@ -79,7 +79,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessag
body, _ := ioutil.ReadAll(request.Body)
err := json.Unmarshal(body, configuration)
if err == nil {
configurationChan <- types.ConfigMessage{"web", configuration}
configurationChan <- types.ConfigMessage{ProviderName: "web", Configuration: configuration}
provider.getConfigHandler(response, request)
} else {
log.Errorf("Error parsing configuration %+v", err)