From 0f4a13d8d244b9210c51e66779de47c3d3d1b1f3 Mon Sep 17 00:00:00 2001 From: Yohan Date: Mon, 27 Apr 2026 06:14:26 +0000 Subject: [PATCH 1/2] feat(soludev): add opencode deployment with oauth2-proxy protection - Add OpenCode web interface deployment in soludev namespace - Configure oauth2-proxy with Logto OIDC authentication - Add ingress for opencode.soludev.tech - Add persistent volume for data storage - Add external secrets for opencode and oauth2-proxy credentials Host: opencode.soludev.tech Auth: Logto via oauth2-proxy --- config/dev/oauth2-proxy/soludev/values.yaml | 14 +++ dev/soludev/kustomization.yaml | 14 +++ dev/soludev/oauth2-proxy/configmap.yaml | 32 +++++ dev/soludev/oauth2-proxy/deployment.yaml | 61 +++++++++ dev/soludev/oauth2-proxy/external-secret.yaml | 16 +++ dev/soludev/oauth2-proxy/ingress.yaml | 20 +++ dev/soludev/oauth2-proxy/service.yaml | 15 +++ dev/soludev/opencode/deployment.yaml | 118 ++++++++++++++++++ dev/soludev/opencode/external-secret.yaml | 16 +++ dev/soludev/opencode/ingress.yaml | 20 +++ dev/soludev/opencode/service.yaml | 15 +++ dev/soludev/opencode/volume-claim.yaml | 12 ++ 12 files changed, 353 insertions(+) create mode 100644 config/dev/oauth2-proxy/soludev/values.yaml create mode 100644 dev/soludev/kustomization.yaml create mode 100644 dev/soludev/oauth2-proxy/configmap.yaml create mode 100644 dev/soludev/oauth2-proxy/deployment.yaml create mode 100644 dev/soludev/oauth2-proxy/external-secret.yaml create mode 100644 dev/soludev/oauth2-proxy/ingress.yaml create mode 100644 dev/soludev/oauth2-proxy/service.yaml create mode 100644 dev/soludev/opencode/deployment.yaml create mode 100644 dev/soludev/opencode/external-secret.yaml create mode 100644 dev/soludev/opencode/ingress.yaml create mode 100644 dev/soludev/opencode/service.yaml create mode 100644 dev/soludev/opencode/volume-claim.yaml diff --git a/config/dev/oauth2-proxy/soludev/values.yaml b/config/dev/oauth2-proxy/soludev/values.yaml new file mode 100644 index 0000000..993b42c --- /dev/null +++ b/config/dev/oauth2-proxy/soludev/values.yaml @@ -0,0 +1,14 @@ +config: + existingSecret: "oauth2-proxy-secret" + existingConfig: "oauth2-proxy-config" + cookieName: "oauth2-proxy-opencode" + +sessionStorage: + type: cookie + +ingress: + enabled: false + +service: + type: ClusterIP + portNumber: 4180 \ No newline at end of file diff --git a/dev/soludev/kustomization.yaml b/dev/soludev/kustomization.yaml new file mode 100644 index 0000000..650f5f9 --- /dev/null +++ b/dev/soludev/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - opencode/deployment.yaml + - opencode/service.yaml + - opencode/ingress.yaml + - opencode/volume-claim.yaml + - opencode/external-secret.yaml + - oauth2-proxy/deployment.yaml + - oauth2-proxy/service.yaml + - oauth2-proxy/ingress.yaml + - oauth2-proxy/configmap.yaml + - oauth2-proxy/external-secret.yaml \ No newline at end of file diff --git a/dev/soludev/oauth2-proxy/configmap.yaml b/dev/soludev/oauth2-proxy/configmap.yaml new file mode 100644 index 0000000..625b171 --- /dev/null +++ b/dev/soludev/oauth2-proxy/configmap.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: oauth2-proxy-config + namespace: soludev +data: + oauth2_proxy.cfg: | + provider = "oidc" + oidc_issuer_url = "https://logto.soludev.tech/oidc" + redirect_url = "https://opencode.soludev.tech/oauth2/callback" + scope = "openid email profile" + + cookie_domains = [".soludev.tech"] + whitelist_domains = [".soludev.tech"] + cookie_name = "oauth2-proxy-opencode" + cookie_secure = true + cookie_httponly = true + cookie_samesite = "lax" + cookie_refresh = "1h" + + upstreams = ["http://opencode.soludev.svc.cluster.local:4096"] + http_address = "0.0.0.0:4180" + reverse_proxy = true + + pass_authorization_header = true + set_authorization_header = true + pass_access_token = true + + skip_provider_button = true + silence_ping_logging = true + + email_domains = ["*"] \ No newline at end of file diff --git a/dev/soludev/oauth2-proxy/deployment.yaml b/dev/soludev/oauth2-proxy/deployment.yaml new file mode 100644 index 0000000..10a330e --- /dev/null +++ b/dev/soludev/oauth2-proxy/deployment.yaml @@ -0,0 +1,61 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: oauth2-proxy + namespace: soludev + labels: + app: oauth2-proxy +spec: + replicas: 1 + selector: + matchLabels: + app: oauth2-proxy + template: + metadata: + labels: + app: oauth2-proxy + spec: + automountServiceAccountToken: false + containers: + - name: oauth2-proxy + image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0 + imagePullPolicy: IfNotPresent + args: + - "--config=/etc/oauth2_proxy.cfg" + - "--cookie-secret=$(OAUTH2_PROXY_COOKIE_SECRET)" + - "--client-id=$(OAUTH2_PROXY_CLIENT_ID)" + - "--client-secret=$(OAUTH2_PROXY_CLIENT_SECRET)" + env: + - name: OAUTH2_PROXY_CLIENT_ID + valueFrom: + secretKeyRef: + name: oauth2-proxy-secret + key: OAUTH2_PROXY_CLIENT_ID + - name: OAUTH2_PROXY_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: oauth2-proxy-secret + key: OAUTH2_PROXY_CLIENT_SECRET + - name: OAUTH2_PROXY_COOKIE_SECRET + valueFrom: + secretKeyRef: + name: oauth2-proxy-secret + key: OAUTH2_PROXY_COOKIE_SECRET + ports: + - name: http + containerPort: 4180 + volumeMounts: + - name: config + mountPath: /etc/oauth2_proxy.cfg + subPath: oauth2_proxy.cfg + resources: + requests: + memory: "64Mi" + cpu: "50m" + limits: + memory: "128Mi" + cpu: "100m" + volumes: + - name: config + configMap: + name: oauth2-proxy-config \ No newline at end of file diff --git a/dev/soludev/oauth2-proxy/external-secret.yaml b/dev/soludev/oauth2-proxy/external-secret.yaml new file mode 100644 index 0000000..aec877d --- /dev/null +++ b/dev/soludev/oauth2-proxy/external-secret.yaml @@ -0,0 +1,16 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: oauth2-proxy-external-secret + namespace: soludev +spec: + refreshInterval: 60s + secretStoreRef: + name: openbao-backend + kind: ClusterSecretStore + target: + name: oauth2-proxy-secret + creationPolicy: Owner + dataFrom: + - extract: + key: soludev/oauth2-proxy \ No newline at end of file diff --git a/dev/soludev/oauth2-proxy/ingress.yaml b/dev/soludev/oauth2-proxy/ingress.yaml new file mode 100644 index 0000000..18bbab2 --- /dev/null +++ b/dev/soludev/oauth2-proxy/ingress.yaml @@ -0,0 +1,20 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: oauth2-proxy-ingress + namespace: soludev + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: web,websecure + traefik.ingress.kubernetes.io/redirect-scheme: https +spec: + rules: + - host: opencode-oauth2-proxy.soludev.tech + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: oauth2-proxy-service + port: + number: 4180 \ No newline at end of file diff --git a/dev/soludev/oauth2-proxy/service.yaml b/dev/soludev/oauth2-proxy/service.yaml new file mode 100644 index 0000000..020d167 --- /dev/null +++ b/dev/soludev/oauth2-proxy/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: oauth2-proxy-service + namespace: soludev + labels: + app: oauth2-proxy +spec: + type: ClusterIP + selector: + app: oauth2-proxy + ports: + - name: http + port: 4180 + targetPort: 4180 \ No newline at end of file diff --git a/dev/soludev/opencode/deployment.yaml b/dev/soludev/opencode/deployment.yaml new file mode 100644 index 0000000..266a008 --- /dev/null +++ b/dev/soludev/opencode/deployment.yaml @@ -0,0 +1,118 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: opencode + namespace: soludev + labels: + app: opencode +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: opencode + template: + metadata: + labels: + app: opencode + spec: + automountServiceAccountToken: false + + initContainers: + - name: init-config + image: busybox:latest + command: + - sh + - -c + - | + if [ ! -f /config/opencode.json ]; then + cat > /config/opencode.json << EOF + { + "server": { + "port": 4096, + "hostname": "0.0.0.0" + }, + "data": { + "directory": "/data" + }, + "providers": { + "openrouter": { + "apiKey": "${OPENROUTER_API_KEY}" + } + }, + "agents": { + "coder": { + "model": "anthropic/claude-sonnet-4-20250514" + } + } + } + EOF + fi + env: + - name: OPENROUTER_API_KEY + valueFrom: + secretKeyRef: + name: opencode-secret + key: OPENROUTER_API_KEY + volumeMounts: + - name: config + mountPath: /config + + containers: + - name: opencode + image: ghcr.io/opencode-ai/opencode:latest + imagePullPolicy: Always + + command: ["opencode", "web", "--port", "4096", "--hostname", "0.0.0.0"] + + ports: + - name: http + containerPort: 4096 + + env: + - name: OPENCODE_SERVER_PASSWORD + valueFrom: + secretKeyRef: + name: opencode-secret + key: OPENCODE_SERVER_PASSWORD + - name: OPENROUTER_API_KEY + valueFrom: + secretKeyRef: + name: opencode-secret + key: OPENROUTER_API_KEY + + volumeMounts: + - name: config + mountPath: /home/node/.config/opencode + - name: data + mountPath: /data + + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "500m" + + livenessProbe: + httpGet: + path: /global/health + port: http + initialDelaySeconds: 30 + periodSeconds: 30 + + readinessProbe: + httpGet: + path: /global/health + port: http + initialDelaySeconds: 10 + periodSeconds: 10 + + volumes: + - name: config + emptyDir: {} + - name: data + persistentVolumeClaim: + claimName: opencode-data \ No newline at end of file diff --git a/dev/soludev/opencode/external-secret.yaml b/dev/soludev/opencode/external-secret.yaml new file mode 100644 index 0000000..6a765f2 --- /dev/null +++ b/dev/soludev/opencode/external-secret.yaml @@ -0,0 +1,16 @@ +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: opencode-external-secret + namespace: soludev +spec: + refreshInterval: 60s + secretStoreRef: + name: openbao-backend + kind: ClusterSecretStore + target: + name: opencode-secret + creationPolicy: Owner + dataFrom: + - extract: + key: soludev/opencode \ No newline at end of file diff --git a/dev/soludev/opencode/ingress.yaml b/dev/soludev/opencode/ingress.yaml new file mode 100644 index 0000000..fc72b3d --- /dev/null +++ b/dev/soludev/opencode/ingress.yaml @@ -0,0 +1,20 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: opencode-ingress + namespace: soludev + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: web,websecure + traefik.ingress.kubernetes.io/redirect-scheme: https +spec: + rules: + - host: opencode.soludev.tech + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: oauth2-proxy-service + port: + number: 4180 \ No newline at end of file diff --git a/dev/soludev/opencode/service.yaml b/dev/soludev/opencode/service.yaml new file mode 100644 index 0000000..c3cf60b --- /dev/null +++ b/dev/soludev/opencode/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: opencode + namespace: soludev + labels: + app: opencode +spec: + type: ClusterIP + selector: + app: opencode + ports: + - name: http + port: 4096 + targetPort: 4096 \ No newline at end of file diff --git a/dev/soludev/opencode/volume-claim.yaml b/dev/soludev/opencode/volume-claim.yaml new file mode 100644 index 0000000..afb99f3 --- /dev/null +++ b/dev/soludev/opencode/volume-claim.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: opencode-data + namespace: soludev +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + storageClassName: local-path \ No newline at end of file From 49b00e69497f4c7d176f813b2fecf5498665addc Mon Sep 17 00:00:00 2001 From: Yohan Date: Mon, 27 Apr 2026 06:16:51 +0000 Subject: [PATCH 2/2] fix(soludev): pin opencode to v0.0.55 instead of latest --- dev/soludev/opencode/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/soludev/opencode/deployment.yaml b/dev/soludev/opencode/deployment.yaml index 266a008..7566bab 100644 --- a/dev/soludev/opencode/deployment.yaml +++ b/dev/soludev/opencode/deployment.yaml @@ -61,7 +61,7 @@ spec: containers: - name: opencode - image: ghcr.io/opencode-ai/opencode:latest + image: ghcr.io/opencode-ai/opencode:v0.0.55 imagePullPolicy: Always command: ["opencode", "web", "--port", "4096", "--hostname", "0.0.0.0"]