update repo

This commit is contained in:
ministicraft
2019-06-04 22:58:42 +02:00
parent b5a7116849
commit 8ed9113bb2
62 changed files with 3810 additions and 82 deletions

View File

@@ -6,4 +6,4 @@ scrapers:
sinks:
sink1:
url: http://tasks.warp10:8080/api/v0/update
token: WARP10_TOKEN
token: token

3
beets/Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM linuxserver/beets
RUN pip install --upgrade pip
RUN pip install --upgrade https://github.com/beetbox/audioread/archive/master.zip

View File

@@ -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

View File

@@ -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

2
heimdall/Dockerfile Normal file
View File

@@ -0,0 +1,2 @@
FROM linuxserver/heimdall
RUN apk add php7-pdo php7-pdo_pgsql

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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

78
plex/docker-compose.yml Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,10 @@
.idea/
vendor/
dist/
dump/
dumpcerts.sh
acme.json
acme-backup.json
traefik-certs-dumper
manifest.json
*.Dockerfile

10
traefik/traefik-certs-dumper/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
.idea/
vendor/
dist/
dump/
dumpcerts.sh
acme.json
acme-backup.json
traefik-certs-dumper
manifest.json
/linux-*.Dockerfile

View File

@@ -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"]

View File

@@ -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

View File

@@ -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$

View File

@@ -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"]

View File

@@ -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.

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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.")
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -0,0 +1,7 @@
// +build !windows
package dumper
func safeName(filename string) string {
return filename
}

View File

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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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
)

View File

@@ -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=

View File

