From 8ed9113bb207331e41b8dd728c6e0a6f74766a1f Mon Sep 17 00:00:00 2001 From: ministicraft Date: Tue, 4 Jun 2019 22:58:42 +0200 Subject: [PATCH] update repo --- admin_stack.yml => admin/docker-compose.yml | 0 beamium/monitoring.yml | 2 +- beets/Dockerfile | 3 + gitea_stack.yml => gitea/docker-compose.yml | 27 +- guacamole/docker-compose.yml | 76 ++++ heimdall/Dockerfile | 2 + heimdall/docker-compose.yml | 98 ++++ keycloack/docker-compose.yml | 92 ++++ monitoring/docker-compose.yml | 112 +++++ monitoring_stack.yml | 61 --- .../docker-compose.yml | 42 +- phantombot/docker-compose.yml | 52 +++ plex/docker-compose.yml | 78 ++++ plex/plex-automation/docker-compose.yml | 386 ++++++++++++++++ plex/plex/docker-compose.yml | 40 ++ .../docker-compose.yml | 4 + .../docker-compose.yml | 16 +- traefik/traefik-certs-dumper/.dockerignore | 10 + traefik/traefik-certs-dumper/.gitignore | 10 + traefik/traefik-certs-dumper/.golangci.toml | 41 ++ traefik/traefik-certs-dumper/.goreleaser.yml | 49 ++ traefik/traefik-certs-dumper/.travis.yml | 51 +++ traefik/traefik-certs-dumper/Dockerfile | 21 + traefik/traefik-certs-dumper/LICENSE | 13 + traefik/traefik-certs-dumper/Makefile | 25 ++ traefik/traefik-certs-dumper/cmd/boltdb.go | 39 ++ traefik/traefik-certs-dumper/cmd/consul.go | 37 ++ traefik/traefik-certs-dumper/cmd/doc.go | 20 + traefik/traefik-certs-dumper/cmd/etcd.go | 43 ++ traefik/traefik-certs-dumper/cmd/file.go | 25 ++ traefik/traefik-certs-dumper/cmd/kv.go | 174 ++++++++ traefik/traefik-certs-dumper/cmd/root.go | 180 ++++++++ traefik/traefik-certs-dumper/cmd/version.go | 38 ++ traefik/traefik-certs-dumper/cmd/zookeeper.go | 33 ++ .../docs/traefik-certs-dumper.md | 31 ++ .../docs/traefik-certs-dumper_file.md | 39 ++ .../docs/traefik-certs-dumper_kv.md | 49 ++ .../docs/traefik-certs-dumper_kv_boltdb.md | 51 +++ .../docs/traefik-certs-dumper_kv_consul.md | 50 +++ .../docs/traefik-certs-dumper_kv_etcd.md | 50 +++ .../docs/traefik-certs-dumper_kv_zookeeper.md | 49 ++ .../docs/traefik-certs-dumper_version.md | 38 ++ traefik/traefik-certs-dumper/dumper/config.go | 12 + traefik/traefik-certs-dumper/dumper/dumper.go | 129 ++++++ .../traefik-certs-dumper/dumper/file/file.go | 167 +++++++ .../traefik-certs-dumper/dumper/filename.go | 7 + .../dumper/filename_windows.go | 9 + traefik/traefik-certs-dumper/dumper/info.go | 35 ++ .../traefik-certs-dumper/dumper/kv/config.go | 11 + .../traefik-certs-dumper/dumper/kv/convert.go | 65 +++ traefik/traefik-certs-dumper/dumper/kv/kv.go | 99 +++++ traefik/traefik-certs-dumper/go.mod | 51 +++ traefik/traefik-certs-dumper/go.sum | 177 ++++++++ traefik/traefik-certs-dumper/godownloader.sh | 420 ++++++++++++++++++ traefik/traefik-certs-dumper/hook/hook.go | 42 ++ .../traefik-certs-dumper/hook/hook_test.go | 29 ++ .../integrationtest/docker-compose.yml | 18 + .../integrationtest/loader.go | 121 +++++ .../integrationtest/readme.md | 54 +++ traefik/traefik-certs-dumper/main.go | 12 + traefik/traefik-certs-dumper/readme.md | 153 +++++++ traefik/traefik-certs-dumper/tmpl.Dockerfile | 24 + 62 files changed, 3810 insertions(+), 82 deletions(-) rename admin_stack.yml => admin/docker-compose.yml (100%) create mode 100644 beets/Dockerfile rename gitea_stack.yml => gitea/docker-compose.yml (68%) create mode 100644 guacamole/docker-compose.yml create mode 100644 heimdall/Dockerfile create mode 100644 heimdall/docker-compose.yml create mode 100644 keycloack/docker-compose.yml create mode 100644 monitoring/docker-compose.yml delete mode 100644 monitoring_stack.yml rename nextcloud_stack.yml => nextcloud/docker-compose.yml (51%) create mode 100644 phantombot/docker-compose.yml create mode 100644 plex/docker-compose.yml create mode 100644 plex/plex-automation/docker-compose.yml create mode 100644 plex/plex/docker-compose.yml rename portainer_stack.yml => portainer/docker-compose.yml (86%) rename traefik_stack.yml => traefik/docker-compose.yml (78%) create mode 100644 traefik/traefik-certs-dumper/.dockerignore create mode 100644 traefik/traefik-certs-dumper/.gitignore create mode 100644 traefik/traefik-certs-dumper/.golangci.toml create mode 100644 traefik/traefik-certs-dumper/.goreleaser.yml create mode 100644 traefik/traefik-certs-dumper/.travis.yml create mode 100644 traefik/traefik-certs-dumper/Dockerfile create mode 100644 traefik/traefik-certs-dumper/LICENSE create mode 100644 traefik/traefik-certs-dumper/Makefile create mode 100644 traefik/traefik-certs-dumper/cmd/boltdb.go create mode 100644 traefik/traefik-certs-dumper/cmd/consul.go create mode 100644 traefik/traefik-certs-dumper/cmd/doc.go create mode 100644 traefik/traefik-certs-dumper/cmd/etcd.go create mode 100644 traefik/traefik-certs-dumper/cmd/file.go create mode 100644 traefik/traefik-certs-dumper/cmd/kv.go create mode 100644 traefik/traefik-certs-dumper/cmd/root.go create mode 100644 traefik/traefik-certs-dumper/cmd/version.go create mode 100644 traefik/traefik-certs-dumper/cmd/zookeeper.go create mode 100644 traefik/traefik-certs-dumper/docs/traefik-certs-dumper.md create mode 100644 traefik/traefik-certs-dumper/docs/traefik-certs-dumper_file.md create mode 100644 traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv.md create mode 100644 traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_boltdb.md create mode 100644 traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_consul.md create mode 100644 traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_etcd.md create mode 100644 traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_zookeeper.md create mode 100644 traefik/traefik-certs-dumper/docs/traefik-certs-dumper_version.md create mode 100644 traefik/traefik-certs-dumper/dumper/config.go create mode 100644 traefik/traefik-certs-dumper/dumper/dumper.go create mode 100644 traefik/traefik-certs-dumper/dumper/file/file.go create mode 100644 traefik/traefik-certs-dumper/dumper/filename.go create mode 100644 traefik/traefik-certs-dumper/dumper/filename_windows.go create mode 100644 traefik/traefik-certs-dumper/dumper/info.go create mode 100644 traefik/traefik-certs-dumper/dumper/kv/config.go create mode 100644 traefik/traefik-certs-dumper/dumper/kv/convert.go create mode 100644 traefik/traefik-certs-dumper/dumper/kv/kv.go create mode 100644 traefik/traefik-certs-dumper/go.mod create mode 100644 traefik/traefik-certs-dumper/go.sum create mode 100644 traefik/traefik-certs-dumper/godownloader.sh create mode 100644 traefik/traefik-certs-dumper/hook/hook.go create mode 100644 traefik/traefik-certs-dumper/hook/hook_test.go create mode 100644 traefik/traefik-certs-dumper/integrationtest/docker-compose.yml create mode 100644 traefik/traefik-certs-dumper/integrationtest/loader.go create mode 100644 traefik/traefik-certs-dumper/integrationtest/readme.md create mode 100644 traefik/traefik-certs-dumper/main.go create mode 100644 traefik/traefik-certs-dumper/readme.md create mode 100644 traefik/traefik-certs-dumper/tmpl.Dockerfile diff --git a/admin_stack.yml b/admin/docker-compose.yml similarity index 100% rename from admin_stack.yml rename to admin/docker-compose.yml diff --git a/beamium/monitoring.yml b/beamium/monitoring.yml index 0da8dd9..c6e0a38 100644 --- a/beamium/monitoring.yml +++ b/beamium/monitoring.yml @@ -6,4 +6,4 @@ scrapers: sinks: sink1: url: http://tasks.warp10:8080/api/v0/update - token: WARP10_TOKEN + token: token diff --git a/beets/Dockerfile b/beets/Dockerfile new file mode 100644 index 0000000..6f79b24 --- /dev/null +++ b/beets/Dockerfile @@ -0,0 +1,3 @@ +FROM linuxserver/beets +RUN pip install --upgrade pip +RUN pip install --upgrade https://github.com/beetbox/audioread/archive/master.zip diff --git a/gitea_stack.yml b/gitea/docker-compose.yml similarity index 68% rename from gitea_stack.yml rename to gitea/docker-compose.yml index d96a2ee..05e3638 100644 --- a/gitea_stack.yml +++ b/gitea/docker-compose.yml @@ -11,14 +11,21 @@ services: - PGID=1000 - DB_TYPE=postgres - DB_HOST=db:5432 - - DB_NAME=xxx - - DB_USER=xxx - - DB_PASSWD=xxx + - DB_NAME=gitea + - DB_USER=user + - DB_PASSWD=pwd volumes: - gitea_data:/data ports: - "10022:22" deploy: + resources: + limits: + cpus: '0.5' + memory: 350M + reservations: + cpus: '0.1' + memory: 100M labels: - "traefik.port=3000" - "traefik.frontend.rule=Host:git.cloud.arnaud-pc.fr" @@ -34,11 +41,19 @@ services: db: image: postgres:latest environment: - - POSTGRES_USER=xxx - - POSTGRES_PASSWORD=xxx - - POSTGRES_DB=xxx + - POSTGRES_USER=user + - POSTGRES_PASSWORD=pwd + - POSTGRES_DB=gitea volumes: - postgres_gitea_data:/var/lib/postgresql/data + deploy: + resources: + limits: + cpus: '0.5' + memory: 200M + reservations: + cpus: '0.1' + memory: 50M networks: - gitea diff --git a/guacamole/docker-compose.yml b/guacamole/docker-compose.yml new file mode 100644 index 0000000..7f6cb10 --- /dev/null +++ b/guacamole/docker-compose.yml @@ -0,0 +1,76 @@ +version: '3' + +services: + web: + image: guacamole/guacamole:latest + depends_on: + - db + - guacd + environment: + - POSTGRES_HOSTNAME=db + - POSTGRES_DATABASE=guacamole_db + - POSTGRES_USER=user + - POSTGRES_PASSWORD=pwd + - GUACD_HOSTNAME=guacd + deploy: + resources: + limits: + cpus: '0.5' + memory: 1G + reservations: + cpus: '0.25' + memory: 500M + labels: + - "traefik.port=8080" + - "traefik.frontend.rule=Host:guacamole.admin.arnaud-pc.fr;PathPrefix:/guacamole" + - "traefik.docker.network=dmz" + - "traefik.passHostHeader=true" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=true" + networks: + - dmz + - guacamole + + guacd: + image: guacamole/guacd:latest + deploy: + resources: + limits: + cpus: '0.5' + memory: 200M + reservations: + cpus: '0.1' + memory: 50M + networks: + - guacamole + + db: + image: postgres:latest + environment: + - POSTGRES_USER=user + - POSTGRES_PASSWORD=pwd + - POSTGRES_DB=guacamole_db + volumes: + - postgres_data:/var/lib/postgresql/data + deploy: + resources: + limits: + cpus: '0.5' + memory: 200M + reservations: + cpus: '0.1' + memory: 50M + networks: + - guacamole + +networks: + guacamole: + driver: overlay + dmz: + external: + name: dmz + +volumes: + postgres_data: + driver: convoy diff --git a/heimdall/Dockerfile b/heimdall/Dockerfile new file mode 100644 index 0000000..c3db0a1 --- /dev/null +++ b/heimdall/Dockerfile @@ -0,0 +1,2 @@ +FROM linuxserver/heimdall +RUN apk add php7-pdo php7-pdo_pgsql diff --git a/heimdall/docker-compose.yml b/heimdall/docker-compose.yml new file mode 100644 index 0000000..51405d6 --- /dev/null +++ b/heimdall/docker-compose.yml @@ -0,0 +1,98 @@ +version: '3' + +services: + web: + image: ministicraft/heimdall + depends_on: + - db + environment: + - TZ=Europe/Paris + - PUID=1000 + - PGID=1000 + - DB_CONNECTION=pgsql + - DB_HOST=db + - DB_DATABASE=heimdall + - DB_USERNAME=user + - DB_PASSWORD=pwd + - APP_URL=https://www.arnaud-pc.fr + - FORCE_HTTPS=true + - APP_DEBUG=true + volumes: + - heimdall_config:/config + deploy: + resources: + limits: + cpus: '0.5' + memory: 200M + reservations: + cpus: '0.1' + memory: 50M + networks: + - dmz + - heimdall + + db: + image: postgres:latest + environment: + - POSTGRES_USER=user + - POSTGRES_PASSWORD=pwd + - POSTGRES_DB=heimdall + volumes: + - postgres_data:/var/lib/postgresql/data + deploy: + resources: + limits: + cpus: '0.5' + memory: 200M + reservations: + cpus: '0.1' + memory: 50M + networks: + - heimdall + + gatekeeper: + image: keycloak/keycloak-gatekeeper + networks: + - heimdall + - dmz + command: | + --discovery-url=https://auth.arnaud-pc.fr/auth/realms/cloud + --client-id=heimdall_proxy + --client-secret=secret + --listen=:3000 + --redirection-url=https://www.arnaud-pc.fr + --enable-refresh-tokens=true + --encryption-key=key + --upstream-url=https://web:443 + --enable-default-deny=true + --resources="uri=/*" + --skip-upstream-tls-verify=true + deploy: + resources: + limits: + cpus: '0.25' + memory: 50M + reservations: + cpus: '0.1' + memory: 10M + labels: + - "traefik.enable=true" + - "traefik.frontend.rule=Host:www.arnaud-pc.fr" + - "traefik.port=3000" + - "traefik.docker.network=dmz" + - "traefik.backend.loadbalancer.swarm=true" + +networks: + heimdall: + driver: overlay + dmz: + external: + name: dmz + +volumes: + heimdall_config: + driver: convoy + postgres_data: + driver: convoy + nginx: + driver: convoy diff --git a/keycloack/docker-compose.yml b/keycloack/docker-compose.yml new file mode 100644 index 0000000..7361c87 --- /dev/null +++ b/keycloack/docker-compose.yml @@ -0,0 +1,92 @@ +version: '3' + +services: + keycloack: + image: jboss/keycloak + depends_on: + - db + environment: + - TZ=Europe/Paris + - PUID=1000 + - PGID=1000 + - DB_VENDOR=postgres + - DB_ADDR=db + - DB_PORT=5432 + - DB_DATABASE=keycloack + - DB_USER=user + - DB_PASSWORD=pwd + - PROXY_ADDRESS_FORWARDING=true + volumes: + - config:/opt/jboss/keycloak/standalone/configuration/ + networks: + - dmz + - keycloack + deploy: + resources: + limits: + cpus: '0.5' + memory: 1G + reservations: + cpus: '0.1' + memory: 500M + labels: + - "traefik.enable=true" + - "traefik.frontend.rule=Host:auth.arnaud-pc.fr" + - "traefik.port=8080" + - "traefik.docker.network=dmz" + - "traefik.protocol=http" + - "traefik.backend.loadbalancer.swarm=true" + db: + image: postgres:latest + environment: + - POSTGRES_USER=user + - POSTGRES_PASSWORD=keycloack + - POSTGRES_DB=pwd + volumes: + - postgres_data:/var/lib/postgresql/data + deploy: + resources: + limits: + cpus: '0.5' + memory: 200M + reservations: + cpus: '0.1' + memory: 50M + networks: + - keycloack + + adminer: + image: adminer + networks: + - keycloack + - dmz + deploy: + mode: replicated + replicas: 0 + resources: + limits: + cpus: '0.5' + memory: 200M + reservations: + cpus: '0.1' + memory: 50M + labels: + - "traefik.enable=false" + - "traefik.frontend.rule=Host:auth.arnaud-pc.fr" + - "traefik.port=8080" + - "traefik.docker.network=dmz" + - "traefik.protocol=http" + - "traefik.backend.loadbalancer.swarm=true" + +networks: + keycloack: + driver: overlay + dmz: + external: + name: dmz + +volumes: + config: + driver: convoy + postgres_data: + driver: convoy diff --git a/monitoring/docker-compose.yml b/monitoring/docker-compose.yml new file mode 100644 index 0000000..088c854 --- /dev/null +++ b/monitoring/docker-compose.yml @@ -0,0 +1,112 @@ +version: '3.5' + +services: + beamium: + image: ministicraft/beamium + command: beamium -c /etc/beamium/scraper.yml + networks: + - monitoring + configs: + - source: beamium_monitoring_conf + target: /etc/beamium/scraper.yml + deploy: + mode: replicated + replicas: 0 + + cadvisor: + image: google/cadvisor + volumes: + - /var/run/docker.sock:/var/lib/docker.sock:ro + - /:/rootfs:ro + - /var/run:/var/run:ro + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + networks: + - monitoring + - dmz + deploy: + mode: replicated + replicas: 0 + labels: + - "traefik.quantum.frontend.rule=Host:warp10.monitoring.arnaud-pc.fr" + - "traefik.quantum.port=8080" + - "traefik.quantum.docker.network=dmz" + - "traefik.quantum.passHostHeader=true" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=true" + placement: + constraints: [node.platform.os == linux] + resources: + limits: + memory: 128M + + warp10: + image: warp10io/warp10 + volumes: + - /var/warp10:/data + networks: + - monitoring + - dmz + deploy: + mode: replicated + replicas: 0 + labels: + - "traefik.quantum.frontend.auth.basic.usersFile=/etc/traefik/.htpasswd" + - "traefik.quantum.frontend.rule=Host:warp10.monitoring.arnaud-pc.fr" + - "traefik.quantum.port=8081" + - "traefik.quantum.docker.network=dmz" + - "traefik.quantum.passHostHeader=true" + - "traefik.warp10.frontend.rule=Host:warp10.monitoring.arnaud-pc.fr;PathPrefix:/api" + - "traefik.warp10.port=8080" + - "traefik.warp10.docker.network=dmz" + - "traefik.frontend.headers.customRequestHeaders=accept-encoding: identity" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=false" + placement: + constraints: + - node.labels.nas == true + + grafana: + image: grafana/grafana + user: 1000:1000 + networks: + - monitoring + - dmz + volumes: + - grafana:/var/lib/grafana + deploy: + mode: replicated + replicas: 0 + placement: + constraints: + - node.role == manager + resources: + limits: + memory: 128M + reservations: + memory: 64M + labels: +# - "traefik.frontend.auth.basic.usersFile=/etc/traefik/.htpasswd" + - "traefik.frontend.rule=Host:grafana.monitoring.arnaud-pc.fr" + - "traefik.port=3000" + - "traefik.docker.network=dmz" + - "traefik.passHostHeader=true" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=false" + +networks: + monitoring: + driver: overlay + dmz: + external: true + +configs: + beamium_monitoring_conf: + file: ./beamium/monitoring.yml + +volumes: + grafana: + driver: convoy diff --git a/monitoring_stack.yml b/monitoring_stack.yml deleted file mode 100644 index 1c86de2..0000000 --- a/monitoring_stack.yml +++ /dev/null @@ -1,61 +0,0 @@ -version: '3.5' - -services: - beamium: - image: ministicraft/beamium - command: beamium -c /etc/beamium/scraper.yml - networks: - - monitoring - configs: - - source: beamium_monitoring_conf - target: /etc/beamium/scraper.yml - deploy: - mode: replicated - replicas: 1 - - cadvisor: - image: google/cadvisor - volumes: - - /var/run/docker.sock:/var/lib/docker.sock:ro - - /:/rootfs:ro - - /var/run:/var/run:ro - - /sys:/sys:ro - - /var/lib/docker/:/var/lib/docker:ro - networks: - - monitoring - deploy: - mode: global - placement: - constraints: [node.platform.os == linux] - - warp10: - image: warp10io/warp10 - volumes: - - /var/warp10:/data - networks: - - monitoring - - dmz - deploy: - mode: replicated - replicas: 1 - labels: - - "traefik.frontend.rule=Host:warp10.cloud.arnaud-pc.fr" - - "traefik.port=8081" - - "traefik.docker.network=dmz" - - "traefik.passHostHeader=true" - - "traefik.backend.loadbalancer.swarm=true" - - "traefik.backend.loadbalancer.method=drr" - - "traefik.enable=true" - placement: - constraints: - - node.labels.nas == true - -networks: - monitoring: - driver: overlay - dmz: - external: true - -configs: - beamium_monitoring_conf: - file: ./beamium/monitoring.yml diff --git a/nextcloud_stack.yml b/nextcloud/docker-compose.yml similarity index 51% rename from nextcloud_stack.yml rename to nextcloud/docker-compose.yml index 99c0d2a..90b09d6 100644 --- a/nextcloud_stack.yml +++ b/nextcloud/docker-compose.yml @@ -2,17 +2,22 @@ version: '3' services: web: - image: nextcloud + image: ministicraft/nextcloud:stable depends_on: - db environment: - - POSTGRES_DB=xxx - - POSTGRES_USER=xxx - - POSTGRES_PASSWORD=xxx - - POSTGRES_HOST=db + - "POSTGRES_DB=nextcloud" + - "POSTGRES_USER=user" + - "POSTGRES_PASSWORD=pwd" + - "POSTGRES_HOST=db" volumes: - - /storage/:/data/ - - nextcloud_data:/var/www/html/data/ + - /storage/plex/:/data/Plex + - /storage/download/:/data/download + - /storage/Upload/:/data/Upload + - /storage/Games/:/data/Games + - /storage/MusicLossy/:/data/Music_lossy + - /storage/syncthing/:/data/syncthing + - /storage/cloud/:/var/www/html/data/ - nextcloud_config:/var/www/html/config networks: - dmz @@ -22,12 +27,20 @@ services: replicas: 1 placement: constraints: [node.role == manager] - + resources: + limits: + cpus: '1.0' + memory: 1G + reservations: + cpus: '0.1' + memory: 100M labels: - "traefik.frontend.rule=Host:nextcloud.cloud.arnaud-pc.fr,cloud.arnaud-pc.fr" - "traefik.port=80" - "traefik.docker.network=dmz" - "traefik.passHostHeader=true" + - "traefik.frontend.headers.customRequestHeaders=X_FORWARDED_PROTO:https" + - "traefik.frontend.headers.SSLProxyHeaders=X_FORWARDED_PROTO:https" - "traefik.backend.loadbalancer.swarm=true" - "traefik.backend.loadbalancer.method=drr" - "traefik.enable=true" @@ -35,9 +48,9 @@ services: db: image: postgres:9.6 environment: - - POSTGRES_USER=xxx - - POSTGRES_PASSWORD=xxx - - POSTGRES_DB=xxx + - "POSTGRES_USER=user" + - "POSTGRES_PASSWORD=pwd" + - "POSTGRES_DB=nextcloud" volumes: - postgres_nextcloud_data:/var/lib/postgresql/data networks: @@ -45,6 +58,13 @@ services: deploy: mode: replicated replicas: 1 + resources: + limits: + cpus: '0.5' + memory: 500M + reservations: + cpus: '0.1' + memory: 100M networks: dmz: diff --git a/phantombot/docker-compose.yml b/phantombot/docker-compose.yml new file mode 100644 index 0000000..8bba999 --- /dev/null +++ b/phantombot/docker-compose.yml @@ -0,0 +1,52 @@ + version: '3' + + services: + phantombot: + image: ministicraft/phantombot:v3.0.0 + networks: + - dmz + ports: + - 25004:25004 + volumes: + - phantombot_addons:/opt/PhantomBot/addons + - phantombot_logs:/opt/PhantomBot/logs + - phantombot_conf:/opt/PhantomBot/config + environment: + - "PHANTOMBOT_USER=wreckstream" + - "PHANTOMBOT_OAUTH=oauth" + - "PHANTOMBOT_APIOAUTH=oauth" + - "PHANTOMBOT_CHANNEL=wreckstream" + - "PHANTOMBOT_PANELUSER=wreckstream" + - "PHANTOMBOT_PANELPASSWORD=wreckstream" + deploy: + mode: replicated + replicas: 1 + placement: + constraints: [node.role == manager] + resources: + limits: + cpus: '0.5' + memory: 250M + reservations: + cpus: '0.1' + memory: 100M + labels: + - "traefik.frontend.rule=Host:phantombot.arnaud-pc.fr" + - "traefik.port=25000" + - "traefik.docker.network=dmz" + - "traefik.passHostHeader=true" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=true" + + networks: + dmz: + external: true + + volumes: + phantombot_logs: + driver: convoy + phantombot_conf: + driver: convoy + phantombot_addons: + driver: convoy diff --git a/plex/docker-compose.yml b/plex/docker-compose.yml new file mode 100644 index 0000000..f596ba5 --- /dev/null +++ b/plex/docker-compose.yml @@ -0,0 +1,78 @@ +version: '3.4' + +services: + reverse_proxy_plex: + image: nginx + volumes: + - nginx_plex:/etc/nginx/ + - /rapidStorage/nginxCache:/cache + - ssl:/traefik/ + networks: + - plex + - dmz + deploy: + mode: replicated + replicas: 1 + placement: + constraints: [node.role == manager] + resources: + limits: + cpus: '0.5' + memory: 50M + reservations: + cpus: '0.1' + memory: 10M + labels: + - "traefik.frontend.rule=Host:plex.arnaud-pc.fr" + - "traefik.port=443" + - "traefik.protocol=https" + - "traefik.docker.network=dmz" + - "traefik.passHostHeader=true" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=true" + + tautulli: + image: linuxserver/tautulli + environment: + TZ: Europe/Paris + volumes: + - tautulli_logs:/logs + - tautulli_conf:/config + networks: + - dmz + deploy: + mode: replicated + replicas: 1 + resources: + limits: + cpus: '0.5' + memory: 500M + reservations: + cpus: '0.1' + memory: 200M + labels: + - "traefik.frontend.rule=Host:plex.arnaud-pc.fr;PathPrefix:/plexpy" + - "traefik.port=8181" + - "traefik.docker.network=dmz" + - "traefik.passHostHeader=true" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=true" + +networks: + dmz: + external: true + plex: + external: true + +volumes: + tautulli_logs: + driver: convoy + tautulli_conf: + driver: convoy + nginx_plex: + driver: convoy + ssl: + external: true + name: traefik_traefik_conf \ No newline at end of file diff --git a/plex/plex-automation/docker-compose.yml b/plex/plex-automation/docker-compose.yml new file mode 100644 index 0000000..d2397ef --- /dev/null +++ b/plex/plex-automation/docker-compose.yml @@ -0,0 +1,386 @@ +version: '3' + +services: + syncthing: + image: linuxserver/syncthing + environment: + - PUID=1000 + - PGID=1000 + - UMASK=<022> + ports: + - "22000:22000" + - "21027:21027/udp" + volumes: + - syncthing_conf:/config + - /storage/syncthing:/mnt/storage/syncthing + networks: + - plex + deploy: + mode: replicated + replicas: 1 + resources: + limits: + cpus: '1.0' + memory: 1G + reservations: + cpus: '0.1' + memory: 500M + + jackett: + image: linuxserver/jackett + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Paris + volumes: + - jackett_conf:/config + networks: + - plex + deploy: + mode: replicated + replicas: 1 + resources: + limits: + cpus: '0.5' + memory: 50M + reservations: + cpus: '0.1' + memory: 10M + + + lidarr: + image: linuxserver/lidarr + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Paris + volumes: + - lidarr_conf:/config + - /storage/plex/Musique:/music + - /storage/syncthing/torrents/musique:/torrents + networks: + - plex + deploy: + mode: replicated + replicas: 1 + resources: + limits: + cpus: '0.5' + memory: 3G + reservations: + cpus: '0.1' + memory: 1G + + radarr: + image: linuxserver/radarr + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Paris + volumes: + - radarr_conf:/config + - /storage/plex/Video/Movies:/movies + networks: + - plex + deploy: + mode: replicated + replicas: 1 + resources: + limits: + cpus: '0.5' + memory: 750M + reservations: + cpus: '0.1' + memory: 250M + labels: + - "traefik.enable=false" + - "traefik.frontend.rule=Host:radarr.media.arnaud-pc.fr" + - "traefik.port=7878" + - "traefik.docker.network=dmz" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + + sonarr-tv: + image: linuxserver/sonarr:preview + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Paris + volumes: + - sonarr-tv_conf:/config + - /storage/plex/Video/Tv Shows:/tv + - /storage/syncthing/torrents/tv:/torrents + networks: + - plex + deploy: + mode: replicated + replicas: 1 + resources: + limits: + cpus: '1.0' + memory: 1G + reservations: + cpus: '0.1' + memory: 500M + + sonarr-anime: + image: linuxserver/sonarr:preview + environment: + - PUID=1000 + - PGID=1000 + - TZ=Europe/Paris + volumes: + - sonarr-anime_conf:/config + - /storage/plex/Video/Anime:/anime + - /storage/syncthing/torrents/anime:/torrents + networks: + - plex + deploy: + mode: replicated + replicas: 1 + resources: + limits: + cpus: '1.0' + memory: 500M + reservations: + cpus: '0.1' + memory: 250M + + gatekeeper_sonarr-anime: + image: keycloak/keycloak-gatekeeper + networks: + - plex + - dmz + command: | + --discovery-url=https://auth.arnaud-pc.fr/auth/realms/cloud + --client-id=sonarr-anime_proxy + --client-secret=secret + --listen=:3000 + --redirection-url=https://sonarr-anime.media.arnaud-pc.fr + --enable-refresh-tokens=true + --encryption-key=key + --upstream-url=http://sonarr-anime:8989 + --enable-default-deny=true + --resources="uri=/*|roles=sonarr-anime" + deploy: + labels: + - "traefik.enable=true" + - "traefik.frontend.rule=Host:sonarr-anime.media.arnaud-pc.fr" + - "traefik.port=3000" + - "traefik.docker.network=dmz" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + + gatekeeper_sonarr-tv: + image: keycloak/keycloak-gatekeeper + networks: + - plex + - dmz + command: | + --discovery-url=https://auth.arnaud-pc.fr/auth/realms/cloud + --client-id=sonarr-tv_proxy + --client-secret=secret + --listen=:3000 + --redirection-url=https://sonarr-tv.media.arnaud-pc.fr + --enable-refresh-tokens=true + --encryption-key=key + --upstream-url=http://sonarr-tv:8989 + --enable-default-deny=true + --resources="uri=/*|roles=sonarr-tv" + --skip-upstream-tls-verify=true + deploy: + labels: + - "traefik.enable=true" + - "traefik.frontend.rule=Host:sonarr-tv.media.arnaud-pc.fr" + - "traefik.port=3000" + - "traefik.docker.network=dmz" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + + gatekeeper_radarr: + image: keycloak/keycloak-gatekeeper + networks: + - plex + - dmz + command: | + --discovery-url=https://auth.arnaud-pc.fr/auth/realms/cloud + --client-id=radarr_proxy + --client-secret=secret + --listen=:3000 + --redirection-url=https://radarr.media.arnaud-pc.fr + --enable-refresh-tokens=true + --encryption-key=key + --upstream-url=http://radarr:7878 + --enable-default-deny=true + --resources="uri=/*|roles=radarr" + --skip-upstream-tls-verify=true + deploy: + labels: + - "traefik.enable=true" + - "traefik.frontend.rule=Host:radarr.media.arnaud-pc.fr" + - "traefik.port=3000" + - "traefik.docker.network=dmz" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + + gatekeeper_lidarr: + image: keycloak/keycloak-gatekeeper + networks: + - plex + - dmz + command: | + --discovery-url=https://auth.arnaud-pc.fr/auth/realms/cloud + --client-id=lidarr_proxy + --client-secret=secret + --listen=:3000 + --redirection-url=https://lidarr.media.arnaud-pc.fr + --enable-refresh-tokens=true + --encryption-key=key + --upstream-url=http://lidarr:8686 + --enable-default-deny=true + --resources="uri=/*|roles=lidarr" + --skip-upstream-tls-verify=true + deploy: + labels: + - "traefik.enable=true" + - "traefik.frontend.rule=Host:lidarr.media.arnaud-pc.fr" + - "traefik.port=3000" + - "traefik.docker.network=dmz" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + + gatekeeper_jackett: + image: keycloak/keycloak-gatekeeper + networks: + - plex + - dmz + command: | + --discovery-url=https://auth.arnaud-pc.fr/auth/realms/cloud + --client-id=jackett_proxy + --client-secret=secret + --listen=:3000 + --redirection-url=https://jackett.media.arnaud-pc.fr + --enable-refresh-tokens=true + --encryption-key=key + --upstream-url=http://jackett:9117 + --enable-default-deny=true + --resources="uri=/*|roles=jackett" + --skip-upstream-tls-verify=true + deploy: + labels: + - "traefik.enable=true" + - "traefik.frontend.rule=Host:jackett.media.arnaud-pc.fr" + - "traefik.port=3000" + - "traefik.docker.network=dmz" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + + gatekeeper_syncthing: + image: keycloak/keycloak-gatekeeper + networks: + - plex + - dmz + command: | + --discovery-url=https://auth.arnaud-pc.fr/auth/realms/cloud + --client-id=syncthing_proxy + --client-secret=secret + --listen=:3000 + --redirection-url=https://syncthing.media.arnaud-pc.fr + --enable-refresh-tokens=true + --encryption-key=key + --upstream-url=http://syncthing:8384 + --enable-default-deny=true + --resources="uri=/*|roles=syncthing" + --skip-upstream-tls-verify=true + deploy: + labels: + - "traefik.enable=true" + - "traefik.frontend.rule=Host:syncthing.media.arnaud-pc.fr" + - "traefik.port=3000" + - "traefik.docker.network=dmz" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + + ombi: + image: linuxserver/ombi + environment: + TZ: Europe/Paris + PGID: 1000 + PUID: 1000 + volumes: + - ombi_conf:/config + networks: + - dmz + - plex + deploy: + mode: replicated + replicas: 1 + resources: + limits: + cpus: '0.5' + memory: 350M + reservations: + cpus: '0.1' + memory: 100M + labels: + - "traefik.frontend.rule=Host:plex.arnaud-pc.fr;PathPrefix:/request" + - "traefik.port=3579" + - "traefik.docker.network=dmz" + - "traefik.passHostHeader=true" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=true" + + ombi-anime: + image: linuxserver/ombi + environment: + TZ: Europe/Paris + PGID: 1000 + PUID: 1000 + volumes: + - ombi-anime_conf:/config + networks: + - dmz + - plex + deploy: + mode: replicated + replicas: 1 + resources: + limits: + cpus: '0.5' + memory: 350M + reservations: + cpus: '0.1' + memory: 100M + labels: + - "traefik.frontend.rule=Host:plex.arnaud-pc.fr;PathPrefix:/anime-request" + - "traefik.port=3579" + - "traefik.docker.network=dmz" + - "traefik.passHostHeader=true" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=true" + +networks: + dmz: + external: true + plex: + driver: overlay + +volumes: + syncthing_conf: + driver: convoy + jackett_conf: + driver: convoy + lidarr_conf: + driver: convoy + radarr_conf: + driver: convoy + sonarr-tv_conf: + driver: convoy + sonarr-anime_conf: + driver: convoy + ombi_conf: + driver: convoy + ombi-anime_conf: + driver: convoy diff --git a/plex/plex/docker-compose.yml b/plex/plex/docker-compose.yml new file mode 100644 index 0000000..3654c68 --- /dev/null +++ b/plex/plex/docker-compose.yml @@ -0,0 +1,40 @@ +version: '3.5' + +services: + plex: + image: ministicraft/plex + container_name: plex + restart: always + environment: + TZ: Europe/Paris + ADVERTISE_IP: https://plex.arnaud-pc.fr:32400 + PLEX_CLAIM: claim + PLEX_UID: 1000 + PLEX_GID: 1000 + ports: + - "32400:32400" + - "33400:33400" + volumes: + - /storage/plex/:/data/ + - /rapidStorage/PlexConfig/:/config + - /tmp:/transcode + networks: + - plex + labels: + - "traefik.frontend.rule=Host:plex.arnaud-pc.fr" + - "traefik.port=32400" + - "traefik.docker.network=dmz" + - "traefik.passHostHeader=true" + - "traefik.backend.loadbalancer.swarm=true" + - "traefik.backend.loadbalancer.method=drr" + - "traefik.enable=true" +# TODO change devices +# when gpu import is implemented + devices: + - "/dev/dri/card0:/dev/dri/card0" + - "/dev/dri/renderD128:/dev/dri/renderD128" + +networks: + plex: + driver: overlay + name: plex diff --git a/portainer_stack.yml b/portainer/docker-compose.yml similarity index 86% rename from portainer_stack.yml rename to portainer/docker-compose.yml index 928d172..5ab23e3 100644 --- a/portainer_stack.yml +++ b/portainer/docker-compose.yml @@ -4,7 +4,11 @@ services: agent: image: portainer/agent environment: + # REQUIRED: Should be equal to the service name prefixed by "tasks." when + # deployed inside an overlay network AGENT_CLUSTER_ADDR: tasks.agent + # AGENT_PORT: 9001 + # LOG_LEVEL: debug volumes: - /var/run/docker.sock:/var/run/docker.sock - /var/lib/docker/volumes:/var/lib/docker/volumes diff --git a/traefik_stack.yml b/traefik/docker-compose.yml similarity index 78% rename from traefik_stack.yml rename to traefik/docker-compose.yml index 2c18bb0..9adb8de 100644 --- a/traefik_stack.yml +++ b/traefik/docker-compose.yml @@ -4,12 +4,13 @@ services: image: traefik environment: - OVH_ENDPOINT=ovh-eu - - OVH_APPLICATION_KEY=xxx - - OVH_APPLICATION_SECRET=xxx - - OVH_CONSUMER_KEY=xxx + - OVH_APPLICATION_KEY=key + - OVH_APPLICATION_SECRET=secret + - OVH_CONSUMER_KEY=key ports: - "80:80" - "443:443" + - "8080:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock - traefik_conf:/etc/traefik @@ -31,6 +32,7 @@ services: --acme.onhostrule --acme.dnschallenge --acme.dnschallenge.provider=ovh + --insecureskipverify=true deploy: mode: replicated replicas: 1 @@ -46,6 +48,14 @@ services: - "traefik.backend.loadbalancer.method=drr" - "traefik.enable=true" + certdumper: + image: ldez/traefik-certs-dumper + depends_on: + - traefik + volumes: + - traefik_conf:/traefik + command: file --source /traefik/acme/acme.json --dest /traefik/ssl/ --domain-subdir=true --crt-ext=.pem --key-ext=.pem + networks: traefik_network: name: dmz diff --git a/traefik/traefik-certs-dumper/.dockerignore b/traefik/traefik-certs-dumper/.dockerignore new file mode 100644 index 0000000..65750f3 --- /dev/null +++ b/traefik/traefik-certs-dumper/.dockerignore @@ -0,0 +1,10 @@ +.idea/ +vendor/ +dist/ +dump/ +dumpcerts.sh +acme.json +acme-backup.json +traefik-certs-dumper +manifest.json +*.Dockerfile diff --git a/traefik/traefik-certs-dumper/.gitignore b/traefik/traefik-certs-dumper/.gitignore new file mode 100644 index 0000000..9f570b3 --- /dev/null +++ b/traefik/traefik-certs-dumper/.gitignore @@ -0,0 +1,10 @@ +.idea/ +vendor/ +dist/ +dump/ +dumpcerts.sh +acme.json +acme-backup.json +traefik-certs-dumper +manifest.json +/linux-*.Dockerfile diff --git a/traefik/traefik-certs-dumper/.golangci.toml b/traefik/traefik-certs-dumper/.golangci.toml new file mode 100644 index 0000000..472cac9 --- /dev/null +++ b/traefik/traefik-certs-dumper/.golangci.toml @@ -0,0 +1,41 @@ +[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 = "cmd/" + linters = ["gochecknoglobals", "gochecknoinits"] diff --git a/traefik/traefik-certs-dumper/.goreleaser.yml b/traefik/traefik-certs-dumper/.goreleaser.yml new file mode 100644 index 0000000..45d4646 --- /dev/null +++ b/traefik/traefik-certs-dumper/.goreleaser.yml @@ -0,0 +1,49 @@ +project_name: traefik-certs-dumper + +builds: + - binary: traefik-certs-dumper + 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 + goos: + - linux + - darwin + - windows + - freebsd + - openbsd + goarch: + - amd64 + - 386 + - arm + - arm64 + goarm: + - 7 + - 6 + - 5 + + ignore: + - goos: darwin + goarch: 386 + +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^doc:' + - '^chore:' + - '^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 + +#release: +# disable: true \ No newline at end of file diff --git a/traefik/traefik-certs-dumper/.travis.yml b/traefik/traefik-certs-dumper/.travis.yml new file mode 100644 index 0000000..4de67cb --- /dev/null +++ b/traefik/traefik-certs-dumper/.travis.yml @@ -0,0 +1,51 @@ +language: go + +go: + - 1.12.x + - 1.x + +dist: xenial + +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 ${GOLANGCI_LINT_VERSION} + - golangci-lint --version + # Install Docker image multi-arch builder + - curl -sfL https://raw.githubusercontent.com/ldez/seihon/master/godownloader.sh | bash -s -- -b "${GOPATH}/bin" ${SEIHON_VERSION} + - seihon --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$ diff --git a/traefik/traefik-certs-dumper/Dockerfile b/traefik/traefik-certs-dumper/Dockerfile new file mode 100644 index 0000000..7d1ac0a --- /dev/null +++ b/traefik/traefik-certs-dumper/Dockerfile @@ -0,0 +1,21 @@ +FROM golang:1-alpine as builder + +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 make build + +FROM alpine:3.9 +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"] diff --git a/traefik/traefik-certs-dumper/LICENSE b/traefik/traefik-certs-dumper/LICENSE new file mode 100644 index 0000000..cd40b66 --- /dev/null +++ b/traefik/traefik-certs-dumper/LICENSE @@ -0,0 +1,13 @@ +Copyright 2019 Fernandez Ludovic + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/traefik/traefik-certs-dumper/Makefile b/traefik/traefik-certs-dumper/Makefile new file mode 100644 index 0000000..280b0dc --- /dev/null +++ b/traefik/traefik-certs-dumper/Makefile @@ -0,0 +1,25 @@ +.PHONY: default clean checks test build + +TAG_NAME := $(shell git tag -l --contains HEAD) +SHA := $(shell git rev-parse --short HEAD) +VERSION := $(if $(TAG_NAME),$(TAG_NAME),$(SHA)) + +BUILD_DATE := $(shell date -u '+%Y-%m-%d_%I:%M:%S%p') + +default: clean checks test build + +test: clean + go test -v -cover ./... + +clean: + rm -rf dist/ cover.out + +build: clean + @echo Version: $(VERSION) $(BUILD_DATE) + go build -v -ldflags '-X "github.com/ldez/traefik-certs-dumper/cmd.version=${VERSION}" -X "github.com/ldez/traefik-certs-dumper/cmd.commit=${SHA}" -X "github.com/ldez/traefik-certs-dumper/cmd.date=${BUILD_DATE}"' -o traefik-certs-dumper + +checks: + golangci-lint run + +publish-images: + seihon publish -v "$(TAG_NAME)" -v "latest" --image-name ldez/traefik-certs-dumper --dry-run=false diff --git a/traefik/traefik-certs-dumper/cmd/boltdb.go b/traefik/traefik-certs-dumper/cmd/boltdb.go new file mode 100644 index 0000000..fd2044b --- /dev/null +++ b/traefik/traefik-certs-dumper/cmd/boltdb.go @@ -0,0 +1,39 @@ +package cmd + +import ( + "github.com/abronan/valkeyrie/store" + "github.com/abronan/valkeyrie/store/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 +var boltdbCmd = &cobra.Command{ + Use: "boltdb", + Short: "Dump the content of BoltDB.", + Long: `Dump the content of BoltDB.`, + RunE: runE(boltdbRun), +} + +func init() { + kvCmd.AddCommand(boltdbCmd) + + boltdbCmd.Flags().Bool("persist-connection", false, "Persist connection for boltdb.") + boltdbCmd.Flags().String("bucket", "traefik", "Bucket for boltdb.") +} + +func boltdbRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error { + config, err := getKvConfig(cmd) + if err != nil { + return err + } + + config.Options.Bucket = cmd.Flag("bucket").Value.String() + config.Options.PersistConnection, _ = cmd.Flags().GetBool("persist-connection") + + config.Backend = store.BOLTDB + boltdb.Register() + + return kv.Dump(config, baseConfig) +} diff --git a/traefik/traefik-certs-dumper/cmd/consul.go b/traefik/traefik-certs-dumper/cmd/consul.go new file mode 100644 index 0000000..ef86386 --- /dev/null +++ b/traefik/traefik-certs-dumper/cmd/consul.go @@ -0,0 +1,37 @@ +package cmd + +import ( + "github.com/abronan/valkeyrie/store" + "github.com/abronan/valkeyrie/store/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 +var consulCmd = &cobra.Command{ + Use: "consul", + Short: "Dump the content of Consul.", + Long: `Dump the content of Consul.`, + RunE: runE(consulRun), +} + +func init() { + kvCmd.AddCommand(consulCmd) + + consulCmd.Flags().String("token", "", "Token for consul.") +} + +func consulRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error { + config, err := getKvConfig(cmd) + if err != nil { + return err + } + + config.Options.Token = cmd.Flag("token").Value.String() + + config.Backend = store.CONSUL + consul.Register() + + return kv.Dump(config, baseConfig) +} diff --git a/traefik/traefik-certs-dumper/cmd/doc.go b/traefik/traefik-certs-dumper/cmd/doc.go new file mode 100644 index 0000000..9218db9 --- /dev/null +++ b/traefik/traefik-certs-dumper/cmd/doc.go @@ -0,0 +1,20 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" +) + +// docCmd represents the doc command +var docCmd = &cobra.Command{ + Use: "doc", + Short: "Generate documentation", + Hidden: true, + RunE: func(cmd *cobra.Command, args []string) error { + return doc.GenMarkdownTree(rootCmd, "./docs") + }, +} + +func init() { + rootCmd.AddCommand(docCmd) +} diff --git a/traefik/traefik-certs-dumper/cmd/etcd.go b/traefik/traefik-certs-dumper/cmd/etcd.go new file mode 100644 index 0000000..9a21577 --- /dev/null +++ b/traefik/traefik-certs-dumper/cmd/etcd.go @@ -0,0 +1,43 @@ +package cmd + +import ( + "time" + + "github.com/abronan/valkeyrie/store" + "github.com/abronan/valkeyrie/store/etcd/v2" + "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 +var etcdCmd = &cobra.Command{ + Use: "etcd", + Short: "Dump the content of etcd.", + Long: `Dump the content of etcd.`, + RunE: runE(etcdRun), +} + +func init() { + kvCmd.AddCommand(etcdCmd) + + etcdCmd.Flags().Int("sync-period", 0, "Sync period for etcd in seconds.") +} + +func etcdRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error { + config, err := getKvConfig(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() + + return kv.Dump(config, baseConfig) +} diff --git a/traefik/traefik-certs-dumper/cmd/file.go b/traefik/traefik-certs-dumper/cmd/file.go new file mode 100644 index 0000000..18f70f0 --- /dev/null +++ b/traefik/traefik-certs-dumper/cmd/file.go @@ -0,0 +1,25 @@ +package cmd + +import ( + "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.`, + Long: `Dump the content of the "acme.json" file from Traefik to certificates.`, + RunE: runE(func(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error { + acmeFile := cmd.Flag("source").Value.String() + + return file.Dump(acmeFile, baseConfig) + }), +} + +func init() { + rootCmd.AddCommand(fileCmd) + + fileCmd.Flags().String("source", "./acme.json", "Path to 'acme.json' file.") +} diff --git a/traefik/traefik-certs-dumper/cmd/kv.go b/traefik/traefik-certs-dumper/cmd/kv.go new file mode 100644 index 0000000..5bca9f8 --- /dev/null +++ b/traefik/traefik-certs-dumper/cmd/kv.go @@ -0,0 +1,174 @@ +package cmd + +import ( + "crypto/tls" + "crypto/x509" + "fmt" + "io/ioutil" + "os" + "time" + + "github.com/abronan/valkeyrie/store" + "github.com/ldez/traefik-certs-dumper/v2/dumper/kv" + "github.com/spf13/cobra" +) + +// kvCmd represents the kv command +var kvCmd = &cobra.Command{ + Use: "kv", + Short: `Dump the content of a KV store.`, + Long: `Dump the content of a KV store.`, +} + +func init() { + rootCmd.AddCommand(kvCmd) + + 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("password", "", "Password for connection.") + kvCmd.PersistentFlags().String("username", "", "Username for connection.") + + kvCmd.PersistentFlags().Bool("tls", false, "Enable TLS encryption.") + kvCmd.PersistentFlags().String("tls.ca", "", "Root CA for certificate verification if TLS is enabled") + kvCmd.PersistentFlags().Bool("tls.ca.optional", false, "") + kvCmd.PersistentFlags().String("tls.cert", "", "TLS cert") + kvCmd.PersistentFlags().String("tls.key", "", "TLS key") + kvCmd.PersistentFlags().Bool("tls.insecureskipverify", false, "Trust unverified certificates if TLS is enabled.") +} + +func getKvConfig(cmd *cobra.Command) (*kv.Config, error) { + endpoints, err := cmd.Flags().GetStringSlice("endpoints") + if err != nil { + 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, + }, + }, nil +} + +func createTLSConfig(cmd *cobra.Command) (*tls.Config, error) { + enable, _ := cmd.Flags().GetBool("tls") + if !enable { + return nil, nil + } + + ca := cmd.Flag("tls.ca").Value.String() + caPool, err := getCertPool(ca) + if err != nil { + return nil, err + } + + caOptional, _ := cmd.Flags().GetBool("tls.ca.optional") + clientAuth := getClientAuth(ca, caOptional) + + insecureSkipVerify, _ := cmd.Flags().GetBool("tls.insecureskipverify") + 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") + } + + cert, err := getCertificate(privateKey, certContent) + if err != nil { + return nil, fmt.Errorf("failed to load TLS keypair: %s", err) + } + + return &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caPool, + InsecureSkipVerify: insecureSkipVerify, + ClientAuth: clientAuth, + }, nil +} + +func getCertPool(ca string) (*x509.CertPool, error) { + caPool := x509.NewCertPool() + + if ca != "" { + caContent, err := getCAContent(ca) + if err != nil { + return nil, fmt.Errorf("failed to read CA. %s", err) + } + + if !caPool.AppendCertsFromPEM(caContent) { + return nil, fmt.Errorf("failed to parse CA") + } + } + + return caPool, nil +} + +func getCAContent(ca string) ([]byte, error) { + if _, errCA := os.Stat(ca); errCA != nil { + return []byte(ca), nil + } + + caContent, err := ioutil.ReadFile(ca) + if err != nil { + return nil, err + } + return caContent, nil +} + +func getClientAuth(ca string, caOptional bool) tls.ClientAuthType { + if ca == "" { + return tls.NoClientCert + } + + if caOptional { + return tls.VerifyClientCertIfGiven + } + return tls.RequireAndVerifyClientCert +} + +func getCertificate(privateKey, certContent string) (tls.Certificate, error) { + if certContent == "" || privateKey == "" { + return tls.Certificate{}, nil + } + + _, errKeyIsFile := os.Stat(privateKey) + _, 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") + } + + if os.IsNotExist(errCertIsFile) && errKeyIsFile == nil { + return tls.Certificate{}, fmt.Errorf("TLS key is a file, but tls cert is not") + } + + // string + if os.IsNotExist(errCertIsFile) && os.IsNotExist(errKeyIsFile) { + return tls.X509KeyPair([]byte(certContent), []byte(privateKey)) + } + + // files + if errCertIsFile == nil && errKeyIsFile == nil { + return tls.LoadX509KeyPair(certContent, privateKey) + } + + if errCertIsFile != nil { + return tls.Certificate{}, errCertIsFile + } + return tls.Certificate{}, errKeyIsFile +} diff --git a/traefik/traefik-certs-dumper/cmd/root.go b/traefik/traefik-certs-dumper/cmd/root.go new file mode 100644 index 0000000..f281016 --- /dev/null +++ b/traefik/traefik-certs-dumper/cmd/root.go @@ -0,0 +1,180 @@ +package cmd + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "strconv" + + "github.com/ldez/traefik-certs-dumper/v2/dumper" + 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 +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 { + if cmd.Name() == "version" { + return nil + } + + crtExt := cmd.Flag("crt-ext").Value.String() + keyExt := cmd.Flag("key-ext").Value.String() + + subDir, _ := strconv.ParseBool(cmd.Flag("domain-subdir").Value.String()) + if !subDir { + if crtExt == keyExt { + return fmt.Errorf("--crt-ext (%q) and --key-ext (%q) are identical, in this case --domain-subdir is required", crtExt, keyExt) + } + } + return nil + }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + if err := rootCmd.Execute(); err != nil { + log.Println(err) + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initConfig) + + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.traefik-certs-dumper.yaml)") + + rootCmd.PersistentFlags().String("dest", "./dump", "Path to store the dump content.") + rootCmd.PersistentFlags().String("crt-ext", ".crt", "The file extension of the generated certificates.") + rootCmd.PersistentFlags().String("crt-name", "certificate", "The file name (without extension) of the generated certificates.") + rootCmd.PersistentFlags().String("key-ext", ".key", "The file extension of the generated private keys.") + rootCmd.PersistentFlags().String("key-name", "privatekey", "The file name (without extension) of the generated private keys.") + rootCmd.PersistentFlags().Bool("domain-subdir", false, "Use domain as sub-directory.") + rootCmd.PersistentFlags().Bool("clean", true, "Clean destination folder before dumping content.") + rootCmd.PersistentFlags().Bool("watch", false, "Enable watching changes.") + rootCmd.PersistentFlags().String("post-hook", "", "Execute a command only if changes occurs on the data source. (works only with the watch mode)") +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + if cfgFile != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFile) + } else { + // Find home directory. + home, err := homedir.Dir() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // Search config in home directory with name ".traefik-certs-dumper" (without extension). + viper.AddConfigPath(home) + viper.SetConfigName(".traefik-certs-dumper") + } + + viper.AutomaticEnv() // read in environment variables that match + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + fmt.Println("Using config file:", viper.ConfigFileUsed()) + } +} + +func runE(apply func(*dumper.BaseConfig, *cobra.Command) error) func(*cobra.Command, []string) error { + return func(cmd *cobra.Command, _ []string) error { + baseConfig, err := getBaseConfig(cmd) + if err != nil { + return err + } + + err = apply(baseConfig, cmd) + if err != nil { + return err + } + + return tree(baseConfig.DumpPath, "") + } +} + +func tree(root, indent string) error { + fi, err := os.Stat(root) + if err != nil { + return fmt.Errorf("could not stat %s: %v", root, err) + } + + fmt.Println(fi.Name()) + if !fi.IsDir() { + return nil + } + + fis, err := ioutil.ReadDir(root) + if err != nil { + return fmt.Errorf("could not read dir %s: %v", root, err) + } + + var names []string + for _, fi := range fis { + if fi.Name()[0] != '.' { + names = append(names, fi.Name()) + } + } + + for i, name := range names { + add := "│ " + if i == len(names)-1 { + fmt.Printf(indent + "└──") + add = " " + } else { + fmt.Printf(indent + "├──") + } + + if err := tree(filepath.Join(root, name), indent+add); err != nil { + return err + } + } + + return nil +} + +func getBaseConfig(cmd *cobra.Command) (*dumper.BaseConfig, error) { + subDir, err := strconv.ParseBool(cmd.Flag("domain-subdir").Value.String()) + if err != nil { + return nil, err + } + + clean, err := strconv.ParseBool(cmd.Flag("clean").Value.String()) + if err != nil { + return nil, err + } + + watch, err := strconv.ParseBool(cmd.Flag("watch").Value.String()) + if err != nil { + return nil, err + } + + return &dumper.BaseConfig{ + DumpPath: cmd.Flag("dest").Value.String(), + CrtInfo: dumper.FileInfo{ + Name: cmd.Flag("crt-name").Value.String(), + Ext: cmd.Flag("crt-ext").Value.String(), + }, + KeyInfo: dumper.FileInfo{ + Name: cmd.Flag("key-name").Value.String(), + Ext: cmd.Flag("key-ext").Value.String(), + }, + DomainSubDir: subDir, + Clean: clean, + Watch: watch, + Hook: cmd.Flag("post-hook").Value.String(), + }, nil +} diff --git a/traefik/traefik-certs-dumper/cmd/version.go b/traefik/traefik-certs-dumper/cmd/version.go new file mode 100644 index 0000000..2b2b4a8 --- /dev/null +++ b/traefik/traefik-certs-dumper/cmd/version.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "fmt" + "runtime" + + "github.com/spf13/cobra" +) + +var ( + version = "dev" + commit = "I don't remember exactly" + date = "I don't remember exactly" +) + +// versionCmd represents the version command +var versionCmd = &cobra.Command{ + Use: "version", + Short: "Display version", + Run: func(cmd *cobra.Command, args []string) { + displayVersion(rootCmd.Name()) + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) +} + +func displayVersion(name string) { + fmt.Printf(name+`: + version : %s + commit : %s + build date : %s + go version : %s + go compiler : %s + platform : %s/%s +`, version, commit, date, runtime.Version(), runtime.Compiler, runtime.GOOS, runtime.GOARCH) +} diff --git a/traefik/traefik-certs-dumper/cmd/zookeeper.go b/traefik/traefik-certs-dumper/cmd/zookeeper.go new file mode 100644 index 0000000..35f5f9d --- /dev/null +++ b/traefik/traefik-certs-dumper/cmd/zookeeper.go @@ -0,0 +1,33 @@ +package cmd + +import ( + "github.com/abronan/valkeyrie/store" + "github.com/abronan/valkeyrie/store/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 +var zookeeperCmd = &cobra.Command{ + Use: "zookeeper", + Short: "Dump the content of zookeeper.", + Long: `Dump the content of zookeeper.`, + RunE: runE(zookeeperRun), +} + +func init() { + kvCmd.AddCommand(zookeeperCmd) +} + +func zookeeperRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error { + config, err := getKvConfig(cmd) + if err != nil { + return err + } + + config.Backend = store.ZK + zookeeper.Register() + + return kv.Dump(config, baseConfig) +} diff --git a/traefik/traefik-certs-dumper/docs/traefik-certs-dumper.md b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper.md new file mode 100644 index 0000000..f2a73aa --- /dev/null +++ b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper.md @@ -0,0 +1,31 @@ +## traefik-certs-dumper + +Dump Let's Encrypt certificates from Traefik. + +### Synopsis + +Dump Let's Encrypt certificates from Traefik. + +### Options + +``` + --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. + -h, --help help for traefik-certs-dumper + --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 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 diff --git a/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_file.md b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_file.md new file mode 100644 index 0000000..d7be5a4 --- /dev/null +++ b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_file.md @@ -0,0 +1,39 @@ +## traefik-certs-dumper file + +Dump the content of the "acme.json" file. + +### Synopsis + +Dump the content of the "acme.json" file from Traefik to certificates. + +``` +traefik-certs-dumper file [flags] +``` + +### Options + +``` + -h, --help help for file + --source string Path to 'acme.json' file. (default "./acme.json") +``` + +### 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. + +###### Auto generated by spf13/cobra on 25-Apr-2019 diff --git a/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv.md b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv.md new file mode 100644 index 0000000..ba510f0 --- /dev/null +++ b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv.md @@ -0,0 +1,49 @@ +## traefik-certs-dumper kv + +Dump the content of a KV store. + +### Synopsis + +Dump the content of a KV store. + +### Options + +``` + --connection-timeout int Connection timeout in seconds. + --endpoints strings List of endpoints. (default [localhost:8500]) + -h, --help help for kv + --password string Password for connection. + --prefix string Prefix used for KV store. (default "traefik") + --tls Enable TLS encryption. + --tls.ca string Root CA for certificate verification if TLS is enabled + --tls.ca.optional + --tls.cert string TLS cert + --tls.insecureskipverify Trust unverified certificates if TLS is enabled. + --tls.key string TLS key + --username string Username for connection. +``` + +### 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 kv boltdb](traefik-certs-dumper_kv_boltdb.md) - Dump the content of BoltDB. +* [traefik-certs-dumper kv consul](traefik-certs-dumper_kv_consul.md) - Dump the content of Consul. +* [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 diff --git a/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_boltdb.md b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_boltdb.md new file mode 100644 index 0000000..f2e774b --- /dev/null +++ b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_boltdb.md @@ -0,0 +1,51 @@ +## traefik-certs-dumper kv boltdb + +Dump the content of BoltDB. + +### Synopsis + +Dump the content of BoltDB. + +``` +traefik-certs-dumper kv boltdb [flags] +``` + +### Options + +``` + --bucket string Bucket for boltdb. (default "traefik") + -h, --help help for boltdb + --persist-connection Persist connection for boltdb. +``` + +### 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) + --connection-timeout int Connection timeout in seconds. + --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. + --endpoints strings List of endpoints. (default [localhost:8500]) + --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") + --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") + --tls Enable TLS encryption. + --tls.ca string Root CA for certificate verification if TLS is enabled + --tls.ca.optional + --tls.cert string TLS cert + --tls.insecureskipverify Trust unverified certificates if TLS is enabled. + --tls.key string TLS key + --username string Username for connection. + --watch Enable watching changes. +``` + +### SEE ALSO + +* [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 diff --git a/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_consul.md b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_consul.md new file mode 100644 index 0000000..6fb0df0 --- /dev/null +++ b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_consul.md @@ -0,0 +1,50 @@ +## traefik-certs-dumper kv consul + +Dump the content of Consul. + +### Synopsis + +Dump the content of Consul. + +``` +traefik-certs-dumper kv consul [flags] +``` + +### Options + +``` + -h, --help help for consul + --token string Token for consul. +``` + +### 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) + --connection-timeout int Connection timeout in seconds. + --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. + --endpoints strings List of endpoints. (default [localhost:8500]) + --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") + --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") + --tls Enable TLS encryption. + --tls.ca string Root CA for certificate verification if TLS is enabled + --tls.ca.optional + --tls.cert string TLS cert + --tls.insecureskipverify Trust unverified certificates if TLS is enabled. + --tls.key string TLS key + --username string Username for connection. + --watch Enable watching changes. +``` + +### SEE ALSO + +* [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 diff --git a/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_etcd.md b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_etcd.md new file mode 100644 index 0000000..878c3b8 --- /dev/null +++ b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_etcd.md @@ -0,0 +1,50 @@ +## traefik-certs-dumper kv etcd + +Dump the content of etcd. + +### Synopsis + +Dump the content of etcd. + +``` +traefik-certs-dumper kv etcd [flags] +``` + +### Options + +``` + -h, --help help for etcd + --sync-period int Sync period for etcd in seconds. +``` + +### 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) + --connection-timeout int Connection timeout in seconds. + --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. + --endpoints strings List of endpoints. (default [localhost:8500]) + --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") + --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") + --tls Enable TLS encryption. + --tls.ca string Root CA for certificate verification if TLS is enabled + --tls.ca.optional + --tls.cert string TLS cert + --tls.insecureskipverify Trust unverified certificates if TLS is enabled. + --tls.key string TLS key + --username string Username for connection. + --watch Enable watching changes. +``` + +### SEE ALSO + +* [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 diff --git a/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_zookeeper.md b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_zookeeper.md new file mode 100644 index 0000000..1ffe529 --- /dev/null +++ b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_kv_zookeeper.md @@ -0,0 +1,49 @@ +## traefik-certs-dumper kv zookeeper + +Dump the content of zookeeper. + +### Synopsis + +Dump the content of zookeeper. + +``` +traefik-certs-dumper kv zookeeper [flags] +``` + +### Options + +``` + -h, --help help for zookeeper +``` + +### 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) + --connection-timeout int Connection timeout in seconds. + --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. + --endpoints strings List of endpoints. (default [localhost:8500]) + --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") + --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") + --tls Enable TLS encryption. + --tls.ca string Root CA for certificate verification if TLS is enabled + --tls.ca.optional + --tls.cert string TLS cert + --tls.insecureskipverify Trust unverified certificates if TLS is enabled. + --tls.key string TLS key + --username string Username for connection. + --watch Enable watching changes. +``` + +### SEE ALSO + +* [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 diff --git a/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_version.md b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_version.md new file mode 100644 index 0000000..4d76473 --- /dev/null +++ b/traefik/traefik-certs-dumper/docs/traefik-certs-dumper_version.md @@ -0,0 +1,38 @@ +## traefik-certs-dumper version + +Display version + +### Synopsis + +Display version + +``` +traefik-certs-dumper version [flags] +``` + +### Options + +``` + -h, --help help for version +``` + +### 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. + +###### Auto generated by spf13/cobra on 25-Apr-2019 diff --git a/traefik/traefik-certs-dumper/dumper/config.go b/traefik/traefik-certs-dumper/dumper/config.go new file mode 100644 index 0000000..7c189c0 --- /dev/null +++ b/traefik/traefik-certs-dumper/dumper/config.go @@ -0,0 +1,12 @@ +package dumper + +// BaseConfig Base dump command configuration. +type BaseConfig struct { + DumpPath string + CrtInfo FileInfo + KeyInfo FileInfo + DomainSubDir bool + Clean bool + Watch bool + Hook string +} diff --git a/traefik/traefik-certs-dumper/dumper/dumper.go b/traefik/traefik-certs-dumper/dumper/dumper.go new file mode 100644 index 0000000..af63a78 --- /dev/null +++ b/traefik/traefik-certs-dumper/dumper/dumper.go @@ -0,0 +1,129 @@ +package dumper + +import ( + "encoding/pem" + "io/ioutil" + "os" + "path/filepath" + + "github.com/go-acme/lego/certcrypto" +) + +const ( + certsSubDir = "certs" + keysSubDir = "private" +) + +// FileInfo File information. +type FileInfo struct { + Name string + Ext string +} + +// Dump Dumps data to certificates. +func Dump(data *StoredData, baseConfig *BaseConfig) error { + if baseConfig.Clean { + err := cleanDir(baseConfig.DumpPath) + if err != nil { + return 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, keysSubDir), 0755); err != nil { + return err + } + + privateKeyPem := extractPEMPrivateKey(data.Account) + err := ioutil.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0600) + if err != nil { + return err + } + + for _, cert := range data.Certificates { + err := writeCert(baseConfig.DumpPath, cert, baseConfig.CrtInfo, baseConfig.DomainSubDir) + if err != nil { + return err + } + + err = writeKey(baseConfig.DumpPath, cert, baseConfig.KeyInfo, baseConfig.DomainSubDir) + if err != nil { + return err + } + } + + return nil +} + +func writeCert(dumpPath string, cert *Certificate, info 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 { + return err + } + } + + return ioutil.WriteFile(certPath, cert.Certificate, 0666) +} + +func writeKey(dumpPath string, cert *Certificate, info FileInfo, domainSubDir bool) error { + keyPath := filepath.Join(dumpPath, keysSubDir, 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 { + return err + } + } + + return ioutil.WriteFile(keyPath, cert.Key, 0600) +} + +func extractPEMPrivateKey(account *Account) []byte { + var block *pem.Block + switch account.KeyType { + case certcrypto.RSA2048, certcrypto.RSA4096, certcrypto.RSA8192: + block = &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: account.PrivateKey, + } + case certcrypto.EC256, certcrypto.EC384: + block = &pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: account.PrivateKey, + } + default: + panic("unsupported key type") + } + + return pem.EncodeToMemory(block) +} + +func cleanDir(dumpPath string) error { + _, errExists := os.Stat(dumpPath) + if os.IsNotExist(errExists) { + return nil + } + + if errExists != nil { + return errExists + } + + dir, err := ioutil.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 +} diff --git a/traefik/traefik-certs-dumper/dumper/file/file.go b/traefik/traefik-certs-dumper/dumper/file/file.go new file mode 100644 index 0000000..33bbacc --- /dev/null +++ b/traefik/traefik-certs-dumper/dumper/file/file.go @@ -0,0 +1,167 @@ +package file + +import ( + "bytes" + "crypto/md5" + "encoding/json" + "io" + "log" + "os" + "strings" + + "github.com/fsnotify/fsnotify" + "github.com/ldez/traefik-certs-dumper/v2/dumper" + "github.com/ldez/traefik-certs-dumper/v2/hook" +) + +// Dump Dumps "acme.json" file to certificates. +func Dump(acmeFile string, baseConfig *dumper.BaseConfig) error { + err := dump(acmeFile, baseConfig) + if err != nil { + return err + } + + if baseConfig.Watch { + return watch(acmeFile, baseConfig) + } + return nil +} + +func dump(acmeFile string, baseConfig *dumper.BaseConfig) error { + data, err := readFile(acmeFile) + if err != nil { + return err + } + + return dumper.Dump(data, baseConfig) +} + +func readFile(acmeFile string) (*dumper.StoredData, error) { + source, err := os.Open(acmeFile) + if err != nil { + return nil, err + } + + data := &dumper.StoredData{} + if err = json.NewDecoder(source).Decode(data); err != nil { + return nil, err + } + + return data, nil +} + +func watch(acmeFile string, baseConfig *dumper.BaseConfig) error { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } + + defer func() { _ = watcher.Close() }() + + done := make(chan bool) + go func() { + var previousHash []byte + + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + + if isDebug() { + log.Println("event:", event) + } + + hash, errW := manageEvent(watcher, event, acmeFile, previousHash, baseConfig) + if errW != nil { + log.Println("error:", errW) + done <- true + return + } + + previousHash = hash + + case errW, ok := <-watcher.Errors: + if !ok { + return + } + + log.Println("error:", errW) + done <- true + return + } + } + }() + + err = watcher.Add(acmeFile) + if err != nil { + return err + } + + <-done + + return nil +} + +func manageEvent(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 + } + + hash, err := calculateHash(acmeFile) + if err != nil { + return nil, err + } + + if !bytes.Equal(previousHash, hash) { + if isDebug() { + log.Println("detected changes on file:", event.Name) + } + + if errD := dump(acmeFile, baseConfig); errD != nil { + return nil, errD + } + + if isDebug() { + log.Println("Dumped new certificate data.") + } + + hook.Exec(baseConfig.Hook) + } + + return hash, nil +} + +func manageRename(watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile string) error { + if event.Op&fsnotify.Rename != fsnotify.Rename { + return nil + } + + if err := watcher.Remove(acmeFile); err != nil { + return err + } + + return watcher.Add(acmeFile) +} + +func calculateHash(acmeFile string) ([]byte, error) { + file, err := os.Open(acmeFile) + if err != nil { + return nil, err + } + defer func() { _ = file.Close() }() + + h := md5.New() + _, err = io.Copy(h, file) + if err != nil { + return nil, err + } + + return h.Sum(nil), nil +} + +func isDebug() bool { + return strings.EqualFold(os.Getenv("TCD_DEBUG"), "true") +} diff --git a/traefik/traefik-certs-dumper/dumper/filename.go b/traefik/traefik-certs-dumper/dumper/filename.go new file mode 100644 index 0000000..0d37a67 --- /dev/null +++ b/traefik/traefik-certs-dumper/dumper/filename.go @@ -0,0 +1,7 @@ +// +build !windows + +package dumper + +func safeName(filename string) string { + return filename +} diff --git a/traefik/traefik-certs-dumper/dumper/filename_windows.go b/traefik/traefik-certs-dumper/dumper/filename_windows.go new file mode 100644 index 0000000..7c48b2b --- /dev/null +++ b/traefik/traefik-certs-dumper/dumper/filename_windows.go @@ -0,0 +1,9 @@ +// +build windows + +package dumper + +import "strings" + +func safeName(filename string) string { + return strings.ReplaceAll(filename, "*", "_") +} diff --git a/traefik/traefik-certs-dumper/dumper/info.go b/traefik/traefik-certs-dumper/dumper/info.go new file mode 100644 index 0000000..793f1f7 --- /dev/null +++ b/traefik/traefik-certs-dumper/dumper/info.go @@ -0,0 +1,35 @@ +package dumper + +import ( + "github.com/go-acme/lego/certcrypto" + "github.com/go-acme/lego/registration" +) + +// StoredData represents the data managed by the Store +type StoredData struct { + Account *Account + Certificates []*Certificate + HTTPChallenges map[string]map[string][]byte + TLSChallenges map[string]*Certificate +} + +// Certificate is a struct which contains all data needed from an ACME certificate +type Certificate struct { + Domain Domain + Certificate []byte + Key []byte +} + +// Domain holds a domain name with SANs +type Domain struct { + Main string + SANs []string +} + +// Account is used to store lets encrypt registration info +type Account struct { + Email string + Registration *registration.Resource + PrivateKey []byte + KeyType certcrypto.KeyType +} diff --git a/traefik/traefik-certs-dumper/dumper/kv/config.go b/traefik/traefik-certs-dumper/dumper/kv/config.go new file mode 100644 index 0000000..76d0e89 --- /dev/null +++ b/traefik/traefik-certs-dumper/dumper/kv/config.go @@ -0,0 +1,11 @@ +package kv + +import "github.com/abronan/valkeyrie/store" + +// Config KV configuration. +type Config struct { + Backend store.Backend + Prefix string + Endpoints []string + Options *store.Config +} diff --git a/traefik/traefik-certs-dumper/dumper/kv/convert.go b/traefik/traefik-certs-dumper/dumper/kv/convert.go new file mode 100644 index 0000000..58448cb --- /dev/null +++ b/traefik/traefik-certs-dumper/dumper/kv/convert.go @@ -0,0 +1,65 @@ +package kv + +import ( + "github.com/go-acme/lego/certcrypto" + "github.com/go-acme/lego/registration" + "github.com/ldez/traefik-certs-dumper/v2/dumper" +) + +// CertificateV1 is used to store certificate info +type CertificateV1 struct { + Domain string + CertURL string + CertStableURL string + PrivateKey []byte + Certificate []byte +} + +// AccountV1 is used to store lets encrypt registration info +type AccountV1 struct { + Email string + Registration *registration.Resource + PrivateKey []byte + KeyType certcrypto.KeyType + DomainsCertificate DomainsCertificates + ChallengeCerts map[string]*ChallengeCert + HTTPChallenge map[string]map[string][]byte +} + +// DomainsCertificates stores a certificate for multiple domains +type DomainsCertificates struct { + Certs []*DomainsCertificate +} + +// ChallengeCert stores a challenge certificate +type ChallengeCert struct { + Certificate []byte + PrivateKey []byte +} + +// DomainsCertificate contains a certificate for multiple domains +type DomainsCertificate struct { + Domains dumper.Domain + Certificate *CertificateV1 +} + +// 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, + } + var certs []*dumper.Certificate + for _, oldCert := range account.DomainsCertificate.Certs { + certs = append(certs, &dumper.Certificate{ + Certificate: oldCert.Certificate.Certificate, + Domain: oldCert.Domains, + Key: oldCert.Certificate.PrivateKey, + }) + } + storedData.Certificates = certs + return storedData +} diff --git a/traefik/traefik-certs-dumper/dumper/kv/kv.go b/traefik/traefik-certs-dumper/dumper/kv/kv.go new file mode 100644 index 0000000..3714a60 --- /dev/null +++ b/traefik/traefik-certs-dumper/dumper/kv/kv.go @@ -0,0 +1,99 @@ +package kv + +import ( + "bytes" + "compress/gzip" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "strings" + + "github.com/abronan/valkeyrie" + "github.com/abronan/valkeyrie/store" + "github.com/ldez/traefik-certs-dumper/v2/dumper" + "github.com/ldez/traefik-certs-dumper/v2/hook" +) + +const storeKeySuffix = "/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) + if err != nil { + return fmt.Errorf("unable to create client of the store: %v", err) + } + + storeKey := config.Prefix + storeKeySuffix + + if baseConfig.Watch { + return watch(kvStore, storeKey, baseConfig) + } + + pair, err := kvStore.Get(storeKey, nil) + if err != nil { + return fmt.Errorf("unable to retrieve %s value: %v", 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) + if err != nil { + return err + } + + for { + pair := <-pairs + if pair == nil { + return fmt.Errorf("could not fetch Key/Value pair for key %v", storeKey) + } + + err = dumpPair(pair, baseConfig) + if err != nil { + return err + } + + if isDebug() { + log.Println("Dumped new certificate data.") + } + + hook.Exec(baseConfig.Hook) + } +} + +func dumpPair(pair *store.KVPair, baseConfig *dumper.BaseConfig) error { + data, err := getStoredDataFromGzip(pair) + if err != nil { + return err + } + + return dumper.Dump(data, baseConfig) +} + +func getStoredDataFromGzip(pair *store.KVPair) (*dumper.StoredData, error) { + reader, err := gzip.NewReader(bytes.NewBuffer(pair.Value)) + if err != nil { + return nil, fmt.Errorf("fail to create GZip reader: %v", err) + } + + acmeData, err := ioutil.ReadAll(reader) + if err != nil { + return nil, fmt.Errorf("unable to read the pair content: %v", err) + } + + account := &AccountV1{} + if err := json.Unmarshal(acmeData, &account); err != nil { + return nil, fmt.Errorf("unable marshal AccountV1: %v", err) + } + + return convertAccountV1ToV2(account), nil +} + +func isDebug() bool { + return strings.EqualFold(os.Getenv("TCD_DEBUG"), "true") +} diff --git a/traefik/traefik-certs-dumper/go.mod b/traefik/traefik-certs-dumper/go.mod new file mode 100644 index 0000000..b2d06e2 --- /dev/null +++ b/traefik/traefik-certs-dumper/go.mod @@ -0,0 +1,51 @@ +module github.com/ldez/traefik-certs-dumper/v2 + +go 1.12 + +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 + 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/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 +) + +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 +) diff --git a/traefik/traefik-certs-dumper/go.sum b/traefik/traefik-certs-dumper/go.sum new file mode 100644 index 0000000..136d0a3 --- /dev/null +++ b/traefik/traefik-certs-dumper/go.sum @@ -0,0 +1,177 @@ +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= +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/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/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/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-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-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/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/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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/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/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/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/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/pkg/errors v0.8.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/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/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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/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= +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/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/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= +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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/traefik/traefik-certs-dumper/godownloader.sh b/traefik/traefik-certs-dumper/godownloader.sh new file mode 100644 index 0000000..9889899 --- /dev/null +++ b/traefik/traefik-certs-dumper/godownloader.sh @@ -0,0 +1,420 @@ +#!/bin/sh +set -e +# Code generated by godownloader on 2019-04-04T19:26:29Z. DO NOT EDIT. +# + +usage() { + this=$1 + cat </dev/null +} +echoerr() { + echo "$@" 1>&2 +} +log_prefix() { + echo "$0" +} +_logp=6 +log_set_priority() { + _logp="$1" +} +log_priority() { + if test -z "$1"; then + echo "$_logp" + return + fi + [ "$1" -le "$_logp" ] +} +log_tag() { + case $1 in + 0) echo "emerg" ;; + 1) echo "alert" ;; + 2) echo "crit" ;; + 3) echo "err" ;; + 4) echo "warning" ;; + 5) echo "notice" ;; + 6) echo "info" ;; + 7) echo "debug" ;; + *) echo "$1" ;; + esac +} +log_debug() { + log_priority 7 || return 0 + echoerr "$(log_prefix)" "$(log_tag 7)" "$@" +} +log_info() { + log_priority 6 || return 0 + echoerr "$(log_prefix)" "$(log_tag 6)" "$@" +} +log_err() { + log_priority 3 || return 0 + echoerr "$(log_prefix)" "$(log_tag 3)" "$@" +} +log_crit() { + log_priority 2 || return 0 + echoerr "$(log_prefix)" "$(log_tag 2)" "$@" +} +uname_os() { + os=$(uname -s | tr '[:upper:]' '[:lower:]') + case "$os" in + msys_nt) os="windows" ;; + esac + echo "$os" +} +uname_arch() { + arch=$(uname -m) + case $arch in + x86_64) arch="amd64" ;; + x86) arch="386" ;; + i686) arch="386" ;; + i386) arch="386" ;; + aarch64) arch="arm64" ;; + armv5*) arch="armv5" ;; + armv6*) arch="armv6" ;; + armv7*) arch="armv7" ;; + esac + echo ${arch} +} +uname_os_check() { + os=$(uname_os) + case "$os" in + darwin) return 0 ;; + dragonfly) return 0 ;; + freebsd) return 0 ;; + linux) return 0 ;; + android) return 0 ;; + nacl) return 0 ;; + netbsd) return 0 ;; + openbsd) return 0 ;; + plan9) return 0 ;; + solaris) return 0 ;; + windows) return 0 ;; + esac + log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" + return 1 +} +uname_arch_check() { + arch=$(uname_arch) + case "$arch" in + 386) return 0 ;; + amd64) return 0 ;; + arm64) return 0 ;; + armv5) return 0 ;; + armv6) return 0 ;; + armv7) return 0 ;; + ppc64) return 0 ;; + ppc64le) return 0 ;; + mips) return 0 ;; + mipsle) return 0 ;; + mips64) return 0 ;; + mips64le) return 0 ;; + s390x) return 0 ;; + amd64p32) return 0 ;; + esac + log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" + return 1 +} +untar() { + tarball=$1 + case "${tarball}" in + *.tar.gz | *.tgz) tar -xzf "${tarball}" ;; + *.tar) tar -xf "${tarball}" ;; + *.zip) unzip "${tarball}" ;; + *) + log_err "untar unknown archive format for ${tarball}" + return 1 + ;; + esac +} +mktmpdir() { + test -z "$TMPDIR" && TMPDIR="$(mktemp -d)" + mkdir -p "${TMPDIR}" + echo "${TMPDIR}" +} +http_download_curl() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") + else + code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") + fi + if [ "$code" != "200" ]; then + log_debug "http_download_curl received HTTP status $code" + return 1 + fi + return 0 +} +http_download_wget() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + wget -q -O "$local_file" "$source_url" + else + wget -q --header "$header" -O "$local_file" "$source_url" + fi +} +http_download() { + log_debug "http_download $2" + if is_command curl; then + http_download_curl "$@" + return + elif is_command wget; then + http_download_wget "$@" + return + fi + log_crit "http_download unable to find wget or curl" + return 1 +} +http_copy() { + tmp=$(mktemp) + http_download "${tmp}" "$1" "$2" || return 1 + body=$(cat "$tmp") + rm -f "${tmp}" + echo "$body" +} +github_release() { + owner_repo=$1 + version=$2 + test -z "$version" && version="latest" + giturl="https://github.com/${owner_repo}/releases/${version}" + json=$(http_copy "$giturl" "Accept:application/json") + test -z "$json" && return 1 + version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') + test -z "$version" && return 1 + echo "$version" +} +hash_sha256() { + TARGET=${1:-/dev/stdin} + if is_command gsha256sum; then + hash=$(gsha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command sha256sum; then + hash=$(sha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command shasum; then + hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command openssl; then + hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f a + else + log_crit "hash_sha256 unable to find command to compute sha-256 hash" + return 1 + fi +} +hash_sha256_verify() { + TARGET=$1 + checksums=$2 + if [ -z "$checksums" ]; then + log_err "hash_sha256_verify checksum file not specified in arg2" + return 1 + fi + BASENAME=${TARGET##*/} + want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) + if [ -z "$want" ]; then + log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" + return 1 + fi + got=$(hash_sha256 "$TARGET") + if [ "$want" != "$got" ]; then + log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" + return 1 + fi +} +cat /dev/null < 0 { + fmt.Println(string(output)) + } + + if ctxCmd.Err() == context.DeadlineExceeded { + return errors.New("hook timed out") + } + + return err +} diff --git a/traefik/traefik-certs-dumper/hook/hook_test.go b/traefik/traefik-certs-dumper/hook/hook_test.go new file mode 100644 index 0000000..ec417b6 --- /dev/null +++ b/traefik/traefik-certs-dumper/hook/hook_test.go @@ -0,0 +1,29 @@ +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(test.command) + if err != nil { + t.Fatal(err) + } + }) + } +} diff --git a/traefik/traefik-certs-dumper/integrationtest/docker-compose.yml b/traefik/traefik-certs-dumper/integrationtest/docker-compose.yml new file mode 100644 index 0000000..2a397c6 --- /dev/null +++ b/traefik/traefik-certs-dumper/integrationtest/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3' + +services: + consul-kv: + image: consul + ports: + - "8500:8500" + + zookeeper-kv: + image: zookeeper + ports: + - "2181:2181" + + etcd-kv: + image: quay.io/coreos/etcd:v3.3.12 + command: etcd --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://0.0.0.0:2380 + ports: + - "2379:2379" diff --git a/traefik/traefik-certs-dumper/integrationtest/loader.go b/traefik/traefik-certs-dumper/integrationtest/loader.go new file mode 100644 index 0000000..e4dd4b8 --- /dev/null +++ b/traefik/traefik-certs-dumper/integrationtest/loader.go @@ -0,0 +1,121 @@ +package main + +import ( + "bytes" + "compress/gzip" + "io/ioutil" + "log" + "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" +) + +const storeKey = "traefik/acme/account/object" + +func main() { + log.SetFlags(log.Lshortfile) + + source := "./acme.json" + err := loadData(source) + if err != nil { + log.Fatal(err) + } +} + +func loadData(source string) error { + content, err := readFile(source) + if err != nil { + return err + } + + // Consul + err = putData(store.CONSUL, []string{"localhost:8500"}, content) + if err != nil { + return err + } + + // ETCD v3 + err = putData(store.ETCDV3, []string{"localhost:2379"}, content) + if err != nil { + return err + } + + // Zookeeper + err = putData(store.ZK, []string{"localhost:2181"}, content) + if err != nil { + return err + } + + // BoltDB + err = putData(store.BOLTDB, []string{"/tmp/test-traefik-certs-dumper.db"}, content) + if err != nil { + return err + } + + 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) + if err != nil { + return err + } + + if err := kvStore.Put(storeKey, content, nil); err != nil { + return err + } + + log.Printf("Successfully updated %s.\n", backend) + return nil +} + +func readFile(source string) ([]byte, error) { + content, err := ioutil.ReadFile(source) + if err != nil { + return nil, err + } + + var b bytes.Buffer + gz := gzip.NewWriter(&b) + + defer func() { + if errC := gz.Close(); errC != nil { + log.Println(errC) + } + }() + + if _, err = gz.Write(content); err != nil { + return nil, err + } + + if err = gz.Flush(); err != nil { + return nil, err + } + + if err := gz.Close(); err != nil { + return nil, err + } + + return b.Bytes(), nil +} diff --git a/traefik/traefik-certs-dumper/integrationtest/readme.md b/traefik/traefik-certs-dumper/integrationtest/readme.md new file mode 100644 index 0000000..ccbc459 --- /dev/null +++ b/traefik/traefik-certs-dumper/integrationtest/readme.md @@ -0,0 +1,54 @@ +# Integration testing + +## Preparation + +- Create valid ACME file `./acme.json` + +- Start backends using docker + +```bash +docker-compose -f integrationtest/docker-compose.yml up +``` + +- Initialize backends + +```bash +go run integrationtest/loader.go +``` + +## Run certs dumper without watching + +```bash +traefik-certs-dumper file + +# http://localhost:8500/ui/ +traefik-certs-dumper kv consul --endpoints localhost:8500 + +traefik-certs-dumper kv etcd --endpoints localhost:2379 + +traefik-certs-dumper kv boltdb --endpoints /tmp/test-traefik-certs-dumper.db + +traefik-certs-dumper kv zookeeper --endpoints localhost:2181 +``` + +## Run certs dumper with watching + +While watching is enabled, manipulate `./acme.json` for file backend or run `loader.go` again for KV backends so that change events are triggered. + +```bash +traefik-certs-dumper file --watch + +traefik-certs-dumper kv consul --watch --endpoints localhost:8500 + +traefik-certs-dumper kv etcd --watch --endpoints localhost:2379 + +traefik-certs-dumper kv zookeeper --watch --endpoints localhost:2181 +``` + +## Cleanup + +- Stop backends + +```bash +docker-compose -f integrationtest/docker-compose.yml down +``` diff --git a/traefik/traefik-certs-dumper/main.go b/traefik/traefik-certs-dumper/main.go new file mode 100644 index 0000000..2c9aad0 --- /dev/null +++ b/traefik/traefik-certs-dumper/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "log" + + "github.com/ldez/traefik-certs-dumper/v2/cmd" +) + +func main() { + log.SetFlags(log.LstdFlags | log.Lshortfile) + cmd.Execute() +} diff --git a/traefik/traefik-certs-dumper/readme.md b/traefik/traefik-certs-dumper/readme.md new file mode 100644 index 0000000..c352783 --- /dev/null +++ b/traefik/traefik-certs-dumper/readme.md @@ -0,0 +1,153 @@ +# 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/) +[![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) + +## Features + +- Supported sources: + - file ("acme.json") + - KV stores (Consul, Etcd, Zookeeper, Boltdb) +- Watch changes: + - from file ("acme.json") + - from KV stores (Consul, Etcd, Zookeeper) +- Output formats: + - use domain as sub-directory (allow custom names and extensions) + - flat (domain as filename) +- Hook (only with watch mode and if the data source changes) + +## 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 +``` + + + +### From Binaries + +You can use pre-compiled binaries: + +* To get the binary just download the latest release for your OS/Arch from [the releases page](https://github.com/ldez/traefik-certs-dumper/releases/) +* Unzip the archive. +* Add `traefik-certs-dumper` in your `PATH`. + +### From Docker + +```bash +docker run ldez/traefik-certs-dumper: +``` + +## Usage + +- [traefik-certs-dumper](docs/traefik-certs-dumper.md) +- [traefik-certs-dumper file](docs/traefik-certs-dumper_file.md) +- [traefik-certs-dumper kv](docs/traefik-certs-dumper_kv.md) + +## Examples + +### Simple Dump + +```console +$ traefik-certs-dumper file +dump +├──certs +│ └──my.domain.com.key +└──private + ├──my.domain.com.crt + └──letsencrypt.key +``` + +### Change source and destination + +```console +$ traefik-certs-dumper file --source ./acme.json --dest ./dump/test +test +├──certs +│ └──my.domain.com.key +└──private + ├──my.domain.com.crt + └──letsencrypt.key +``` + +### Use domain as sub-directory + +```console +$ traefik-certs-dumper file --domain-subdir=true +dump +├──my.domain.com +│ ├──certificate.crt +│ └──privatekey.key +└──private + └──letsencrypt.key +``` + +#### Change file extension + +```console +$ traefik-certs-dumper file --domain-subdir --crt-ext=.pem --key-ext=.pem +dump +├──my.domain.com +│ ├──certificate.pem +│ └──privatekey.pem +└──private + └──letsencrypt.key +``` + +#### Change file name + +```console +$ traefik-certs-dumper file --domain-subdir --crt-name=fullchain --key-name=privkey +dump +├──my.domain.com +│ ├──fullchain.crt +│ └──privkey.key +└──private + └──letsencrypt.key +``` + +### KV store + +#### Consul + +```console +$ traefik-certs-dumper kv consul --endpoints localhost:8500 +``` + +#### Etcd + +```console +$ traefik-certs-dumper kv etcd --endpoints localhost:2379 +``` + +#### Boltdb + +```console +$ traefik-certs-dumper kv boltdb --endpoints /the/path/to/mydb.db +``` + +#### Zookeeper + +```console +$ traefik-certs-dumper kv zookeeper --endpoints localhost:2181 +``` + + diff --git a/traefik/traefik-certs-dumper/tmpl.Dockerfile b/traefik/traefik-certs-dumper/tmpl.Dockerfile new file mode 100644 index 0000000..f616f51 --- /dev/null +++ b/traefik/traefik-certs-dumper/tmpl.Dockerfile @@ -0,0 +1,24 @@ +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"]