acmesh-official / acme.sh

A pure Unix shell script implementing ACME client protocol
https://acme.sh
GNU General Public License v3.0
38.41k stars 4.89k forks source link

Automatic renewed with docker #1842

Open RudmanMario opened 5 years ago

RudmanMario commented 5 years ago

I'm trying to issue and install wildcard certificate for Apache using Docker image neilpang/acme.sh. So I have one container with Apache running in it. I've managed to issue a certificate and install it with the following command:

docker run --rm  -it \
-v "${HOME}/.acme":/acme.sh \
-e GANDI_LIVEDNS_KEY=$GANDI_LIVEDNS_KEY \
-e HOME=/root \
neilpang/acme.sh --issue  -d example.com -d '*.example.com'  --dns dns_gandi_livedns 

and then:

docker run --rm  -it \
-v "${HOME}/.acme":/acme.sh \
-v "${HOME}/letsencrypt/inxelo.aero":/letsencrypt/inxelo.aero \
-e HOME=/root \
neilpang/acme.sh --install-cert -d inxelo.aero \
--cert-file      /letsencrypt/inxelo.aero/cert.pem  \
--key-file       /letsencrypt/inxelo.aero/key.pem  \
--fullchain-file /letsencrypt/inxelo.aero/fullchain.pem \
--reloadcmd     "service apache2 force-reload"

So I've got my certificate in ${HOME}/letsencrypt/inxelo.aero and I've managed to connect it with Apache container and everything works fine.

The problem I'm having is with automatic renewing the certificate. The command service apache2 force-reload will not work as Apache is run in another container. How is automatic renewing supposed to work with Docker in this case?

FernandoMiguel commented 5 years ago

you will have to use some sort of script that talks to your apache to restart it.... since you are inside the docker, it isnt so easy

xiaket commented 5 years ago

I was facing the same problem and my approach is to have a cron job running on the host and generate the cert using one docker run command, then run another docker command to reload the cert.

moqmar commented 4 years ago

Would it make sense to add the Docker CLI to the image? That way it would just be possible to use --reloadcmd "docker restart myapachecontainer", assuming the image was started with -v /var/run/docker.sock:/var/run/docker.sock.

This would obviously create a larger attack surface, as acme.sh now has practically full root access to the host system, but I think it would still be a viable solution for this use case, as restarting production services should require root access anyways, with or without Docker.

Edit: a workaround for the problem is to use --reloadcmd "apk add --no-cache docker-cli && docker restart myapachecontainer" and mount docker.sock as described above. It's not a beautiful solution, but a simple one.

Neilpang commented 4 years ago

@moqmar why not use our docker deploy hook: https://github.com/Neilpang/acme.sh/wiki/deploy-to-docker-containers

moqmar commented 4 years ago

Ah, seems like this solves this whole issue 😄

Is there any possibility to target multiple containers with that approach though?!

PMExtra commented 4 years ago

@moqmar you can assign the same label with multiple containers.

StrangePeanut commented 2 years ago

Is there any possibility to target multiple containers with that approach though?!

Would also like a better solution to dealing with multiple Docker containers.

@moqmar you can assign the same label with multiple containers.

Passing multiple reload conditions to every labelled container is not very clean considering different containers run different services in almost every situation.

PMExtra commented 2 years ago

@StrangePeanut I have a solution. Make a reload script such as /etc/acme/reload. Then set DEPLOY_DOCKER_CONTAINER_RELOAD_CMD=/etc/acme/reload domain.tld. The argument domain.tld was optional, if your script want to know which domain should be reloaded.

For nginx, the reload script should be

#!/bin/sh
service nginx force-reload

For openldap, the reload script should be

#!/bin/sh
service slapd force-reload

By the way, for manage multiple domains (eg. doamin1 and domain2 for container A, domain3 for container B). I use the label sh.acme.autoload.example.com=true rather than sh.acme.autoload.domain=example.com, the latter is the official docs suggested. After that, I can deploy multiple domains for one container. (You can also ignore the domains which is not its own business in the reload script)

Notice that, don't forget to grant execute permission for reload script.

yvolchkov commented 2 years ago

@PMExtra, could you help me figure out how your solution is working without granting permissions? Does it mean you don't have to pass the /var/run/docker.sock?

maffe commented 5 months ago

You can build a very basic API to send commands from one container to another without the need for /var/run/docker.sock using Ncat and GNU sed, see this StackOverflow answer.