Establish clean homelab infrastructure baseline
Reorganize the brownfield repository, remove retired and generated artifacts, harden ignore rules, and record the GitOps/IaC redesign.
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# The one store every namespace reads from.
|
||||
#
|
||||
# Cluster-scoped on purpose: authelia, gitea and cloudflared all consume it, and a
|
||||
# per-namespace SecretStore would mean duplicating the OpenBao connection details
|
||||
# three times.
|
||||
#
|
||||
# AUTH: no credential is stored anywhere. ESO presents its own ServiceAccount JWT,
|
||||
# OpenBao validates it against the cluster's TokenReview API, and hands back a
|
||||
# short-lived token scoped by the `external-secrets` role. The reviewer JWT that
|
||||
# makes that possible lives on the bao host, configured by
|
||||
# ../../infrastructure/openbao/ansible/roles/openbao_bootstrap/tasks/auth_kubernetes.yml — it is key
|
||||
# material, which is why Ansible owns it and Terraform does not.
|
||||
#
|
||||
# TLS: bao presents a Let's Encrypt cert for bao.ad.ddupan.top, so no caBundle or
|
||||
# caProvider is needed — verified from inside a pod (HTTP 200, ssl_verify_result 0).
|
||||
# ⚠ Address it by HOSTNAME, never 192.168.10.8: the cert carries a DNS SAN only,
|
||||
# so connecting by IP fails verification. Same trap as dc1's LDAPS cert.
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ClusterSecretStore
|
||||
metadata:
|
||||
name: openbao
|
||||
spec:
|
||||
provider:
|
||||
vault:
|
||||
# OpenBao is Vault-API compatible; ESO's vault provider drives it unchanged.
|
||||
server: 'https://bao.ad.ddupan.top:8200'
|
||||
# Mount path of the KV engine, from ../../infrastructure/openbao/terraform/mounts.tf.
|
||||
path: 'kv'
|
||||
version: 'v2'
|
||||
auth:
|
||||
kubernetes:
|
||||
mountPath: 'kubernetes'
|
||||
role: 'external-secrets'
|
||||
serviceAccountRef:
|
||||
name: 'external-secrets'
|
||||
# namespace is MANDATORY on a ClusterSecretStore (it has no namespace
|
||||
# of its own to resolve the reference against).
|
||||
namespace: 'external-secrets'
|
||||
# NOTE: Vault 1.21+ requires an `audiences: ['vault']` entry here, and
|
||||
# the bao role must declare a matching audience. OpenBao 2.6.1 does not,
|
||||
# so it is omitted — if auth ever starts failing with an audience
|
||||
# mismatch after an upgrade, this is the first thing to add.
|
||||
@@ -0,0 +1,119 @@
|
||||
# One ExternalSecret per Kubernetes Secret that was previously created by hand.
|
||||
#
|
||||
# These REPLACE the hand-applied secret.yaml files in ../../apps/authelia, ../../apps/gitea and
|
||||
# ../../infrastructure/cloudflared. Those files stay gitignored as the break-glass copy — if OpenBao
|
||||
# is down during a rebuild you can still `kubectl apply -f <svc>/secret.yaml` and
|
||||
# carry on. ESO will reconcile back over it once bao returns.
|
||||
#
|
||||
# ⚠ ESO takes OWNERSHIP of the target Secret. The existing hand-created Secrets have
|
||||
# the same names on purpose, so ESO adopts rather than duplicates them — but that
|
||||
# also means a wrong key name here will OVERWRITE a working Secret with an empty or
|
||||
# partial one. Check `kubectl get externalsecret -A` reports SecretSynced before
|
||||
# trusting it, and remember Authelia and Gitea only read their secrets at startup.
|
||||
#
|
||||
# refreshInterval is deliberately long. Nothing here rotates on its own yet, and a
|
||||
# short interval only adds load plus a wider window to notice a bad sync. When the
|
||||
# OpenBao PostgreSQL secrets engine lands (see ../docs/cicd.md) the rotating
|
||||
# credentials will want a shorter interval AND something to restart the consumer.
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: authelia-secrets
|
||||
namespace: authelia
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: openbao
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: authelia-secrets
|
||||
creationPolicy: Owner
|
||||
# extract pulls EVERY key at the path, so the six key names live in OpenBao
|
||||
# rather than being restated here. They must match the `path:` values in
|
||||
# ../../apps/authelia/values.yaml exactly.
|
||||
dataFrom:
|
||||
- extract:
|
||||
key: k8s/authelia
|
||||
---
|
||||
# Separate Secret, not a seventh key in authelia-secrets — the chart projects the
|
||||
# existingSecret volume with an explicit items: list of only the six keys it
|
||||
# generates, so an extra key would be stored but never mounted. That mistake took
|
||||
# SSO down on 2026-07-28.
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: authelia-oidc-jwks
|
||||
namespace: authelia
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: openbao
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: authelia-oidc-jwks
|
||||
creationPolicy: Owner
|
||||
dataFrom:
|
||||
- extract:
|
||||
key: k8s/authelia-oidc-jwks
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: gitea-db
|
||||
namespace: gitea
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: openbao
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: gitea-db
|
||||
creationPolicy: Owner
|
||||
dataFrom:
|
||||
- extract:
|
||||
key: k8s/gitea
|
||||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: cloudflared-tunnel
|
||||
namespace: cloudflared
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: openbao
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: cloudflared-tunnel
|
||||
creationPolicy: Owner
|
||||
dataFrom:
|
||||
- extract:
|
||||
key: k8s/cloudflared
|
||||
---
|
||||
# SeaweedFS S3 identities, including the least-privilege `terraform` identity that
|
||||
# holds Terraform remote state.
|
||||
#
|
||||
# ⚠ These credentials were previously INLINE in ../../apps/seaweedfs/values.yaml and went
|
||||
# into git with the initial commit. Externalising stops the bleeding; the leaked
|
||||
# anvAdmin key still needs rotating separately, since it is in history.
|
||||
#
|
||||
# The chart normally GENERATES seaweedfs-s3-secret from s3.credentials. We point
|
||||
# filer.s3.existingConfigSecret at this one instead, so the chart stops rendering
|
||||
# credentials from values entirely.
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: seaweedfs-s3-config
|
||||
namespace: seaweedfs
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: openbao
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: seaweedfs-s3-config
|
||||
creationPolicy: Owner
|
||||
dataFrom:
|
||||
- extract:
|
||||
key: k8s/seaweedfs-s3
|
||||
@@ -0,0 +1,43 @@
|
||||
# External Secrets Operator — pulls secret material from OpenBao into Kubernetes
|
||||
# Secrets, so the Secrets themselves become declarative instead of hand-created.
|
||||
#
|
||||
# Install:
|
||||
# helm upgrade --install external-secrets external-secrets/external-secrets \
|
||||
# -n external-secrets --create-namespace -f values.yaml
|
||||
#
|
||||
# WHY this and not SOPS: OpenBao is already the secrets store and the internal CA
|
||||
# here, and its Kubernetes auth backend is already bootstrapped
|
||||
# (../../infrastructure/openbao/ansible/roles/openbao_bootstrap/tasks/auth_kubernetes.yml), so a pod
|
||||
# authenticates with its own ServiceAccount JWT and NOTHING long-lived is stored
|
||||
# in the cluster. SOPS would mean managing an age key and committing ciphertext.
|
||||
#
|
||||
# Single node, so one replica of each component. The webhook and cert-controller
|
||||
# are not optional — the CRDs use conversion/validating webhooks.
|
||||
replicaCount: 1
|
||||
|
||||
webhook:
|
||||
replicaCount: 1
|
||||
resources:
|
||||
requests: {cpu: 10m, memory: 32Mi}
|
||||
limits: {memory: 128Mi}
|
||||
|
||||
certController:
|
||||
replicaCount: 1
|
||||
resources:
|
||||
requests: {cpu: 10m, memory: 32Mi}
|
||||
limits: {memory: 128Mi}
|
||||
|
||||
resources:
|
||||
requests: {cpu: 10m, memory: 64Mi}
|
||||
limits: {memory: 256Mi}
|
||||
|
||||
# The controller's own ServiceAccount is what the ClusterSecretStore presents to
|
||||
# OpenBao, so its name is part of the contract with the bao Kubernetes auth role
|
||||
# (bound_service_account_names). Pinned rather than left to the chart's default.
|
||||
serviceAccount:
|
||||
create: true
|
||||
name: external-secrets
|
||||
|
||||
# ClusterSecretStore is cluster-scoped; leaving this on lets one store serve every
|
||||
# namespace, which is the point here (authelia, gitea and cloudflared all consume it).
|
||||
installCRDs: true
|
||||
Reference in New Issue
Block a user