nginx

Настройка HTTPS с помощью Nginx, Let’s Encrypt и Docker

Поскольку это довольно распространенная задача, я написал этот пост, чтобы провести вас через пошаговый процесс защиты вашего сайта (и ваших пользователей) с помощью HTTPS. Особенность заключается в том, что мы будем делать это в среде Docker.

В этом посте я буду использовать Docker Compose, чтобы упростить руководство, а также потому, что мне нравится концепция Infrastructure as Code.

Nginx в роли сервера

Для того чтобы использовать nginx с Docker в качестве сервера для любого проекта, необходимо создать для него контейнер и/или службу Docker Compose. Docker будет управлять загрузкой соответствующего образа и всеми задачами, которые без Docker выполняются вручную.

version: '3' services: webserver: image: nginx:latest ports: - 80:80 - 443:443
Code language: JavaScript (javascript)

В любой момент по ходу этого руководства вы можете выполнить docker compose up, чтобы запустить среду и проверить, все ли работает нормально.

Теперь у нас есть чистая установка nginx, прослушивающая порт 80 для HTTP и порт 443 для HTTPS.

version: '3' services: webserver: image: nginx:latest ports: - 80:80 - 443:443 restart: always
Code language: JavaScript (javascript)

Поскольку я хочу, чтобы сервер был всегда включен, я говорю Docker, что он должен позаботиться о перезапуске службы “webserver”, если она неожиданно выйдет со строя.

version: '3' services: webserver: image: nginx:latest ports: - 80:80 - 443:443 restart: always volumes: - ./nginx/conf/:/etc/nginx/conf.d/:ro
Code language: JavaScript (javascript)

Конечная цель этой установки не заключается в том, чтобы обслуживать только страницу приветствия nginx. Поэтому нам нужно обновить конфигурацию nginx и объявить наш сайт.

Для этого мы используем функциональность volume в Docker. Это означает, что мы связываем папку, расположенную по адресу /etc/nginx/conf.d/ в контейнере docker, с папкой, расположенной по адресу ./nginx/conf/ на нашей локальной машине. Каждый файл, который мы добавляем, удаляем или обновляем в этой локальной папке, будет обновляться и в контейнере.

Обратите внимание, что я добавил :ro в конце объявления раздела. ro означает “только для чтения”. Контейнеру никогда не будет разрешено обновлять файлы в этой папке. Это не имеет решающего значения, но это хорошая практика. Она позволяет сэкономить часы ненужной отладки.

Далее мы обновляем файл Docker Compose следующим образом:

version: '3' services: webserver: image: nginx:latest ports: - 80:80 - 443:443 restart: always volumes: - ./nginx/conf/:/etc/nginx/conf.d/:ro - ./certbot/www:/var/www/certbot/:ro
Code language: JavaScript (javascript)

Затем добавим следующий файл конфигурации в локальную папку ./nginx/conf/. Не забудьте внести в него свои собственные данные.

