From a006c5ceaea6ba3a688d962f4b9530d63a9152af Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 14 Aug 2026 08:09:39 +0000 Subject: [PATCH 01/19] chore(deps): update helm release meilisearch to 0.38.* --- k8s/argo-apps/meili.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/argo-apps/meili.yaml b/k8s/argo-apps/meili.yaml index f0bbab3..bf49ade 100644 --- a/k8s/argo-apps/meili.yaml +++ b/k8s/argo-apps/meili.yaml @@ -18,7 +18,7 @@ spec: targetRevision: main - chart: meilisearch repoURL: https://meilisearch.github.io/meilisearch-kubernetes - targetRevision: 0.37.* + targetRevision: 0.38.* helm: valuesObject: environment: From f14bb601ce9057736f8a22ba95eb85336c8bb149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Mon, 17 Aug 2026 09:21:51 +0200 Subject: [PATCH 02/19] feat: add lldap chart and app --- k8s/argo-apps/lldap.yaml | 55 ++++++++ k8s/charts/lldap/.helmignore | 13 ++ k8s/charts/lldap/Chart.yaml | 15 +++ k8s/charts/lldap/README.md | 150 +++++++++++++++++++++ k8s/charts/lldap/templates/_helpers.tpl | 37 +++++ k8s/charts/lldap/templates/configmap.yaml | 33 +++++ k8s/charts/lldap/templates/deployment.yaml | 111 +++++++++++++++ k8s/charts/lldap/templates/ingress.yaml | 41 ++++++ k8s/charts/lldap/templates/pdb.yaml | 18 +++ k8s/charts/lldap/templates/pvc.yaml | 17 +++ k8s/charts/lldap/templates/service.yaml | 19 +++ k8s/charts/lldap/values.yaml | 90 +++++++++++++ k8s/services/argo/project-fuku.yaml | 2 + k8s/services/argo/project-management.yaml | 1 + k8s/services/argo/project-roboces.yaml | 1 + tofu/adguard/main.tf | 4 + 16 files changed, 607 insertions(+) create mode 100644 k8s/argo-apps/lldap.yaml create mode 100644 k8s/charts/lldap/.helmignore create mode 100644 k8s/charts/lldap/Chart.yaml create mode 100644 k8s/charts/lldap/README.md create mode 100644 k8s/charts/lldap/templates/_helpers.tpl create mode 100644 k8s/charts/lldap/templates/configmap.yaml create mode 100644 k8s/charts/lldap/templates/deployment.yaml create mode 100644 k8s/charts/lldap/templates/ingress.yaml create mode 100644 k8s/charts/lldap/templates/pdb.yaml create mode 100644 k8s/charts/lldap/templates/pvc.yaml create mode 100644 k8s/charts/lldap/templates/service.yaml create mode 100644 k8s/charts/lldap/values.yaml diff --git a/k8s/argo-apps/lldap.yaml b/k8s/argo-apps/lldap.yaml new file mode 100644 index 0000000..f25ad4c --- /dev/null +++ b/k8s/argo-apps/lldap.yaml @@ -0,0 +1,55 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: lldap + namespace: argo-cd +spec: + destination: + name: '' + namespace: apps-fuku + server: https://kubernetes.default.svc + sources: + - repoURL: oci://git.roboces.dev/catalin/fukuops + chart: lldap + targetRevision: 1.0.0 + helm: + valuesObject: + lldap: + service: + type: LoadBalancer + httpPort: 17170 + ldapPort: 3890 + ingress: + enabled: true + className: traefik + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + hosts: + - host: ldap.fuku + paths: + - path: / + pathType: Prefix + - host: ldap.fukurokuju.dev + paths: + - path: / + pathType: Prefix + config: + ldapBaseDn: "dc=fuku,dc=local" + httpUrl: "https://ldap.fuku" + persistence: + enabled: true + storageClass: "truenas-nfs-csi" + accessMode: ReadWriteMany + size: 5Gi + secret: + existingSecretName: "lldap-secrets" + podDisruptionBudget: + enabled: true + minAvailable: 1 + project: fuku + syncPolicy: + automated: + prune: true + selfHeal: true diff --git a/k8s/charts/lldap/.helmignore b/k8s/charts/lldap/.helmignore new file mode 100644 index 0000000..c113420 --- /dev/null +++ b/k8s/charts/lldap/.helmignore @@ -0,0 +1,13 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching. +*.tgz +.env +.git +.git_cache +.idea +*.md +docs/* +tmp/* +vendor/* +.travis.yml +.testignore diff --git a/k8s/charts/lldap/Chart.yaml b/k8s/charts/lldap/Chart.yaml new file mode 100644 index 0000000..059a454 --- /dev/null +++ b/k8s/charts/lldap/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: lldap +description: Light LDAP implementation for authentication +type: application +version: 1.0.0 +appVersion: stable +annotations: + artifacthub.io/images: "- name: lldap\n image: lldap/lldap:stable\n" + artifacthub.io/changes: "- kind: added\n description: Initial chart release\n" + artifacthub.io/maintainers: "- name: catalin\n email: catalin@roboces.dev\n" +keywords: + - lldap + - ldap + - authentication + - identity diff --git a/k8s/charts/lldap/README.md b/k8s/charts/lldap/README.md new file mode 100644 index 0000000..1e7ea66 --- /dev/null +++ b/k8s/charts/lldap/README.md @@ -0,0 +1,150 @@ +# lldap + +Lightweight LDAP authentication server with a web UI. + +## TL;DR + +```bash +helm install lldap oci://git.roboces.dev/catalin/fukuops/lldap --version 1.0.0 -n apps-roboces +``` + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3+ +- A NFS storage class (default: `truenas-nfs-csi`) - only if persistence is enabled +- An existing `Secret` with credentials (see `lldap.secret.existingSecretName`) + +## Configuration + +All values are nested under the `lldap:` key. Example: + +```yaml +lldap: + service: + type: LoadBalancer + httpPort: 17170 + ldapPort: 3890 + config: + ldapBaseDn: "dc=example,dc=com" + httpUrl: "https://lldap.example.com" + persistence: + enabled: true + storageClass: "truenas-nfs-csi" + accessMode: ReadWriteMany + size: 10Gi + secret: + existingSecretName: lldap-secrets +``` + +## Secret Requirements + +The secret must contain the following keys: + +- `LLDAP_JWT_SECRET` - JWT signing secret (generate with `openssl rand -base64 32`) +- `LLDAP_KEY_SEED` - Key seed for password hashing (generate with `openssl rand -base64 32`) +- `LLDAP_LDAP_USER_PASS` - Admin password for LDAP and web UI + +Example secret: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: lldap-secrets +type: Opaque +stringData: + LLDAP_JWT_SECRET: + LLDAP_KEY_SEED: + LLDAP_LDAP_USER_PASS: +``` + +## Values Reference + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| `lldap.replicaCount` | int | `1` | Number of replicas | +| `lldap.image.repository` | string | `lldap/lldap` | Image repository | +| `lldap.image.pullPolicy` | string | `IfNotPresent` | Image pull policy | +| `lldap.image.tag` | string | `stable` | Image tag | +| `lldap.service.type` | string | `LoadBalancer` | Service type | +| `lldap.service.httpPort` | int | `17170` | Web UI port | +| `lldap.service.ldapPort` | int | `3890` | LDAP port | +| `lldap.config.ldapBaseDn` | string | `dc=example,dc=com` | LDAP base DN | +| `lldap.config.httpUrl` | string | `https://lldap.example.com` | Public URL for web UI | +| `lldap.config.databaseUrl` | string | `""` | Database URL (empty = SQLite) | +| `lldap.config.verbose` | bool | `false` | Enable verbose logging | +| `lldap.ldaps.enabled` | bool | `false` | Enable LDAPS | +| `lldap.ldaps.certFile` | string | `""` | TLS certificate file path | +| `lldap.ldaps.keyFile` | string | `""` | TLS key file path | +| `lldap.smtp.enabled` | bool | `false` | Enable SMTP for password reset | +| `lldap.smtp.enablePasswordReset` | bool | `false` | Enable password reset via email | +| `lldap.smtp.server` | string | `""` | SMTP server | +| `lldap.smtp.port` | int | `465` | SMTP port | +| `lldap.smtp.encryption` | string | `"TLS"` | SMTP encryption (NONE, TLS, STARTTLS) | +| `lldap.smtp.user` | string | `""` | SMTP username | +| `lldap.smtp.fromAddress` | string | `""` | From email address | +| `lldap.smtp.fromName` | string | `""` | From name | +| `lldap.smtp.replyTo` | string | `""` | Reply-to address | +| `lldap.persistence.enabled` | bool | `false` | Enable persistence | +| `lldap.persistence.storageClass` | string | `truenas-nfs-csi` | Storage class | +| `lldap.persistence.accessMode` | string | `ReadWriteMany` | Access mode | +| `lldap.persistence.size` | string | `1Gi` | PVC size | +| `lldap.secret.existingSecretName` | string | `""` | Existing secret name | +| `lldap.debug.enabled` | bool | `false` | Enable debug mode | +| `lldap.resources` | object | `{}` | Container resources | +| `lldap.livenessProbe` | object | (see values.yaml) | Liveness probe | +| `lldap.readinessProbe` | object | (see values.yaml) | Readiness probe | +| `lldap.nodeSelector` | object | `{}` | Node selector | +| `lldap.tolerations` | array | `[]` | Tolerations | +| `lldap.affinity` | object | `{}` | Affinity rules | + +## Database + +By default, lldap uses SQLite stored in `/data`. To use an external database: + +```yaml +lldap: + config: + databaseUrl: "mysql://user:password@mysql-server/my-database" +``` + +Supported databases: SQLite (default), MySQL/MariaDB, PostgreSQL. + +## Persistence + +When persistence is enabled, a PVC named `lldap-data` is created. The PVC uses the `truenas-nfs-csi` storage class by default with `ReadWriteMany` access mode. + +## Troubleshooting + +### Debug Mode + +Enable debug mode to troubleshoot issues: + +```yaml +lldap: + debug: + enabled: true +``` + +### Pod not starting + +```bash +kubectl get pvc -n apps-roboces -l app.kubernetes.io/name=lldap +``` + +### Check logs + +```bash +kubectl logs lldap-0 -n apps-roboces +``` + +### Verify configmap + +```bash +kubectl get configmap lldap-config -n apps-roboces -o yaml +``` + +### Default Credentials + +If no secret is provided, the default admin password is `password`. **Change this immediately in production.** diff --git a/k8s/charts/lldap/templates/_helpers.tpl b/k8s/charts/lldap/templates/_helpers.tpl new file mode 100644 index 0000000..7d26272 --- /dev/null +++ b/k8s/charts/lldap/templates/_helpers.tpl @@ -0,0 +1,37 @@ +{{/* Expand the name of the chart. */}} +{{- define "name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* Create a default fully qualified app name. */}} +{{- define "fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* Common labels */}} +{{- define "labels" -}} +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{ include "selectorLabels" . }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* Selector labels */}} +{{- define "selectorLabels" -}} +app.kubernetes.io/name: {{ include "name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* Chart name as the PVC name */}} +{{- define "pvcName" -}} +{{- .Values.lldap.persistence.name | default (printf "%s-data" (include "name" .)) }} +{{- end }} diff --git a/k8s/charts/lldap/templates/configmap.yaml b/k8s/charts/lldap/templates/configmap.yaml new file mode 100644 index 0000000..f753e1d --- /dev/null +++ b/k8s/charts/lldap/templates/configmap.yaml @@ -0,0 +1,33 @@ +{{- if .Values.lldap.config }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "fullname" . }}-config + labels: + {{- include "labels" . | nindent 4 }} +data: + LLDAP_LDAP_BASE_DN: {{ .Values.lldap.config.ldapBaseDn | quote }} + LLDAP_HTTP_URL: {{ .Values.lldap.config.httpUrl | quote }} + LLDAP_LDAP_OPTIONS__PORT: {{ .Values.lldap.config.ldapPort | quote }} + LLDAP_HTTP_OPTIONS__PORT: {{ .Values.lldap.config.httpPort | quote }} + LLDAP_VERBOSE: {{ .Values.lldap.config.verbose | quote }} + {{- if .Values.lldap.config.databaseUrl }} + LLDAP_DATABASE_URL: {{ .Values.lldap.config.databaseUrl | quote }} + {{- end }} + {{- if .Values.lldap.ldaps.enabled }} + LLDAP_LDAPS_OPTIONS__ENABLED: "true" + LLDAP_LDAPS_OPTIONS__CERT_FILE: {{ .Values.lldap.ldaps.certFile | quote }} + LLDAP_LDAPS_OPTIONS__KEY_FILE: {{ .Values.lldap.ldaps.keyFile | quote }} + {{- end }} + {{- if .Values.lldap.smtp.enabled }} + LLDAP_SMTP_OPTIONS__ENABLE_PASSWORD_RESET: {{ .Values.lldap.smtp.enablePasswordReset | quote }} + LLDAP_SMTP_OPTIONS__SERVER: {{ .Values.lldap.smtp.server | quote }} + LLDAP_SMTP_OPTIONS__PORT: {{ .Values.lldap.smtp.port | quote }} + LLDAP_SMTP_OPTIONS__SMTP_ENCRYPTION: {{ .Values.lldap.smtp.encryption | quote }} + LLDAP_SMTP_OPTIONS__USER: {{ .Values.lldap.smtp.user | quote }} + LLDAP_SMTP_OPTIONS__FROM: {{ printf "%s <%s>" .Values.lldap.smtp.fromName .Values.lldap.smtp.fromAddress | quote }} + {{- if .Values.lldap.smtp.replyTo }} + LLDAP_SMTP_OPTIONS__TO: {{ .Values.lldap.smtp.replyTo | quote }} + {{- end }} + {{- end }} +{{- end }} diff --git a/k8s/charts/lldap/templates/deployment.yaml b/k8s/charts/lldap/templates/deployment.yaml new file mode 100644 index 0000000..391f5c5 --- /dev/null +++ b/k8s/charts/lldap/templates/deployment.yaml @@ -0,0 +1,111 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} +spec: + replicas: {{ .Values.lldap.replicaCount }} + selector: + matchLabels: + {{- include "selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "selectorLabels" . | nindent 8 }} + spec: + {{- with .Values.lldap.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.lldap.debug.enabled }} + initContainers: + - name: debug + image: alpine:3.19 + command: ["sleep", "infinity"] + envFrom: + {{- if .Values.lldap.config }} + - configMapRef: + name: {{ include "fullname" . }}-config + {{- end }} + {{- if .Values.lldap.secret.existingSecretName }} + - secretRef: + name: {{ .Values.lldap.secret.existingSecretName }} + {{- end }} + {{- if .Values.lldap.persistence.enabled }} + volumeMounts: + - name: {{ include "pvcName" . }} + mountPath: /data + {{- end }} + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: + - all + {{- end }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.lldap.image.repository }}:{{ .Values.lldap.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.lldap.image.pullPolicy }} + {{- with .Values.lldap.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.lldap.service.httpPort }} + protocol: TCP + - name: ldap + containerPort: {{ .Values.lldap.service.ldapPort }} + protocol: TCP + envFrom: + {{- if .Values.lldap.config }} + - configMapRef: + name: {{ include "fullname" . }}-config + {{- end }} + {{- if .Values.lldap.secret.existingSecretName }} + - secretRef: + name: {{ .Values.lldap.secret.existingSecretName }} + {{- end }} + {{- if .Values.lldap.persistence.enabled }} + volumeMounts: + - name: {{ include "pvcName" . }} + mountPath: /data + {{- end }} + {{- with .Values.lldap.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if not .Values.lldap.debug.enabled }} + {{- with .Values.lldap.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.lldap.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.lldap.persistence.enabled }} + volumes: + - name: {{ include "pvcName" . }} + persistentVolumeClaim: + claimName: {{ include "pvcName" . }} + {{- else }} + volumes: + - name: {{ include "pvcName" . }} + emptyDir: {} + {{- end }} + {{- with .Values.lldap.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.lldap.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.lldap.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/k8s/charts/lldap/templates/ingress.yaml b/k8s/charts/lldap/templates/ingress.yaml new file mode 100644 index 0000000..02c9d37 --- /dev/null +++ b/k8s/charts/lldap/templates/ingress.yaml @@ -0,0 +1,41 @@ +{{- if .Values.lldap.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} + {{- with .Values.lldap.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if .Values.lldap.ingress.className }} + ingressClassName: {{ .Values.lldap.ingress.className }} + {{- end }} + {{- if .Values.lldap.ingress.tls }} + tls: + {{- range .Values.lldap.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.lldap.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType | default "Prefix" }} + backend: + service: + name: {{ include "fullname" $ }} + port: + number: {{ $.Values.lldap.service.httpPort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/k8s/charts/lldap/templates/pdb.yaml b/k8s/charts/lldap/templates/pdb.yaml new file mode 100644 index 0000000..00c3ddf --- /dev/null +++ b/k8s/charts/lldap/templates/pdb.yaml @@ -0,0 +1,18 @@ +{{- if .Values.lldap.podDisruptionBudget.enabled }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} +spec: + {{- if .Values.lldap.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.lldap.podDisruptionBudget.minAvailable }} + {{- end }} + {{- if .Values.lldap.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.lldap.podDisruptionBudget.maxUnavailable }} + {{- end }} + selector: + matchLabels: + {{- include "selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/k8s/charts/lldap/templates/pvc.yaml b/k8s/charts/lldap/templates/pvc.yaml new file mode 100644 index 0000000..68594fd --- /dev/null +++ b/k8s/charts/lldap/templates/pvc.yaml @@ -0,0 +1,17 @@ +{{- if .Values.lldap.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "pvcName" . }} + labels: + {{- include "labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.lldap.persistence.accessMode | default "ReadWriteMany" }} + {{- if .Values.lldap.persistence.storageClass }} + storageClassName: {{ .Values.lldap.persistence.storageClass }} + {{- end }} + resources: + requests: + storage: {{ .Values.lldap.persistence.size | default "1Gi" }} +{{- end }} diff --git a/k8s/charts/lldap/templates/service.yaml b/k8s/charts/lldap/templates/service.yaml new file mode 100644 index 0000000..06df975 --- /dev/null +++ b/k8s/charts/lldap/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "fullname" . }} + labels: + {{- include "labels" . | nindent 4 }} +spec: + type: {{ .Values.lldap.service.type }} + ports: + - port: {{ .Values.lldap.service.httpPort }} + targetPort: http + protocol: TCP + name: http + - port: {{ .Values.lldap.service.ldapPort }} + targetPort: ldap + protocol: TCP + name: ldap + selector: + {{- include "selectorLabels" . | nindent 4 }} diff --git a/k8s/charts/lldap/values.yaml b/k8s/charts/lldap/values.yaml new file mode 100644 index 0000000..bb19244 --- /dev/null +++ b/k8s/charts/lldap/values.yaml @@ -0,0 +1,90 @@ +lldap: + replicaCount: 1 + + image: + repository: lldap/lldap + pullPolicy: IfNotPresent + tag: "stable" + + service: + type: LoadBalancer + httpPort: 17170 + ldapPort: 3890 + + ingress: + enabled: false + className: "traefik" + annotations: {} + hosts: + - host: lldap.example.com + paths: + - path: / + pathType: Prefix + tls: [] + + config: + ldapBaseDn: "dc=example,dc=com" + httpUrl: "https://lldap.example.com" + databaseUrl: "" + verbose: false + ldapPort: 3890 + httpPort: 17170 + + ldaps: + enabled: false + certFile: "" + keyFile: "" + + smtp: + enabled: false + enablePasswordReset: false + server: "" + port: 465 + encryption: "TLS" + user: "" + fromAddress: "" + fromName: "" + replyTo: "" + + persistence: + enabled: false + storageClass: "truenas-nfs-csi" + accessMode: ReadWriteMany + size: 1Gi + + resources: {} + + livenessProbe: + tcpSocket: + port: http + initialDelaySeconds: 30 + timeoutSeconds: 15 + successThreshold: 1 + failureThreshold: 3 + periodSeconds: 10 + + readinessProbe: + tcpSocket: + port: http + initialDelaySeconds: 15 + timeoutSeconds: 2 + successThreshold: 1 + failureThreshold: 3 + periodSeconds: 10 + + nodeSelector: {} + tolerations: [] + affinity: {} + + secret: + existingSecretName: "" + + podSecurityContext: {} + securityContext: {} + + podDisruptionBudget: + enabled: false + minAvailable: 1 + + debug: + enabled: false diff --git a/k8s/services/argo/project-fuku.yaml b/k8s/services/argo/project-fuku.yaml index 9e7ba4a..9ce27d7 100644 --- a/k8s/services/argo/project-fuku.yaml +++ b/k8s/services/argo/project-fuku.yaml @@ -26,3 +26,5 @@ spec: - https://repo.helmforge.dev - https://harish2k01.github.io/helm-charts/ - git.roboces.dev/catalin/fukuops + - https://kubereboot.github.io/charts + - https://charts.goauthentik.io/ diff --git a/k8s/services/argo/project-management.yaml b/k8s/services/argo/project-management.yaml index 4ef7347..180673b 100644 --- a/k8s/services/argo/project-management.yaml +++ b/k8s/services/argo/project-management.yaml @@ -25,3 +25,4 @@ spec: - https://git.roboces.dev/catalin/fukuops.git - https://democratic-csi.github.io/charts/ - https://bitnami-labs.github.io/sealed-secrets + - https://kubernetes-sigs.github.io/descheduler diff --git a/k8s/services/argo/project-roboces.yaml b/k8s/services/argo/project-roboces.yaml index b7a335a..7749122 100644 --- a/k8s/services/argo/project-roboces.yaml +++ b/k8s/services/argo/project-roboces.yaml @@ -16,4 +16,5 @@ spec: sourceRepos: - code.forgejo.org/forgejo-helm - git.roboces.dev/catalin/fukuops + - https://git.roboces.dev/catalin/fukuops.git - https://git.roboces.dev/catalin/huesoporro.git diff --git a/tofu/adguard/main.tf b/tofu/adguard/main.tf index afd06bf..47f397b 100644 --- a/tofu/adguard/main.tf +++ b/tofu/adguard/main.tf @@ -123,3 +123,7 @@ resource "adguard_rewrite" "dns" { domain = "dns.fuku" answer = "192.168.1.12" } +resource "adguard_rewrite" "ldap" { + domain = "ldap.fuku" + answer = "192.168.1.12" +} From 98c165897d04f0b7a92f7a6bd2c88e610bf0541f Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 18 Aug 2026 05:21:59 +0000 Subject: [PATCH 03/19] chore(deps): update git.roboces.dev/catalin/fukuops/lldap docker tag to v1.0.1 --- k8s/argo-apps/lldap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/argo-apps/lldap.yaml b/k8s/argo-apps/lldap.yaml index f25ad4c..fe54655 100644 --- a/k8s/argo-apps/lldap.yaml +++ b/k8s/argo-apps/lldap.yaml @@ -12,7 +12,7 @@ spec: sources: - repoURL: oci://git.roboces.dev/catalin/fukuops chart: lldap - targetRevision: 1.0.0 + targetRevision: 1.0.1 helm: valuesObject: lldap: From c9fcd56bdaa285cc6693d7bb63f7de8e44a138d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Tue, 18 Aug 2026 19:05:02 +0200 Subject: [PATCH 04/19] feat: add k8s/argo-apps/authelia - add scripts/users.py to manage users against lldap - add smtp values for lldap so it can send emails --- .gitignore | 1 + .yamllint.yaml | 1 + README.md | 6 + k8s/argo-apps/authelia.yaml | 128 ++++++++++ k8s/services/authelia/pvc.yaml | 13 + k8s/services/authelia/sealedsecrets.yaml | 17 ++ scripts/users.py | 293 +++++++++++++++++++++++ tofu/adguard/main.tf | 7 +- 8 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 k8s/argo-apps/authelia.yaml create mode 100644 k8s/services/authelia/pvc.yaml create mode 100644 k8s/services/authelia/sealedsecrets.yaml create mode 100755 scripts/users.py diff --git a/.gitignore b/.gitignore index 7d11c76..e645ace 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ AGENTS.md k8s/charts/README.md .opencode/ opencode.json +*.pyc diff --git a/.yamllint.yaml b/.yamllint.yaml index 4ffc165..f10a375 100644 --- a/.yamllint.yaml +++ b/.yamllint.yaml @@ -7,6 +7,7 @@ yaml-files: ignore: | k8s/charts/ + **/sealedsecrets.yaml extends: default diff --git a/README.md b/README.md index ac42e9c..aff389b 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,9 @@ ethtool -K eno1 tx off rx off ```bash helm upgrade argo-cd argo/argo-cd -n argo-cd -f k8s/helm/argo-cd/values.yaml ``` + +- create a new user and send them an email + +```bash +scripts/users.py add -u catalin -n cătălin -e catalin@roboces.dev +``` diff --git a/k8s/argo-apps/authelia.yaml b/k8s/argo-apps/authelia.yaml new file mode 100644 index 0000000..5b36d05 --- /dev/null +++ b/k8s/argo-apps/authelia.yaml @@ -0,0 +1,128 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: authelia + namespace: argo-cd +spec: + destination: + name: '' + namespace: apps-roboces + server: https://kubernetes.default.svc + sources: + - chart: authelia + repoURL: https://charts.authelia.com + targetRevision: 0.11.6 + helm: + valuesObject: + + enabled: true + pod: + autoscaling: + enabled: true + kind: Deployment + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 1 + memory: 1024Mi + service: + type: LoadBalancer + ingress: + enabled: true + className: traefik + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + secret: + existingSecret: secrets-authelia + mountPath: /secrets + additionalSecrets: + secrets-authelia: + path: secrets-authelia + items: + - key: authentication.ldap.password.txt + path: authentication.ldap.password.txt + - key: identity_validation.reset_password.jwt.hmac.key + path: identity_validation.reset_password.jwt.hmac.key + - key: session.encryption.key + path: session.encryption.key + - key: smtp_password.txt + path: smtp_password.txt + - key: storage.encryption.key + path: storage.encryption.key + persistence: + enabled: true + existingClaim: authelia-data + configMap: + log: + level: info + session: + name: authelia_session + same_site: lax + expiration: 1 hour + inactivity: 5 minutes + remember_me: 1 month + cookies: + - domain: roboces.dev + subdomain: auth + - domain: auth.fuku + subdomain: '' + authentication_backend: + ldap: + enabled: true + implementation: lldap + address: ldap://lldap.apps-fuku.svc.cluster.local:3890 + base_dn: dc=fuku,dc=local + additional_users_dn: ou=people + users_filter: (&({username_attribute}={input})(objectClass=person)) + additional_groups_dn: ou=groups + groups_filter: (member={dn}) + user: uid=admin,ou=people,dc=fuku,dc=local + attributes: + distinguished_name: dn + username: uid + display_name: cn + mail: mail + member_of: memberOf + group_name: cn + password: + disabled: false + secret_name: secrets-authelia + path: authentication.ldap.password.txt + storage: + local: + enabled: true + path: /config/db.sqlite3 + notifier: + smtp: + enabled: true + address: submissions://mail.fukurokuju.dev:465 + sender: Authelia + subject: "Your verification code is {code}" + identifier: auth.fuku + startup_check_address: test@authelia.com + disable_html_emails: false + disable_require_tls: false + disable_starttls: false + username: auth@fukurokuju.dev + password: + disabled: false + secret_name: secrets-authelia + path: smtp_password.txt + tls: + server_name: mail.fukurokuju.dev + skip_verify: false + minimum_version: TLS1.2 + access_control: + default_policy: two_factor + - repoURL: https://git.roboces.dev/catalin/fukuops.git + path: k8s/services/authelia + targetRevision: feat/authelia + project: roboces + syncPolicy: + automated: + prune: true + selfHeal: true diff --git a/k8s/services/authelia/pvc.yaml b/k8s/services/authelia/pvc.yaml new file mode 100644 index 0000000..84f9fee --- /dev/null +++ b/k8s/services/authelia/pvc.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: authelia-data + namespace: apps-roboces +spec: + accessModes: + - ReadWriteMany + storageClassName: truenas-nfs-csi + resources: + requests: + storage: 1Gi diff --git a/k8s/services/authelia/sealedsecrets.yaml b/k8s/services/authelia/sealedsecrets.yaml new file mode 100644 index 0000000..e6d58d7 --- /dev/null +++ b/k8s/services/authelia/sealedsecrets.yaml @@ -0,0 +1,17 @@ +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: secrets-authelia + namespace: apps-roboces +spec: + encryptedData: + authentication.ldap.password.txt: AgBwsqdgEOQ8r/yUOD82Cwr44jxqV9RPDTzMYju8IfWx7b4iDKtZVidLK4RxBCp1+MPyWmLs7H4jec3Xsr10da/V+LaS1Elib+Sp2CkFZPGj1/XAcu5w3k/BnMcsRWJEJtzU251NVon6IVEzszcGOaVsPqf6aIvnKgOJEmp1boMlnemkvtaeXVS0TuSLXCQEMDfGMh9X8D9je315Sdqpczo8YLz6jyj6RxlgXeygRXkXANd2KFOyfLNUAnzm4Tl6PMjULnXvsxLhlc35+am6IsZGVLcdRMPY12UpHeZyVIgJBZSdo/gLTJS/hLhL2sPJv8HksL6YfvDnIWqu3PxVwI3BGtW2znBQaEIFXtOIXL9EDG0NMAZSBYCRpjrPNHP7xj7ruqjg4eU1iUj6yJpIv9322L/qhFWViK9wXc3+0L2n2q4qVBgrZxcacgqtHrCYj5Uo9iluVPWwagcKFYRXBupqaUUTso7LtSsv4WbOrJzYzE9mZk+feH1ZNvBp13JfWNdRiso5rpl0tZFJ7oW4G/CJuWg2MmL3UNViYB/ntwFWZWT8dxCEq2LI3UxeawHntzvk82kFyZduLUwhLvY4WfQB4yb//gBOpc0OyHa+djwECIyy3Jh+70LG+1YeVc8yQm52eu9+mzRSwDlqspKZYfwbMPCCiQj6AiSfg9Ho5+ITq1PgEt3oo9b0JkYFmEAgT88Y6z0DRQrM9YOXCfazMg== + identity_validation.reset_password.jwt.hmac.key: AgAQRknTOoC5tbCitLZkRtcIs3VOaubEvlhRmGtHh/OS0v6lgU2Gcl5C6jisu/+MqQba4euzMACLVgSMiEkERY2GrMijecfVf5LOkKKwQ0Xjiw4a+MlSVs5KR1Ovlz414//3GfUCttunnDRnH6xQbsQjWLCI6lv6Umb40TQHOnRmb83wmSkBa/P9WR35EQ6IcES3q7Ad4ZpNtLaavEnB8NuLZg3yaJpN+1yuZPeMgTPUtTG/e9FLdBs3S8AYFIcrUnDLViUo9DfXumMs1DlTsmABpjyIgjIeywOmCxMYZYvQCJrRGYlKZRdoCoafhAPxt67hdUYOe56cq3kqlTNTdURMbYup7cIkW2hQ8ZiyPvYk++RIhV/cPWOSjh6CLxMq26Si1c2Lg3aAVFxQcDiXbdprjHzbKzFfKO/5LVnhT7O651BqkCguPC2vVoaeuxblHjqouBD7ZmPazIQ9J/uDUmvm5UPjEN+Ln+enrLXdoJN9HMor410/PJ+9xPAHXhdrXRS6j5TlN5q1pz0gj2Qmnv3cunsyLo+ZJ97F3EkSYPVoK3sMXGn2fdKy/lc9vPMMvtH/tLdhWefxd25APahStZHv4Mb7O6Z+A4uFiP9Qao64jeLj08LWBbJPefT7Y+KthugN6cW4y/xQN4UFvpPHhGB36yb+45eIBzOJHltOqZ+wldjUHeBgMdkODAQYYbCzhDuVVm2iMGQqUi/Vk+EayaV70prcAELgRskseME2MwfrhYMAKS91tfMbL2dGTQ== + session.encryption.key: AgCjw3K7solE1INH+VIwEeP2U70HCaY25Iu6sq5C7j8nuW8JzbSCBPeY+qTS76l4sIyLlLekk7ke5kCPpZo+Jsz62UQJMGaSASRy0FKQIxgn5UIkObB+qziGjC8WNj6y/JmbvPNiIp+aw0Nd3OmVLRkvIjJDkyMf/iW1oGeLctwRop9Y+wi7lDPyEHH+CaNPCLYidhAJFG785qibqY8jTxtHsEts2T+hSXz75v5abYS+lTFtFV7RDWudkWG6dPjUKwYGw7l/yQm5p2qzzvhoyCrQItdNuEvGrZQsnXZlq4ik0C7t977uiU+fumYKFQvHzVuQTMjjo9G5AlhUG/4w2olMqOMZoJ0u+hHFBX1K1uYCUr1hd2343jd+y86iObZzTazlTGdP142lXk9+CT+akpQIVX0Mk5mwtemCYG+64ebSQm+SVB9Ffgxx3Ips6BOhqwIO0fLB1iz4BFVUp+87juyHZOP/5uC+KeV3+DHaQDrHRo6DxF5D5CRXBCcHXeWBLkDXSENgD4kvRee7fEQf60ajvi9VFTPjk86KXCbOZVlZPPrqWG7Dh+kaYOjWCQXQvFzYAq6/Dr5ZDRPJ/X989zHjPFFGRQ7Ol9uyjT/7I/2IBfdQB1Ep5BWty50PBVIqbYKRzQN0g0x5BAj5/2J9mOX/0BNGExVCNn53rfc8yDurfP0q1nbYk1t+fv0T9oI7RTPYIVWtUnshG8fwYK1PARNYBbKrGwjWtTdVxzV+ocunmmOHQhvOFrTnsfupGQ== + smtp_password.txt: AgB8pRbQxScSBRty04Z5oen7F52Y7PnwF9gJ6fdbUTRobE7j2fWiHMQk4l1FTztW3Muz3B/MQZBgBEzulpkDJ1TrvTe5I7JFdANr3lmlKjy0CG2NcQ7Vma7Bqu2SVnL8wxi/3IBTGxVa6bplGrkUCsIr/qWLhkVknOpAaUvOUuECT8PVxUZq/s4UMddkEf4N3IQW7j/60xwzomMp4e5eZ8YTH0/ZGcuw9JOQSOoJDzvJ5B+wZF7q7j9LQc4gBPHQiFFMVlZrhHA0fdjfPxbb7QEiGdMBxeZ0JqaaFBnr4FG7hRY5/H9RS9iZCFH9QY6X86ul9ZgiJYVyrvZFKSBsW+THlLZ7MACLvOs/zdXhpc2SthccoK8ykGC/inWTnfodQpH7N4zJq7akXxFy9/em4ssjWul4coeLNc+xhyi5VauLQQfRpgijc1kVJeQbrUrMe+aE1tlRdgRx76OKiFym1t47+zN3eqRf+NegNBGPSdAtKEBKfNPrcFQmlZVR6fRZqiBxG0wE90nfq9fuw3RDnVU/0LsuxwTz99gWMWl8ySuo+EAhf+JmB26/Pi1hnByeNeD8FsVkUiC6XnwAVC+ytnaVEfatrKmSJyzRhTSuFUoFxD5YV0aQgILks4+CpQwn300s0bqkLOP0etigZCCPq4cR+t+FFYAkuVjr+xPsoww0UOgokHsZKoXYi0n96UjbT0w+zoHhqQTBrPPWhPjDogZBKBCnth5eUtgrhSA2 + storage.encryption.key: AgBt8lTRtRqn+PiTNjgmJ97/rEqKEE70KZLuVtyvBM2ek70E555vx16BsCN3ATvwcr5dw+nAwYKpHJzS+mgTloLWmz6L2HqtxhlnaKC7elFeOza7tPKmKeKV3zXQ8YEA9eWjKdUN2TzO5s10iPCp4esUy5asO5DS/QVyPHMUfufu3iwHCDgnDEGlQ0pO1LMT2FwJ2QYX4Cub+OjUVrkBm8VbNdFqIw3OBtahRX2gx+o1d55psfi08ylEMKUwkHXYtkBheDvYL7SRv8xHKHGrN1loWHZOby1GOTzOnOpL6mjJoU+4nlvEzVwxzuPOHDq24cfey7engu0IfrZ+9Fs+icFjInY7cpowlNl0CXIWQm++ljtKVOUGUB2Bp6IwY1iwu3NVlwYVoZRCfSw5rLhccXnODYjXGMAwHF1cau4K8mkx1mD48dJnQE+y6AfrOPH+tCJ0dSDyuebNNlcE9HDmJseIGDP37Z3E/S9CCGprvLCdUYeOhXERyTevm5VJoboYq1/mhonPvY0bU4w3zVV3mXgr3Xi4SOupneUL+XfuQASRz1eGoQpAGxI8h4qWkgA66f8uPTh0hdM1JfLus9oXCtFsiYJzd+VtIZE+0JtI4ztGtm2JtgH80t2eiJTQolQihfpAXazg9fQSPH6NHsFcRplqC7emxdBZnI2F+TSjqcoX+RA78z+J2OP4ISs4ngF8USmm9/rJIRUq26XNLMjktSREBh/IwrpZWgiZqU/QzMhNriClH3n4vDHWJ03Xiw== + template: + metadata: + name: secrets-authelia + namespace: apps-roboces + type: Opaque diff --git a/scripts/users.py b/scripts/users.py new file mode 100755 index 0000000..eb49ca3 --- /dev/null +++ b/scripts/users.py @@ -0,0 +1,293 @@ +#!/usr/bin/env -S uv run --script +# /// script +# dependencies = ["ldap3", "typer", "httpx"] +# /// + +import base64 +import os +import re +import secrets +import subprocess +import sys + +import httpx +import typer +from ldap3 import ALL, Connection, Server, SUBTREE + +LLDAP_NAMESPACE = "apps-fuku" +LLDAP_SECRET = "lldap-secrets" +LLDAP_SERVICE = "lldap.apps-fuku.svc.cluster.local" +LLDAP_LDAP_PORT = 3890 +LLDAP_BASE_DN = "dc=fuku,dc=local" +LLDAP_ADMIN_USER = "uid=admin,ou=people,dc=fuku,dc=local" +LLDAP_URL = "https://ldap.fukurokuju.dev" + +app = typer.Typer(help="Manage Authelia users in the LLDAP backend.") + + +def fail(msg: str) -> None: + print(f"ERROR: {msg}", file=sys.stderr) + raise typer.Exit(1) + + +def run_kubectl(args: list[str]) -> subprocess.CompletedProcess[str]: + return subprocess.run( + ["kubectl", *args], + capture_output=True, + text=True, + check=True, + ) + + +def get_secret_value(key: str) -> str: + result = run_kubectl( + [ + "get", + "secret", + LLDAP_SECRET, + "-n", + LLDAP_NAMESPACE, + "-o", + f"jsonpath={{.data.{key}}}", + ] + ) + return base64.b64decode(result.stdout).decode() + + +def get_configmap_value(key: str) -> str: + result = run_kubectl( + [ + "get", + "configmap", + "lldap-config", + "-n", + LLDAP_NAMESPACE, + "-o", + f"jsonpath={{.data.{key}}}", + ] + ) + return result.stdout.strip() + + +def get_lldap_password() -> str: + return get_secret_value("LLDAP_LDAP_USER_PASS") + + +def get_lldap_hosts() -> list[str]: + result = run_kubectl( + [ + "get", + "svc", + "lldap", + "-n", + LLDAP_NAMESPACE, + "-o", + "jsonpath={.status.loadBalancer.ingress[*].ip}", + ] + ) + ips = [ip.strip() for ip in result.stdout.split() if ip.strip()] + if not ips: + fail("could not discover lldap LoadBalancer IPs") + return ips + + +def lldap_connection() -> Connection: + password = get_lldap_password() + hosts = get_lldap_hosts() + last_error = None + for host in hosts: + try: + server = Server(host, port=LLDAP_LDAP_PORT, use_ssl=False, get_info=ALL) + conn = Connection( + server, + user=LLDAP_ADMIN_USER, + password=password, + auto_bind=True, + read_only=False, + ) + return conn + except Exception as e: + last_error = e + continue + fail(f"could not connect to any lldap endpoint: {last_error}") + + +def validate_username(username: str) -> None: + if not re.match(r"^[a-zA-Z0-9_.-]+$", username): + fail(f"invalid username: {username} (allowed: a-z, 0-9, _, ., -)") + + +def trigger_password_reset(username: str) -> None: + url = f"{LLDAP_URL}/auth/reset/step1/{username}" + try: + response = httpx.post(url, timeout=30.0) + response.raise_for_status() + except httpx.HTTPError as e: + fail(f"failed to trigger password reset email: {e}") + + print(f"Password reset email triggered for '{username}'.") + + +def generate_random_password() -> str: + return secrets.token_urlsafe(32) + + +def get_group_id(conn: Connection, group_name: str) -> int | None: + conn.search( + f"ou=groups,{LLDAP_BASE_DN}", + f"(cn={group_name})", + SUBTREE, + attributes=["uid"], + ) + if not conn.entries: + return None + uid = conn.entries[0].uid.value + try: + return int(uid) + except (TypeError, ValueError): + return None + + +def user_exists(conn: Connection, username: str) -> bool: + conn.search( + f"ou=people,{LLDAP_BASE_DN}", + f"(uid={username})", + SUBTREE, + attributes=["uid"], + ) + return bool(conn.entries) + + +def email_in_use(conn: Connection, email: str) -> bool: + conn.search( + f"ou=people,{LLDAP_BASE_DN}", + f"(mail={email})", + SUBTREE, + attributes=["uid"], + ) + return bool(conn.entries) + + +@app.command("list", help="List existing users ") +def cmd_list() -> None: + with lldap_connection() as conn: + conn.search( + f"ou=people,{LLDAP_BASE_DN}", + "(objectClass=person)", + SUBTREE, + attributes=["uid", "cn", "mail", "memberOf"], + ) + if not conn.entries: + print("No users found.") + return + print(f"{'USERNAME':<20} {'DISPLAY NAME':<30} {'EMAIL':<30} GROUPS") + for entry in conn.entries: + uid = entry.uid.value if entry.uid else "" + cn = entry.cn.value if entry.cn else "" + mail = entry.mail.value if entry.mail else "" + groups = ",".join( + g.split(",")[0].replace("cn=", "") for g in entry.memberOf.values + ) if entry.memberOf else "" + print(f"{uid:<20} {cn:<30} {mail:<30} {groups}") + + +@app.command("add", help="Add a user. Omit --password to send an invite email") +def cmd_add( + username: str = typer.Option(..., "--username", "-u", help="Username"), + displayname: str = typer.Option(..., "--displayname", "-n", help="Display name"), + password: str | None = typer.Option(None, "--password", "-p", help="Password"), + email: str = typer.Option("", "--email", "-e", help="Email address"), + groups: list[str] = typer.Option( + ["users"], "--group", "-g", help="Group to add the user to (repeatable)" + ), +) -> None: + validate_username(username) + + invite_mode = password is None + if invite_mode and not email: + fail("--email is required when --password is omitted") + + effective_email = email or f"{username}@roboces.dev" + + with lldap_connection() as conn: + if user_exists(conn, username): + fail(f"user '{username}' already exists") + if email_in_use(conn, effective_email): + fail(f"email '{effective_email}' is already in use") + + if invite_mode: + password = generate_random_password() + + user_dn = f"uid={username},ou=people,{LLDAP_BASE_DN}" + attrs = { + "objectClass": ["inetOrgPerson", "posixAccount", "mailAccount", "person"], + "uid": username, + "cn": displayname, + "mail": effective_email, + } + if not conn.add(user_dn, attributes=attrs): + fail(f"failed to create user: {conn.result}") + + if not conn.extend.standard.modify_password(user=user_dn, new_password=password): + fail(f"failed to set password: {conn.result}") + + for group_name in groups: + group_id = get_group_id(conn, group_name) + if group_id is None: + print(f" WARNING: group '{group_name}' not found, skipping") + continue + if not conn.add_user_to_group(user_dn, group_id): + print(f" WARNING: could not add to group '{group_name}': {conn.result}") + else: + print(f" Added to group '{group_name}'.") + + if invite_mode: + trigger_password_reset(username) + print(f"User '{username}' created and invite email sent to '{effective_email}'.") + else: + print(f"User '{username}' created.") + + +@app.command("remove", help="Remove a user") +def cmd_remove( + username: str = typer.Option(..., "--username", "-u", help="Username"), +) -> None: + validate_username(username) + with lldap_connection() as conn: + user_dn = f"uid={username},ou=people,{LLDAP_BASE_DN}" + conn.search(user_dn, "(objectClass=*)", SUBTREE, attributes=["uid"]) + if not conn.entries: + fail(f"user '{username}' not found") + if not conn.delete(user_dn): + fail(f"failed to delete user: {conn.result}") + print(f"User '{username}' removed from LLDAP.") + + +@app.command("reset-password", help="Trigger a password reset email for an existing user") +def cmd_reset_password( + username: str = typer.Option(..., "--username", "-u", help="Username"), + confirm: bool = typer.Option( + True, + "--confirm/--no-confirm", + help="Prompt for confirmation before sending the reset email", + ), +) -> None: + validate_username(username) + with lldap_connection() as conn: + if not user_exists(conn=conn, username=username): + fail(f"user '{username}' not found") + + if confirm: + typer.confirm( + f"Send password reset email to '{username}'?", + abort=True, + ) + + trigger_password_reset(username) + print(f"Password reset email triggered for '{username}'.") + + +if __name__ == "__main__": + os.environ.setdefault("PYTHONUNBUFFERED", "1") + sys.stdout.reconfigure(line_buffering=True) + app() diff --git a/tofu/adguard/main.tf b/tofu/adguard/main.tf index 47f397b..7c40871 100644 --- a/tofu/adguard/main.tf +++ b/tofu/adguard/main.tf @@ -114,11 +114,16 @@ resource "adguard_rewrite" "vault" { answer = "192.168.1.12" } -resource "adguard_rewrite" "pocketid" { +resource "adguard_rewrite" "authelia-roboces" { domain = "auth.roboces.dev" answer = "192.168.1.12" } +resource "adguard_rewrite" "authelia" { + domain = "auth.fuku" + answer = "192.168.1.12" +} + resource "adguard_rewrite" "dns" { domain = "dns.fuku" answer = "192.168.1.12" From eb0a8d135e527c29a4ca7be3e45c90dd5fcbf28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Tue, 18 Aug 2026 23:08:45 +0200 Subject: [PATCH 05/19] feat: add audiobookshelf argo app --- .gitignore | 1 + k8s/argo-apps/audiobookshelf.yaml | 78 ++++++++++++++++++++++++ k8s/argo-apps/authelia.yaml | 48 ++++++++++++++- k8s/services/authelia/sealedsecrets.yaml | 44 +++++++------ tofu/adguard/main.tf | 5 ++ 5 files changed, 158 insertions(+), 18 deletions(-) create mode 100644 k8s/argo-apps/audiobookshelf.yaml diff --git a/.gitignore b/.gitignore index e645ace..e7b695a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ k8s/charts/README.md .opencode/ opencode.json *.pyc +.venv/ diff --git a/k8s/argo-apps/audiobookshelf.yaml b/k8s/argo-apps/audiobookshelf.yaml new file mode 100644 index 0000000..835cc54 --- /dev/null +++ b/k8s/argo-apps/audiobookshelf.yaml @@ -0,0 +1,78 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: audiobookshelf + namespace: argo-cd +spec: + destination: + name: '' + namespace: apps-roboces + server: https://kubernetes.default.svc + sources: + - chart: audiobookshelf + repoURL: https://charts.christianhuth.de + targetRevision: 2.4.0 + helm: + valuesObject: + service: + type: LoadBalancer + ingress: + enabled: true + className: traefik + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + hosts: + - host: audiobooks.roboces.dev + paths: + - path: / + pathType: Prefix + - host: audiobooks.fuku + paths: + - path: / + pathType: Prefix + tls: [] + audiobookshelf: + timezone: Europe/Madrid + config: + persistence: + enabled: true + storageClassName: truenas-nfs-csi + accessModes: + - ReadWriteMany + resources: + requests: + storage: 5Gi + metadata: + persistence: + enabled: true + storageClassName: truenas-nfs-csi + accessModes: + - ReadWriteMany + resources: + requests: + storage: 20Gi + backup: + persistence: + enabled: true + storageClassName: truenas-nfs-csi + accessModes: + - ReadWriteMany + resources: + requests: + storage: 20Gi + library: + persistence: + enabled: true + storageClassName: truenas-nfs-csi + accessModes: + - ReadWriteMany + resources: + requests: + storage: 500Gi + project: roboces + syncPolicy: + automated: + prune: true + selfHeal: true diff --git a/k8s/argo-apps/authelia.yaml b/k8s/argo-apps/authelia.yaml index 5b36d05..5c34567 100644 --- a/k8s/argo-apps/authelia.yaml +++ b/k8s/argo-apps/authelia.yaml @@ -53,6 +53,10 @@ spec: path: smtp_password.txt - key: storage.encryption.key path: storage.encryption.key + - key: oidc.audiobookshelf.client_secret + path: oidc.audiobookshelf.client_secret + - key: identity_providers.oidc.jwks.key.pem + path: identity_providers.oidc.jwks.key.pem persistence: enabled: true existingClaim: authelia-data @@ -118,9 +122,51 @@ spec: minimum_version: TLS1.2 access_control: default_policy: two_factor + identity_providers: + oidc: + enabled: true + hmac_secret: + path: identity_providers.oidc.hmac.key + jwks: + - key_id: authelia + algorithm: RS256 + use: sig + key: + path: /secrets/secrets-authelia/identity_providers.oidc.jwks.key.pem + clients: + - client_id: audiobookshelf + client_name: Audiobookshelf + client_secret: + path: /secrets/secrets-authelia/oidc.audiobookshelf.client_secret + public: false + authorization_policy: two_factor + require_pkce: true + pkce_challenge_method: S256 + redirect_uris: + - https://audiobooks.roboces.dev/auth/openid/callback + - https://audiobooks.roboces.dev/auth/openid/mobile-redirect + - https://audiobooks.roboces.dev/audiobookshelf/auth/openid/callback + - https://audiobooks.roboces.dev/audiobookshelf/auth/openid/mobile-redirect + - https://audiobooks.fuku/auth/openid/callback + - https://audiobooks.fuku/auth/openid/mobile-redirect + - https://audiobooks.fuku/audiobookshelf/auth/openid/callback + - https://audiobooks.fuku/audiobookshelf/auth/openid/mobile-redirect + - audiobookshelf://oauth + scopes: + - openid + - profile + - groups + - email + response_types: + - code + grant_types: + - authorization_code + access_token_signed_response_alg: none + userinfo_signed_response_alg: none + token_endpoint_auth_method: client_secret_basic - repoURL: https://git.roboces.dev/catalin/fukuops.git path: k8s/services/authelia - targetRevision: feat/authelia + targetRevision: main project: roboces syncPolicy: automated: diff --git a/k8s/services/authelia/sealedsecrets.yaml b/k8s/services/authelia/sealedsecrets.yaml index e6d58d7..18f8ea8 100644 --- a/k8s/services/authelia/sealedsecrets.yaml +++ b/k8s/services/authelia/sealedsecrets.yaml @@ -1,17 +1,27 @@ -apiVersion: bitnami.com/v1alpha1 -kind: SealedSecret -metadata: - name: secrets-authelia - namespace: apps-roboces -spec: - encryptedData: - authentication.ldap.password.txt: AgBwsqdgEOQ8r/yUOD82Cwr44jxqV9RPDTzMYju8IfWx7b4iDKtZVidLK4RxBCp1+MPyWmLs7H4jec3Xsr10da/V+LaS1Elib+Sp2CkFZPGj1/XAcu5w3k/BnMcsRWJEJtzU251NVon6IVEzszcGOaVsPqf6aIvnKgOJEmp1boMlnemkvtaeXVS0TuSLXCQEMDfGMh9X8D9je315Sdqpczo8YLz6jyj6RxlgXeygRXkXANd2KFOyfLNUAnzm4Tl6PMjULnXvsxLhlc35+am6IsZGVLcdRMPY12UpHeZyVIgJBZSdo/gLTJS/hLhL2sPJv8HksL6YfvDnIWqu3PxVwI3BGtW2znBQaEIFXtOIXL9EDG0NMAZSBYCRpjrPNHP7xj7ruqjg4eU1iUj6yJpIv9322L/qhFWViK9wXc3+0L2n2q4qVBgrZxcacgqtHrCYj5Uo9iluVPWwagcKFYRXBupqaUUTso7LtSsv4WbOrJzYzE9mZk+feH1ZNvBp13JfWNdRiso5rpl0tZFJ7oW4G/CJuWg2MmL3UNViYB/ntwFWZWT8dxCEq2LI3UxeawHntzvk82kFyZduLUwhLvY4WfQB4yb//gBOpc0OyHa+djwECIyy3Jh+70LG+1YeVc8yQm52eu9+mzRSwDlqspKZYfwbMPCCiQj6AiSfg9Ho5+ITq1PgEt3oo9b0JkYFmEAgT88Y6z0DRQrM9YOXCfazMg== - identity_validation.reset_password.jwt.hmac.key: AgAQRknTOoC5tbCitLZkRtcIs3VOaubEvlhRmGtHh/OS0v6lgU2Gcl5C6jisu/+MqQba4euzMACLVgSMiEkERY2GrMijecfVf5LOkKKwQ0Xjiw4a+MlSVs5KR1Ovlz414//3GfUCttunnDRnH6xQbsQjWLCI6lv6Umb40TQHOnRmb83wmSkBa/P9WR35EQ6IcES3q7Ad4ZpNtLaavEnB8NuLZg3yaJpN+1yuZPeMgTPUtTG/e9FLdBs3S8AYFIcrUnDLViUo9DfXumMs1DlTsmABpjyIgjIeywOmCxMYZYvQCJrRGYlKZRdoCoafhAPxt67hdUYOe56cq3kqlTNTdURMbYup7cIkW2hQ8ZiyPvYk++RIhV/cPWOSjh6CLxMq26Si1c2Lg3aAVFxQcDiXbdprjHzbKzFfKO/5LVnhT7O651BqkCguPC2vVoaeuxblHjqouBD7ZmPazIQ9J/uDUmvm5UPjEN+Ln+enrLXdoJN9HMor410/PJ+9xPAHXhdrXRS6j5TlN5q1pz0gj2Qmnv3cunsyLo+ZJ97F3EkSYPVoK3sMXGn2fdKy/lc9vPMMvtH/tLdhWefxd25APahStZHv4Mb7O6Z+A4uFiP9Qao64jeLj08LWBbJPefT7Y+KthugN6cW4y/xQN4UFvpPHhGB36yb+45eIBzOJHltOqZ+wldjUHeBgMdkODAQYYbCzhDuVVm2iMGQqUi/Vk+EayaV70prcAELgRskseME2MwfrhYMAKS91tfMbL2dGTQ== - session.encryption.key: AgCjw3K7solE1INH+VIwEeP2U70HCaY25Iu6sq5C7j8nuW8JzbSCBPeY+qTS76l4sIyLlLekk7ke5kCPpZo+Jsz62UQJMGaSASRy0FKQIxgn5UIkObB+qziGjC8WNj6y/JmbvPNiIp+aw0Nd3OmVLRkvIjJDkyMf/iW1oGeLctwRop9Y+wi7lDPyEHH+CaNPCLYidhAJFG785qibqY8jTxtHsEts2T+hSXz75v5abYS+lTFtFV7RDWudkWG6dPjUKwYGw7l/yQm5p2qzzvhoyCrQItdNuEvGrZQsnXZlq4ik0C7t977uiU+fumYKFQvHzVuQTMjjo9G5AlhUG/4w2olMqOMZoJ0u+hHFBX1K1uYCUr1hd2343jd+y86iObZzTazlTGdP142lXk9+CT+akpQIVX0Mk5mwtemCYG+64ebSQm+SVB9Ffgxx3Ips6BOhqwIO0fLB1iz4BFVUp+87juyHZOP/5uC+KeV3+DHaQDrHRo6DxF5D5CRXBCcHXeWBLkDXSENgD4kvRee7fEQf60ajvi9VFTPjk86KXCbOZVlZPPrqWG7Dh+kaYOjWCQXQvFzYAq6/Dr5ZDRPJ/X989zHjPFFGRQ7Ol9uyjT/7I/2IBfdQB1Ep5BWty50PBVIqbYKRzQN0g0x5BAj5/2J9mOX/0BNGExVCNn53rfc8yDurfP0q1nbYk1t+fv0T9oI7RTPYIVWtUnshG8fwYK1PARNYBbKrGwjWtTdVxzV+ocunmmOHQhvOFrTnsfupGQ== - smtp_password.txt: AgB8pRbQxScSBRty04Z5oen7F52Y7PnwF9gJ6fdbUTRobE7j2fWiHMQk4l1FTztW3Muz3B/MQZBgBEzulpkDJ1TrvTe5I7JFdANr3lmlKjy0CG2NcQ7Vma7Bqu2SVnL8wxi/3IBTGxVa6bplGrkUCsIr/qWLhkVknOpAaUvOUuECT8PVxUZq/s4UMddkEf4N3IQW7j/60xwzomMp4e5eZ8YTH0/ZGcuw9JOQSOoJDzvJ5B+wZF7q7j9LQc4gBPHQiFFMVlZrhHA0fdjfPxbb7QEiGdMBxeZ0JqaaFBnr4FG7hRY5/H9RS9iZCFH9QY6X86ul9ZgiJYVyrvZFKSBsW+THlLZ7MACLvOs/zdXhpc2SthccoK8ykGC/inWTnfodQpH7N4zJq7akXxFy9/em4ssjWul4coeLNc+xhyi5VauLQQfRpgijc1kVJeQbrUrMe+aE1tlRdgRx76OKiFym1t47+zN3eqRf+NegNBGPSdAtKEBKfNPrcFQmlZVR6fRZqiBxG0wE90nfq9fuw3RDnVU/0LsuxwTz99gWMWl8ySuo+EAhf+JmB26/Pi1hnByeNeD8FsVkUiC6XnwAVC+ytnaVEfatrKmSJyzRhTSuFUoFxD5YV0aQgILks4+CpQwn300s0bqkLOP0etigZCCPq4cR+t+FFYAkuVjr+xPsoww0UOgokHsZKoXYi0n96UjbT0w+zoHhqQTBrPPWhPjDogZBKBCnth5eUtgrhSA2 - storage.encryption.key: AgBt8lTRtRqn+PiTNjgmJ97/rEqKEE70KZLuVtyvBM2ek70E555vx16BsCN3ATvwcr5dw+nAwYKpHJzS+mgTloLWmz6L2HqtxhlnaKC7elFeOza7tPKmKeKV3zXQ8YEA9eWjKdUN2TzO5s10iPCp4esUy5asO5DS/QVyPHMUfufu3iwHCDgnDEGlQ0pO1LMT2FwJ2QYX4Cub+OjUVrkBm8VbNdFqIw3OBtahRX2gx+o1d55psfi08ylEMKUwkHXYtkBheDvYL7SRv8xHKHGrN1loWHZOby1GOTzOnOpL6mjJoU+4nlvEzVwxzuPOHDq24cfey7engu0IfrZ+9Fs+icFjInY7cpowlNl0CXIWQm++ljtKVOUGUB2Bp6IwY1iwu3NVlwYVoZRCfSw5rLhccXnODYjXGMAwHF1cau4K8mkx1mD48dJnQE+y6AfrOPH+tCJ0dSDyuebNNlcE9HDmJseIGDP37Z3E/S9CCGprvLCdUYeOhXERyTevm5VJoboYq1/mhonPvY0bU4w3zVV3mXgr3Xi4SOupneUL+XfuQASRz1eGoQpAGxI8h4qWkgA66f8uPTh0hdM1JfLus9oXCtFsiYJzd+VtIZE+0JtI4ztGtm2JtgH80t2eiJTQolQihfpAXazg9fQSPH6NHsFcRplqC7emxdBZnI2F+TSjqcoX+RA78z+J2OP4ISs4ngF8USmm9/rJIRUq26XNLMjktSREBh/IwrpZWgiZqU/QzMhNriClH3n4vDHWJ03Xiw== - template: - metadata: - name: secrets-authelia - namespace: apps-roboces - type: Opaque +{ + "kind": "SealedSecret", + "apiVersion": "bitnami.com/v1alpha1", + "metadata": { + "name": "secrets-authelia", + "namespace": "apps-roboces" + }, + "spec": { + "template": { + "metadata": { + "name": "secrets-authelia", + "namespace": "apps-roboces" + }, + "type": "Opaque" + }, + "encryptedData": { + "authentication.ldap.password.txt": "AgBwsqdgEOQ8r/yUOD82Cwr44jxqV9RPDTzMYju8IfWx7b4iDKtZVidLK4RxBCp1+MPyWmLs7H4jec3Xsr10da/V+LaS1Elib+Sp2CkFZPGj1/XAcu5w3k/BnMcsRWJEJtzU251NVon6IVEzszcGOaVsPqf6aIvnKgOJEmp1boMlnemkvtaeXVS0TuSLXCQEMDfGMh9X8D9je315Sdqpczo8YLz6jyj6RxlgXeygRXkXANd2KFOyfLNUAnzm4Tl6PMjULnXvsxLhlc35+am6IsZGVLcdRMPY12UpHeZyVIgJBZSdo/gLTJS/hLhL2sPJv8HksL6YfvDnIWqu3PxVwI3BGtW2znBQaEIFXtOIXL9EDG0NMAZSBYCRpjrPNHP7xj7ruqjg4eU1iUj6yJpIv9322L/qhFWViK9wXc3+0L2n2q4qVBgrZxcacgqtHrCYj5Uo9iluVPWwagcKFYRXBupqaUUTso7LtSsv4WbOrJzYzE9mZk+feH1ZNvBp13JfWNdRiso5rpl0tZFJ7oW4G/CJuWg2MmL3UNViYB/ntwFWZWT8dxCEq2LI3UxeawHntzvk82kFyZduLUwhLvY4WfQB4yb//gBOpc0OyHa+djwECIyy3Jh+70LG+1YeVc8yQm52eu9+mzRSwDlqspKZYfwbMPCCiQj6AiSfg9Ho5+ITq1PgEt3oo9b0JkYFmEAgT88Y6z0DRQrM9YOXCfazMg==", + "identity_providers.oidc.hmac.key": "AgCMb1NgRFW7jB8xAXDzTDK336k1w63FNJGJPj9fUrkM/5QT6WuAAPYPeD/3PWG/+0XjUycU4Q2PK/ei8rK6BJ1jwdSRQqBe/ybdxl3WCWGmjB2/jKLIAFQonJdgIzZiY4rUHUepQqhva/wA0eM1nCV/KYuOCIIzdNAGj3E+zyP9yquZcDxknadLuvY6XbTTCdsdwYgFP9qYy8+Vg/K8c2GGubuU34uLkfCG3afow9/gvtSDU5lsZwGtZPcYibOVULQ/CEiGiXBXs7g5W1HjqcJdPaJZDfkG2hPWh4aWMxMkjORt/9NrNmBi70e4LW5cq32wX0Mz+UblDf66Qz3wnGmZ93owAw1N0MtlI/V0W2XvSnxcXvmloHGfbll2rhFiUFPFmWq71Uv52Qc8rAMOeuJgRnrlG3QHxLFkTonMYcasQrfm8MjHpaNkemsdA6XO9VvrRVxLyN31lV+2bbeqh2wHZgCRIbU4gUyApi69Rd1TyEYVW9QSxWbPkX5B5eaCqDCZLX9aQocfElrKk/zzvY2J53s07AkvsowgwbWhwpzwAYB2ajBndAcNhzS311ve1kzZlp498ePGCdpcyuTJfVWTKoXdmJu3xSg0P7Czts/zstQYGSpp+0I4gwzYTtSuf1M841Obc78l2PaUqN5P3YIHdq4ZiMTWPHhqgerobyf6XxtXcO5iEf7zSGYexRwaTroMp/Sqx9P0eTX1rufwkmvMlPH6mClr3RMVGno7uFuC1SkRM3Ox0a4wJ0Y2B5IX/7C+Pd68Z1VQX+wJeson0X2K", + "identity_providers.oidc.jwks.key.pem": "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", + "identity_validation.reset_password.jwt.hmac.key": "AgAQRknTOoC5tbCitLZkRtcIs3VOaubEvlhRmGtHh/OS0v6lgU2Gcl5C6jisu/+MqQba4euzMACLVgSMiEkERY2GrMijecfVf5LOkKKwQ0Xjiw4a+MlSVs5KR1Ovlz414//3GfUCttunnDRnH6xQbsQjWLCI6lv6Umb40TQHOnRmb83wmSkBa/P9WR35EQ6IcES3q7Ad4ZpNtLaavEnB8NuLZg3yaJpN+1yuZPeMgTPUtTG/e9FLdBs3S8AYFIcrUnDLViUo9DfXumMs1DlTsmABpjyIgjIeywOmCxMYZYvQCJrRGYlKZRdoCoafhAPxt67hdUYOe56cq3kqlTNTdURMbYup7cIkW2hQ8ZiyPvYk++RIhV/cPWOSjh6CLxMq26Si1c2Lg3aAVFxQcDiXbdprjHzbKzFfKO/5LVnhT7O651BqkCguPC2vVoaeuxblHjqouBD7ZmPazIQ9J/uDUmvm5UPjEN+Ln+enrLXdoJN9HMor410/PJ+9xPAHXhdrXRS6j5TlN5q1pz0gj2Qmnv3cunsyLo+ZJ97F3EkSYPVoK3sMXGn2fdKy/lc9vPMMvtH/tLdhWefxd25APahStZHv4Mb7O6Z+A4uFiP9Qao64jeLj08LWBbJPefT7Y+KthugN6cW4y/xQN4UFvpPHhGB36yb+45eIBzOJHltOqZ+wldjUHeBgMdkODAQYYbCzhDuVVm2iMGQqUi/Vk+EayaV70prcAELgRskseME2MwfrhYMAKS91tfMbL2dGTQ==", + "oidc.audiobookshelf.client_secret": "AgAf92OIkvVhM8MC+KvWkmUtQma5k9Ewm2AwG0uSUPT8QEAsVou8Nl5QFPdn2sjMygf7zEK0tVrMcTI78CpNXO/GvRbDffVDIciU1uD2Hr2vB7cM++PfyjAzUC98+yEVKFJgxZRtLiP27RfYpo2QlxzIhUfuzGQA3acCJ0ABh9FsxUoJmR8hGtWxbljI+slKr539C22pxeJ4wGx9Rr66x6ccVH4P7MNEqbtmY1tlpb3lTh3v5xJgjiSR6CCP1BpSocQslz+gh2OZbRc2S+SGNWEs8yhbd8dqr5MpcaZjHDFLW6//SDZfYylYM1Q5KtfMcBY+Vra3N6O1t8FTWypD2/R4IBKQDTJ1sTmDP8sWfHG1xufwOy26YmTTB7DrEOVknCeA9Yc6BKii5k43UgVFm1T8wLVOW2nsUDBj2iYghKNeEtOimotxHybjpg5qeLgO/hV9N0VPjRnAyeCJ0dTvRiXIvj3fhAQFaTXUBvHRhzCtZ1UowmxllMtFn/F4pVwNyHifowJOU15DNYKevkDF06mlFbNgQ4W47HWGaemMComSMZGqB0jT4tA13X1MN6dpkOZBvwokvp7llgK5D0k+fOqWZskiuangy/2gcX0bQEZjWCAPQoF1s9YMtSKge8YJr6Bxov3xICWacsbFAbOxS6Jj2wJAFMYKVIo9NwOt8MPnLL4rS5GaQrPYjRJYDJOupQlx2jNAcs+2C8gSdNJTstK/l+qWIusqmOeQEHYJ1QkxZ7csvyMgeLfroIev8U16gGxpXs54P4brxcX4QhqgYp3doA52X6kbUJHIei2e+ZOU7soNAwHO7soyF3d2BLF2GcmT1LzB/M0ubtFyYh435pxtgCFwavWbLTYkvu6d9qEMcvIhxw==", + "session.encryption.key": "AgCjw3K7solE1INH+VIwEeP2U70HCaY25Iu6sq5C7j8nuW8JzbSCBPeY+qTS76l4sIyLlLekk7ke5kCPpZo+Jsz62UQJMGaSASRy0FKQIxgn5UIkObB+qziGjC8WNj6y/JmbvPNiIp+aw0Nd3OmVLRkvIjJDkyMf/iW1oGeLctwRop9Y+wi7lDPyEHH+CaNPCLYidhAJFG785qibqY8jTxtHsEts2T+hSXz75v5abYS+lTFtFV7RDWudkWG6dPjUKwYGw7l/yQm5p2qzzvhoyCrQItdNuEvGrZQsnXZlq4ik0C7t977uiU+fumYKFQvHzVuQTMjjo9G5AlhUG/4w2olMqOMZoJ0u+hHFBX1K1uYCUr1hd2343jd+y86iObZzTazlTGdP142lXk9+CT+akpQIVX0Mk5mwtemCYG+64ebSQm+SVB9Ffgxx3Ips6BOhqwIO0fLB1iz4BFVUp+87juyHZOP/5uC+KeV3+DHaQDrHRo6DxF5D5CRXBCcHXeWBLkDXSENgD4kvRee7fEQf60ajvi9VFTPjk86KXCbOZVlZPPrqWG7Dh+kaYOjWCQXQvFzYAq6/Dr5ZDRPJ/X989zHjPFFGRQ7Ol9uyjT/7I/2IBfdQB1Ep5BWty50PBVIqbYKRzQN0g0x5BAj5/2J9mOX/0BNGExVCNn53rfc8yDurfP0q1nbYk1t+fv0T9oI7RTPYIVWtUnshG8fwYK1PARNYBbKrGwjWtTdVxzV+ocunmmOHQhvOFrTnsfupGQ==", + "smtp_password.txt": "AgB8pRbQxScSBRty04Z5oen7F52Y7PnwF9gJ6fdbUTRobE7j2fWiHMQk4l1FTztW3Muz3B/MQZBgBEzulpkDJ1TrvTe5I7JFdANr3lmlKjy0CG2NcQ7Vma7Bqu2SVnL8wxi/3IBTGxVa6bplGrkUCsIr/qWLhkVknOpAaUvOUuECT8PVxUZq/s4UMddkEf4N3IQW7j/60xwzomMp4e5eZ8YTH0/ZGcuw9JOQSOoJDzvJ5B+wZF7q7j9LQc4gBPHQiFFMVlZrhHA0fdjfPxbb7QEiGdMBxeZ0JqaaFBnr4FG7hRY5/H9RS9iZCFH9QY6X86ul9ZgiJYVyrvZFKSBsW+THlLZ7MACLvOs/zdXhpc2SthccoK8ykGC/inWTnfodQpH7N4zJq7akXxFy9/em4ssjWul4coeLNc+xhyi5VauLQQfRpgijc1kVJeQbrUrMe+aE1tlRdgRx76OKiFym1t47+zN3eqRf+NegNBGPSdAtKEBKfNPrcFQmlZVR6fRZqiBxG0wE90nfq9fuw3RDnVU/0LsuxwTz99gWMWl8ySuo+EAhf+JmB26/Pi1hnByeNeD8FsVkUiC6XnwAVC+ytnaVEfatrKmSJyzRhTSuFUoFxD5YV0aQgILks4+CpQwn300s0bqkLOP0etigZCCPq4cR+t+FFYAkuVjr+xPsoww0UOgokHsZKoXYi0n96UjbT0w+zoHhqQTBrPPWhPjDogZBKBCnth5eUtgrhSA2", + "storage.encryption.key": "AgBt8lTRtRqn+PiTNjgmJ97/rEqKEE70KZLuVtyvBM2ek70E555vx16BsCN3ATvwcr5dw+nAwYKpHJzS+mgTloLWmz6L2HqtxhlnaKC7elFeOza7tPKmKeKV3zXQ8YEA9eWjKdUN2TzO5s10iPCp4esUy5asO5DS/QVyPHMUfufu3iwHCDgnDEGlQ0pO1LMT2FwJ2QYX4Cub+OjUVrkBm8VbNdFqIw3OBtahRX2gx+o1d55psfi08ylEMKUwkHXYtkBheDvYL7SRv8xHKHGrN1loWHZOby1GOTzOnOpL6mjJoU+4nlvEzVwxzuPOHDq24cfey7engu0IfrZ+9Fs+icFjInY7cpowlNl0CXIWQm++ljtKVOUGUB2Bp6IwY1iwu3NVlwYVoZRCfSw5rLhccXnODYjXGMAwHF1cau4K8mkx1mD48dJnQE+y6AfrOPH+tCJ0dSDyuebNNlcE9HDmJseIGDP37Z3E/S9CCGprvLCdUYeOhXERyTevm5VJoboYq1/mhonPvY0bU4w3zVV3mXgr3Xi4SOupneUL+XfuQASRz1eGoQpAGxI8h4qWkgA66f8uPTh0hdM1JfLus9oXCtFsiYJzd+VtIZE+0JtI4ztGtm2JtgH80t2eiJTQolQihfpAXazg9fQSPH6NHsFcRplqC7emxdBZnI2F+TSjqcoX+RA78z+J2OP4ISs4ngF8USmm9/rJIRUq26XNLMjktSREBh/IwrpZWgiZqU/QzMhNriClH3n4vDHWJ03Xiw==" + } + } +} diff --git a/tofu/adguard/main.tf b/tofu/adguard/main.tf index 7c40871..324220b 100644 --- a/tofu/adguard/main.tf +++ b/tofu/adguard/main.tf @@ -132,3 +132,8 @@ resource "adguard_rewrite" "ldap" { domain = "ldap.fuku" answer = "192.168.1.12" } + +resource "adguard_rewrite" "audiobooks" { + domain = "audiobooks.fuku" + answer = "192.168.1.12" +} From 98384cc2581518820a68b9abdbe1abb25feae397 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 19 Aug 2026 04:58:59 +0000 Subject: [PATCH 06/19] chore(deps): update helm release authentik to 2026.8.* --- k8s/argo-apps/authentik.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/argo-apps/authentik.yaml b/k8s/argo-apps/authentik.yaml index 5bfe1bf..193c947 100644 --- a/k8s/argo-apps/authentik.yaml +++ b/k8s/argo-apps/authentik.yaml @@ -12,7 +12,7 @@ spec: sources: - chart: authentik repoURL: https://charts.goauthentik.io/ - targetRevision: 2026.5.* + targetRevision: 2026.8.* helm: valuesObject: authentik: From 62231689c750c7505d8175125cff9525f3d6bfd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Wed, 19 Aug 2026 10:15:20 +0200 Subject: [PATCH 07/19] feat: add scripts/add-secret.py --- k8s/services/authelia/sealedsecrets.yaml | 48 +++-- scripts/add-secret.py | 212 +++++++++++++++++++++++ 2 files changed, 233 insertions(+), 27 deletions(-) create mode 100755 scripts/add-secret.py diff --git a/k8s/services/authelia/sealedsecrets.yaml b/k8s/services/authelia/sealedsecrets.yaml index 18f8ea8..ddd5349 100644 --- a/k8s/services/authelia/sealedsecrets.yaml +++ b/k8s/services/authelia/sealedsecrets.yaml @@ -1,27 +1,21 @@ -{ - "kind": "SealedSecret", - "apiVersion": "bitnami.com/v1alpha1", - "metadata": { - "name": "secrets-authelia", - "namespace": "apps-roboces" - }, - "spec": { - "template": { - "metadata": { - "name": "secrets-authelia", - "namespace": "apps-roboces" - }, - "type": "Opaque" - }, - "encryptedData": { - "authentication.ldap.password.txt": "AgBwsqdgEOQ8r/yUOD82Cwr44jxqV9RPDTzMYju8IfWx7b4iDKtZVidLK4RxBCp1+MPyWmLs7H4jec3Xsr10da/V+LaS1Elib+Sp2CkFZPGj1/XAcu5w3k/BnMcsRWJEJtzU251NVon6IVEzszcGOaVsPqf6aIvnKgOJEmp1boMlnemkvtaeXVS0TuSLXCQEMDfGMh9X8D9je315Sdqpczo8YLz6jyj6RxlgXeygRXkXANd2KFOyfLNUAnzm4Tl6PMjULnXvsxLhlc35+am6IsZGVLcdRMPY12UpHeZyVIgJBZSdo/gLTJS/hLhL2sPJv8HksL6YfvDnIWqu3PxVwI3BGtW2znBQaEIFXtOIXL9EDG0NMAZSBYCRpjrPNHP7xj7ruqjg4eU1iUj6yJpIv9322L/qhFWViK9wXc3+0L2n2q4qVBgrZxcacgqtHrCYj5Uo9iluVPWwagcKFYRXBupqaUUTso7LtSsv4WbOrJzYzE9mZk+feH1ZNvBp13JfWNdRiso5rpl0tZFJ7oW4G/CJuWg2MmL3UNViYB/ntwFWZWT8dxCEq2LI3UxeawHntzvk82kFyZduLUwhLvY4WfQB4yb//gBOpc0OyHa+djwECIyy3Jh+70LG+1YeVc8yQm52eu9+mzRSwDlqspKZYfwbMPCCiQj6AiSfg9Ho5+ITq1PgEt3oo9b0JkYFmEAgT88Y6z0DRQrM9YOXCfazMg==", - "identity_providers.oidc.hmac.key": "AgCMb1NgRFW7jB8xAXDzTDK336k1w63FNJGJPj9fUrkM/5QT6WuAAPYPeD/3PWG/+0XjUycU4Q2PK/ei8rK6BJ1jwdSRQqBe/ybdxl3WCWGmjB2/jKLIAFQonJdgIzZiY4rUHUepQqhva/wA0eM1nCV/KYuOCIIzdNAGj3E+zyP9yquZcDxknadLuvY6XbTTCdsdwYgFP9qYy8+Vg/K8c2GGubuU34uLkfCG3afow9/gvtSDU5lsZwGtZPcYibOVULQ/CEiGiXBXs7g5W1HjqcJdPaJZDfkG2hPWh4aWMxMkjORt/9NrNmBi70e4LW5cq32wX0Mz+UblDf66Qz3wnGmZ93owAw1N0MtlI/V0W2XvSnxcXvmloHGfbll2rhFiUFPFmWq71Uv52Qc8rAMOeuJgRnrlG3QHxLFkTonMYcasQrfm8MjHpaNkemsdA6XO9VvrRVxLyN31lV+2bbeqh2wHZgCRIbU4gUyApi69Rd1TyEYVW9QSxWbPkX5B5eaCqDCZLX9aQocfElrKk/zzvY2J53s07AkvsowgwbWhwpzwAYB2ajBndAcNhzS311ve1kzZlp498ePGCdpcyuTJfVWTKoXdmJu3xSg0P7Czts/zstQYGSpp+0I4gwzYTtSuf1M841Obc78l2PaUqN5P3YIHdq4ZiMTWPHhqgerobyf6XxtXcO5iEf7zSGYexRwaTroMp/Sqx9P0eTX1rufwkmvMlPH6mClr3RMVGno7uFuC1SkRM3Ox0a4wJ0Y2B5IX/7C+Pd68Z1VQX+wJeson0X2K", - "identity_providers.oidc.jwks.key.pem": "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", - "identity_validation.reset_password.jwt.hmac.key": "AgAQRknTOoC5tbCitLZkRtcIs3VOaubEvlhRmGtHh/OS0v6lgU2Gcl5C6jisu/+MqQba4euzMACLVgSMiEkERY2GrMijecfVf5LOkKKwQ0Xjiw4a+MlSVs5KR1Ovlz414//3GfUCttunnDRnH6xQbsQjWLCI6lv6Umb40TQHOnRmb83wmSkBa/P9WR35EQ6IcES3q7Ad4ZpNtLaavEnB8NuLZg3yaJpN+1yuZPeMgTPUtTG/e9FLdBs3S8AYFIcrUnDLViUo9DfXumMs1DlTsmABpjyIgjIeywOmCxMYZYvQCJrRGYlKZRdoCoafhAPxt67hdUYOe56cq3kqlTNTdURMbYup7cIkW2hQ8ZiyPvYk++RIhV/cPWOSjh6CLxMq26Si1c2Lg3aAVFxQcDiXbdprjHzbKzFfKO/5LVnhT7O651BqkCguPC2vVoaeuxblHjqouBD7ZmPazIQ9J/uDUmvm5UPjEN+Ln+enrLXdoJN9HMor410/PJ+9xPAHXhdrXRS6j5TlN5q1pz0gj2Qmnv3cunsyLo+ZJ97F3EkSYPVoK3sMXGn2fdKy/lc9vPMMvtH/tLdhWefxd25APahStZHv4Mb7O6Z+A4uFiP9Qao64jeLj08LWBbJPefT7Y+KthugN6cW4y/xQN4UFvpPHhGB36yb+45eIBzOJHltOqZ+wldjUHeBgMdkODAQYYbCzhDuVVm2iMGQqUi/Vk+EayaV70prcAELgRskseME2MwfrhYMAKS91tfMbL2dGTQ==", - "oidc.audiobookshelf.client_secret": "AgAf92OIkvVhM8MC+KvWkmUtQma5k9Ewm2AwG0uSUPT8QEAsVou8Nl5QFPdn2sjMygf7zEK0tVrMcTI78CpNXO/GvRbDffVDIciU1uD2Hr2vB7cM++PfyjAzUC98+yEVKFJgxZRtLiP27RfYpo2QlxzIhUfuzGQA3acCJ0ABh9FsxUoJmR8hGtWxbljI+slKr539C22pxeJ4wGx9Rr66x6ccVH4P7MNEqbtmY1tlpb3lTh3v5xJgjiSR6CCP1BpSocQslz+gh2OZbRc2S+SGNWEs8yhbd8dqr5MpcaZjHDFLW6//SDZfYylYM1Q5KtfMcBY+Vra3N6O1t8FTWypD2/R4IBKQDTJ1sTmDP8sWfHG1xufwOy26YmTTB7DrEOVknCeA9Yc6BKii5k43UgVFm1T8wLVOW2nsUDBj2iYghKNeEtOimotxHybjpg5qeLgO/hV9N0VPjRnAyeCJ0dTvRiXIvj3fhAQFaTXUBvHRhzCtZ1UowmxllMtFn/F4pVwNyHifowJOU15DNYKevkDF06mlFbNgQ4W47HWGaemMComSMZGqB0jT4tA13X1MN6dpkOZBvwokvp7llgK5D0k+fOqWZskiuangy/2gcX0bQEZjWCAPQoF1s9YMtSKge8YJr6Bxov3xICWacsbFAbOxS6Jj2wJAFMYKVIo9NwOt8MPnLL4rS5GaQrPYjRJYDJOupQlx2jNAcs+2C8gSdNJTstK/l+qWIusqmOeQEHYJ1QkxZ7csvyMgeLfroIev8U16gGxpXs54P4brxcX4QhqgYp3doA52X6kbUJHIei2e+ZOU7soNAwHO7soyF3d2BLF2GcmT1LzB/M0ubtFyYh435pxtgCFwavWbLTYkvu6d9qEMcvIhxw==", - "session.encryption.key": "AgCjw3K7solE1INH+VIwEeP2U70HCaY25Iu6sq5C7j8nuW8JzbSCBPeY+qTS76l4sIyLlLekk7ke5kCPpZo+Jsz62UQJMGaSASRy0FKQIxgn5UIkObB+qziGjC8WNj6y/JmbvPNiIp+aw0Nd3OmVLRkvIjJDkyMf/iW1oGeLctwRop9Y+wi7lDPyEHH+CaNPCLYidhAJFG785qibqY8jTxtHsEts2T+hSXz75v5abYS+lTFtFV7RDWudkWG6dPjUKwYGw7l/yQm5p2qzzvhoyCrQItdNuEvGrZQsnXZlq4ik0C7t977uiU+fumYKFQvHzVuQTMjjo9G5AlhUG/4w2olMqOMZoJ0u+hHFBX1K1uYCUr1hd2343jd+y86iObZzTazlTGdP142lXk9+CT+akpQIVX0Mk5mwtemCYG+64ebSQm+SVB9Ffgxx3Ips6BOhqwIO0fLB1iz4BFVUp+87juyHZOP/5uC+KeV3+DHaQDrHRo6DxF5D5CRXBCcHXeWBLkDXSENgD4kvRee7fEQf60ajvi9VFTPjk86KXCbOZVlZPPrqWG7Dh+kaYOjWCQXQvFzYAq6/Dr5ZDRPJ/X989zHjPFFGRQ7Ol9uyjT/7I/2IBfdQB1Ep5BWty50PBVIqbYKRzQN0g0x5BAj5/2J9mOX/0BNGExVCNn53rfc8yDurfP0q1nbYk1t+fv0T9oI7RTPYIVWtUnshG8fwYK1PARNYBbKrGwjWtTdVxzV+ocunmmOHQhvOFrTnsfupGQ==", - "smtp_password.txt": "AgB8pRbQxScSBRty04Z5oen7F52Y7PnwF9gJ6fdbUTRobE7j2fWiHMQk4l1FTztW3Muz3B/MQZBgBEzulpkDJ1TrvTe5I7JFdANr3lmlKjy0CG2NcQ7Vma7Bqu2SVnL8wxi/3IBTGxVa6bplGrkUCsIr/qWLhkVknOpAaUvOUuECT8PVxUZq/s4UMddkEf4N3IQW7j/60xwzomMp4e5eZ8YTH0/ZGcuw9JOQSOoJDzvJ5B+wZF7q7j9LQc4gBPHQiFFMVlZrhHA0fdjfPxbb7QEiGdMBxeZ0JqaaFBnr4FG7hRY5/H9RS9iZCFH9QY6X86ul9ZgiJYVyrvZFKSBsW+THlLZ7MACLvOs/zdXhpc2SthccoK8ykGC/inWTnfodQpH7N4zJq7akXxFy9/em4ssjWul4coeLNc+xhyi5VauLQQfRpgijc1kVJeQbrUrMe+aE1tlRdgRx76OKiFym1t47+zN3eqRf+NegNBGPSdAtKEBKfNPrcFQmlZVR6fRZqiBxG0wE90nfq9fuw3RDnVU/0LsuxwTz99gWMWl8ySuo+EAhf+JmB26/Pi1hnByeNeD8FsVkUiC6XnwAVC+ytnaVEfatrKmSJyzRhTSuFUoFxD5YV0aQgILks4+CpQwn300s0bqkLOP0etigZCCPq4cR+t+FFYAkuVjr+xPsoww0UOgokHsZKoXYi0n96UjbT0w+zoHhqQTBrPPWhPjDogZBKBCnth5eUtgrhSA2", - "storage.encryption.key": "AgBt8lTRtRqn+PiTNjgmJ97/rEqKEE70KZLuVtyvBM2ek70E555vx16BsCN3ATvwcr5dw+nAwYKpHJzS+mgTloLWmz6L2HqtxhlnaKC7elFeOza7tPKmKeKV3zXQ8YEA9eWjKdUN2TzO5s10iPCp4esUy5asO5DS/QVyPHMUfufu3iwHCDgnDEGlQ0pO1LMT2FwJ2QYX4Cub+OjUVrkBm8VbNdFqIw3OBtahRX2gx+o1d55psfi08ylEMKUwkHXYtkBheDvYL7SRv8xHKHGrN1loWHZOby1GOTzOnOpL6mjJoU+4nlvEzVwxzuPOHDq24cfey7engu0IfrZ+9Fs+icFjInY7cpowlNl0CXIWQm++ljtKVOUGUB2Bp6IwY1iwu3NVlwYVoZRCfSw5rLhccXnODYjXGMAwHF1cau4K8mkx1mD48dJnQE+y6AfrOPH+tCJ0dSDyuebNNlcE9HDmJseIGDP37Z3E/S9CCGprvLCdUYeOhXERyTevm5VJoboYq1/mhonPvY0bU4w3zVV3mXgr3Xi4SOupneUL+XfuQASRz1eGoQpAGxI8h4qWkgA66f8uPTh0hdM1JfLus9oXCtFsiYJzd+VtIZE+0JtI4ztGtm2JtgH80t2eiJTQolQihfpAXazg9fQSPH6NHsFcRplqC7emxdBZnI2F+TSjqcoX+RA78z+J2OP4ISs4ngF8USmm9/rJIRUq26XNLMjktSREBh/IwrpZWgiZqU/QzMhNriClH3n4vDHWJ03Xiw==" - } - } -} +--- +kind: SealedSecret +apiVersion: bitnami.com/v1alpha1 +metadata: + name: secrets-authelia + namespace: apps-roboces +spec: + template: + metadata: + name: secrets-authelia + namespace: apps-roboces + type: Opaque + encryptedData: + authentication.ldap.password.txt: AgBwsqdgEOQ8r/yUOD82Cwr44jxqV9RPDTzMYju8IfWx7b4iDKtZVidLK4RxBCp1+MPyWmLs7H4jec3Xsr10da/V+LaS1Elib+Sp2CkFZPGj1/XAcu5w3k/BnMcsRWJEJtzU251NVon6IVEzszcGOaVsPqf6aIvnKgOJEmp1boMlnemkvtaeXVS0TuSLXCQEMDfGMh9X8D9je315Sdqpczo8YLz6jyj6RxlgXeygRXkXANd2KFOyfLNUAnzm4Tl6PMjULnXvsxLhlc35+am6IsZGVLcdRMPY12UpHeZyVIgJBZSdo/gLTJS/hLhL2sPJv8HksL6YfvDnIWqu3PxVwI3BGtW2znBQaEIFXtOIXL9EDG0NMAZSBYCRpjrPNHP7xj7ruqjg4eU1iUj6yJpIv9322L/qhFWViK9wXc3+0L2n2q4qVBgrZxcacgqtHrCYj5Uo9iluVPWwagcKFYRXBupqaUUTso7LtSsv4WbOrJzYzE9mZk+feH1ZNvBp13JfWNdRiso5rpl0tZFJ7oW4G/CJuWg2MmL3UNViYB/ntwFWZWT8dxCEq2LI3UxeawHntzvk82kFyZduLUwhLvY4WfQB4yb//gBOpc0OyHa+djwECIyy3Jh+70LG+1YeVc8yQm52eu9+mzRSwDlqspKZYfwbMPCCiQj6AiSfg9Ho5+ITq1PgEt3oo9b0JkYFmEAgT88Y6z0DRQrM9YOXCfazMg== + identity_providers.oidc.hmac.key: AgCMb1NgRFW7jB8xAXDzTDK336k1w63FNJGJPj9fUrkM/5QT6WuAAPYPeD/3PWG/+0XjUycU4Q2PK/ei8rK6BJ1jwdSRQqBe/ybdxl3WCWGmjB2/jKLIAFQonJdgIzZiY4rUHUepQqhva/wA0eM1nCV/KYuOCIIzdNAGj3E+zyP9yquZcDxknadLuvY6XbTTCdsdwYgFP9qYy8+Vg/K8c2GGubuU34uLkfCG3afow9/gvtSDU5lsZwGtZPcYibOVULQ/CEiGiXBXs7g5W1HjqcJdPaJZDfkG2hPWh4aWMxMkjORt/9NrNmBi70e4LW5cq32wX0Mz+UblDf66Qz3wnGmZ93owAw1N0MtlI/V0W2XvSnxcXvmloHGfbll2rhFiUFPFmWq71Uv52Qc8rAMOeuJgRnrlG3QHxLFkTonMYcasQrfm8MjHpaNkemsdA6XO9VvrRVxLyN31lV+2bbeqh2wHZgCRIbU4gUyApi69Rd1TyEYVW9QSxWbPkX5B5eaCqDCZLX9aQocfElrKk/zzvY2J53s07AkvsowgwbWhwpzwAYB2ajBndAcNhzS311ve1kzZlp498ePGCdpcyuTJfVWTKoXdmJu3xSg0P7Czts/zstQYGSpp+0I4gwzYTtSuf1M841Obc78l2PaUqN5P3YIHdq4ZiMTWPHhqgerobyf6XxtXcO5iEf7zSGYexRwaTroMp/Sqx9P0eTX1rufwkmvMlPH6mClr3RMVGno7uFuC1SkRM3Ox0a4wJ0Y2B5IX/7C+Pd68Z1VQX+wJeson0X2K + identity_providers.oidc.jwks.key.pem: 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 + identity_validation.reset_password.jwt.hmac.key: AgAQRknTOoC5tbCitLZkRtcIs3VOaubEvlhRmGtHh/OS0v6lgU2Gcl5C6jisu/+MqQba4euzMACLVgSMiEkERY2GrMijecfVf5LOkKKwQ0Xjiw4a+MlSVs5KR1Ovlz414//3GfUCttunnDRnH6xQbsQjWLCI6lv6Umb40TQHOnRmb83wmSkBa/P9WR35EQ6IcES3q7Ad4ZpNtLaavEnB8NuLZg3yaJpN+1yuZPeMgTPUtTG/e9FLdBs3S8AYFIcrUnDLViUo9DfXumMs1DlTsmABpjyIgjIeywOmCxMYZYvQCJrRGYlKZRdoCoafhAPxt67hdUYOe56cq3kqlTNTdURMbYup7cIkW2hQ8ZiyPvYk++RIhV/cPWOSjh6CLxMq26Si1c2Lg3aAVFxQcDiXbdprjHzbKzFfKO/5LVnhT7O651BqkCguPC2vVoaeuxblHjqouBD7ZmPazIQ9J/uDUmvm5UPjEN+Ln+enrLXdoJN9HMor410/PJ+9xPAHXhdrXRS6j5TlN5q1pz0gj2Qmnv3cunsyLo+ZJ97F3EkSYPVoK3sMXGn2fdKy/lc9vPMMvtH/tLdhWefxd25APahStZHv4Mb7O6Z+A4uFiP9Qao64jeLj08LWBbJPefT7Y+KthugN6cW4y/xQN4UFvpPHhGB36yb+45eIBzOJHltOqZ+wldjUHeBgMdkODAQYYbCzhDuVVm2iMGQqUi/Vk+EayaV70prcAELgRskseME2MwfrhYMAKS91tfMbL2dGTQ== + oidc.audiobookshelf.client_secret: AgB5usnmgdDGm4MjtMTZDxhGb6qCxoMqx/2f3tZ7JYDgoVEspeL2tFBxXBOUDamCxpnuSp12O1ZcUpOw+9XAj4079vG7sWHAzjX0nIIJCNIjH3Y6+rhGh7J7nnWc9UJqzeWM6T4k9Dntyhsn96Xp5RDSUqhH68/u4OeqwzRJJofHlbqLmYq6+bq4MZfv/wZIsqfA02aWFJk7ST2qqD1Sc+oz0HkP8jXk9WwCCnRCuZoZgYUTHbPZAgOLMMRTlqTF4jHjFQ5B+IQTh2z53Agq6G8QLmU49J7RtPIunG+2Gf71KYXIeKktH8QwI9nBof1PXGYMmOvCCzqoBLsIBqYg/iAQ+kjlkbJov/3SBk+uwdj/wvG99+AJPVB2VHr8rinW0WXptzUATqYrHg0ufBxX4lKvVhqdkY7AaHPoI5ZwKi7AYftuo0IxDHGYC4GXVg9mWY7T6nNcvqB6s1epBNDjQnsCk+krxfZ83OecHUA4apwrSX4/Ytc+GkuQ/eJcdr9SoMXW7yzFfOLMh4+8syH7xigLwszw0t6lxJ37DnVbuYewhvazIU2TBWyTtQYnBeqVm2Wllh6xZiP5PyJ+VEi2fwGZVW6NX0Hh0gVmYNduj8xAJS4GRLwE2cOrN99kTMIXBdqmjXdYuAQKYDMlWU8QtTqtsoa4Y3UV/s7f7DdVtW+nIwxHFStNSQE4kOqPtUpgKbJPpPTjlPmIhW5Zm1gj3eOWIRLRV6UAkiJ2t6T2BEh+uqhcf26pbSjbfnqSEPVhfAzarnFV32Dkgue0I6rka/GPlf4fSK89JuXT9Igwdm0cKwOepYynZIf0NSSB1bWcgTTuL3N4CjteWc/XzacFkXvWuNJ3/WgN/brGZb2nRmmHEA== + session.encryption.key: AgCjw3K7solE1INH+VIwEeP2U70HCaY25Iu6sq5C7j8nuW8JzbSCBPeY+qTS76l4sIyLlLekk7ke5kCPpZo+Jsz62UQJMGaSASRy0FKQIxgn5UIkObB+qziGjC8WNj6y/JmbvPNiIp+aw0Nd3OmVLRkvIjJDkyMf/iW1oGeLctwRop9Y+wi7lDPyEHH+CaNPCLYidhAJFG785qibqY8jTxtHsEts2T+hSXz75v5abYS+lTFtFV7RDWudkWG6dPjUKwYGw7l/yQm5p2qzzvhoyCrQItdNuEvGrZQsnXZlq4ik0C7t977uiU+fumYKFQvHzVuQTMjjo9G5AlhUG/4w2olMqOMZoJ0u+hHFBX1K1uYCUr1hd2343jd+y86iObZzTazlTGdP142lXk9+CT+akpQIVX0Mk5mwtemCYG+64ebSQm+SVB9Ffgxx3Ips6BOhqwIO0fLB1iz4BFVUp+87juyHZOP/5uC+KeV3+DHaQDrHRo6DxF5D5CRXBCcHXeWBLkDXSENgD4kvRee7fEQf60ajvi9VFTPjk86KXCbOZVlZPPrqWG7Dh+kaYOjWCQXQvFzYAq6/Dr5ZDRPJ/X989zHjPFFGRQ7Ol9uyjT/7I/2IBfdQB1Ep5BWty50PBVIqbYKRzQN0g0x5BAj5/2J9mOX/0BNGExVCNn53rfc8yDurfP0q1nbYk1t+fv0T9oI7RTPYIVWtUnshG8fwYK1PARNYBbKrGwjWtTdVxzV+ocunmmOHQhvOFrTnsfupGQ== + smtp_password.txt: AgB8pRbQxScSBRty04Z5oen7F52Y7PnwF9gJ6fdbUTRobE7j2fWiHMQk4l1FTztW3Muz3B/MQZBgBEzulpkDJ1TrvTe5I7JFdANr3lmlKjy0CG2NcQ7Vma7Bqu2SVnL8wxi/3IBTGxVa6bplGrkUCsIr/qWLhkVknOpAaUvOUuECT8PVxUZq/s4UMddkEf4N3IQW7j/60xwzomMp4e5eZ8YTH0/ZGcuw9JOQSOoJDzvJ5B+wZF7q7j9LQc4gBPHQiFFMVlZrhHA0fdjfPxbb7QEiGdMBxeZ0JqaaFBnr4FG7hRY5/H9RS9iZCFH9QY6X86ul9ZgiJYVyrvZFKSBsW+THlLZ7MACLvOs/zdXhpc2SthccoK8ykGC/inWTnfodQpH7N4zJq7akXxFy9/em4ssjWul4coeLNc+xhyi5VauLQQfRpgijc1kVJeQbrUrMe+aE1tlRdgRx76OKiFym1t47+zN3eqRf+NegNBGPSdAtKEBKfNPrcFQmlZVR6fRZqiBxG0wE90nfq9fuw3RDnVU/0LsuxwTz99gWMWl8ySuo+EAhf+JmB26/Pi1hnByeNeD8FsVkUiC6XnwAVC+ytnaVEfatrKmSJyzRhTSuFUoFxD5YV0aQgILks4+CpQwn300s0bqkLOP0etigZCCPq4cR+t+FFYAkuVjr+xPsoww0UOgokHsZKoXYi0n96UjbT0w+zoHhqQTBrPPWhPjDogZBKBCnth5eUtgrhSA2 + storage.encryption.key: AgBt8lTRtRqn+PiTNjgmJ97/rEqKEE70KZLuVtyvBM2ek70E555vx16BsCN3ATvwcr5dw+nAwYKpHJzS+mgTloLWmz6L2HqtxhlnaKC7elFeOza7tPKmKeKV3zXQ8YEA9eWjKdUN2TzO5s10iPCp4esUy5asO5DS/QVyPHMUfufu3iwHCDgnDEGlQ0pO1LMT2FwJ2QYX4Cub+OjUVrkBm8VbNdFqIw3OBtahRX2gx+o1d55psfi08ylEMKUwkHXYtkBheDvYL7SRv8xHKHGrN1loWHZOby1GOTzOnOpL6mjJoU+4nlvEzVwxzuPOHDq24cfey7engu0IfrZ+9Fs+icFjInY7cpowlNl0CXIWQm++ljtKVOUGUB2Bp6IwY1iwu3NVlwYVoZRCfSw5rLhccXnODYjXGMAwHF1cau4K8mkx1mD48dJnQE+y6AfrOPH+tCJ0dSDyuebNNlcE9HDmJseIGDP37Z3E/S9CCGprvLCdUYeOhXERyTevm5VJoboYq1/mhonPvY0bU4w3zVV3mXgr3Xi4SOupneUL+XfuQASRz1eGoQpAGxI8h4qWkgA66f8uPTh0hdM1JfLus9oXCtFsiYJzd+VtIZE+0JtI4ztGtm2JtgH80t2eiJTQolQihfpAXazg9fQSPH6NHsFcRplqC7emxdBZnI2F+TSjqcoX+RA78z+J2OP4ISs4ngF8USmm9/rJIRUq26XNLMjktSREBh/IwrpZWgiZqU/QzMhNriClH3n4vDHWJ03Xiw== diff --git a/scripts/add-secret.py b/scripts/add-secret.py new file mode 100755 index 0000000..83c27ca --- /dev/null +++ b/scripts/add-secret.py @@ -0,0 +1,212 @@ +#!/usr/bin/env -S uv run --script +# /// script +# dependencies = ["typer", "ruamel.yaml"] +# /// + +import json +import shutil +import subprocess +import sys +from difflib import unified_diff +from pathlib import Path +from typing import Annotated, Any, Optional + +import typer +from ruamel.yaml import YAML + +app = typer.Typer(help="Add or update a secret field in an existing SealedSecret") + + +def resolve_file(file: Path) -> Path: + if file.is_dir(): + candidate = file / "sealedsecrets.yaml" + if not candidate.is_file(): + raise typer.BadParameter(f"{file} is a directory and contains no sealedsecrets.yaml") + return candidate + if not file.is_file(): + raise typer.BadParameter(f"{file} does not exist") + return file + + +def looks_like_json(text: str) -> bool: + for char in text: + if char in (" ", "\t", "\n", "\r"): + continue + return char == "{" + return False + + +def extract_header_comments(text: str) -> tuple[str, str]: + """Split leading comments/blank lines from the rest of the file.""" + lines = text.splitlines(keepends=True) + idx = 0 + while idx < len(lines): + stripped = lines[idx].strip() + if stripped == "" or stripped.startswith("#"): + idx += 1 + else: + break + return "".join(lines[:idx]), "".join(lines[idx:]) + + +def load_documents(text: str) -> list[Any]: + if looks_like_json(text): + # JSON is valid YAML, but parse it explicitly so we can normalize it to YAML output. + return [json.loads(text)] + yaml = YAML(typ="rt") + yaml.preserve_quotes = True + return list(yaml.load_all(text)) + + +def _represent_none(representer, data): + return representer.represent_scalar("tag:yaml.org,2002:null", "null") + + +def dump_documents(docs: list[Any]) -> str: + yaml = YAML(typ="rt") + yaml.default_flow_style = False + yaml.preserve_quotes = True + yaml.width = 4096 + yaml.explicit_start = True + yaml.representer.add_representer(type(None), _represent_none) + from ruamel.yaml.compat import StringIO + + stream = StringIO() + yaml.dump_all(docs, stream) + return stream.getvalue() + + +def detect_scope(doc: Any) -> str: + annotations = doc.get("metadata", {}).get("annotations", {}) or {} + for key, value in annotations.items(): + if key == "sealedsecrets.bitnami.com/cluster-wide" and str(value).lower() == "true": + return "cluster-wide" + if key == "sealedsecrets.bitnami.com/namespace-wide" and str(value).lower() == "true": + return "namespace-wide" + return "strict" + + +def seal_value(value: str, doc: Any, cert: Optional[Path]) -> str: + metadata = doc.get("metadata", {}) + spec = doc.get("spec", {}) + + name = metadata.get("name") + if not name: + raise typer.BadParameter("SealedSecret has no metadata.name") + + namespace = metadata.get("namespace") or spec.get("template", {}).get("metadata", {}).get("namespace") + if not namespace: + raise typer.BadParameter("SealedSecret has no namespace in metadata or spec.template.metadata") + + cmd = ["kubeseal", "--raw", "--from-file=/dev/stdin"] + if cert: + cmd.extend(["--cert", str(cert)]) + + scope = detect_scope(doc) + if scope == "cluster-wide": + cmd.extend(["--scope", "cluster-wide"]) + elif scope == "namespace-wide": + cmd.extend(["--namespace", namespace, "--scope", "namespace-wide"]) + else: + cmd.extend(["--namespace", namespace, "--name", name]) + + result = subprocess.run( + cmd, + input=value, + text=True, + capture_output=True, + ) + if result.returncode != 0: + typer.echo(f"kubeseal failed:\n{result.stderr}", err=True) + raise typer.Exit(result.returncode) + + return result.stdout.strip() + + +def find_target_document(docs: list[Any], key: str, secret_name: Optional[str]) -> Any: + if secret_name: + for doc in docs: + if doc.get("metadata", {}).get("name") == secret_name: + return doc + raise typer.BadParameter(f"No SealedSecret named '{secret_name}' found in file") + + if len(docs) == 1: + return docs[0] + + candidates = [ + doc for doc in docs + if key in (doc.get("spec", {}).get("encryptedData", {}) or {}) + ] + if len(candidates) == 1: + return candidates[0] + if len(candidates) > 1: + names = [doc.get("metadata", {}).get("name", "") for doc in candidates] + raise typer.BadParameter( + f"Key '{key}' exists in multiple SealedSecrets ({', '.join(names)}). " + "Use --secret-name to choose one." + ) + + names = [doc.get("metadata", {}).get("name", "") for doc in docs] + raise typer.BadParameter( + f"File contains multiple SealedSecrets and key '{key}' does not exist yet. " + f"Available: {', '.join(names)}. Use --secret-name to choose one." + ) + + +@app.command() +def add( + name: Annotated[str, typer.Option("--name", help="Key name in SealedSecret encryptedData")], + secret: Annotated[str, typer.Option("--secret", help="Plaintext secret value")], + file: Annotated[Path, typer.Option("--file", help="Path to sealedsecrets.yaml or its directory")], + secret_name: Annotated[ + Optional[str], + typer.Option("--secret-name", help="Name of the SealedSecret resource to update (required for multi-secret files when the key is new)"), + ] = None, + cert: Annotated[ + Optional[Path], + typer.Option("--cert", help="Path to a kubeseal certificate for offline sealing"), + ] = None, + dry_run: Annotated[bool, typer.Option("--dry-run", help="Show diff without writing")] = False, +) -> None: + target_path = resolve_file(file) + original_text = target_path.read_text() + header, body = extract_header_comments(original_text) + + docs = load_documents(body) + if not docs: + raise typer.BadParameter(f"{target_path} contains no documents") + + target = find_target_document(docs, name, secret_name) + sealed = seal_value(secret, target, cert) + + if "spec" not in target or target["spec"] is None: + target["spec"] = {} + if "encryptedData" not in target["spec"] or target["spec"]["encryptedData"] is None: + target["spec"]["encryptedData"] = {} + + old_value = target["spec"]["encryptedData"].get(name) + action = "updated" if old_value is not None else "added" + target["spec"]["encryptedData"][name] = sealed + + new_text = header + dump_documents(docs) + + if dry_run: + diff = unified_diff( + original_text.splitlines(keepends=True), + new_text.splitlines(keepends=True), + fromfile=str(target_path), + tofile=str(target_path), + ) + sys.stdout.writelines(diff) + raise typer.Exit() + + backup_path = target_path.with_suffix(target_path.suffix + ".bak") + shutil.copy2(target_path, backup_path) + target_path.write_text(new_text) + + typer.echo(f"{action} '{name}' in SealedSecret '{target.get('metadata', {}).get('name')}' ({target_path})") + typer.echo(f"backup saved to {backup_path}") + + +if __name__ == "__main__": + app() From 18ef9d87c1e86e6ec1a5766033411f523f4c568d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Wed, 19 Aug 2026 12:14:46 +0200 Subject: [PATCH 08/19] chore: fix audiobookshelf's authelia redirect_uris --- k8s/argo-apps/authelia.yaml | 4 ---- k8s/argo-apps/lldap.yaml | 4 ++-- k8s/services/argo/project-fuku.yaml | 1 + 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/k8s/argo-apps/authelia.yaml b/k8s/argo-apps/authelia.yaml index 5c34567..df6b385 100644 --- a/k8s/argo-apps/authelia.yaml +++ b/k8s/argo-apps/authelia.yaml @@ -143,12 +143,8 @@ spec: require_pkce: true pkce_challenge_method: S256 redirect_uris: - - https://audiobooks.roboces.dev/auth/openid/callback - - https://audiobooks.roboces.dev/auth/openid/mobile-redirect - https://audiobooks.roboces.dev/audiobookshelf/auth/openid/callback - https://audiobooks.roboces.dev/audiobookshelf/auth/openid/mobile-redirect - - https://audiobooks.fuku/auth/openid/callback - - https://audiobooks.fuku/auth/openid/mobile-redirect - https://audiobooks.fuku/audiobookshelf/auth/openid/callback - https://audiobooks.fuku/audiobookshelf/auth/openid/mobile-redirect - audiobookshelf://oauth diff --git a/k8s/argo-apps/lldap.yaml b/k8s/argo-apps/lldap.yaml index fe54655..c2218b2 100644 --- a/k8s/argo-apps/lldap.yaml +++ b/k8s/argo-apps/lldap.yaml @@ -10,7 +10,7 @@ spec: namespace: apps-fuku server: https://kubernetes.default.svc sources: - - repoURL: oci://git.roboces.dev/catalin/fukuops + - repoURL: git.roboces.dev/catalin/fukuops chart: lldap targetRevision: 1.0.1 helm: @@ -37,7 +37,7 @@ spec: pathType: Prefix config: ldapBaseDn: "dc=fuku,dc=local" - httpUrl: "https://ldap.fuku" + httpUrl: "https://ldap.fukurokuju.dev" persistence: enabled: true storageClass: "truenas-nfs-csi" diff --git a/k8s/services/argo/project-fuku.yaml b/k8s/services/argo/project-fuku.yaml index 9ce27d7..5f3a9d8 100644 --- a/k8s/services/argo/project-fuku.yaml +++ b/k8s/services/argo/project-fuku.yaml @@ -28,3 +28,4 @@ spec: - git.roboces.dev/catalin/fukuops - https://kubereboot.github.io/charts - https://charts.goauthentik.io/ + - https://git.roboces.dev/catalin/fukuops From 173633c6f1aa1899000edd216420c6d619216862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Wed, 19 Aug 2026 15:28:46 +0200 Subject: [PATCH 09/19] feat: add pgadmin argo app --- k8s/argo-apps/audiobookshelf.yaml | 1 + k8s/argo-apps/authelia.yaml | 2 +- k8s/argo-apps/authentik.yaml | 4 +-- k8s/argo-apps/pgadmin.yaml | 48 +++++++++++++++++++++++++++++ k8s/argo-apps/portainer.yaml | 4 +++ k8s/services/argo/project-fuku.yaml | 1 + scripts/k8s-ports.sh | 45 +++++++++++++-------------- 7 files changed, 79 insertions(+), 26 deletions(-) create mode 100644 k8s/argo-apps/pgadmin.yaml diff --git a/k8s/argo-apps/audiobookshelf.yaml b/k8s/argo-apps/audiobookshelf.yaml index 835cc54..c1863e2 100644 --- a/k8s/argo-apps/audiobookshelf.yaml +++ b/k8s/argo-apps/audiobookshelf.yaml @@ -17,6 +17,7 @@ spec: valuesObject: service: type: LoadBalancer + port: 8081 ingress: enabled: true className: traefik diff --git a/k8s/argo-apps/authelia.yaml b/k8s/argo-apps/authelia.yaml index df6b385..c3096c2 100644 --- a/k8s/argo-apps/authelia.yaml +++ b/k8s/argo-apps/authelia.yaml @@ -15,7 +15,6 @@ spec: targetRevision: 0.11.6 helm: valuesObject: - enabled: true pod: autoscaling: @@ -30,6 +29,7 @@ spec: memory: 1024Mi service: type: LoadBalancer + port: 9091 ingress: enabled: true className: traefik diff --git a/k8s/argo-apps/authentik.yaml b/k8s/argo-apps/authentik.yaml index 193c947..9479e43 100644 --- a/k8s/argo-apps/authentik.yaml +++ b/k8s/argo-apps/authentik.yaml @@ -64,8 +64,8 @@ spec: minAvailable: 1 service: type: LoadBalancer - servicePortHttp: 9000 - servicePortHttps: 9443 + servicePortHttp: 9001 + servicePortHttps: 9444 annotations: traefik.ingress.kubernetes.io/service.serversscheme: https traefik.ingress.kubernetes.io/service.serverstransport: apps-fuku-skipverify-authentik@kubernetescrd # yamllint disable rule:line-length diff --git a/k8s/argo-apps/pgadmin.yaml b/k8s/argo-apps/pgadmin.yaml new file mode 100644 index 0000000..225898f --- /dev/null +++ b/k8s/argo-apps/pgadmin.yaml @@ -0,0 +1,48 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: pgadmin + namespace: argo-cd +spec: + destination: + name: '' + namespace: apps-fuku + server: https://kubernetes.default.svc + project: fuku + sources: + - chart: pgadmin4 + repoURL: https://helm.runix.net + targetRevision: 1.49.0 + helm: + valuesObject: + image: + tag: "9.7" + service: + type: LoadBalancer + port: 8010 + ingress: + enabled: true + ingressClassName: traefik + hosts: + - host: pgadmin.fuku + paths: + - path: / + pathType: Prefix + env: + email: admin@fukurokuju.dev + enhanced_cookie_protection: "False" + existingSecret: secrets-pgadmin + persistentVolume: + enabled: true + existingClaim: pgadmin-pgadmin4 + accessModes: + - ReadWriteMany + size: 10Gi + storageClass: truenas-nfs-csi + test: + enabled: false + syncPolicy: + automated: + prune: true + selfHeal: true diff --git a/k8s/argo-apps/portainer.yaml b/k8s/argo-apps/portainer.yaml index 3a16da4..691cbc9 100644 --- a/k8s/argo-apps/portainer.yaml +++ b/k8s/argo-apps/portainer.yaml @@ -20,6 +20,9 @@ spec: valuesObject: service: type: LoadBalancer + httpPort: 9002 + httpsPort: 9445 + edgePort: 8005 ingress: enabled: true ingressClassName: traefik @@ -27,6 +30,7 @@ spec: - host: containers.fukurokuju.dev paths: - path: / + port: 9002 tls: [] persistence: storageClass: truenas-nfs-csi diff --git a/k8s/services/argo/project-fuku.yaml b/k8s/services/argo/project-fuku.yaml index 5f3a9d8..5a1ac75 100644 --- a/k8s/services/argo/project-fuku.yaml +++ b/k8s/services/argo/project-fuku.yaml @@ -25,6 +25,7 @@ spec: - ghcr.io/antoniolago/charts - https://repo.helmforge.dev - https://harish2k01.github.io/helm-charts/ + - https://helm.runix.net - git.roboces.dev/catalin/fukuops - https://kubereboot.github.io/charts - https://charts.goauthentik.io/ diff --git a/scripts/k8s-ports.sh b/scripts/k8s-ports.sh index 534d980..ca5b2cf 100755 --- a/scripts/k8s-ports.sh +++ b/scripts/k8s-ports.sh @@ -92,6 +92,19 @@ fi SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" K8S_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +extract_lb_port() { + local file="$1" + local field="$2" + local selector="$3" + local value + + value=$(yq -r '.. | objects | select(.type == "LoadBalancer") | '"$selector"' // empty' "$file" 2>/dev/null || true) + if [[ -n "$value" ]] && [[ "$value" != "null" ]] && [[ "$value" =~ ^[0-9]+$ ]]; then + PORTS+=("$value") + [[ $VERBOSE -eq 1 ]] && echo "[DEBUG] Found $field $value in $file" + fi +} + collect_ports() { PORTS=() @@ -104,29 +117,15 @@ collect_ports() { continue fi - lb_port=$(yq -r '.. | objects | select(.type == "LoadBalancer") | select(.port != null) | .port // empty' "$file" 2>/dev/null || true) - if [[ -n "$lb_port" ]] && [[ "$lb_port" != "null" ]] && [[ "$lb_port" =~ ^[0-9]+$ ]]; then - PORTS+=("$lb_port") - [[ $VERBOSE -eq 1 ]] && echo "[DEBUG] Found .port $lb_port in $file" - fi - - service_port_http=$(yq -r '.. | objects | select(.type == "LoadBalancer") | .servicePortHttp // empty' "$file" 2>/dev/null || true) - if [[ -n "$service_port_http" ]] && [[ "$service_port_http" != "null" ]] && [[ "$service_port_http" =~ ^[0-9]+$ ]]; then - PORTS+=("$service_port_http") - [[ $VERBOSE -eq 1 ]] && echo "[DEBUG] Found servicePortHttp $service_port_http in $file" - fi - - service_port_https=$(yq -r '.. | objects | select(.type == "LoadBalancer") | .servicePortHttps // empty' "$file" 2>/dev/null || true) - if [[ -n "$service_port_https" ]] && [[ "$service_port_https" != "null" ]] && [[ "$service_port_https" =~ ^[0-9]+$ ]]; then - PORTS+=("$service_port_https") - [[ $VERBOSE -eq 1 ]] && echo "[DEBUG] Found servicePortHttps $service_port_https in $file" - fi - - service_dns_port=$(yq -r '.. | objects | select(.type == "LoadBalancer") | .dns.port // empty' "$file" 2>/dev/null || true) - if [[ -n "$service_dns_port" ]] && [[ "$service_dns_port" != "null" ]] && [[ "$service_dns_port" =~ ^[0-9]+$ ]]; then - PORTS+=("$service_dns_port") - [[ $VERBOSE -eq 1 ]] && echo "[DEBUG] Found dns.port $service_dns_port in $file" - fi + extract_lb_port "$file" ".port" ".port" + extract_lb_port "$file" "servicePortHttp" ".servicePortHttp" + extract_lb_port "$file" "servicePortHttps" ".servicePortHttps" + extract_lb_port "$file" "dns.port" ".dns.port" + extract_lb_port "$file" "httpPort" ".httpPort" + extract_lb_port "$file" "httpsPort" ".httpsPort" + extract_lb_port "$file" "edgePort" ".edgePort" + extract_lb_port "$file" "ldapPort" ".ldapPort" + extract_lb_port "$file" "servicePort" ".servicePort" done < <(find "$K8S_DIR/k8s" -name "*.yaml" -type f 2>/dev/null) || true if command -v kubectl &>/dev/null && kubectl cluster-info &>/dev/null 2>&1; then From 9a2165912795fc9c27d65f5ccf20b51a475ac679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Wed, 19 Aug 2026 16:25:52 +0200 Subject: [PATCH 10/19] feat: change svc ports for adguard, vault and vault-sm --- k8s/argo-apps/adguard.yaml | 3 +++ k8s/argo-apps/vault-sm.yaml | 3 ++- k8s/argo-apps/vaultwarden.yaml | 2 +- k8s/charts/vaultwarden/Chart.yaml | 2 +- k8s/charts/vaultwarden/values.yaml | 4 ++-- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/k8s/argo-apps/adguard.yaml b/k8s/argo-apps/adguard.yaml index c36b601..de1880e 100644 --- a/k8s/argo-apps/adguard.yaml +++ b/k8s/argo-apps/adguard.yaml @@ -41,3 +41,6 @@ spec: service: dns: type: LoadBalancer + web: + type: LoadBalancer + port: 8006 diff --git a/k8s/argo-apps/vault-sm.yaml b/k8s/argo-apps/vault-sm.yaml index 60f0564..d017a0b 100644 --- a/k8s/argo-apps/vault-sm.yaml +++ b/k8s/argo-apps/vault-sm.yaml @@ -26,6 +26,7 @@ spec: enabled: true service: type: LoadBalancer + port: 8007 ingress: enabled: true className: traefik @@ -35,7 +36,7 @@ spec: - path: / pathType: Prefix backend: dashboard - port: 80 + port: 8007 - path: /api pathType: Prefix backend: api diff --git a/k8s/argo-apps/vaultwarden.yaml b/k8s/argo-apps/vaultwarden.yaml index 11573e5..7ff01e2 100644 --- a/k8s/argo-apps/vaultwarden.yaml +++ b/k8s/argo-apps/vaultwarden.yaml @@ -12,7 +12,7 @@ spec: sources: - repoURL: git.roboces.dev/catalin/fukuops chart: vaultwarden - targetRevision: 1.0.2 + targetRevision: 1.0.3 helm: valuesObject: vaultwarden: diff --git a/k8s/charts/vaultwarden/Chart.yaml b/k8s/charts/vaultwarden/Chart.yaml index 3d3255d..8ff58dd 100644 --- a/k8s/charts/vaultwarden/Chart.yaml +++ b/k8s/charts/vaultwarden/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: vaultwarden description: A Helm chart for Vaultwarden password manager type: application -version: 1.0.2 +version: 1.0.3 appVersion: 1.37.1 annotations: artifacthub.io/images: "- name: vaultwarden\n image: vaultwarden/server:1.37.1-alpine\n" diff --git a/k8s/charts/vaultwarden/values.yaml b/k8s/charts/vaultwarden/values.yaml index ffee53e..c3c1baa 100644 --- a/k8s/charts/vaultwarden/values.yaml +++ b/k8s/charts/vaultwarden/values.yaml @@ -12,8 +12,8 @@ vaultwarden: securityContext: {} service: - type: ClusterIP - port: 80 + type: LoadBalancer + port: 8008 targetPort: 80 ingress: From 921be2e54c2b472b5d6b46f0edf38822583b5505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Wed, 19 Aug 2026 17:05:13 +0200 Subject: [PATCH 11/19] feat: add authelia and audiobookshelf's chart urls to the allowed repo sources --- k8s/services/argo/project-roboces.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/k8s/services/argo/project-roboces.yaml b/k8s/services/argo/project-roboces.yaml index 7749122..e38ab70 100644 --- a/k8s/services/argo/project-roboces.yaml +++ b/k8s/services/argo/project-roboces.yaml @@ -18,3 +18,5 @@ spec: - git.roboces.dev/catalin/fukuops - https://git.roboces.dev/catalin/fukuops.git - https://git.roboces.dev/catalin/huesoporro.git + - https://charts.christianhuth.de + - https://charts.authelia.com From 4701fb9f9f6730e94b1a384e151c0fd575ce7d5b Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 20 Aug 2026 05:34:00 +0000 Subject: [PATCH 12/19] chore(deps): update helm release pgadmin4 to v1.66.0 --- k8s/argo-apps/pgadmin.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/argo-apps/pgadmin.yaml b/k8s/argo-apps/pgadmin.yaml index 225898f..d961954 100644 --- a/k8s/argo-apps/pgadmin.yaml +++ b/k8s/argo-apps/pgadmin.yaml @@ -13,7 +13,7 @@ spec: sources: - chart: pgadmin4 repoURL: https://helm.runix.net - targetRevision: 1.49.0 + targetRevision: 1.66.0 helm: valuesObject: image: From fc0146d26fc48a2bb537e8ae7db1a0e9b40b84df Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 21 Aug 2026 05:03:26 +0000 Subject: [PATCH 13/19] chore(deps): update docker.io/library/redis docker tag to v8.10.1 --- k8s/helm/argo-cd/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/helm/argo-cd/values.yaml b/k8s/helm/argo-cd/values.yaml index 418be05..89d4cd4 100644 --- a/k8s/helm/argo-cd/values.yaml +++ b/k8s/helm/argo-cd/values.yaml @@ -15,7 +15,7 @@ server: redis: image: repository: docker.io/library/redis - tag: 8.10.0-alpine + tag: 8.10.1-alpine repoServer: livenessProbe: From 09d1296c238e96e97c0556c7bf3f28eafc00d0db Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 21 Aug 2026 05:03:04 +0000 Subject: [PATCH 14/19] chore(deps): update code.forgejo.org/forgejo-helm/forgejo docker tag to v17.1.5 --- k8s/argo-apps/forgejo.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/argo-apps/forgejo.yaml b/k8s/argo-apps/forgejo.yaml index a6e6599..5f234e6 100644 --- a/k8s/argo-apps/forgejo.yaml +++ b/k8s/argo-apps/forgejo.yaml @@ -14,7 +14,7 @@ spec: sources: - chart: forgejo repoURL: code.forgejo.org/forgejo-helm - targetRevision: 17.1.4 + targetRevision: 17.1.5 helm: valuesObject: replicaCount: 1 From 3cee21b532cb8de6b2c89384efe34a9cf616ff27 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 22 Aug 2026 11:50:34 +0000 Subject: [PATCH 15/19] chore(deps): update helm release renovate to 46.254.* --- k8s/argo-apps/renovate.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s/argo-apps/renovate.yaml b/k8s/argo-apps/renovate.yaml index 91932e3..1384c39 100644 --- a/k8s/argo-apps/renovate.yaml +++ b/k8s/argo-apps/renovate.yaml @@ -13,7 +13,7 @@ spec: sources: - chart: renovate repoURL: https://docs.renovatebot.com/helm-charts - targetRevision: 46.251.* + targetRevision: 46.254.* helm: valuesObject: renovate: From d36233e7b8bd5f3f5353162ed0d89a35a13da5cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Sun, 23 Aug 2026 21:08:31 +0200 Subject: [PATCH 16/19] feat: add audiobooks and photos dns rewrites --- tofu/adguard/main.tf | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tofu/adguard/main.tf b/tofu/adguard/main.tf index 324220b..79f4872 100644 --- a/tofu/adguard/main.tf +++ b/tofu/adguard/main.tf @@ -134,6 +134,11 @@ resource "adguard_rewrite" "ldap" { } resource "adguard_rewrite" "audiobooks" { - domain = "audiobooks.fuku" + domain = "audiobooks.roboces.dev" + answer = "192.168.1.12" +} + +resource "adguard_rewrite" "immich" { + domain = "photos.roboces.dev" answer = "192.168.1.12" } From bd0c392b57001aa58d95da4d520d7c15c978b270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Mon, 24 Aug 2026 09:40:32 +0200 Subject: [PATCH 17/19] feat: update vault to 1.37.2 --- k8s/argo-apps/vaultwarden.yaml | 2 +- k8s/charts/vaultwarden/Chart.yaml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/k8s/argo-apps/vaultwarden.yaml b/k8s/argo-apps/vaultwarden.yaml index 7ff01e2..98009c1 100644 --- a/k8s/argo-apps/vaultwarden.yaml +++ b/k8s/argo-apps/vaultwarden.yaml @@ -12,7 +12,7 @@ spec: sources: - repoURL: git.roboces.dev/catalin/fukuops chart: vaultwarden - targetRevision: 1.0.3 + targetRevision: 1.0.4 helm: valuesObject: vaultwarden: diff --git a/k8s/charts/vaultwarden/Chart.yaml b/k8s/charts/vaultwarden/Chart.yaml index 8ff58dd..9cedbc5 100644 --- a/k8s/charts/vaultwarden/Chart.yaml +++ b/k8s/charts/vaultwarden/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: vaultwarden description: A Helm chart for Vaultwarden password manager type: application -version: 1.0.3 -appVersion: 1.37.1 +version: 1.0.4 +appVersion: 1.37.2 annotations: - artifacthub.io/images: "- name: vaultwarden\n image: vaultwarden/server:1.37.1-alpine\n" + artifacthub.io/images: "- name: vaultwarden\n image: vaultwarden/server:1.37.2-alpine\n" From 1eab0689772fb29015ac5a213333cdb2dfae9a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Mon, 24 Aug 2026 13:23:19 +0200 Subject: [PATCH 18/19] feat: add metallb --- .gitignore | 1 + README.md | 5 +++++ k8s/argo-apps/adguard.yaml | 6 +++++- k8s/argo-apps/audiobookshelf.yaml | 2 +- k8s/argo-apps/authelia.yaml | 2 +- k8s/argo-apps/authentik.yaml | 4 ++-- k8s/argo-apps/dokuwiki.yaml | 2 +- k8s/argo-apps/forgejo.yaml | 2 +- k8s/argo-apps/helm-rustical.yaml | 2 +- k8s/argo-apps/immich.yaml | 4 ++-- k8s/argo-apps/lldap.yaml | 2 +- k8s/argo-apps/metallb.yaml | 25 ++++++++++++++++++++++ k8s/argo-apps/miniflux.yaml | 2 +- k8s/argo-apps/oxicloud.yaml | 2 +- k8s/argo-apps/pgadmin.yaml | 2 +- k8s/argo-apps/portainer.yaml | 2 +- k8s/argo-apps/vault-sm.yaml | 4 ++-- k8s/argo-apps/vaultwarden.yaml | 2 +- k8s/charts/_template/values.yaml | 2 +- k8s/charts/dokuwiki/values.yaml | 2 +- k8s/charts/lldap/values.yaml | 2 +- k8s/charts/oxicloud/values.yaml | 2 +- k8s/charts/vaultwarden/values.yaml | 2 +- k8s/helm/argo-cd/values.yaml | 2 +- k8s/services/argo/project-management.yaml | 3 +++ k8s/services/metallb/l2advert-adguard.yaml | 9 ++++++++ k8s/services/metallb/l2advert-prod.yaml | 9 ++++++++ k8s/services/metallb/namespace.yaml | 5 +++++ k8s/services/metallb/pool-adguard.yaml | 10 +++++++++ k8s/services/metallb/pool-prod.yaml | 12 +++++++++++ 30 files changed, 107 insertions(+), 24 deletions(-) create mode 100644 k8s/argo-apps/metallb.yaml create mode 100644 k8s/services/metallb/l2advert-adguard.yaml create mode 100644 k8s/services/metallb/l2advert-prod.yaml create mode 100644 k8s/services/metallb/namespace.yaml create mode 100644 k8s/services/metallb/pool-adguard.yaml create mode 100644 k8s/services/metallb/pool-prod.yaml diff --git a/.gitignore b/.gitignore index e7b695a..a1df2e8 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ k8s/charts/README.md opencode.json *.pyc .venv/ +METALLB.md diff --git a/README.md b/README.md index aff389b..3da2575 100644 --- a/README.md +++ b/README.md @@ -19,3 +19,8 @@ helm upgrade argo-cd argo/argo-cd -n argo-cd -f k8s/helm/argo-cd/values.yaml ```bash scripts/users.py add -u catalin -n cătălin -e catalin@roboces.dev ``` + +## MetalLB and reserved VIPs + +- traefik VIP: 192.168.1.127 +- adguard dns VIP: 192.168.1.125 diff --git a/k8s/argo-apps/adguard.yaml b/k8s/argo-apps/adguard.yaml index de1880e..d7611da 100644 --- a/k8s/argo-apps/adguard.yaml +++ b/k8s/argo-apps/adguard.yaml @@ -41,6 +41,10 @@ spec: service: dns: type: LoadBalancer + port: 53 + annotations: + metallb.universe.tf/address-pool: adguard-dns-pool + loadBalancerIP: 192.168.1.125 web: - type: LoadBalancer + type: ClusterIP port: 8006 diff --git a/k8s/argo-apps/audiobookshelf.yaml b/k8s/argo-apps/audiobookshelf.yaml index c1863e2..d52b97a 100644 --- a/k8s/argo-apps/audiobookshelf.yaml +++ b/k8s/argo-apps/audiobookshelf.yaml @@ -16,7 +16,7 @@ spec: helm: valuesObject: service: - type: LoadBalancer + type: ClusterIP port: 8081 ingress: enabled: true diff --git a/k8s/argo-apps/authelia.yaml b/k8s/argo-apps/authelia.yaml index c3096c2..0bd9ddc 100644 --- a/k8s/argo-apps/authelia.yaml +++ b/k8s/argo-apps/authelia.yaml @@ -28,7 +28,7 @@ spec: cpu: 1 memory: 1024Mi service: - type: LoadBalancer + type: ClusterIP port: 9091 ingress: enabled: true diff --git a/k8s/argo-apps/authentik.yaml b/k8s/argo-apps/authentik.yaml index 9479e43..e421fa8 100644 --- a/k8s/argo-apps/authentik.yaml +++ b/k8s/argo-apps/authentik.yaml @@ -63,7 +63,7 @@ spec: enabled: true minAvailable: 1 service: - type: LoadBalancer + type: ClusterIP servicePortHttp: 9001 servicePortHttps: 9444 annotations: @@ -72,7 +72,7 @@ spec: metrics: enabled: true service: - type: LoadBalancer + type: ClusterIP serviceMonitor: enabled: false ingress: diff --git a/k8s/argo-apps/dokuwiki.yaml b/k8s/argo-apps/dokuwiki.yaml index 6e5216d..83e2daf 100644 --- a/k8s/argo-apps/dokuwiki.yaml +++ b/k8s/argo-apps/dokuwiki.yaml @@ -26,7 +26,7 @@ spec: drop: - all service: - type: LoadBalancer + type: ClusterIP port: 8004 ingress: enabled: true diff --git a/k8s/argo-apps/forgejo.yaml b/k8s/argo-apps/forgejo.yaml index 5f234e6..3ceecd3 100644 --- a/k8s/argo-apps/forgejo.yaml +++ b/k8s/argo-apps/forgejo.yaml @@ -20,7 +20,7 @@ spec: replicaCount: 1 service: http: - type: LoadBalancer + type: ClusterIP ssh: type: NodePort ingress: diff --git a/k8s/argo-apps/helm-rustical.yaml b/k8s/argo-apps/helm-rustical.yaml index 7fcba18..2962404 100644 --- a/k8s/argo-apps/helm-rustical.yaml +++ b/k8s/argo-apps/helm-rustical.yaml @@ -25,7 +25,7 @@ spec: targetCPUUtilizationPercentage: 80 targetMemoryUtilizationPercentage: 80 service: - type: LoadBalancer + type: ClusterIP port: 8001 targetPort: 4000 ingress: diff --git a/k8s/argo-apps/immich.yaml b/k8s/argo-apps/immich.yaml index 31e0c8b..65cca62 100644 --- a/k8s/argo-apps/immich.yaml +++ b/k8s/argo-apps/immich.yaml @@ -27,7 +27,7 @@ spec: drop: - all service: - type: LoadBalancer + type: ClusterIP config: database: hostname: "192.168.1.3" @@ -96,7 +96,7 @@ spec: machineLearning: enabled: true service: - type: LoadBalancer + type: ClusterIP secret: enabled: true existingSecretName: "immich-secrets" diff --git a/k8s/argo-apps/lldap.yaml b/k8s/argo-apps/lldap.yaml index c2218b2..577dfd3 100644 --- a/k8s/argo-apps/lldap.yaml +++ b/k8s/argo-apps/lldap.yaml @@ -17,7 +17,7 @@ spec: valuesObject: lldap: service: - type: LoadBalancer + type: ClusterIP httpPort: 17170 ldapPort: 3890 ingress: diff --git a/k8s/argo-apps/metallb.yaml b/k8s/argo-apps/metallb.yaml new file mode 100644 index 0000000..7f909f5 --- /dev/null +++ b/k8s/argo-apps/metallb.yaml @@ -0,0 +1,25 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: metallb + namespace: argo-cd +spec: + destination: + name: '' + namespace: metallb-system + server: https://kubernetes.default.svc + sources: + - repoURL: https://metallb.github.io/metallb + chart: metallb + targetRevision: 0.16.* + helm: + valuesObject: + crds: + enabled: true + - repoURL: https://git.roboces.dev/catalin/fukuops.git + path: k8s/services/metallb + targetRevision: main + project: management + syncPolicy: + automated: {} diff --git a/k8s/argo-apps/miniflux.yaml b/k8s/argo-apps/miniflux.yaml index 2339c6f..af22dc8 100644 --- a/k8s/argo-apps/miniflux.yaml +++ b/k8s/argo-apps/miniflux.yaml @@ -27,7 +27,7 @@ spec: drop: - all service: - type: LoadBalancer + type: ClusterIP ingress: enabled: true className: "traefik" diff --git a/k8s/argo-apps/oxicloud.yaml b/k8s/argo-apps/oxicloud.yaml index aa5556e..fcf6b13 100644 --- a/k8s/argo-apps/oxicloud.yaml +++ b/k8s/argo-apps/oxicloud.yaml @@ -22,7 +22,7 @@ spec: accessMode: ReadWriteMany size: 50Gi service: - type: LoadBalancer + type: ClusterIP config: server: port: 8086 diff --git a/k8s/argo-apps/pgadmin.yaml b/k8s/argo-apps/pgadmin.yaml index d961954..9ff22e8 100644 --- a/k8s/argo-apps/pgadmin.yaml +++ b/k8s/argo-apps/pgadmin.yaml @@ -19,7 +19,7 @@ spec: image: tag: "9.7" service: - type: LoadBalancer + type: ClusterIP port: 8010 ingress: enabled: true diff --git a/k8s/argo-apps/portainer.yaml b/k8s/argo-apps/portainer.yaml index 691cbc9..8751358 100644 --- a/k8s/argo-apps/portainer.yaml +++ b/k8s/argo-apps/portainer.yaml @@ -19,7 +19,7 @@ spec: helm: valuesObject: service: - type: LoadBalancer + type: NodePort httpPort: 9002 httpsPort: 9445 edgePort: 8005 diff --git a/k8s/argo-apps/vault-sm.yaml b/k8s/argo-apps/vault-sm.yaml index d017a0b..22f0f3c 100644 --- a/k8s/argo-apps/vault-sm.yaml +++ b/k8s/argo-apps/vault-sm.yaml @@ -18,14 +18,14 @@ spec: api: enabled: true service: - type: LoadBalancer + type: ClusterIP persistence: storageClass: truenas-nfs-csi accessMode: ReadWriteMany dashboard: enabled: true service: - type: LoadBalancer + type: ClusterIP port: 8007 ingress: enabled: true diff --git a/k8s/argo-apps/vaultwarden.yaml b/k8s/argo-apps/vaultwarden.yaml index 98009c1..d35a61b 100644 --- a/k8s/argo-apps/vaultwarden.yaml +++ b/k8s/argo-apps/vaultwarden.yaml @@ -34,7 +34,7 @@ spec: drop: - all service: - type: LoadBalancer + type: ClusterIP ingress: enabled: true className: "traefik" diff --git a/k8s/charts/_template/values.yaml b/k8s/charts/_template/values.yaml index 7aa1f5f..5f70727 100644 --- a/k8s/charts/_template/values.yaml +++ b/k8s/charts/_template/values.yaml @@ -7,7 +7,7 @@ chart: tag: "" service: - type: LoadBalancer + type: ClusterIP port: 80 targetPort: 80 diff --git a/k8s/charts/dokuwiki/values.yaml b/k8s/charts/dokuwiki/values.yaml index f003331..31c68df 100644 --- a/k8s/charts/dokuwiki/values.yaml +++ b/k8s/charts/dokuwiki/values.yaml @@ -12,7 +12,7 @@ dokuwiki: securityContext: {} service: - type: LoadBalancer + type: ClusterIP port: 8004 targetPort: 8080 diff --git a/k8s/charts/lldap/values.yaml b/k8s/charts/lldap/values.yaml index bb19244..0a3230f 100644 --- a/k8s/charts/lldap/values.yaml +++ b/k8s/charts/lldap/values.yaml @@ -7,7 +7,7 @@ lldap: tag: "stable" service: - type: LoadBalancer + type: ClusterIP httpPort: 17170 ldapPort: 3890 diff --git a/k8s/charts/oxicloud/values.yaml b/k8s/charts/oxicloud/values.yaml index 1348f56..fa16dca 100644 --- a/k8s/charts/oxicloud/values.yaml +++ b/k8s/charts/oxicloud/values.yaml @@ -8,7 +8,7 @@ oxicloud: tag: "" service: - type: LoadBalancer + type: ClusterIP port: 8086 ingress: diff --git a/k8s/charts/vaultwarden/values.yaml b/k8s/charts/vaultwarden/values.yaml index c3c1baa..f93218f 100644 --- a/k8s/charts/vaultwarden/values.yaml +++ b/k8s/charts/vaultwarden/values.yaml @@ -12,7 +12,7 @@ vaultwarden: securityContext: {} service: - type: LoadBalancer + type: ClusterIP port: 8008 targetPort: 80 diff --git a/k8s/helm/argo-cd/values.yaml b/k8s/helm/argo-cd/values.yaml index 89d4cd4..8e618b6 100644 --- a/k8s/helm/argo-cd/values.yaml +++ b/k8s/helm/argo-cd/values.yaml @@ -8,7 +8,7 @@ configs: server: service: - type: LoadBalancer + type: ClusterIP servicePortHttp: 8002 servicePortHttps: 8003 diff --git a/k8s/services/argo/project-management.yaml b/k8s/services/argo/project-management.yaml index 180673b..141f7a2 100644 --- a/k8s/services/argo/project-management.yaml +++ b/k8s/services/argo/project-management.yaml @@ -18,6 +18,8 @@ spec: server: https://kubernetes.default.svc - namespace: argo-workflows server: https://kubernetes.default.svc + - namespace: metallb-system + server: https://kubernetes.default.svc clusterResourceWhitelist: - group: '*' kind: '*' @@ -26,3 +28,4 @@ spec: - https://democratic-csi.github.io/charts/ - https://bitnami-labs.github.io/sealed-secrets - https://kubernetes-sigs.github.io/descheduler + - https://metallb.github.io/metallb diff --git a/k8s/services/metallb/l2advert-adguard.yaml b/k8s/services/metallb/l2advert-adguard.yaml new file mode 100644 index 0000000..707c837 --- /dev/null +++ b/k8s/services/metallb/l2advert-adguard.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: adguard-dns-l2 + namespace: metallb-system +spec: + ipAddressPools: + - adguard-dns-pool diff --git a/k8s/services/metallb/l2advert-prod.yaml b/k8s/services/metallb/l2advert-prod.yaml new file mode 100644 index 0000000..6b422c2 --- /dev/null +++ b/k8s/services/metallb/l2advert-prod.yaml @@ -0,0 +1,9 @@ +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: ingress-l2 + namespace: metallb-system +spec: + ipAddressPools: + - ingress-pool diff --git a/k8s/services/metallb/namespace.yaml b/k8s/services/metallb/namespace.yaml new file mode 100644 index 0000000..928cb91 --- /dev/null +++ b/k8s/services/metallb/namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: metallb-system diff --git a/k8s/services/metallb/pool-adguard.yaml b/k8s/services/metallb/pool-adguard.yaml new file mode 100644 index 0000000..12f9c77 --- /dev/null +++ b/k8s/services/metallb/pool-adguard.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: adguard-dns-pool + namespace: metallb-system +spec: + addresses: + - 192.168.1.125/32 + autoAssign: false diff --git a/k8s/services/metallb/pool-prod.yaml b/k8s/services/metallb/pool-prod.yaml new file mode 100644 index 0000000..b090516 --- /dev/null +++ b/k8s/services/metallb/pool-prod.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: ingress-pool + namespace: metallb-system +spec: + addresses: + - 192.168.1.126/32 + # Only Traefik should get this IP. Other LoadBalancer services that are + # not yet migrated will stay pending instead of stealing the single VIP. + autoAssign: false From a852d7217b8555912cf8440d31fbea437f2e2823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?c=C4=83t=C4=83lin?= Date: Mon, 24 Aug 2026 15:41:05 +0200 Subject: [PATCH 19/19] feat: add lldap VIP --- README.md | 3 ++- k8s/argo-apps/lldap.yaml | 9 ++++++- k8s/charts/lldap/Chart.yaml | 5 ++-- k8s/charts/lldap/templates/service-ldap.yaml | 24 +++++++++++++++++++ k8s/charts/lldap/values.yaml | 7 ++++++ k8s/services/metallb/l2advert-adguard.yaml | 9 ------- k8s/services/metallb/l2advert-prod.yaml | 9 ------- ...pool-adguard.yaml => metallb-adguard.yaml} | 9 +++++++ k8s/services/metallb/metallb-ldap.yaml | 19 +++++++++++++++ .../{pool-prod.yaml => metallb-traefik.yaml} | 9 +++++++ 10 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 k8s/charts/lldap/templates/service-ldap.yaml delete mode 100644 k8s/services/metallb/l2advert-adguard.yaml delete mode 100644 k8s/services/metallb/l2advert-prod.yaml rename k8s/services/metallb/{pool-adguard.yaml => metallb-adguard.yaml} (52%) create mode 100644 k8s/services/metallb/metallb-ldap.yaml rename k8s/services/metallb/{pool-prod.yaml => metallb-traefik.yaml} (67%) diff --git a/README.md b/README.md index 3da2575..748180b 100644 --- a/README.md +++ b/README.md @@ -22,5 +22,6 @@ scripts/users.py add -u catalin -n cătălin -e catalin@roboces.dev ## MetalLB and reserved VIPs -- traefik VIP: 192.168.1.127 +- traefik VIP: 192.168.1.126 - adguard dns VIP: 192.168.1.125 +- ldap VIP: 192.168.1.124 diff --git a/k8s/argo-apps/lldap.yaml b/k8s/argo-apps/lldap.yaml index 577dfd3..b3bf3f6 100644 --- a/k8s/argo-apps/lldap.yaml +++ b/k8s/argo-apps/lldap.yaml @@ -12,7 +12,7 @@ spec: sources: - repoURL: git.roboces.dev/catalin/fukuops chart: lldap - targetRevision: 1.0.1 + targetRevision: 1.0.2 helm: valuesObject: lldap: @@ -20,6 +20,13 @@ spec: type: ClusterIP httpPort: 17170 ldapPort: 3890 + ldapService: + enabled: true + type: LoadBalancer + port: 3890 + loadBalancerIP: 192.168.1.124 + annotations: + metallb.universe.tf/address-pool: ldap-pool ingress: enabled: true className: traefik diff --git a/k8s/charts/lldap/Chart.yaml b/k8s/charts/lldap/Chart.yaml index 059a454..ebe4b49 100644 --- a/k8s/charts/lldap/Chart.yaml +++ b/k8s/charts/lldap/Chart.yaml @@ -2,11 +2,12 @@ apiVersion: v2 name: lldap description: Light LDAP implementation for authentication type: application -version: 1.0.0 +version: 1.0.2 appVersion: stable annotations: artifacthub.io/images: "- name: lldap\n image: lldap/lldap:stable\n" - artifacthub.io/changes: "- kind: added\n description: Initial chart release\n" + artifacthub.io/changes: "- kind: added\n description: Add optional LDAP LoadBalancer\ + \ service\n" artifacthub.io/maintainers: "- name: catalin\n email: catalin@roboces.dev\n" keywords: - lldap diff --git a/k8s/charts/lldap/templates/service-ldap.yaml b/k8s/charts/lldap/templates/service-ldap.yaml new file mode 100644 index 0000000..ca8aab4 --- /dev/null +++ b/k8s/charts/lldap/templates/service-ldap.yaml @@ -0,0 +1,24 @@ +{{- if .Values.lldap.ldapService.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "fullname" . }}-ldap + labels: + {{- include "labels" . | nindent 4 }} + {{- with .Values.lldap.ldapService.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.lldap.ldapService.type }} + {{- if .Values.lldap.ldapService.loadBalancerIP }} + loadBalancerIP: {{ .Values.lldap.ldapService.loadBalancerIP }} + {{- end }} + ports: + - port: {{ .Values.lldap.ldapService.port }} + targetPort: ldap + protocol: TCP + name: ldap + selector: + {{- include "selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/k8s/charts/lldap/values.yaml b/k8s/charts/lldap/values.yaml index 0a3230f..a56391b 100644 --- a/k8s/charts/lldap/values.yaml +++ b/k8s/charts/lldap/values.yaml @@ -11,6 +11,13 @@ lldap: httpPort: 17170 ldapPort: 3890 + ldapService: + enabled: false + type: LoadBalancer + port: 3890 + loadBalancerIP: "" + annotations: {} + ingress: enabled: false className: "traefik" diff --git a/k8s/services/metallb/l2advert-adguard.yaml b/k8s/services/metallb/l2advert-adguard.yaml deleted file mode 100644 index 707c837..0000000 --- a/k8s/services/metallb/l2advert-adguard.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -apiVersion: metallb.io/v1beta1 -kind: L2Advertisement -metadata: - name: adguard-dns-l2 - namespace: metallb-system -spec: - ipAddressPools: - - adguard-dns-pool diff --git a/k8s/services/metallb/l2advert-prod.yaml b/k8s/services/metallb/l2advert-prod.yaml deleted file mode 100644 index 6b422c2..0000000 --- a/k8s/services/metallb/l2advert-prod.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -apiVersion: metallb.io/v1beta1 -kind: L2Advertisement -metadata: - name: ingress-l2 - namespace: metallb-system -spec: - ipAddressPools: - - ingress-pool diff --git a/k8s/services/metallb/pool-adguard.yaml b/k8s/services/metallb/metallb-adguard.yaml similarity index 52% rename from k8s/services/metallb/pool-adguard.yaml rename to k8s/services/metallb/metallb-adguard.yaml index 12f9c77..0075d91 100644 --- a/k8s/services/metallb/pool-adguard.yaml +++ b/k8s/services/metallb/metallb-adguard.yaml @@ -8,3 +8,12 @@ spec: addresses: - 192.168.1.125/32 autoAssign: false +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: adguard-dns-l2 + namespace: metallb-system +spec: + ipAddressPools: + - adguard-dns-pool diff --git a/k8s/services/metallb/metallb-ldap.yaml b/k8s/services/metallb/metallb-ldap.yaml new file mode 100644 index 0000000..cc306c1 --- /dev/null +++ b/k8s/services/metallb/metallb-ldap.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: ldap-pool + namespace: metallb-system +spec: + addresses: + - 192.168.1.124/32 + autoAssign: false +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: ldap-l2 + namespace: metallb-system +spec: + ipAddressPools: + - ldap-pool diff --git a/k8s/services/metallb/pool-prod.yaml b/k8s/services/metallb/metallb-traefik.yaml similarity index 67% rename from k8s/services/metallb/pool-prod.yaml rename to k8s/services/metallb/metallb-traefik.yaml index b090516..22bbd9d 100644 --- a/k8s/services/metallb/pool-prod.yaml +++ b/k8s/services/metallb/metallb-traefik.yaml @@ -10,3 +10,12 @@ spec: # Only Traefik should get this IP. Other LoadBalancer services that are # not yet migrated will stay pending instead of stealing the single VIP. autoAssign: false +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: ingress-l2 + namespace: metallb-system +spec: + ipAddressPools: + - ingress-pool