From baf4d7a906ca9d56f9ef150522d23be743e242de Mon Sep 17 00:00:00 2001 From: zosimovaa Date: Thu, 19 Mar 2026 11:47:17 +0300 Subject: [PATCH] Init --- .gitignore | 4 + Dockerfile | 13 ++ README.md | 237 ++++++++++++++++++++++++++++++ certbot/letsencrypt/.gitkeep | 1 + certbot/www/.gitkeep | 1 + conf.d/registry.lesha.spb.ru.conf | 44 ++++++ docker-compose.yml | 19 +++ docker-entrypoint.sh | 23 +++ nginx.conf | 22 +++ renew.sh | 5 + 10 files changed, 369 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 certbot/letsencrypt/.gitkeep create mode 100644 certbot/www/.gitkeep create mode 100644 conf.d/registry.lesha.spb.ru.conf create mode 100644 docker-compose.yml create mode 100644 docker-entrypoint.sh create mode 100644 nginx.conf create mode 100644 renew.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..61015b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +certbot/letsencrypt/* +certbot/www/* +!certbot/letsencrypt/.gitkeep +!certbot/www/.gitkeep diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c8c4ca1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM nginx:stable + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends certbot cron tzdata \ + && rm -rf /var/lib/apt/lists/* + +COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh + +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..31635d1 --- /dev/null +++ b/README.md @@ -0,0 +1,237 @@ +# Nginx + Certbot Ingress + +Этот каталог содержит автономный ingress-модуль в одном контейнере: + +- `nginx` как reverse proxy +- `certbot` для выпуска и обновления сертификатов Let's Encrypt +- `cron` внутри контейнера для автообновления сертификатов + +## Структура + +```text +. +├── docker-compose.yml +├── Dockerfile +├── docker-entrypoint.sh +├── renew.sh +├── nginx.conf +├── conf.d/ +│ └── registry.lesha.spb.ru.conf +└── certbot/ + ├── letsencrypt/ + └── www/ +``` + +## Что делает контейнер + +После старта контейнер: + +1. запускает `cron` +2. запускает `nginx` в foreground +3. два раза в день вызывает `renew.sh` + +Скрипт [renew.sh](/Users/alex/Dev_projects_v2/apps/ingress/renew.sh) выполняет: + +```bash +certbot renew --webroot -w /var/www/certbot --quiet +nginx -s reload +``` + +## Первый запуск + +Перейти в каталог: + +```bash +cd /opt/apps/nginx +``` + +Локально в этом репозитории аналогичный путь: + +```bash +cd /Users/alex/Dev_projects_v2/apps/ingress +``` + +Собрать и запустить контейнер: + +```bash +docker compose up -d --build +``` + +Проверить, что `nginx` поднялся: + +```bash +docker compose ps +docker logs nginx +``` + +После первого старта `nginx` уже отвечает на `80` и обслуживает путь: + +```text +/.well-known/acme-challenge/ +``` + +Это нужно для выпуска сертификата через `webroot`. + +## Выпуск сертификата для существующего домена + +Текущий конфиг домена лежит в [conf.d/registry.lesha.spb.ru.conf](/Users/alex/Dev_projects_v2/apps/ingress/conf.d/registry.lesha.spb.ru.conf). + +Выпуск сертификата: + +```bash +docker exec -it nginx certbot certonly \ + --webroot -w /var/www/certbot \ + -d registry.lesha.spb.ru \ + --email you@example.com \ + --agree-tos \ + --no-eff-email +``` + +Сертификаты будут сохранены в: + +```text +./certbot/letsencrypt +``` + +Они переживают рестарты контейнера, потому что каталог подключён как volume. + +## Включение HTTPS после выпуска сертификата + +В файле [conf.d/registry.lesha.spb.ru.conf](/Users/alex/Dev_projects_v2/apps/ingress/conf.d/registry.lesha.spb.ru.conf) уже есть готовый HTTPS-блок в комментарии. + +Нужно: + +1. раскомментировать блок `server { listen 443 ssl; ... }` +2. при необходимости включить редирект с HTTP на HTTPS +3. перезагрузить `nginx` + +Перезагрузка: + +```bash +docker exec nginx nginx -s reload +``` + +## Автообновление сертификатов + +Cron создаётся в контейнере автоматически при старте. + +Расписание по умолчанию: + +- `03:17` +- `15:17` + +Проверить cron-задачу внутри контейнера: + +```bash +docker exec nginx cat /etc/cron.d/certbot-renew +``` + +Проверить ручной запуск renewal: + +```bash +docker exec nginx /usr/local/bin/renew.sh +``` + +## Добавление нового домена + +Для нового домена нужно создать отдельный файл в `conf.d`. + +Пример для `example.com`: + +```nginx +server { + listen 80; + server_name example.com; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + auth_basic off; + try_files $uri =404; + } + + location / { + return 404; + } +} + +# После выпуска сертификата: +# server { +# listen 443 ssl; +# http2 on; +# server_name example.com; +# +# ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; +# ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; +# +# location / { +# proxy_pass http://app:8080; +# proxy_set_header Host $http_host; +# proxy_set_header X-Real-IP $remote_addr; +# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +# proxy_set_header X-Forwarded-Proto https; +# } +# } +``` + +Дальше порядок такой: + +1. создать новый конфиг в `conf.d/.conf` +2. проверить имя upstream, например `app:8080` +3. перезагрузить `nginx` +4. выпустить сертификат через `certbot certonly --webroot` +5. включить HTTPS-блок +6. снова перезагрузить `nginx` + +Команда выпуска сертификата для нового домена: + +```bash +docker exec -it nginx certbot certonly \ + --webroot -w /var/www/certbot \ + -d example.com \ + --email you@example.com \ + --agree-tos \ + --no-eff-email +``` + +## Где менять backend + +Для `registry.lesha.spb.ru` upstream сейчас задан так: + +```nginx +proxy_pass http://registry:5000; +``` + +Если backend доступен по другому имени контейнера, DNS-алиасу или порту, измените это значение в [conf.d/registry.lesha.spb.ru.conf](/Users/alex/Dev_projects_v2/apps/ingress/conf.d/registry.lesha.spb.ru.conf). + +## Полезные команды + +Проверка итогового compose: + +```bash +docker compose config +``` + +Просмотр логов: + +```bash +docker logs nginx +``` + +Проверка конфига `nginx`: + +```bash +docker exec nginx nginx -t +``` + +Перезапуск контейнера: + +```bash +docker compose restart +``` + +## Важные замечания + +- Модуль использует один контейнер, без отдельного `certbot`-сервиса. +- Выпуск сертификатов выполняется только через `webroot`. +- Путь `/var/www/certbot` должен совпадать в `nginx`, `certbot` и `renew.sh`. +- На первом старте HTTPS не включён специально, чтобы `nginx` не падал без сертификата. diff --git a/certbot/letsencrypt/.gitkeep b/certbot/letsencrypt/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/certbot/letsencrypt/.gitkeep @@ -0,0 +1 @@ + diff --git a/certbot/www/.gitkeep b/certbot/www/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/certbot/www/.gitkeep @@ -0,0 +1 @@ + diff --git a/conf.d/registry.lesha.spb.ru.conf b/conf.d/registry.lesha.spb.ru.conf new file mode 100644 index 0000000..2e99ec1 --- /dev/null +++ b/conf.d/registry.lesha.spb.ru.conf @@ -0,0 +1,44 @@ +server { + listen 80; + server_name registry.lesha.spb.ru; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + auth_basic off; + try_files $uri =404; + } + + location / { + return 404; + } +} + +# Enable this block after the certificate has been issued. +# If you want HTTP -> HTTPS redirect after issuance, replace the HTTP +# `location / { return 404; }` above with `return 301 https://$host$request_uri;`. +# +# server { +# listen 443 ssl; +# http2 on; +# server_name registry.lesha.spb.ru; +# +# client_max_body_size 1024m; +# proxy_read_timeout 900; +# proxy_connect_timeout 10s; +# +# ssl_certificate /etc/letsencrypt/live/registry.lesha.spb.ru/fullchain.pem; +# ssl_certificate_key /etc/letsencrypt/live/registry.lesha.spb.ru/privkey.pem; +# +# location /v2/ { +# proxy_pass http://registry:5000; +# proxy_set_header Host $http_host; +# proxy_set_header X-Real-IP $remote_addr; +# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +# proxy_set_header X-Forwarded-Proto https; +# add_header Docker-Distribution-Api-Version registry/2.0 always; +# } +# +# location / { +# return 404; +# } +# } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..52b2ec6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +services: + nginx: + container_name: nginx + build: + context: . + dockerfile: Dockerfile + restart: unless-stopped + environment: + TZ: Europe/Moscow + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./conf.d:/etc/nginx/conf.d:ro + - ./certbot/letsencrypt:/etc/letsencrypt + - ./certbot/www:/var/www/certbot + - ./renew.sh:/usr/local/bin/renew.sh:ro + diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..b2d8323 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/sh +set -eu + +CRON_FILE="/etc/cron.d/certbot-renew" +CRON_SCHEDULE="${CERTBOT_RENEW_SCHEDULE:-17 3,15 * * *}" + +mkdir -p /var/www/certbot +chmod +x /usr/local/bin/renew.sh + +cat > "$CRON_FILE" <> /var/log/cron.log 2>&1 +EOF + +chmod 0644 "$CRON_FILE" +touch /var/log/cron.log + +cron + +exec nginx -g 'daemon off;' diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..c96f8ec --- /dev/null +++ b/nginx.conf @@ -0,0 +1,22 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + ssl_session_cache shared:SSL:10m; + + access_log /var/log/nginx/access.log; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/renew.sh b/renew.sh new file mode 100644 index 0000000..78a01a7 --- /dev/null +++ b/renew.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -eu + +certbot renew --webroot -w /var/www/certbot --quiet +nginx -s reload