{{- if .Values.deployment.kong.enabled -}} apiVersion: v1 kind: ConfigMap metadata: name: {{ include "supabase.kong.fullname" . }} labels: {{- include "supabase.labels" . | nindent 4 }} data: wrapper.sh: | #!/bin/bash set -euo pipefail echo "Replacing env placeholders of /usr/local/kong/kong.yml" sed \ -e "s|\${SUPABASE_ANON_KEY}|${SUPABASE_ANON_KEY}|" \ -e "s|\${SUPABASE_SERVICE_KEY}|${SUPABASE_SERVICE_KEY}|" \ /usr/local/kong/template.yml \ > /usr/local/kong/kong.yml exec /docker-entrypoint.sh kong docker-start template.yml: | _format_version: '2.1' _transform: true consumers: - username: anon keyauth_credentials: - key: ${SUPABASE_ANON_KEY} - username: service_role keyauth_credentials: - key: ${SUPABASE_SERVICE_KEY} acls: - consumer: anon group: anon - consumer: service_role group: admin services: # Dedicated health endpoint used by the GCE LB BackendConfig. # request-termination returns 200 synchronously without hitting any # upstream, so the probe passes as long as the Kong proxy itself is # accepting requests -- same liveness semantics as the TCP check we # originally wanted, but using the HTTP protocol that GCE's L7 # BackendConfig CRD actually accepts (TCP is rejected with # `Protocol "TCP" is not valid, must be one of [HTTP,HTTPS,HTTP2]`). # # URL is a RFC-2606 reserved `.invalid` hostname that never resolves. # We originally tried `http://127.0.0.1:8000/` here, which is Kong's # own proxy port -- this crashlooped the pod on startup (rollout timed # out waiting for new pod to become Ready). Suspected cause: Kong's # declarative-config parser rejects the self-reference. Since the # request-termination plugin short-circuits before any DNS lookup or # upstream connection, using a non-resolvable placeholder URL is # equivalent in behavior but avoids the loop detection. - name: healthz url: http://knoe.healthz.invalid/ routes: - name: healthz strip_path: true paths: - /healthz plugins: - name: request-termination config: status_code: 200 message: ok # Stop-gap support endpoint -- 302s to mailto:support@knoe.dev so any user # who lands at https://db.0.knoe.dev/support gets a working escape hatch. # The in-Studio "Report a problem" / "Send feedback" buttons in upstream # Studio are hardcoded to supabase.com endpoints and are not yet rewired # (would require forking the Studio image -- tracked in docs/TODO.md). # In the meantime we tell users: "for help, go to db.0.knoe.dev/support". # request-termination synthesizes a 302 status; response-transformer # injects the Location header (request-termination alone can't set it). - name: support url: http://knoe.support.invalid/ routes: - name: support-redirect strip_path: true paths: - /support plugins: - name: request-termination config: status_code: 302 message: "Redirecting to support@knoe.dev" - name: response-transformer config: add: headers: - "Location:mailto:support@knoe.dev" {{- if .Values.knoeAuth.enabled }} - name: knoe-auth _comment: "knoe-auth OIDC provider: /auth (no /v1 prefix) -> knoe-auth.knoe-system" url: http://knoe-auth.knoe-system.svc.cluster.local:8080 routes: - name: knoe-auth-all strip_path: false paths: - /auth plugins: - name: cors {{- end }} {{- if .Values.knoeJobs.enabled }} - name: knoe-secret _comment: "Secure-dropbox bridge: /secret/* -> knoe-jobs.knoe-system:8081. No key-auth/acl: knoe-jobs validates the knoe-auth Bearer JWT itself; strip_path:false so /secret/retrieve reaches the handler." url: http://knoe-jobs.knoe-system.svc.cluster.local:8081 routes: - name: knoe-secret-all strip_path: false paths: - /secret/ plugins: - name: cors {{- end }} {{- if .Values.deployment.auth.enabled }} - name: auth-v1-open url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.service.auth.port }}/verify routes: - name: auth-v1-open strip_path: true paths: - /auth/v1/verify plugins: - name: cors - name: auth-v1-open-callback url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.service.auth.port }}/callback routes: - name: auth-v1-open-callback strip_path: true paths: - /auth/v1/callback plugins: - name: cors - name: auth-v1-open-authorize url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.service.auth.port }}/authorize routes: - name: auth-v1-open-authorize strip_path: true paths: - /auth/v1/authorize plugins: - name: cors - name: auth-v1 _comment: "GoTrue: /auth/v1/* -> http://{{ include "supabase.auth.fullname" . }}:{{ .Values.service.auth.port }}/*" url: http://{{ include "supabase.auth.fullname" . }}:{{ .Values.service.auth.port }} routes: - name: auth-v1-all strip_path: true paths: - /auth/v1/ plugins: - name: cors - name: key-auth config: hide_credentials: false - name: acl config: hide_groups_header: true allow: - admin - anon {{- end }} {{- if .Values.deployment.rest.enabled }} - name: rest-v1 _comment: "PostgREST: /rest/v1/* -> http://{{ include "supabase.rest.fullname" . }}:{{ .Values.service.rest.port }}/*" url: http://{{ include "supabase.rest.fullname" . }}:{{ .Values.service.rest.port }}/ routes: - name: rest-v1-all strip_path: true paths: - /rest/v1/ plugins: - name: cors - name: key-auth config: hide_credentials: true - name: acl config: hide_groups_header: true allow: - admin - anon - name: graphql-v1 _comment: 'PostgREST: /graphql/v1/* -> http://{{ include "supabase.rest.fullname" . }}:{{ .Values.service.rest.port }}/rpc/graphql' url: http://{{ include "supabase.rest.fullname" . }}:{{ .Values.service.rest.port }}/rpc/graphql routes: - name: graphql-v1-all strip_path: true paths: - /graphql/v1 plugins: - name: cors - name: key-auth config: hide_credentials: true - name: request-transformer config: add: headers: - Content-Profile:graphql_public - name: acl config: hide_groups_header: true allow: - admin - anon {{- end }} {{- if .Values.deployment.realtime.enabled }} - name: realtime-v1-ws _comment: "Realtime: /realtime/v1/* -> ws://{{ include "supabase.realtime.fullname" . }}:{{ .Values.service.realtime.port }}/socket/*" url: http://{{ include "supabase.realtime.fullname" . }}:{{ .Values.service.realtime.port }}/socket protocol: ws routes: - name: realtime-v1-ws strip_path: true paths: - /realtime/v1/ plugins: - name: cors - name: key-auth config: hide_credentials: false - name: acl config: hide_groups_header: true allow: - admin - anon - name: realtime-v1-rest _comment: 'Realtime: /realtime/v1/* -> http://{{ include "supabase.realtime.fullname" . }}:{{ .Values.service.realtime.port }}/api/*' url: http://{{ include "supabase.realtime.fullname" . }}:{{ .Values.service.realtime.port }}/api protocol: http routes: - name: realtime-v1-rest strip_path: true paths: - /realtime/v1/api plugins: - name: cors - name: key-auth config: hide_credentials: false - name: acl config: hide_groups_header: true allow: - admin - anon {{- end }} {{- if .Values.deployment.storage.enabled }} - name: storage-v1 _comment: "Storage: /storage/v1/* -> http://{{ include "supabase.storage.fullname" . }}:{{ .Values.service.storage.port }}/*" url: http://{{ include "supabase.storage.fullname" . }}:{{ .Values.service.storage.port }}/ routes: - name: storage-v1-all strip_path: true paths: - /storage/v1/ plugins: - name: cors {{- end }} {{- if .Values.deployment.functions.enabled }} - name: functions-v1 _comment: 'Edge Functions: /functions/v1/* -> http://{{ include "supabase.functions.fullname" . }}:{{ .Values.service.functions.port }}/*' url: http://{{ include "supabase.functions.fullname" . }}:{{ .Values.service.functions.port }}/ routes: - name: functions-v1-all strip_path: true paths: - /functions/v1/ plugins: - name: cors {{- end }} {{/* ## Not used - Studio and Vector talk directly to analytics via Docker networking. ## If external access is needed, add routes with key-auth matching Logflare's x-api-key auth. */}} {{/* {{- if .Values.deployment.analytics.enabled }} - name: analytics-v1 _comment: 'Analytics: /analytics/v1/* -> http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.service.analytics.port }}/*' url: http://{{ include "supabase.analytics.fullname" . }}:{{ .Values.service.analytics.port }}/ routes: - name: analytics-v1-all strip_path: true paths: - /analytics/v1/ {{- end }} */}} {{- if .Values.deployment.meta.enabled }} - name: meta _comment: "pg-meta: /pg/* -> http://{{ include "supabase.meta.fullname" . }}:{{ .Values.service.meta.port }}/*" url: http://{{ include "supabase.meta.fullname" . }}:{{ .Values.service.meta.port }}/ routes: - name: meta-all strip_path: true paths: - /pg/ plugins: - name: key-auth config: hide_credentials: false - name: acl config: hide_groups_header: true allow: - admin {{- end }} # Onboarding reveal page (knoe-onboard nginx pod). Listed BEFORE the # /-wildcard dashboard route so Kong's longest-prefix match picks # /onboard.html over the dashboard. The page itself is also exempted # from oauth2-proxy gating (`--skip-auth-route=^/onboard\.html$`) since # URL fragments don't survive an OAuth redirect; URL secrecy + 24h # expiry + immediate-rotation is the security envelope. - name: onboard _comment: 'Onboarding reveal: /onboard.html -> knoe-onboard nginx pod' url: http://knoe-onboard:80/ routes: - name: onboard-html strip_path: false paths: - /onboard.html plugins: - name: cors - name: dashboard _comment: 'Studio: /* -> http://{{ include "supabase.studio.fullname" . }}:{{ .Values.service.studio.port }}/*' url: http://{{ include "supabase.studio.fullname" . }}:{{ .Values.service.studio.port }}/ routes: - name: dashboard-all strip_path: true paths: - / plugins: - name: cors {{- end }}