From 166ff0ffb79b454af467ad006c0fa6f6ea7f6dd0 Mon Sep 17 00:00:00 2001 From: Scooby Husky Date: Sat, 22 Aug 2026 13:59:03 -0500 Subject: [PATCH] GitLab cross-site replication Phase 2b: home-side Gitaly network exposure Per-pod NodePort Services for the 3 existing Gitaly nodes (ha-gitaly-nodeport.yaml) plus home's Rails internal API (ha-gitlab-internal-api-nodeport.yaml, needed for gitlab-shell hooks whenever the VPS node is primary for a repo). PeerAuthentication PERMISSIVE + AuthorizationPolicy port rules for both, matching the proven Postgres pattern. CoreDNS rewrites so home's own Praefect resolves the floating hostnames locally instead of round-tripping. Reached over the Netbird mesh, not the UniFi WAN forward used for Postgres - Gitaly gRPC here is plaintext (carries the gitaly_token, gitlab_shell_secret, and raw repo contents), unlike CNPG's TLS-negotiated stream. Co-Authored-By: Claude Sonnet 5 --- apps/gitlab/manifests/ha-gitaly-nodeport.yaml | 59 +++++++++++++++++++ .../ha-gitlab-internal-api-nodeport.yaml | 23 ++++++++ apps/gitlab/manifests/ha-peerauth.yaml | 39 ++++++++++++ .../coredns/manifests/coredns-configmap.yaml | 10 ++++ .../apps/authorization-policies-apps.yaml | 2 + 5 files changed, 133 insertions(+) create mode 100644 apps/gitlab/manifests/ha-gitaly-nodeport.yaml create mode 100644 apps/gitlab/manifests/ha-gitlab-internal-api-nodeport.yaml diff --git a/apps/gitlab/manifests/ha-gitaly-nodeport.yaml b/apps/gitlab/manifests/ha-gitaly-nodeport.yaml new file mode 100644 index 0000000..276a854 --- /dev/null +++ b/apps/gitlab/manifests/ha-gitaly-nodeport.yaml @@ -0,0 +1,59 @@ +--- +# GitLab cross-site replication Phase 2b (see +# /home/scooby/.claude/plans/jiggly-snacking-iverson.md) - exposes each +# of home's 3 existing Gitaly nodes individually to the VPS over the +# Netbird mesh, so Praefect can register the VPS as a 4th node in the +# SAME virtual storage and inter-Gitaly replication RPCs can reach each +# node directly. Per-pod (not per-StatefulSet), unlike the chart's own +# gitlab-gitaly-default Service - Praefect/Gitaly must address each node +# individually, not load-balance across them. +# +# Reached over the Netbird mesh (100.108.x.x), NOT the UniFi WAN forward +# used for Postgres - Gitaly's gRPC here is PLAINTEXT (carries the +# gitaly_token, the gitlab_shell_secret, and raw repository contents), +# unlike CNPG's TLS-negotiated stream. Home's nodes are directly +# reachable from Netbird peers on their real node IP (confirmed live for +# the ha-failover etcd precedent - infrastructure/ha-failover/manifests/ +# etcd.yaml), so NodePort binds on that same interface without any +# additional exposure. +apiVersion: v1 +kind: Service +metadata: + name: ha-gitaly-0 + namespace: gitlab +spec: + type: NodePort + selector: + statefulset.kubernetes.io/pod-name: gitlab-gitaly-default-0 + ports: + - port: 8075 + targetPort: 8075 + nodePort: 32446 +--- +apiVersion: v1 +kind: Service +metadata: + name: ha-gitaly-1 + namespace: gitlab +spec: + type: NodePort + selector: + statefulset.kubernetes.io/pod-name: gitlab-gitaly-default-1 + ports: + - port: 8075 + targetPort: 8075 + nodePort: 32447 +--- +apiVersion: v1 +kind: Service +metadata: + name: ha-gitaly-2 + namespace: gitlab +spec: + type: NodePort + selector: + statefulset.kubernetes.io/pod-name: gitlab-gitaly-default-2 + ports: + - port: 8075 + targetPort: 8075 + nodePort: 32448 diff --git a/apps/gitlab/manifests/ha-gitlab-internal-api-nodeport.yaml b/apps/gitlab/manifests/ha-gitlab-internal-api-nodeport.yaml new file mode 100644 index 0000000..dad4ba4 --- /dev/null +++ b/apps/gitlab/manifests/ha-gitlab-internal-api-nodeport.yaml @@ -0,0 +1,23 @@ +# GitLab cross-site replication Phase 2b - exposes home's Rails internal +# API (port 8181, a dedicated internal-only listener, deliberately NOT +# exposed through the public Istio ingress on 8080) to the VPS's Gitaly +# node. Gitaly's gitlab-shell hooks call POST /api/v4/internal/{allowed, +# pre_receive,post_receive} against this whenever the VPS node is +# primary for a repository (per-repository election can cause this even +# in normal operation) or after a real failover. Carries the shell +# secret in a header - same plaintext-over-Netbird-mesh reasoning as +# ha-gitaly-nodeport.yaml, not the UniFi WAN forward. +apiVersion: v1 +kind: Service +metadata: + name: ha-gitlab-internal-api + namespace: gitlab +spec: + type: NodePort + selector: + app: webservice + release: gitlab + ports: + - port: 8181 + targetPort: 8181 + nodePort: 32449 diff --git a/apps/gitlab/manifests/ha-peerauth.yaml b/apps/gitlab/manifests/ha-peerauth.yaml index ace7673..7c6efc2 100644 --- a/apps/gitlab/manifests/ha-peerauth.yaml +++ b/apps/gitlab/manifests/ha-peerauth.yaml @@ -49,3 +49,42 @@ spec: mode: PERMISSIVE "61443": mode: PERMISSIVE +--- +# Phase 2b (Gitaly cross-site replication) - the 3 Gitaly pods, exposed +# individually via ha-gitaly-nodeport.yaml for the VPS's 4th node to +# join the same virtual storage. Gitaly pods already carry +# istio.io/use-waypoint: none (chart default, gRPC doesn't tolerate L7 +# waypoint processing well) but that only skips L7 processing - L4 +# ztunnel mTLS enforcement is a separate concern, same fix needed as the +# Postgres precedent. +apiVersion: security.istio.io/v1 +kind: PeerAuthentication +metadata: + name: allow-ha-gitaly-replication + namespace: gitlab +spec: + selector: + matchLabels: + app: gitaly + mtls: + mode: STRICT + portLevelMtls: + "8075": + mode: PERMISSIVE +--- +# Phase 2b - the VPS's Gitaly node calling home's Rails internal API +# (ha-gitlab-internal-api-nodeport.yaml) for gitlab-shell hooks. +apiVersion: security.istio.io/v1 +kind: PeerAuthentication +metadata: + name: allow-ha-gitlab-internal-api + namespace: gitlab +spec: + selector: + matchLabels: + app: webservice + mtls: + mode: STRICT + portLevelMtls: + "8181": + mode: PERMISSIVE diff --git a/infrastructure/coredns/manifests/coredns-configmap.yaml b/infrastructure/coredns/manifests/coredns-configmap.yaml index 5a6e327..86e44ad 100644 --- a/infrastructure/coredns/manifests/coredns-configmap.yaml +++ b/infrastructure/coredns/manifests/coredns-configmap.yaml @@ -67,6 +67,16 @@ data: rewrite name pg-gitlab.ha.huskypup.net ha-gitlab-postgres.gitlab.svc.cluster.local rewrite name pg-praefect.ha.huskypup.net ha-praefect-postgres.gitlab.svc.cluster.local + # GitLab cross-site replication Phase 2b (Gitaly) - home's own + # Praefect dials these SAME floating hostnames it hands to the + # VPS's Gitaly node as peer addresses (Praefect uses one address + # per node for everyone, not different addresses per caller), so + # home's own in-cluster traffic to its own local Gitaly nodes + # needs to resolve locally too, same reasoning as pg-*.ha above. + rewrite name gitaly-0.ha.huskypup.net ha-gitaly-0.gitlab.svc.cluster.local + rewrite name gitaly-1.ha.huskypup.net ha-gitaly-1.gitlab.svc.cluster.local + rewrite name gitaly-2.ha.huskypup.net ha-gitaly-2.gitlab.svc.cluster.local + kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa diff --git a/infrastructure/istio/manifests/apps/authorization-policies-apps.yaml b/infrastructure/istio/manifests/apps/authorization-policies-apps.yaml index 34adfff..232e45e 100644 --- a/infrastructure/istio/manifests/apps/authorization-policies-apps.yaml +++ b/infrastructure/istio/manifests/apps/authorization-policies-apps.yaml @@ -100,6 +100,8 @@ spec: - "5432" - "61442" # pg-gitlab floating-hostname port, see ha-postgres-nodeport.yaml - "61443" # pg-praefect floating-hostname port, see ha-postgres-nodeport.yaml + - "8075" # Gitaly gRPC, see ha-gitaly-nodeport.yaml + - "8181" # Rails internal API, see ha-gitlab-internal-api-nodeport.yaml --- # --- n8n: Allow ingress + Prometheus + intra-namespace ---