Route runner daemon through nginx CF Access proxy

act_runner cannot inject custom HTTP headers, so CF Access blocks its
unauthenticated calls to git.neuralplatform.ai. Add a gitea-proxy
deployment (nginx:alpine) in the ci namespace that injects the CF
Access service-token headers and proxies to https://git.neuralplatform.ai.

Both runner secrets now point GITEA_INSTANCE_URL at the in-cluster proxy
(http://gitea-proxy.ci.svc.cluster.local:3000). Build containers still
clone via SSH through git-ssh-init.sh — unaffected.
This commit is contained in:
Will Anderson
2026-05-05 04:17:52 -05:00
parent ae3257525e
commit 939e66bfbb
3 changed files with 140 additions and 8 deletions
@@ -8,7 +8,7 @@ metadata:
labels:
app: gitea-runner
annotations:
config-version: "2026-05-05-ssh-clone"
config-version: "2026-05-05-gitea-proxy"
spec:
replicas: 1
selector:
@@ -19,7 +19,7 @@ spec:
labels:
app: gitea-runner
annotations:
config-version: "2026-05-05-ssh-clone"
config-version: "2026-05-05-gitea-proxy"
spec:
securityContext:
runAsNonRoot: false
@@ -100,7 +100,7 @@ metadata:
labels:
app: neuron-technologies-runner
annotations:
config-version: "2026-05-05-ssh-clone"
config-version: "2026-05-05-gitea-proxy"
spec:
replicas: 2
selector:
@@ -111,7 +111,7 @@ spec:
labels:
app: neuron-technologies-runner
annotations:
config-version: "2026-05-05-ssh-clone"
config-version: "2026-05-05-gitea-proxy"
spec:
securityContext:
runAsNonRoot: false
@@ -13,7 +13,7 @@ metadata:
name: gitea-runner-secret
namespace: ci
annotations:
force-sync: "2026-05-05-ssh-clone"
force-sync: "2026-05-05-gitea-proxy"
spec:
refreshInterval: 1h
secretStoreRef:
@@ -24,7 +24,7 @@ spec:
creationPolicy: Owner
template:
data:
GITEA_INSTANCE_URL: "https://git.neuralplatform.ai"
GITEA_INSTANCE_URL: "http://gitea-proxy.ci.svc.cluster.local:3000"
GITEA_RUNNER_REGISTRATION_TOKEN: "{{ .runner_token }}"
GITEA_SSH_PRIVATE_KEY: "{{ .ci_ssh_private_key }}"
data:
@@ -44,7 +44,7 @@ metadata:
name: neuron-technologies-runner-secret
namespace: ci
annotations:
force-sync: "2026-05-05-ssh-clone"
force-sync: "2026-05-05-gitea-proxy"
spec:
refreshInterval: 1h
secretStoreRef:
@@ -55,7 +55,7 @@ spec:
creationPolicy: Owner
template:
data:
GITEA_INSTANCE_URL: "https://git.neuralplatform.ai"
GITEA_INSTANCE_URL: "http://gitea-proxy.ci.svc.cluster.local:3000"
GITEA_RUNNER_REGISTRATION_TOKEN: "{{ .runner_token }}"
GITEA_SSH_PRIVATE_KEY: "{{ .ci_ssh_private_key }}"
data:
@@ -0,0 +1,132 @@
---
# gitea-proxy — nginx CF Access header-injecting proxy
#
# act_runner cannot add custom HTTP headers, so it can't send the
# CF-Access-Client-Id / CF-Access-Client-Secret headers that Cloudflare
# Access requires for service-to-service access to git.neuralplatform.ai.
#
# This proxy sits in the ci namespace. Runners use:
# GITEA_INSTANCE_URL: http://gitea-proxy.ci.svc.cluster.local:3000
#
# nginx:alpine processes *.conf.template files in /etc/nginx/templates/ via
# envsubst at startup — so CF Access credentials come from the Secret as env
# vars and get interpolated into the nginx config before nginx starts.
apiVersion: v1
kind: ConfigMap
metadata:
name: gitea-proxy-config
namespace: ci
data:
# Placed at /etc/nginx/templates/default.conf.template — nginx:alpine
# runs envsubst on this and writes the result to /etc/nginx/conf.d/default.conf
default.conf.template: |
server {
listen 3000;
location / {
proxy_pass https://git.neuralplatform.ai;
# Inject CF Access service-token headers so Cloudflare lets
# the request through without interactive auth.
proxy_set_header CF-Access-Client-Id "${CF_ACCESS_CLIENT_ID}";
proxy_set_header CF-Access-Client-Secret "${CF_ACCESS_CLIENT_SECRET}";
# Preserve Host so Gitea/CF edge routes correctly.
proxy_set_header Host git.neuralplatform.ai;
# Forward all original request headers (auth tokens, etc).
proxy_pass_request_headers on;
# Rewrite Location headers in redirects back to the proxy URL.
proxy_redirect https://git.neuralplatform.ai/
http://gitea-proxy.ci.svc.cluster.local:3000/;
# Standard proxy headers.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto http;
# Allow large request bodies (git push payloads).
client_max_body_size 512m;
}
}
---
# ExternalSecret — CF Access service token for the proxy
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: gitea-proxy-secret
namespace: ci
annotations:
force-sync: "2026-05-05-initial"
spec:
refreshInterval: 1h
secretStoreRef:
name: vault
kind: ClusterSecretStore
target:
name: gitea-proxy-secret
creationPolicy: Owner
data:
- secretKey: CF_ACCESS_CLIENT_ID
remoteRef:
key: secret/data/gitea-runner-cf-access
property: client_id
- secretKey: CF_ACCESS_CLIENT_SECRET
remoteRef:
key: secret/data/gitea-runner-cf-access
property: client_secret
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea-proxy
namespace: ci
labels:
app: gitea-proxy
spec:
replicas: 1
selector:
matchLabels:
app: gitea-proxy
template:
metadata:
labels:
app: gitea-proxy
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 3000
volumeMounts:
- name: config-template
mountPath: /etc/nginx/templates
envFrom:
- secretRef:
name: gitea-proxy-secret
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 200m
memory: 64Mi
volumes:
- name: config-template
configMap:
name: gitea-proxy-config
---
# Service — ClusterIP reachable by all runner pods as gitea-proxy.ci:3000
apiVersion: v1
kind: Service
metadata:
name: gitea-proxy
namespace: ci
spec:
selector:
app: gitea-proxy
ports:
- port: 3000
targetPort: 3000