feat(apps): onboard Gitea via ServiceDefinition renderer

Author catalog/gitea.yaml (kind: container, public, DR-critical git host per
docs/13 row 12), render to apps/gitea.yaml (--no-image-automation), and wire it
into apps/kustomization.yaml.

- image gitea/gitea:1.24.3, web port 3000 at gitea.home.keithjackson.dev
- 10Gi Longhorn PVC for /data (repos + SQLite DB), daily-offcluster backup
- exposure public, egress internet, auth oidc (platform-admins), metrics on
- Traces: R-STATE-02/03, R-NET-01/02, R-DNS/R-TLS, R-IDP-02/06, R-OBS-01, R-UPD-01/03

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
jakman85andClaude Opus 4.8 committed 2026-08-25 08:23:33 -05:00
1 parent 90714cb910
commit b76fb7bea7
3 files changed
+237

No files matched your search

+194
View File
@@ -0,0 +1,194 @@
---
# Namespace (trust zone; default-deny + policies apply)
apiVersion: v1
kind: Namespace
metadata:
name: gitea
labels:
homelab.keithjackson.dev/app-namespace: 'true'
gateway-access: shared
annotations:
homelab.keithjackson.dev/backup-policy: daily-offcluster
---
# Default-deny baseline (R-NET-01): deny all, allow only DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: gitea
labels:
homelab.keithjackson.dev/baseline: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress: []
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
---
# PVC gitea-data (R-STATE)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitea-data
namespace: gitea
labels:
app.kubernetes.io/name: gitea
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 10Gi
---
# Deployment (R-PLAT-02)
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea
namespace: gitea
labels:
app.kubernetes.io/name: gitea
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: gitea
template:
metadata:
labels:
app.kubernetes.io/name: gitea
homelab.keithjackson.dev/egress-class: internet
homelab.keithjackson.dev/auth-mode: oidc
annotations:
homelab.keithjackson.dev/auth-mode: oidc
homelab.keithjackson.dev/auth-groups: platform-admins
spec:
containers:
- name: gitea
image: gitea/gitea:1.24.3
ports:
- containerPort: 3000
volumeMounts:
- name: data
mountPath: /data/data
volumes:
- name: data
persistentVolumeClaim:
claimName: gitea-data
---
# Service
apiVersion: v1
kind: Service
metadata:
name: gitea
namespace: gitea
labels:
app.kubernetes.io/name: gitea
spec:
selector:
app.kubernetes.io/name: gitea
ports:
- name: port-3000
port: 3000
targetPort: 3000
---
# CiliumNetworkPolicy gateway-ingress allow (R-NET-01/02): gitea-allow-gateway-ingress
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: gitea-allow-gateway-ingress
namespace: gitea
labels:
app.kubernetes.io/name: gitea
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: gitea
ingress:
- fromEntities:
- ingress
toPorts:
- ports:
- port: '3000'
protocol: TCP
---
# HTTPRoute gitea-3000 (R-TLS/R-DNS)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: gitea-3000
namespace: gitea
labels:
app.kubernetes.io/name: gitea
annotations:
homelab.keithjackson.dev/auth-mode: oidc
homelab.keithjackson.dev/auth-groups: platform-admins
spec:
parentRefs:
- name: shared
namespace: gateway
sectionName: https
hostnames:
- gitea.home.keithjackson.dev
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: gitea
port: 3000
---
# ServiceMonitor (R-OBS-01)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: gitea
namespace: gitea
labels:
release: kube-prometheus-stack
app.kubernetes.io/name: gitea
spec:
selector:
matchLabels:
app.kubernetes.io/name: gitea
namespaceSelector:
matchNames:
- gitea
endpoints:
- port: metrics
interval: 30s
---
# PrometheusRule SLI alerts (R-OBS-01)
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: gitea-slis
namespace: gitea
labels:
release: kube-prometheus-stack
app.kubernetes.io/name: gitea
spec:
groups:
- name: gitea.slis
rules:
- alert: gitea-down
expr: up{job="gitea"} == 0
for: 5m
labels:
severity: critical
service: gitea
annotations:
summary: gitea is down
description: 'gitea has been unreachable for 5m (SLI: up).'
+1
View File
@@ -7,3 +7,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- pihole.yaml
- gitea.yaml
+42
View File
@@ -0,0 +1,42 @@
# ServiceDefinition: gitea
# Source of truth: spec docs/13-service-catalog.md row 12; docs/15-identity-and-access.md; docs/05.
# public git hosting | egress internet | repos+DB on Longhorn (irreplaceable, DR-critical) | auth oidc.
# Bootstrapping-critical: self-hosts the platform's GitOps source + CI/CD, onboarded EARLY (docs/13
# "Gitea is bootstrapping-critical", WS-APPS step 2, right after PiHole). A cluster loss would take its
# own git host with it, so repos/DB are backed up off-cluster + mirrored (R-STATE-03).
# Single-container Gitea with the built-in SQLite datastore is sufficient at home scale; the repos and
# DB live on one Longhorn PVC (/data), so no external PostgreSQL is required for this onboarding.
# Validates against schemas/servicedefinition.schema.json.
name: gitea
kind: container
source:
image: gitea/gitea:1.24.3 # pinned recent stable tag (R-UPD-01); update.range governs advancement
exposure: public # edge-forwarded git host at gitea.home.keithjackson.dev (docs/09; R-DNS/R-NET)
egress: internet # normal WAN egress (docs/03; R-NET-05)
storage:
- name: data
size: 10Gi
class: longhorn # repos + SQLite DB on 3-replica Longhorn; irreplaceable (docs/04; R-STATE-02)
dependencies: [] # default-deny: reaches only DNS; git clients reach it north-south via the Gateway (R-NET-01/02)
ingressRoutes:
- host: gitea.home.keithjackson.dev
port: 3000 # Gitea HTTP web/API; TLS terminates at the shared Gateway (docs/08, docs/09)
auth:
mode: oidc # native Keycloak OIDC client (docs/13 SSO row, docs/15 "Native OIDC" set; R-IDP-02)
groups:
- platform-admins # access scoped to an authorized group (R-IDP-06)
# NOTE (OIDC follow-up + break-glass): Gitea keeps a LOCAL admin as the break-glass path so an IdP
# outage never locks the operator out of recovery tools (docs/15 "Availability caveat"). At initial
# deploy Gitea comes up on that local admin; the Keycloak OIDC client (+ SOPS client secret) is wired
# once Keycloak is live. This declaration records the target integration; it does not embed a secret.
update:
channel: stable # R-UPD-01
range: ">=1.22.0 <2.0.0" # stay on the 1.x stable line
cadence: "0 4 * * *"
autoRollbackOnFailedHealth: true # R-UPD-03
observability:
metrics: true
slis:
- up # git host down is a critical alert (docs/10; R-OBS-01)
backup:
policy: daily-offcluster # DR-critical: repos/DB backed up off-cluster + mirrored (docs/13, docs/04; R-STATE-03)