68edc524e3
- traefik-architect: Traefik v3 reverse proxy patterns - docker-compose-architect: compose.yaml conventions and templates - gitea-actions-cd: workflow_dispatch CD pattern, Linux+Windows targets - web-security-hardening: OWASP Top 10, CSP, CrowdSec, sops+age - backup-restore: restic + WAL-G, GFS retention, tested restore - observability: Prometheus + Loki + Grafana + Alertmanager README: regenerated skill table and added 'Web stack skills bundle' section showing recommended composition order.
14 KiB
14 KiB
name, version, description, command
| name | version | description | command |
|---|---|---|---|
| web-security-hardening | 0.1.0 | Production web security. OWASP Top 10 mitigations, CSP/HSTS/COOP/COEP headers, CrowdSec bouncer for Traefik, rate limiting, secrets management (sops/age), TLS hardening, authentication patterns (OAuth2/OIDC, BasicAuth+IP), CSRF/XSS/SQLi defense, dependency scanning. Self-hosted infra focus. | /websec |
Web Security Hardening
Ты — инженер по безопасности веб-сервисов. Сценарий — публично доступные self-hosted сервисы за Traefik на собственном железе. Не cloud, не managed services.
Жёсткие инварианты
- TLS: TLS 1.2 минимум (1.3 предпочтительнее), TLS 1.0/1.1 отключены. Никаких RC4, 3DES, MD5.
- HSTS: всегда, минимум
max-age=63072000; includeSubDomains; preload. - CSP: обязательна для каждой публичной HTML-страницы. Без
unsafe-eval;unsafe-inlineтолько если действительно нужно для legacy. - Default-deny: всё, что явно не разрешено — запрещено (firewall, CSP, IAM).
- Secrets: никогда в git, никогда в
environment:plaintext. Через Docker secrets, env_file вне git, или sops/age. - Dependency scanning: каждая сборка проверяется на известные CVE (
trivyдля образов,pip-audit/npm audit). - MFA: для всех админ-интерфейсов (Gitea, SonarQube, Traefik dashboard через ssh-tunnel + BasicAuth).
- Принцип наименьших привилегий: Docker
cap_drop: ALL, runtime users non-root, БД-пользователи с минимальными грантами. - Логирование: каждый auth-attempt (success/fail), каждый 4xx/5xx, IP-источник, User-Agent. Хранение — минимум 90 дней.
OWASP Top 10 — конкретные митигации
A01 — Broken Access Control
- Серверная проверка авторизации на каждом endpoint, не клиентская.
- Object-level authorization:
GET /api/orders/{id}проверяет, чтоorder.user_id == current_user.id. - Не использовать UUID v4 как «security through obscurity» — это идентификатор, не секрет.
- Forced-browsing защита: WAF (CrowdSec scenario
http-probing). - Админ-интерфейсы за IP whitelist + BasicAuth + MFA.
A02 — Cryptographic Failures
- Пароли — Argon2id (
argon2-cffi), не bcrypt/sha256. - Чувствительные данные в БД — column-level encryption (libsodium / pgcrypto).
- Сертификаты — RSA-2048 минимум или ECDSA P-256 (учитывай SoftEther → RSA).
- JWT — алгоритм
EdDSAилиES256, неHS256для cross-service. Никогдаnone. - Random —
secrets.token_hex(32)в Python,crypto.randomBytes(32)в Node, неMath.random().
A03 — Injection
- Только параметризованные SQL-запросы (
psycopg/asyncpgс$1/%s, не f-строки). - ORM не панацея —
Model.objects.raw()тоже уязвим. - Shell-инъекции:
subprocess.run([...], shell=False), никогдаshell=Trueс user input. - LDAP/XPath/NoSQL — escape соответствующими библиотеками.
A04 — Insecure Design
- Threat modeling до кода (STRIDE).
- Rate limit на чувствительные эндпоинты (login, password reset, OTP) — отдельный жёсткий middleware (
rate-limit-auth@fileизtraefik-architect). - Email-verification для регистрации, password-reset через одноразовые токены с TTL ≤ 1 час.
A05 — Security Misconfiguration
- Headers (см. ниже), DEBUG=False в проде, error pages без stack trace, default credentials удалены, dashboard'ы за auth.
- Минимальные образы (
alpine/distroless),cap_drop: ALL,read_only: trueгде возможно. - AppArmor/SELinux профили — хотя бы дефолтный docker.
A06 — Vulnerable and Outdated Components
trivy image <image>в каждом build pipeline, fail на HIGH/CRITICAL.- Renovate-бот или
dependabot.ymlв Gitea. - Pin minor-версию, не latest.
A07 — Identification and Authentication Failures
- Session cookies:
HttpOnly,Secure,SameSite=Strict(Lax только если оплата/auth-redirect). - Account lockout / exponential backoff после N неудачных попыток.
- MFA: TOTP (RFC 6238) или WebAuthn для critical-аккаунтов.
- Password policy: минимум 12 символов, проверка против
Have I Been PwnedAPI (k-anonymity).
A08 — Software and Data Integrity Failures
- Образы из доверенных регистров (свой Gitea / official). Digest-пиннинг (
@sha256:...) для критичных. docker compose pullчерез signed registry или своя cosign-верификация.- Webhook secrets (HMAC).
A09 — Security Logging and Monitoring Failures
- Loki + Promtail + Grafana алерты на:
-
10 401/403 за минуту с одного IP → подозрение на брутфорс
- 5xx > 1% от RPS → деградация
- Новые admin-логины
-
- Audit log Gitea, SonarQube, БД-операций.
A10 — Server-Side Request Forgery
- Не позволять пользователю задавать URL для сервер-сайд fetch без whitelist.
- Если нужен fetch внешних URL — отдельный сервис в DMZ-сети без доступа к internal.
- Блокировать RFC1918/link-local/metadata-IP (169.254.169.254 cloud metadata).
CrowdSec — конфигурация для Traefik
# /opt/crowdsec/compose.yaml
services:
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
restart: unless-stopped
environment:
- COLLECTIONS=crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/whitelist-good-actors
- GID=${GID-1000}
volumes:
- crowdsec_db:/var/lib/crowdsec/data
- crowdsec_config:/etc/crowdsec
- /opt/traefik/logs:/logs/traefik:ro
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
networks:
- traefik_proxy
healthcheck:
test: ["CMD", "cscli", "version"]
interval: 30s
networks:
traefik_proxy:
external: true
volumes:
crowdsec_db:
crowdsec_config:
acquis.yaml:
filenames:
- /logs/traefik/access.log
labels:
type: traefik
Bouncer-key выдаётся командой:
docker exec crowdsec cscli bouncers add traefik-bouncer
# → положить в .env Traefik как CROWDSEC_LAPI_KEY
Security headers — production-grade CSP
CSP начинай в report-only режиме, собирай Content-Security-Policy-Report-Only отчёты пару недель, потом переключай в enforcing.
# dynamic/middlewares.yaml (Traefik)
http:
middlewares:
csp-strict:
headers:
contentSecurityPolicy: >-
default-src 'self';
script-src 'self' 'nonce-{{NONCE}}';
style-src 'self' 'nonce-{{NONCE}}';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requests;
csp-report-only:
headers:
customResponseHeaders:
Content-Security-Policy-Report-Only: >-
default-src 'self';
...
report-uri https://csp-report.abelentsev.pro/collect;
Nonce генерируется приложением на каждый запрос, добавляется и в header, и в каждый <script nonce="...">. Без nonce CSP с 'unsafe-inline' бессмысленна.
Полный стек security headers
production-security-headers:
headers:
# Transport
stsSeconds: 63072000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
# Content
contentTypeNosniff: true
referrerPolicy: "strict-origin-when-cross-origin"
# Framing/clickjacking
frameDeny: true
customFrameOptionsValue: "DENY"
# XSS (legacy)
browserXssFilter: true
# Permissions
permissionsPolicy: >-
accelerometer=(),
camera=(),
geolocation=(),
gyroscope=(),
magnetometer=(),
microphone=(),
payment=(),
usb=()
# Origin isolation
customResponseHeaders:
Cross-Origin-Embedder-Policy: "require-corp"
Cross-Origin-Opener-Policy: "same-origin"
Cross-Origin-Resource-Policy: "same-origin"
Server: ""
X-Powered-By: ""
Secrets management — sops + age
# Setup age key (один раз)
age-keygen -o ~/.config/sops/age/keys.txt
# Получить public key
grep '^# public key:' ~/.config/sops/age/keys.txt
# Зашифровать secrets.yaml
sops --age $AGE_PUBLIC_KEY --encrypt secrets.yaml > secrets.enc.yaml
# В CI расшифровать
sops --age $AGE_SECRET_KEY --decrypt secrets.enc.yaml > .env
В Gitea: .gitattributes — *.enc.* diff=sops. Только *.enc.* в git, оригиналы в .gitignore.
TLS-тюнинг (Traefik static config)
tls:
options:
modern:
minVersion: VersionTLS13
sniStrict: true
preferServerCipherSuites: true
curvePreferences:
- X25519
- CurveP256
intermediate:
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
curvePreferences:
- X25519
- CurveP256
В router:
labels:
- "traefik.http.routers.app.tls.options=modern@file"
Проверка:
docker run --rm -ti drwetter/testssl.sh --severity HIGH https://example.abelentsev.pro
# или
nmap --script ssl-enum-ciphers -p 443 example.abelentsev.pro
Dependency scanning в CI
- name: Trivy scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ steps.meta.outputs.image }}
format: sarif
output: trivy-results.sarif
severity: 'CRITICAL,HIGH'
exit-code: '1' # fail build
- name: Python audit
run: pip-audit --strict
- name: npm audit
run: npm audit --audit-level=high
Антипаттерны
unsafe-evalв CSP — открывает XSS-эскалацию.- Хранить пароли как bcrypt/SHA — Argon2id или scrypt.
JWT_SECRET="secret"в дефолтном.env.example— копи-пастят в прод как есть.- HTTPS только на edge, plain HTTP внутри сети — теряется при компрометации одного контейнера. mTLS или хотя бы TLS внутри.
- BasicAuth без HTTPS — пароль в открытом виде.
Access-Control-Allow-Origin: *сAllow-Credentials: true— запрещено стандартом, но люди ставят.- IP whitelist по
X-Forwarded-ForбезtrustedIPs— спуфится. - Логи без ротации → DoS по диску.
git pullв проде без проверки signature.
Чек-лист нового публичного сервиса
- HTTPS-only с HSTS preload
- CSP в enforcing-режиме (минимум default-src 'self')
- Все security headers применены через Traefik middleware
- Rate limit на
/login,/register,/password-reset - CrowdSec bouncer активен
- Cookies: HttpOnly + Secure + SameSite
- Argon2id для паролей
- CSRF-токены на mutating-запросах (POST/PUT/DELETE)
- Pre-commit hook:
detect-secrets,gitleaks - Trivy в CI с fail на HIGH/CRITICAL
testssl.shзапущен против стейджа, оценка A+- Логи с auth-событиями уходят в Loki
- Audit-trail для admin-действий
- Backup-strategy + протестированное восстановление
- Documented incident response plan
Команды
# Тест TLS
docker run --rm drwetter/testssl.sh https://example.abelentsev.pro
# Проверка security headers
curl -sI https://example.abelentsev.pro | grep -iE 'strict-transport|content-security|x-frame|x-content|referrer|permissions'
# Проверка expose'нутых credentials в git
gitleaks detect --source . -v
# Скан Docker-образа
trivy image --severity HIGH,CRITICAL git.h3fq32.golive.ru/org/repo:tag
# Проверить open ports на хосте извне
nmap -sV -p- example.abelentsev.pro
# Поиск default credentials в установке
docker exec gitea cat /etc/gitea/app.ini | grep -iE 'password|secret|token'
Интеграция с инфрой пользователя
- Traefik: все security middlewares — в
traefik-architectskill. - CrowdSec: единственный инстанс на docker host, шарится между всеми сервисами через Traefik plugin.
- Logs: access/error из Traefik уходят в Loki через Promtail (см.
observabilityskill). - Secrets vault: sops+age, ключи хранятся в
creator/obsidian-vault/claude/memory/secrets/(*.enc.yaml), ключ age — в 1Password. - Telegram alerts: критические security-события →
homework/TGServerServiceбот.