forked from zovos/bot_tg
upd nginx with tls
This commit is contained in:
parent
c307f48ebb
commit
90977abda7
7 changed files with 167 additions and 17 deletions
12
README.md
12
README.md
|
|
@ -78,6 +78,18 @@ docker compose down
|
||||||
- В образ копируется весь проект, а runtime-данные исключены через `.dockerignore`.
|
- В образ копируется весь проект, а runtime-данные исключены через `.dockerignore`.
|
||||||
- Если меняешь код или картинки, достаточно пересобрать: `docker compose up --build -d`.
|
- Если меняешь код или картинки, достаточно пересобрать: `docker compose up --build -d`.
|
||||||
|
|
||||||
|
## HTTPS / Let's Encrypt
|
||||||
|
|
||||||
|
- Фронтендовый `nginx` теперь слушает `80` и `443`, отдает ACME challenge и сам запускает `certbot` внутри контейнера.
|
||||||
|
- Для автоматической выдачи сертификата задай в `.env`:
|
||||||
|
- `APP_DOMAIN=bot.example.com`
|
||||||
|
- `APP_WWW_DOMAIN=www.bot.example.com` (опционально)
|
||||||
|
- `ACME_EMAIL=admin@example.com`
|
||||||
|
- Домен должен уже смотреть на сервер, а порты `80` и `443` должны быть доступны снаружи.
|
||||||
|
- Для безопасной первой проверки можно временно включить `CERTBOT_STAGING=1`, потом вернуть `0`.
|
||||||
|
- Сертификаты и webroot challenge хранятся в docker volumes `certbot_etc` и `certbot_www`, обновление запускается по кругу с интервалом `CERTBOT_RENEW_INTERVAL` (по умолчанию `12h`).
|
||||||
|
- Если нужен старый локальный маппинг портов, можно переопределить `WEBAPP_HTTP_PORT` и `WEBAPP_HTTPS_PORT`, но для реального Let's Encrypt внешний `80` должен оставаться доступным.
|
||||||
|
|
||||||
## Кратко о логике
|
## Кратко о логике
|
||||||
|
|
||||||
- `/maket` подбирает размер текста, чтобы уместить его в область макета.
|
- `/maket` подбирает размер текста, чтобы уместить его в область макета.
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,21 @@ services:
|
||||||
build:
|
build:
|
||||||
context: ./webapp/frontend
|
context: ./webapp/frontend
|
||||||
ports:
|
ports:
|
||||||
- "3000:80"
|
- "${WEBAPP_HTTP_PORT:-80}:80"
|
||||||
|
- "${WEBAPP_HTTPS_PORT:-443}:443"
|
||||||
|
environment:
|
||||||
|
APP_DOMAIN: ${APP_DOMAIN:-}
|
||||||
|
APP_WWW_DOMAIN: ${APP_WWW_DOMAIN:-}
|
||||||
|
ACME_EMAIL: ${ACME_EMAIL:-}
|
||||||
|
CERTBOT_STAGING: ${CERTBOT_STAGING:-0}
|
||||||
|
CERTBOT_RENEW_INTERVAL: ${CERTBOT_RENEW_INTERVAL:-12h}
|
||||||
|
volumes:
|
||||||
|
- certbot_etc:/etc/letsencrypt
|
||||||
|
- certbot_www:/var/www/certbot
|
||||||
depends_on:
|
depends_on:
|
||||||
- webapp-api
|
- webapp-api
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
certbot_etc:
|
||||||
|
certbot_www:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,18 @@
|
||||||
FROM nginx:alpine
|
FROM nginx:alpine
|
||||||
COPY . /usr/share/nginx/html
|
|
||||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
RUN apk add --no-cache certbot openssl
|
||||||
EXPOSE 80
|
|
||||||
|
COPY index.html /usr/share/nginx/html/index.html
|
||||||
|
COPY css /usr/share/nginx/html/css
|
||||||
|
COPY js /usr/share/nginx/html/js
|
||||||
|
COPY nginx.conf /etc/nginx/default.conf.template
|
||||||
|
COPY docker-entrypoint.d/01-render-nginx-conf.sh /docker-entrypoint.d/01-render-nginx-conf.sh
|
||||||
|
COPY docker-entrypoint.d/05-init-self-signed.sh /docker-entrypoint.d/05-init-self-signed.sh
|
||||||
|
COPY docker-entrypoint.d/10-certbot-loop.sh /docker-entrypoint.d/10-certbot-loop.sh
|
||||||
|
|
||||||
|
RUN chmod +x /docker-entrypoint.d/01-render-nginx-conf.sh \
|
||||||
|
/docker-entrypoint.d/05-init-self-signed.sh \
|
||||||
|
/docker-entrypoint.d/10-certbot-loop.sh \
|
||||||
|
&& rm -f /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80 443
|
||||||
|
|
|
||||||
20
webapp/frontend/docker-entrypoint.d/01-render-nginx-conf.sh
Normal file
20
webapp/frontend/docker-entrypoint.d/01-render-nginx-conf.sh
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
APP_DOMAIN="${APP_DOMAIN:-}"
|
||||||
|
APP_WWW_DOMAIN="${APP_WWW_DOMAIN:-}"
|
||||||
|
|
||||||
|
PRIMARY_DOMAIN="${APP_DOMAIN:-localhost}"
|
||||||
|
SERVER_NAMES="_"
|
||||||
|
|
||||||
|
if [ -n "$APP_DOMAIN" ]; then
|
||||||
|
SERVER_NAMES="$APP_DOMAIN"
|
||||||
|
if [ -n "$APP_WWW_DOMAIN" ]; then
|
||||||
|
SERVER_NAMES="$SERVER_NAMES $APP_WWW_DOMAIN"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
sed \
|
||||||
|
-e "s|__SERVER_NAMES__|$SERVER_NAMES|g" \
|
||||||
|
-e "s|__PRIMARY_DOMAIN__|$PRIMARY_DOMAIN|g" \
|
||||||
|
/etc/nginx/default.conf.template > /etc/nginx/conf.d/default.conf
|
||||||
16
webapp/frontend/docker-entrypoint.d/05-init-self-signed.sh
Normal file
16
webapp/frontend/docker-entrypoint.d/05-init-self-signed.sh
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
PRIMARY_DOMAIN="${APP_DOMAIN:-localhost}"
|
||||||
|
CERT_DIR="/etc/letsencrypt/live/$PRIMARY_DOMAIN"
|
||||||
|
FULLCHAIN="$CERT_DIR/fullchain.pem"
|
||||||
|
PRIVKEY="$CERT_DIR/privkey.pem"
|
||||||
|
|
||||||
|
if [ ! -s "$FULLCHAIN" ] || [ ! -s "$PRIVKEY" ]; then
|
||||||
|
echo "No TLS certificate for $PRIMARY_DOMAIN. Generating temporary self-signed cert."
|
||||||
|
mkdir -p "$CERT_DIR"
|
||||||
|
openssl req -x509 -nodes -newkey rsa:2048 -days 1 \
|
||||||
|
-keyout "$PRIVKEY" \
|
||||||
|
-out "$FULLCHAIN" \
|
||||||
|
-subj "/CN=$PRIMARY_DOMAIN" >/dev/null 2>&1
|
||||||
|
fi
|
||||||
47
webapp/frontend/docker-entrypoint.d/10-certbot-loop.sh
Normal file
47
webapp/frontend/docker-entrypoint.d/10-certbot-loop.sh
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
APP_DOMAIN="${APP_DOMAIN:-}"
|
||||||
|
APP_WWW_DOMAIN="${APP_WWW_DOMAIN:-}"
|
||||||
|
ACME_EMAIL="${ACME_EMAIL:-}"
|
||||||
|
CERTBOT_STAGING="${CERTBOT_STAGING:-0}"
|
||||||
|
CERTBOT_RENEW_INTERVAL="${CERTBOT_RENEW_INTERVAL:-12h}"
|
||||||
|
|
||||||
|
if [ -z "$APP_DOMAIN" ] || [ -z "$ACME_EMAIL" ]; then
|
||||||
|
echo "Skipping certbot: APP_DOMAIN or ACME_EMAIL is empty."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
(
|
||||||
|
sleep 20
|
||||||
|
|
||||||
|
if [ ! -f "/etc/letsencrypt/renewal/$APP_DOMAIN.conf" ]; then
|
||||||
|
rm -rf "/etc/letsencrypt/live/$APP_DOMAIN" "/etc/letsencrypt/archive/$APP_DOMAIN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while :; do
|
||||||
|
DOMAINS="-d $APP_DOMAIN"
|
||||||
|
if [ -n "$APP_WWW_DOMAIN" ]; then
|
||||||
|
DOMAINS="$DOMAINS -d $APP_WWW_DOMAIN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
STAGING_FLAG=""
|
||||||
|
if [ "$CERTBOT_STAGING" = "1" ]; then
|
||||||
|
STAGING_FLAG="--staging"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
certbot certonly \
|
||||||
|
--webroot \
|
||||||
|
-w /var/www/certbot \
|
||||||
|
--agree-tos \
|
||||||
|
--non-interactive \
|
||||||
|
--email "$ACME_EMAIL" \
|
||||||
|
--keep-until-expiring \
|
||||||
|
$STAGING_FLAG \
|
||||||
|
$DOMAINS || true
|
||||||
|
|
||||||
|
nginx -s reload || true
|
||||||
|
sleep "$CERTBOT_RENEW_INTERVAL"
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
|
@ -1,25 +1,52 @@
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name _;
|
listen [::]:80;
|
||||||
|
server_name __SERVER_NAMES__;
|
||||||
|
|
||||||
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
server_name __SERVER_NAMES__;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/__PRIMARY_DOMAIN__/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/__PRIMARY_DOMAIN__/privkey.pem;
|
||||||
|
ssl_session_timeout 1d;
|
||||||
|
ssl_session_cache shared:SSL:10m;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_prefer_server_ciphers off;
|
||||||
|
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
# SPA fallback
|
location /.well-known/acme-challenge/ {
|
||||||
|
root /var/www/certbot;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://webapp-api:8080;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
proxy_set_header Host $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 $scheme;
|
||||||
|
proxy_set_header X-Telegram-Init-Data $http_x_telegram_init_data;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Proxy API requests to backend
|
location ~* \.(css|js|png|jpg|jpeg|svg|woff2?)$ {
|
||||||
location /api/ {
|
|
||||||
proxy_pass http://webapp-api:8080;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Telegram-Init-Data $http_x_telegram_init_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Cache static assets
|
|
||||||
location ~* \.(css|js|png|jpg|svg|woff2?)$ {
|
|
||||||
expires 7d;
|
expires 7d;
|
||||||
add_header Cache-Control "public, immutable";
|
add_header Cache-Control "public, immutable";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue