Establish clean homelab infrastructure baseline
lint / yaml (push) Has been cancelled
lint / ansible (push) Has been cancelled
lint / terraform (push) Has been cancelled

Reorganize the brownfield repository, remove retired and generated artifacts, harden ignore rules, and record the GitOps/IaC redesign.
This commit is contained in:
2026-09-09 16:47:20 +00:00
commit 88a02ababa
418 changed files with 50579 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
# Gitea holds a leveldb queue lock on its RWO /data volume, so two pods can't run
# at once. The chart's default RollingUpdate (maxSurge 100%) deadlocks on upgrade;
# Recreate terminates the old pod before starting the new one.
strategy:
type: Recreate
ingress:
enabled: true
hosts:
- host: git.ddupan.top
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- git.ddupan.top
secretName: git-ddupan-top-tls
gitea:
config:
server:
ROOT_URL: https://git.ddupan.top/
APP_NAME: Gitea on ddupan.top
service:
ENABLE_SSH: false
ALLOW_ONLY_EXTERNAL_REGISTRATION: true
SHOW_REGISTRATION_BUTTON: false
ENABLE_PASSWORD_SIGNIN_FORM: false
auth:
AUTO_REGISTER: true
database:
DB_TYPE: postgres
HOST: shared-postgresql.shared-db.svc.cluster.local:5432
NAME: gitea
USER: gitea
# PASSWD is deliberately absent — it arrives via additionalConfigFromEnvs
# below, so this file carries no credential and stays in git.
SCHEMA: public
queue:
TYPE: database
QUEUE_TYPE: database
actions:
# CI for services/ (see .gitea/workflows/lint.yml). Enabling this only turns
# on the Actions API and UI — nothing runs until an act_runner registers
# against it, so this flag alone is inert.
ENABLED: true
# Where `uses:` steps are resolved from. Left at the github default because
# github.com is reachable from this network (verified 2026-07-28) even when
# pypi.org/Fastly is not — see the flaky-WAN notes in the lint workflow.
DEFAULT_ACTIONS_URL: github
mailer:
# Outbound mail via the in-cluster Postfix+OAuth relay (see ../smtp-relay/).
# Plain SMTP on :25 — the relay does STARTTLS + OAuth to M365. From must be the
# relay's send-as identity ([email protected]) or O365 rejects with 5.7.60.
ENABLED: true
PROTOCOL: smtp
SMTP_ADDR: smtp-relay.smtp-relay.svc.cluster.local
SMTP_PORT: 25
FROM: Gitea <[email protected]>
oauth2_client:
# Auto-link an OIDC login to an existing account with the same email
# (migrating panxiao81 from the retired Keycloak source to Authelia).
ACCOUNT_LINKING: auto
ENABLE_AUTO_REGISTRATION: true
USERNAME: preferred_username
UPDATE_AVATAR: true
# ⚠ FRAGILE BY DESIGN — this block is fetched at POD START, not at login.
# The chart's `configure-gitea` INIT container runs `gitea admin auth
# update-oauth`, which resolves and fetches autoDiscoverUrl before Gitea will
# start. So anything that makes this URL unreachable turns every restart into a
# CrashLoopBackOff, not merely a broken login.
#
# That happened on 2026-07-28: auth.ddupan.top resolved to Cloudflare proxy IPs
# (104.21.6.55 / 172.67.154.245) whose TCP/443 is persistently unroutable from
# this network, while other Cloudflare IPs (104.16.132.229) were fine. Gitea was
# hairpinning through the public internet to reach a Service in its own cluster.
#
# Fixed by resolving this hostname on the LAN instead — CoreDNS answers
# auth.ddupan.top with the Envoy gateway (../../platform/k3s/coredns-custom.yaml), which
# terminates TLS with a real LE cert for the name
# (../../platform/cert-manager/certificate-auth-ddupan.yaml) and routes to Authelia
# (../authelia/httproute.yaml). The URL below is deliberately UNCHANGED: the
# issuer, redirect URIs and cookie domain all stay auth.ddupan.top, so no OIDC
# client needed re-registering. Only the network path moved.
# The DB password, injected as an env var rather than written into this file.
# Gitea's env-to-ini step turns GITEA__DATABASE__PASSWD into `[database] PASSWD`,
# so the rendered app.ini is identical to hardcoding it — but the credential
# lives only in the `gitea-db` Secret (see secret.example.yaml), and this file
# stays in git.
#
# Next step for rotation: `gitea.extraEnvSourceFile` reads an env file written
# by an OpenBao agent-injector sidecar, which is the route to credentials from
# OpenBao's database secrets engine. See docs/cicd.md. Note Gitea reads app.ini
# once at startup, so rotation needs a restart — static roles suit it better
# than short-TTL dynamic credentials.
additionalConfigFromEnvs:
- name: GITEA__DATABASE__PASSWD
valueFrom:
secretKeyRef:
name: gitea-db
key: password
oauth:
- name: authelia
provider: openidConnect
existingSecret: gitea-oidc-secret
autoDiscoverUrl: https://auth.ddupan.top/.well-known/openid-configuration
# Without explicit scopes Gitea only requests `openid`, so email/preferred_username
# claims are never released. Space-joined into the OAuth scope request.
scopes: openid profile email groups
groupClaimName: groups
adminGroup: gitea-admins
persistence:
size: 20Gi
claimName: gitea-shared-storage
postgresql-ha:
enabled: false
postgresql:
enabled: false
valkey-cluster:
enabled: false
valkey:
enabled: false
+37
View File
@@ -0,0 +1,37 @@
# LAN route to Gitea.
#
# Pairs with ../../platform/cert-manager/certificate-git-ddupan.yaml and the `https-git`
# listener in ../../platform/envoy-gateway/gateway.yaml. Split-horizon on the PUBLIC hostname:
# git.ddupan.top resolves to the gateway on the LAN and to Cloudflare from
# outside, so a clone URL works unchanged in both places and nothing that already
# has a remote configured needs touching.
#
# NOTE Gitea's chart also renders an Ingress (ingress.enabled: true in
# gitea-values.yaml) for git.ddupan.top. That Ingress is INERT — it declares no
# class, and the only IngressClasses present are `contour` (retired 2026-07-25)
# and `tailscale`. Nothing serves it. This HTTPRoute is what actually works; the
# Ingress should be turned off in the values rather than left to look meaningful.
#
# NO SecurityPolicy here on purpose. Gitea does its own authentication (local
# accounts plus Authelia OIDC), and git over HTTPS uses token/basic auth that
# forward-auth would intercept and 302 to a login page — the same breakage
# documented for NetBox's API in ../netbox/securitypolicy.yaml.
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: gitea
namespace: gitea
spec:
parentRefs:
- name: eg
namespace: envoy-gateway-system
sectionName: https-git
hostnames:
- git.ddupan.top
rules:
- backendRefs:
# Same Service the Cloudflare tunnel targets, so both paths terminate in
# exactly one place.
- name: gitea-http
port: 3000
+21
View File
@@ -0,0 +1,21 @@
# Template. Copy to secret.yaml, fill in the real password, apply, then
# `helm upgrade`. secret.yaml is gitignored — same convention as ../../platform/cert-manager,
# ../netbox, ../smtp-relay and ../../infrastructure/cloudflared.
#
# Consumed by gitea.additionalConfigFromEnvs in gitea-values.yaml as
# GITEA__DATABASE__PASSWD, which Gitea's env-to-ini step renders into
# `[database] PASSWD`.
#
# This is the `gitea` role's password on the shared CloudNativePG cluster
# (shared-postgresql.shared-db.svc.cluster.local). Rotating it means updating
# both this Secret and the role in Postgres, then restarting Gitea — Gitea reads
# app.ini once at startup and does not re-read it.
---
apiVersion: v1
kind: Secret
metadata:
name: gitea-db
namespace: gitea
type: Opaque
stringData:
password: REPLACE_WITH_GITEA_DB_PASSWORD