mirror of
https://github.com/dredx/prole.git
synced 2026-09-24 15:44:33 +00:00
feat(auth): add Google Workspace OAuth (prole.org) for Grafana + Supabase Studio
- monitoring/kps-values-k3s.yaml: Grafana helm values for k3s homelab with dual auth (auth.proxy Kerberos + auth.google for prole.org Workspace) - grafana-proxy-configmap.yaml: nginx passthrough for /grafana/login/google and /grafana/login to allow Google OAuth flow without knoe-auth redirect - grafana-google-oidc-secret-prole.example.yaml: Secret template for Grafana Google OAuth client (svc.prole.org, Internal consent, prole.org Workspace) - oauth2-proxy-google-oidc-secret-prole.example.yaml: Secret template for oauth2-proxy gating db.prole.org Studio - oauth2-proxy-deployment-prole.yaml: k3s oauth2-proxy deployment for db.prole.org (prole.org domain, no BackendConfig) - init_grafana_oauth_prole.sh: Bootstrap script for Grafana OAuth secret - init_oauth2_proxy_prole.sh: Bootstrap script for Studio oauth2-proxy Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
52f4053718
commit
885fa99a29
29
deploy/gcp/gke/grafana-google-oidc-secret-prole.example.yaml
Normal file
29
deploy/gcp/gke/grafana-google-oidc-secret-prole.example.yaml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: grafana-google-oidc
|
||||||
|
namespace: monitoring
|
||||||
|
labels:
|
||||||
|
app: grafana
|
||||||
|
app.kubernetes.io/managed-by: knoe-installer
|
||||||
|
# Google OAuth 2.0 client credentials for Grafana's native Google sign-in
|
||||||
|
# (Grafana auth.google plugin) on the prole.org k3s homelab cluster.
|
||||||
|
# Mounted into kps-grafana via `envFromSecret: grafana-google-oidc` in
|
||||||
|
# monitoring/kps-values-k3s.yaml. Grafana reads GF_AUTH_GOOGLE_CLIENT_ID /
|
||||||
|
# GF_AUTH_GOOGLE_CLIENT_SECRET and uses them as auth.google.client_id / client_secret.
|
||||||
|
#
|
||||||
|
# Create the OAuth client at: https://console.cloud.google.com/apis/credentials
|
||||||
|
# (in the prole.org GCP project — separate from the knoey.com project)
|
||||||
|
# - Application type: Web application
|
||||||
|
# - Name: prole.org Grafana
|
||||||
|
# - Authorized JavaScript origins: https://svc.prole.org
|
||||||
|
# - Authorized redirect URIs: https://svc.prole.org/grafana/login/google
|
||||||
|
# - OAuth consent screen: Internal (prole.org Workspace only)
|
||||||
|
# - Scopes: openid, email, profile
|
||||||
|
#
|
||||||
|
# Why a separate client from prole.org Studio: independent rotation surface.
|
||||||
|
# Companion to oauth2-proxy-google-oidc-secret-prole.example.yaml.
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
GF_AUTH_GOOGLE_CLIENT_ID: "${GRAFANA_GOOGLE_CLIENT_ID}"
|
||||||
|
GF_AUTH_GOOGLE_CLIENT_SECRET: "${GRAFANA_GOOGLE_CLIENT_SECRET}"
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: oauth2-proxy-google-oidc
|
||||||
|
namespace: supabase
|
||||||
|
labels:
|
||||||
|
app: oauth2-proxy
|
||||||
|
app.kubernetes.io/managed-by: knoe-installer
|
||||||
|
# Google OAuth 2.0 client credentials + cookie secret for the oauth2-proxy
|
||||||
|
# gating Supabase Studio at db.prole.org on the k3s homelab cluster.
|
||||||
|
# Applied by etc/init_oauth2_proxy_prole.sh via envsubst from
|
||||||
|
# etc/secrets/oauth2-proxy-{client-id,client-secret,cookie-secret}-prole.
|
||||||
|
#
|
||||||
|
# Create the OAuth client at: https://console.cloud.google.com/apis/credentials
|
||||||
|
# (in the prole.org GCP project — separate from the knoey.com project)
|
||||||
|
# - Application type: Web application
|
||||||
|
# - Name: prole.org Studio
|
||||||
|
# - Authorized JavaScript origins: https://db.prole.org
|
||||||
|
# - Authorized redirect URIs: https://db.prole.org/oauth2/callback
|
||||||
|
# - OAuth consent screen: Internal (prole.org Workspace only)
|
||||||
|
# - Scopes: openid, email, profile
|
||||||
|
#
|
||||||
|
# cookie_secret: must be 32 bytes. Generate with:
|
||||||
|
# openssl rand -base64 32
|
||||||
|
#
|
||||||
|
# Why a separate client from prole.org Grafana: independent rotation surface.
|
||||||
|
# Companion to grafana-google-oidc-secret-prole.example.yaml.
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
client_id: "${OAUTH2_PROXY_CLIENT_ID}"
|
||||||
|
client_secret: "${OAUTH2_PROXY_CLIENT_SECRET}"
|
||||||
|
cookie_secret: "${OAUTH2_PROXY_COOKIE_SECRET}"
|
||||||
@ -30,6 +30,29 @@ data:
|
|||||||
proxy_set_header X-Forwarded-Host $host;
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Pass Google OAuth and Grafana login paths through without auth_request
|
||||||
|
# so auth.google sign-in can complete. Kerberos users still reach Grafana
|
||||||
|
# via the auth_request path below and get X-WEBAUTH-USER injected.
|
||||||
|
location = /grafana/login/google {
|
||||||
|
proxy_set_header X-WEBAUTH-USER "";
|
||||||
|
proxy_set_header X-Knoe-Groups "";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_pass http://kps-grafana.monitoring.svc.cluster.local:80;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^/grafana/login(/.*)?$ {
|
||||||
|
proxy_set_header X-WEBAUTH-USER "";
|
||||||
|
proxy_set_header X-Knoe-Groups "";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_pass http://kps-grafana.monitoring.svc.cluster.local:80;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
auth_request /_auth_verify;
|
auth_request /_auth_verify;
|
||||||
auth_request_set $knoe_user $upstream_http_x_knoe_user;
|
auth_request_set $knoe_user $upstream_http_x_knoe_user;
|
||||||
|
|||||||
@ -0,0 +1,150 @@
|
|||||||
|
# oauth2-proxy gate in front of Supabase Studio at db.prole.org (k3s homelab).
|
||||||
|
#
|
||||||
|
# Sits between the Traefik ingress and supabase-kong; redirects unauthenticated
|
||||||
|
# requests to Google Workspace (prole.org), allows any @prole.org identity through,
|
||||||
|
# then proxies the request upstream to supabase-kong:8000.
|
||||||
|
#
|
||||||
|
# Bootstrap: ./etc/init_oauth2_proxy_prole.sh
|
||||||
|
# Cluster: k3s homelab (myrddin/merlin/gandalf)
|
||||||
|
# Namespace: supabase
|
||||||
|
#
|
||||||
|
# After this Deployment is Ready, patch the Traefik IngressRoute (or the
|
||||||
|
# db.prole.org Ingress) to route through oauth2-proxy:80 instead of
|
||||||
|
# supabase-kong:8000 directly.
|
||||||
|
#
|
||||||
|
# skip-auth-route rules mirror the knoe.dev deployment — Kong key-auth is
|
||||||
|
# the enforcer on API paths; only the Studio UI at / is Google-gated.
|
||||||
|
#
|
||||||
|
# When knoe-auth Round 1 ships an OIDC OP, swap --provider=google to
|
||||||
|
# --provider=oidc --oidc-issuer-url=https://api.prole.org/auth.
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: oauth2-proxy
|
||||||
|
namespace: supabase
|
||||||
|
labels:
|
||||||
|
app: oauth2-proxy
|
||||||
|
app.kubernetes.io/managed-by: knoe-installer
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: oauth2-proxy
|
||||||
|
namespace: supabase
|
||||||
|
labels:
|
||||||
|
app: oauth2-proxy
|
||||||
|
app.kubernetes.io/managed-by: knoe-installer
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector:
|
||||||
|
app: oauth2-proxy
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 4180
|
||||||
|
protocol: TCP
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: oauth2-proxy
|
||||||
|
namespace: supabase
|
||||||
|
labels:
|
||||||
|
app: oauth2-proxy
|
||||||
|
app.kubernetes.io/managed-by: knoe-installer
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate:
|
||||||
|
maxSurge: 1
|
||||||
|
maxUnavailable: 0
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: oauth2-proxy
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: oauth2-proxy
|
||||||
|
spec:
|
||||||
|
serviceAccountName: oauth2-proxy
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
runAsUser: 65532
|
||||||
|
runAsGroup: 65532
|
||||||
|
seccompProfile:
|
||||||
|
type: RuntimeDefault
|
||||||
|
containers:
|
||||||
|
- name: oauth2-proxy
|
||||||
|
image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
args:
|
||||||
|
- --provider=google
|
||||||
|
- --email-domain=prole.org
|
||||||
|
- --upstream=http://supabase-kong:8000
|
||||||
|
- --http-address=0.0.0.0:4180
|
||||||
|
- --reverse-proxy=true
|
||||||
|
- --cookie-secure=true
|
||||||
|
- --cookie-name=_prole_studio_oauth2
|
||||||
|
- --cookie-domain=db.prole.org
|
||||||
|
- --whitelist-domain=db.prole.org
|
||||||
|
- --redirect-url=https://db.prole.org/oauth2/callback
|
||||||
|
- --skip-provider-button=true
|
||||||
|
- --scope=openid email profile
|
||||||
|
# Do NOT set --pass-authorization-header / --set-authorization-header.
|
||||||
|
# Those would clobber the Authorization: Bearer <apikey> that
|
||||||
|
# supabase-py and Studio send to Kong's key-auth plugin.
|
||||||
|
- --skip-auth-route=^/(auth|rest|realtime|storage|functions|graphql)/v1/.*
|
||||||
|
- --skip-auth-route=^/pg/.*
|
||||||
|
- --skip-auth-route=^/onboard\.html$
|
||||||
|
- --skip-auth-route=^/support$
|
||||||
|
- --request-logging=true
|
||||||
|
- --auth-logging=true
|
||||||
|
- --standard-logging=true
|
||||||
|
env:
|
||||||
|
- name: OAUTH2_PROXY_CLIENT_ID
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: oauth2-proxy-google-oidc
|
||||||
|
key: client_id
|
||||||
|
- name: OAUTH2_PROXY_CLIENT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: oauth2-proxy-google-oidc
|
||||||
|
key: client_secret
|
||||||
|
- name: OAUTH2_PROXY_COOKIE_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: oauth2-proxy-google-oidc
|
||||||
|
key: cookie_secret
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 4180
|
||||||
|
protocol: TCP
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /ping
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 3
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /ping
|
||||||
|
port: http
|
||||||
|
initialDelaySeconds: 5
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 3
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 64Mi
|
||||||
|
limits:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
capabilities:
|
||||||
|
drop: ["ALL"]
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
100
etc/init_grafana_oauth_prole.sh
Normal file
100
etc/init_grafana_oauth_prole.sh
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# init_grafana_oauth_prole.sh
|
||||||
|
#
|
||||||
|
# Bootstrap the Google-OAuth secret for Grafana on the prole.org k3s homelab
|
||||||
|
# cluster. Companion to the knoe.dev version (init_grafana_oauth.sh) but uses
|
||||||
|
# prole.org GCP project credentials and targets the k3s kubecontext.
|
||||||
|
#
|
||||||
|
# Creates the grafana-google-oidc Secret in the monitoring namespace, which
|
||||||
|
# kps-grafana mounts via envFromSecret to get GF_AUTH_GOOGLE_CLIENT_ID and
|
||||||
|
# GF_AUTH_GOOGLE_CLIENT_SECRET for its auth.google sign-in flow.
|
||||||
|
#
|
||||||
|
# Auth model (monitoring/kps-values-k3s.yaml):
|
||||||
|
# - Kerberos/knoe-auth users auto-login via auth.proxy (X-WEBAUTH-USER).
|
||||||
|
# - chrisfu@prole.org (and any future @prole.org Workspace user) signs in
|
||||||
|
# with the Google button on the Grafana login page.
|
||||||
|
# - chrisfu@prole.org → Admin; all other @prole.org users → Editor.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./etc/init_grafana_oauth_prole.sh
|
||||||
|
#
|
||||||
|
# Env vars (resolved from etc/secrets/* if not set in the shell):
|
||||||
|
# GRAFANA_GOOGLE_CLIENT_ID ← from etc/secrets/grafana-google-oidc-client-id-prole
|
||||||
|
# GRAFANA_GOOGLE_CLIENT_SECRET ← from etc/secrets/grafana-google-oidc-client-secret-prole
|
||||||
|
#
|
||||||
|
# Optional:
|
||||||
|
# K3S_KUBECONTEXT (default: $KUBECONTEXT then ambient)
|
||||||
|
# NAMESPACE (default: monitoring)
|
||||||
|
#
|
||||||
|
# Pre-reqs:
|
||||||
|
# - OAuth 2.0 Web Application client created in the prole.org GCP project:
|
||||||
|
# Authorized JS origins: https://svc.prole.org
|
||||||
|
# Authorized redirect URI: https://svc.prole.org/grafana/login/google
|
||||||
|
# Consent screen: Internal (prole.org Workspace)
|
||||||
|
# Scopes: openid, email, profile
|
||||||
|
# See deploy/gcp/gke/grafana-google-oidc-secret-prole.example.yaml for details.
|
||||||
|
# - Client ID and secret saved (chmod 0600) to:
|
||||||
|
# etc/secrets/grafana-google-oidc-client-id-prole
|
||||||
|
# etc/secrets/grafana-google-oidc-client-secret-prole
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
MANIFEST_DIR="$REPO_ROOT/deploy/gcp/gke"
|
||||||
|
|
||||||
|
NAMESPACE="${NAMESPACE:-monitoring}"
|
||||||
|
KCTX="${K3S_KUBECONTEXT:-${KUBECONTEXT:-}}"
|
||||||
|
|
||||||
|
if [[ -n "$KCTX" ]]; then
|
||||||
|
KCTX_FLAG=(--context="$KCTX")
|
||||||
|
else
|
||||||
|
KCTX_FLAG=()
|
||||||
|
fi
|
||||||
|
|
||||||
|
log() { printf "[%s] %s\n" "$(date +%H:%M:%S)" "$*"; }
|
||||||
|
die() { log "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
resolve_secret() {
|
||||||
|
local var="$1" file="$2" val="${!1:-}"
|
||||||
|
if [[ -z "$val" && -f "$REPO_ROOT/etc/secrets/$file" ]]; then
|
||||||
|
val="$(cat "$REPO_ROOT/etc/secrets/$file")"
|
||||||
|
fi
|
||||||
|
if [[ -z "$val" ]]; then
|
||||||
|
die "missing $var (set the env var, or save the value into etc/secrets/$file)"
|
||||||
|
fi
|
||||||
|
printf '%s' "$val"
|
||||||
|
}
|
||||||
|
|
||||||
|
for tool in kubectl envsubst; do
|
||||||
|
command -v "$tool" >/dev/null 2>&1 || die "required tool not found: $tool"
|
||||||
|
done
|
||||||
|
|
||||||
|
GRAFANA_GOOGLE_CLIENT_ID="$(resolve_secret GRAFANA_GOOGLE_CLIENT_ID grafana-google-oidc-client-id-prole)"
|
||||||
|
GRAFANA_GOOGLE_CLIENT_SECRET="$(resolve_secret GRAFANA_GOOGLE_CLIENT_SECRET grafana-google-oidc-client-secret-prole)"
|
||||||
|
export GRAFANA_GOOGLE_CLIENT_ID GRAFANA_GOOGLE_CLIENT_SECRET
|
||||||
|
|
||||||
|
SECRET_TMPL="$MANIFEST_DIR/grafana-google-oidc-secret-prole.example.yaml"
|
||||||
|
[[ -f "$SECRET_TMPL" ]] || die "missing manifest: $SECRET_TMPL"
|
||||||
|
|
||||||
|
log "==> grafana google-oauth bootstrap (prole.org / k3s)"
|
||||||
|
log " namespace : $NAMESPACE"
|
||||||
|
log " kubectx : ${KCTX:-<ambient>}"
|
||||||
|
|
||||||
|
log "Applying grafana-google-oidc Secret ..."
|
||||||
|
envsubst '${GRAFANA_GOOGLE_CLIENT_ID} ${GRAFANA_GOOGLE_CLIENT_SECRET}' \
|
||||||
|
< "$SECRET_TMPL" \
|
||||||
|
| kubectl "${KCTX_FLAG[@]}" -n "$NAMESPACE" apply -f -
|
||||||
|
|
||||||
|
log "==> grafana google-oauth secret applied."
|
||||||
|
echo ""
|
||||||
|
echo " Next steps:"
|
||||||
|
echo " 1. Helm upgrade kube-prometheus-stack with the k3s values:"
|
||||||
|
echo " helm upgrade --reuse-values kps prometheus-community/kube-prometheus-stack \\"
|
||||||
|
echo " --namespace $NAMESPACE \\"
|
||||||
|
echo " -f $REPO_ROOT/monitoring/kps-values-k3s.yaml"
|
||||||
|
echo " 2. Restart grafana-proxy to pick up the updated configmap:"
|
||||||
|
echo " kubectl rollout restart deployment/knoe-grafana-proxy -n $NAMESPACE"
|
||||||
|
echo " 3. Browser-test: https://svc.prole.org/grafana/login → Google sign-in button"
|
||||||
|
echo " present; Kerberos users still auto-login via X-WEBAUTH-USER."
|
||||||
|
echo ""
|
||||||
116
etc/init_oauth2_proxy_prole.sh
Normal file
116
etc/init_oauth2_proxy_prole.sh
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# init_oauth2_proxy_prole.sh
|
||||||
|
#
|
||||||
|
# Bootstrap the oauth2-proxy gate in front of Supabase Studio at db.prole.org
|
||||||
|
# on the k3s homelab cluster. Companion to init_oauth2_proxy.sh (knoe.dev GKE)
|
||||||
|
# but uses prole.org GCP project credentials and targets the k3s kubecontext.
|
||||||
|
#
|
||||||
|
# Gates access via Google Workspace OIDC (prole.org) so @prole.org identities
|
||||||
|
# can sign in to Studio. Outside-domain users are rejected at this layer.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./etc/init_oauth2_proxy_prole.sh
|
||||||
|
#
|
||||||
|
# Env vars (resolved from etc/secrets/* if not set in the shell):
|
||||||
|
# OAUTH2_PROXY_CLIENT_ID ← from etc/secrets/oauth2-proxy-client-id-prole
|
||||||
|
# OAUTH2_PROXY_CLIENT_SECRET ← from etc/secrets/oauth2-proxy-client-secret-prole
|
||||||
|
# OAUTH2_PROXY_COOKIE_SECRET ← from etc/secrets/oauth2-proxy-cookie-secret-prole
|
||||||
|
#
|
||||||
|
# Optional:
|
||||||
|
# K3S_KUBECONTEXT (default: $KUBECONTEXT then ambient)
|
||||||
|
# NAMESPACE (default: supabase)
|
||||||
|
#
|
||||||
|
# Pre-reqs:
|
||||||
|
# - OAuth 2.0 Web Application client created in the prole.org GCP project:
|
||||||
|
# Authorized JS origins: https://db.prole.org
|
||||||
|
# Authorized redirect URI: https://db.prole.org/oauth2/callback
|
||||||
|
# Consent screen: Internal (prole.org Workspace)
|
||||||
|
# Scopes: openid, email, profile
|
||||||
|
# See deploy/gcp/gke/oauth2-proxy-google-oidc-secret-prole.example.yaml.
|
||||||
|
# - Cookie secret generated with: openssl rand -base64 32
|
||||||
|
# - Three values saved (chmod 0600) to:
|
||||||
|
# etc/secrets/oauth2-proxy-client-id-prole
|
||||||
|
# etc/secrets/oauth2-proxy-client-secret-prole
|
||||||
|
# etc/secrets/oauth2-proxy-cookie-secret-prole
|
||||||
|
#
|
||||||
|
# After this script runs, patch the db.prole.org Ingress/IngressRoute to route
|
||||||
|
# through oauth2-proxy:80 instead of supabase-kong:8000 directly (see next
|
||||||
|
# steps printed at the end of this script).
|
||||||
|
#
|
||||||
|
# When knoe-auth Round 1 ships an OIDC OP at https://api.prole.org/auth, change
|
||||||
|
# --provider=google to --provider=oidc --oidc-issuer-url=https://api.prole.org/auth
|
||||||
|
# in deploy/opentofu/k3s/manifests/knoe/oauth2-proxy-deployment-prole.yaml and
|
||||||
|
# re-run this script.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
MANIFEST_DIR="$REPO_ROOT/deploy/gcp/gke"
|
||||||
|
K3S_MANIFEST_DIR="$REPO_ROOT/deploy/opentofu/k3s/manifests/knoe"
|
||||||
|
|
||||||
|
NAMESPACE="${NAMESPACE:-supabase}"
|
||||||
|
KCTX="${K3S_KUBECONTEXT:-${KUBECONTEXT:-}}"
|
||||||
|
|
||||||
|
if [[ -n "$KCTX" ]]; then
|
||||||
|
KCTX_FLAG=(--context="$KCTX")
|
||||||
|
else
|
||||||
|
KCTX_FLAG=()
|
||||||
|
fi
|
||||||
|
|
||||||
|
log() { printf "[%s] %s\n" "$(date +%H:%M:%S)" "$*"; }
|
||||||
|
die() { log "ERROR: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
resolve_secret() {
|
||||||
|
local var="$1" file="$2" val="${!1:-}"
|
||||||
|
if [[ -z "$val" && -f "$REPO_ROOT/etc/secrets/$file" ]]; then
|
||||||
|
val="$(cat "$REPO_ROOT/etc/secrets/$file")"
|
||||||
|
fi
|
||||||
|
if [[ -z "$val" ]]; then
|
||||||
|
die "missing $var (set the env var, or save the value into etc/secrets/$file)"
|
||||||
|
fi
|
||||||
|
printf '%s' "$val"
|
||||||
|
}
|
||||||
|
|
||||||
|
for tool in kubectl envsubst; do
|
||||||
|
command -v "$tool" >/dev/null 2>&1 || die "required tool not found: $tool"
|
||||||
|
done
|
||||||
|
|
||||||
|
OAUTH2_PROXY_CLIENT_ID="$(resolve_secret OAUTH2_PROXY_CLIENT_ID oauth2-proxy-client-id-prole)"
|
||||||
|
OAUTH2_PROXY_CLIENT_SECRET="$(resolve_secret OAUTH2_PROXY_CLIENT_SECRET oauth2-proxy-client-secret-prole)"
|
||||||
|
OAUTH2_PROXY_COOKIE_SECRET="$(resolve_secret OAUTH2_PROXY_COOKIE_SECRET oauth2-proxy-cookie-secret-prole)"
|
||||||
|
export OAUTH2_PROXY_CLIENT_ID OAUTH2_PROXY_CLIENT_SECRET OAUTH2_PROXY_COOKIE_SECRET
|
||||||
|
|
||||||
|
SECRET_TMPL="$MANIFEST_DIR/oauth2-proxy-google-oidc-secret-prole.example.yaml"
|
||||||
|
DEPLOY_MANIFEST="$K3S_MANIFEST_DIR/oauth2-proxy-deployment-prole.yaml"
|
||||||
|
|
||||||
|
[[ -f "$SECRET_TMPL" ]] || die "missing manifest: $SECRET_TMPL"
|
||||||
|
[[ -f "$DEPLOY_MANIFEST" ]] || die "missing manifest: $DEPLOY_MANIFEST"
|
||||||
|
|
||||||
|
log "==> oauth2-proxy bootstrap (prole.org / k3s)"
|
||||||
|
log " namespace : $NAMESPACE"
|
||||||
|
log " kubectx : ${KCTX:-<ambient>}"
|
||||||
|
|
||||||
|
log "Applying oauth2-proxy-google-oidc Secret ..."
|
||||||
|
envsubst '${OAUTH2_PROXY_CLIENT_ID} ${OAUTH2_PROXY_CLIENT_SECRET} ${OAUTH2_PROXY_COOKIE_SECRET}' \
|
||||||
|
< "$SECRET_TMPL" \
|
||||||
|
| kubectl "${KCTX_FLAG[@]}" -n "$NAMESPACE" apply -f -
|
||||||
|
|
||||||
|
log "Applying oauth2-proxy ServiceAccount + Service + Deployment ..."
|
||||||
|
kubectl "${KCTX_FLAG[@]}" -n "$NAMESPACE" apply -f "$DEPLOY_MANIFEST"
|
||||||
|
|
||||||
|
log "Waiting for oauth2-proxy Deployment to become Ready (timeout 180s) ..."
|
||||||
|
kubectl "${KCTX_FLAG[@]}" -n "$NAMESPACE" rollout status deployment/oauth2-proxy --timeout=180s
|
||||||
|
|
||||||
|
log "==> oauth2-proxy bootstrap complete."
|
||||||
|
echo ""
|
||||||
|
echo " Next steps (NOT performed by this script):"
|
||||||
|
echo " 1. Patch the db.prole.org Ingress/IngressRoute so traffic routes"
|
||||||
|
echo " through oauth2-proxy:80 instead of supabase-kong:8000 directly."
|
||||||
|
echo " Check current routing:"
|
||||||
|
echo " kubectl -n supabase get ingress,ingressroute"
|
||||||
|
echo " 2. Remove or disable any basic-auth plugin on the Studio route in"
|
||||||
|
echo " the supabase-kong configmap; rollout-restart supabase-kong."
|
||||||
|
echo " 3. Browser-test: https://db.prole.org/ → Google sign-in (prole.org"
|
||||||
|
echo " account). Verify a non-prole.org account receives 403."
|
||||||
|
echo ""
|
||||||
146
monitoring/kps-values-k3s.yaml
Normal file
146
monitoring/kps-values-k3s.yaml
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
# kube-prometheus-stack (kps) Helm values — k3s deploy mode (prole.org homelab).
|
||||||
|
#
|
||||||
|
# Apply with:
|
||||||
|
# helm upgrade kps prometheus-community/kube-prometheus-stack \
|
||||||
|
# --namespace monitoring \
|
||||||
|
# -f monitoring/kps-values-k3s.yaml
|
||||||
|
#
|
||||||
|
# Auth model:
|
||||||
|
# - Kerberos/knoe-auth users arrive via grafana-proxy nginx with X-WEBAUTH-USER
|
||||||
|
# header → Grafana auth.proxy auto-login (no password prompt).
|
||||||
|
# - Google Workspace users (chrisfu@prole.org etc.) use auth.google sign-in
|
||||||
|
# button on the Grafana login page. The grafana-proxy nginx passes login
|
||||||
|
# paths through unauthenticated so the OAuth flow can complete.
|
||||||
|
# - chrisfu@prole.org → Admin; all other @prole.org users → Editor.
|
||||||
|
# - Local break-glass login (adminPassword) remains available.
|
||||||
|
#
|
||||||
|
# Storage: uses local-path (k3s Rancher default). Override storageClassName if
|
||||||
|
# the homelab has a different provisioner (NFS, Longhorn, etc.).
|
||||||
|
---
|
||||||
|
alertmanager:
|
||||||
|
alertmanagerSpec:
|
||||||
|
nodeSelector:
|
||||||
|
prole.org/node-role: general
|
||||||
|
storage:
|
||||||
|
volumeClaimTemplate:
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 5Gi
|
||||||
|
storageClassName: local-path
|
||||||
|
|
||||||
|
grafana:
|
||||||
|
enabled: true
|
||||||
|
# adminPassword is the local break-glass; primary auth is Google OAuth +
|
||||||
|
# knoe-auth proxy (below). Stored in the prole.org vault.
|
||||||
|
adminPassword: admin
|
||||||
|
|
||||||
|
nodeSelector:
|
||||||
|
prole.org/node-role: general
|
||||||
|
|
||||||
|
initChownData:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
enabled: true
|
||||||
|
size: 10Gi
|
||||||
|
storageClassName: local-path
|
||||||
|
type: sts
|
||||||
|
|
||||||
|
service:
|
||||||
|
port: 80
|
||||||
|
targetPort: 3000
|
||||||
|
|
||||||
|
sidecar:
|
||||||
|
dashboards:
|
||||||
|
enabled: true
|
||||||
|
label: grafana_dashboard
|
||||||
|
labelValue: "1"
|
||||||
|
datasources:
|
||||||
|
enabled: true
|
||||||
|
label: grafana_datasource
|
||||||
|
labelValue: "1"
|
||||||
|
|
||||||
|
# Mount Google OAuth client credentials from the grafana-google-oidc Secret.
|
||||||
|
# Created by etc/init_grafana_oauth_prole.sh from
|
||||||
|
# etc/secrets/grafana-google-oidc-client-{id,secret}-prole.
|
||||||
|
# Provides GF_AUTH_GOOGLE_CLIENT_ID and GF_AUTH_GOOGLE_CLIENT_SECRET env vars.
|
||||||
|
envFromSecret: grafana-google-oidc
|
||||||
|
|
||||||
|
grafana.ini:
|
||||||
|
server:
|
||||||
|
domain: svc.prole.org
|
||||||
|
root_url: "https://svc.prole.org/grafana"
|
||||||
|
serve_from_sub_path: true
|
||||||
|
security:
|
||||||
|
cookie_secure: true
|
||||||
|
cookie_samesite: lax
|
||||||
|
csrf_trusted_origins: svc.prole.org
|
||||||
|
csrf_additional_headers: X-Forwarded-Host
|
||||||
|
"live":
|
||||||
|
allowed_origins: "https://svc.prole.org"
|
||||||
|
auth:
|
||||||
|
disable_login_form: false
|
||||||
|
token_rotation_interval_minutes: 1440
|
||||||
|
# Proxy auth: Kerberos/knoe-auth users arriving via grafana-proxy nginx.
|
||||||
|
# The nginx proxy injects X-WEBAUTH-USER after verifying with knoe-auth;
|
||||||
|
# Grafana trusts this header and auto-logs the user in without a password.
|
||||||
|
"auth.proxy":
|
||||||
|
enabled: true
|
||||||
|
header_name: X-WEBAUTH-USER
|
||||||
|
header_property: username
|
||||||
|
auto_sign_up: true
|
||||||
|
# Google OAuth: prole.org Workspace users sign in via the Google button on
|
||||||
|
# the Grafana login page. Requires grafana-proxy nginx to pass /grafana/login
|
||||||
|
# endpoints through unauthenticated (see grafana-proxy-configmap.yaml).
|
||||||
|
"auth.google":
|
||||||
|
enabled: true
|
||||||
|
allowed_domains: prole.org
|
||||||
|
scopes: "openid email profile"
|
||||||
|
auth_url: https://accounts.google.com/o/oauth2/v2/auth
|
||||||
|
token_url: https://oauth2.googleapis.com/token
|
||||||
|
api_url: https://openidconnect.googleapis.com/v1/userinfo
|
||||||
|
# JMESPath: chrisfu gets Admin; every other @prole.org user gets Editor.
|
||||||
|
role_attribute_path: "contains(['chrisfu@prole.org'], email) && 'Admin' || 'Editor'"
|
||||||
|
skip_org_role_sync: false
|
||||||
|
users:
|
||||||
|
auto_assign_org_role: Editor
|
||||||
|
|
||||||
|
kube-state-metrics:
|
||||||
|
nodeSelector:
|
||||||
|
prole.org/node-role: general
|
||||||
|
|
||||||
|
prometheus:
|
||||||
|
prometheusSpec:
|
||||||
|
nodeSelector:
|
||||||
|
prole.org/node-role: general
|
||||||
|
storageSpec:
|
||||||
|
volumeClaimTemplate:
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 30Gi
|
||||||
|
storageClassName: local-path
|
||||||
|
|
||||||
|
prometheus-node-exporter:
|
||||||
|
affinity:
|
||||||
|
nodeAffinity:
|
||||||
|
requiredDuringSchedulingIgnoredDuringExecution:
|
||||||
|
nodeSelectorTerms:
|
||||||
|
- matchExpressions:
|
||||||
|
- key: kubernetes.io/hostname
|
||||||
|
operator: NotIn
|
||||||
|
values:
|
||||||
|
- pi.prole.org
|
||||||
|
nodeSelector:
|
||||||
|
prole.org/node-role: general
|
||||||
|
|
||||||
|
prometheusOperator:
|
||||||
|
nodeSelector:
|
||||||
|
prole.org/node-role: general
|
||||||
Loading…
Reference in New Issue
Block a user