This repository documents my personal homelab infrastructure and GitOps journey. It is intentionally public-safe: no secrets, private keys, kubeconfigs, or internal-only configuration is committed.
- Server: HP Proliant DL380 G9
- Switch: Cisco WS-C3560CX-12-TC-S
- Access Point: Ubiquiti UniFi AC Lite
- Virtualization: Proxmox
- Firewall / Router: OPNsense
- Kubernetes: RKE2 (managed via Rancher, cis compliant)
- GitOps: Argo CD
- DNS: AdGuard Home
- Upstream: Unbound
- Internal zones / identity integration: FreeIPA
home.arpamgmt.home.arpaapp.home.arpa
- User & Access Management: FreeIPA (SSO / centralized authN/authZ)
- NAS: TrueNAS
- Cloud (Self-hosted): Nextcloud
Full kube-proxy replacement with native eBPF datapath:
- Routing: Native routing,
bpf.masquerade,bpf.tproxy, BBR congestion control - Load Balancing: LB-IPAM (pool
192.168.20.200–250), L2 announcements, BGP (AS 64501 ↔ OPNsense AS 64500) - Gateway API: v1.4.1 Experimental channel with embedded Envoy proxy
WireGuard transparent encryption — all pod-to-pod traffic between nodes is encrypted at the kernel level. Strict mode enabled: unencrypted inter-node pod traffic is dropped (no plaintext fallback). Control-plane node is opted out of NodeEncryption (host-network traffic already TLS-protected), with allowRemoteNodeIdentities: true to permit those flows.
SPIRE mutual authentication — every workload receives a SPIFFE SVID (X.509 identity) from a dedicated SPIRE infrastructure (trust domain: spire.cilium):
| Component | Scope | Details |
|---|---|---|
| SPIRE Server | 1 replica, StatefulSet | Longhorn 1Gi PV, cilium-spire namespace |
| SPIRE Agent | DaemonSet (all 3 nodes) | Full tolerations including etcd:NoExecute |
| Cilium Auth | authentication.enabled: true |
Chart defaults to false — must be explicit |
Enforcement via authentication.mode: required on CiliumNetworkPolicy ingress rules:
| Policy | Source → Destination |
|---|---|
allow-hubble-relay |
hubble-ui → hubble-relay (port 4245) |
Not eligible for mutual auth: Policies using
fromEntities(CoreDNS, metrics-server, webhooks, gateway ingress, SPIRE agent→server) — reserved identities don't carry SPIFFE SVIDs. SPIRE agents runhostNetwork: true, so they appear asremote-node/hostentities. Longhorninstance-managerpods are ephemeral and don't register SVIDs quickly enough.
End-to-end encrypted path from client to pod:
Client ──TLS──▶ HAProxy (edge VIP) ──TLS──▶ Gateway/Envoy ──plaintext──▶ Pod
- Edge: HAProxy 2.8 LTS behind Keepalived VIP (
192.168.20.22), terminates and re-encrypts to cluster - Certificates: Vault PKI (Root RSA-4096 10yr → Intermediate 5yr) via cert-manager
ClusterIssuer - Gateway: HTTPS listener on
:443with TLS Terminate mode
Zero-trust model — every namespace starts with default-deny-ingress, then explicit CiliumNetworkPolicy allow rules per service:
allow-coredns— DNS from all cluster sources (port 53)allow-hubble-relay— hubble-ui only (mutual auth enforced)allow-metrics-server— kube-apiserver onlyallow-gateway-to-*— Envoy ingress identity to backend servicesallow-apiserver-webhook— webhook ports for cert-manager, Longhornallow-spire-agent-to-server— host/remote-node/kube-apiserver entities (hostNetwork agents)allow-longhorn-internal— intra-namespace
- SIEM/XDR: Wazuh
- Flow Observability: Hubble (UI exposed via Gateway API + TLS)
- Reverse Proxy: HAProxy + Keepalived (failover / high availability)
- Vault PKI: 3-node HA + failover
All architecture diagrams live under architecture/diagrams/.
Note: A Draw.io diagram will be added first, but it is currently outdated and will be updated as the architecture evolves.