Compare commits
35
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aea4be734c | ||
|
|
60361d4c8a | ||
|
|
a8cef5de1b | ||
|
|
505802dad5 | ||
|
|
ba66b604d4 | ||
|
|
4fbc49fc8c | ||
|
|
f493c9fe52 | ||
|
|
8a3c66eb2e | ||
|
|
9928999da4 | ||
|
|
b3def28f7c | ||
|
|
93dbcdb2fb | ||
|
|
47bf16cfac | ||
|
|
4664de8f97 | ||
|
|
fb52e4be88 | ||
|
|
f11a71c33f | ||
|
|
29326eead3 | ||
|
|
eb09b1b2a3 | ||
|
|
dd3a1f1e90 | ||
|
|
98a0462137 | ||
|
|
9aef2e0669 | ||
|
|
fd7597ddc2 | ||
|
|
10b0c1b853 | ||
|
|
674169cb08 | ||
|
|
0c6174b7a6 | ||
|
|
76888c3771 | ||
|
|
eb23d3ac6e | ||
|
|
c1e3f3fb98 | ||
|
|
036399095b | ||
|
|
0bac0051b2 | ||
|
|
41a65b65a3 | ||
|
|
a530f7c8e6 | ||
|
|
8fff4b4012 | ||
|
|
0ada3df260 | ||
|
|
2144554cfc | ||
|
|
dba810b87b |
@@ -101,7 +101,7 @@ steps:
|
||||
bash scripts/ci/make-diag-kubeconfig.sh
|
||||
|
||||
- name: alert-triage-agent
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.250
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
from_secret: claude_code_oauth_token
|
||||
|
||||
@@ -350,17 +350,17 @@ steps:
|
||||
--destination gitea.renner.uno/gitea_admin/heating-can:$TAG \
|
||||
--destination gitea.renner.uno/gitea_admin/heating-can:latest
|
||||
|
||||
# netbird-proxy-services: the controller that reconciles NetBird reverse-proxy
|
||||
# netbird-service-sync: the controller that reconciles NetBird reverse-proxy
|
||||
# services (public Ingress hosts -> TLS passthrough -> Traefik ClusterIP).
|
||||
# Runtime artifact like heating-can; same registry so the audit tracks it.
|
||||
- name: build-netbird-proxy-services
|
||||
- name: build-netbird-service-sync
|
||||
image: registry.gitlab.com/gitlab-ci-utils/container-images/kaniko:v1.25.18-debug
|
||||
directory: ci/images/netbird-proxy-services
|
||||
directory: ci/images/netbird-service-sync
|
||||
depends_on: []
|
||||
when:
|
||||
- event: deployment
|
||||
- event: push
|
||||
path: ["ci/images/netbird-proxy-services/**", ".woodpecker/build-ci-images.yml"]
|
||||
path: ["ci/images/netbird-service-sync/**", ".woodpecker/build-ci-images.yml"]
|
||||
environment:
|
||||
GITEA_USER: gitea_admin
|
||||
GITEA_TOKEN:
|
||||
@@ -374,11 +374,11 @@ steps:
|
||||
/kaniko/executor \
|
||||
--context . \
|
||||
--dockerfile Containerfile \
|
||||
--destination gitea.renner.uno/gitea_admin/netbird-proxy-services:$TAG \
|
||||
--destination gitea.renner.uno/gitea_admin/netbird-proxy-services:latest
|
||||
--destination gitea.renner.uno/gitea_admin/netbird-service-sync:$TAG \
|
||||
--destination gitea.renner.uno/gitea_admin/netbird-service-sync:latest
|
||||
|
||||
# wiki-index: embedding index over the Notes repo's wiki/, fed from Gitea and
|
||||
# queried by ~/Notes/bin/wiki_rank.py. Runtime artifact like netbird-proxy-services.
|
||||
# queried by ~/Notes/bin/wiki_rank.py. Runtime artifact like netbird-service-sync.
|
||||
- name: build-wiki-index
|
||||
image: registry.gitlab.com/gitlab-ci-utils/container-images/kaniko:v1.25.18-debug
|
||||
directory: ci/images/wiki-index
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
# and ci-claude:latest. The Containerfile makes this layer
|
||||
# byte-reproducible, so rebuilding an unchanged version dedups
|
||||
# in the registry instead of orphaning a fresh ~260 MB copy.
|
||||
# 3. bump-pr — (only when the version actually changed) rewrite the 5
|
||||
# consumer pins to ci-claude:<version> and open a PR. A human
|
||||
# 3. bump-pr — (only when any pin is off the latest version) rewrite every
|
||||
# consumer pin to ci-claude:<version> and open a PR. A human
|
||||
# reviews and merges it; nothing auto-merges.
|
||||
#
|
||||
# TAG SCHEME — deliberately version tags (ci-claude:<claude-version>), NOT the dated
|
||||
@@ -107,31 +107,22 @@ steps:
|
||||
[0-9]*.[0-9]*.[0-9]*) : ;;
|
||||
*) echo "npm returned an implausible version '$$NEW' — aborting."; exit 1 ;;
|
||||
esac
|
||||
# The tag every consumer currently references. They are kept identical,
|
||||
# so this must resolve to EXACTLY ONE non-empty tag; anything else
|
||||
# (no match, or a partial/disagreeing set) aborts rather than risk a
|
||||
# corrupting sed later. Counting with `grep -c .` (non-empty lines)
|
||||
# instead of `wc -l` is deliberate: an empty result yields 0 here but
|
||||
# 1 under wc -l, which would slip past the guard.
|
||||
CUR=$$(grep -hoE 'ci-claude:[0-9A-Za-z._-]+' $$CONSUMERS | sed 's/^ci-claude://' | sort -u)
|
||||
NTAGS=$$(printf '%s' "$$CUR" | grep -c . || true)
|
||||
if [ -z "$$CUR" ] || [ "$$NTAGS" -ne 1 ]; then
|
||||
echo "Expected exactly one ci-claude tag across the consumers, got [$$CUR]."
|
||||
echo "Refusing to guess — fix the pins by hand."; exit 1
|
||||
fi
|
||||
echo "latest=$$NEW current=$$CUR event=$$CI_PIPELINE_EVENT"
|
||||
# Distinct tags the consumers currently reference. Divergent pins are
|
||||
# normal (a pipeline bumped on its own); the bump rewrites all of them.
|
||||
CUR=$$(grep -hoE 'ci-claude:[0-9][0-9A-Za-z._-]*' $$CONSUMERS | sed 's/^ci-claude://' | sort -u | tr '\n' ' ' | sed 's/ $$//')
|
||||
[ -n "$$CUR" ] || { echo "No ci-claude pin found in the consumers — aborting."; exit 1; }
|
||||
echo "latest=$$NEW current=[$$CUR] event=$$CI_PIPELINE_EVENT"
|
||||
printf '%s' "$$NEW" > "$$CI_WORKSPACE/.ci-refresh/version"
|
||||
printf '%s' "$$CUR" > "$$CI_WORKSPACE/.ci-refresh/current"
|
||||
# Build when the version changed, or on a push (Containerfile edit must
|
||||
# ship even if the version is unchanged). Bump the pins only when the
|
||||
# version the consumers reference is not the latest.
|
||||
# Build when any pin is off latest, or on a push (Containerfile edit must
|
||||
# ship even if the version is unchanged). Bump when any pin is off latest.
|
||||
if [ "$$CUR" != "$$NEW" ] || [ "$$CI_PIPELINE_EVENT" = "push" ]; then
|
||||
touch "$$CI_WORKSPACE/.ci-refresh/do-build"; echo "will build $$NEW"
|
||||
else
|
||||
echo "already on $$NEW — nothing to build"
|
||||
fi
|
||||
if [ "$$CUR" != "$$NEW" ]; then
|
||||
touch "$$CI_WORKSPACE/.ci-refresh/do-bump"; echo "will open bump PR $$CUR -> $$NEW"
|
||||
touch "$$CI_WORKSPACE/.ci-refresh/do-bump"; echo "will open bump PR [$$CUR] -> $$NEW"
|
||||
else
|
||||
echo "consumers already pin $$NEW — no PR"
|
||||
fi
|
||||
@@ -175,6 +166,9 @@ steps:
|
||||
- |
|
||||
[ -f "$$CI_WORKSPACE/.ci-refresh/do-bump" ] || { echo "no version change — no PR"; exit 0; }
|
||||
set -eu
|
||||
# Secret values can carry a trailing newline; it breaks the push URL and the auth header.
|
||||
PUSH_TOKEN=$$(printf '%s' "$$PUSH_TOKEN" | tr -d '\r\n')
|
||||
[ -n "$$PUSH_TOKEN" ] || { echo "PUSH_TOKEN is empty — aborting"; exit 1; }
|
||||
NEW=$$(cat "$$CI_WORKSPACE/.ci-refresh/version")
|
||||
CUR=$$(cat "$$CI_WORKSPACE/.ci-refresh/current")
|
||||
# Both were validated non-empty by the decide step; re-assert before a
|
||||
@@ -190,9 +184,11 @@ steps:
|
||||
# is not a maintained list.
|
||||
CONSUMERS=$$(grep -rlE 'ci-claude:[0-9]' .woodpecker)
|
||||
[ -n "$$CONSUMERS" ] || { echo "No ci-claude consumers found — refusing to continue."; exit 1; }
|
||||
# Exact-string swap of the current tag for the new one, consumers only.
|
||||
# NEW goes into a sed replacement: allow only a plain x.y.z version.
|
||||
printf '%s' "$$NEW" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$$' || { echo "refusing non-x.y.z version '$$NEW'"; exit 1; }
|
||||
# Every version-pinned tag becomes NEW, whatever it was; consumers only.
|
||||
for f in $$CONSUMERS; do
|
||||
sed -i "s#ci-claude:$$CUR#ci-claude:$$NEW#g" "$$f"
|
||||
sed -i "s#ci-claude:[0-9][0-9A-Za-z._-]*#ci-claude:$$NEW#g" "$$f"
|
||||
done
|
||||
# Author is claude-bot (the PAT's account); the commit subject makes
|
||||
# the automated-bump nature clear.
|
||||
|
||||
@@ -17,6 +17,6 @@ when:
|
||||
|
||||
steps:
|
||||
- name: model-selection-tests
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.250
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
commands:
|
||||
- sh scripts/ci/test-model-selection.sh
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ steps:
|
||||
# fetch-models.sh response mapping. No cluster, no secrets, no network — the
|
||||
# ci-claude image just provides sh + jq. Keeps the flag-injection regression
|
||||
# test from rotting (it exercises the exact injection shapes).
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.250
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
commands:
|
||||
- sh scripts/ci/test-model-selection.sh
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ steps:
|
||||
- bash scripts/ci/make-diag-kubeconfig.sh
|
||||
|
||||
- name: issue-fix-agent
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.250
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
from_secret: claude_code_oauth_token
|
||||
|
||||
@@ -65,7 +65,7 @@ when:
|
||||
|
||||
steps:
|
||||
- name: pr-review-agent
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.250
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
from_secret: claude_code_oauth_token
|
||||
|
||||
@@ -70,7 +70,7 @@ steps:
|
||||
# this step is skipped — otherwise we'd fork-bomb PRs.
|
||||
when:
|
||||
- branch: master
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.250
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
from_secret: claude_code_oauth_token
|
||||
|
||||
@@ -30,7 +30,7 @@ when:
|
||||
|
||||
steps:
|
||||
- name: update-audit-agent
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.250
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
from_secret: claude_code_oauth_token
|
||||
|
||||
@@ -16,7 +16,7 @@ when:
|
||||
|
||||
steps:
|
||||
- name: checkout-notes
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.278
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
pull: true
|
||||
environment:
|
||||
WIKI_BOT_TOKEN:
|
||||
@@ -32,7 +32,7 @@ steps:
|
||||
echo "raw backlog: $$(ls notes/raw/*.md | grep -v _index.md | wc -l) file(s)"
|
||||
|
||||
- name: distill
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.278
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
pull: true
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
@@ -62,7 +62,7 @@ steps:
|
||||
-p
|
||||
|
||||
- name: audit
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.278
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
pull: true
|
||||
environment:
|
||||
CLAUDE_CODE_OAUTH_TOKEN:
|
||||
@@ -91,7 +91,7 @@ steps:
|
||||
-p
|
||||
|
||||
- name: push-notes
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.278
|
||||
image: gitea.renner.uno/gitea_admin/ci-claude:2.1.280
|
||||
pull: true
|
||||
environment:
|
||||
WIKI_BOT_TOKEN:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# netbird-proxy-services: reconciles NetBird reverse-proxy services (public
|
||||
# netbird-service-sync: reconciles NetBird reverse-proxy services (public
|
||||
# domains -> TLS passthrough -> Traefik ClusterIP) from Ingresses carrying the
|
||||
# public label. Standard library only, so the image is the interpreter plus one
|
||||
# file; runs unprivileged as its own user.
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""netbird-proxy-services: reconcile NetBird reverse-proxy services from Ingresses.
|
||||
"""netbird-service-sync: reconcile NetBird reverse-proxy services from Ingresses.
|
||||
|
||||
Source of truth: every Ingress carrying the public label (dns.renner.uno/public).
|
||||
For each host on such an Ingress the NetBird management gets one reverse-proxy
|
||||
@@ -30,11 +30,11 @@ import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
LOG = logging.getLogger("netbird-proxy-services")
|
||||
LOG = logging.getLogger("netbird-service-sync")
|
||||
|
||||
NB = os.environ.get("NB_MANAGEMENT_URL", "https://netbird.renner.uno").rstrip("/")
|
||||
BAO_ADDR = os.environ.get("BAO_ADDR", "http://openbao.openbao.svc.cluster.local:8200").rstrip("/")
|
||||
BAO_ROLE = os.environ.get("BAO_ROLE", "netbird-proxy-services")
|
||||
BAO_ROLE = os.environ.get("BAO_ROLE", "netbird-service-sync")
|
||||
BAO_PAT_PATH = os.environ.get("BAO_PAT_PATH", "homelab/data/infrastructure/secret-rotators-bootstrap/netbird")
|
||||
BAO_PAT_KEY = os.environ.get("BAO_PAT_KEY", "pat")
|
||||
SERVICE_CIDR = os.environ.get("SERVICE_CIDR", "10.96.0.0/12")
|
||||
@@ -156,7 +156,7 @@ def ensure_network(nb):
|
||||
if n["name"] == NETWORK_NAME:
|
||||
return n["id"]
|
||||
n = nb.post("networks", {"name": NETWORK_NAME,
|
||||
"description": "managed by netbird-proxy-services; public ingress via TLS passthrough"})
|
||||
"description": "managed by netbird-service-sync; public ingress via TLS passthrough"})
|
||||
LOG.info("created network %s (%s)", NETWORK_NAME, n["id"])
|
||||
return n["id"]
|
||||
|
||||
@@ -166,7 +166,7 @@ def ensure_resource(nb, net):
|
||||
if r.get("address") == SERVICE_CIDR:
|
||||
return r["id"]
|
||||
r = nb.post(f"networks/{net}/resources", {"name": "cluster service CIDR", "address": SERVICE_CIDR,
|
||||
"description": "managed by netbird-proxy-services",
|
||||
"description": "managed by netbird-service-sync",
|
||||
"enabled": True, "groups": []})
|
||||
LOG.info("created subnet resource %s (%s)", SERVICE_CIDR, r["id"])
|
||||
return r["id"]
|
||||
@@ -2,7 +2,7 @@ apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: gitea
|
||||
namespace: apps
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
annotations:
|
||||
|
||||
@@ -2,7 +2,7 @@ apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRouteTCP
|
||||
metadata:
|
||||
name: gitea-ssh
|
||||
namespace: apps
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
spec:
|
||||
|
||||
@@ -1,28 +1,8 @@
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-metrics-scrape
|
||||
namespace: apps
|
||||
spec:
|
||||
# Prometheus (monitoring ns) scrapes gitea's /metrics on :3000. The apps
|
||||
# default-deny-ingress only allows same-ns / home-assistant / host, so the
|
||||
# scrape is dropped (context deadline exceeded). This became necessary only
|
||||
# after the LXC->in-cluster cutover — the old static target was external, so
|
||||
# no in-cluster policy applied. Scoped to the gitea pod + :3000.
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gitea
|
||||
ingress:
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": monitoring
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "3000"
|
||||
protocol: TCP
|
||||
---
|
||||
# Prometheus scrape and Traefik are admitted by the namespace baseline in
|
||||
# infrastructure/network-policies/gitea-ns.yaml.
|
||||
#
|
||||
# Secret rotation: the flux-git rotator (openbao ns) reaches gitea to mint a new
|
||||
# flux PAT. It uses the in-cluster Service (gitea.apps.svc:3000) — the public
|
||||
# flux PAT. It uses the in-cluster Service (gitea.gitea.svc:3000) — the public
|
||||
# gitea.renner.uno name hairpins off the ingress LB from a pod (same failure as
|
||||
# pve.renner.uno). Scoped to the gitea pod :3000 and the rotator label; the
|
||||
# egress half is rotator-gitea-flux-egress in network-policies/openbao-ns.yaml.
|
||||
@@ -30,7 +10,7 @@ apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-flux-rotator
|
||||
namespace: apps
|
||||
namespace: gitea
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
@@ -54,7 +34,7 @@ apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-review-bot-rotator
|
||||
namespace: apps
|
||||
namespace: gitea
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
|
||||
@@ -13,7 +13,7 @@ apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: gitea
|
||||
namespace: apps
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
spec:
|
||||
|
||||
@@ -2,7 +2,7 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: gitea
|
||||
namespace: apps
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
spec:
|
||||
|
||||
@@ -2,7 +2,7 @@ apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: gitea
|
||||
namespace: apps
|
||||
namespace: gitea
|
||||
labels:
|
||||
app.kubernetes.io/name: gitea
|
||||
spec:
|
||||
@@ -152,7 +152,7 @@ spec:
|
||||
# NOTE: volumeClaimTemplates is immutable on an existing StatefulSet,
|
||||
# so bumping this value cannot be `kubectl apply`-ed in place — Flux
|
||||
# will reject the whole object. Landing this size change requires a
|
||||
# one-time `kubectl delete statefulset gitea -n apps --cascade=orphan`
|
||||
# one-time `kubectl delete statefulset gitea -n gitea --cascade=orphan`
|
||||
# (pod + PVC keep running) so Flux re-creates the STS from this
|
||||
# template and re-adopts them. After the cleanup_packages GC reclaims
|
||||
# the ~10G of orphaned blobs, real usage is ~9G, so 40Gi is headroom.
|
||||
|
||||
@@ -3,8 +3,8 @@ kind: Kustomization
|
||||
resources:
|
||||
- gatus/
|
||||
- external-dns/
|
||||
- gitea/
|
||||
- ical-proxy/
|
||||
# gitea/ has its own `gitea` Kustomization (clusters/homelab/gitea.yaml, #878).
|
||||
# woodpecker/ moved to its own dedicated `woodpecker` Kustomization
|
||||
# (clusters/homelab/woodpecker.yaml) so its wp-pg CNPG cluster's barman
|
||||
# dependency + Cluster health-gating don't contaminate this shared apps KS.
|
||||
|
||||
@@ -91,12 +91,11 @@ spec:
|
||||
# Additional scrape targets not covered by ServiceMonitors.
|
||||
additionalScrapeConfigs:
|
||||
# Gitea exposes /metrics anonymously on its main HTTP port. The
|
||||
# `gitea_*` metrics power the gitea-overview dashboard. Gitea now runs
|
||||
# in-cluster (StatefulSet in apps ns; LXC 114 retired), so scrape the
|
||||
# ClusterIP Service directly instead of the dead LXC hostname.
|
||||
# `gitea_*` metrics power the gitea-overview dashboard. Scrapes the
|
||||
# ClusterIP Service of the StatefulSet in the gitea namespace.
|
||||
- job_name: gitea
|
||||
static_configs:
|
||||
- targets: ["gitea.apps.svc.cluster.local:3000"]
|
||||
- targets: ["gitea.gitea.svc.cluster.local:3000"]
|
||||
metrics_path: /metrics
|
||||
|
||||
# Grafana
|
||||
|
||||
@@ -1946,6 +1946,7 @@ if phase_runs wait-apps; then
|
||||
# apps waits on infrastructure-ceph-cluster (Ceph mon/mgr/OSD bring-up
|
||||
# is genuinely 5–10 min on a fresh OSD-wiped cluster).
|
||||
wait_kustomization apps "apps" 90
|
||||
wait_kustomization gitea "Gitea (incl. data-gitea-0 PVC)" 90
|
||||
wait_kustomization influxdb "InfluxDB (HR install + token Jobs)" 600
|
||||
wait_kustomization woodpecker "Woodpecker CI (incl. wp-pg CNPG backend)" 600
|
||||
wait_kustomization authentik "Authentik (incl. ak-pg CNPG backend)" 600
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: gitea
|
||||
namespace: flux-system
|
||||
spec:
|
||||
# Gitea in its own `gitea` namespace (#878), formerly under the shared `apps` KS.
|
||||
interval: 10m
|
||||
retryInterval: 2m
|
||||
timeout: 15m
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
path: ./kubernetes/apps/gitea
|
||||
prune: true
|
||||
wait: true
|
||||
dependsOn:
|
||||
- name: infrastructure
|
||||
- name: infrastructure-crds
|
||||
- name: infrastructure-ceph-cluster
|
||||
@@ -23,7 +23,7 @@ protocol device { scan time 10; }
|
||||
# down. The usual advice is not to export these to the kernel because it
|
||||
# maintains them itself — true of main, not of a table the kernel never fills.
|
||||
protocol direct attached {
|
||||
interface "mgmt0", "ingress0", "pod0";
|
||||
interface "mgmt0", "ingress0", "pod0", "node0";
|
||||
ipv4 { table master4; };
|
||||
ipv6 { table master6; };
|
||||
}
|
||||
|
||||
@@ -118,14 +118,21 @@ spec:
|
||||
# traffic out to the VIP and back (which fails with L2 announcements).
|
||||
lbExternalClusterIP: true
|
||||
cni:
|
||||
# Multus (kubernetes/infrastructure/multus) runs alongside Cilium and
|
||||
# writes 00-multus.conf to /etc/cni/net.d so kubelet picks multus
|
||||
# first; multus then delegates to Cilium's 05-cilium.conflist via
|
||||
# clusterNetwork. The default `exclusive: true` makes cilium-cni's
|
||||
# install step rename anything alphabetically before its own config
|
||||
# to *.cilium_bak on every reconcile, which kept nuking multus.
|
||||
# Setting it false lets multus's config coexist.
|
||||
# Multus is the only writer of /etc/cni/net.d (#832): Cilium's conflist
|
||||
# goes to /etc/cni/net.d/multus/, which the container runtime does not
|
||||
# read, and Multus delegates to it via clusterNetwork. Without this a
|
||||
# pod created after a reboot, before Multus rewrote 00-multus.conf, came
|
||||
# up with Cilium alone. See kubernetes/infrastructure/multus.
|
||||
confPath: /etc/cni/net.d/multus
|
||||
# The default `exclusive: true` renames other configs in the conf dir to
|
||||
# *.cilium_bak on every reconcile, which kept nuking multus.
|
||||
exclusive: false
|
||||
# One agent at a time: the chart default of 2 restarts two of three
|
||||
# nodes' datapath at once.
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: 1
|
||||
# Talos-specific settings
|
||||
securityContext:
|
||||
capabilities:
|
||||
|
||||
@@ -19,7 +19,7 @@ resources:
|
||||
- cloudnative-pg/
|
||||
- mariadb-operator/
|
||||
- netbird-ingress/
|
||||
- netbird-proxy-services/
|
||||
- netbird-service-sync/
|
||||
- coredns-config/
|
||||
- k8s-gateway/
|
||||
- external-dns-unifi/
|
||||
|
||||
@@ -111,10 +111,18 @@ metadata:
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
# Multus is the only writer of /etc/cni/net.d (#832). Cilium writes its conflist
|
||||
# to /etc/cni/net.d/multus/, a subdirectory the container runtime does not read
|
||||
# (libcni skips directories); Multus picks it up there via multusAutoconfigDir
|
||||
# and writes 00-multus.conf. Until that file exists the runtime has no network
|
||||
# config at all, so no pod can be created with Cilium alone after a reboot.
|
||||
#
|
||||
# The "-subdir" names date from the node-by-node rollout; renaming would roll
|
||||
# Multus again for nothing.
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: multus-daemon-config
|
||||
name: multus-daemon-config-subdir
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
@@ -127,7 +135,7 @@ data:
|
||||
"logLevel": "verbose",
|
||||
"logToStderr": true,
|
||||
"cniConfigDir": "/host/etc/cni/net.d",
|
||||
"multusAutoconfigDir": "/host/etc/cni/net.d",
|
||||
"multusAutoconfigDir": "/host/etc/cni/net.d/multus",
|
||||
"multusConfigFile": "auto",
|
||||
"socketDir": "/host/run/multus/"
|
||||
}
|
||||
@@ -135,16 +143,16 @@ data:
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: kube-multus-ds
|
||||
name: kube-multus-ds-subdir
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
name: multus-subdir
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
name: multus
|
||||
name: multus-subdir
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
@@ -152,7 +160,7 @@ spec:
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
name: multus-subdir
|
||||
spec:
|
||||
# Upstream ships this DaemonSet with no priority class, so it runs at 0
|
||||
# and can preempt nothing. Any event that makes the pod need a fresh
|
||||
@@ -224,6 +232,10 @@ spec:
|
||||
volumeMounts:
|
||||
- name: cni
|
||||
mountPath: /host/etc/cni/net.d
|
||||
# Exists before Multus starts, so it waits for Cilium's conflist
|
||||
# instead of exiting on a missing directory.
|
||||
- name: cni-cilium-conf
|
||||
mountPath: /host/etc/cni/net.d/multus
|
||||
# multus-daemon expects that cnibin path must be identical between pod and container host.
|
||||
# e.g. if the cni bin is in '/opt/cni/bin' on the container host side, then it should be mount to '/opt/cni/bin' in multus-daemon,
|
||||
# not to any other directory, like '/opt/bin' or '/usr/bin'.
|
||||
@@ -279,6 +291,10 @@ spec:
|
||||
- name: cni
|
||||
hostPath:
|
||||
path: /etc/cni/net.d
|
||||
- name: cni-cilium-conf
|
||||
hostPath:
|
||||
path: /etc/cni/net.d/multus
|
||||
type: DirectoryOrCreate
|
||||
- name: cnibin
|
||||
hostPath:
|
||||
path: /opt/cni/bin
|
||||
@@ -287,7 +303,7 @@ spec:
|
||||
path: /
|
||||
- name: multus-daemon-config
|
||||
configMap:
|
||||
name: multus-daemon-config
|
||||
name: multus-daemon-config-subdir
|
||||
items:
|
||||
- key: daemon-config.json
|
||||
path: daemon-config.json
|
||||
|
||||
@@ -39,9 +39,11 @@ spec:
|
||||
name: whereabouts-reconciler
|
||||
spec:
|
||||
serviceAccountName: whereabouts
|
||||
# Every node here is a control plane node.
|
||||
# Every node here is a control plane node. Keyed: a keyless Exists would
|
||||
# also tolerate node.kubernetes.io/unschedulable and ignore cordons.
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
containers:
|
||||
- name: whereabouts-reconciler
|
||||
|
||||
@@ -179,6 +179,22 @@ kind: Namespace
|
||||
metadata:
|
||||
name: woodpecker
|
||||
---
|
||||
# Gitea, moving out of the shared `apps` namespace (issue #878). Declared here
|
||||
# like `woodpecker` above, so it exists before infrastructure-crds applies its
|
||||
# CiliumNetworkPolicies. No pod-security labels: the cluster default applies.
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: gitea
|
||||
---
|
||||
# netbird-service-sync, moving out of `openbao` (issue #876). Declared here like
|
||||
# `woodpecker` above, so it exists before infrastructure-crds applies its
|
||||
# CiliumNetworkPolicies. No pod-security labels: the cluster default applies.
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: netbird-service-sync
|
||||
---
|
||||
# Local LLM inference (kubernetes/apps/llama-server). Declared here, like
|
||||
# `woodpecker` above, so the `infrastructure` KS creates it before
|
||||
# `infrastructure-crds` applies the namespace's CiliumNetworkPolicies.
|
||||
|
||||
+23
-23
@@ -1,28 +1,28 @@
|
||||
# Reconciles NetBird reverse-proxy services from Ingresses that carry
|
||||
# dns.renner.uno/public: one TLS-passthrough service per host on :443,
|
||||
# targeting Traefik's ClusterIP through a subnet resource over the service
|
||||
# CIDR (ci/images/netbird-proxy-services/controller.py).
|
||||
# CIDR (ci/images/netbird-service-sync/controller.py).
|
||||
#
|
||||
# openbao namespace, like the rotators: its default-deny already allows egress
|
||||
# to OpenBao, DNS, the apiserver and `world` (netbird.renner.uno), and the
|
||||
# NetBird PAT is read from OpenBao on every pass through the
|
||||
# netbird-proxy-services role — policies/netbird-proxy-services.hcl, applied
|
||||
# once by hand like the rotator roles. Until that role exists the pod logs an
|
||||
# auth error each minute and does nothing.
|
||||
# Own namespace; its egress (DNS, apiserver, OpenBao, NetBird API) is in
|
||||
# network-policies/netbird-service-sync-ns.yaml. The NetBird PAT is read from
|
||||
# OpenBao on every pass through the netbird-service-sync role —
|
||||
# policies/netbird-service-sync.hcl, applied once by hand like the rotator
|
||||
# roles. Until that role exists the pod logs an auth error each minute and does
|
||||
# nothing.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: netbird-proxy-services
|
||||
namespace: openbao
|
||||
name: netbird-service-sync
|
||||
namespace: netbird-service-sync
|
||||
labels:
|
||||
app.kubernetes.io/name: netbird-proxy-services
|
||||
app.kubernetes.io/name: netbird-service-sync
|
||||
---
|
||||
# Read-only, cluster-wide: public Ingresses live in many namespaces, the
|
||||
# Traefik Service in ingress/.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: netbird-proxy-services-read
|
||||
name: netbird-service-sync-read
|
||||
rules:
|
||||
- apiGroups: ["networking.k8s.io"]
|
||||
resources: ["ingresses"]
|
||||
@@ -34,23 +34,23 @@ rules:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: netbird-proxy-services-read
|
||||
name: netbird-service-sync-read
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: netbird-proxy-services-read
|
||||
name: netbird-service-sync-read
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: netbird-proxy-services
|
||||
namespace: openbao
|
||||
name: netbird-service-sync
|
||||
namespace: netbird-service-sync
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: netbird-proxy-services
|
||||
namespace: openbao
|
||||
name: netbird-service-sync
|
||||
namespace: netbird-service-sync
|
||||
labels:
|
||||
app.kubernetes.io/name: netbird-proxy-services
|
||||
app.kubernetes.io/name: netbird-service-sync
|
||||
spec:
|
||||
replicas: 1
|
||||
# One writer against the management API at a time.
|
||||
@@ -58,13 +58,13 @@ spec:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: netbird-proxy-services
|
||||
app.kubernetes.io/name: netbird-service-sync
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: netbird-proxy-services
|
||||
app.kubernetes.io/name: netbird-service-sync
|
||||
spec:
|
||||
serviceAccountName: netbird-proxy-services
|
||||
serviceAccountName: netbird-service-sync
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
# Numeric, or the kubelet refuses the image's named user with
|
||||
@@ -80,7 +80,7 @@ spec:
|
||||
# image directory (branch and master alike) and the registry keeps
|
||||
# only the newest manifest per tag, so the digest must come from
|
||||
# the MASTER build of the code change.
|
||||
image: gitea.renner.uno/gitea_admin/netbird-proxy-services:2026-09-07@sha256:caffa308d802c6fcc6aea919ac94657c08b993651baa6b8d18de395bcc40523e
|
||||
image: gitea.renner.uno/gitea_admin/netbird-service-sync:2026-09-23@sha256:64f6057c508640e1dbc50457dc26e9b2de058a111fd46ca096021d7444b5bbff
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
@@ -92,7 +92,7 @@ spec:
|
||||
- name: BAO_ADDR
|
||||
value: http://openbao.openbao.svc.cluster.local:8200
|
||||
- name: BAO_ROLE
|
||||
value: netbird-proxy-services
|
||||
value: netbird-service-sync
|
||||
# Must match cluster.network.serviceSubnets in talos/patches/cluster.yaml.
|
||||
- name: SERVICE_CIDR
|
||||
value: "10.96.0.0/12"
|
||||
@@ -167,7 +167,7 @@ spec:
|
||||
"k8s:io.kubernetes.pod.namespace": ingress
|
||||
app.kubernetes.io/name: traefik
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": apps
|
||||
"k8s:io.kubernetes.pod.namespace": gitea
|
||||
app.kubernetes.io/name: gitea
|
||||
toPorts:
|
||||
- ports:
|
||||
|
||||
@@ -45,16 +45,6 @@ spec:
|
||||
- ports:
|
||||
- port: "1883"
|
||||
protocol: TCP
|
||||
# gitea delivers the notes repo's push webhook to wiki-index (ai ns) on :8080.
|
||||
# wiki-index's own ingress policy scopes this to the gitea pod.
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": ai
|
||||
app.kubernetes.io/name: wiki-index
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8080"
|
||||
protocol: TCP
|
||||
- toEntities:
|
||||
- host
|
||||
- remote-node
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
# Baseline policy for the `gitea` namespace (issue #878).
|
||||
#
|
||||
# endpointSelector: {} — every rule applies to every pod in the namespace.
|
||||
# Workload-scoped grants go in their own policy with their own selector.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: gitea
|
||||
spec:
|
||||
endpointSelector: {}
|
||||
ingress:
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": gitea
|
||||
# Prometheus scrape of /metrics.
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": monitoring
|
||||
# Kubelet probes and hostNetwork siblings.
|
||||
- fromEntities:
|
||||
- host
|
||||
- remote-node
|
||||
# ICMP from any cluster pod; required once an L4 policy selects the pod.
|
||||
# See woodpecker-ns.yaml for the rationale. INGRESS ONLY.
|
||||
- fromEndpoints:
|
||||
- matchExpressions:
|
||||
- key: io.kubernetes.pod.namespace
|
||||
operator: Exists
|
||||
icmps:
|
||||
- fields:
|
||||
- { type: 0, family: IPv4 }
|
||||
- { type: 3, family: IPv4 }
|
||||
- { type: 8, family: IPv4 }
|
||||
- { type: 11, family: IPv4 }
|
||||
- { type: 12, family: IPv4 }
|
||||
- { type: 1, family: IPv6 }
|
||||
- { type: 2, family: IPv6 }
|
||||
- { type: 3, family: IPv6 }
|
||||
- { type: 4, family: IPv6 }
|
||||
- { type: 128, family: IPv6 }
|
||||
- { type: 129, family: IPv6 }
|
||||
- { type: 133, family: IPv6 }
|
||||
- { type: 134, family: IPv6 }
|
||||
- { type: 135, family: IPv6 }
|
||||
- { type: 136, family: IPv6 }
|
||||
- { type: 143, family: IPv6 }
|
||||
egress:
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": gitea
|
||||
# Webhooks to hosts served by Traefik (ci.renner.uno).
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": ingress
|
||||
app.kubernetes.io/name: traefik
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": kube-system
|
||||
k8s-app: kube-dns
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "53"
|
||||
protocol: UDP
|
||||
- port: "53"
|
||||
protocol: TCP
|
||||
# Push webhook of the notes repo to wiki-index.
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": ai
|
||||
app.kubernetes.io/name: wiki-index
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8080"
|
||||
protocol: TCP
|
||||
# Push mirror to GitHub and other external targets.
|
||||
- toEntities:
|
||||
- host
|
||||
- remote-node
|
||||
- world
|
||||
---
|
||||
# Traefik serves git.renner.uno / gitea.renner.uno (:3000) and the gitea-ssh
|
||||
# entrypoint (:2222). Lives beside the baseline so both land in one reconcile.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: gitea-allow-traefik
|
||||
namespace: gitea
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gitea
|
||||
ingress:
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": ingress
|
||||
app.kubernetes.io/name: traefik
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "3000"
|
||||
protocol: TCP
|
||||
- port: "2222"
|
||||
protocol: TCP
|
||||
@@ -22,6 +22,8 @@ resources:
|
||||
- external-secrets-ns.yaml
|
||||
- woodpecker-ci-ns.yaml
|
||||
- woodpecker-ns.yaml
|
||||
- gitea-ns.yaml
|
||||
- netbird-service-sync-ns.yaml
|
||||
- trmnl-ns.yaml
|
||||
- wordpress-ns.yaml
|
||||
- ai-ns.yaml
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
# Baseline policy for the `netbird-service-sync` namespace (issue #876).
|
||||
# The controller serves nothing; it only calls out: the Kubernetes API
|
||||
# (Ingress/Service reads), OpenBao (NetBird PAT) and the NetBird management API.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: netbird-service-sync
|
||||
spec:
|
||||
endpointSelector: {}
|
||||
ingress:
|
||||
- fromEntities:
|
||||
- host
|
||||
- remote-node
|
||||
egress:
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": kube-system
|
||||
k8s-app: kube-dns
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "53"
|
||||
protocol: UDP
|
||||
- port: "53"
|
||||
protocol: TCP
|
||||
- toEntities:
|
||||
- kube-apiserver
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": openbao
|
||||
app.kubernetes.io/name: openbao
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8200"
|
||||
protocol: TCP
|
||||
# NetBird management API (netbird.renner.uno).
|
||||
- toEntities:
|
||||
- world
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "443"
|
||||
protocol: TCP
|
||||
@@ -41,6 +41,16 @@ spec:
|
||||
- ports:
|
||||
- port: "8200"
|
||||
protocol: TCP
|
||||
# netbird-service-sync logs in via auth/kubernetes/role/netbird-service-sync
|
||||
# and reads the NetBird PAT on every pass.
|
||||
- fromEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": netbird-service-sync
|
||||
app.kubernetes.io/name: netbird-service-sync
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8200"
|
||||
protocol: TCP
|
||||
# (Secret-rotation CronJobs run in THIS namespace since 2026-07-18, so their
|
||||
# OpenBao access is covered by the intra-namespace ingress rule above — no
|
||||
# per-app rule needed.)
|
||||
@@ -127,7 +137,7 @@ spec:
|
||||
# Egress complement to gitea-allow-flux-rotator (apps/gitea/networkpolicy.yaml).
|
||||
# The flux-git rotator mints a PAT against the in-cluster gitea Service; the
|
||||
# openbao default-deny only permits TCP egress to openbao/kube-dns/apiserver/
|
||||
# world, and gitea (apps ns) is in-cluster, so it needs an explicit allow.
|
||||
# world, and gitea (gitea ns) is in-cluster, so it needs an explicit allow.
|
||||
# Scoped to the flux-git rotator label and gitea :3000.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
@@ -142,7 +152,7 @@ spec:
|
||||
egress:
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": apps
|
||||
"k8s:io.kubernetes.pod.namespace": gitea
|
||||
app.kubernetes.io/name: gitea
|
||||
toPorts:
|
||||
- ports:
|
||||
@@ -150,10 +160,10 @@ spec:
|
||||
protocol: TCP
|
||||
---
|
||||
# Egress for the review-bot rotator. Its CronJob has two steps: the init step
|
||||
# mints review-bot's PAT against gitea (apps :3000), the main step upserts the
|
||||
# mints review-bot's PAT against gitea (gitea :3000), the main step upserts the
|
||||
# Woodpecker org secret against the woodpecker server (woodpecker :8000). Both run
|
||||
# under the same pod labels, so one egress policy covers both hops. Complements
|
||||
# gitea-allow-review-bot-rotator (apps) + woodpecker-allow-rotators (woodpecker).
|
||||
# gitea-allow-review-bot-rotator (gitea) + woodpecker-allow-rotators (woodpecker).
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
@@ -167,7 +177,7 @@ spec:
|
||||
egress:
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
"k8s:io.kubernetes.pod.namespace": apps
|
||||
"k8s:io.kubernetes.pod.namespace": gitea
|
||||
app.kubernetes.io/name: gitea
|
||||
toPorts:
|
||||
- ports:
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# OpenBao policy: netbird-proxy-services
|
||||
#
|
||||
# The reverse-proxy-services controller (Deployment netbird-proxy-services in
|
||||
# the openbao namespace) reads the NetBird PAT on every reconcile pass and
|
||||
# calls the management API with it. Read-only on that one path; it shares the
|
||||
# bootstrap credential with rotator-netbird but not that role's PATCH rights.
|
||||
#
|
||||
# NOT applied by Flux. Apply ONCE with an admin token:
|
||||
#
|
||||
# bao policy write netbird-proxy-services kubernetes/infrastructure/openbao/policies/netbird-proxy-services.hcl
|
||||
# bao write auth/kubernetes/role/netbird-proxy-services \
|
||||
# bound_service_account_names=netbird-proxy-services \
|
||||
# bound_service_account_namespaces=openbao \
|
||||
# policies=netbird-proxy-services ttl=15m
|
||||
#
|
||||
# The PAT itself is the one rotator-netbird already uses
|
||||
# (homelab/infrastructure/secret-rotators-bootstrap/netbird, key `pat`).
|
||||
#
|
||||
# Do NOT set token_no_default_policy=true (kv-v2 preflight needs the default policy).
|
||||
|
||||
path "homelab/data/infrastructure/secret-rotators-bootstrap/netbird" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# OpenBao policy: netbird-service-sync
|
||||
#
|
||||
# The controller that syncs public Ingresses into NetBird reverse-proxy services
|
||||
# (Deployment netbird-service-sync in the netbird-service-sync namespace) reads
|
||||
# the NetBird PAT on every reconcile pass and calls the management API with it.
|
||||
# Read-only on that one path; it shares the bootstrap credential with
|
||||
# rotator-netbird but not that role's PATCH rights.
|
||||
#
|
||||
# NOT applied by Flux. Apply ONCE with an admin token:
|
||||
#
|
||||
# bao policy write netbird-service-sync kubernetes/infrastructure/openbao/policies/netbird-service-sync.hcl
|
||||
# bao write auth/kubernetes/role/netbird-service-sync \
|
||||
# bound_service_account_names=netbird-service-sync \
|
||||
# bound_service_account_namespaces=netbird-service-sync \
|
||||
# policies=netbird-service-sync ttl=15m
|
||||
#
|
||||
# The PAT itself is the one rotator-netbird already uses
|
||||
# (homelab/infrastructure/secret-rotators-bootstrap/netbird, key `pat`).
|
||||
#
|
||||
# Do NOT set token_no_default_policy=true (kv-v2 preflight needs the default policy).
|
||||
|
||||
path "homelab/data/infrastructure/secret-rotators-bootstrap/netbird" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
# newest. A token that fails verification is deleted and the run aborts without
|
||||
# storing, so Flux is never stranded.
|
||||
#
|
||||
# gitea is reached at the in-cluster Service gitea.apps.svc:3000 — the public
|
||||
# gitea is reached at the in-cluster Service gitea.gitea.svc:3000 — the public
|
||||
# name hairpins off the ingress LB from a pod. Needs BOTH netpol halves
|
||||
# (gitea-allow-flux-rotator + rotator-gitea-flux-egress).
|
||||
#
|
||||
@@ -76,7 +76,7 @@ spec:
|
||||
- name: BAO_PATH
|
||||
value: homelab/infrastructure/flux-git
|
||||
- name: GITEA_URL
|
||||
value: http://gitea.apps.svc.cluster.local:3000
|
||||
value: http://gitea.gitea.svc.cluster.local:3000
|
||||
- name: GITEA_VERIFY_REPO
|
||||
value: k.renner/homelab
|
||||
- name: GITEA_SCOPES
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
# synced. Quarterly cadence + backoffLimit:1 makes a two-cycle failure unlikely,
|
||||
# but a failed sync should be fixed before the next run rather than left.
|
||||
#
|
||||
# gitea is reached in-cluster at gitea.apps.svc:3000, woodpecker at
|
||||
# gitea is reached in-cluster at gitea.gitea.svc:3000, woodpecker at
|
||||
# woodpecker.woodpecker.svc:8000 (it moved out of `apps` in the namespace
|
||||
# migration — see kubernetes/apps/woodpecker/MIGRATION-namespace.md). Needs
|
||||
# the netpol halves: gitea-allow-review-bot-rotator + woodpecker-allow-rotators
|
||||
@@ -99,7 +99,7 @@ spec:
|
||||
- name: BAO_PATH
|
||||
value: homelab/infrastructure/review-bot
|
||||
- name: GITEA_URL
|
||||
value: http://gitea.apps.svc.cluster.local:3000
|
||||
value: http://gitea.gitea.svc.cluster.local:3000
|
||||
# Private repo review-bot collaborates on: the pull check is a
|
||||
# real access verification (git-upload-pack on a PRIVATE repo
|
||||
# needs a token with access), not the "any valid token" weakness
|
||||
|
||||
@@ -225,15 +225,8 @@ metadata:
|
||||
labels:
|
||||
app.kubernetes.io/part-of: homelab-backup
|
||||
spec:
|
||||
# Daily at 07:15 local (next free stagger slot after home-assistant-daily
|
||||
# at 06:45). Covers every stateful workload in the shared `apps`
|
||||
# namespace — currently Gitea (data-gitea-0, the Flux GitOps source of
|
||||
# truth) and Woodpecker (data-woodpecker-server-0), plus gatus-data.
|
||||
# Added 2026-07-14 (issue #186): the apps namespace had no Velero
|
||||
# schedule at all, so the in-cluster Gitea/Woodpecker PVCs migrated from
|
||||
# the legacy LXCs (see MIGRATION-gitea-woodpecker.md) were unprotected.
|
||||
# No CNPG cluster runs in this namespace (both Gitea and Woodpecker use
|
||||
# SQLite), so no labelSelector exclusion is needed.
|
||||
# Daily at 07:15 local. Every PVC in the shared `apps` namespace (gatus-data).
|
||||
# No CNPG cluster runs here, so no labelSelector exclusion is needed.
|
||||
schedule: "15 7 * * *"
|
||||
useOwnerReferencesInBackup: false
|
||||
template:
|
||||
@@ -247,6 +240,26 @@ spec:
|
||||
---
|
||||
apiVersion: velero.io/v1
|
||||
kind: Schedule
|
||||
metadata:
|
||||
name: gitea-daily
|
||||
namespace: velero
|
||||
labels:
|
||||
app.kubernetes.io/part-of: homelab-backup
|
||||
spec:
|
||||
# Daily at 07:45 local. data-gitea-0 (SQLite, repos, LFS, packages).
|
||||
schedule: "45 7 * * *"
|
||||
useOwnerReferencesInBackup: false
|
||||
template:
|
||||
includedNamespaces:
|
||||
- gitea
|
||||
includedResources:
|
||||
- persistentvolumeclaims
|
||||
storageLocation: default
|
||||
ttl: 720h0m0s
|
||||
snapshotMoveData: true
|
||||
---
|
||||
apiVersion: velero.io/v1
|
||||
kind: Schedule
|
||||
metadata:
|
||||
name: trmnl-daily
|
||||
namespace: velero
|
||||
|
||||
@@ -96,7 +96,7 @@ spec:
|
||||
# that traffic is always a pod IP — collapsing all external clients
|
||||
# to one source. The NetBird reverse-proxy services are TLS
|
||||
# passthrough and carry the WAN client address in a PROXY protocol
|
||||
# v2 header (options.proxy_protocol, set by netbird-proxy-services);
|
||||
# v2 header (options.proxy_protocol, set by netbird-service-sync);
|
||||
# X-Forwarded-For stays trusted for HTTP-mode services. Both are
|
||||
# accepted from the pod CIDRs only; connections without a header
|
||||
# (direct LAN/mesh) keep their RemoteAddr.
|
||||
|
||||
Reference in New Issue
Block a user