Обновление TLS сертификатов

Обновление TLS сертификатов

Или почему я не любил работать с certbot.

По своей натуре, я человек очень торопливый. Если можно что-то сделать быстро и не вникая в суть (по какому нибудь туториалу например), то я только рад. Сделал и забыл и желательно не думать что когда то придется это делать опять. Однако, часто случается так, что у меня какая то особая ситуация в плане конфигурации сервера и приложений на нем, как например вот эта.

Есть docker контейнер с wordpress и docker контейнер с nginx. Работают они независимо, так сложилось исторически. И когда-то как-то я выпустил сертификаты letsencrypt и вот пора выпускать их снова. Так в чем проблема?

Непонимание где лежат эти сертификаты сейчас и откуда они берутся.

Так вышло что сертификаты были выпущены при помощи docker контейнера certbot. И жонглированием волюмами и файлами между ними (для меня) становится уже очень сложным. Надо как-то перекинуть файлы сертификатов (проставив нужные пермишены и сохранить ссылки на файлы ключей) между вольюмами контейнеров certbot и nginx. Каким-то образом я это сделал и уже не помню как.

Непонимание работы certbot

При попытке нахрапом быстренько обновить серты, получаешь ошибки, значения которых нужно разбирать с гуглом или чатом гпт. + еще добавляется конфиг докер компоуза для nginx + сам конфиг nginx + еще настройка шаринга volume между контейнерами. ТЬМА!

Сейчас попробуем остановиться и разложить немного по полкам.

Есть конфиг docker-compose для сайта на wordpress:

version: '3'

services:
  db:
    image: mysql:8.0
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MYSQL_DATABASE=wordpress
    volumes:
      - dbdata:/var/lib/mysql
    command: '--default-authentication-plugin=mysql_native_password --table-definition-cache=200'
    networks:
      - app-network
    cap_add:
      - SYS_NICE

  wordpress:
    depends_on:
      - db
    image: wordpress:5.1.1-fpm-alpine
    container_name: wordpress
    restart: unless-stopped
    env_file: .env
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - wordpress:/var/www/html
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
    networks:
      - app-network

volumes:
  wordpress:
  dbdata:

networks:
  app-network:
    driver: bridge

Где мы видим, что есть volume wordpress. Запомнили.

Есть конфиг docker-compose для nginx:

version: '3'
services:
  web:
    image: nginx-ssl-preread
    volumes:
      - codeartsite_wordpress:/var/www/html
      - /etc/letsencrypt:/etc/letsencrypt
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./conf.d:/etc/nginx/conf.d
      - ./modules-enabled:/etc/nginx/modules-enabled
    ports:
      - "80:80"
      - "4443:4443"
      - "8444:8444"
      - "1337:1338"
    networks:
      - codeartsite_app-network
    command: [nginx-debug, '-g', 'daemon off;']

volumes:
  codeartsite_wordpress:
    external: true

networks:
  codeartsite_app-network:
    external: true

У него есть мапинг codeartsite_wordpress:/var/www/html.

Это значит, что volume wordpress, объявленный в проекте codeartsite мапится внутри контейнера по пути /var/www/html. Запомнили.

И финально, есть конфиг для самого nginx:

server {
    listen 80;
    listen [::]:80;
    client_max_body_size 12m;
    server_name jem-art.ru www.jem-art.ru;

    location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html/wordpress;
    }

    location / {
        rewrite ^ https://$host$request_uri? permanent;
    }
}

Что тут интересного?  location ~ /.well-known/acme-challenge- эта директива отвечает за проверку сертификата при выпуске.

Т е, когда вы попросите certbot выпустить сертификат для вашего сайта командой

sudo certbot certonly --webroot -w /var/www/html/ --email test@gmail.com --agree-tos --no-eff-email -d jem-art.ru -d www.jem-art.ru

То после выпуска, перед тем как положить сертификаты по пути /etc/letsencrypt/..., certbot положит какой то ключ (не уверен какой, может и сам сертификат, не суть) по пути /var/www/html, и пойдет проверять его запросом на http://jem-art.ru/.well-known/acme-challenge, а ваш nginx должен будет ответить на этот запрос.

Вот тут и первая моя проблема. Я получаю 404 на этот запрос.

Domain: www.jem-art.ru
  Type:   unauthorized
  Detail: 104.248.43.82: Invalid response from http://www.jem-art.ru/.well-known/acme-challenge/P0hEw2RfnX8gJYFwTz-NzRTbBYOwiUmbHhE7gpVqaC0: 404

Первая ошибка - директория /var/www/html/ не смонтирована в контейнер nginx. Я что-то кладу в  /var/www/html/ , а в nginx это не появляется. Все верно, как мы помним nginx раздает у себя все, что лежит в codeartsite_wordpress. Как найти папку на диске у этого volume? Несложно

docker volume ls - список всех волюмов (видим наш)

docker volume inspect codeartsite_wordpress - смотрим детали

А вот и "Mountpoint": "/var/lib/docker/volumes/codeartsite_wordpress/_data"

Да, наверно, не совсем красиво, но будем указывать этот путь для certbot.

Вторая ошибка - тупо нет директории wordpress при обращении к /var/www/html/wordpress в nginx. Просто оставим /var/www/html/

location ~ /.well-known/acme-challenge {
        allow all;
        root /var/www/html;
    }

Получаем следующую ошибку:

archive directory exists for jem-art.ru

Это значит, что есть папка archive у letsencypt и в ней есть ключ для сайта jem-art.ru. Значит надо не заново получать сертификат, а обновлять его. Но т.к. я что-то там крутил-вертел, в папке renewal для данного сайта нет нужного конфига, поэтому просто вызывам насильно получение нового сертификата (потом его можно будет обновить, просто вызвав certbot renew).

sudo certbot certonly --webroot -w /var/lib/docker/volumes/codeartsite_wordpress/_data --email [email protected] --agree-tos --no-eff-email --force-renewal -d jem-art.ru -d www.jem-art.ru --force-renewal

Готово! Сертификаты в нужной директории. Перезапускаем nginx в докере и наслаждаемся.

Ну а по-хорошему добавим задачу в crontab:

30 2 1 */3 * /usr/bin/certbot renew --quiet --deploy-hook "/home/jem/projects/nginx/restart.sh"

Не то, чтобы очень глубоко копнул, но хотя бы не бездумно теперь копирую команды в консоль)