feat(ext-proxy): поддержка username/password для basic auth — хэш генерируется автоматически
Раньше требовалась готовая строка htpasswd -nb admin 'пароль'. Теперь достаточно указать username и password — Ansible автоматически вызывает openssl passwd -apr1 и записывает хэш в K8s Secret. Изменения: - defaults/main.yml: добавлены поля auth.username и auth.password - tasks/main.yml: Python-скрипт обрабатывает все прокси перед деплоем: вызывает openssl passwd -apr1, убирает открытый пароль из values, поддерживает глобальные defaults и per-proxy overrides - templates/values.yaml.j2: использует _ext_proxy_proxies_final и _ext_proxy_def_auth_final (с уже подставленными хэшами) - README.md: обновлена документация по basic auth — username/password основной вариант Приоритет: secretName > credentials > username+password
This commit is contained in:
@@ -108,7 +108,9 @@ ext_proxy_defaults:
|
||||
|
||||
auth:
|
||||
enabled: false # включить nginx basic authentication
|
||||
credentials: "" # строка htpasswd: htpasswd -nb admin 'пароль'
|
||||
username: "" # логин (пароль хэшируется автоматически)
|
||||
password: "" # пароль в открытом виде — задавать через vault!
|
||||
credentials: "" # готовая htpasswd-строка (приоритет над username/password)
|
||||
secretName: "" # использовать существующий Secret вместо генерации нового
|
||||
|
||||
websocket: true # включить WebSocket (заголовки HTTP/1.1 upgrade)
|
||||
@@ -148,8 +150,10 @@ ext_proxy_proxies:
|
||||
|
||||
auth:
|
||||
enabled: true
|
||||
credentials: "admin:$apr1$abc..." # строка htpasswd
|
||||
# ИЛИ: secretName: my-existing-auth-secret
|
||||
username: admin # логин — хэш генерируется автоматически
|
||||
password: "{{ vault_mypass }}" # пароль из vault (рекомендуется)
|
||||
# ИЛИ готовая строка: credentials: "admin:$apr1$..."
|
||||
# ИЛИ существующий Secret: secretName: my-existing-auth-secret
|
||||
|
||||
annotations: # аннотации уровня прокси (переопределяют всё)
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
@@ -207,21 +211,26 @@ cert-manager автоматически выпустит сертификат д
|
||||
|
||||
### Basic Authentication
|
||||
|
||||
Сгенерируй учётные данные htpasswd (установи `apache2-utils` или `httpd-tools`):
|
||||
|
||||
```bash
|
||||
htpasswd -nb admin 'мойсекретныйпароль'
|
||||
# выводит: admin:$apr1$Rh0Ycxl9$rPTH7gRHfMBkS.7.Q1BxM/
|
||||
```
|
||||
Просто укажи логин и пароль — хэш генерируется автоматически:
|
||||
|
||||
```yaml
|
||||
# group_vars/all/addons.yml
|
||||
|
||||
ext_proxy_defaults:
|
||||
auth:
|
||||
enabled: true
|
||||
credentials: "admin:$apr1$Rh0Ycxl9$rPTH7gRHfMBkS.7.Q1BxM/"
|
||||
username: admin
|
||||
password: "{{ vault_ext_proxy_password }}" # пароль из vault
|
||||
```
|
||||
|
||||
Или выборочно по конкретным прокси:
|
||||
```yaml
|
||||
# group_vars/all/vault.yml
|
||||
vault_ext_proxy_password: "мойсекретныйпароль"
|
||||
```
|
||||
|
||||
Ansible автоматически вызовет `openssl passwd -apr1` и запишет хэш в Kubernetes Secret. Пароль в открытом виде **не попадает** в Helm values, логи или конфиги.
|
||||
|
||||
Выборочно для конкретного прокси (остальные без auth):
|
||||
|
||||
```yaml
|
||||
ext_proxy_proxies:
|
||||
@@ -231,7 +240,8 @@ ext_proxy_proxies:
|
||||
port: 8080
|
||||
auth:
|
||||
enabled: true
|
||||
credentials: "admin:$apr1$..."
|
||||
username: admin
|
||||
password: "{{ vault_router_password }}"
|
||||
|
||||
- name: plex # без auth
|
||||
hosts: [plex.home.ru]
|
||||
@@ -239,7 +249,18 @@ ext_proxy_proxies:
|
||||
port: 32400
|
||||
```
|
||||
|
||||
Использование существующего Secret (ключ должен называться `auth`, тип `Opaque`):
|
||||
Разные пароли для разных прокси — каждый прокси создаёт свой отдельный Secret.
|
||||
|
||||
**Если уже есть готовая htpasswd-строка** (поле `credentials` имеет приоритет над `username`/`password`):
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
enabled: true
|
||||
credentials: "admin:$apr1$Rh0Ycxl9$rPTH7gRHfMBkS.7.Q1BxM/"
|
||||
# Сгенерировать вручную: htpasswd -nb admin 'пароль'
|
||||
```
|
||||
|
||||
**Использование существующего Secret** (ключ должен называться `auth`, тип `Opaque`):
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
@@ -247,6 +268,8 @@ auth:
|
||||
secretName: my-shared-htpasswd # создать вручную через kubectl
|
||||
```
|
||||
|
||||
**Приоритет:** `secretName` > `credentials` > `username` + `password`
|
||||
|
||||
> **Примечание:** `configuration-snippet` должен быть разрешён в ingress-nginx (`allow-snippet-annotations: true`), если добавляешь кастомные сниппеты. Стандартные аннотации работают без этого.
|
||||
|
||||
### WebSocket (Plex, Grafana, Home Assistant и т.д.)
|
||||
|
||||
@@ -15,8 +15,10 @@ ext_proxy_defaults:
|
||||
issuerKind: ClusterIssuer
|
||||
auth:
|
||||
enabled: false
|
||||
credentials: "" # htpasswd -nb user pass
|
||||
secretName: ""
|
||||
username: "" # логин — пароль хэшируется автоматически через openssl passwd -apr1
|
||||
password: "" # пароль в открытом виде (задай в vault.yml!)
|
||||
credentials: "" # готовая htpasswd-строка (если задана — username/password игнорируются)
|
||||
secretName: "" # использовать существующий Secret вместо генерации нового
|
||||
websocket: true
|
||||
path: /
|
||||
pathType: Prefix
|
||||
@@ -59,7 +61,10 @@ ext_proxy_defaults:
|
||||
# issuerKind: ClusterIssuer
|
||||
# auth:
|
||||
# enabled: true
|
||||
# credentials: "admin:$apr1$..." # htpasswd -nb admin password
|
||||
# username: admin # простой логин и пароль — хэш генерируется автоматически
|
||||
# password: "{{ vault_myapp_password }}"
|
||||
# # ИЛИ готовая htpasswd-строка:
|
||||
# # credentials: "admin:$apr1$..."
|
||||
# annotations:
|
||||
# nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
ext_proxy_proxies: []
|
||||
|
||||
@@ -42,6 +42,71 @@
|
||||
mode: preserve
|
||||
become: true
|
||||
|
||||
# ── Generate htpasswd hashes from plain username/password ────────────────────
|
||||
# Если auth.username + auth.password заданы — автоматически хэшируем через
|
||||
# openssl passwd -apr1, записываем в auth.credentials и убираем открытые поля.
|
||||
# Если auth.credentials уже задан — используем его без изменений.
|
||||
# Пароли в открытом виде нигде не попадают в Helm values или логи.
|
||||
|
||||
- name: Generate htpasswd credentials (username/password → apr1 hash)
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- python3
|
||||
- -c
|
||||
- |
|
||||
import json, subprocess, sys
|
||||
|
||||
proxies = json.loads(sys.argv[1])
|
||||
def_auth = json.loads(sys.argv[2])
|
||||
|
||||
def gen_credentials(auth, fallback):
|
||||
"""Return htpasswd string or '' if nothing to generate."""
|
||||
username = auth.get('username') or fallback.get('username', '')
|
||||
password = auth.get('password') or fallback.get('password', '')
|
||||
credentials = auth.get('credentials') or fallback.get('credentials', '')
|
||||
secret_name = auth.get('secretName') or fallback.get('secretName', '')
|
||||
if credentials or secret_name:
|
||||
return credentials # уже готово или внешний Secret
|
||||
if username and password:
|
||||
r = subprocess.run(
|
||||
['openssl', 'passwd', '-apr1', password],
|
||||
capture_output=True, text=True, check=True
|
||||
)
|
||||
return username + ':' + r.stdout.strip()
|
||||
return ''
|
||||
|
||||
# Обработать глобальный default auth
|
||||
cleaned_def_auth = {k: v for k, v in def_auth.items()
|
||||
if k not in ('username', 'password')}
|
||||
creds = gen_credentials(def_auth, {})
|
||||
if creds:
|
||||
cleaned_def_auth['credentials'] = creds
|
||||
|
||||
# Обработать каждый прокси
|
||||
for proxy in proxies:
|
||||
auth = dict(proxy.get('auth') or {})
|
||||
enabled = auth.get('enabled', def_auth.get('enabled', False))
|
||||
if enabled:
|
||||
creds = gen_credentials(auth, def_auth)
|
||||
if creds:
|
||||
auth['credentials'] = creds
|
||||
# Убрать открытый текст из итоговых values
|
||||
auth.pop('username', None)
|
||||
auth.pop('password', None)
|
||||
proxy['auth'] = auth
|
||||
|
||||
print(json.dumps({'proxies': proxies, 'def_auth': cleaned_def_auth}))
|
||||
- "{{ ext_proxy_proxies | to_json }}"
|
||||
- "{{ ext_proxy_defaults.auth | to_json }}"
|
||||
register: _auth_processed
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Set final proxies and defaults with generated credentials
|
||||
ansible.builtin.set_fact:
|
||||
_ext_proxy_proxies_final: "{{ (_auth_processed.stdout | from_json).proxies }}"
|
||||
_ext_proxy_def_auth_final: "{{ (_auth_processed.stdout | from_json).def_auth }}"
|
||||
|
||||
# ── Template Helm values ──────────────────────────────────────────────────────
|
||||
|
||||
- name: Template Helm values
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Generated by Ansible — do not edit manually.
|
||||
# Configure via: group_vars/all/addons.yml → ext_proxy_* variables.
|
||||
# Note: auth.username/password are resolved to htpasswd hashes before this file is written.
|
||||
|
||||
defaults:
|
||||
{{ ext_proxy_defaults | to_yaml | indent(2, True) }}
|
||||
{{ (ext_proxy_defaults | combine({'auth': _ext_proxy_def_auth_final})) | to_yaml | indent(2, True) }}
|
||||
|
||||
proxies:
|
||||
{{ ext_proxy_proxies | to_yaml | indent(2, True) }}
|
||||
{{ _ext_proxy_proxies_final | to_yaml | indent(2, True) }}
|
||||
|
||||
Reference in New Issue
Block a user