Инициализировать репозиторий «Дейл»
Первый коммит: модульный монолит ядра (.NET 10) и gRPC-сервисы ai/ml/telegram, фронтенд Vue 3/Vite/Tailwind, документация (ТЗ, инструкция пользователя, техдокументация, код-стайл), бэклог, скрипты развёртывания и архив прототипа LeadRadar.
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env sh
|
||||
#
|
||||
# mtls-certs.sh — генерация dev-CA и сертификатов mTLS внутреннего gRPC «Дейла» (этап 7, Ruling 6, Task 13).
|
||||
#
|
||||
# Требуется openssl. Вывод — deploy/certs/:
|
||||
# ca.pem / ca.key — dev-CA (ca.pem — runtime всех процессов, ca.key — только подпись новых
|
||||
# сертификатов; хранится в deploy/certs, в репозиторий/образ не попадает,
|
||||
# см. .dockerignore; права 600);
|
||||
# <service>-server.pfx — серверный сертификат процесса: core, telegram-service, ai-service,
|
||||
# ml-service (SAN: localhost + имя compose-сервиса + host.docker.internal);
|
||||
# deal-client.pfx — общий клиентский сертификат исходящих каналов (core → сервисы,
|
||||
# telegram-service → core-ингресс);
|
||||
# deal-client.crt/.key — тот же клиентский сертификат в PEM (без пароля): нужен docker
|
||||
# healthcheck'ам compose.prod при mTLS — grpc_health_probe принимает
|
||||
# только PEM (флаги -tls-client-cert/-tls-client-key; см. Task 14).
|
||||
#
|
||||
# Сертификаты dev (срок 825 дней — как Let's Encrypt; CA — 10 лет). Пароль PFX — env DEAL_MTLS_CERT_PASSWORD
|
||||
# (по умолчанию deal_mtls_dev_password — dev); в PROD задайте свой и пропишите его же в
|
||||
# DEAL_MTLS_SERVER_CERT_PASSWORD / DEAL_MTLS_CLIENT_CERT_PASSWORD процессов (compose-prod — Task 14).
|
||||
# Повторный запуск без -f отказывается перезаписывать существующую CA (смена CA ломает доверие всех
|
||||
# сертификатов — перегенерация нужна осознанная). Subject-имена задаются config-файлами openssl, а не
|
||||
# -subj, — скрипт работает и в Git Bash (MSYS не «съедает» аргументы вида /CN=...).
|
||||
#
|
||||
# Использование:
|
||||
# scripts/mtls-certs.sh [-f] [DEAL_MTLS_CERT_PASSWORD=...]
|
||||
#
|
||||
# После генерации процессы поднимаются с env (пути — как смонтировано в compose-prod, Task 14):
|
||||
# DEAL_MTLS_ENABLED=1
|
||||
# DEAL_MTLS_CA_PEM=/etc/deal/certs/ca.pem
|
||||
# DEAL_MTLS_SERVER_CERT_PFX=/etc/deal/certs/<service>-server.pfx
|
||||
# DEAL_MTLS_SERVER_CERT_PASSWORD=<тот же пароль>
|
||||
# DEAL_MTLS_CLIENT_CERT_PFX=/etc/deal/certs/deal-client.pfx
|
||||
# DEAL_MTLS_CLIENT_CERT_PASSWORD=<тот же пароль>
|
||||
#
|
||||
# Dev-стек (deploy/compose.dev.yml) остаётся plaintext + service-token — DEAL_MTLS_ENABLED не задаётся.
|
||||
|
||||
set -eu
|
||||
|
||||
# Корень репозитория (каталог скрипта/..).
|
||||
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
ROOT_DIR=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)
|
||||
CERT_DIR="$ROOT_DIR/deploy/certs"
|
||||
|
||||
# Пароль PFX: env DEAL_MTLS_CERT_PASSWORD (dev-дефолт — фиксированное значение; PROD — свой).
|
||||
CERT_PASSWORD="${DEAL_MTLS_CERT_PASSWORD:-deal_mtls_dev_password}"
|
||||
|
||||
# Сроки сертификатов (дней): CA — 10 лет, листовые — 825 (как Let's Encrypt).
|
||||
CA_DAYS=3650
|
||||
LEAF_DAYS=825
|
||||
|
||||
# Пространство имён сертификатов: SAN листовых — localhost + имя compose-сервиса + host.docker.internal
|
||||
# (dev-прогон сервиса в контейнере против core на хосте, Ruling 12) + loopback-IP.
|
||||
SERVICES="core telegram-service ai-service ml-service"
|
||||
|
||||
FORCE=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-f|--force) FORCE=1 ;;
|
||||
*)
|
||||
echo "mtls-certs: неизвестный аргумент: $arg (ожидалось -f/--force)" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! command -v openssl >/dev/null 2>&1; then
|
||||
echo "mtls-certs: openssl не найден — установите openssl (например: apt install openssl / brew install openssl)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$CERT_DIR"
|
||||
|
||||
if [ -f "$CERT_DIR/ca.pem" ]; then
|
||||
if [ "$FORCE" -eq 0 ]; then
|
||||
echo "mtls-certs: $CERT_DIR/ca.pem уже существует — повторный запуск не перезаписывает CA" >&2
|
||||
echo "mtls-certs: (смена CA ломает доверие выданных сертификатов). Перегенерировать всё: $0 -f" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Принудительная перегенерация: старые сертификаты удаляются целиком (их доверие утеряно вместе с CA).
|
||||
rm -f "$CERT_DIR/ca.pem" "$CERT_DIR/ca.key" "$CERT_DIR/ca.srl" "$CERT_DIR"/*-server.pfx \
|
||||
"$CERT_DIR/deal-client.pfx" "$CERT_DIR/deal-client.crt" "$CERT_DIR/deal-client.key"
|
||||
fi
|
||||
|
||||
# Временный каталог для CSR/конфигов/ключей (всегда чистится, в т.ч. при ошибке).
|
||||
TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/deal-mtls.XXXXXX")
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT HUP INT TERM
|
||||
|
||||
echo "mtls-certs: генерирую dev-CA (CN=Deal mTLS Dev CA, ${CA_DAYS} дн.) в $CERT_DIR ..."
|
||||
cat > "$TMP_DIR/ca.cnf" <<'EOF'
|
||||
[req]
|
||||
distinguished_name = ca_dn
|
||||
prompt = no
|
||||
x509_extensions = ca_ext
|
||||
|
||||
[ca_dn]
|
||||
CN = Deal mTLS Dev CA
|
||||
|
||||
[ca_ext]
|
||||
basicConstraints = critical,CA:TRUE
|
||||
keyUsage = critical,keyCertSign,cRLSign,digitalSignature
|
||||
subjectKeyIdentifier = hash
|
||||
EOF
|
||||
openssl req -x509 -newkey rsa:2048 -sha256 -days "$CA_DAYS" -nodes \
|
||||
-config "$TMP_DIR/ca.cnf" \
|
||||
-keyout "$CERT_DIR/ca.key" \
|
||||
-out "$CERT_DIR/ca.pem"
|
||||
chmod 600 "$CERT_DIR/ca.key"
|
||||
chmod 644 "$CERT_DIR/ca.pem"
|
||||
|
||||
# ── Серверные сертификаты процессов (SAN: localhost + compose-имя + host.docker.internal) ──
|
||||
|
||||
for service in $SERVICES; do
|
||||
echo "mtls-certs: серверный сертификат $service (${LEAF_DAYS} дн., SAN: localhost,$service,host.docker.internal) ..."
|
||||
cat > "$TMP_DIR/server-req.cnf" <<EOF
|
||||
[req]
|
||||
distinguished_name = server_dn
|
||||
prompt = no
|
||||
|
||||
[server_dn]
|
||||
CN = $service
|
||||
EOF
|
||||
cat > "$TMP_DIR/server-ext.cnf" <<EOF
|
||||
[server]
|
||||
basicConstraints = critical,CA:FALSE
|
||||
keyUsage = critical,digitalSignature,keyEncipherment
|
||||
extendedKeyUsage = serverAuth
|
||||
subjectAltName = DNS:localhost,DNS:${service},DNS:host.docker.internal,IP:127.0.0.1
|
||||
EOF
|
||||
openssl req -new -newkey rsa:2048 -nodes \
|
||||
-config "$TMP_DIR/server-req.cnf" \
|
||||
-keyout "$TMP_DIR/server.key" \
|
||||
-out "$TMP_DIR/server.csr"
|
||||
openssl x509 -req \
|
||||
-in "$TMP_DIR/server.csr" \
|
||||
-CA "$CERT_DIR/ca.pem" \
|
||||
-CAkey "$CERT_DIR/ca.key" \
|
||||
-CAcreateserial \
|
||||
-out "$TMP_DIR/server.crt" \
|
||||
-days "$LEAF_DAYS" -sha256 \
|
||||
-extfile "$TMP_DIR/server-ext.cnf" \
|
||||
-extensions server
|
||||
openssl pkcs12 -export \
|
||||
-out "$CERT_DIR/$service-server.pfx" \
|
||||
-inkey "$TMP_DIR/server.key" \
|
||||
-in "$TMP_DIR/server.crt" \
|
||||
-name "$service-server" \
|
||||
-passout "pass:$CERT_PASSWORD"
|
||||
rm -f "$TMP_DIR/server.key" "$TMP_DIR/server.csr" "$TMP_DIR/server.crt"
|
||||
done
|
||||
|
||||
# ── Общий клиентский сертификат deal-client (исходящие каналы core и telegram-service) ──
|
||||
|
||||
echo "mtls-certs: клиентский сертификат deal-client (${LEAF_DAYS} дн.) ..."
|
||||
cat > "$TMP_DIR/client-req.cnf" <<'EOF'
|
||||
[req]
|
||||
distinguished_name = client_dn
|
||||
prompt = no
|
||||
|
||||
[client_dn]
|
||||
CN = deal-client
|
||||
EOF
|
||||
cat > "$TMP_DIR/client-ext.cnf" <<'EOF'
|
||||
[client]
|
||||
basicConstraints = critical,CA:FALSE
|
||||
keyUsage = critical,digitalSignature
|
||||
extendedKeyUsage = clientAuth
|
||||
EOF
|
||||
openssl req -new -newkey rsa:2048 -nodes \
|
||||
-config "$TMP_DIR/client-req.cnf" \
|
||||
-keyout "$TMP_DIR/client.key" \
|
||||
-out "$TMP_DIR/client.csr"
|
||||
openssl x509 -req \
|
||||
-in "$TMP_DIR/client.csr" \
|
||||
-CA "$CERT_DIR/ca.pem" \
|
||||
-CAkey "$CERT_DIR/ca.key" \
|
||||
-CAcreateserial \
|
||||
-out "$TMP_DIR/client.crt" \
|
||||
-days "$LEAF_DAYS" -sha256 \
|
||||
-extfile "$TMP_DIR/client-ext.cnf" \
|
||||
-extensions client
|
||||
openssl pkcs12 -export \
|
||||
-out "$CERT_DIR/deal-client.pfx" \
|
||||
-inkey "$TMP_DIR/client.key" \
|
||||
-in "$TMP_DIR/client.crt" \
|
||||
-name "deal-client" \
|
||||
-passout "pass:$CERT_PASSWORD"
|
||||
|
||||
# PEM-копии клиентского сертификата для grpc_health_probe compose.prod при mTLS (Task 14): утилита
|
||||
# принимает только PEM и без пароля. Сертификат публичный; приватный ключ — права 600, как ca.key.
|
||||
openssl pkcs12 -in "$CERT_DIR/deal-client.pfx" -clcerts -nokeys -passin "pass:$CERT_PASSWORD" \
|
||||
-out "$CERT_DIR/deal-client.crt"
|
||||
openssl pkcs12 -in "$CERT_DIR/deal-client.pfx" -nocerts -nodes -passin "pass:$CERT_PASSWORD" \
|
||||
-out "$CERT_DIR/deal-client.key"
|
||||
chmod 644 "$CERT_DIR/deal-client.crt"
|
||||
chmod 600 "$CERT_DIR/deal-client.key"
|
||||
|
||||
echo
|
||||
echo "mtls-certs: готово. Файлы в $CERT_DIR:"
|
||||
ls -1 "$CERT_DIR"
|
||||
echo
|
||||
echo "Пароль PFX (DEAL_MTLS_CERT_PASSWORD): $CERT_PASSWORD"
|
||||
echo "Проверка (SAN/подпись): openssl pkcs12 -in $CERT_DIR/<service>-server.pfx -nokeys | openssl verify -CAfile $CERT_DIR/ca.pem"
|
||||
echo
|
||||
echo "Включение mTLS (dev остаётся plaintext — флаг не задаётся; PROD env передаёт compose-prod, Task 14):"
|
||||
echo " DEAL_MTLS_ENABLED=1"
|
||||
echo " DEAL_MTLS_CA_PEM=$CERT_DIR/ca.pem"
|
||||
echo " DEAL_MTLS_SERVER_CERT_PFX=$CERT_DIR/<service>-server.pfx # для каждого процесса свой"
|
||||
echo " DEAL_MTLS_SERVER_CERT_PASSWORD=$CERT_PASSWORD"
|
||||
echo " DEAL_MTLS_CLIENT_CERT_PFX=$CERT_DIR/deal-client.pfx"
|
||||
echo " DEAL_MTLS_CLIENT_CERT_PASSWORD=$CERT_PASSWORD"
|
||||
Reference in New Issue
Block a user