BeerCamper
BeerCamper

Reputation: 103

Store traefik let's encrypt certificates not as json

you know traefik is able to do all the Let's Encrypt stuff (request and renew) for your webservices. But traefik stores the requested certificates as a JSON-file, which isn't the common format for certificates.

I want to use the Let's Encrypt certificates also for my mail-server, so I need them in simple format: *.pem or *.crt.

Here my question: Is it possible that traefik stores the Let's Encrypt certificates in a common format?

Thanks for help!

Upvotes: 7

Views: 7240

Answers (4)

x-yuri
x-yuri

Reputation: 18973

The most popular solution is ldez/traefik-certs-dumper. In case of docker-compose you need something along the following lines:

version: '3'

services:
    traefik:
        image: traefik:1.7
        command:
            --entryPoints='Name:http Address::80'
            --entryPoints='Name:https Address::443 TLS'
            --defaultentrypoints=http,https
            --logLevel=DEBUG
            --docker
            --docker.exposedByDefault=false
            --acme
            --acme.acmeLogging=true
            --acme.entrypoint=https
            --acme.storage=/data/acme.json
            --acme.onHostRule=true
            --acme.httpChallenge.entryPoint=http
        ports:
            - 8001:80
            - 8002:443
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock
            - .:/data

    traefik-certs-dumper:
        image: ldez/traefik-certs-dumper:v2.7.0
        entrypoint: sh -c '
            apk add jq
            ; while ! [ -e /data/acme.json ]
                || ! [ `jq ".Certificates | length" /data/acme.json` != 0 ]; do
                    sleep 1
                ; done
            && traefik-certs-dumper file --watch 
                --source /data/acme.json --dest /data/certs'
        volumes:
            - .:/data

    # test service
    whoami:
        image: containous/whoami
        labels:
            traefik.enable: true
            traefik.frontend.rule: Host:example.com

traefik ports are published to 8001 and 8002. I assume here that you need certificates as separate files because you want to put traefik behind another proxy.

more

Upvotes: 2

andig
andig

Reputation: 13898

I'm late to the party but meanwhile there are some dockerized solutions that will extract the certificates for you and watch the acme.json file for changes, e.g. https://github.com/SnowMB/traefik-certificate-extractor (I'm not affiliated in any way with it).

Upvotes: 0

Camil
Camil

Reputation: 8426

I'm using jq to do this

export certificate

cat acme.json | jq -r '.Certificates[] | select(.Domain.Main=="'www.example.com'") | .Certificate' | base64 -d > www.example.com.crt

export private key

cat acme.json | jq -r '.Certificates[] | select(.Domain.Main=="'www.example.com'") | .Key' | base64 -d > www.example.com.key


Traefik with Consul as KV store

export JSON containing certificate and private key

consul kv get traefik/acme/account/object | gzip -dc | jq -r '.DomainsCertificate.Certs[] | select(.Domains.Main=="'www.example.com'") | .Certificate' > www.example.com.json

export certificate only

consul kv get traefik/acme/account/object | gzip -dc | jq -r '.DomainsCertificate.Certs[] | select(.Domains.Main=="'www.example.com'") | .Certificate.Certificate' | base64 -D >  www.example.com.crt

export private key only

consul kv get traefik/acme/account/object | gzip -dc | jq -r '.DomainsCertificate.Certs[] | select(.Domains.Main=="'www.example.com'") | .Certificate.PrivateKey' | base64 -D > www.example.com.key

backup Consul

backup

consul kv get -base64 traefik/acme/account/object > backup-base64

restore

cat -s backup-base64 | base64 --decode | consul kv put traefik/acme/account/object -

Upvotes: 9

Logical Fallacy
Logical Fallacy

Reputation: 3107

Disclaimer: I'm new to Traefik, so there might be a better solution than this that I'm not aware of.

What I've done is used a Python script by JayH5 to extract the keys files from the acme.json file.

def read_domain_certs(acme_json_path, domain):
    with open(acme_json_path) as acme_json_file:
        acme_json = json.load(acme_json_file)

    certs_json = acme_json['DomainsCertificate']['Certs']
    domain_certs = [cert['Certificate'] for cert in certs_json
                    if cert['Domains']['Main'] == domain]

    if not domain_certs:
        raise RuntimeError(
            'Unable to find certificate for domain "%s"' % (domain,))
    elif len(domain_certs) > 1:
        raise RuntimeError(
            'More than one (%d) certificates for domain "%s"' % (domain,))

    [domain_cert] = domain_certs
    return (base64.b64decode(domain_cert['PrivateKey']),
            base64.b64decode(domain_cert['Certificate']))

Depending on your use case, you could skip saving the files and load the keys directly from the JSON file using that code. However, if you do need the PEM files, the script does also writes the key contents, if you need the files on disk.

def write_cert(storage_dir, filename, cert_content):
    cert_path = os.path.join(storage_dir, filename)
    with open(cert_path, 'w') as cert_file:
        cert_file.write(cert_content)
    os.chmod(cert_path, 0o600)

Upvotes: 1

Related Questions