diff --git a/admin/Caddyfile b/admin/Caddyfile index 44ccd02..c9ede64 100644 --- a/admin/Caddyfile +++ b/admin/Caddyfile @@ -1,34 +1,97 @@ { email dead@cxap.space dns cloudflare {$CF_API_TOKEN} - order authorize before reverse_proxy + + order authenticate before respond + order authorize before respond + servers { trusted_proxies static private_ranges } + + certificates { + automate auth.realy.nothing.help + automate *.realy.nothing.help + } } -# Портал аутентификации -auth.realy.nothing.help { - route { - authp { - backends { - local_backend { - method local - path /data/users.json - } - } - ui { - links { - "Portainer" "https://port.realy.nothing.help" - } +security { + # Локальное хранилище пользователей + local identity store localdb { + realm local + path /data/users.json + } + + # Портал аутентификации (используем в site-блоке ниже) + authentication portal authportal { + # криптополитика и тайминги + crypto default token lifetime {$AUTH_TOKEN_LIFETIME:-12h} + enable identity store localdb + + # единый cookie для всех поддоменов realy.nothing.help + cookie domain realy.nothing.help + cookie lifetime {$AUTH_COOKIE_LIFETIME:-12h} + cookie idle_timeout {$AUTH_IDLE_TIMEOUT:-30m} + cookie samesite lax + + ui { + title "Account" + # можно добавить ярлыки на часто используемые сервисы + links { + "Portainer" "https://port.realy.nothing.help" + "WhoAmI" "/whoami" } } + + # Всем локальным пользователям — роль admin + требование MFA + transform user { + match origin local + action add role admin + # require mfa + } + # И базовая роль "user" всем аутентифицированным + transform user { + action add role user + } + } + + # Политика для API: либо admin-роль, либо API-ключи портала + authorization policy apikey_or_admin { + set auth url https://auth.realy.nothing.help + allow roles admin + with api key auth portal authportal realm local + + # Явные ACL с логами + acl rule { comment "Accept" match role admin allow stop log info } + acl rule { comment "Deny" match any deny log warn } } } -# Сниппет для защиты сервисов -(auth_portal_forwarder) { - authorize { - set auth url https://auth.realy.nothing.help +auth.realy.nothing.help { + # сам портал на отдельном хосте + route { + authenticate with authportal + } + + # Базовые security-заголовки + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + X-Content-Type-Options "nosniff" + X-Frame-Options "DENY" + Referrer-Policy "no-referrer" } } + +# Обычная «пользовательская» защита: просто требует валидную сессию, +# редиректит на портал, прокидывает клеймы в заголовки. +(auth_forward) { + authorize { + set auth url https://auth.realy.nothing.help + inject headers with claims + } +} + +# Защита API: только admin или по API-ключу (policy объявлена выше) +(authorize_apikey_or_admin) { + authorize with apikey_or_admin +} diff --git a/admin/bootstrap/README.MD b/admin/bootstrap/README.MD index 4bbd1f3..6acc1e7 100644 --- a/admin/bootstrap/README.MD +++ b/admin/bootstrap/README.MD @@ -26,6 +26,15 @@ caddy hash-password --plaintext 'секрет' ``` +Или на python: + +```bash +pip install bcrypt +``` +```bash +python -c "import bcrypt,sys; print(bcrypt.hashpw(sys.argv[1].encode(), bcrypt.gensalt(rounds=12)).decode())" 'секрет' +``` + После изменения файла нужно перезапустить контейнер `caddy`. ## Защита контейнеров @@ -33,8 +42,20 @@ caddy hash-password --plaintext 'секрет' Чтобы ограничить доступ к какому-либо сервису, добавьте к контейнеру метку: ``` -caddy.import: auth_portal_forwarder +caddy.import: auth_forward ``` и обычные метки для виртуального хоста и `reverse_proxy`. Пример для Portainer уже есть в `docker-compose.yml`. После авторизации пользователь возвращается к запрошенному сервису. + +### Открытие пути для API + +Чтобы открыть некий путь без авторизации, добавить лейблы: + +``` + caddy.handle_path: /api/* + caddy.handle_path.0_reverse_proxy: "{{upstreams 9000}}" +``` + +и не прописывать им `caddy.import: auth_forward`. +И наоборот, аналогично можно закрыть авторизацией только нужный путь. \ No newline at end of file diff --git a/admin/bootstrap/docker-compose.yml b/admin/bootstrap/docker-compose.yml index edd390d..183dfcd 100644 --- a/admin/bootstrap/docker-compose.yml +++ b/admin/bootstrap/docker-compose.yml @@ -33,8 +33,10 @@ services: networks: [proxy] labels: caddy: port.realy.nothing.help + caddy.handle_path: /api/* + caddy.handle_path.0_reverse_proxy: "{{upstreams 9000}}" caddy.encode: zstd gzip - caddy.import: auth_portal_forwarder + caddy.import: auth_forward caddy.reverse_proxy: "{{upstreams 9000}}" volumes: diff --git a/admin/bootstrap/users.json b/admin/bootstrap/users.json index 8dd6bc9..ff25a6d 100644 --- a/admin/bootstrap/users.json +++ b/admin/bootstrap/users.json @@ -3,8 +3,10 @@ { "username": "deadcxap", "email": "dead@cxap.space", - "password": "$2a$10$5x5iG8uDD/A.zxTCr14iUuLS1d8FgEiH8oi1de6pF2Nl/iZNDBEvG", - "roles": ["admin"] + "password": "$2b$12$s5SZcTu0THrdIHmdqTIuS.Hb89nokYfwF5xSDPSJPZsKoYnFkv61i", + "roles": [ + "admin" + ] } ] -} +} \ No newline at end of file