server { listen 80; listen [::]:80; server_name example.org www.example.org; server_tokens off; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://example.org$request_uri; } }
Code language: JavaScript (javascript)

Конфигурация проста. Мы сообщаем nginx, что он должен прослушивать порт 80 (либо IPv4, либо IPv6) для определенного доменного имени example.org.

По умолчанию мы хотим перенаправить человека, пришедшего по порту 80, на тот же маршрут по порту 443. Это то, что вы делаете с блоком / location.

Специфика здесь заключается в другом блоке расположения. Он содержит файлы, которые нужны Certbot для аутентификации нашего сервера и создания для него сертификата HTTPS.

По сути, мы говорим: “Всегда перенаправляйте на HTTPS, кроме маршрута /.wellknow/acme-challenge/”.

Теперь мы можем перезагрузить nginx, выполнив перезапуск docker compose, или, если вы хотите избежать прерывания обслуживания (даже на несколько секунд), перезагрузить его непосредственно в контейнер с помощью docker compose exec webserver nginx -s reload.

Создание сертификата с помощью Certbot

На данный момент ничего не будет отображаться, потому что nginx все еще перенаправляет на порт 443, который еще не управляется nginx. Но это нормально. Мы просто хотим, чтобы Certbot мог аутентифицировать наш сервер.

Для этого мы воспользуемся докер-образом Certbot и добавим его в качестве сервиса в наш проект Docker Compose.

version: '3' services: webserver: image: nginx:latest ports: - 80:80 - 443:443 restart: always volumes: - ./nginx/conf/:/etc/nginx/conf.d/:ro - ./certbot/www:/var/www/certbot/:ro certbot: image: certbot/cerbot:latest volumes: - ./certbot/www/:/var/www/certbot/:rw
Code language: JavaScript (javascript)

Теперь у нас есть две службы, одна для nginx и одна для Certbot. Вы могли заметить, что они оба объявили одинаковый том. Это позволит им общаться друг с другом.

Certbot будет записывать свои файлы в ./certbot/www/, а nginx будет обслуживать их через порт 80 каждому пользователю, запрашивающему /.wellknow/acme-challenge/. Так Certbot может аутентифицировать наш сервер.

Обратите внимание, что для Certbot мы использовали :rw, что означает “чтение и запись” в конце объявления тома. Если вместо этого вы используете :ro, он не сможет записать в папку, и аутентификация будет неудачной.

Теперь вы можете проверить, что все работает как надо, выполнив docker compose run –rm certbot certonly –webroot –webroot-path /var/www/certbot/ –dry-run -d example.org. Если вы получите сообщение типа “The dry run was successfully”, значит, все работает.

Теперь мы можем создать сертификаты для сервера. Мы хотим, чтобы они использовали nginx для управления безопасностью соединений с браузерами пользователей.

Certbot создает сертификаты в папке /etc/letsencrypt/. По тому же принципу, что и для webroot, мы будем использовать тома для обмена файлами между контейнерами.

version: '3' services: webserver: image: nginx:latest ports: - 80:80 - 443:443 restart: always volumes: - ./nginx/conf/:/etc/nginx/conf.d/:ro - ./certbot/www:/var/www/certbot/:ro - ./certbot/conf/:/etc/nginx/ssl/:ro certbot: image: certbot/cerbot:latest volumes: - ./certbot/www/:/var/www/certbot/:rw - ./certbot/conf/:/etc/letsencrypt/:rw
Code language: JavaScript (javascript)

Перезапустите свой контейнер с помощью docker compose restart. Теперь Nginx должен иметь доступ к папке, в которой Certbot создает сертификаты.

Однако на данный момент эта папка пуста. Перезапустите Certbot без флага –dry-run, чтобы заполнить папку сертификатов:

$ docker compose run --rm certbot certonly --webroot --webroot-path /var/www/certbot/ -d example.org
Code language: JavaScript (javascript)

Теперь, когда у нас есть сертификаты, нам нужно настроить порт 443 для nginx.

server { listen 80; listen [::]:80; server_name example.org www.example.org; server_tokens off; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://example.org$request_uri; } } server { listen 443 default_server ssl http2; listen [::]:443 ssl http2; server_name example.org; ssl_certificate /etc/nginx/ssl/live/example.org/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/live/example.org/privkey.pem; location / { # ... } }
Code language: PHP (php)

Перезагрузив сейчас сервер nginx, мы обеспечим безопасность соединений с помощью HTTPS. Nginx использует сертификаты и закрытые ключи из томов Certbot.

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

Одна небольшая проблема с Certbot и Let’s Encrypt заключается в том, что сертификаты действуют только в течение трех месяцев. Вы должны регулярно обновлять их, если не хотите, чтобы ваши пользователи столкнулись с пугающим сообщением браузера.

Но поскольку у нас уже есть среда Docker, обновлять сертификаты Let’s Encrypt стало проще!

$ docker compose run --rm certbot renew

Этой небольшой команды “обновить” достаточно, чтобы система работала как нужно. Вам необходимо вспоминать об этом лишь раз в три месяца. Вы можете даже автоматизировать этот процесс.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *