From a8a5a10410940939d1805367972aa449a9b600da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20M=C3=BCller?= Date: Thu, 18 Apr 2019 21:37:41 +0200 Subject: [PATCH] restructure project & ad support for file watch & abstract backends --- acme.go | 176 +++++++++++++++++++++++++++++++ dumper.go | 301 +++++------------------------------------------------- file.go | 82 +++++++++++++++ go.mod | 45 ++++++-- go.sum | 161 ++++++++++++++++++++++------- kv.go | 110 ++++++++++++++++++++ main.go | 77 +++++++++----- readme.md | 15 --- 8 files changed, 609 insertions(+), 358 deletions(-) create mode 100644 acme.go create mode 100644 file.go create mode 100644 kv.go diff --git a/acme.go b/acme.go new file mode 100644 index 0000000..55ecf2e --- /dev/null +++ b/acme.go @@ -0,0 +1,176 @@ +package main + +import ( + "encoding/pem" + "fmt" + "io/ioutil" + "os" + "path/filepath" + + "github.com/xenolf/lego/certcrypto" + "github.com/xenolf/lego/registration" +) + +// Certificate is a struct which contains all data needed from an ACME certificate +type Certificate struct { + Domain Domain + Certificate []byte + Key []byte +} + +// Domain holds a domain name with SANs +type Domain struct { + Main string + SANs []string +} + +// Account is used to store lets encrypt registration info +type Account struct { + Email string + Registration *registration.Resource + PrivateKey []byte + KeyType certcrypto.KeyType +} + +type fileInfo struct { + Name string + Ext string +} + +const ( + certsSubDir = "certs" + keysSubDir = "private" +) + +// StoredData represents the data managed by the Store +type StoredData struct { + Account *Account + Certificates []*Certificate + HTTPChallenges map[string]map[string][]byte + TLSChallenges map[string]*Certificate +} + +func dump(config *Config, data *StoredData) error { + + if err := os.RemoveAll(config.Path); err != nil { + return err + } + + if !config.DomainSubDir { + if err := os.MkdirAll(filepath.Join(config.Path, certsSubDir), 0755); err != nil { + return err + } + } + + if err := os.MkdirAll(filepath.Join(config.Path, keysSubDir), 0755); err != nil { + return err + } + + privateKeyPem := extractPEMPrivateKey(data.Account) + err := ioutil.WriteFile(filepath.Join(config.Path, keysSubDir, "letsencrypt"+config.KeyInfo.Ext), privateKeyPem, 0666) + if err != nil { + return err + } + + for _, cert := range data.Certificates { + err := writeCert(config.Path, cert, config.CertInfo, config.DomainSubDir) + if err != nil { + return err + } + + err = writeKey(config.Path, cert, config.KeyInfo, config.DomainSubDir) + if err != nil { + return err + } + } + + if err := tree(config.Path, ""); err != nil { + return err + } + + return nil +} + +func writeCert(dumpPath string, cert *Certificate, info fileInfo, domainSubDir bool) error { + certPath := filepath.Join(dumpPath, keysSubDir, cert.Domain.Main+info.Ext) + if domainSubDir { + certPath = filepath.Join(dumpPath, cert.Domain.Main, info.Name+info.Ext) + if err := os.MkdirAll(filepath.Join(dumpPath, cert.Domain.Main), 0755); err != nil { + return err + } + } + + return ioutil.WriteFile(certPath, cert.Certificate, 0666) +} + +func writeKey(dumpPath string, cert *Certificate, info fileInfo, domainSubDir bool) error { + keyPath := filepath.Join(dumpPath, certsSubDir, cert.Domain.Main+info.Ext) + if domainSubDir { + keyPath = filepath.Join(dumpPath, cert.Domain.Main, info.Name+info.Ext) + if err := os.MkdirAll(filepath.Join(dumpPath, cert.Domain.Main), 0755); err != nil { + return err + } + } + + return ioutil.WriteFile(keyPath, cert.Key, 0666) +} + +func extractPEMPrivateKey(account *Account) []byte { + var block *pem.Block + switch account.KeyType { + case certcrypto.RSA2048, certcrypto.RSA4096, certcrypto.RSA8192: + block = &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: account.PrivateKey, + } + case certcrypto.EC256, certcrypto.EC384: + block = &pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: account.PrivateKey, + } + default: + panic("unsupported key type") + } + + return pem.EncodeToMemory(block) +} + +func tree(root, indent string) error { + fi, err := os.Stat(root) + if err != nil { + return fmt.Errorf("could not stat %s: %v", root, err) + } + + fmt.Println(fi.Name()) + if !fi.IsDir() { + return nil + } + + fis, err := ioutil.ReadDir(root) + if err != nil { + return fmt.Errorf("could not read dir %s: %v", root, err) + } + + var names []string + for _, fi := range fis { + if fi.Name()[0] != '.' { + names = append(names, fi.Name()) + } + } + + for i, name := range names { + add := "│ " + if i == len(names)-1 { + fmt.Printf(indent + "└──") + add = " " + } else { + fmt.Printf(indent + "├──") + } + + if err := tree(filepath.Join(root, name), indent+add); err != nil { + return err + } + } + + return nil +} diff --git a/dumper.go b/dumper.go index 1acf2f1..61b6a11 100644 --- a/dumper.go +++ b/dumper.go @@ -1,296 +1,49 @@ package main -import ( - "bytes" - "compress/gzip" - "encoding/json" - "encoding/pem" - "fmt" - "io/ioutil" - "os" - "path/filepath" - - "github.com/abronan/valkeyrie" - "github.com/abronan/valkeyrie/store" - "github.com/abronan/valkeyrie/store/boltdb" - "github.com/abronan/valkeyrie/store/consul" - etcdv3 "github.com/abronan/valkeyrie/store/etcd/v3" - "github.com/abronan/valkeyrie/store/zookeeper" - "github.com/xenolf/lego/certcrypto" - "github.com/xenolf/lego/registration" -) - -const ( - certsSubDir = "certs" - keysSubDir = "private" - storeKey = "traefik/acme/account/object" -) - -// StoredData represents the data managed by the Store -type StoredData struct { - Account *Account - Certificates []*Certificate - HTTPChallenges map[string]map[string][]byte - TLSChallenges map[string]*Certificate -} - -// Certificate is a struct which contains all data needed from an ACME certificate -type Certificate struct { - Domain Domain - Certificate []byte - Key []byte -} - -// Domain holds a domain name with SANs -type Domain struct { - Main string - SANs []string -} - -// Account is used to store lets encrypt registration info -type Account struct { - Email string - Registration *registration.Resource - PrivateKey []byte - KeyType certcrypto.KeyType -} - -type fileInfo struct { - Name string - Ext string -} - -// Backend represents a data source for ACME data -type Backend string +import "fmt" const ( // FILE backend - FILE Backend = "file" + FILE string = "file" // CONSUL backend - CONSUL Backend = "consul" + CONSUL string = "consul" // ETCD backend - ETCD Backend = "etcd" + ETCD string = "etcd" // ZK backend - ZK Backend = "zk" + ZK string = "zk" // BOLTDB backend - BOLTDB Backend = "boltdb" + BOLTDB string = "boltdb" ) -type dumpConfig struct { - Path string - CertInfo fileInfo - KeyInfo fileInfo - DomainSubDir bool - Watch bool +// Config represents a configuration for dumping cerificates +type Config struct { + Path string + CertInfo fileInfo + KeyInfo fileInfo + DomainSubDir bool + Watch bool + BackendConfig interface{} } -func getAcmeDataFromJSONFile(file string) (*StoredData, error) { - data := &StoredData{} - - f, err := os.Open(file) - if err != nil { - return data, err - } - fmt.Println(data) - - if err = json.NewDecoder(f).Decode(&data); err != nil { - return data, err - } - - return data, nil +// Backend represents an object storage of ACME data +type Backend interface { + loop(watch bool) (<-chan *StoredData, <-chan error) } -func getStoredDataFromGzip(value []byte) (*StoredData, error) { - data := &StoredData{} - - r, err := gzip.NewReader(bytes.NewBuffer(value)) - defer r.Close() - if err != nil { - return data, err - } - - acmeData, err := ioutil.ReadAll(r) - if err != nil { - return data, err - } - - storedData := &StoredData{} - if err := json.Unmarshal(acmeData, &storedData); err != nil { - return data, err - } - - return storedData, nil -} - -func loop(config *dumpConfig, backend Backend) error { - - // TODO change to env parameter - client := "localhost:8500" - - var storeBackend store.Backend - switch backend { - case CONSUL: - storeBackend = store.CONSUL - consul.Register() - case ETCD: - storeBackend = store.ETCDV3 - etcdv3.Register() - case ZK: - storeBackend = store.ZK - zookeeper.Register() - case BOLTDB: - storeBackend = store.BOLTDB - boltdb.Register() - } - kvstore, err := valkeyrie.NewStore( - storeBackend, - []string{client}, - &store.Config{}, - ) - if err != nil { - return err - } - - stopCh := make(<-chan struct{}) - events, _ := kvstore.Watch(storeKey, stopCh, nil) +func run(config *Config) error { + data, errors := config.BackendConfig.(Backend).loop(config.Watch) for { select { - case kvpair := <-events: - storedData, err := getStoredDataFromGzip(kvpair.Value) - if err != nil { - return err - } - if err := dump(config, storedData); err != nil { - return err - } - if !config.Watch { + case err := <-errors: + fmt.Println(err) + return err + case acmeData, ok := <-data: + if !ok { return nil } + if err := dump(config, acmeData); err != nil { + return err + } } } } - -func dump(config *dumpConfig, data *StoredData) error { - - if err := os.RemoveAll(config.Path); err != nil { - return err - } - - if !config.DomainSubDir { - if err := os.MkdirAll(filepath.Join(config.Path, certsSubDir), 0755); err != nil { - return err - } - } - - if err := os.MkdirAll(filepath.Join(config.Path, keysSubDir), 0755); err != nil { - return err - } - - privateKeyPem := extractPEMPrivateKey(data.Account) - err := ioutil.WriteFile(filepath.Join(config.Path, keysSubDir, "letsencrypt"+config.KeyInfo.Ext), privateKeyPem, 0666) - if err != nil { - return err - } - - for _, cert := range data.Certificates { - err := writeCert(config.Path, cert, config.CertInfo, config.DomainSubDir) - if err != nil { - return err - } - - err = writeKey(config.Path, cert, config.KeyInfo, config.DomainSubDir) - if err != nil { - return err - } - } - - if err := tree(config.Path, ""); err != nil { - return err - } - - return nil -} - -func writeCert(dumpPath string, cert *Certificate, info fileInfo, domainSubDir bool) error { - certPath := filepath.Join(dumpPath, certsSubDir, cert.Domain.Main+info.Ext) - if domainSubDir { - certPath = filepath.Join(dumpPath, cert.Domain.Main, info.Name+info.Ext) - if err := os.MkdirAll(filepath.Join(dumpPath, cert.Domain.Main), 0755); err != nil { - return err - } - } - - return ioutil.WriteFile(certPath, cert.Certificate, 0666) -} - -func writeKey(dumpPath string, cert *Certificate, info fileInfo, domainSubDir bool) error { - keyPath := filepath.Join(dumpPath, keysSubDir, cert.Domain.Main+info.Ext) - if domainSubDir { - keyPath = filepath.Join(dumpPath, cert.Domain.Main, info.Name+info.Ext) - if err := os.MkdirAll(filepath.Join(dumpPath, cert.Domain.Main), 0755); err != nil { - return err - } - } - - return ioutil.WriteFile(keyPath, cert.Key, 0666) -} - -func extractPEMPrivateKey(account *Account) []byte { - var block *pem.Block - switch account.KeyType { - case certcrypto.RSA2048, certcrypto.RSA4096, certcrypto.RSA8192: - block = &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: account.PrivateKey, - } - case certcrypto.EC256, certcrypto.EC384: - block = &pem.Block{ - Type: "EC PRIVATE KEY", - Bytes: account.PrivateKey, - } - default: - panic("unsupported key type") - } - - return pem.EncodeToMemory(block) -} - -func tree(root, indent string) error { - fi, err := os.Stat(root) - if err != nil { - return fmt.Errorf("could not stat %s: %v", root, err) - } - - fmt.Println(fi.Name()) - if !fi.IsDir() { - return nil - } - - fis, err := ioutil.ReadDir(root) - if err != nil { - return fmt.Errorf("could not read dir %s: %v", root, err) - } - - var names []string - for _, fi := range fis { - if fi.Name()[0] != '.' { - names = append(names, fi.Name()) - } - } - - for i, name := range names { - add := "│ " - if i == len(names)-1 { - fmt.Printf(indent + "└──") - add = " " - } else { - fmt.Printf(indent + "├──") - } - - if err := tree(filepath.Join(root, name), indent+add); err != nil { - return err - } - } - - return nil -} diff --git a/file.go b/file.go new file mode 100644 index 0000000..ac349c0 --- /dev/null +++ b/file.go @@ -0,0 +1,82 @@ +package main + +import ( + "encoding/json" + "os" + + "github.com/fsnotify/fsnotify" +) + +// FileBackend stores the config for file backend +type FileBackend struct { + Name string + Path string +} + +func getStoredDataFromFile(path string) (*StoredData, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + data := &StoredData{} + if err = json.NewDecoder(f).Decode(&data); err != nil { + return nil, err + } + return data, nil +} + +func (b FileBackend) loop(watch bool) (<-chan *StoredData, <-chan error) { + + dataCh := make(chan *StoredData) + errCh := make(chan error) + go func() { + data, err := getStoredDataFromFile(b.Path) + if err != nil { + errCh <- err + } + dataCh <- data + if !watch { + close(dataCh) + close(errCh) + } + }() + + if !watch { + return dataCh, errCh + } + + watcher, err := fsnotify.NewWatcher() + if err != nil { + errCh <- err + } + + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + if event.Op&fsnotify.Write == fsnotify.Write { + data, err := getStoredDataFromFile(b.Path) + if err != nil { + errCh <- err + } + dataCh <- data + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + errCh <- err + } + } + }() + + err = watcher.Add(b.Path) + if err != nil { + errCh <- err + } + + return dataCh, errCh +} diff --git a/go.mod b/go.mod index f34ce46..eba3032 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,48 @@ module github.com/ldez/traefik-certs-dumper +go 1.12 + require ( github.com/abronan/valkeyrie v0.0.0-20190313085051-8fd1c7cad28d github.com/cenkalti/backoff v2.1.1+incompatible // indirect - github.com/hashicorp/consul v1.4.4 - github.com/hashicorp/go-cleanhttp v0.5.1 // indirect - github.com/hashicorp/serf v0.8.3 // indirect + github.com/containous/traefik v1.7.10 + github.com/coreos/bbolt v1.3.2 // indirect + github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect + github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/fsnotify/fsnotify v1.4.7 + github.com/go-acme/lego v2.4.0+incompatible // indirect + github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect + github.com/google/btree v1.0.0 // indirect + github.com/gorilla/websocket v1.4.0 // indirect + github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect + github.com/hashicorp/go-uuid v1.0.1 // indirect + github.com/hashicorp/memberlist v0.1.3 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jonboulle/clockwork v0.1.0 // indirect + github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/pascaldekloe/goe v0.1.0 // indirect + github.com/philhofer/fwd v1.0.0 // indirect + github.com/pkg/errors v0.8.1 // indirect + github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7 // indirect + github.com/prometheus/client_golang v0.9.2 // indirect + github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect + github.com/sirupsen/logrus v1.4.1 // indirect + github.com/soheilhy/cmux v0.1.4 // indirect github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 // indirect - github.com/xenolf/lego v2.2.0+incompatible - golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f // indirect - gopkg.in/square/go-jose.v2 v2.2.2 // indirect + github.com/tinylib/msgp v1.1.0 // indirect + github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect + github.com/xenolf/lego v2.4.0+incompatible + github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect + go.uber.org/atomic v1.3.2 // indirect + go.uber.org/multierr v1.1.0 // indirect + go.uber.org/zap v1.9.1 // indirect + golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af // indirect + golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect + gopkg.in/square/go-jose.v2 v2.3.1 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect ) diff --git a/go.sum b/go.sum index 496a48d..a337090 100644 --- a/go.sum +++ b/go.sum @@ -1,123 +1,212 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/abronan/valkeyrie v0.0.0-20190313085051-8fd1c7cad28d h1:P4aevzwd2Xm9RzPSfOWQMfhAVu5/jGVI3/7BKgoOG94= github.com/abronan/valkeyrie v0.0.0-20190313085051-8fd1c7cad28d/go.mod h1:A/IdrwsPb8nboz577OaOlF9StXVQHPXus26p6ZOEhZM= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.16.23/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containous/traefik v1.7.10 h1:kLwkz/+MK8LAJVyK07O2fLXFe3sdsJjVOXSohwXlElU= +github.com/containous/traefik v1.7.10/go.mod h1:epDRqge3JzKOhlSWzOpNYEEKXmM6yfN5tPzDGKk3ljo= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.11+incompatible h1:0gCnqKsq7XxMi69JsnbmMc1o+RJH3XH64sV9aiTTYko= github.com/coreos/etcd v3.3.11+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-acme/lego v2.4.0+incompatible h1:+BTLUfLtDc5qQauyiTCXH6lupEUOCvXyGlEjdeU0YQI= +github.com/go-acme/lego v2.4.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw= github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= -github.com/hashicorp/consul v1.4.4 h1:DR1+5EGgnPsd/LIsK3c9RDvajcsV5GOkGQBSNd3dpn8= -github.com/hashicorp/consul v1.4.4/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= -github.com/hashicorp/consul/api v1.0.1 h1:LkHu3cLXjya4lgrAyZVe/CUBXgJ7AcDWKSeCjAYN9w0= -github.com/hashicorp/consul/api v1.0.1/go.mod h1:LQlewHPiuaRhn1mP2XE4RrjnlRgOeWa/ZM0xWLCen2M= -github.com/hashicorp/consul/sdk v0.1.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.1 h1:mYs6SMzu72+90OcPa5wr3nfznA4Dw9UyR791ZFNOIf4= github.com/hashicorp/serf v0.8.1/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE= -github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/serf v0.8.3 h1:MWYcmct5EtKz0efYooPcL0yNkem+7kWxqXDi/UIh+8k= -github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983 h1:wL11wNW7dhKIcRCHSm4sHKPWz0tt4mwBsVodG7+Xyqg= +github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= +github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7 h1:gGBSHPOU7g8YjTbhwn+lvFm2VDEhhA+PwDIlstkgSxE= +github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= +github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec h1:6ncX5ko6B9LntYM0YBRXkiSaZMmLYeZ/NWcmeB43mMY= github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= +github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1 h1:UvhxfNjNqlZ/x3cDyqxMhoiUpemd3zXkVQApN6bM/lg= github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= -github.com/xenolf/lego v2.2.0+incompatible h1:r4UAcpgPmX3j0aThoVrRM1FFLcvyy08UyGbIwFU4zoQ= -github.com/xenolf/lego v2.2.0+incompatible/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY= +github.com/xenolf/lego v2.4.0+incompatible h1:xJBVNh+sucwOQxrTNGFTzAteKzxBG9UPpZf1W9r5GCk= +github.com/xenolf/lego v2.4.0+incompatible/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= go.etcd.io/bbolt v1.3.1-etcd.8 h1:6J7QAKqfFBGnU80KRnuQxfjjeE5xAGE/qB810I3FQHQ= go.etcd.io/bbolt v1.3.1-etcd.8/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v3.3.11+incompatible h1:AVwRXu9VIzZcvVe1nSirTVkNv7WT3/hwdMRrDVFsf3A= go.etcd.io/etcd v3.3.11+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= +go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f h1:ETU2VEl7TnT5bl7IvuKEzTDpplg5wzGYsOCAPhdoEIg= -golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af h1:6qGQw30u837TXZbCmLFR9AVA+RjJU1LIbvk0oIkDZGY= +golang.org/x/crypto v0.0.0-20190417174047-f416ebab96af/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522 h1:Ve1ORMCxvRmSXBwJK+t3Oy+V2vRW2OetUQBq4rJIkZE= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuqEASK6ob3auvWYM4/8U= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e h1:nFYrTHrdrAOpShe27kaFHjsqYSEQ0KWqdWLu3xuZJts= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/grpc v1.18.0 h1:IZl7mfBGfbhYx2p2rKRtYgDFw6SBz+kclmxYrCksPPA= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/redis.v5 v5.2.9/go.mod h1:6gtv0/+A4iM08kdRfocWYB3bLX2tebpNtfKlFT6H4mY= -gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/kv.go b/kv.go new file mode 100644 index 0000000..a02a1b0 --- /dev/null +++ b/kv.go @@ -0,0 +1,110 @@ +package main + +import ( + "bytes" + "compress/gzip" + "encoding/json" + "fmt" + "io/ioutil" + + "github.com/abronan/valkeyrie" + "github.com/abronan/valkeyrie/store" + "github.com/abronan/valkeyrie/store/boltdb" + "github.com/abronan/valkeyrie/store/consul" + etcdv3 "github.com/abronan/valkeyrie/store/etcd/v3" + "github.com/abronan/valkeyrie/store/zookeeper" +) + +const ( + storeKey = "traefik/acme/account/object" +) + +func getStoredDataFromGzip(value []byte) (*StoredData, error) { + data := &StoredData{} + + r, err := gzip.NewReader(bytes.NewBuffer(value)) + defer r.Close() + if err != nil { + return data, err + } + + acmeData, err := ioutil.ReadAll(r) + if err != nil { + return data, err + } + + storedData := &StoredData{} + if err := json.Unmarshal(acmeData, &storedData); err != nil { + return data, err + } + + return storedData, nil +} + +// KVBackend represents a Key/Value pair backend +type KVBackend struct { + Name string + Client []string + Config *store.Config +} + +func register(backend string) (store.Backend, error) { + switch backend { + case CONSUL: + consul.Register() + return store.CONSUL, nil + case ETCD: + etcdv3.Register() + return store.ETCDV3, nil + case ZK: + zookeeper.Register() + return store.ZK, nil + case BOLTDB: + boltdb.Register() + return store.BOLTDB, nil + default: + return "", fmt.Errorf("No backend found for %v", backend) + } +} + +func (b KVBackend) loop(watch bool) (<-chan *StoredData, <-chan error) { + + dataCh := make(chan *StoredData) + errors := make(chan error) + + backend, err := register(b.Name) + if err != nil { + errors <- err + } + + kvstore, err := valkeyrie.NewStore( + backend, + b.Client, + b.Config, + ) + if err != nil { + errors <- err + } + + go func() { + stopCh := make(<-chan struct{}) + events, _ := kvstore.Watch(storeKey, stopCh, nil) + for { + select { + case kvpair := <-events: + storedData, err := getStoredDataFromGzip(kvpair.Value) + if err != nil { + errors <- err + } + dataCh <- storedData + } + if !watch { + close(dataCh) + close(errors) + } + } + }() + + return dataCh, errors + +} diff --git a/main.go b/main.go index a997d5a..a817a79 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "os" "strconv" + "github.com/abronan/valkeyrie/store" "github.com/spf13/cobra" ) @@ -16,7 +17,7 @@ func main() { Version: version, } - dumpConfig := &dumpConfig{} + config := &Config{} var dumpCmd = &cobra.Command{ Use: "dump", @@ -50,56 +51,78 @@ func main() { source := cmd.Flag("source").Value.String() acmeFile := cmd.Flag("source-file").Value.String() - var backend Backend switch source { case "file": - backend = FILE + config.BackendConfig = FileBackend{ + Name: FILE, + Path: acmeFile, + } case "consul": - backend = CONSUL + config.BackendConfig = KVBackend{ + Name: CONSUL, + Client: []string{"localhost:8500"}, + Config: &store.Config{}, + } case "etcd": - backend = ETCD + config.BackendConfig = KVBackend{ + Name: ETCD, + Client: []string{"localhost:8500"}, + Config: &store.Config{}, + } case "zookeeper": - backend = ZK + config.BackendConfig = KVBackend{ + Name: ZK, + Client: []string{"localhost:8500"}, + Config: &store.Config{}, + } case "boltdb": - backend = BOLTDB + config.BackendConfig = KVBackend{ + Name: BOLTDB, + Client: []string{"localhost:8500"}, + Config: &store.Config{}, + } } - dumpConfig.Path = cmd.Flag("dest").Value.String() + config.Path = cmd.Flag("dest").Value.String() - dumpConfig.CertInfo = fileInfo{ + config.CertInfo = fileInfo{ Name: cmd.Flag("crt-name").Value.String(), Ext: cmd.Flag("crt-ext").Value.String(), } - dumpConfig.KeyInfo = fileInfo{ + config.KeyInfo = fileInfo{ Name: cmd.Flag("key-name").Value.String(), Ext: cmd.Flag("key-ext").Value.String(), } - dumpConfig.DomainSubDir, _ = strconv.ParseBool(cmd.Flag("domain-subdir").Value.String()) - dumpConfig.Watch, _ = strconv.ParseBool(cmd.Flag("watch").Value.String()) + config.DomainSubDir, _ = strconv.ParseBool(cmd.Flag("domain-subdir").Value.String()) + config.Watch, _ = strconv.ParseBool(cmd.Flag("watch").Value.String()) - fmt.Println(dumpConfig) - - if backend == FILE { - data, err := getAcmeDataFromJSONFile(acmeFile) - if err != nil { - return fmt.Errorf("[ERR] %v", err) - } - if err := dump(dumpConfig, data); err != nil { - return err - } - } else if err := loop(dumpConfig, backend); err != nil { - return err + if err := run(config); err != nil { + fmt.Println(err) } return nil }, } - // TODO fill readme - dumpCmd.Flags().String("source", "file", "Source type. One of 'file', 'consul', 'etcd', 'zookeeper', 'boltdb'. For configuration options of the Key/Value stores see https://github.com/ldez/traefik-certs-dumper#configuration-of-key-value-stores.") - dumpCmd.Flags().String("source-file", "./acme.json", "Path to 'acme.json' file if source type is 'file'") + dumpCmd.Flags().String("source", "file", "Source type. One of 'file', 'consul', 'etcd', 'zookeeper', 'boltdb'.") + dumpCmd.Flags().String("file", "./acme.json", "Path to 'acme.json' file if source type is 'file'") + + /* TODO implement this + dumpCmd.Flags().String("kv.client") + dumpCmd.Flags().String("kv.connection-timeout") + dumpCmd.Flags().String("kv.sync-period") + dumpCmd.Flags().String("kv.bucket") + dumpCmd.Flags().Bool("kv.persist-connection") + dumpCmd.Flags().String("kv.username") + dumpCmd.Flags().String("kv.password") + dumpCmd.Flags().String("kv.token") + dumpCmd.Flags().String("kv.tls-cert-file") + dumpCmd.Flags().String("kv.tls-key-file") + dumpCmd.Flags().String("kv.tls-ca-cert-file") + */ + dumpCmd.Flags().Bool("watch", true, "Enable watching changes.") dumpCmd.Flags().String("dest", "./dump", "Path to store the dump content.") dumpCmd.Flags().String("crt-ext", ".crt", "The file extension of the generated certificates.") diff --git a/readme.md b/readme.md index 11209ff..6a33fa4 100644 --- a/readme.md +++ b/readme.md @@ -78,21 +78,6 @@ Flags: --source string Path to 'acme.json' file. (default "./acme.json") ``` -### Configuration of Key/Value stores - -#### Consul - -Consul connection parameters can be set via environment variables, see https://www.consul.io/docs/commands/index.html#environment-variables. - -#### Etcd - -Version 3 is not supported. - -#### Boltdb - -#### Zookeeper - - ## Examples ### Simple Dump