feat: добавить аддон ingress-add-domains — добавить домены к сервисам кластера
Helm chart + Ansible роль, создающие Ingress-only правила для уже существующих K8s сервисов. Каждый Ingress создаётся в namespace целевого сервиса. Поддерживает: TLS (cert-manager или готовый Secret), basic auth (автохэш пароля через openssl passwd -apr1), WebSocket, несколько хостов, per-entry аннотации.
This commit is contained in:
214
addons/ingress-add-domains/README.md
Normal file
214
addons/ingress-add-domains/README.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# ingress-add-domains
|
||||
|
||||
Добавляет дополнительные домены к уже существующим сервисам внутри кластера.
|
||||
|
||||
**Не создаёт** Service или Endpoints — только Ingress, указывающий на готовый K8s Service.
|
||||
Каждый Ingress создаётся **в namespace целевого сервиса**, что является требованием Kubernetes.
|
||||
|
||||
Поддерживает:
|
||||
- Внутренние домены (`*.local`) без TLS
|
||||
- Внешние домены (`*.home.ru`) с TLS и cert-manager
|
||||
- Basic auth (логин/пароль — хэш генерируется автоматически)
|
||||
- WebSocket
|
||||
- Per-entry переопределение любых параметров
|
||||
|
||||
## Когда использовать
|
||||
|
||||
| Сценарий | Инструмент |
|
||||
|---|---|
|
||||
| Открыть сервис кластера по новому домену | **ingress-add-domains** |
|
||||
| Проксировать сервис вне кластера (по IP) | [ingress-proxypass](../ingress-proxypass/README.md) |
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
```yaml
|
||||
# group_vars/all/addons.yml
|
||||
addon_ingress_add_domains: true
|
||||
|
||||
ingress_add_domains_entries:
|
||||
# Внутренний домен без TLS
|
||||
- name: gitea-local
|
||||
hosts: [gitea.local]
|
||||
service:
|
||||
name: gitea-http
|
||||
namespace: gitea
|
||||
port: 3000
|
||||
|
||||
# Внешний домен с TLS + basic auth
|
||||
- name: gitea-home
|
||||
hosts: [gitea.home.ru]
|
||||
service:
|
||||
name: gitea-http
|
||||
namespace: gitea
|
||||
port: 3000
|
||||
tls:
|
||||
enabled: true
|
||||
certManager:
|
||||
enabled: true
|
||||
issuer: letsencrypt-prod
|
||||
issuerKind: ClusterIssuer
|
||||
auth:
|
||||
enabled: true
|
||||
username: admin
|
||||
password: "{{ vault_gitea_proxy_password }}"
|
||||
```
|
||||
|
||||
```bash
|
||||
make addon-ingress-add-domains
|
||||
```
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### Переменные Ansible
|
||||
|
||||
| Переменная | По умолчанию | Описание |
|
||||
|---|---|---|
|
||||
| `ingress_add_domains_release_name` | `ingress-add-domains` | Имя Helm release |
|
||||
| `ingress_add_domains_release_namespace` | `ingress-add-domains` | Namespace для Helm-метаданных |
|
||||
| `ingress_add_domains_defaults` | см. ниже | Глобальные умолчания |
|
||||
| `ingress_add_domains_entries` | `[]` | Список записей |
|
||||
|
||||
### Поля entry
|
||||
|
||||
| Поле | Обязательно | Описание |
|
||||
|---|---|---|
|
||||
| `name` | да | Уникальное имя (становится именем Ingress-ресурса) |
|
||||
| `hosts` | да | Список доменов |
|
||||
| `service.name` | да | Имя существующего K8s Service |
|
||||
| `service.namespace` | да | Namespace сервиса (Ingress создаётся в этом namespace) |
|
||||
| `service.port` | да | Порт сервиса |
|
||||
| `tls` | нет | Настройки TLS (см. ниже) |
|
||||
| `auth` | нет | Basic auth (см. ниже) |
|
||||
| `websocket` | нет | Включить поддержку WebSocket (`true`/`false`) |
|
||||
| `path` | нет | URL-путь (по умолчанию `/`) |
|
||||
| `pathType` | нет | `Prefix` или `Exact` (по умолчанию `Prefix`) |
|
||||
| `ingressClass` | нет | Класс ingress (по умолчанию `nginx`) |
|
||||
| `annotations` | нет | Дополнительные аннотации (перекрывают всё) |
|
||||
|
||||
### TLS
|
||||
|
||||
```yaml
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: wildcard-tls # использовать готовый Secret (опционально)
|
||||
certManager:
|
||||
enabled: true # автоматически создать Certificate
|
||||
issuer: letsencrypt-prod
|
||||
issuerKind: ClusterIssuer # или Issuer
|
||||
```
|
||||
|
||||
### Basic auth
|
||||
|
||||
```yaml
|
||||
auth:
|
||||
enabled: true
|
||||
username: admin
|
||||
password: "{{ vault_password }}" # хэш генерируется автоматически
|
||||
|
||||
# ИЛИ готовая htpasswd-строка:
|
||||
# credentials: "admin:$apr1$..."
|
||||
|
||||
# ИЛИ использовать существующий Secret:
|
||||
# secretName: my-auth-secret
|
||||
```
|
||||
|
||||
Если задан `username` + `password` — Ansible автоматически хэширует пароль через
|
||||
`openssl passwd -apr1` и создаёт K8s Secret. Открытый пароль не попадает в Helm values.
|
||||
|
||||
### Глобальные умолчания
|
||||
|
||||
```yaml
|
||||
ingress_add_domains_defaults:
|
||||
ingressClass: nginx
|
||||
tls:
|
||||
enabled: false
|
||||
certManager:
|
||||
enabled: false
|
||||
issuer: ""
|
||||
issuerKind: ClusterIssuer
|
||||
auth:
|
||||
enabled: false
|
||||
websocket: false
|
||||
path: /
|
||||
pathType: Prefix
|
||||
annotations: {}
|
||||
```
|
||||
|
||||
Любое поле defaults можно переопределить на уровне entry.
|
||||
|
||||
## Примеры
|
||||
|
||||
### Несколько доменов для одного сервиса
|
||||
|
||||
```yaml
|
||||
ingress_add_domains_entries:
|
||||
- name: nextcloud-all-domains
|
||||
hosts:
|
||||
- nextcloud.local
|
||||
- cloud.local
|
||||
- nextcloud.home.ru
|
||||
service:
|
||||
name: nextcloud
|
||||
namespace: nextcloud
|
||||
port: 8080
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: wildcard-home-tls
|
||||
```
|
||||
|
||||
### Wildcard TLS Secret для всех записей
|
||||
|
||||
```yaml
|
||||
ingress_add_domains_defaults:
|
||||
tls:
|
||||
enabled: true
|
||||
secretName: wildcard-home-tls
|
||||
|
||||
ingress_add_domains_entries:
|
||||
- name: gitea-home
|
||||
hosts: [gitea.home.ru]
|
||||
service: {name: gitea-http, namespace: gitea, port: 3000}
|
||||
|
||||
- name: harbor-home
|
||||
hosts: [harbor.home.ru]
|
||||
service: {name: harbor-core, namespace: harbor, port: 80}
|
||||
tls:
|
||||
secretName: harbor-specific-tls # перекрывает default
|
||||
```
|
||||
|
||||
### Basic auth для всех записей
|
||||
|
||||
```yaml
|
||||
ingress_add_domains_defaults:
|
||||
auth:
|
||||
enabled: true
|
||||
username: admin
|
||||
password: "{{ vault_proxy_password }}"
|
||||
|
||||
ingress_add_domains_entries:
|
||||
- name: argocd-home
|
||||
hosts: [argocd.home.ru]
|
||||
service: {name: argocd-server, namespace: argocd, port: 80}
|
||||
|
||||
- name: grafana-home
|
||||
hosts: [grafana.home.ru]
|
||||
service: {name: prometheus-stack-grafana, namespace: monitoring, port: 80}
|
||||
auth:
|
||||
enabled: false # отключить auth для конкретной записи
|
||||
```
|
||||
|
||||
## Диагностика
|
||||
|
||||
```bash
|
||||
# Посмотреть все созданные Ingresses
|
||||
kubectl get ingress -A -l app.kubernetes.io/instance=ingress-add-domains
|
||||
|
||||
# Детали конкретного Ingress
|
||||
kubectl describe ingress <name> -n <namespace>
|
||||
|
||||
# Логи ingress-nginx
|
||||
kubectl -n ingress-nginx logs -l app.kubernetes.io/name=ingress-nginx --tail=50
|
||||
|
||||
# Проверить auth Secret
|
||||
kubectl -n <namespace> get secret <name>-auth
|
||||
```
|
||||
7
addons/ingress-add-domains/playbook.yml
Normal file
7
addons/ingress-add-domains/playbook.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Install ingress-add-domains
|
||||
hosts: k3s_master[0]
|
||||
gather_facts: false
|
||||
become: true
|
||||
roles:
|
||||
- role: "{{ playbook_dir }}/role"
|
||||
15
addons/ingress-add-domains/role/chart/Chart.yaml
Normal file
15
addons/ingress-add-domains/role/chart/Chart.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v2
|
||||
name: ingress-add-domains
|
||||
description: |
|
||||
Добавляет дополнительные домены (Ingress) к уже существующим сервисам внутри кластера.
|
||||
Не создаёт Service/Endpoints — только Ingress, указывающий на готовый K8s Service.
|
||||
Каждый Ingress создаётся в namespace целевого сервиса.
|
||||
Поддерживает: TLS (готовый Secret или cert-manager), basic auth, WebSocket, несколько хостов.
|
||||
type: application
|
||||
version: 1.0.0
|
||||
appVersion: "1.0.0"
|
||||
keywords:
|
||||
- ingress
|
||||
- domains
|
||||
- nginx
|
||||
home: https://git.antropoff.ru/DevOpsTools/K3S
|
||||
12
addons/ingress-add-domains/role/chart/templates/NOTES.txt
Normal file
12
addons/ingress-add-domains/role/chart/templates/NOTES.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
ingress-add-domains deployed successfully.
|
||||
|
||||
Ingresses created (each in the namespace of its target Service):
|
||||
{{- range .Values.entries }}
|
||||
{{- $svc := .service }}
|
||||
• {{ include "ingress-add-domains.resourceName" .name }} (namespace: {{ $svc.namespace }})
|
||||
hosts: {{ .hosts | default (list (.host | default "")) | join ", " }}
|
||||
→ {{ $svc.name }}:{{ $svc.port }}
|
||||
{{- end }}
|
||||
|
||||
Verify:
|
||||
kubectl get ingress -A -l app.kubernetes.io/instance={{ .Release.Name }}
|
||||
24
addons/ingress-add-domains/role/chart/templates/_helpers.tpl
Normal file
24
addons/ingress-add-domains/role/chart/templates/_helpers.tpl
Normal file
@@ -0,0 +1,24 @@
|
||||
{{/*
|
||||
Normalize an entry name to a safe Kubernetes resource name.
|
||||
Usage: {{ include "ingress-add-domains.resourceName" "my_service.name" }}
|
||||
*/}}
|
||||
{{- define "ingress-add-domains.resourceName" -}}
|
||||
{{- . | lower | replace "_" "-" | replace "." "-" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Chart label string.
|
||||
*/}}
|
||||
{{- define "ingress-add-domains.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels applied to all resources.
|
||||
*/}}
|
||||
{{- define "ingress-add-domains.labels" -}}
|
||||
helm.sh/chart: {{ include "ingress-add-domains.chart" . }}
|
||||
app.kubernetes.io/name: {{ default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
122
addons/ingress-add-domains/role/chart/templates/ingress.yaml
Normal file
122
addons/ingress-add-domains/role/chart/templates/ingress.yaml
Normal file
@@ -0,0 +1,122 @@
|
||||
{{/*
|
||||
Creates one Ingress per entry, placed in the SAME namespace as the target Service.
|
||||
metadata.namespace is set explicitly so each Ingress lives next to its backend.
|
||||
|
||||
Feature resolution order (highest → lowest priority):
|
||||
1. Per-entry annotations (.entries[*].annotations) — override everything
|
||||
2. Per-entry feature flags (tls, auth, websocket, …)
|
||||
3. Global defaults (.defaults.*)
|
||||
4. Built-in generated annotations (ingressClass, auth secret pointer)
|
||||
*/}}
|
||||
{{- range .Values.entries }}
|
||||
{{- $entry := . }}
|
||||
{{- $d := $.Values.defaults }}
|
||||
{{- $entryName := include "ingress-add-domains.resourceName" $entry.name }}
|
||||
{{- $svc := $entry.service }}
|
||||
|
||||
{{/* ── Resolve settings with fallback to defaults ─────────────────────────── */}}
|
||||
{{- $ingressClass := $entry.ingressClass | default $d.ingressClass | default "nginx" }}
|
||||
{{- $path := $entry.path | default $d.path | default "/" }}
|
||||
{{- $pathType := $entry.pathType | default $d.pathType | default "Prefix" }}
|
||||
|
||||
{{/* websocket: nil-safe three-way check */}}
|
||||
{{- $websocket := false }}
|
||||
{{- if ne ($entry.websocket | toString) "<nil>" }}
|
||||
{{- $websocket = $entry.websocket }}
|
||||
{{- else if ne ($d.websocket | toString) "<nil>" }}
|
||||
{{- $websocket = $d.websocket }}
|
||||
{{- end }}
|
||||
|
||||
{{/* ── TLS ──────────────────────────────────────────────────────────────────── */}}
|
||||
{{- $entryTLS := $entry.tls | default dict }}
|
||||
{{- $defTLS := $d.tls | default dict }}
|
||||
{{- $entryCM := $entryTLS.certManager | default dict }}
|
||||
{{- $defCM := $defTLS.certManager | default dict }}
|
||||
{{- $tlsEnabled := $entryTLS.enabled | default $defTLS.enabled | default false }}
|
||||
{{- $tlsSecret := $entryTLS.secretName | default $defTLS.secretName | default "" }}
|
||||
{{- $cmEnabled := $entryCM.enabled | default $defCM.enabled | default false }}
|
||||
{{- $cmIssuer := $entryCM.issuer | default $defCM.issuer | default "" }}
|
||||
{{- $cmKind := $entryCM.issuerKind | default $defCM.issuerKind | default "ClusterIssuer" }}
|
||||
|
||||
{{/* ── Auth ─────────────────────────────────────────────────────────────────── */}}
|
||||
{{- $entryAuth := $entry.auth | default dict }}
|
||||
{{- $defAuth := $d.auth | default dict }}
|
||||
{{- $authEnabled := $entryAuth.enabled | default $defAuth.enabled | default false }}
|
||||
{{- $authSecret := "" }}
|
||||
{{- if $authEnabled }}
|
||||
{{- $authSecret = $entryAuth.secretName | default $defAuth.secretName | default (printf "%s-auth" $entryName) }}
|
||||
{{- end }}
|
||||
|
||||
{{/* ── Hosts ────────────────────────────────────────────────────────────────── */}}
|
||||
{{- $hosts := $entry.hosts | default (list ($entry.host | default "")) }}
|
||||
|
||||
{{/* ── Build annotation dict ───────────────────────────────────────────────── */}}
|
||||
{{- $ann := dict }}
|
||||
|
||||
{{/* Step 1: global default annotations (lowest priority) */}}
|
||||
{{- range $k, $v := ($d.annotations | default dict) }}
|
||||
{{- $_ := set $ann $k ($v | toString) }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Step 2: WebSocket */}}
|
||||
{{- if $websocket }}
|
||||
{{- $_ := set $ann "nginx.ingress.kubernetes.io/proxy-http-version" "1.1" }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Step 3: basic auth */}}
|
||||
{{- if $authEnabled }}
|
||||
{{- $_ := set $ann "nginx.ingress.kubernetes.io/auth-type" "basic" }}
|
||||
{{- $_ := set $ann "nginx.ingress.kubernetes.io/auth-secret" $authSecret }}
|
||||
{{- $_ := set $ann "nginx.ingress.kubernetes.io/auth-realm" "Authentication Required" }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Step 4: cert-manager */}}
|
||||
{{- if $cmEnabled }}
|
||||
{{- $cmAnnotationKey := printf "cert-manager.io/%s" ($cmKind | lower) }}
|
||||
{{- $_ := set $ann $cmAnnotationKey $cmIssuer }}
|
||||
{{- end }}
|
||||
|
||||
{{/* Step 5: per-entry custom annotations override everything above */}}
|
||||
{{- range $k, $v := ($entry.annotations | default dict) }}
|
||||
{{- $_ := set $ann $k ($v | toString) }}
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ $entryName }}
|
||||
namespace: {{ $svc.namespace }}
|
||||
labels:
|
||||
{{- include "ingress-add-domains.labels" $ | nindent 4 }}
|
||||
app.kubernetes.io/component: {{ $entryName }}
|
||||
{{- if $ann }}
|
||||
annotations:
|
||||
{{- toYaml $ann | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
ingressClassName: {{ $ingressClass }}
|
||||
{{- if $tlsEnabled }}
|
||||
tls:
|
||||
- hosts:
|
||||
{{- range $hosts }}
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- if $tlsSecret }}
|
||||
secretName: {{ $tlsSecret | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
rules:
|
||||
{{- range $hosts }}
|
||||
- host: {{ . | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: {{ $path | quote }}
|
||||
pathType: {{ $pathType }}
|
||||
backend:
|
||||
service:
|
||||
name: {{ $svc.name }}
|
||||
port:
|
||||
number: {{ $svc.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,38 @@
|
||||
{{/*
|
||||
Creates a basic-auth Secret for each entry that has:
|
||||
auth.enabled: true
|
||||
auth.credentials: "<user:$apr1$...>" (no auth.secretName — use existing instead)
|
||||
|
||||
The Secret is created in the SAME namespace as the target Service so that
|
||||
nginx can read it (Ingress and Secret must be in the same namespace).
|
||||
|
||||
The Secret key MUST be "auth" for nginx's auth-file type.
|
||||
*/}}
|
||||
{{- range .Values.entries }}
|
||||
{{- $entry := . }}
|
||||
{{- $d := $.Values.defaults }}
|
||||
{{- $entryName := include "ingress-add-domains.resourceName" $entry.name }}
|
||||
{{- $svc := $entry.service }}
|
||||
|
||||
{{- $entryAuth := $entry.auth | default dict }}
|
||||
{{- $defAuth := $d.auth | default dict }}
|
||||
{{- $authEnabled := $entryAuth.enabled | default $defAuth.enabled | default false }}
|
||||
{{- $existingSec := $entryAuth.secretName | default $defAuth.secretName | default "" }}
|
||||
{{- $credentials := $entryAuth.credentials | default $defAuth.credentials | default "" }}
|
||||
|
||||
{{/* Only create a Secret when auth is on, no existing secret is referenced, and credentials are provided */}}
|
||||
{{- if and $authEnabled (not $existingSec) $credentials }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ $entryName }}-auth
|
||||
namespace: {{ $svc.namespace }}
|
||||
labels:
|
||||
{{- include "ingress-add-domains.labels" $ | nindent 4 }}
|
||||
app.kubernetes.io/component: {{ $entryName }}
|
||||
type: Opaque
|
||||
data:
|
||||
auth: {{ $credentials | b64enc | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
23
addons/ingress-add-domains/role/chart/values.yaml
Normal file
23
addons/ingress-add-domains/role/chart/values.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
# Default values — override via group_vars/all/addons.yml → ingress_add_domains_*
|
||||
|
||||
defaults:
|
||||
ingressClass: nginx
|
||||
tls:
|
||||
enabled: false
|
||||
secretName: ""
|
||||
certManager:
|
||||
enabled: false
|
||||
issuer: ""
|
||||
issuerKind: ClusterIssuer
|
||||
auth:
|
||||
enabled: false
|
||||
credentials: "" # htpasswd string: user:$apr1$...
|
||||
secretName: "" # use existing Secret instead
|
||||
websocket: false
|
||||
path: /
|
||||
pathType: Prefix
|
||||
annotations: {}
|
||||
|
||||
# Each entry creates one Ingress in the same namespace as the target Service.
|
||||
# See README.md for full field reference.
|
||||
entries: []
|
||||
61
addons/ingress-add-domains/role/defaults/main.yml
Normal file
61
addons/ingress-add-domains/role/defaults/main.yml
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
# ─── Helm release ─────────────────────────────────────────────────────────────
|
||||
ingress_add_domains_release_name: "ingress-add-domains"
|
||||
ingress_add_domains_release_namespace: "ingress-add-domains"
|
||||
|
||||
# ─── Global defaults (mirror of chart values.defaults) ────────────────────────
|
||||
ingress_add_domains_defaults:
|
||||
ingressClass: nginx
|
||||
tls:
|
||||
enabled: false
|
||||
secretName: ""
|
||||
certManager:
|
||||
enabled: false
|
||||
issuer: ""
|
||||
issuerKind: ClusterIssuer
|
||||
auth:
|
||||
enabled: false
|
||||
username: "" # логин — хэшируется автоматически через openssl passwd -apr1
|
||||
password: "" # пароль в открытом виде (задай в vault.yml!)
|
||||
credentials: "" # готовая htpasswd-строка (если задана — username/password игнорируются)
|
||||
secretName: "" # использовать существующий Secret вместо генерации нового
|
||||
websocket: false
|
||||
path: /
|
||||
pathType: Prefix
|
||||
annotations: {}
|
||||
|
||||
# ─── Entry definitions ────────────────────────────────────────────────────────
|
||||
# Каждая запись создаёт один Ingress в namespace целевого сервиса.
|
||||
# Все поля поддерживают per-entry переопределение ingress_add_domains_defaults.
|
||||
#
|
||||
# Минимальный пример (*.local без TLS):
|
||||
# ingress_add_domains_entries:
|
||||
# - name: gitea-local
|
||||
# hosts: [gitea.local]
|
||||
# service:
|
||||
# name: gitea-http
|
||||
# namespace: gitea
|
||||
# port: 3000
|
||||
#
|
||||
# Полный пример (*.home.ru с TLS + basic auth):
|
||||
# ingress_add_domains_entries:
|
||||
# - name: gitea-home
|
||||
# hosts:
|
||||
# - gitea.home.ru
|
||||
# service:
|
||||
# name: gitea-http
|
||||
# namespace: gitea
|
||||
# port: 3000
|
||||
# tls:
|
||||
# enabled: true
|
||||
# certManager:
|
||||
# enabled: true
|
||||
# issuer: letsencrypt-prod
|
||||
# issuerKind: ClusterIssuer
|
||||
# auth:
|
||||
# enabled: true
|
||||
# username: admin
|
||||
# password: "{{ vault_gitea_proxy_password }}"
|
||||
# annotations:
|
||||
# nginx.ingress.kubernetes.io/proxy-body-size: "0"
|
||||
ingress_add_domains_entries: []
|
||||
176
addons/ingress-add-domains/role/tasks/main.yml
Normal file
176
addons/ingress-add-domains/role/tasks/main.yml
Normal file
@@ -0,0 +1,176 @@
|
||||
---
|
||||
# ── Validate inputs ───────────────────────────────────────────────────────────
|
||||
|
||||
- name: Validate ingress_add_domains_entries is defined and non-empty
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ingress_add_domains_entries is defined
|
||||
- ingress_add_domains_entries | length > 0
|
||||
fail_msg: >
|
||||
ingress_add_domains_entries is empty. Define at least one entry in
|
||||
group_vars/all/addons.yml → ingress_add_domains_entries.
|
||||
success_msg: "ingress_add_domains_entries: {{ ingress_add_domains_entries | length }} entry/entries defined"
|
||||
|
||||
# ── Create Helm release namespace ─────────────────────────────────────────────
|
||||
|
||||
- name: Create ingress-add-domains namespace
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl create namespace {{ ingress_add_domains_release_namespace }}
|
||||
--dry-run=client -o yaml | k3s kubectl apply -f -
|
||||
become: true
|
||||
changed_when: false
|
||||
|
||||
# ── Copy Helm chart to master node ───────────────────────────────────────────
|
||||
|
||||
- name: Ensure chart temp directory is clean
|
||||
ansible.builtin.file:
|
||||
path: /tmp/ingress-add-domains-chart
|
||||
state: absent
|
||||
become: true
|
||||
|
||||
- name: Create chart temp directory
|
||||
ansible.builtin.file:
|
||||
path: /tmp/ingress-add-domains-chart
|
||||
state: directory
|
||||
mode: "0755"
|
||||
become: true
|
||||
|
||||
- name: Copy Helm chart to master
|
||||
ansible.builtin.copy:
|
||||
src: "{{ role_path }}/chart/"
|
||||
dest: /tmp/ingress-add-domains-chart/
|
||||
mode: preserve
|
||||
become: true
|
||||
|
||||
# ── Generate htpasswd hashes from plain username/password ────────────────────
|
||||
# Если auth.username + auth.password заданы — автоматически хэшируем через
|
||||
# openssl passwd -apr1, записываем в auth.credentials и убираем открытые поля.
|
||||
# Пароли в открытом виде нигде не попадают в Helm values или логи.
|
||||
|
||||
- name: Generate htpasswd credentials (username/password → apr1 hash)
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- python3
|
||||
- -c
|
||||
- |
|
||||
import json, subprocess, sys
|
||||
|
||||
entries = json.loads(sys.argv[1])
|
||||
def_auth = json.loads(sys.argv[2])
|
||||
|
||||
def gen_credentials(auth, fallback):
|
||||
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
|
||||
if username and password:
|
||||
r = subprocess.run(
|
||||
['openssl', 'passwd', '-apr1', password],
|
||||
capture_output=True, text=True, check=True
|
||||
)
|
||||
return username + ':' + r.stdout.strip()
|
||||
return ''
|
||||
|
||||
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 entry in entries:
|
||||
auth = dict(entry.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
|
||||
auth.pop('username', None)
|
||||
auth.pop('password', None)
|
||||
entry['auth'] = auth
|
||||
|
||||
print(json.dumps({'entries': entries, 'def_auth': cleaned_def_auth}))
|
||||
- "{{ ingress_add_domains_entries | to_json }}"
|
||||
- "{{ ingress_add_domains_defaults.auth | to_json }}"
|
||||
register: _auth_processed
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Set final entries and defaults with generated credentials
|
||||
ansible.builtin.set_fact:
|
||||
_ingress_add_domains_entries_final: "{{ (_auth_processed.stdout | from_json).entries }}"
|
||||
_ingress_add_domains_def_auth_final: "{{ (_auth_processed.stdout | from_json).def_auth }}"
|
||||
|
||||
# ── Template Helm values ──────────────────────────────────────────────────────
|
||||
|
||||
- name: Template Helm values
|
||||
ansible.builtin.template:
|
||||
src: values.yaml.j2
|
||||
dest: /tmp/ingress-add-domains-values.yaml
|
||||
mode: "0640"
|
||||
become: true
|
||||
|
||||
- name: Show generated Helm values
|
||||
ansible.builtin.command: cat /tmp/ingress-add-domains-values.yaml
|
||||
become: true
|
||||
changed_when: false
|
||||
register: _ingress_add_domains_values
|
||||
|
||||
- name: Debug generated values
|
||||
ansible.builtin.debug:
|
||||
var: _ingress_add_domains_values.stdout_lines
|
||||
|
||||
# ── Lint chart before deploying ───────────────────────────────────────────────
|
||||
|
||||
- name: Lint Helm chart
|
||||
ansible.builtin.command: >
|
||||
helm lint /tmp/ingress-add-domains-chart
|
||||
--values /tmp/ingress-add-domains-values.yaml
|
||||
become: true
|
||||
changed_when: false
|
||||
register: _helm_lint
|
||||
failed_when: _helm_lint.rc != 0
|
||||
|
||||
# ── Deploy chart ──────────────────────────────────────────────────────────────
|
||||
|
||||
- name: Deploy ingress-add-domains via Helm
|
||||
ansible.builtin.command: >
|
||||
helm upgrade --install {{ ingress_add_domains_release_name }}
|
||||
/tmp/ingress-add-domains-chart
|
||||
--namespace {{ ingress_add_domains_release_namespace }}
|
||||
--values /tmp/ingress-add-domains-values.yaml
|
||||
--atomic
|
||||
--wait
|
||||
--timeout 60s
|
||||
become: true
|
||||
register: _helm_result
|
||||
changed_when: true
|
||||
|
||||
# ── Verify deployment ─────────────────────────────────────────────────────────
|
||||
|
||||
- name: Get all created Ingresses
|
||||
ansible.builtin.command: >
|
||||
k3s kubectl get ingress -A
|
||||
-l app.kubernetes.io/instance={{ ingress_add_domains_release_name }}
|
||||
-o wide
|
||||
become: true
|
||||
changed_when: false
|
||||
register: _ingress_list
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────────────────────────
|
||||
|
||||
- name: "=== ingress-add-domains Ready ==="
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "╔══════════════════════════════════════════════════════════════╗"
|
||||
- "║ ingress-add-domains — Deployed ║"
|
||||
- "╚══════════════════════════════════════════════════════════════╝"
|
||||
- ""
|
||||
- " Release : {{ ingress_add_domains_release_name }}"
|
||||
- " Entries : {{ ingress_add_domains_entries | length }}"
|
||||
- ""
|
||||
- " Ingress resources:"
|
||||
- "{{ _ingress_list.stdout_lines | to_yaml }}"
|
||||
- ""
|
||||
- " Verify: kubectl get ingress -A -l app.kubernetes.io/instance={{ ingress_add_domains_release_name }}"
|
||||
9
addons/ingress-add-domains/role/templates/values.yaml.j2
Normal file
9
addons/ingress-add-domains/role/templates/values.yaml.j2
Normal file
@@ -0,0 +1,9 @@
|
||||
# Generated by Ansible — do not edit manually.
|
||||
# Configure via: group_vars/all/addons.yml → ingress_add_domains_* variables.
|
||||
# Note: auth.username/password are resolved to htpasswd hashes before this file is written.
|
||||
|
||||
defaults:
|
||||
{{ (ingress_add_domains_defaults | combine({'auth': _ingress_add_domains_def_auth_final})) | to_yaml | indent(2, True) }}
|
||||
|
||||
entries:
|
||||
{{ _ingress_add_domains_entries_final | to_yaml | indent(2, True) }}
|
||||
Reference in New Issue
Block a user