@@ -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 <<EOF
$this: download go binaries for ldez/traefik-certs-dumper
Usage: $this [-b] bindir [-d] [tag]
-b sets bindir or installation directory, Defaults to ./bin
-d turns on debug logging
[tag] is a tag from
https://github.com/ldez/traefik-certs-dumper/releases
If tag is missing, then the latest will be used.
Generated by godownloader
https://github.com/goreleaser/godownloader
EOF
exit 2
}
parse_args() {
#BINDIR is ./bin unless set be ENV
# over-ridden by flag below
BINDIR=${BINDIR:-./bin}
while getopts "b:dh?x" arg; do
case "$arg" in
b) BINDIR="$OPTARG" ;;
d) log_set_priority 10 ;;
h | \?) usage "$0" ;;
x) set -x ;;
esac
done
shift $((OPTIND - 1))
TAG=$1
}
# this function wraps all the destructive operations
# if a curl|bash cuts off the end of the script due to
# network, either nothing will happen or will syntax error
# out preventing half-done work
execute() {
tmpdir=$(mktmpdir)
log_debug "downloading files into ${tmpdir}"
http_download "${tmpdir}/${TARBALL}" "${TARBALL_URL}"
http_download "${tmpdir}/${CHECKSUM}" "${CHECKSUM_URL}"
hash_sha256_verify "${tmpdir}/${TARBALL}" "${tmpdir}/${CHECKSUM}"
srcdir="${tmpdir}"
(cd "${tmpdir}" && untar "${TARBALL}")
install -d "${BINDIR}"
for binexe in "traefik-certs-dumper" ; do
if [ "$OS" = "windows" ]; then
binexe="${binexe}.exe"
fi
install "${srcdir}/${binexe}" "${BINDIR}/"
log_info "installed ${BINDIR}/${binexe}"
done
}
is_supported_platform() {
platform=$1
found=1
case "$platform" in
windows/amd64) found=0 ;;
windows/386) found=0 ;;
windows/arm64) found=0 ;;
darwin/amd64) found=0 ;;
darwin/386) found=0 ;;
darwin/arm64) found=0 ;;
linux/amd64) found=0 ;;
linux/386) found=0 ;;
linux/arm64) found=0 ;;
freebsd/amd64) found=0 ;;
freebsd/386) found=0 ;;
freebsd/arm64) found=0 ;;
openbsd/amd64) found=0 ;;
openbsd/386) found=0 ;;
openbsd/arm64) found=0 ;;
windows/armv7) found=0 ;;
windows/armv6) found=0 ;;
windows/armv5) found=0 ;;
darwin/armv7) found=0 ;;
darwin/armv6) found=0 ;;
darwin/armv5) found=0 ;;
linux/armv7) found=0 ;;
linux/armv6) found=0 ;;
linux/armv5) found=0 ;;
freebsd/armv7) found=0 ;;
freebsd/armv6) found=0 ;;
freebsd/armv5) found=0 ;;
openbsd/armv7) found=0 ;;
openbsd/armv6) found=0 ;;
openbsd/armv5) found=0 ;;
esac
case "$platform" in
darwin/386) found=1 ;;
esac
return $found
}
check_platform() {
if is_supported_platform "$PLATFORM"; then
# optional logging goes here
true
else
log_crit "platform $PLATFORM is not supported. Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new"
exit 1
fi
}
tag_to_version() {
if [ -z "${TAG}" ]; then
log_info "checking GitHub for latest tag"
else
log_info "checking GitHub for tag '${TAG}'"
fi
REALTAG=$(github_release "$OWNER/$REPO" "${TAG}") && true
if test -z "$REALTAG"; then
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${PREFIX}/releases for details"
exit 1
fi
# if version starts with 'v', remove it
TAG="$REALTAG"
VERSION=${TAG#v}
}
adjust_format() {
# change format (tar.gz or zip) based on ARCH
case ${ARCH} in
windows) FORMAT=zip ;;
esac
true
}
adjust_os() {
# adjust archive name based on OS
true
}
adjust_arch() {
# adjust archive name based on ARCH
true
}
cat /dev/null <<EOF
------------------------------------------------------------------------
https://github.com/client9/shlib - portable posix shell functions
Public domain - http://unlicense.org
https://github.com/client9/shlib/blob/master/LICENSE.md
but credit (and pull requests) appreciated.
------------------------------------------------------------------------
EOF
is_command() {
command -v "$1" >/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 <<EOF
------------------------------------------------------------------------
End of functions from https://github.com/client9/shlib
------------------------------------------------------------------------
EOF
PROJECT_NAME="traefik-certs-dumper"
OWNER=ldez
REPO="traefik-certs-dumper"
BINARY=traefik-certs-dumper
FORMAT=tar.gz
OS=$(uname_os)
ARCH=$(uname_arch)
PREFIX="$OWNER/$REPO"
# use in logging routines
log_prefix() {
echo "$PREFIX"
}
PLATFORM="${OS}/${ARCH}"
GITHUB_DOWNLOAD=https://github.com/${OWNER}/${REPO}/releases/download
uname_os_check "$OS"
uname_arch_check "$ARCH"
parse_args "$@"
check_platform
tag_to_version
adjust_format
adjust_os
adjust_arch
log_info "found version: ${VERSION} for ${TAG}/${OS}/${ARCH}"
NAME=${PROJECT_NAME}_v${VERSION}_${OS}_${ARCH}
TARBALL=${NAME}.${FORMAT}
TARBALL_URL=${GITHUB_DOWNLOAD}/${TAG}/${TARBALL}
CHECKSUM=${PROJECT_NAME}_${VERSION}_checksums.txt
CHECKSUM_URL=${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM}
execute

View File

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

View File

@@ -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)
}
})
}
}

View File

@@ -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"

View File

@@ -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
}

View File

@@ -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
```

View File

@@ -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()
}

View File

@@ -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
```
<!--
To generate the script:
```bash
godownloader --repo=ldez/traefik-certs-dumper -o godownloader.sh
# or
godownloader --repo=ldez/traefik-certs-dumper > godownloader.sh
```
-->
### 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:<tag_name>
```
## 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
```

View File

@@ -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"]