skip to Main Content

I have an application with Nginx docker container for which the TLS certs are generated manually using the following command in the host (with Ubuntu OS) where application is deployed:

certbot certonly --manual --manual-public-ip-logging-ok --preferred-challenges dns -d my.app.com

When the certificates gets expired, i have to renew them.

But i can’t use the following certbot renew command for this purpose as it will give an error:

$ sudo certbot renew

Failed to renew certificate my.app.com with error: The manual plugin is not working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively.')

So, what I’m doing now is to create the certs once again (using the same certbot certonly command that was used previously) instead of renewing them.

How can I fix the error with certbot renew command?

How can I automate this setup?

2

Answers


  1. Here is my setup. It involves the LE secrets living in a docker volume that is shared between nginx and certbot, and nginx proxying the renewal requests to certbot, so you do not have to stop nginx while certbot does its validation.

    nginx setup

    proxy LE verififcation to certbot backend

    Requests on port 80 to letsencrypt validation are forwarded to certbot, anything else gets redirecte to https.
    (In case you are wondering why I define the proxy pass backend as a variable, see this SO answer)

      server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name _;
    
        location /.well-known/acme-challenge {
          resolver 127.0.0.11 valid=30s;
          set $upstream letsencrypt;
          proxy_pass http://$upstream:80;
          proxy_set_header Host            $host;
          proxy_set_header X-Forwarded-For $remote_addr;
          proxy_set_header X-Forwarded-Proto https;
        }
    
        location / {
          return 301 https://$host$request_uri;
        }
      }
    

    SSL setup

    pretty much standard stuff here:

      server {
        listen 443 ssl;
        server_name ${DOMAINNAME};
    
        ssl_certificate /etc/letsencrypt/live/${DOMAINNAME}/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/${DOMAINNAME}/privkey.pem;
        ssl_session_timeout 5m;
        ssl_protocols TLSv1.2;
        ssl_ciphers 'EECDH+AESGCM: EDH+AESGCM:AES256+EECDH:AES256+EDH';
        ssl_prefer_server_ciphers on;
    
        ssl_session_cache shared:SSL:10m;
        ssl_dhparam dhparam.pem;
    
        ... your lcoation block goes here ...
    
    }
    

    docker-compose magic

    certbot

    Have a special "docker-compose-LE.yml" to single run certbot:

    version: '3.4'
    
    services:
    
      letsencrypt:
        image: certbot/certbot:latest
        command: sh -c "certbot certonly --standalone -d ${DOMAINNAME} --text --agree-tos --email [email protected] --server https://acme-v02.api.letsencrypt.org/directory --rsa-key-size 4096 --verbose --keep-until-expiring --preferred-challenges=http"
        entrypoint: ""
        volumes:
          - "letsencrypt:/etc/letsencrypt"
        environment:
          - TERM=xterm
    
    volumes:
      letsencrypt:
        name: letsencrypt_keys
    

    By running "docker-compose -f docker-compose-LE.yml up" you will create and validate a certificate. You can use the same command to renew the certificate, certbot is that smart. You may run this command as often as you like (daily), because it will only renew your certificate when it is about to expire.

    See "caveat" below before running this command the first time.

    nginx

    in docker-compose.yml mount the certificates from a volume. That volume has already been created by letsencrypt, so declare it as external.

    services:
      nginx:
        image: nginx:1.18
        restart: always
        volumes:
          - letsencrypt:/etc/letsencrypt:ro
    
    volumes:
      letsencrypt:
        external:
          name: letsencrypt_keys
    

    caveat

    This method causes a chicken-egg-problem when creating the certifivate the first time: Without a cert file nginx won’t start and can’t proxy the LE validation. No nginx means no certificate, and no certificate means no nginx.

    To get around this you have to do the very first call of certbot without nginx and using certbots internal http server exposed. So the first time you run certbot add these lines to docker-compose-LE.yml:

      letsencrypt:
        ports:
          - "80:80"
    

    cert renewal

    Simply run these two command in a daily cronjob:

    docker-compose -f docker-compose-LE.yml up
    

    Will check the certificate and start renewal process once it is due. The now running nginx will proxy the certification validation to certbot.

    docker-compose exec nginx nginx -s reload
    

    Once the certificate is updated inplace inside the docker volume certbot and nginx are sharing, simply send a SIGHUP to nginx so it reloads the cert files without interrupting service.

    Login or Signup to reply.
  2. I needed to use an ‘external’ network to allow the containers from the two docker-compose files to communicate. Thanks for this!

    version: '3.4'
    services:
      letsencrypt:
        container_name: "letsencrypt"
          #ports:
            #- "80:80"
        image: certbot/certbot:latest
        command: sh -c "certbot certonly --standalone --rsa-key-size 4096 --agree-tos --preferred-challenges http -d ${DOMAINNAME} -m ${CONTACT_EMAIL} -n"
        entrypoint: ""
        volumes:
          - "letsencrypt:/etc/letsencrypt"
        networks:
          - web_nw
    networks:
      web_nw:
        external: true
    volumes:
      letsencrypt:
        name: letsencrypt
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search