feat: run platform gateway through Tailscale sidecar
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-08-12 18:11:24 +09:00
parent c90e8d5745
commit 204300c927
5 changed files with 74 additions and 2 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ AGENT_PORT=3100
AGENT_GATEWAY_TOKEN= AGENT_GATEWAY_TOKEN=
HEADSCALE_BASE_URL=https://atnmy-hs.duckdns.org HEADSCALE_BASE_URL=https://atnmy-hs.duckdns.org
HEADSCALE_API_KEY= HEADSCALE_API_KEY=
HEADSCALE_GATEWAY_USERS=local-platform-gateway,platform-gateway HEADSCALE_GATEWAY_USERS=platform-gateway
HEADSCALE_TEACHING_DEVICE_OWNER=tagged-devices HEADSCALE_TEACHING_DEVICE_OWNER=tagged-devices
LMS_GATEWAY_TOKEN=change-me LMS_GATEWAY_TOKEN=change-me
+1
View File
@@ -24,6 +24,7 @@ steps:
volumes: volumes:
- /home/future2/Studio_v2/woodpecker/kube/config:/tmp/kubeconfig:ro - /home/future2/Studio_v2/woodpecker/kube/config:/tmp/kubeconfig:ro
commands: commands:
- kubectl --kubeconfig=/tmp/kubeconfig apply -f k8s/platform-gateway.yaml
- kubectl --kubeconfig=/tmp/kubeconfig -n studio-system rollout restart deployment/platform-gateway - kubectl --kubeconfig=/tmp/kubeconfig -n studio-system rollout restart deployment/platform-gateway
- kubectl --kubeconfig=/tmp/kubeconfig -n studio-system rollout status deployment/platform-gateway - kubectl --kubeconfig=/tmp/kubeconfig -n studio-system rollout status deployment/platform-gateway
when: when:
+13
View File
@@ -30,3 +30,16 @@ Default URL: `http://localhost:4500`
## Network Model ## Network Model
Students and instructors never connect directly to Headscale/Tailscale device endpoints. Clients call this gateway only. The gateway verifies auth, checks platform grants/session ownership, records audit context, and proxies allowed operations to the mobility agent running on each platform node. Students and instructors never connect directly to Headscale/Tailscale device endpoints. Clients call this gateway only. The gateway verifies auth, checks platform grants/session ownership, records audit context, and proxies allowed operations to the mobility agent running on each platform node.
## Kubernetes Tailscale Sidecar
The Kubernetes deployment runs a Tailscale sidecar in the same network namespace as
the gateway. Create the one-time Headscale key secret before the first deployment:
```bash
kubectl -n studio-system create secret generic platform-gateway-tailscale-auth \
--from-literal=authkey='tskey-auth-...'
```
The sidecar persists its machine state in `platform-gateway-tailscale-state`, so
subsequent Gateway pod restarts do not consume another pre-auth key.
+58
View File
@@ -1,3 +1,33 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: platform-gateway
namespace: studio-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: platform-gateway-tailscale-state
namespace: studio-system
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: platform-gateway-tailscale-state
namespace: studio-system
subjects:
- kind: ServiceAccount
name: platform-gateway
namespace: studio-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: platform-gateway-tailscale-state
---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -15,7 +45,30 @@ spec:
labels: labels:
app: platform-gateway app: platform-gateway
spec: spec:
serviceAccountName: platform-gateway
containers: containers:
- name: tailscale
image: tailscale/tailscale:stable
env:
- name: TS_KUBE_SECRET
value: platform-gateway-tailscale-state
- name: TS_AUTHKEY
valueFrom:
secretKeyRef:
name: platform-gateway-tailscale-auth
key: authkey
- name: TS_HOSTNAME
value: platform-gateway
- name: TS_USERSPACE
value: "false"
- name: TS_EXTRA_ARGS
value: --login-server=https://atnmy-hs.duckdns.org --accept-routes
securityContext:
capabilities:
add: ["NET_ADMIN"]
volumeMounts:
- name: dev-net-tun
mountPath: /dev/net/tun
- name: platform-gateway - name: platform-gateway
image: localhost:5000/platform-gateway:0.1.0 image: localhost:5000/platform-gateway:0.1.0
imagePullPolicy: Always imagePullPolicy: Always
@@ -37,6 +90,11 @@ spec:
port: 4500 port: 4500
initialDelaySeconds: 15 initialDelaySeconds: 15
periodSeconds: 20 periodSeconds: 20
volumes:
- name: dev-net-tun
hostPath:
path: /dev/net/tun
type: CharDevice
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
+1 -1
View File
@@ -21,7 +21,7 @@ export const config = {
headscaleBaseUrl: required('HEADSCALE_BASE_URL', 'http://localhost:8080'), headscaleBaseUrl: required('HEADSCALE_BASE_URL', 'http://localhost:8080'),
headscaleApiKey: process.env.HEADSCALE_API_KEY ?? '', headscaleApiKey: process.env.HEADSCALE_API_KEY ?? '',
headscaleTeachingDeviceOwner: (process.env.HEADSCALE_TEACHING_DEVICE_OWNER ?? 'tagged-devices').trim().replace(/@+$/, ''), headscaleTeachingDeviceOwner: (process.env.HEADSCALE_TEACHING_DEVICE_OWNER ?? 'tagged-devices').trim().replace(/@+$/, ''),
headscaleGatewayUsers: (process.env.HEADSCALE_GATEWAY_USERS ?? process.env.HEADSCALE_GATEWAY_USER ?? 'local-platform-gateway') headscaleGatewayUsers: (process.env.HEADSCALE_GATEWAY_USERS ?? process.env.HEADSCALE_GATEWAY_USER ?? 'platform-gateway')
.split(',') .split(',')
.map((user) => user.trim().replace(/@+$/, '')) .map((user) => user.trim().replace(/@+$/, ''))
.filter(Boolean), .filter(Boolean),