Files
aurum/frontend/docker-entrypoint.d/25-allowed-hosts.sh
ZProger 14948aff1d security: TLS, проверка Host, шифрованное запоминание входа и аудит-лог
- TLS через подключаемый overlay docker-compose.tls.yml + Caddyfile:
  Caddy сам получает и продлевает сертификат Let's Encrypt, HSTS
  добавляется на уровне прокси — намеренно там, а не в nginx.conf,
  иначе установки без TLS заперли бы сами себя;
- проверка заголовка Host против DNS rebinding: AURUM_ALLOWED_HOSTS,
  по умолчанию localhost и 127.0.0.1, чужой Host получает 421 с
  подсказкой, какую переменную поправить;
- «запомнить меня на 7 дней» вернулось в безопасном виде: пароль
  шифруется AES-GCM ключом с extractable: false в IndexedDB, вместо
  прежнего base64 в localStorage, который разворачивался обратно в
  пароль одним atob. Требует secure context, поэтому по обычному HTTP
  к LAN-адресу чекбокс не показывается, а не притворяется рабочим;
- аудит-лог необратимых операций (восстановление бэкапа, массовое
  создание транзакций, удаление счёта) в лог контейнера. Не в БД
  сознательно: таблица аудита исчезла бы ровно в том событии, которое
  должна объяснять;
- README: раздел про TLS и таблица двух переменных, решающих, кто
  может достучаться до инстанса.
2026-09-10 21:34:40 +03:00

46 lines
1.8 KiB
Bash

#!/bin/sh
# Runs before nginx starts, same convention as 20-basic-auth.sh next door.
#
# Writes the Host allow-list that nginx.conf includes. This is what stops DNS
# rebinding: a page on the open internet can point its own hostname at
# 127.0.0.1, wait for the DNS TTL to expire, and then have the victim's own
# browser issue same-origin requests to whatever is listening there. The
# browser's origin checks don't help — as far as it's concerned the request
# really is same-origin — but the Host header still carries the attacker's
# domain, so refusing hostnames we don't expect closes it.
#
# AURUM_ALLOWED_HOSTS is a space- or comma-separated list. Default covers
# local access only; add your own hostname when you serve Aurum under one
# (see .env.example). "*" disables the check entirely.
set -eu
FRAGMENT=/etc/nginx/allowed-hosts.conf
HOSTS="${AURUM_ALLOWED_HOSTS:-localhost 127.0.0.1}"
if [ "$HOSTS" = "*" ]; then
: > "$FRAGMENT"
echo "[aurum] Host header check disabled (AURUM_ALLOWED_HOSTS=*)." >&2
return 0 2>/dev/null || exit 0
fi
# "a,b c" -> "a|b|c", with regex metacharacters in each entry escaped so an
# IP's dots can't match any character.
PATTERN="$(printf '%s' "$HOSTS" \
| tr ',' ' ' \
| tr -s ' ' '\n' \
| sed '/^$/d' \
| sed 's/[].[^$*\/+?(){}|\\]/\\&/g' \
| paste -sd '|' -)"
cat > "$FRAGMENT" <<EOF
# Generated at container start by docker-entrypoint.d/25-allowed-hosts.sh.
# \$host is the Host header without the port, so entries here are bare
# hostnames. "if" is normally best avoided in nginx, but a bare "return"
# inside it is one of the two documented-safe uses.
if (\$host !~* '^(${PATTERN})\$') {
return 421 "Aurum: unexpected Host header '\$host'. Add it to AURUM_ALLOWED_HOSTS in .env (see .env.example), or set that variable to * to disable this check.\n";
}
EOF
echo "[aurum] Host header allow-list: ${HOSTS}" >&2