Compare commits

..

231 Commits

Author SHA1 Message Date
dependabot[bot]
0d8d35889d
chore: bump github.com/go-acme/lego/v4 from 4.22.2 to 4.25.2 (#224) 2025-08-06 21:55:29 +02:00
vgdh
ef2b9f9e1b
docs: disable network access inside examples (#223) 2025-07-06 22:27:30 +02:00
dependabot[bot]
d83e436bbb
chore: bump golang.org/x/net from 0.37.0 to 0.38.0 (#219) 2025-04-17 01:46:09 +02:00
Fernandez Ludovic
00ebf584ff chore: update linter 2025-03-25 03:34:46 +01:00
Fernandez Ludovic
38dd4b1898 chore: update linter 2025-03-09 19:36:21 +01:00
Fernandez Ludovic
ece40e90d6 chore: update dependencies 2025-03-09 19:35:45 +01:00
Fernandez Ludovic
5e2a080d2a chore: publish Docker images in GHCR 2025-03-09 19:32:33 +01:00
Fernandez Ludovic
07decbb976 chore: restrict workflows 2025-03-09 19:32:33 +01:00
Fernandez Ludovic
9a7fa88d60 docs: update readme 2025-02-21 22:35:42 +01:00
Fernandez Ludovic
a32520bcba docs: update examples 2025-02-21 22:34:35 +01:00
Fernandez Ludovic
39b542952b docs: update documentation 2025-02-21 22:30:27 +01:00
Fernandez Ludovic
b0aafdcc54 docs: improve readme 2025-02-13 03:04:45 +01:00
Fernandez Ludovic
565e4316ca chore: linting 2025-02-13 03:03:12 +01:00
Fernandez Ludovic
961192eb9b chore: update dependencies and Go version 2025-02-13 03:00:13 +01:00
Fernandez Ludovic
1bd1c094f2 chore: update linter 2025-02-13 02:53:42 +01:00
Fernandez Ludovic
380fa0efda chore: update linter 2025-02-13 02:51:32 +01:00
Ludovic Fernandez
059bfd27eb
chore: update donation URL 2025-01-24 22:38:24 +01:00
Fernandez Ludovic
45954b02ca chore: update funding 2025-01-06 20:17:49 +01:00
dependabot[bot]
e1b924f614
chore: bump golang.org/x/crypto from 0.28.0 to 0.31.0 (#212) 2024-12-12 02:10:59 +01:00
Blackmoon
865dceddf9
docs: adds all counts to one sum (#211) 2024-12-06 13:12:30 +00:00
Fernandez Ludovic
172bd870c6 feat: improve binary size 2024-12-05 14:58:48 +01:00
Fernandez Ludovic
ee3231c879 docs: fix examples 2024-12-05 14:51:38 +01:00
Fernandez Ludovic
d18ae5e249 fix: Docker image 2024-12-05 13:29:27 +01:00
Fernandez Ludovic
48166b4990 chore: update dependencies, and linter 2024-12-04 22:43:30 +01:00
Fernandez Ludovic
ec006d3466 chore: use goreleaser to publish Docker images 2024-12-04 22:43:30 +01:00
Fernandez Ludovic
754051fcb4 chore: help popup 2024-12-04 22:43:30 +01:00
Fernandez Ludovic
a4f74712d8 chore: update workflow 2024-12-02 22:19:30 +01:00
dependabot[bot]
4f9ee0b2aa
chore: bump github.com/traefik/traefik/v3 from 3.2.0 to 3.2.1 (#208) 2024-12-02 18:56:52 +01:00
dependabot[bot]
06a6f04be9
chore: bump github.com/traefik/traefik/v2 from 2.11.13 to 2.11.14 (#207) 2024-12-02 17:36:13 +00:00
Fernandez Ludovic
094a429688 fix: release workflow 2024-11-08 20:20:01 +01:00
Fernandez Ludovic
520caaf67f feat: dedicated Traefik v3 implementation 2024-11-08 18:34:59 +01:00
dependabot[bot]
cffec51d48
chore: bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 (#204) 2024-11-05 01:45:35 +01:00
dependabot[bot]
8ccd28a009
chore: bump github.com/traefik/traefik/v2 from 2.11.6 to 2.11.9 (#202) 2024-09-19 18:21:18 +00:00
Fernandez Ludovic
5ecc336d73 chore: update Go, linter, dependencies, and CI 2024-09-19 20:09:28 +02:00
Fernandez Ludovic
8fb6765066 chore: update Go, linter, dependencies, and CI 2024-09-19 20:06:57 +02:00
dependabot[bot]
3e8a362a1b
chore: bump github.com/traefik/traefik/v2 from 2.11.5 to 2.11.6 (#201) 2024-07-09 22:20:31 +00:00
dependabot[bot]
7a3d5d053b
chore: bump github.com/traefik/traefik/v2 from 2.11.4 to 2.11.5 (#200) 2024-06-20 16:42:36 +00:00
dependabot[bot]
6cd6e89652
chore: bump github.com/Azure/azure-sdk-for-go/sdk/azidentity (#199) 2024-06-11 23:57:42 +02:00
dependabot[bot]
22beab49f0
chore: bump github.com/traefik/traefik/v2 from 2.11.3 to 2.11.4 (#198) 2024-06-11 21:57:12 +02:00
Fernandez Ludovic
72a6e2e8e4 docs: add Traefik v3 2024-05-29 06:04:18 +02:00
Fernandez Ludovic
588ece1608 docs: update Traefik version 2024-05-29 06:03:31 +02:00
Fernandez Ludovic
cbe95869b0 chore: update license 2024-05-29 06:03:14 +02:00
Fernandez Ludovic
21c8f5fa9c chore: update linter 2024-05-29 06:03:00 +02:00
dependabot[bot]
2d6d95b809
chore: bump github.com/traefik/traefik/v2 from 2.11.2 to 2.11.3 (#197) 2024-05-23 15:31:48 +00:00
dependabot[bot]
09413952ec
chore: bump github.com/traefik/traefik/v2 from 2.11.0 to 2.11.2 (#196) 2024-04-12 18:16:19 +00:00
Fernandez Ludovic
3e89131ae2 chore: update Go, dependencies, and linter 2024-04-12 20:05:47 +02:00
dependabot[bot]
381e90d319
chore: bump google.golang.org/protobuf from 1.31.0 to 1.33.0 (#194) 2024-03-13 23:26:05 +00:00
dependabot[bot]
b70afe012b
chore: bump github.com/go-jose/go-jose/v3 from 3.0.1 to 3.0.3 (#193) 2024-03-08 00:15:18 +01:00
Fernandez Ludovic
a437576753 chore: update sponsoring sources 2024-01-31 20:50:54 +01:00
Fernandez Ludovic
8c96f62349 chore: update Go, dependencies, and linter 2024-01-31 20:36:49 +01:00
dependabot[bot]
f0b114e8d8
chore: bump golang.org/x/crypto from 0.14.0 to 0.17.0 (#192) 2023-12-18 23:40:10 +00:00
Fernandez Ludovic
7b2c71435e chore: fix CI 2023-12-05 23:05:16 +01:00
dependabot[bot]
20d34c6c0a
chore: bump github.com/traefik/traefik/v2 from 2.10.3 to 2.10.6 (#191)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2023-12-05 19:54:28 +01:00
dependabot[bot]
b1ebb09184
chore: bump github.com/go-jose/go-jose/v3 from 3.0.0 to 3.0.1 (#190) 2023-11-22 00:50:33 +01:00
dependabot[bot]
756e93fbbb
chore: bump google.golang.org/grpc from 1.55.0 to 1.56.3 (#189) 2023-10-26 12:42:12 +02:00
dependabot[bot]
40e28f9677
chore: bump golang.org/x/net from 0.11.0 to 0.17.0 (#188) 2023-10-12 01:27:59 +02:00
Fernandez Ludovic
9d10b07e10 chore: update Go, and linter 2023-07-23 23:16:21 +02:00
Fernandez Ludovic
1ad5a1eb42 chore: update Go, linter, and dependencies 2023-05-29 22:00:08 +02:00
dependabot[bot]
d60c333193
chore: bump github.com/traefik/traefik/v2 from 2.9.8 to 2.9.10 (#185) 2023-04-12 08:24:35 +02:00
Fernandez Ludovic
a9d5a3da9d chore: update Go, CI, and linter 2023-02-22 10:06:39 +01:00
Fernandez Ludovic
c11dd6630f feat: update traefik and valkeyrie 2023-01-29 16:42:14 +01:00
dependabot[bot]
78234b674c
chore: bump github.com/traefik/traefik/v2 from 2.8.3 to 2.8.8 (#181) 2022-10-10 23:48:31 +02:00
Fernandez Ludovic
7b9a0b72d6 chore: update Go, linter, and dependencies 2022-08-29 08:57:30 +02:00
Fernandez Ludovic
a645d164e0 chore: update whoami 2022-07-28 03:52:54 +02:00
Fernandez Ludovic
f58a7a9b06 chore: update dependencies 2022-07-27 23:01:35 +02:00
Fernandez Ludovic
cd16576a1f chore: update linter 2022-07-27 22:00:35 +02:00
Fernandez Ludovic
b02023370b doc: note about contrib directory 2022-07-27 21:57:03 +02:00
Fernandez Ludovic
3acdd86c88 doc: update versions 2022-07-27 21:54:50 +02:00
Jens Kohl
52a46ce079
docs: add systemd service unit (#178) 2022-07-17 12:23:48 +00:00
dependabot[bot]
c30cddfa06
chore: bump github.com/traefik/traefik/v2 from 2.6.0 to 2.6.1 (#175) 2022-02-17 14:26:36 +00:00
Fernandez Ludovic
3b7bdfc16e chore: fix workflow 2022-02-13 16:13:58 +01:00
Fernandez Ludovic
cc9161d223 chore: change CI process 2022-01-26 17:34:02 +01:00
Fernandez Ludovic
1de505f615 chore: update linter 2022-01-26 14:32:08 +01:00
Fernandez Ludovic
32293934dd chore: update dependencies 2022-01-26 14:31:40 +01:00
Fernandez Ludovic
740891ff39 doc: update version 2021-12-12 01:46:53 +01:00
Fernandez Ludovic
1b91299899 chore: increase goreleaser timeout 2021-12-12 01:45:55 +01:00
Fernandez Ludovic
372fb3c434 doc: update version 2021-12-12 00:40:17 +01:00
Fernandez Ludovic
d8a1a2644f chore: update seihon 2021-12-12 00:22:52 +01:00
Fernandez Ludovic
dd3c206955 doc: replace microbadger 2021-12-12 00:22:06 +01:00
Fernandez Ludovic
4db6310bf8 chore: upgrade to go1.17 and linter 2021-12-12 00:18:07 +01:00
Fernandez Ludovic
91e301ab39 chore: update dependencies 2021-12-12 00:07:41 +01:00
Ludovic Fernandez
8a775d55bb
chore: remove dependabot 2021-12-11 15:23:24 +01:00
Matthieu Talbot
e7fcbd70ad
Add a docker-compose hook example (#170)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2021-11-22 23:06:55 +01:00
dependabot[bot]
a8beef4f92
chore: bump github.com/go-acme/lego/v4 from 4.5.2 to 4.5.3 (#169) 2021-10-07 12:33:02 +02:00
dependabot[bot]
31028809c4
chore: bump github.com/go-acme/lego/v4 from 4.4.0 to 4.5.2 (#168) 2021-10-06 09:37:33 +02:00
dependabot[bot]
018323851f
chore: bump github.com/spf13/viper from 1.8.1 to 1.9.0 (#167) 2021-09-20 09:33:20 +02:00
dependabot[bot]
2d83180d3e
chore: bump github.com/fsnotify/fsnotify from 1.5.0 to 1.5.1 (#166) 2021-08-25 11:44:38 +02:00
dependabot[bot]
8209dc2b9d
chore: bump github.com/fsnotify/fsnotify from 1.4.9 to 1.5.0 (#165) 2021-08-19 11:30:49 +02:00
dependabot[bot]
25b4a44d79
chore: bump github.com/traefik/traefik/v2 from 2.4.13 to 2.4.14 (#164) 2021-08-17 10:26:21 +02:00
dependabot[bot]
de2ae2dd93
chore: bump github.com/traefik/traefik/v2 from 2.4.12 to 2.4.13 (#163) 2021-08-02 07:36:26 +02:00
dependabot[bot]
05deaa3d15
chore: bump github.com/traefik/traefik/v2 from 2.4.11 to 2.4.12 (#162) 2021-07-27 11:16:44 +02:00
Fernandez Ludovic
29d75cac57 chore: fix github action cache. 2021-07-16 20:18:17 +02:00
dependabot[bot]
526a001ee8
chore: bump github.com/traefik/traefik/v2 from 2.4.10 to 2.4.11 (#161) 2021-07-16 04:37:43 +00:00
dependabot[bot]
b1a0560f70
chore: bump github.com/traefik/traefik/v2 from 2.4.9 to 2.4.10 (#160) 2021-07-14 04:45:40 +00:00
Fernandez Ludovic
3c7141923c chore: update linter 2021-07-09 10:46:58 +02:00
Fernandez Ludovic
ca6eb2f04a chore: update dependencies 2021-07-09 09:07:45 +02:00
dependabot[bot]
fae0bc765c
chore: bump github.com/spf13/viper from 1.7.1 to 1.8.1 (#156) 2021-07-09 08:57:54 +02:00
dependabot[bot]
1331346177
chore: bump github.com/abronan/valkeyrie from 0.1.0 to 0.2.0 (#159) 2021-07-09 08:36:51 +02:00
dependabot[bot]
c459caf95b
chore: bump github.com/traefik/traefik/v2 from 2.4.8 to 2.4.9 (#155) 2021-06-22 08:07:05 +00:00
dependabot[bot]
83c551d915
chore: bump github.com/go-acme/lego/v4 from 4.3.1 to 4.4.0 (#153) 2021-06-09 10:02:07 +02:00
dependabot[bot]
a909fe7184
chore: bump github.com/traefik/traefik/v2 from 2.4.7 to 2.4.8 (#150) 2021-03-24 10:01:17 +01:00
dependabot[bot]
94652cf772
chore: bump github.com/go-acme/lego/v4 from 4.2.0 to 4.3.1 (#149) 2021-03-12 08:16:23 +00:00
dependabot[bot]
125bf4dcc2
chore: bump github.com/traefik/traefik/v2 from 2.4.6 to 2.4.7 (#148) 2021-03-09 10:13:44 +00:00
Fernandez Ludovic
0c0662615b chore: go1.16 and linter update 2021-03-09 10:54:00 +01:00
dependabot[bot]
c1eebcb16e
chore: bump github.com/traefik/traefik/v2 from 2.4.5 to 2.4.6 (#147) 2021-03-02 09:16:00 +01:00
dependabot-preview[bot]
082667a542
chore: create Dependabot config file (#146) 2021-02-26 00:59:07 +00:00
dependabot-preview[bot]
6b4855d571
chore(deps): bump github.com/traefik/traefik/v2 from 2.4.3 to 2.4.5 (#145) 2021-02-19 11:07:17 +00:00
dependabot-preview[bot]
3c9a239f96
chore(deps): bump github.com/traefik/traefik/v2 from 2.4.2 to 2.4.3 (#144) 2021-02-16 08:20:01 +00:00
dependabot-preview[bot]
14600988d8
chore(deps): bump github.com/spf13/cobra from 1.1.2 to 1.1.3 (#143) 2021-02-11 04:42:30 +00:00
dependabot-preview[bot]
ec35c93bb7
chore(deps): bump github.com/spf13/cobra from 1.1.1 to 1.1.2 (#142) 2021-02-10 08:15:40 +00:00
dependabot-preview[bot]
352457da76
chore(deps): bump github.com/traefik/traefik/v2 from 2.4.1 to 2.4.2 (#141) 2021-02-03 08:18:55 +00:00
dependabot-preview[bot]
1e5c66ddd6
chore(deps): bump github.com/traefik/traefik/v2 from 2.4.0 to 2.4.1 (#140) 2021-02-02 08:18:05 +00:00
dependabot-preview[bot]
a656e813f8
chore(deps): bump github.com/go-acme/lego/v4 from 4.1.3 to 4.2.0 (#139) 2021-01-25 09:21:51 +00:00
dependabot-preview[bot]
7905b608b9
chore(deps): bump github.com/traefik/traefik/v2 from 2.3.7 to 2.4.0 (#138) 2021-01-20 11:44:52 +00:00
dependabot-preview[bot]
6e422fe9c2
chore(deps): bump github.com/stretchr/testify from 1.6.1 to 1.7.0 (#137)
* chore(deps): bump github.com/stretchr/testify from 1.6.1 to 1.7.0

Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.6.1...v1.7.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

* go mod tidy

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2021-01-14 05:36:29 +00:00
dependabot-preview[bot]
36df44fdbf
chore(deps): bump github.com/traefik/traefik/v2 from 2.3.6 to 2.3.7 (#136)
* chore(deps): bump github.com/traefik/traefik/v2 from 2.3.6 to 2.3.7

Bumps [github.com/traefik/traefik/v2](https://github.com/traefik/traefik) from 2.3.6 to 2.3.7.
- [Release notes](https://github.com/traefik/traefik/releases)
- [Changelog](https://github.com/traefik/traefik/blob/v2.3.7/CHANGELOG.md)
- [Commits](https://github.com/traefik/traefik/compare/v2.3.6...v2.3.7)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

* go mod tidy

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2021-01-12 08:46:52 +00:00
dependabot-preview[bot]
26e5525cef
chore(deps): bump github.com/traefik/traefik/v2 from 2.3.5 to 2.3.6 (#133)
* chore(deps): bump github.com/traefik/traefik/v2 from 2.3.5 to 2.3.6

Bumps [github.com/traefik/traefik/v2](https://github.com/traefik/traefik) from 2.3.5 to 2.3.6.
- [Release notes](https://github.com/traefik/traefik/releases)
- [Changelog](https://github.com/traefik/traefik/blob/v2.3.6/CHANGELOG.md)
- [Commits](https://github.com/traefik/traefik/compare/v2.3.5...v2.3.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

* go mod tidy

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
2020-12-18 05:47:00 +00:00
Ludovic Fernandez
e90655c23b
chore: clean go module replace directives. 2020-12-16 22:46:57 +00:00
dependabot-preview[bot]
f82d5fff7a
chore(deps): bump github.com/traefik/traefik/v2 from 2.3.4 to 2.3.5 (#131) 2020-12-11 09:22:44 +01:00
Ludovic Fernandez
cb06f5f58d
doc: add Traefik v2 example. (#129) 2020-12-05 21:23:25 +01:00
Fernandez Ludovic
4c1a7ad08d chore: fix GH_TOKEN 2020-12-04 00:12:32 +01:00
Fernandez Ludovic
334dbc04a6 chore: fix GH_TOKEN 2020-12-04 00:00:01 +01:00
Fernandez Ludovic
830d23fae7 chore: fix seihon installation. 2020-12-03 23:44:34 +01:00
Fernandez Ludovic
aaebcdaa19 chore: goodbye TravisCI. 2020-12-03 23:29:08 +01:00
dependabot-preview[bot]
edcd0c2f87
chore(deps): bump github.com/go-acme/lego/v4 from 4.1.2 to 4.1.3 (#127) 2020-11-26 09:07:47 +01:00
dependabot-preview[bot]
4e4ffe8086
chore(deps): bump github.com/traefik/traefik/v2 from 2.3.3 to 2.3.4 (#126) 2020-11-25 09:47:13 +01:00
dependabot-preview[bot]
e66158aba7
chore(deps): bump github.com/go-acme/lego/v4 from 4.1.1 to 4.1.2 (#125) 2020-11-23 09:26:59 +01:00
Fernandez Ludovic
26fad3526f chore: update travic configuration. 2020-11-20 09:33:53 +01:00
dependabot-preview[bot]
de8498e5c3
chore(deps): bump github.com/traefik/traefik/v2 from 2.3.2 to 2.3.3 (#122) 2020-11-20 09:19:55 +01:00
dependabot-preview[bot]
89b2109e94
chore(deps): bump github.com/go-acme/lego/v4 from 4.1.0 to 4.1.1 (#123) 2020-11-20 09:12:35 +01:00
dependabot-preview[bot]
16f73ee009
chore(deps): bump github.com/go-acme/lego/v4 from 4.0.1 to 4.1.0 (#121) 2020-11-09 09:37:12 +01:00
dependabot-preview[bot]
b36ed5879c
chore(deps): bump github.com/traefik/traefik/v2 from 2.3.1 to 2.3.2 (#118) 2020-10-20 09:10:14 +02:00
dependabot-preview[bot]
693f8b1afa
chore(deps): bump github.com/spf13/cobra from 1.1.0 to 1.1.1 (#117) 2020-10-19 09:43:20 +02:00
dependabot-preview[bot]
ef7b6cc5eb
chore(deps): bump github.com/spf13/cobra from 1.0.0 to 1.1.0 (#116) 2020-10-15 09:22:13 +02:00
Fernandez Ludovic
85296e817c chore: update dependencies. 2020-10-03 15:12:09 +02:00
dependabot-preview[bot]
7c5a2b3e1b
chore(deps): bump github.com/traefik/traefik/v2 from 2.3.0 to 2.3.1 (#115) 2020-09-30 09:42:50 +02:00
Fernandez Ludovic
a340341bbd chore: update traefik. 2020-09-24 11:28:13 +02:00
Fernandez Ludovic
5f1c30bb66 feat: update lego. 2020-09-11 18:55:23 +02:00
dependabot-preview[bot]
4aa1883478
chore(deps): bump github.com/containous/traefik/v2 from 2.2.8 to 2.2.11 (#112) 2020-09-08 09:12:02 +02:00
dependabot-preview[bot]
4b7d19cc7b
chore(deps): bump github.com/go-acme/lego/v3 from 3.8.0 to 3.9.0 (#110) 2020-09-02 16:35:25 +02:00
dependabot-preview[bot]
9998faaae2
chore(deps): bump github.com/spf13/viper from 1.7.0 to 1.7.1 (#109) 2020-08-03 09:39:44 +02:00
dependabot-preview[bot]
eb1d63d3de
chore(deps): bump github.com/containous/traefik/v2 from 2.2.7 to 2.2.8 (#108) 2020-07-29 09:19:41 +02:00
dependabot-preview[bot]
a0a06b9bb6
chore(deps): bump github.com/containous/traefik/v2 from 2.2.6 to 2.2.7 (#106) 2020-07-21 08:51:08 +02:00
dependabot-preview[bot]
83ea2bcee4
chore(deps): bump github.com/containous/traefik/v2 from 2.2.5 to 2.2.6 (#105) 2020-07-20 10:23:51 +02:00
dependabot-preview[bot]
922ad393aa
chore(deps): bump github.com/containous/traefik/v2 from 2.2.3 to 2.2.5 (#104) 2020-07-14 10:59:13 +02:00
dependabot-preview[bot]
88366d31ce
chore(deps): bump github.com/containous/traefik/v2 from 2.2.2 to 2.2.3 (#103) 2020-07-10 09:57:07 +02:00
dependabot-preview[bot]
024b4cf6e7
chore(deps): bump github.com/containous/traefik/v2 from 2.2.1 to 2.2.2 (#102) 2020-07-09 22:19:14 +02:00
dependabot-preview[bot]
728f010c0c
chore(deps): bump github.com/go-acme/lego/v3 from 3.7.0 to 3.8.0 (#101) 2020-07-03 09:34:15 +02:00
Elie Alawoe
09d444c6e4
Fix docker-compose.yml volume error (#99) 2020-06-09 16:58:31 +02:00
dependabot-preview[bot]
e63e110862
chore(deps): bump github.com/stretchr/testify from 1.6.0 to 1.6.1 (#97) 2020-06-08 13:56:01 +02:00
dependabot-preview[bot]
b38938a417
chore(deps): bump github.com/stretchr/testify from 1.5.1 to 1.6.0 (#96) 2020-06-01 15:22:17 +02:00
x-yuri
58fda4c048
doc: fix indents (#94) 2020-05-18 15:47:00 +02:00
x-yuri
040b0b9d24
doc: Add docker-compose example (#93) 2020-05-18 15:30:18 +02:00
dependabot-preview[bot]
ceee81afb5
chore(deps): bump github.com/go-acme/lego/v3 from 3.6.0 to 3.7.0 (#91) 2020-05-12 11:03:40 +02:00
dependabot-preview[bot]
cca5aae4bd
chore(deps): bump github.com/spf13/viper from 1.6.3 to 1.7.0 (#90) 2020-05-11 11:10:00 +02:00
Fernandez Ludovic
7414ec328c chore: update linter. 2020-05-09 15:50:40 +02:00
Fernandez Ludovic
9023b046d8 chore: add funding ♥ 2020-05-09 15:47:17 +02:00
dependabot-preview[bot]
2e0122c7c3
chore(deps): bump github.com/containous/traefik/v2 from 2.2.0 to 2.2.1 (#88) 2020-04-30 09:27:18 +02:00
dependabot-preview[bot]
6d62773ba0
chore(deps): bump github.com/go-acme/lego/v3 from 3.5.0 to 3.6.0 (#87) 2020-04-27 15:40:24 +02:00
dependabot-preview[bot]
75dac5305f
chore(deps): bump github.com/spf13/viper from 1.6.2 to 1.6.3 (#85) 2020-04-10 14:11:44 +02:00
dependabot-preview[bot]
8c86d15e0c
chore(deps): bump github.com/spf13/cobra from 0.0.6 to 0.0.7 (#83) 2020-03-30 09:56:15 +02:00
dependabot-preview[bot]
230ecb285a
chore(deps): bump github.com/containous/traefik/v2 from 2.1.6 to 2.2.0 (#82) 2020-03-26 13:33:35 +01:00
dependabot-preview[bot]
0ad349de7a
chore(deps): bump github.com/go-acme/lego/v3 from 3.4.0 to 3.5.0 (#79) 2020-03-16 09:22:44 +01:00
dependabot-preview[bot]
ea4b91c8cb
chore(deps): bump github.com/containous/traefik/v2 from 2.1.4 to 2.1.6 (#78) 2020-03-02 09:20:12 +01:00
joshuaclausen
cd9f88d981
doc: Traefik v2 acme.json files require "--version v2" on command line (#77)
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
2020-02-27 01:37:39 +01:00
dependabot-preview[bot]
624da6cad1
chore(deps): bump github.com/go-acme/lego/v3 from 3.3.0 to 3.4.0 (#76) 2020-02-26 09:46:48 +01:00
dependabot-preview[bot]
19faca21db
chore(deps): bump github.com/spf13/cobra from 0.0.5 to 0.0.6 (#75) 2020-02-21 15:16:45 +01:00
dependabot-preview[bot]
b7a133a50a
chore(deps): bump github.com/stretchr/testify from 1.5.0 to 1.5.1 (#74) 2020-02-20 07:01:57 +01:00
dependabot-preview[bot]
fbf342853a
chore(deps): bump github.com/stretchr/testify from 1.4.0 to 1.5.0 (#73) 2020-02-19 10:08:01 +01:00
Fernandez Ludovic
e1275d1d09 chore: migrate travis. 2020-02-16 20:27:08 +01:00
Fernandez Ludovic
5005a38227 fix: clean file path. 2020-02-16 20:26:48 +01:00
Fernandez Ludovic
6a71a1655e chore: update golangci-lint. 2020-02-16 20:26:13 +01:00
dependabot-preview[bot]
aff9dad1f4
chore(deps): bump github.com/containous/traefik/v2 from 2.1.3 to 2.1.4 (#72) 2020-02-07 10:03:50 +01:00
dependabot-preview[bot]
edd06d8d82 chore(deps): bump github.com/containous/traefik/v2 from 2.1.2 to 2.1.3 (#69) 2020-01-22 10:28:41 +01:00
dependabot-preview[bot]
6458f3bebe chore(deps): bump github.com/spf13/viper from 1.6.1 to 1.6.2 (#68) 2020-01-17 07:36:53 +01:00
dependabot-preview[bot]
e756a3c6c6 chore(deps): bump github.com/go-acme/lego/v3 from 3.2.0 to 3.3.0 (#66) 2020-01-09 09:27:49 +01:00
dependabot-preview[bot]
65fe3c3228 chore(deps): bump github.com/containous/traefik/v2 from 2.1.1 to 2.1.2 (#65) 2020-01-08 09:29:21 +01:00
Fernandez Ludovic
520d1ba91e doc: update version used in example. 2019-12-26 12:54:30 +01:00
Fernandez Ludovic
7c381b582e feat: add more unit tests. 2019-12-26 12:49:50 +01:00
Fernandez Ludovic
e37c8ee42b chore: update dependencies. 2019-12-26 12:49:50 +01:00
Fernandez Ludovic
1ef3d37498 feat: improve error handling.
- skip EOF error
2019-12-26 12:49:50 +01:00
dependabot-preview[bot]
8c8f1ea37f chore(deps): bump github.com/containous/traefik/v2 from 2.0.7 to 2.1.0 (#59) 2019-12-12 09:41:47 +01:00
dependabot-preview[bot]
4575e9dcff chore(deps): bump github.com/containous/traefik/v2 from 2.0.6 to 2.0.7 (#58) 2019-12-10 09:42:06 +01:00
dependabot-preview[bot]
f753d82829 chore(deps): bump github.com/spf13/viper from 1.5.0 to 1.6.1 (#57) 2019-12-09 09:46:32 +01:00
dependabot-preview[bot]
4e777b10db chore(deps): bump github.com/containous/traefik/v2 from 2.0.5 to 2.0.6 (#56) 2019-12-03 09:55:28 +01:00
dependabot-preview[bot]
b9b8e0249b chore(deps): bump github.com/containous/traefik/v2 from 2.0.4 to 2.0.5 (#55) 2019-11-15 06:03:25 +01:00
dependabot-preview[bot]
7e65b701c2 chore(deps): bump github.com/go-acme/lego/v3 from 3.1.0 to 3.2.0 (#54) 2019-11-11 13:13:32 +01:00
dependabot-preview[bot]
e40252ef2d chore(deps): bump github.com/spf13/viper from 1.4.0 to 1.5.0 (#52) 2019-11-05 11:15:06 +01:00
dependabot-preview[bot]
09bc3cd31d chore(deps): bump github.com/containous/traefik/v2 from 2.0.2 to 2.0.4 (#51) 2019-10-29 10:03:54 +01:00
dependabot-preview[bot]
a3970b2624 chore(deps): bump github.com/containous/traefik/v2 from 2.0.1 to 2.0.2 (#49) 2019-10-10 10:03:59 +02:00
Fernandez Ludovic
7560062fbb chore: simplify. 2019-10-09 03:29:24 +02:00
Fernandez Ludovic
e07b613ac4 fix(kv): etcd v3 support. 2019-10-09 03:27:38 +02:00
Fernandez Ludovic
e338373231 chore: increase linter timeout. 2019-10-09 03:17:25 +02:00
Fernandez Ludovic
e309f6b7a8 feat: support etcd v3. 2019-10-09 03:08:33 +02:00
Fernandez Ludovic
0ce281d820 chore: update linter configuration. 2019-10-09 02:54:51 +02:00
Fernandez Ludovic
70543d3449 fix: lint. 2019-10-09 02:51:34 +02:00
Fernandez Ludovic
072d407807 feat(kv): support suffix/storage 2019-10-09 02:43:07 +02:00
Fernandez Ludovic
867c59b8fb chore: fix labbsr0x dependencies. 2019-09-30 22:11:40 +02:00
Fernandez Ludovic
5384d88e8c fix: version flag. 2019-09-30 20:43:33 +02:00
Fernandez Ludovic
574db1a1e0 fix: multiple resolvers. 2019-09-30 14:51:30 +02:00
Fernandez Ludovic
f8e2d89d6c fix(v2): field cases. 2019-09-30 12:53:37 +02:00
Fernandez Ludovic
e6fb708c0f chore: update to Traefik v2.0.0 2019-09-17 10:47:28 +02:00
Fernandez Ludovic
2d72b0d57c chore: fix invalid hash. 2019-09-16 10:05:26 +02:00
dependabot-preview[bot]
6e4f400b54 chore(deps): bump github.com/containous/traefik/v2 from 2.0.0-rc2 to 2.0.0-rc3 (#42) 2019-09-12 15:14:30 +02:00
Fernandez Ludovic
10a2b0521a chore: update to alpine 3.10 2019-09-06 02:48:51 +02:00
Fernandez Ludovic
d79fa809fb fix: don't panic when no account. 2019-09-05 22:02:06 +02:00
dependabot-preview[bot]
dd144f140d chore(deps): bump github.com/containous/traefik/v2 from 2.0.0-rc1 to 2.0.0-rc2 (#41) 2019-09-05 00:11:51 +02:00
Fernandez Ludovic
3bc4dc15a7 chore: update to go1.13 2019-09-04 11:47:42 +02:00
Fernandez Ludovic
1d07a29bcd chore: regenerate with go1.12 2019-09-01 21:32:32 +02:00
Fernandez Ludovic
bbb45a38ae chore: remove temporary go modules validation. 2019-09-01 20:14:23 +02:00
Fernandez Ludovic
82e309fc0d feat: support Traefik v2. 2019-09-01 19:20:29 +02:00
Fernandez Ludovic
701a8bd4f6 refactor: storeddata. 2019-09-01 18:28:46 +02:00
Fernandez Ludovic
ffad4229fd chore: update dependencies. 2019-09-01 18:18:17 +02:00
Fernandez Ludovic
6c974f7903 refactor: remane old KV account format. 2019-09-01 18:15:22 +02:00
Fernandez Ludovic
07c396dea5 chore: fix lego version. 2019-08-16 12:15:27 +02:00
Fernandez Ludovic
4fd46f8bbd chore: update dependencies. 2019-08-15 05:39:13 +02:00
Fernandez Ludovic
76880d271d chore: update goreleaser config. 2019-08-01 08:11:06 +02:00
dependabot-preview[bot]
fbb5e09ed9 chore(deps): bump github.com/go-acme/lego from 2.6.0+incompatible to 2.7.2+incompatible (#36) 2019-08-01 07:40:35 +02:00
dependabot-preview[bot]
33c3553346 chore(deps): bump github.com/spf13/cobra from 0.0.3 to 0.0.5 (#32) 2019-07-03 14:47:51 +02:00
dependabot-preview[bot]
12b333de4c chore(deps): bump github.com/go-acme/lego (#31) 2019-07-03 14:31:37 +02:00
dependabot-preview[bot]
ea15fd6ec2 chore(deps): bump github.com/spf13/viper from 1.3.2 to 1.4.0 (#30) 2019-07-03 14:03:53 +02:00
Ludovic Fernandez
05afa6f5a0
fix: call the hook at the first call. (#26) 2019-06-18 11:56:49 +02:00
Fernandez Ludovic
28e4c55785 fix: restrict private file permissions. 2019-05-14 11:10:14 +02:00
Fernandez Ludovic
34c8608c9f feat(hook): expand environment variables. 2019-05-09 09:55:44 +02:00
Fernandez Ludovic
a4b4be1fd4 chore: update seihon version. 2019-05-08 17:55:06 +02:00
Fernandez Ludovic
efaca797e4 chore: migrate to seihon. 2019-04-30 23:05:08 +02:00
Fernandez Ludovic
e5c0081c44 chore: review publisher and ci config. 2019-04-30 01:06:33 +02:00
Fernandez Ludovic
21826f6b12 fix: publish images commands. 2019-04-29 02:43:19 +02:00
Fernandez Ludovic
ba33c84540 chore: add Travis configuration. 2019-04-29 02:27:08 +02:00
Fernandez Ludovic
91075600ed New multi-arch Docker images publisher. 2019-04-29 01:20:05 +02:00
Fernandez Ludovic
1d6b5b8bec Revert "Add multi-arch build script (#21)"
This reverts commit cb9c54f996.
This reverts commit 36835c87f8.
2019-04-28 16:53:30 +02:00
71 changed files with 2442 additions and 947 deletions

View File

@ -6,5 +6,5 @@ dumpcerts.sh
acme.json
acme-backup.json
traefik-certs-dumper
build-docker.sh
manifest.json
*.Dockerfile

4
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,4 @@
github: ldez
ko_fi: ldez_oss
liberapay: ldez
thanks_dev: u/gh/ldez

40
.github/workflows/go-cross.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: Go Matrix
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
cross:
name: Go
runs-on: ${{ matrix.os }}
env:
CGO_ENABLED: 0
strategy:
matrix:
go-version: [ stable ]
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
# https://github.com/marketplace/actions/checkout
- name: Checkout code
uses: actions/checkout@v4
# https://github.com/marketplace/actions/setup-go-environment
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
- name: Test
run: go test -v -cover ./...
- name: Build
run: go build -v -ldflags "-s -w" -trimpath

41
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: Main
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
main:
name: Main Process
runs-on: ubuntu-latest
env:
GO_VERSION: stable
GOLANGCI_LINT_VERSION: v2.0.1
CGO_ENABLED: 0
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: Check and get dependencies
run: |
go mod download
go mod tidy
git diff --exit-code go.mod
git diff --exit-code go.sum
# https://golangci-lint.run/usage/install#other-ci
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
golangci-lint --version
- name: Make
run: make

70
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,70 @@
name: "Release a tag"
on:
push:
tags:
- v*
jobs:
release:
name: Release Process
runs-on: ubuntu-latest
env:
GO_VERSION: stable
CGO_ENABLED: 0
steps:
# temporary workaround for an error in free disk space action
# https://github.com/jlumbroso/free-disk-space/issues/14
- name: Update Package List and Remove Dotnet
run: |
sudo apt-get update
sudo apt-get remove -y '^dotnet-.*'
# https://github.com/marketplace/actions/free-disk-space-ubuntu
- name: Free Disk Space
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed
tool-cache: false
# all of these default to true
android: true
dotnet: true
haskell: true
large-packages: true
docker-images: true
swap-storage: false
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
- name: dockerhub-login
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: ghcr-login
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
version: latest
args: release -p 1 --clean --timeout=90m
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View File

@ -7,4 +7,4 @@ acme.json
acme-backup.json
traefik-certs-dumper
manifest.json
/*.Dockerfile
/linux-*.Dockerfile

View File

@ -1,44 +0,0 @@
[run]
deadline = "2m"
skip-files = []
[linters-settings]
[linters-settings.govet]
check-shadowing = true
[linters-settings.gocyclo]
min-complexity = 12.0
[linters-settings.maligned]
suggest-new = true
[linters-settings.goconst]
min-len = 3.0
min-occurrences = 3.0
[linters-settings.misspell]
locale = "US"
[linters]
enable-all = true
disable = [
"maligned",
"lll",
"gas",
"dupl",
"prealloc",
"scopelint",
]
[issues]
exclude-use-default = false
max-per-linter = 0
max-same-issues = 0
exclude = []
[[issues.exclude-rules]]
path = "version.go"
text = "`(version|commit|date)` is a global variable"
[[issues.exclude-rules]]
path = "cmd/"
linters = ["gochecknoglobals", "gochecknoinits"]

108
.golangci.yml Normal file
View File

@ -0,0 +1,108 @@
version: "2"
formatters:
enable:
- gci
- gofumpt
settings:
gofumpt:
extra-rules: true
linters:
default: all
disable:
- cyclop # duplicate of gocyclo
- dupl
- err113
- exhaustive
- exhaustruct
- lll
- mnd
- nilnil
- nlreturn
- paralleltest
- prealloc
- rowserrcheck # not relevant (SQL)
- sqlclosecheck # not relevant (SQL)
- testpackage
- tparallel
- varnamelen
- wrapcheck
- wsl
settings:
depguard:
rules:
main:
deny:
- pkg: github.com/instana/testify
desc: not allowed
- pkg: github.com/pkg/errors
desc: Should be replaced by standard lib errors package
forbidigo:
forbid:
- pattern: ^print(ln)?$
- pattern: ^spew\.Print(f|ln)?$
- pattern: ^spew\.Dump$
funlen:
lines: -1
statements: 40
goconst:
min-len: 3
min-occurrences: 3
gocritic:
disabled-checks:
- sloppyReassign
- rangeValCopy
- octalLiteral
- paramTypeCombine # already handle by gofumpt.extra-rules
enabled-tags:
- diagnostic
- style
- performance
settings:
hugeParam:
sizeThreshold: 100
gocyclo:
min-complexity: 12
godox:
keywords:
- FIXME
gomoddirectives:
replace-allow-list:
- github.com/abbot/go-http-auth
- github.com/go-check/check
- github.com/gorilla/mux
- github.com/mailgun/minheap
- github.com/mailgun/multibuf
- github.com/jaguilar/vt100
gosec:
excludes:
- G204 # Subprocess launched with a potential tainted input or cmd arguments
- G301 # Expect directory permissions to be 0750 or less
- G306 # Expect WriteFile permissions to be 0600 or less
govet:
disable:
- fieldalignment
enable-all: true
misspell:
locale: US
exclusions:
presets:
- comments
rules:
- linters:
- gochecknoglobals
- gochecknoinits
path: cmd/
- linters:
- tagalign
path: internal/traefikv[1-3]/
- path: (.+)\.go$
text: 'ST1000: at least one file in a package should have a package comment'
- path: (.+)\.go$
text: 'package-comments: should have a package comment'
issues:
max-issues-per-linter: 0
max-same-issues: 0

View File

@ -1,3 +1,4 @@
version: 2
project_name: traefik-certs-dumper
builds:
@ -5,7 +6,7 @@ builds:
ldflags:
- -s -w -X github.com/ldez/traefik-certs-dumper/cmd.version={{.Version}} -X github.com/ldez/traefik-certs-dumper/cmd.commit={{.ShortCommit}} -X github.com/ldez/traefik-certs-dumper/cmd.date={{.Date}}
env:
- GO111MODULE=on
- CGO_ENABLED=0
goos:
- linux
- darwin
@ -33,17 +34,189 @@ changelog:
- '^docs:'
- '^doc:'
- '^chore:'
- '^chore\(deps\):'
- '^test:'
- '^tests:'
archive:
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
format: tar.gz
format_overrides:
- goos: windows
format: zip
files:
- LICENSE
archives:
- id: tcd
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
formats: [ 'tar.gz' ]
format_overrides:
- goos: windows
formats: [ 'zip' ]
files:
- LICENSE
#release:
# disable: true
docker_manifests:
- name_template: 'ldez/traefik-certs-dumper:{{ .Tag }}'
image_templates:
- 'ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-386'
- name_template: 'ldez/traefik-certs-dumper:latest'
image_templates:
- 'ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-386'
- name_template: 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}'
image_templates:
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-amd64'
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-arm64'
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv7'
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv6'
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-386'
- name_template: 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}'
image_templates:
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-386'
- name_template: 'ghcr.io/ldez/traefik-certs-dumper:latest'
image_templates:
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-386'
- name_template: 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}'
image_templates:
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-amd64'
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-arm64'
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv7'
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv6'
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-386'
dockers:
- use: buildx
goos: linux
goarch: amd64
dockerfile: buildx.Dockerfile
image_templates:
- 'ldez/traefik-certs-dumper:latest-amd64'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-amd64'
- 'ghcr.io/ldez/traefik-certs-dumper:latest-amd64'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-amd64'
build_flag_templates:
- '--pull'
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
- '--label=org.opencontainers.image.title={{.ProjectName}}'
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
- '--label=org.opencontainers.image.source={{.GitURL}}'
- '--label=org.opencontainers.image.url={{.GitURL}}'
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
- '--label=org.opencontainers.image.created={{.Date}}'
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
- '--label=org.opencontainers.image.version={{.Version}}'
- '--platform=linux/amd64'
- use: buildx
goos: linux
goarch: arm64
dockerfile: buildx.Dockerfile
image_templates:
- 'ldez/traefik-certs-dumper:latest-arm64'
- 'ldez/traefik-certs-dumper:latest-arm.v8' # only for compatibility with Seihon
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm.v8' # only for compatibility with Seihon
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-arm64'
- 'ghcr.io/ldez/traefik-certs-dumper:latest-arm64'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-arm64'
build_flag_templates:
- '--pull'
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
- '--label=org.opencontainers.image.title={{.ProjectName}}'
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
- '--label=org.opencontainers.image.source={{.GitURL}}'
- '--label=org.opencontainers.image.url={{.GitURL}}'
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
- '--label=org.opencontainers.image.created={{.Date}}'
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
- '--label=org.opencontainers.image.version={{.Version}}'
- '--platform=linux/arm64'
- use: buildx
goos: linux
goarch: arm
goarm: '7'
dockerfile: buildx.Dockerfile
image_templates:
- 'ldez/traefik-certs-dumper:latest-armv7'
- 'ldez/traefik-certs-dumper:latest-arm.v7' # only for compatibility with Seihon
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm.v7' # only for compatibility with Seihon
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv7'
- 'ghcr.io/ldez/traefik-certs-dumper:latest-armv7'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv7'
build_flag_templates:
- '--pull'
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
- '--label=org.opencontainers.image.title={{.ProjectName}}'
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
- '--label=org.opencontainers.image.source={{.GitURL}}'
- '--label=org.opencontainers.image.url={{.GitURL}}'
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
- '--label=org.opencontainers.image.created={{.Date}}'
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
- '--label=org.opencontainers.image.version={{.Version}}'
- '--platform=linux/arm/v7'
- use: buildx
goos: linux
goarch: arm
goarm: '6'
dockerfile: buildx.Dockerfile
image_templates:
- 'ldez/traefik-certs-dumper:latest-armv6'
- 'ldez/traefik-certs-dumper:latest-arm.v6' # only for compatibility with Seihon
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm.v6' # only for compatibility with Seihon
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv6'
- 'ghcr.io/ldez/traefik-certs-dumper:latest-armv6'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv6'
build_flag_templates:
- '--pull'
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
- '--label=org.opencontainers.image.title={{.ProjectName}}'
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
- '--label=org.opencontainers.image.source={{.GitURL}}'
- '--label=org.opencontainers.image.url={{.GitURL}}'
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
- '--label=org.opencontainers.image.created={{.Date}}'
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
- '--label=org.opencontainers.image.version={{.Version}}'
- '--platform=linux/arm/v6'
- use: buildx
goos: linux
goarch: '386'
dockerfile: buildx.Dockerfile
image_templates:
- 'ldez/traefik-certs-dumper:latest-386'
- 'ldez/traefik-certs-dumper:{{ .Tag }}-386'
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-386'
- 'ghcr.io/ldez/traefik-certs-dumper:latest-386'
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-386'
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-386'
build_flag_templates:
- '--pull'
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
- '--label=org.opencontainers.image.title={{.ProjectName}}'
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
- '--label=org.opencontainers.image.source={{.GitURL}}'
- '--label=org.opencontainers.image.url={{.GitURL}}'
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
- '--label=org.opencontainers.image.created={{.Date}}'
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
- '--label=org.opencontainers.image.version={{.Version}}'
- '--platform=linux/386'

View File

@ -1,48 +0,0 @@
dist: xenial
language: go
go:
- 1.11.x
- 1.x
services:
- docker
env:
- GO111MODULE=on
notifications:
email:
on_success: never
on_failure: change
before_install:
# Install linters and misspell
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
- golangci-lint --version
install:
- echo "TRAVIS_GO_VERSION=$TRAVIS_GO_VERSION"
- go mod download
before_deploy:
- >
if ! [ "$BEFORE_DEPLOY_RUN" ]; then
export BEFORE_DEPLOY_RUN=1;
echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
fi
deploy:
- provider: script
skip_cleanup: true
script: curl -sL https://git.io/goreleaser | bash
on:
tags: true
condition: $TRAVIS_GO_VERSION =~ ^1\.x$
- provider: script
skip_cleanup: true
script: make publish-images
on:
tags: true
condition: $TRAVIS_GO_VERSION =~ ^1\.x$

View File

@ -1,26 +0,0 @@
FROM golang:1-alpine as builder
ARG RUNTIME_HASH
ARG GOARCH
ARG GOARM
ARG GOOS
RUN apk --update upgrade \
&& apk --no-cache --no-progress add git make gcc musl-dev
WORKDIR /go/src/github.com/ldez/traefik-certs-dumper
ENV GO111MODULE on
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN GOARCH=${GOARCH} GOARM=${GOARM} GOOS=${GOOS} make build
FROM alpine:3.9${RUNTIME_HASH}
RUN apk --update upgrade \
&& apk --no-cache --no-progress add ca-certificates
COPY --from=builder /go/src/github.com/ldez/traefik-certs-dumper/traefik-certs-dumper /usr/bin/traefik-certs-dumper
ENTRYPOINT ["/usr/bin/traefik-certs-dumper"]

View File

@ -1,4 +1,4 @@
Copyright 2019 Fernandez Ludovic
Copyright 2019-2024 Fernandez Ludovic
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -1,5 +1,7 @@
.PHONY: default clean checks test build
export GO111MODULE=on
TAG_NAME := $(shell git tag -l --contains HEAD)
SHA := $(shell git rev-parse --short HEAD)
VERSION := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
@ -21,8 +23,5 @@ build: clean
checks:
golangci-lint run
publish-images:
go run ./internal/multiarch.go --version="$(TAG_NAME)"
go run ./internal/multiarch.go --version="latest"
# VERSION=$(TAG_NAME) ./build-docker.sh
# VERSION="latest" ./build-docker.sh
doc:
go run . doc

View File

@ -1,88 +0,0 @@
#!/usr/bin/env bash
set -o errexit
set -o pipefail
VERSION=v666
# safe guard
#if [ -n "$TRAVIS_TAG" ] && [ -n "$VERSION" ]; then
# echo "Deploying..."
#else
# echo "Skipping deploy"
# exit 0
#fi
# base docker image name
IMAGE_NAME="ldez/traefik-certs-dumper"
# only linux for now
OS=linux
# target platforms in docker manifest notation
declare -a PLATFORMS=( "amd64" "arm.v6" "arm.v7" "arm64")
# images from Dockerfile
FROM_IMAGE=$(grep "{RUNTIME_HASH}" < Dockerfile | sed "s/FROM //" | sed 's/\$.*//')
# manifest cache file
MANIFEST_FILE=/tmp/tcd-manifest.${FROM_IMAGE}.json
# get platform image hash from docker manifest
function platformHash () {
local ARCHITECTURE VARIANT HASH
read -r ARCHITECTURE VARIANT <<< "$@"
if [ -z "$VARIANT" ]; then
HASH=$(jq -r ".manifests[] | select(.platform.architecture == \"$ARCHITECTURE\") | .digest" < "$MANIFEST_FILE")
else
HASH=$(jq -r ".manifests[] | select(.platform.architecture == \"$ARCHITECTURE\" and .platform.variant == \"$VARIANT\") | .digest" < "$MANIFEST_FILE")
fi
echo "$HASH"
}
# get manifest
if [ ! -f "$MANIFEST_FILE" ]; then
docker pull "$FROM_IMAGE"
echo "docker manifest inspect $FROM_IMAGE"
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$FROM_IMAGE" > "$MANIFEST_FILE"
fi
# create and push images
for platform in "${PLATFORMS[@]}"; do
# split architecture.version
IFS='.' read -r ARCHITECTURE VARIANT <<< "$platform"
# add xargs to trim whitespace
RUNTIME_HASH=$(platformHash "$ARCHITECTURE" "$VARIANT")
# arm architectures flavors, strip "v" prefix
GOARM=${VARIANT:1}
# build for target runtime image and architecture
# echo "docker build --build-arg=RUNTIME_HASH=@${RUNTIME_HASH} --build-arg=GOARCH=${ARCHITECTURE} --build-arg=GOARM=${GOARM} -t $IMAGE_NAME:${VERSION}-$platform" .
docker build --build-arg="RUNTIME_HASH=@${RUNTIME_HASH}" --build-arg="GOARCH=${ARCHITECTURE}" --build-arg="GOARM=${GOARM}" -t "$IMAGE_NAME:${VERSION}-$platform" .
# push images
echo "docker push $IMAGE_NAME:${VERSION}-$platform"
# docker push "$IMAGE_NAME:${VERSION}-$platform"
done
# create manifest
TAG_LIST=$(printf "$IMAGE_NAME:${VERSION}-%s " "${PLATFORMS[@]}")
# shellcheck disable=SC2086
echo "docker manifest create --amend $IMAGE_NAME:${VERSION} $TAG_LIST"
#DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create --amend "$IMAGE_NAME:${VERSION}" $TAG_LIST
for platform in "${PLATFORMS[@]}"; do
# split architecture.version
IFS='.' read -r ARCHITECTURE VARIANT <<< "$platform"
echo "docker manifest annotate $IMAGE_NAME:${VERSION} $IMAGE_NAME:${VERSION}-$platform --os $OS --arch $ARCHITECTURE --variant $VARIANT"
# DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate "$IMAGE_NAME:${VERSION}" "$IMAGE_NAME:${VERSION}-$platform" --os "$OS" --arch "$ARCHITECTURE" --variant "$VARIANT"
done
# push manifest
echo "docker manifest push $IMAGE_NAME:${VERSION}"
#DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$IMAGE_NAME:${VERSION}"

9
buildx.Dockerfile Normal file
View File

@ -0,0 +1,9 @@
# syntax=docker/dockerfile:1.4
FROM alpine:3
RUN apk --no-cache --no-progress add git ca-certificates tzdata jq \
&& rm -rf /var/cache/apk/*
COPY traefik-certs-dumper /usr/bin/traefik-certs-dumper
ENTRYPOINT ["/usr/bin/traefik-certs-dumper"]

View File

@ -1,14 +1,16 @@
package cmd
import (
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/boltdb"
"context"
"time"
"github.com/kvtools/boltdb"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
"github.com/spf13/cobra"
)
// boltdbCmd represents the boltdb command
// boltdbCmd represents the boltdb command.
var boltdbCmd = &cobra.Command{
Use: "boltdb",
Short: "Dump the content of BoltDB.",
@ -29,11 +31,20 @@ func boltdbRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
return err
}
config.Options.Bucket = cmd.Flag("bucket").Value.String()
config.Options.PersistConnection, _ = cmd.Flags().GetBool("persist-connection")
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
if err != nil {
return err
}
config.Backend = store.BOLTDB
boltdb.Register()
persistConnection, _ := cmd.Flags().GetBool("persist-connection")
return kv.Dump(config, baseConfig)
config.Options = &boltdb.Config{
Bucket: cmd.Flag("bucket").Value.String(),
PersistConnection: persistConnection,
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
}
config.StoreName = boltdb.StoreName
return kv.Dump(context.Background(), config, baseConfig)
}

View File

@ -1,14 +1,16 @@
package cmd
import (
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/consul"
"context"
"time"
"github.com/kvtools/consul"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
"github.com/spf13/cobra"
)
// consulCmd represents the consul command
// consulCmd represents the consul command.
var consulCmd = &cobra.Command{
Use: "consul",
Short: "Dump the content of Consul.",
@ -28,10 +30,24 @@ func consulRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
return err
}
config.Options.Token = cmd.Flag("token").Value.String()
tlsConfig, err := createTLSConfig(cmd)
if err != nil {
return err
}
config.Backend = store.CONSUL
consul.Register()
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
if err != nil {
return err
}
return kv.Dump(config, baseConfig)
config.Options = &consul.Config{
TLS: tlsConfig,
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
Token: cmd.Flag("token").Value.String(),
Namespace: "",
}
config.StoreName = consul.StoreName
return kv.Dump(context.Background(), config, baseConfig)
}

View File

@ -5,12 +5,12 @@ import (
"github.com/spf13/cobra/doc"
)
// docCmd represents the doc command
// docCmd represents the doc command.
var docCmd = &cobra.Command{
Use: "doc",
Short: "Generate documentation",
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, _ []string) error {
return doc.GenMarkdownTree(rootCmd, "./docs")
},
}

View File

@ -1,16 +1,17 @@
package cmd
import (
"context"
"time"
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/etcd/v2"
"github.com/kvtools/etcdv2"
"github.com/kvtools/etcdv3"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
"github.com/spf13/cobra"
)
// etcdCmd represents the etcd command
// etcdCmd represents the etcd command.
var etcdCmd = &cobra.Command{
Use: "etcd",
Short: "Dump the content of etcd.",
@ -22,6 +23,7 @@ func init() {
kvCmd.AddCommand(etcdCmd)
etcdCmd.Flags().Int("sync-period", 0, "Sync period for etcd in seconds.")
etcdCmd.Flags().String("etcd-version", "etcd", "The etcd version can be: 'etcd' or 'etcdv3'.")
}
func etcdRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
@ -30,14 +32,47 @@ func etcdRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
return err
}
backend, err := cmd.Flags().GetString("etcd-version")
if err != nil {
return err
}
tlsConfig, err := createTLSConfig(cmd)
if err != nil {
return err
}
synPeriod, err := cmd.Flags().GetInt("sync-period")
if err != nil {
return err
}
config.Options.SyncPeriod = time.Duration(synPeriod) * time.Second
config.Backend = store.ETCD
etcd.Register()
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
if err != nil {
return err
}
return kv.Dump(config, baseConfig)
switch backend {
case "etcdv3":
config.Options = &etcdv3.Config{
TLS: tlsConfig,
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
SyncPeriod: time.Duration(synPeriod) * time.Second,
Username: cmd.Flag("password").Value.String(),
Password: cmd.Flag("username").Value.String(),
}
config.StoreName = etcdv3.StoreName
default:
config.Options = &etcdv2.Config{
TLS: tlsConfig,
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
SyncPeriod: time.Duration(synPeriod) * time.Second,
Username: cmd.Flag("password").Value.String(),
Password: cmd.Flag("username").Value.String(),
}
config.StoreName = etcdv2.StoreName
}
return kv.Dump(context.Background(), config, baseConfig)
}

View File

@ -1,12 +1,13 @@
package cmd
import (
"context"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/ldez/traefik-certs-dumper/v2/dumper/file"
"github.com/spf13/cobra"
)
// fileCmd represents the file command
var fileCmd = &cobra.Command{
Use: "file",
Short: `Dump the content of the "acme.json" file.`,
@ -14,7 +15,9 @@ var fileCmd = &cobra.Command{
RunE: runE(func(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
acmeFile := cmd.Flag("source").Value.String()
return file.Dump(acmeFile, baseConfig)
baseConfig.Version = cmd.Flag("version").Value.String()
return file.Dump(context.Background(), acmeFile, baseConfig)
}),
}
@ -22,4 +25,5 @@ func init() {
rootCmd.AddCommand(fileCmd)
fileCmd.Flags().String("source", "./acme.json", "Path to 'acme.json' file.")
fileCmd.Flags().String("version", "", "Traefik version. If empty use v1. Possible values: 'v2', 'v3'.")
}

View File

@ -3,17 +3,16 @@ package cmd
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io/ioutil"
"os"
"time"
"path/filepath"
"github.com/abronan/valkeyrie/store"
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
"github.com/spf13/cobra"
)
// kvCmd represents the kv command
// kvCmd represents the kv command.
var kvCmd = &cobra.Command{
Use: "kv",
Short: `Dump the content of a KV store.`,
@ -26,6 +25,7 @@ func init() {
kvCmd.PersistentFlags().StringSlice("endpoints", []string{"localhost:8500"}, "List of endpoints.")
kvCmd.PersistentFlags().Int("connection-timeout", 0, "Connection timeout in seconds.")
kvCmd.PersistentFlags().String("prefix", "traefik", "Prefix used for KV store.")
kvCmd.PersistentFlags().String("suffix", kv.DefaultStoreKeySuffix, "Suffix/Storage used for KV store.")
kvCmd.PersistentFlags().String("password", "", "Password for connection.")
kvCmd.PersistentFlags().String("username", "", "Username for connection.")
@ -43,25 +43,10 @@ func getKvConfig(cmd *cobra.Command) (*kv.Config, error) {
return nil, err
}
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
if err != nil {
return nil, err
}
tlsConfig, err := createTLSConfig(cmd)
if err != nil {
return nil, err
}
return &kv.Config{
Endpoints: endpoints,
Prefix: cmd.Flag("prefix").Value.String(),
Options: &store.Config{
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
Username: cmd.Flag("password").Value.String(),
Password: cmd.Flag("username").Value.String(),
TLS: tlsConfig,
},
Suffix: cmd.Flag("suffix").Value.String(),
}, nil
}
@ -84,19 +69,19 @@ func createTLSConfig(cmd *cobra.Command) (*tls.Config, error) {
privateKey := cmd.Flag("tls.key").Value.String()
certContent := cmd.Flag("tls.cert").Value.String()
if !insecureSkipVerify && (len(certContent) == 0 || len(privateKey) == 0) {
return nil, fmt.Errorf("TLS Certificate or Key file must be set when TLS configuration is created")
if !insecureSkipVerify && (certContent == "" || privateKey == "") {
return nil, errors.New("TLS Certificate or Key file must be set when TLS configuration is created")
}
cert, err := getCertificate(privateKey, certContent)
if err != nil {
return nil, fmt.Errorf("failed to load TLS keypair: %s", err)
return nil, fmt.Errorf("failed to load TLS keypair: %w", err)
}
return &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caPool,
InsecureSkipVerify: insecureSkipVerify,
InsecureSkipVerify: insecureSkipVerify, //nolint:gosec // it's a CLI option.
ClientAuth: clientAuth,
}, nil
}
@ -107,11 +92,11 @@ func getCertPool(ca string) (*x509.CertPool, error) {
if ca != "" {
caContent, err := getCAContent(ca)
if err != nil {
return nil, fmt.Errorf("failed to read CA. %s", err)
return nil, fmt.Errorf("failed to read CA. %w", err)
}
if !caPool.AppendCertsFromPEM(caContent) {
return nil, fmt.Errorf("failed to parse CA")
return nil, errors.New("failed to parse CA")
}
}
@ -119,11 +104,14 @@ func getCertPool(ca string) (*x509.CertPool, error) {
}
func getCAContent(ca string) ([]byte, error) {
if _, errCA := os.Stat(ca); errCA != nil {
return []byte(ca), nil
if _, err := os.Stat(ca); err != nil {
if os.IsNotExist(err) {
return []byte(ca), nil
}
return nil, err
}
caContent, err := ioutil.ReadFile(ca)
caContent, err := os.ReadFile(filepath.Clean(ca))
if err != nil {
return nil, err
}
@ -150,11 +138,11 @@ func getCertificate(privateKey, certContent string) (tls.Certificate, error) {
_, errCertIsFile := os.Stat(certContent)
if errCertIsFile == nil && os.IsNotExist(errKeyIsFile) {
return tls.Certificate{}, fmt.Errorf("tls cert is a file, but tls key is not")
return tls.Certificate{}, errors.New("tls cert is a file, but tls key is not")
}
if os.IsNotExist(errCertIsFile) && errKeyIsFile == nil {
return tls.Certificate{}, fmt.Errorf("TLS key is a file, but tls cert is not")
return tls.Certificate{}, errors.New("TLS key is a file, but tls cert is not")
}
// string

View File

@ -1,27 +1,30 @@
package cmd
import (
"crypto/rand"
"fmt"
"io/ioutil"
"log"
"math/big"
"os"
"path/filepath"
"strconv"
"time"
"github.com/charmbracelet/lipgloss"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
homedir "github.com/mitchellh/go-homedir"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
// rootCmd represents the base command when called without any subcommands
// rootCmd represents the base command when called without any subcommands.
var rootCmd = &cobra.Command{
Use: "traefik-certs-dumper",
Short: "Dump Let's Encrypt certificates from Traefik.",
Long: `Dump Let's Encrypt certificates from Traefik.`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
if cmd.Name() == "version" {
return nil
}
@ -35,6 +38,7 @@ var rootCmd = &cobra.Command{
return fmt.Errorf("--crt-ext (%q) and --key-ext (%q) are identical, in this case --domain-subdir is required", crtExt, keyExt)
}
}
return nil
},
}
@ -46,6 +50,8 @@ func Execute() {
log.Println(err)
os.Exit(1)
}
help()
}
func init() {
@ -109,7 +115,7 @@ func runE(apply func(*dumper.BaseConfig, *cobra.Command) error) func(*cobra.Comm
func tree(root, indent string) error {
fi, err := os.Stat(root)
if err != nil {
return fmt.Errorf("could not stat %s: %v", root, err)
return fmt.Errorf("could not stat %s: %w", root, err)
}
fmt.Println(fi.Name())
@ -117,9 +123,9 @@ func tree(root, indent string) error {
return nil
}
fis, err := ioutil.ReadDir(root)
fis, err := os.ReadDir(root)
if err != nil {
return fmt.Errorf("could not read dir %s: %v", root, err)
return fmt.Errorf("could not read dir %s: %w", root, err)
}
var names []string
@ -132,10 +138,10 @@ func tree(root, indent string) error {
for i, name := range names {
add := "│ "
if i == len(names)-1 {
fmt.Printf(indent + "└──")
fmt.Print(indent + "└──")
add = " "
} else {
fmt.Printf(indent + "├──")
fmt.Print(indent + "├──")
}
if err := tree(filepath.Join(root, name), indent+add); err != nil {
@ -178,3 +184,34 @@ func getBaseConfig(cmd *cobra.Command) (*dumper.BaseConfig, error) {
Hook: cmd.Flag("post-hook").Value.String(),
}, nil
}
func help() {
var maxInt int64 = 2 // -> 50%
if time.Now().Month() == time.December {
maxInt = 1 // -> 100%
}
n, _ := rand.Int(rand.Reader, big.NewInt(maxInt))
if n.Cmp(big.NewInt(0)) != 0 {
return
}
log.SetFlags(0)
pStyle := lipgloss.NewStyle().
Padding(1).
BorderStyle(lipgloss.RoundedBorder()).
BorderForeground(lipgloss.Color("161")).
Align(lipgloss.Center)
hStyle := lipgloss.NewStyle().Bold(true)
s := fmt.Sprintln(hStyle.Render("Request for Donation."))
s += `
I need your help!
Donations fund the maintenance and development of traefik-certs-dumper.
Click on this link to donate: https://donate.ldez.dev`
log.Println(pStyle.Render(s))
log.SetFlags(log.LstdFlags | log.Lshortfile)
}

View File

@ -13,11 +13,11 @@ var (
date = "I don't remember exactly"
)
// versionCmd represents the version command
// versionCmd represents the version command.
var versionCmd = &cobra.Command{
Use: "version",
Short: "Display version",
Run: func(cmd *cobra.Command, args []string) {
Run: func(_ *cobra.Command, _ []string) {
displayVersion(rootCmd.Name())
},
}

View File

@ -1,14 +1,16 @@
package cmd
import (
"github.com/abronan/valkeyrie/store"
"github.com/abronan/valkeyrie/store/zookeeper"
"context"
"time"
"github.com/kvtools/zookeeper"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
"github.com/spf13/cobra"
)
// zookeeperCmd represents the zookeeper command
// zookeeperCmd represents the zookeeper command.
var zookeeperCmd = &cobra.Command{
Use: "zookeeper",
Short: "Dump the content of zookeeper.",
@ -26,8 +28,19 @@ func zookeeperRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
return err
}
config.Backend = store.ZK
zookeeper.Register()
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
if err != nil {
return err
}
return kv.Dump(config, baseConfig)
config.Options = &zookeeper.Config{
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
Username: cmd.Flag("password").Value.String(),
Password: cmd.Flag("username").Value.String(),
MaxBufferSize: 0,
}
config.StoreName = zookeeper.StoreName
return kv.Dump(context.Background(), config, baseConfig)
}

1
contrib/readme.md Normal file
View File

@ -0,0 +1 @@
This directory content external contributions that are not maintain by @ldez.

View File

@ -0,0 +1,40 @@
[Unit]
Description=traefik certs dumper
; If you do not start traefik via systemd, choose network.target or docker.target
After=traefik.target
Wants=network-online.target systemd-networkd-wait-online.service
[Service]
Restart=on-abnormal
User=root
ExecStart=/usr/local/bin/traefik-certs-dumper file --version v2 --source /etc/traefik/acme/acme.json --dest /etc/ssl --watch
RestartSec=30
TimeoutSec=30
;WatchdogSec=30
; Limit the number of file descriptors; see `man systemd.exec` for more limit settings.
; LimitNOFILE=1048576
; Limit number of processes in this unit
LimitNPROC=1
; Use private /tmp and /var/tmp, which are discarded after traefik stops.
PrivateTmp=true
; Use a minimal /dev (May bring additional security if switched to 'true', but it may not work on Raspberry Pis or other devices)
PrivateDevices=true
; Hide /home, /root, and /run/user. Nobody will steal your SSH-keys.
ProtectHome=true
; Make cgroups /sys/fs/cgroup read-only
ProtectControlGroups=true
; Make kernel settings (procfs and sysfs) read-only
ProtectKernelTunables=true
; Make /usr, /boot, /etc and possibly some more folders read-only.
ProtectSystem=full
; This merely retains r/w access rights, it does not add any new. Must still be writable on the host!
ReadWriteDirectories=/etc/ssl
ReadOnlyPaths=/etc/traefik/acme/acme.json
; The following additional security directives only work with systemd v229 or later.
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,42 @@
services:
traefik:
image: traefik:v1.7
command:
- --logLevel=INFO
- --defaultEntryPoints=web,websecure
- "--entryPoints=Name:web Address::80 Redirect.EntryPoint:websecure"
- "--entryPoints=Name:websecure Address::443 TLS"
- --docker
- --docker.exposedByDefault=false
- --acme
- --acme.email=email@example.com
- --acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
- --acme.entrypoint=websecure
- --acme.storage=/letsencrypt/acme.json
- --acme.onHostRule
- --acme.tlsChallenge
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./letsencrypt:/letsencrypt
traefik-certs-dumper:
image: ldez/traefik-certs-dumper:v2.9.3
entrypoint: sh -c '
while ! [ -e /data/acme.json ]
|| ! [ `jq ".Certificates | length" /data/acme.json` != 0 ]; do
sleep 1
; done
&& traefik-certs-dumper file --watch
--source /data/acme.json --dest /data/certs'
volumes:
- ./letsencrypt:/data
network_mode: "none"
whoami:
image: traefik/whoami:v1.8.1
labels:
traefik.enable: true
traefik.frontend.rule: Host:example.com

View File

@ -0,0 +1,44 @@
services:
traefik:
image: traefik:v2.11.3
command:
- --log.level=INFO
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.websecure.address=:443
- --entrypoints.websecure.http.tls=true
- --entrypoints.websecure.http.tls.certResolver=le
- --providers.docker.exposedbydefault=false
- --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
- --certificatesresolvers.le.acme.email=email@example.com
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.tlsChallenge=true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt/:/letsencrypt
traefik-certs-dumper:
image: ldez/traefik-certs-dumper:v2.9.3
entrypoint: sh -c '
while ! [ -e /data/acme.json ]
|| ! [ `jq ".[] | .Certificates | length" /data/acme.json | jq -s "add" ` != 0 ]; do
sleep 1
; done
&& traefik-certs-dumper file --version v2 --watch
--source /data/acme.json --dest /data/certs'
volumes:
- ./letsencrypt:/data
network_mode: "none"
whoami:
image: traefik/whoami:v1.8.1
labels:
traefik.enable: 'true'
traefik.http.routers.app.rule: Host(`example.com`)
traefik.http.routers.app.entrypoints: websecure

View File

@ -24,8 +24,9 @@ Dump Let's Encrypt certificates from Traefik.
### SEE ALSO
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
* [traefik-certs-dumper file](traefik-certs-dumper_file.md) - Dump the content of the "acme.json" file.
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
* [traefik-certs-dumper version](traefik-certs-dumper_version.md) - Display version
###### Auto generated by spf13/cobra on 25-Apr-2019
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -0,0 +1,40 @@
## traefik-certs-dumper completion
Generate the autocompletion script for the specified shell
### Synopsis
Generate the autocompletion script for traefik-certs-dumper for the specified shell.
See each sub-command's help for details on how to use the generated script.
### Options
```
-h, --help help for completion
```
### Options inherited from parent commands
```
--clean Clean destination folder before dumping content. (default true)
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
--crt-ext string The file extension of the generated certificates. (default ".crt")
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
--dest string Path to store the dump content. (default "./dump")
--domain-subdir Use domain as sub-directory.
--key-ext string The file extension of the generated private keys. (default ".key")
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
--watch Enable watching changes.
```
### SEE ALSO
* [traefik-certs-dumper](traefik-certs-dumper.md) - Dump Let's Encrypt certificates from Traefik.
* [traefik-certs-dumper completion bash](traefik-certs-dumper_completion_bash.md) - Generate the autocompletion script for bash
* [traefik-certs-dumper completion fish](traefik-certs-dumper_completion_fish.md) - Generate the autocompletion script for fish
* [traefik-certs-dumper completion powershell](traefik-certs-dumper_completion_powershell.md) - Generate the autocompletion script for powershell
* [traefik-certs-dumper completion zsh](traefik-certs-dumper_completion_zsh.md) - Generate the autocompletion script for zsh
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -0,0 +1,59 @@
## traefik-certs-dumper completion bash
Generate the autocompletion script for bash
### Synopsis
Generate the autocompletion script for the bash shell.
This script depends on the 'bash-completion' package.
If it is not installed already, you can install it via your OS's package manager.
To load completions in your current shell session:
source <(traefik-certs-dumper completion bash)
To load completions for every new session, execute once:
#### Linux:
traefik-certs-dumper completion bash > /etc/bash_completion.d/traefik-certs-dumper
#### macOS:
traefik-certs-dumper completion bash > $(brew --prefix)/etc/bash_completion.d/traefik-certs-dumper
You will need to start a new shell for this setup to take effect.
```
traefik-certs-dumper completion bash
```
### Options
```
-h, --help help for bash
--no-descriptions disable completion descriptions
```
### Options inherited from parent commands
```
--clean Clean destination folder before dumping content. (default true)
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
--crt-ext string The file extension of the generated certificates. (default ".crt")
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
--dest string Path to store the dump content. (default "./dump")
--domain-subdir Use domain as sub-directory.
--key-ext string The file extension of the generated private keys. (default ".key")
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
--watch Enable watching changes.
```
### SEE ALSO
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -0,0 +1,50 @@
## traefik-certs-dumper completion fish
Generate the autocompletion script for fish
### Synopsis
Generate the autocompletion script for the fish shell.
To load completions in your current shell session:
traefik-certs-dumper completion fish | source
To load completions for every new session, execute once:
traefik-certs-dumper completion fish > ~/.config/fish/completions/traefik-certs-dumper.fish
You will need to start a new shell for this setup to take effect.
```
traefik-certs-dumper completion fish [flags]
```
### Options
```
-h, --help help for fish
--no-descriptions disable completion descriptions
```
### Options inherited from parent commands
```
--clean Clean destination folder before dumping content. (default true)
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
--crt-ext string The file extension of the generated certificates. (default ".crt")
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
--dest string Path to store the dump content. (default "./dump")
--domain-subdir Use domain as sub-directory.
--key-ext string The file extension of the generated private keys. (default ".key")
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
--watch Enable watching changes.
```
### SEE ALSO
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -0,0 +1,47 @@
## traefik-certs-dumper completion powershell
Generate the autocompletion script for powershell
### Synopsis
Generate the autocompletion script for powershell.
To load completions in your current shell session:
traefik-certs-dumper completion powershell | Out-String | Invoke-Expression
To load completions for every new session, add the output of the above command
to your powershell profile.
```
traefik-certs-dumper completion powershell [flags]
```
### Options
```
-h, --help help for powershell
--no-descriptions disable completion descriptions
```
### Options inherited from parent commands
```
--clean Clean destination folder before dumping content. (default true)
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
--crt-ext string The file extension of the generated certificates. (default ".crt")
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
--dest string Path to store the dump content. (default "./dump")
--domain-subdir Use domain as sub-directory.
--key-ext string The file extension of the generated private keys. (default ".key")
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
--watch Enable watching changes.
```
### SEE ALSO
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -0,0 +1,61 @@
## traefik-certs-dumper completion zsh
Generate the autocompletion script for zsh
### Synopsis
Generate the autocompletion script for the zsh shell.
If shell completion is not already enabled in your environment you will need
to enable it. You can execute the following once:
echo "autoload -U compinit; compinit" >> ~/.zshrc
To load completions in your current shell session:
source <(traefik-certs-dumper completion zsh)
To load completions for every new session, execute once:
#### Linux:
traefik-certs-dumper completion zsh > "${fpath[1]}/_traefik-certs-dumper"
#### macOS:
traefik-certs-dumper completion zsh > $(brew --prefix)/share/zsh/site-functions/_traefik-certs-dumper
You will need to start a new shell for this setup to take effect.
```
traefik-certs-dumper completion zsh [flags]
```
### Options
```
-h, --help help for zsh
--no-descriptions disable completion descriptions
```
### Options inherited from parent commands
```
--clean Clean destination folder before dumping content. (default true)
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
--crt-ext string The file extension of the generated certificates. (default ".crt")
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
--dest string Path to store the dump content. (default "./dump")
--domain-subdir Use domain as sub-directory.
--key-ext string The file extension of the generated private keys. (default ".key")
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
--watch Enable watching changes.
```
### SEE ALSO
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -13,8 +13,9 @@ traefik-certs-dumper file [flags]
### Options
```
-h, --help help for file
--source string Path to 'acme.json' file. (default "./acme.json")
-h, --help help for file
--source string Path to 'acme.json' file. (default "./acme.json")
--version string Traefik version. If empty use v1. Possible values: 'v2', 'v3'.
```
### Options inherited from parent commands
@ -36,4 +37,4 @@ traefik-certs-dumper file [flags]
* [traefik-certs-dumper](traefik-certs-dumper.md) - Dump Let's Encrypt certificates from Traefik.
###### Auto generated by spf13/cobra on 25-Apr-2019
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -14,6 +14,7 @@ Dump the content of a KV store.
-h, --help help for kv
--password string Password for connection.
--prefix string Prefix used for KV store. (default "traefik")
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
--tls Enable TLS encryption.
--tls.ca string Root CA for certificate verification if TLS is enabled
--tls.ca.optional
@ -46,4 +47,4 @@ Dump the content of a KV store.
* [traefik-certs-dumper kv etcd](traefik-certs-dumper_kv_etcd.md) - Dump the content of etcd.
* [traefik-certs-dumper kv zookeeper](traefik-certs-dumper_kv_zookeeper.md) - Dump the content of zookeeper.
###### Auto generated by spf13/cobra on 25-Apr-2019
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -34,6 +34,7 @@ traefik-certs-dumper kv boltdb [flags]
--password string Password for connection.
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
--prefix string Prefix used for KV store. (default "traefik")
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
--tls Enable TLS encryption.
--tls.ca string Root CA for certificate verification if TLS is enabled
--tls.ca.optional
@ -48,4 +49,4 @@ traefik-certs-dumper kv boltdb [flags]
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
###### Auto generated by spf13/cobra on 25-Apr-2019
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -33,6 +33,7 @@ traefik-certs-dumper kv consul [flags]
--password string Password for connection.
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
--prefix string Prefix used for KV store. (default "traefik")
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
--tls Enable TLS encryption.
--tls.ca string Root CA for certificate verification if TLS is enabled
--tls.ca.optional
@ -47,4 +48,4 @@ traefik-certs-dumper kv consul [flags]
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
###### Auto generated by spf13/cobra on 25-Apr-2019
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -13,8 +13,9 @@ traefik-certs-dumper kv etcd [flags]
### Options
```
-h, --help help for etcd
--sync-period int Sync period for etcd in seconds.
--etcd-version string The etcd version can be: 'etcd' or 'etcdv3'. (default "etcd")
-h, --help help for etcd
--sync-period int Sync period for etcd in seconds.
```
### Options inherited from parent commands
@ -33,6 +34,7 @@ traefik-certs-dumper kv etcd [flags]
--password string Password for connection.
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
--prefix string Prefix used for KV store. (default "traefik")
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
--tls Enable TLS encryption.
--tls.ca string Root CA for certificate verification if TLS is enabled
--tls.ca.optional
@ -47,4 +49,4 @@ traefik-certs-dumper kv etcd [flags]
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
###### Auto generated by spf13/cobra on 25-Apr-2019
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -32,6 +32,7 @@ traefik-certs-dumper kv zookeeper [flags]
--password string Password for connection.
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
--prefix string Prefix used for KV store. (default "traefik")
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
--tls Enable TLS encryption.
--tls.ca string Root CA for certificate verification if TLS is enabled
--tls.ca.optional
@ -46,4 +47,4 @@ traefik-certs-dumper kv zookeeper [flags]
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
###### Auto generated by spf13/cobra on 25-Apr-2019
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -2,10 +2,6 @@
Display version
### Synopsis
Display version
```
traefik-certs-dumper version [flags]
```
@ -35,4 +31,4 @@ traefik-certs-dumper version [flags]
* [traefik-certs-dumper](traefik-certs-dumper.md) - Dump Let's Encrypt certificates from Traefik.
###### Auto generated by spf13/cobra on 25-Apr-2019
###### Auto generated by spf13/cobra on 21-Feb-2025

View File

@ -9,4 +9,11 @@ type BaseConfig struct {
Clean bool
Watch bool
Hook string
Version string
}
// FileInfo File information.
type FileInfo struct {
Name string
Ext string
}

View File

@ -2,58 +2,133 @@ package file
import (
"bytes"
"crypto/md5"
"context"
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"github.com/fsnotify/fsnotify"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
dumperv1 "github.com/ldez/traefik-certs-dumper/v2/dumper/v1"
dumperv2 "github.com/ldez/traefik-certs-dumper/v2/dumper/v2"
dumperv3 "github.com/ldez/traefik-certs-dumper/v2/dumper/v3"
"github.com/ldez/traefik-certs-dumper/v2/hook"
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv1"
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv2"
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv3"
)
// Dump Dumps "acme.json" file to certificates.
func Dump(acmeFile string, baseConfig *dumper.BaseConfig) error {
func Dump(ctx context.Context, acmeFile string, baseConfig *dumper.BaseConfig) error {
err := dump(acmeFile, baseConfig)
if err != nil {
return err
}
if baseConfig.Watch {
return watch(acmeFile, baseConfig)
hook.Exec(ctx, baseConfig.Hook)
return watch(ctx, acmeFile, baseConfig)
}
return nil
}
func dump(acmeFile string, baseConfig *dumper.BaseConfig) error {
data, err := readFile(acmeFile)
switch baseConfig.Version {
case "v3":
err := dumpV3(acmeFile, baseConfig)
if err != nil {
return fmt.Errorf("v3: dump failed: %w", err)
}
return nil
case "v2":
err := dumpV2(acmeFile, baseConfig)
if err != nil {
return fmt.Errorf("v2: dump failed: %w", err)
}
return nil
case "v1":
err := dumpV1(acmeFile, baseConfig)
if err != nil {
return fmt.Errorf("v1: dump failed: %w", err)
}
return nil
default:
err := dumpV1(acmeFile, baseConfig)
if err != nil {
return fmt.Errorf("v1: dump failed: %w", err)
}
return nil
}
}
func dumpV1(acmeFile string, baseConfig *dumper.BaseConfig) error {
data := &traefikv1.StoredData{}
err := readJSONFile(acmeFile, data)
if err != nil {
return err
}
return dumper.Dump(data, baseConfig)
return dumperv1.Dump(data, baseConfig)
}
func readFile(acmeFile string) (*dumper.StoredData, error) {
source, err := os.Open(acmeFile)
func dumpV2(acmeFile string, baseConfig *dumper.BaseConfig) error {
data := map[string]*traefikv2.StoredData{}
err := readJSONFile(acmeFile, &data)
if err != nil {
return nil, err
return err
}
data := &dumper.StoredData{}
if err = json.NewDecoder(source).Decode(data); err != nil {
return nil, err
}
return data, nil
return dumperv2.Dump(data, baseConfig)
}
func watch(acmeFile string, baseConfig *dumper.BaseConfig) error {
func dumpV3(acmeFile string, baseConfig *dumper.BaseConfig) error {
data := map[string]*traefikv3.StoredData{}
err := readJSONFile(acmeFile, &data)
if err != nil {
return err
}
return dumperv3.Dump(data, baseConfig)
}
func readJSONFile(acmeFile string, data interface{}) error {
source, err := os.Open(filepath.Clean(acmeFile))
if err != nil {
return fmt.Errorf("failed to open file %q: %w", acmeFile, err)
}
defer func() { _ = source.Close() }()
err = json.NewDecoder(source).Decode(data)
if errors.Is(err, io.EOF) {
log.Printf("warn: file %q may not be ready: %v", acmeFile, err)
return nil
}
if err != nil {
return fmt.Errorf("failed to unmarshal file %q: %w", acmeFile, err)
}
return nil
}
func watch(ctx context.Context, acmeFile string, baseConfig *dumper.BaseConfig) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
return fmt.Errorf("failed to create new watcher: %w", err)
}
defer func() { _ = watcher.Close() }()
@ -73,7 +148,7 @@ func watch(acmeFile string, baseConfig *dumper.BaseConfig) error {
log.Println("event:", event)
}
hash, errW := manageEvent(watcher, event, acmeFile, previousHash, baseConfig)
hash, errW := manageEvent(ctx, watcher, event, acmeFile, previousHash, baseConfig)
if errW != nil {
log.Println("error:", errW)
done <- true
@ -96,7 +171,7 @@ func watch(acmeFile string, baseConfig *dumper.BaseConfig) error {
err = watcher.Add(acmeFile)
if err != nil {
return err
return fmt.Errorf("failed to add a new watcher: %w", err)
}
<-done
@ -104,15 +179,15 @@ func watch(acmeFile string, baseConfig *dumper.BaseConfig) error {
return nil
}
func manageEvent(watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile string, previousHash []byte, baseConfig *dumper.BaseConfig) ([]byte, error) {
func manageEvent(ctx context.Context, watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile string, previousHash []byte, baseConfig *dumper.BaseConfig) ([]byte, error) {
err := manageRename(watcher, event, acmeFile)
if err != nil {
return nil, err
return nil, fmt.Errorf("watcher renewal failed: %w", err)
}
hash, err := calculateHash(acmeFile)
if err != nil {
return nil, err
return nil, fmt.Errorf("file hash calculation failed: %w", err)
}
if !bytes.Equal(previousHash, hash) {
@ -128,7 +203,7 @@ func manageEvent(watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile strin
log.Println("Dumped new certificate data.")
}
hook.Exec(baseConfig.Hook)
hook.Exec(ctx, baseConfig.Hook)
}
return hash, nil
@ -147,13 +222,13 @@ func manageRename(watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile stri
}
func calculateHash(acmeFile string) ([]byte, error) {
file, err := os.Open(acmeFile)
file, err := os.Open(filepath.Clean(acmeFile))
if err != nil {
return nil, err
}
defer func() { _ = file.Close() }()
h := md5.New()
h := sha256.New()
_, err = io.Copy(h, file)
if err != nil {
return nil, err

60
dumper/file/file_test.go Normal file
View File

@ -0,0 +1,60 @@
package file
import (
"testing"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/stretchr/testify/require"
)
func TestDump(t *testing.T) {
testCases := []struct {
desc string
acmeFile string
version string
}{
{
desc: "should skip EOF error",
acmeFile: "./fixtures/acme-empty.json",
},
{
desc: "should dump traefik v1 file content",
acmeFile: "./fixtures/acme-v1.json",
},
{
desc: "should dump traefik v2 file content",
acmeFile: "./fixtures/acme-v2.json",
version: "v2",
},
{
desc: "should dump traefik v3 file content",
acmeFile: "./fixtures/acme-v3.json",
version: "v3",
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
dir := t.TempDir()
cfg := &dumper.BaseConfig{
DumpPath: dir,
CrtInfo: dumper.FileInfo{
Name: "certificate",
Ext: ".crt",
},
KeyInfo: dumper.FileInfo{
Name: "privatekey",
Ext: ".key",
},
Clean: true,
Version: test.version,
}
err := Dump(t.Context(), test.acmeFile, cfg)
require.NoError(t, err)
})
}
}

View File

View File

@ -0,0 +1,12 @@
{
"Certificates": [
{
"domain": {
"main": "test.example.com"
},
"certificate": "Q2VydGlmaWNhdGU=",
"key": "Q2VydGlmaWNhdGUgS2V5",
"Store": "default"
}
]
}

View File

@ -0,0 +1,36 @@
{
"default": {
"Account": {
"Email": "test@email.com",
"Registration": {
"body": {
"status": "valid",
"contact": [
"mailto:test@email.com"
]
},
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/12345678"
},
"PrivateKey": "Q2VydGlmaWNhdGUgS2V5",
"KeyType": "4096"
},
"Certificates": [
{
"domain": {
"main": "my.domain.com"
},
"certificate": "Q2VydGlmaWNhdGU=",
"key": "Q2VydGlmaWNhdGUgS2V5",
"Store": "default"
},
{
"domain": {
"main": "my.domain2.com"
},
"certificate": "Q2VydGlmaWNhdGU=",
"key": "Q2VydGlmaWNhdGUgS2V5",
"Store": "default"
}
]
}
}

View File

@ -0,0 +1,36 @@
{
"default": {
"Account": {
"Email": "test@email.com",
"Registration": {
"body": {
"status": "valid",
"contact": [
"mailto:test@email.com"
]
},
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/12345678"
},
"PrivateKey": "Q2VydGlmaWNhdGUgS2V5",
"KeyType": "4096"
},
"Certificates": [
{
"domain": {
"main": "my.domain.com"
},
"certificate": "Q2VydGlmaWNhdGU=",
"key": "Q2VydGlmaWNhdGUgS2V5",
"Store": "default"
},
{
"domain": {
"main": "my.domain2.com"
},
"certificate": "Q2VydGlmaWNhdGU=",
"key": "Q2VydGlmaWNhdGUgS2V5",
"Store": "default"
}
]
}
}

View File

@ -1,11 +1,14 @@
package kv
import "github.com/abronan/valkeyrie/store"
import (
"github.com/kvtools/valkeyrie"
)
// Config KV configuration.
type Config struct {
Backend store.Backend
StoreName string
Prefix string
Suffix string
Endpoints []string
Options *store.Config
Options valkeyrie.Config
}

View File

@ -1,13 +1,13 @@
package kv
import (
"github.com/go-acme/lego/certcrypto"
"github.com/go-acme/lego/registration"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/registration"
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv1"
)
// CertificateV1 is used to store certificate info
type CertificateV1 struct {
// CertificateOld is used to store certificate info.
type CertificateOld struct {
Domain string
CertURL string
CertStableURL string
@ -15,8 +15,8 @@ type CertificateV1 struct {
Certificate []byte
}
// AccountV1 is used to store lets encrypt registration info
type AccountV1 struct {
// AccountOld is used to store lets encrypt registration info.
type AccountOld struct {
Email string
Registration *registration.Resource
PrivateKey []byte
@ -26,40 +26,43 @@ type AccountV1 struct {
HTTPChallenge map[string]map[string][]byte
}
// DomainsCertificates stores a certificate for multiple domains
// DomainsCertificates stores a certificate for multiple domains.
type DomainsCertificates struct {
Certs []*DomainsCertificate
}
// ChallengeCert stores a challenge certificate
// ChallengeCert stores a challenge certificate.
type ChallengeCert struct {
Certificate []byte
PrivateKey []byte
}
// DomainsCertificate contains a certificate for multiple domains
// DomainsCertificate contains a certificate for multiple domains.
type DomainsCertificate struct {
Domains dumper.Domain
Certificate *CertificateV1
Domains traefikv1.Domain
Certificate *CertificateOld
}
// convertAccountV1ToV2 converts account information from version 1 to 2
func convertAccountV1ToV2(account *AccountV1) *dumper.StoredData {
storedData := &dumper.StoredData{}
storedData.Account = &dumper.Account{
PrivateKey: account.PrivateKey,
Registration: account.Registration,
Email: account.Email,
KeyType: account.KeyType,
// convertOldAccount converts account information from old account format.
func convertOldAccount(account *AccountOld) *traefikv1.StoredData {
storedData := &traefikv1.StoredData{
Account: &traefikv1.Account{
PrivateKey: account.PrivateKey,
Registration: account.Registration,
Email: account.Email,
KeyType: account.KeyType,
},
}
var certs []*dumper.Certificate
var certs []*traefikv1.Certificate
for _, oldCert := range account.DomainsCertificate.Certs {
certs = append(certs, &dumper.Certificate{
certs = append(certs, &traefikv1.Certificate{
Certificate: oldCert.Certificate.Certificate,
Domain: oldCert.Domains,
Key: oldCert.Certificate.PrivateKey,
})
}
storedData.Certificates = certs
return storedData
}

View File

@ -3,46 +3,48 @@ package kv
import (
"bytes"
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"log"
"os"
"strings"
"github.com/abronan/valkeyrie"
"github.com/abronan/valkeyrie/store"
"github.com/kvtools/valkeyrie"
"github.com/kvtools/valkeyrie/store"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
v1 "github.com/ldez/traefik-certs-dumper/v2/dumper/v1"
"github.com/ldez/traefik-certs-dumper/v2/hook"
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv1"
)
const storeKeySuffix = "/acme/account/object"
// DefaultStoreKeySuffix is the default suffix/storage.
const DefaultStoreKeySuffix = "/acme/account/object"
// Dump Dumps KV content to certificates.
func Dump(config *Config, baseConfig *dumper.BaseConfig) error {
kvStore, err := valkeyrie.NewStore(config.Backend, config.Endpoints, config.Options)
func Dump(ctx context.Context, config *Config, baseConfig *dumper.BaseConfig) error {
kvStore, err := valkeyrie.NewStore(ctx, config.StoreName, config.Endpoints, config.Options)
if err != nil {
return fmt.Errorf("unable to create client of the store: %v", err)
return fmt.Errorf("unable to create client of the store: %w", err)
}
storeKey := config.Prefix + storeKeySuffix
storeKey := config.Prefix + config.Suffix
if baseConfig.Watch {
return watch(kvStore, storeKey, baseConfig)
return watch(ctx, kvStore, storeKey, baseConfig)
}
pair, err := kvStore.Get(storeKey, nil)
pair, err := kvStore.Get(ctx, storeKey, nil)
if err != nil {
return fmt.Errorf("unable to retrieve %s value: %v", storeKey, err)
return fmt.Errorf("unable to retrieve %s value: %w", storeKey, err)
}
return dumpPair(pair, baseConfig)
}
func watch(kvStore store.Store, storeKey string, baseConfig *dumper.BaseConfig) error {
stopCh := make(<-chan struct{})
pairs, err := kvStore.Watch(storeKey, stopCh, nil)
func watch(ctx context.Context, kvStore store.Store, storeKey string, baseConfig *dumper.BaseConfig) error {
pairs, err := kvStore.Watch(ctx, storeKey, nil)
if err != nil {
return err
}
@ -62,7 +64,7 @@ func watch(kvStore store.Store, storeKey string, baseConfig *dumper.BaseConfig)
log.Println("Dumped new certificate data.")
}
hook.Exec(baseConfig.Hook)
hook.Exec(ctx, baseConfig.Hook)
}
}
@ -72,26 +74,27 @@ func dumpPair(pair *store.KVPair, baseConfig *dumper.BaseConfig) error {
return err
}
return dumper.Dump(data, baseConfig)
return v1.Dump(data, baseConfig)
}
func getStoredDataFromGzip(pair *store.KVPair) (*dumper.StoredData, error) {
func getStoredDataFromGzip(pair *store.KVPair) (*traefikv1.StoredData, error) {
reader, err := gzip.NewReader(bytes.NewBuffer(pair.Value))
if err != nil {
return nil, fmt.Errorf("fail to create GZip reader: %v", err)
return nil, fmt.Errorf("fail to create GZip reader: %w", err)
}
acmeData, err := ioutil.ReadAll(reader)
acmeData, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("unable to read the pair content: %v", err)
return nil, fmt.Errorf("unable to read the pair content: %w", err)
}
account := &AccountV1{}
account := &AccountOld{}
//nolint:musttag // old format
if err := json.Unmarshal(acmeData, &account); err != nil {
return nil, fmt.Errorf("unable marshal AccountV1: %v", err)
return nil, fmt.Errorf("unable marshal AccountOld: %w", err)
}
return convertAccountV1ToV2(account), nil
return convertOldAccount(account), nil
}
func isDebug() bool {

View File

@ -1,12 +1,14 @@
package dumper
package v1
import (
"encoding/pem"
"io/ioutil"
"fmt"
"os"
"path/filepath"
"github.com/go-acme/lego/certcrypto"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv1"
)
const (
@ -14,77 +16,70 @@ const (
keysSubDir = "private"
)
// FileInfo File information.
type FileInfo struct {
Name string
Ext string
}
// Dump Dumps data to certificates.
func Dump(data *StoredData, baseConfig *BaseConfig) error {
func Dump(data *traefikv1.StoredData, baseConfig *dumper.BaseConfig) error {
if baseConfig.Clean {
err := cleanDir(baseConfig.DumpPath)
if err != nil {
return err
return fmt.Errorf("folder cleaning failed: %w", err)
}
}
if !baseConfig.DomainSubDir {
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0755); err != nil {
return err
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0o755); err != nil {
return fmt.Errorf("certs folder creation failure: %w", err)
}
}
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0755); err != nil {
return err
}
privateKeyPem := extractPEMPrivateKey(data.Account)
err := ioutil.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0666)
if err != nil {
return err
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0o755); err != nil {
return fmt.Errorf("keys folder creation failure: %w", err)
}
for _, cert := range data.Certificates {
err := writeCert(baseConfig.DumpPath, cert, baseConfig.CrtInfo, baseConfig.DomainSubDir)
if err != nil {
return err
return fmt.Errorf("failed to write certificates: %w", err)
}
err = writeKey(baseConfig.DumpPath, cert, baseConfig.KeyInfo, baseConfig.DomainSubDir)
if err != nil {
return err
return fmt.Errorf("failed to write certificate keys: %w", err)
}
}
return nil
if data.Account == nil {
return nil
}
privateKeyPem := extractPEMPrivateKey(data.Account)
return os.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0o600)
}
func writeCert(dumpPath string, cert *Certificate, info FileInfo, domainSubDir bool) error {
func writeCert(dumpPath string, cert *traefikv1.Certificate, info dumper.FileInfo, domainSubDir bool) error {
certPath := filepath.Join(dumpPath, certsSubDir, safeName(cert.Domain.Main+info.Ext))
if domainSubDir {
certPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0755); err != nil {
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
return err
}
}
return ioutil.WriteFile(certPath, cert.Certificate, 0666)
return os.WriteFile(certPath, cert.Certificate, 0o666)
}
func writeKey(dumpPath string, cert *Certificate, info FileInfo, domainSubDir bool) error {
func writeKey(dumpPath string, cert *traefikv1.Certificate, info dumper.FileInfo, domainSubDir bool) error {
keyPath := filepath.Join(dumpPath, keysSubDir, safeName(cert.Domain.Main+info.Ext))
if domainSubDir {
keyPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0755); err != nil {
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
return err
}
}
return ioutil.WriteFile(keyPath, cert.Key, 0666)
return os.WriteFile(keyPath, cert.Key, 0o600)
}
func extractPEMPrivateKey(account *Account) []byte {
func extractPEMPrivateKey(account *traefikv1.Account) []byte {
var block *pem.Block
switch account.KeyType {
case certcrypto.RSA2048, certcrypto.RSA4096, certcrypto.RSA8192:
@ -98,7 +93,7 @@ func extractPEMPrivateKey(account *Account) []byte {
Bytes: account.PrivateKey,
}
default:
panic("unsupported key type")
panic(fmt.Sprintf("unsupported key type: '%v'", account.KeyType))
}
return pem.EncodeToMemory(block)
@ -114,7 +109,7 @@ func cleanDir(dumpPath string) error {
return errExists
}
dir, err := ioutil.ReadDir(dumpPath)
dir, err := os.ReadDir(dumpPath)
if err != nil {
return err
}

View File

@ -1,6 +1,7 @@
//go:build !windows
// +build !windows
package dumper
package v1
func safeName(filename string) string {
return filename

View File

@ -1,6 +1,6 @@
// +build windows
package dumper
package v1
import "strings"

132
dumper/v2/dumper.go Normal file
View File

@ -0,0 +1,132 @@
package v2
import (
"encoding/pem"
"fmt"
"os"
"path/filepath"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv2"
)
const (
certsSubDir = "certs"
keysSubDir = "private"
)
// Dump Dumps data to certificates.
func Dump(data map[string]*traefikv2.StoredData, baseConfig *dumper.BaseConfig) error {
if baseConfig.Clean {
err := cleanDir(baseConfig.DumpPath)
if err != nil {
return fmt.Errorf("folder cleaning failed: %w", err)
}
}
if !baseConfig.DomainSubDir {
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0o755); err != nil {
return fmt.Errorf("certs folder creation failure: %w", err)
}
}
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0o755); err != nil {
return fmt.Errorf("keys folder creation failure: %w", err)
}
for _, store := range data {
for _, cert := range store.Certificates {
err := writeCert(baseConfig.DumpPath, cert.Certificate, baseConfig.CrtInfo, baseConfig.DomainSubDir)
if err != nil {
return fmt.Errorf("failed to write certificates: %w", err)
}
err = writeKey(baseConfig.DumpPath, cert.Certificate, baseConfig.KeyInfo, baseConfig.DomainSubDir)
if err != nil {
return fmt.Errorf("failed to write certificate keys: %w", err)
}
}
if store.Account == nil {
continue
}
privateKeyPem := extractPEMPrivateKey(store.Account)
err := os.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0o600)
if err != nil {
return fmt.Errorf("failed to write private key: %w", err)
}
}
return nil
}
func writeCert(dumpPath string, cert traefikv2.Certificate, info dumper.FileInfo, domainSubDir bool) error {
certPath := filepath.Join(dumpPath, certsSubDir, safeName(cert.Domain.Main+info.Ext))
if domainSubDir {
certPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
return err
}
}
return os.WriteFile(certPath, cert.Certificate, 0o666)
}
func writeKey(dumpPath string, cert traefikv2.Certificate, info dumper.FileInfo, domainSubDir bool) error {
keyPath := filepath.Join(dumpPath, keysSubDir, safeName(cert.Domain.Main+info.Ext))
if domainSubDir {
keyPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
return err
}
}
return os.WriteFile(keyPath, cert.Key, 0o600)
}
func extractPEMPrivateKey(account *traefikv2.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(fmt.Sprintf("unsupported key type: '%v'", account.KeyType))
}
return pem.EncodeToMemory(block)
}
func cleanDir(dumpPath string) error {
_, errExists := os.Stat(dumpPath)
if os.IsNotExist(errExists) {
return nil
}
if errExists != nil {
return errExists
}
dir, err := os.ReadDir(dumpPath)
if err != nil {
return err
}
for _, f := range dir {
if err := os.RemoveAll(filepath.Join(dumpPath, f.Name())); err != nil {
return err
}
}
return nil
}

8
dumper/v2/filename.go Normal file
View File

@ -0,0 +1,8 @@
//go:build !windows
// +build !windows
package v2
func safeName(filename string) string {
return filename
}

View File

@ -0,0 +1,9 @@
// +build windows
package v2
import "strings"
func safeName(filename string) string {
return strings.ReplaceAll(filename, "*", "_")
}

132
dumper/v3/dumper.go Normal file
View File

@ -0,0 +1,132 @@
package v3
import (
"encoding/pem"
"fmt"
"os"
"path/filepath"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/ldez/traefik-certs-dumper/v2/dumper"
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv3"
)
const (
certsSubDir = "certs"
keysSubDir = "private"
)
// Dump Dumps data to certificates.
func Dump(data map[string]*traefikv3.StoredData, baseConfig *dumper.BaseConfig) error {
if baseConfig.Clean {
err := cleanDir(baseConfig.DumpPath)
if err != nil {
return fmt.Errorf("folder cleaning failed: %w", err)
}
}
if !baseConfig.DomainSubDir {
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0o755); err != nil {
return fmt.Errorf("certs folder creation failure: %w", err)
}
}
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0o755); err != nil {
return fmt.Errorf("keys folder creation failure: %w", err)
}
for _, store := range data {
for _, cert := range store.Certificates {
err := writeCert(baseConfig.DumpPath, cert.Certificate, baseConfig.CrtInfo, baseConfig.DomainSubDir)
if err != nil {
return fmt.Errorf("failed to write certificates: %w", err)
}
err = writeKey(baseConfig.DumpPath, cert.Certificate, baseConfig.KeyInfo, baseConfig.DomainSubDir)
if err != nil {
return fmt.Errorf("failed to write certificate keys: %w", err)
}
}
if store.Account == nil {
continue
}
privateKeyPem := extractPEMPrivateKey(store.Account)
err := os.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0o600)
if err != nil {
return fmt.Errorf("failed to write private key: %w", err)
}
}
return nil
}
func writeCert(dumpPath string, cert traefikv3.Certificate, info dumper.FileInfo, domainSubDir bool) error {
certPath := filepath.Join(dumpPath, certsSubDir, safeName(cert.Domain.Main+info.Ext))
if domainSubDir {
certPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
return err
}
}
return os.WriteFile(certPath, cert.Certificate, 0o666)
}
func writeKey(dumpPath string, cert traefikv3.Certificate, info dumper.FileInfo, domainSubDir bool) error {
keyPath := filepath.Join(dumpPath, keysSubDir, safeName(cert.Domain.Main+info.Ext))
if domainSubDir {
keyPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
return err
}
}
return os.WriteFile(keyPath, cert.Key, 0o600)
}
func extractPEMPrivateKey(account *traefikv3.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(fmt.Sprintf("unsupported key type: '%v'", account.KeyType))
}
return pem.EncodeToMemory(block)
}
func cleanDir(dumpPath string) error {
_, errExists := os.Stat(dumpPath)
if os.IsNotExist(errExists) {
return nil
}
if errExists != nil {
return errExists
}
dir, err := os.ReadDir(dumpPath)
if err != nil {
return err
}
for _, f := range dir {
if err := os.RemoveAll(filepath.Join(dumpPath, f.Name())); err != nil {
return err
}
}
return nil
}

8
dumper/v3/filename.go Normal file
View File

@ -0,0 +1,8 @@
//go:build !windows
// +build !windows
package v3
func safeName(filename string) string {
return filename
}

View File

@ -0,0 +1,10 @@
//go:build windows
// +build windows
package v3
import "strings"
func safeName(filename string) string {
return strings.ReplaceAll(filename, "*", "_")
}

125
go.mod
View File

@ -1,51 +1,90 @@
module github.com/ldez/traefik-certs-dumper/v2
go 1.12
go 1.24.0
require (
github.com/abronan/valkeyrie v0.0.0-20190419181538-ccf7df650fe4
github.com/cenkalti/backoff v2.1.1+incompatible // indirect
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/cpuguy83/go-md2man v1.0.10 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/fsnotify/fsnotify v1.4.7
github.com/go-acme/lego v2.5.0+incompatible
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-msgpack v0.5.4 // 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/charmbracelet/lipgloss v1.0.0
github.com/fsnotify/fsnotify v1.9.0
github.com/go-acme/lego/v4 v4.25.2
github.com/kvtools/boltdb v1.0.2
github.com/kvtools/consul v1.0.2
github.com/kvtools/etcdv2 v1.0.2
github.com/kvtools/etcdv3 v1.0.2
github.com/kvtools/valkeyrie v1.0.0
github.com/kvtools/zookeeper v1.0.2
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/pascaldekloe/goe v0.1.0 // indirect
github.com/pkg/errors v0.8.1 // 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/viper v1.3.2
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
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/time v0.0.0-20190308202827-9d24e82272b4 // indirect
gopkg.in/square/go-jose.v2 v2.3.1 // indirect
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.10.0
)
replace (
github.com/ugorji/go => github.com/ugorji/go v1.1.2-0.20181022190402-e5e69e061d4f
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 => github.com/ugorji/go/codec v1.1.2-0.20181022190402-e5e69e061d4f
require (
github.com/armon/go-metrics v0.4.1 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/charmbracelet/x/ansi v0.4.2 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
github.com/go-zookeeper/zk v1.0.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/hashicorp/consul/api v1.28.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-metrics v0.5.4 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
github.com/hashicorp/serf v0.10.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/etcd/api/v3 v3.5.14 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect
go.etcd.io/etcd/client/v2 v2.305.12 // indirect
go.etcd.io/etcd/client/v3 v3.5.14 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
exclude github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible

504
go.sum
View File

@ -1,177 +1,393 @@
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-20190419181538-ccf7df650fe4 h1:DrTAEU8rVfy2tRZObh8Hdjs819By7XfFhoOKh8xqX7Y=
github.com/abronan/valkeyrie v0.0.0-20190419181538-ccf7df650fe4/go.mod h1:NOvlKBjVll/vPwdjPHGLNhKk7VrnLzLGU/VGOVPLiog=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/aws/aws-sdk-go v1.16.23/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
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/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
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-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
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/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
github.com/charmbracelet/x/ansi v0.4.2 h1:0JM6Aj/g/KC154/gOP4vfxun0ff6itogDYk41kof+qk=
github.com/charmbracelet/x/ansi v0.4.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
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/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-acme/lego v2.5.0+incompatible h1:5fNN9yRQfv8ymH3DSsxla+4aYeQt2IgfZqHKVnK8f0s=
github.com/go-acme/lego v2.5.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/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-acme/lego/v4 v4.25.2 h1:+D1Q+VnZrD+WJdlkgUEGHFFTcDrwGlE7q24IFtMmHDI=
github.com/go-acme/lego/v4 v4.25.2/go.mod h1:OORYyVNZPaNdIdVYCGSBNRNZDIjhQbPuFxwGDgWj/yM=
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-zookeeper/zk v1.0.4 h1:DPzxraQx7OrPyXq2phlGlNSIyWEsAox0RJmjTseMV6I=
github.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
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/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
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/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
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/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/consul/api v1.28.2 h1:mXfkRHrpHN4YY3RqL09nXU1eHKLNiuAN4kHvDQ16k/8=
github.com/hashicorp/consul/api v1.28.2/go.mod h1:KyzqzgMEya+IZPcD65YFoOVAgPpbfERu4I/tzG6/ueE=
github.com/hashicorp/consul/sdk v0.16.0 h1:SE9m0W6DEfgIVCJX7xU+iv/hUl4m/nxqMTnCdMxDpJ8=
github.com/hashicorp/consul/sdk v0.16.0/go.mod h1:7pxqqhqoaPqnBnzXD1StKed62LqJeClzVsUEy85Zr0A=
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/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.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.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
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/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY=
github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI=
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0=
github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
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/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/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
github.com/hashicorp/memberlist v0.5.2 h1:rJoNPWZ0juJBgqn48gjy59K5H4rNgvUoM1kUD7bXiuI=
github.com/hashicorp/memberlist v0.5.2/go.mod h1:Ri9p/tRShbjYnpNf4FFPXG7wxEGY4Nrcn6E7jrVa//4=
github.com/hashicorp/serf v0.10.2 h1:m5IORhuNSjaxeljg5DeQVDlQyVkhRIjJDimbkCa8aAc=
github.com/hashicorp/serf v0.10.2/go.mod h1:T1CmSGfSeGfnfNy/w0odXQUR1rfECGd2Qdsp84DjOiY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kvtools/boltdb v1.0.2 h1:GZaUZ3AOw33mdK8r7p5bRqfhe504/BcwSVj6efQzLNs=
github.com/kvtools/boltdb v1.0.2/go.mod h1:ERqvkjiHCFTLy7WcHitvgpDH0y14pT3r+ZwkfqJ6tQM=
github.com/kvtools/consul v1.0.2 h1:ltPgs4Ld09Xaa7zrOJ/TewBYKAsr11/LRFpErdkb8AA=
github.com/kvtools/consul v1.0.2/go.mod h1:bFnzfGJ5ZIRRXCBGBmwhJlLdEWOlrjOcS1WjyAQzaJA=
github.com/kvtools/etcdv2 v1.0.2 h1:Bigfbm2f4uZveD0Av/5Vxs7MdqnWDVnAFpSF4QEL1c8=
github.com/kvtools/etcdv2 v1.0.2/go.mod h1:Ye5IwvG5KxUdcP14Yag6Pc3E9bzxy1j7zlvLK8eI/bU=
github.com/kvtools/etcdv3 v1.0.2 h1:EB0mAtzqe1folE7m7Q6wnCXcGwaOmrYmsVmF3hNsTKI=
github.com/kvtools/etcdv3 v1.0.2/go.mod h1:Xr6DbwqjuCEcXAIWmXxw0DX+N5BhuvablXgN90XeqMM=
github.com/kvtools/valkeyrie v1.0.0 h1:LAITop2wPoYCMitR24GZZsW0b57hmI+ePD18VRTtOf0=
github.com/kvtools/valkeyrie v1.0.0/go.mod h1:bDi/OdhJCSbGPMsCgUQl881yuEweKCSItAtTBI+ZjpU=
github.com/kvtools/zookeeper v1.0.2 h1:uK0CzQa+mtKGxDDH+DeqXo2HC1Kx4hWXZ7pX/zS4aTo=
github.com/kvtools/zookeeper v1.0.2/go.mod h1:6TfxUwJ7IuBk5srgnoe528W0ftanNECHgOiShx/t0Aw=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
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/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
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/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
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/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.2-0.20181022190402-e5e69e061d4f h1:E6ip3gLExd3v9o1iiZMMxOaC/XiWk3mPbDTOPLL0eWw=
github.com/ugorji/go v1.1.2-0.20181022190402-e5e69e061d4f/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go/codec v1.1.2-0.20181022190402-e5e69e061d4f h1:CZG9W9a8rpiPXPmkGcyXoD9sLF+JfLh/x+BpYHGhK+o=
github.com/ugorji/go/codec v1.1.2-0.20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
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/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
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-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0=
go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU=
go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ=
go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI=
go.etcd.io/etcd/client/v2 v2.305.12 h1:0m4ovXYo1CHaA/Mp3X/Fak5sRNIWf01wk/X1/G3sGKI=
go.etcd.io/etcd/client/v2 v2.305.12/go.mod h1:aQ/yhsxMu+Oht1FOupSr60oBvcS9cKXHrzBpDsPTf9E=
go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
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-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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/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=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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/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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -4,36 +4,37 @@ import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
// Exec Execute a command on a go routine.
func Exec(command string) {
func Exec(ctx context.Context, command string) {
if command == "" {
return
}
go func() {
errH := execute(command)
errH := execute(ctx, command)
if errH != nil {
panic(errH)
}
}()
}
func execute(command string) error {
ctxCmd, cancel := context.WithTimeout(context.Background(), 30*time.Second)
func execute(ctx context.Context, command string) error {
ctxCmd, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
parts := strings.Fields(command)
parts := strings.Fields(os.ExpandEnv(command))
output, err := exec.CommandContext(ctxCmd, parts[0], parts[1:]...).CombinedOutput()
if len(output) > 0 {
fmt.Println(string(output))
}
if ctxCmd.Err() == context.DeadlineExceeded {
if errors.Is(ctxCmd.Err(), context.DeadlineExceeded) {
return errors.New("hook timed out")
}

30
hook/hook_test.go Normal file
View File

@ -0,0 +1,30 @@
package hook
import (
"testing"
)
func Test_execute(t *testing.T) {
testCases := []struct {
desc string
command string
}{
{
desc: "expand env vars",
command: `echo "${GOPATH} ${GOARCH}"`,
},
{
desc: "simple",
command: `echo 'hello'`,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
err := execute(t.Context(), test.command)
if err != nil {
t.Fatal(err)
}
})
}
}

View File

@ -3,16 +3,17 @@ package main
import (
"bytes"
"compress/gzip"
"io/ioutil"
"context"
"log"
"os"
"path/filepath"
"time"
"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/kvtools/boltdb"
"github.com/kvtools/consul"
"github.com/kvtools/etcdv3"
"github.com/kvtools/valkeyrie"
"github.com/kvtools/zookeeper"
)
const storeKey = "traefik/acme/account/object"
@ -21,38 +22,43 @@ func main() {
log.SetFlags(log.Lshortfile)
source := "./acme.json"
err := loadData(source)
err := loadData(context.Background(), source)
if err != nil {
log.Fatal(err)
}
}
func loadData(source string) error {
func loadData(ctx context.Context, source string) error {
content, err := readFile(source)
if err != nil {
return err
}
// Consul
err = putData(store.CONSUL, []string{"localhost:8500"}, content)
err = putData(ctx, consul.StoreName, []string{"localhost:8500"},
&consul.Config{ConnectionTimeout: 3 * time.Second}, content)
if err != nil {
return err
}
// ETCD v3
err = putData(store.ETCDV3, []string{"localhost:2379"}, content)
err = putData(ctx, etcdv3.StoreName, []string{"localhost:2379"},
&etcdv3.Config{ConnectionTimeout: 3 * time.Second}, content)
if err != nil {
return err
}
// Zookeeper
err = putData(store.ZK, []string{"localhost:2181"}, content)
err = putData(ctx, zookeeper.StoreName, []string{"localhost:2181"},
&zookeeper.Config{ConnectionTimeout: 3 * time.Second}, content)
if err != nil {
return err
}
// BoltDB
err = putData(store.BOLTDB, []string{"/tmp/test-traefik-certs-dumper.db"}, content)
err = putData(ctx, boltdb.StoreName, []string{"/tmp/test-traefik-certs-dumper.db"},
&boltdb.Config{ConnectionTimeout: 3 * time.Second, Bucket: "traefik"}, content)
if err != nil {
return err
}
@ -60,29 +66,13 @@ func loadData(source string) error {
return nil
}
func putData(backend store.Backend, addrs []string, content []byte) error {
storeConfig := &store.Config{
ConnectionTimeout: 3 * time.Second,
Bucket: "traefik",
}
switch backend {
case store.CONSUL:
consul.Register()
case store.ETCDV3:
etcdv3.Register()
case store.ZK:
zookeeper.Register()
case store.BOLTDB:
boltdb.Register()
}
kvStore, err := valkeyrie.NewStore(backend, addrs, storeConfig)
func putData(ctx context.Context, backend string, addrs []string, options valkeyrie.Config, content []byte) error {
kvStore, err := valkeyrie.NewStore(ctx, backend, addrs, options)
if err != nil {
return err
}
if err := kvStore.Put(storeKey, content, nil); err != nil {
if err := kvStore.Put(ctx, storeKey, content, nil); err != nil {
return err
}
@ -91,7 +81,7 @@ func putData(backend store.Backend, addrs []string, content []byte) error {
}
func readFile(source string) ([]byte, error) {
content, err := ioutil.ReadFile(source)
content, err := os.ReadFile(filepath.Clean(source))
if err != nil {
return nil, err
}

View File

@ -1,255 +0,0 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"strings"
"text/template"
"github.com/docker/distribution/manifest/manifestlist"
)
type buildOption struct {
OS string
GoARM string
GoARCH string
Variant string
}
type actions struct {
Builds [][]string
Push [][]string
ManifestAnnotate [][]string
ManifestCreate []string
ManifestPush []string
}
func main() {
log.SetFlags(log.Lshortfile)
imageName := flag.String("image-name", "ldez/traefik-certs-dumper", "")
version := flag.String("version", "", "")
baseImageName := flag.String("base-image-name", "alpine:3.9", "")
dryRun := flag.Bool("dry-run", true, "")
flag.Parse()
require("image-name", imageName)
require("version", version)
require("base-image-name", baseImageName)
// FIXME
// _, travisTag := os.LookupEnv("TRAVIS_TAG")
// if !travisTag {
// log.Println("Skipping deploy")
// os.Exit(0)
// }
targets := []string{"arm.v6", "arm.v7", "arm.v8", "amd64", "386"}
actions, err := buildActions(*imageName, *version, *baseImageName, targets)
if err != nil {
log.Fatal(err)
}
err = execute(actions, *dryRun)
if err != nil {
log.Fatal(err)
}
}
func require(fieldName string, field *string) {
if field == nil || *field == "" {
log.Fatalf("%s is required", fieldName)
}
}
func buildActions(imageName, version, baseImageName string, targets []string) (actions, error) {
manifest, err := getManifest(baseImageName)
if err != nil {
return actions{}, err
}
buildOptions := map[string]buildOption{
"arm.v5": {OS: "linux", GoARM: "5", GoARCH: "arm", Variant: "v5"},
"arm.v6": {OS: "linux", GoARM: "6", GoARCH: "arm", Variant: "v6"},
"arm.v7": {OS: "linux", GoARM: "7", GoARCH: "arm", Variant: "v7"},
"arm.v8": {OS: "linux", GoARCH: "arm64", Variant: "v8"},
"amd64": {OS: "linux", GoARCH: "amd64"},
"386": {OS: "linux", GoARCH: "386"},
}
actions := actions{}
for _, target := range targets {
buildOption := buildOptions[target]
descriptor, err := findManifestDescriptor(buildOption, manifest.Manifests)
if err != nil {
log.Fatal(err)
}
dockerfile := fmt.Sprintf("%s-%s-%s.Dockerfile", buildOption.OS, buildOption.GoARCH, buildOption.GoARM)
actions.Builds = append(actions.Builds, []string{
"build",
"-t", fmt.Sprintf("%s:%s-%s", imageName, version, target),
"-f", dockerfile,
".",
})
err = createDockerfile(dockerfile, buildOption, descriptor, baseImageName)
if err != nil {
log.Fatal(err)
}
actions.Push = append(actions.Push, []string{"push", fmt.Sprintf(`%s:%s-%s`, imageName, version, target)})
ma := []string{
"manifest", "annotate",
fmt.Sprintf(`"%s:%s"`, imageName, version),
fmt.Sprintf(`"%s:%s-%s"`, imageName, version, target),
fmt.Sprintf(`--os="%s"`, buildOption.OS),
fmt.Sprintf(`--arch="%s"`, buildOption.GoARCH),
}
if buildOption.Variant != "" {
ma = append(ma, fmt.Sprintf(`--variant="%s"`, buildOption.Variant))
}
actions.ManifestAnnotate = append(actions.ManifestAnnotate, ma)
}
actions.ManifestCreate = []string{
"manifest", "create", "--amend",
fmt.Sprintf("%s:%s", imageName, version),
}
for _, target := range targets {
actions.ManifestCreate = append(actions.ManifestCreate, fmt.Sprintf(`"%s:%s-%s"`, imageName, version, target))
}
actions.ManifestPush = []string{
"manifest", "push", fmt.Sprintf("%s:%s", imageName, version),
}
return actions, nil
}
func createDockerfile(dockerfile string, buildOption buildOption, descriptor manifestlist.ManifestDescriptor, baseImageName string) error {
base := template.New("tmpl.Dockerfile")
parse, err := base.ParseFiles("./internal/tmpl.Dockerfile")
if err != nil {
return err
}
data := map[string]interface{}{
"GoOS": buildOption.OS,
"GoARCH": buildOption.GoARCH,
"GoARM": buildOption.GoARM,
"RuntimeImage": fmt.Sprintf("%s@%s", baseImageName, descriptor.Digest),
}
file, err := os.Create(dockerfile)
if err != nil {
return err
}
return parse.Execute(file, data)
}
func getManifest(baseImageName string) (*manifestlist.ManifestList, error) {
manifestPath := "./manifest.json"
if _, errExist := os.Stat(manifestPath); os.IsNotExist(errExist) {
cmd := exec.Command("docker", "manifest", "inspect", baseImageName)
cmd.Env = append(cmd.Env, "DOCKER_CLI_EXPERIMENTAL=enabled")
output, err := cmd.CombinedOutput()
if err != nil {
return nil, err
}
err = ioutil.WriteFile(manifestPath, output, 777)
if err != nil {
return nil, err
}
} else if errExist != nil {
return nil, errExist
}
bytes, err := ioutil.ReadFile(manifestPath)
if err != nil {
return nil, err
}
manifest := &manifestlist.ManifestList{}
err = json.Unmarshal(bytes, manifest)
if err != nil {
return nil, err
}
return manifest, nil
}
func findManifestDescriptor(criterion buildOption, descriptors []manifestlist.ManifestDescriptor) (manifestlist.ManifestDescriptor, error) {
for _, descriptor := range descriptors {
if descriptor.Platform.OS == criterion.OS &&
descriptor.Platform.Architecture == criterion.GoARCH &&
descriptor.Platform.Variant == criterion.Variant {
return descriptor, nil
}
}
return manifestlist.ManifestDescriptor{}, fmt.Errorf("not supported: %v", criterion)
}
func execute(actions actions, dryRun bool) error {
for _, args := range actions.Builds {
if err := execDocker(args, dryRun); err != nil {
return err
}
}
return nil
// for _, args := range actions.Push {
// if err := execDocker(args, dryRun); err != nil {
// return err
// }
// }
//
// if err := execDocker(actions.ManifestCreate, dryRun); err != nil {
// return err
// }
//
// for _, args := range actions.ManifestAnnotate {
// if err := execDocker(args, dryRun); err != nil {
// return err
// }
// }
//
// return execDocker(actions.ManifestPush, dryRun)
}
func execDocker(args []string, dryRun bool) error {
if dryRun {
fmt.Println("docker", strings.Join(args, " "))
return nil
}
cmd := exec.Command("docker", args...)
cmd.Env = append(cmd.Env, "DOCKER_CLI_EXPERIMENTAL=enabled")
output, err := cmd.CombinedOutput()
log.Println(string(output))
if err != nil {
return err
}
return nil
}

View File

@ -1,24 +0,0 @@
FROM golang:1-alpine as builder
RUN apk --update upgrade \
&& apk --no-cache --no-progress add git make gcc musl-dev ca-certificates tzdata
WORKDIR /go/src/github.com/ldez/traefik-certs-dumper
ENV GO111MODULE on
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN GOARCH={{ .GoARCH }} GOARM={{ .GoARM }} make build
FROM {{ .RuntimeImage }}
# Not supported for multi-arch without Buildkit or QEMU
#RUN apk --update upgrade \
# && apk --no-cache --no-progress add ca-certificates
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /go/src/github.com/ldez/traefik-certs-dumper/traefik-certs-dumper /usr/bin/traefik-certs-dumper
ENTRYPOINT ["/usr/bin/traefik-certs-dumper"]

View File

@ -1,11 +1,11 @@
package dumper
package traefikv1
import (
"github.com/go-acme/lego/certcrypto"
"github.com/go-acme/lego/registration"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/registration"
)
// StoredData represents the data managed by the Store
// StoredData represents the data managed by the Store.
type StoredData struct {
Account *Account
Certificates []*Certificate
@ -13,20 +13,20 @@ type StoredData struct {
TLSChallenges map[string]*Certificate
}
// Certificate is a struct which contains all data needed from an ACME 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
// Domain holds a domain name with SANs.
type Domain struct {
Main string
SANs []string
}
// Account is used to store lets encrypt registration info
// Account is used to store lets encrypt registration info.
type Account struct {
Email string
Registration *registration.Resource

101
internal/traefikv2/acme.go Normal file
View File

@ -0,0 +1,101 @@
package traefikv2
import (
"crypto"
"crypto/x509"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/registration"
)
// StoredData represents the data managed by Store.
type StoredData struct {
Account *Account
Certificates []*CertAndStore
}
// Account is used to store lets encrypt registration info.
type Account struct {
Email string
Registration *registration.Resource
PrivateKey []byte
KeyType certcrypto.KeyType
}
// GetEmail returns email.
func (a *Account) GetEmail() string {
return a.Email
}
// GetRegistration returns lets encrypt registration resource.
func (a *Account) GetRegistration() *registration.Resource {
return a.Registration
}
// GetPrivateKey returns private key.
func (a *Account) GetPrivateKey() crypto.PrivateKey {
privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey)
if err != nil {
return nil
}
return privateKey
}
// CertAndStore allows mapping a TLS certificate to a TLS store.
type CertAndStore struct {
Certificate
Store string
}
// Certificate is a struct which contains all data needed from an ACME certificate.
type Certificate struct {
Domain Domain `json:"domain,omitempty" toml:"domain,omitempty" yaml:"domain,omitempty"`
Certificate []byte `json:"certificate,omitempty" toml:"certificate,omitempty" yaml:"certificate,omitempty"`
Key []byte `json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"`
}
// Domain holds a domain name with SANs.
type Domain struct {
// Main defines the main domain name.
Main string `description:"Default subject name." json:"main,omitempty" toml:"main,omitempty" yaml:"main,omitempty"`
// SANs defines the subject alternative domain names.
SANs []string `description:"Subject alternative names." json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty"`
}
// ToStrArray convert a domain into an array of strings.
func (d *Domain) ToStrArray() []string {
var domains []string
if d.Main != "" {
domains = []string{d.Main}
}
return append(domains, d.SANs...)
}
// Set sets a domains from an array of strings.
func (d *Domain) Set(domains []string) {
if len(domains) > 0 {
d.Main = domains[0]
d.SANs = domains[1:]
}
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (d *Domain) DeepCopyInto(out *Domain) {
*out = *d
if d.SANs != nil {
in, out := &d.SANs, &out.SANs
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Domain.
func (d *Domain) DeepCopy() *Domain {
if d == nil {
return nil
}
out := new(Domain)
d.DeepCopyInto(out)
return out
}

101
internal/traefikv3/acme.go Normal file
View File

@ -0,0 +1,101 @@
package traefikv3
import (
"crypto"
"crypto/x509"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/registration"
)
// StoredData represents the data managed by Store.
type StoredData struct {
Account *Account
Certificates []*CertAndStore
}
// Account is used to store lets encrypt registration info.
type Account struct {
Email string
Registration *registration.Resource
PrivateKey []byte
KeyType certcrypto.KeyType
}
// GetEmail returns email.
func (a *Account) GetEmail() string {
return a.Email
}
// GetRegistration returns lets encrypt registration resource.
func (a *Account) GetRegistration() *registration.Resource {
return a.Registration
}
// GetPrivateKey returns private key.
func (a *Account) GetPrivateKey() crypto.PrivateKey {
privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey)
if err != nil {
return nil
}
return privateKey
}
// CertAndStore allows mapping a TLS certificate to a TLS store.
type CertAndStore struct {
Certificate
Store string
}
// Certificate is a struct which contains all data needed from an ACME certificate.
type Certificate struct {
Domain Domain `json:"domain,omitempty" toml:"domain,omitempty" yaml:"domain,omitempty"`
Certificate []byte `json:"certificate,omitempty" toml:"certificate,omitempty" yaml:"certificate,omitempty"`
Key []byte `json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"`
}
// Domain holds a domain name with SANs.
type Domain struct {
// Main defines the main domain name.
Main string `description:"Default subject name." json:"main,omitempty" toml:"main,omitempty" yaml:"main,omitempty"`
// SANs defines the subject alternative domain names.
SANs []string `description:"Subject alternative names." json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty"`
}
// ToStrArray convert a domain into an array of strings.
func (d *Domain) ToStrArray() []string {
var domains []string
if d.Main != "" {
domains = []string{d.Main}
}
return append(domains, d.SANs...)
}
// Set sets a domains from an array of strings.
func (d *Domain) Set(domains []string) {
if len(domains) > 0 {
d.Main = domains[0]
d.SANs = domains[1:]
}
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (d *Domain) DeepCopyInto(out *Domain) {
*out = *d
if d.SANs != nil {
in, out := &d.SANs, &out.SANs
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Domain.
func (d *Domain) DeepCopy() *Domain {
if d == nil {
return nil
}
out := new(Domain)
d.DeepCopyInto(out)
return out
}

View File

@ -1,13 +1,13 @@
# traefik-certs-dumper
[![GitHub release](https://img.shields.io/github/release/ldez/traefik-certs-dumper.svg)](https://github.com/ldez/traefik-certs-dumper/releases/latest)
[![Build Status](https://travis-ci.org/ldez/traefik-certs-dumper.svg?branch=master)](https://travis-ci.org/ldez/traefik-certs-dumper)
[![Docker Information](https://images.microbadger.com/badges/image/ldez/traefik-certs-dumper.svg)](https://hub.docker.com/r/ldez/traefik-certs-dumper/)
[![Build Status](https://github.com/ldez/traefik-certs-dumper/workflows/Main/badge.svg?branch=master)](https://github.com/ldez/traefik-certs-dumper/actions)
[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/ldez/traefik-certs-dumper)](https://hub.docker.com/r/ldez/traefik-certs-dumper/)
[![Go Report Card](https://goreportcard.com/badge/github.com/ldez/traefik-certs-dumper)](https://goreportcard.com/report/github.com/ldez/traefik-certs-dumper)
If you appreciate this project:
[![Say Thanks!](https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg?style=for-the-badge)](https://saythanks.io/to/ldez)
[![Sponsor](https://img.shields.io/badge/Sponsor%20me-%E2%9D%A4%EF%B8%8F-pink)](https://github.com/sponsors/ldez)
## Features
@ -18,16 +18,17 @@ If you appreciate this project:
- from file ("acme.json")
- from KV stores (Consul, Etcd, Zookeeper)
- Output formats:
- use domain as sub-directory (allow custom names and extensions)
- use domain as subdirectory (allow custom names and extensions)
- flat (domain as filename)
- Hook (only with watch mode and if the data source changes)
- Support Traefik v1, v2, and v3.
## Installation
### Download / CI Integration
```bash
curl -sfL https://raw.githubusercontent.com/ldez/traefik-certs-dumper/master/godownloader.sh | bash -s -- -b $GOPATH/bin v1.5.0
curl -sfL https://raw.githubusercontent.com/ldez/traefik-certs-dumper/master/godownloader.sh | bash -s -- -b $(go env GOPATH)/bin v2.9.3
```
<!--
@ -56,6 +57,12 @@ You can use pre-compiled binaries:
docker run ldez/traefik-certs-dumper:<tag_name>
```
Examples:
- Traefik v1: [docker-compose](docs/docker-compose-traefik-v1.yml)
- Traefik v2: [docker-compose](docs/docker-compose-traefik-v2.yml)
- Traefik v3: TODO
## Usage
- [traefik-certs-dumper](docs/traefik-certs-dumper.md)
@ -67,7 +74,7 @@ docker run ldez/traefik-certs-dumper:<tag_name>
### Simple Dump
```console
$ traefik-certs-dumper file
$ traefik-certs-dumper file --version v3
dump
├──certs
│ └──my.domain.com.key
@ -79,7 +86,7 @@ dump
### Change source and destination
```console
$ traefik-certs-dumper file --source ./acme.json --dest ./dump/test
$ traefik-certs-dumper file --version v3 --source ./acme.json --dest ./dump/test
test
├──certs
│ └──my.domain.com.key
@ -91,7 +98,7 @@ test
### Use domain as sub-directory
```console
$ traefik-certs-dumper file --domain-subdir=true
$ traefik-certs-dumper file --version v3 --domain-subdir=true
dump
├──my.domain.com
│ ├──certificate.crt
@ -103,7 +110,7 @@ dump
#### Change file extension
```console
$ traefik-certs-dumper file --domain-subdir --crt-ext=.pem --key-ext=.pem
$ traefik-certs-dumper file --version v3 --domain-subdir --crt-ext=.pem --key-ext=.pem
dump
├──my.domain.com
│ ├──certificate.pem
@ -115,7 +122,7 @@ dump
#### Change file name
```console
$ traefik-certs-dumper file --domain-subdir --crt-name=fullchain --key-name=privkey
$ traefik-certs-dumper file --version v3 --domain-subdir --crt-name=fullchain --key-name=privkey
dump
├──my.domain.com
│ ├──fullchain.crt
@ -124,6 +131,37 @@ dump
└──letsencrypt.key
```
## Hook
Hook can be a one-liner passed as a string, or a file for more complex post-hook scenarios.
For the former, create a file (ex: `hook.sh`) and mount it, then pass `sh hooksh` as a parameter to `--post-hook`.
Here is a docker-compose example:
```yml
services:
# ...
traefik-certs-dumper:
image: ldez/traefik-certs-dumper:v2.9.3
container_name: traefik-certs-dumper
entrypoint: sh -c '
while ! [ -e /data/acme.json ]
|| ! [ `jq ".[] | .Certificates | length" /data/acme.json | jq -s "add" ` != 0 ]; do
sleep 1
; done
&& traefik-certs-dumper file --version v2 --watch
--source /data/acme.json --dest /data/certs
--post-hook "sh /hook.sh"'
labels:
traefik.enable: false
volumes:
- ./letsencrypt:/data
- ./hook.sh:/hook.sh
# ...
```
### KV store
#### Consul
@ -149,5 +187,3 @@ $ traefik-certs-dumper kv boltdb --endpoints /the/path/to/mydb.db
```console
$ traefik-certs-dumper kv zookeeper --endpoints localhost:2181
```