1
0
Fork 0
forked from zovos/bot_tg

upd nginx with tls

This commit is contained in:
q 2026-04-12 12:53:01 +03:00
parent c307f48ebb
commit 90977abda7
7 changed files with 167 additions and 17 deletions

View file

@ -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` подбирает размер текста, чтобы уместить его в область макета.

View file

@ -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:

View file

@ -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

View 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

View 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

View 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
) &

View file

@ -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";
} }