use abronan/valkeyrie for multiple backend support

This commit is contained in:
Stephan Müller 2019-04-18 00:09:05 +02:00
parent 732e325df2
commit c2252e8114
No known key found for this signature in database
GPG Key ID: 4650F39E5B5E1894
5 changed files with 232 additions and 87 deletions

194
dumper.go
View File

@ -10,8 +10,12 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/hashicorp/consul/api" "github.com/abronan/valkeyrie"
consulwatch "github.com/hashicorp/consul/watch" "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/certcrypto"
"github.com/xenolf/lego/registration" "github.com/xenolf/lego/registration"
) )
@ -19,6 +23,7 @@ import (
const ( const (
certsSubDir = "certs" certsSubDir = "certs"
keysSubDir = "private" keysSubDir = "private"
storeKey = "traefik/acme/account/object"
) )
// StoredData represents the data managed by the Store // StoredData represents the data managed by the Store
@ -55,100 +60,155 @@ type fileInfo struct {
Ext string Ext string
} }
func dump(data StoredData, dumpPath string, crtInfo, keyInfo fileInfo, domainSubDir bool) error { // Backend represents a data source for ACME data
type Backend string
if err := os.RemoveAll(dumpPath); err != nil { const (
// FILE backend
FILE Backend = "file"
// CONSUL backend
CONSUL Backend = "consul"
// ETCD backend
ETCD Backend = "etcd"
// ZK backend
ZK Backend = "zk"
// BOLTDB backend
BOLTDB Backend = "boltdb"
)
type dumpConfig struct {
Path string
CertInfo fileInfo
KeyInfo fileInfo
DomainSubDir bool
Watch bool
}
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
}
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{}
json.Unmarshal(acmeData, &storedData)
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 return err
} }
if !domainSubDir { stopCh := make(<-chan struct{})
if err := os.MkdirAll(filepath.Join(dumpPath, certsSubDir), 0755); err != nil { events, _ := kvstore.Watch(storeKey, stopCh, nil)
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 {
return nil
}
}
}
}
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 return err
} }
} }
if err := os.MkdirAll(filepath.Join(dumpPath, keysSubDir), 0755); err != nil { if err := os.MkdirAll(filepath.Join(config.Path, keysSubDir), 0755); err != nil {
return err return err
} }
privateKeyPem := extractPEMPrivateKey(data.Account) privateKeyPem := extractPEMPrivateKey(data.Account)
err := ioutil.WriteFile(filepath.Join(dumpPath, keysSubDir, "letsencrypt"+keyInfo.Ext), privateKeyPem, 0666) err := ioutil.WriteFile(filepath.Join(config.Path, keysSubDir, "letsencrypt"+config.KeyInfo.Ext), privateKeyPem, 0666)
if err != nil { if err != nil {
return err return err
} }
for _, cert := range data.Certificates { for _, cert := range data.Certificates {
err := writeCert(dumpPath, cert, crtInfo, domainSubDir) err := writeCert(config.Path, cert, config.CertInfo, config.DomainSubDir)
if err != nil { if err != nil {
return err return err
} }
err = writeKey(dumpPath, cert, keyInfo, domainSubDir) err = writeKey(config.Path, cert, config.KeyInfo, config.DomainSubDir)
if err != nil { if err != nil {
return err return err
} }
} }
if err := tree(config.Path, ""); err != nil {
return err
}
return nil return nil
} }
func dumpFile(acmeFile string, dumpPath string, crtInfo, keyInfo fileInfo, domainSubDir bool) error {
f, err := os.Open(acmeFile)
if err != nil {
return err
}
data := StoredData{}
if err = json.NewDecoder(f).Decode(&data); err != nil {
return err
}
return dump(data, dumpPath, crtInfo, keyInfo, domainSubDir)
}
func dumpConsul(watch bool, dumpPath string, crtInfo, keyInfo fileInfo, domainSubDir bool) {
params := map[string]interface{}{
"type": "key",
"key": "traefik/acme/account/object",
}
plan, _ := consulwatch.Parse(params)
plan.Handler = func(idx uint64, data interface{}) {
// TODO is here a better way?
var buf bytes.Buffer
json.NewEncoder(&buf).Encode(data)
kvpair := api.KVPair{}
json.Unmarshal(buf.Bytes(), &kvpair)
r, err := gzip.NewReader(bytes.NewBuffer(kvpair.Value))
defer r.Close()
if err != nil {
fmt.Printf("[ERR] %s", err)
}
acmeData, err := ioutil.ReadAll(r)
if err != nil {
fmt.Printf("[ERR] %s", err)
}
storedData := StoredData{}
json.Unmarshal(acmeData, &storedData)
dump(storedData, dumpPath, crtInfo, keyInfo, domainSubDir)
if err := tree(dumpPath, ""); err != nil {
fmt.Printf("[ERR] %s", err)
}
if !watch {
plan.Stop()
}
}
config := api.DefaultConfig()
fmt.Println("Start watching consul...")
plan.Run(config.Address)
}
func writeCert(dumpPath string, cert *Certificate, info fileInfo, domainSubDir bool) error { func writeCert(dumpPath string, cert *Certificate, info fileInfo, domainSubDir bool) error {
certPath := filepath.Join(dumpPath, certsSubDir, cert.Domain.Main+info.Ext) certPath := filepath.Join(dumpPath, certsSubDir, cert.Domain.Main+info.Ext)
if domainSubDir { if domainSubDir {

3
go.mod
View File

@ -1,13 +1,12 @@
module github.com/ldez/traefik-certs-dumper module github.com/ldez/traefik-certs-dumper
require ( require (
github.com/abronan/valkeyrie v0.0.0-20190313085051-8fd1c7cad28d
github.com/cenkalti/backoff v2.1.1+incompatible // indirect github.com/cenkalti/backoff v2.1.1+incompatible // indirect
github.com/hashicorp/consul v1.4.4 github.com/hashicorp/consul v1.4.4
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.0 // indirect
github.com/hashicorp/serf v0.8.3 // indirect github.com/hashicorp/serf v0.8.3 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.1.2 // indirect
github.com/spf13/cobra v0.0.3 github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect github.com/spf13/pflag v1.0.3 // indirect
github.com/xenolf/lego v2.2.0+incompatible github.com/xenolf/lego v2.2.0+incompatible

43
go.sum
View File

@ -1,21 +1,37 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
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/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 h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 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/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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY= 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/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
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/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
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/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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 v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
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 h1:DR1+5EGgnPsd/LIsK3c9RDvajcsV5GOkGQBSNd3dpn8=
github.com/hashicorp/consul v1.4.4/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= 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 h1:LkHu3cLXjya4lgrAyZVe/CUBXgJ7AcDWKSeCjAYN9w0=
github.com/hashicorp/consul/api v1.0.1/go.mod h1:LQlewHPiuaRhn1mP2XE4RrjnlRgOeWa/ZM0xWLCen2M= 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/consul/sdk v0.1.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
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 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 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 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
@ -34,12 +50,15 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 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/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
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 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 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 h1:MWYcmct5EtKz0efYooPcL0yNkem+7kWxqXDi/UIh+8k=
github.com/hashicorp/serf v0.8.3/go.mod h1:UpNcs7fFbpKIyZaUuSW6EPiH+eZC7OuyFD+wc1oal+k= 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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 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/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-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/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@ -57,6 +76,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
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/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
@ -66,15 +87,37 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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 h1:r4UAcpgPmX3j0aThoVrRM1FFLcvyy08UyGbIwFU4zoQ=
github.com/xenolf/lego v2.2.0+incompatible/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY= github.com/xenolf/lego v2.2.0+incompatible/go.mod h1:fwiGnfsIjG7OHPfOvgK7Y/Qo6+2Ox0iozjNTkZICKbY=
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=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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 h1:ETU2VEl7TnT5bl7IvuKEzTDpplg5wzGYsOCAPhdoEIg=
golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190208162236-193df9c0f06f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
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-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-20181201002055-351d144fa1fc/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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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/tools v0.0.0-20180828015842-6cd1fcedba52/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=
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 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

64
main.go
View File

@ -16,14 +16,21 @@ func main() {
Version: version, Version: version,
} }
dumpConfig := &dumpConfig{}
var dumpCmd = &cobra.Command{ var dumpCmd = &cobra.Command{
Use: "dump", Use: "dump",
Short: "Dump Let's Encrypt certificates from Traefik", Short: "Dump Let's Encrypt certificates from Traefik",
Long: `Dump the content of the "acme.json" file from Traefik to certificates.`, Long: `Dump the content of the "acme.json" file from Traefik to certificates.`,
PreRunE: func(cmd *cobra.Command, args []string) error { PreRunE: func(cmd *cobra.Command, args []string) error {
source := cmd.Flag("source").Value.String() source := cmd.Flag("source").Value.String()
if source != "file" && source != "consul" { sourceFile := cmd.Flag("source-file").Value.String()
return fmt.Errorf("--source (%q) is not allowed, use one of 'file' or 'consul'", source) if source == "file" {
if _, err := os.Stat(sourceFile); os.IsNotExist(err) {
return fmt.Errorf("--source-file (%q) does not exist", sourceFile)
}
} else if source != "consul" && source != "etcd" && source != "zookeeper" && source != "boltdb" {
return fmt.Errorf("--source (%q) is not allowed, use one of 'file', 'consul', 'etcd', 'zookeeper', 'boltdb'", source)
} }
crtExt := cmd.Flag("crt-ext").Value.String() crtExt := cmd.Flag("crt-ext").Value.String()
@ -39,42 +46,63 @@ func main() {
return nil return nil
}, },
RunE: func(cmd *cobra.Command, _ []string) error { RunE: func(cmd *cobra.Command, _ []string) error {
acmeFile := cmd.Flag("source-file").Value.String()
dumpPath := cmd.Flag("dest").Value.String()
crtInfo := fileInfo{ source := cmd.Flag("source").Value.String()
acmeFile := cmd.Flag("source-file").Value.String()
var backend Backend
switch source {
case "file":
backend = FILE
case "consul":
backend = CONSUL
case "etcd":
backend = ETCD
case "zookeeper":
backend = ZK
case "boltdb":
backend = BOLTDB
}
dumpConfig.Path = cmd.Flag("dest").Value.String()
dumpConfig.CertInfo = fileInfo{
Name: cmd.Flag("crt-name").Value.String(), Name: cmd.Flag("crt-name").Value.String(),
Ext: cmd.Flag("crt-ext").Value.String(), Ext: cmd.Flag("crt-ext").Value.String(),
} }
keyInfo := fileInfo{ dumpConfig.KeyInfo = fileInfo{
Name: cmd.Flag("key-name").Value.String(), Name: cmd.Flag("key-name").Value.String(),
Ext: cmd.Flag("key-ext").Value.String(), Ext: cmd.Flag("key-ext").Value.String(),
} }
subDir, _ := strconv.ParseBool(cmd.Flag("domain-subdir").Value.String()) dumpConfig.DomainSubDir, _ = strconv.ParseBool(cmd.Flag("domain-subdir").Value.String())
watchConsul, _ := strconv.ParseBool(cmd.Flag("source-consul-watch").Value.String()) dumpConfig.Watch, _ = strconv.ParseBool(cmd.Flag("watch").Value.String())
switch cmd.Flag("source").Value.String() { fmt.Println(dumpConfig)
case "consul": if backend == FILE {
dumpConsul(watchConsul, dumpPath, crtInfo, keyInfo, subDir) data, err := getAcmeDataFromJSONFile(acmeFile)
case "file":
default:
err := dumpFile(acmeFile, dumpPath, crtInfo, keyInfo, subDir)
if err != nil { 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 return err
} }
return tree(dumpPath, "")
} }
return nil return nil
}, },
} }
dumpCmd.Flags().String("source", "file", "Source type. One of 'file' or 'consul'. Consul connection parameters can be set via environment variables, see https://www.consul.io/docs/commands/index.html#environment-variables") // 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", "./acme.json", "Path to 'acme.json' file if source type is 'file'")
dumpCmd.Flags().Bool("source-consul-watch", true, "Enable watching changes in Consul.") dumpCmd.Flags().Bool("watch", true, "Enable watching changes.")
dumpCmd.Flags().String("dest", "./dump", "Path to store the dump content.") dumpCmd.Flags().String("dest", "./dump", "Path to store the dump content.")
dumpCmd.Flags().String("crt-ext", ".crt", "The file extension of the generated certificates.") dumpCmd.Flags().String("crt-ext", ".crt", "The file extension of the generated certificates.")
dumpCmd.Flags().String("crt-name", "certificate", "The file name (without extension) of the generated certificates.") dumpCmd.Flags().String("crt-name", "certificate", "The file name (without extension) of the generated certificates.")

View File

@ -78,6 +78,21 @@ Flags:
--source string Path to 'acme.json' file. (default "./acme.json") --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 ## Examples
### Simple Dump ### Simple Dump