feat(oauth2-proxy) add oauth2-proxy module
This commit is contained in:
130
oauth2-proxy/justfile
Normal file
130
oauth2-proxy/justfile
Normal file
@@ -0,0 +1,130 @@
|
||||
set fallback := true
|
||||
|
||||
export OAUTH2_PROXY_NAMESPACE := env("OAUTH2_PROXY_NAMESPACE", "default")
|
||||
export KEYCLOAK_HOST := env("KEYCLOAK_HOST", "")
|
||||
export KEYCLOAK_REALM := env("KEYCLOAK_REALM", "buunstack")
|
||||
export OAUTH2_PROXY_HOST := env("OAUTH2_PROXY_HOST", "")
|
||||
export EXTERNAL_SECRETS_NAMESPACE := env("EXTERNAL_SECRETS_NAMESPACE", "external-secrets")
|
||||
|
||||
[private]
|
||||
default:
|
||||
@just --list --unsorted --list-submodules
|
||||
|
||||
# Setup OAuth2 Proxy for an application
|
||||
setup-for-app app_name app_host app_namespace="default" upstream_service="":
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Setting up OAuth2 Proxy for {{ app_name }}"
|
||||
|
||||
# Create Keycloak client
|
||||
echo "Creating Keycloak client for {{ app_name }}..."
|
||||
client_id="{{ app_name }}-oauth2-proxy"
|
||||
redirect_url="https://{{ app_host }}/oauth2/callback"
|
||||
|
||||
# Generate client secret for confidential client
|
||||
client_secret=$(just utils::random-password 32)
|
||||
|
||||
if ! just keycloak::create-client "${KEYCLOAK_REALM}" "${client_id}" "${redirect_url}" "${client_secret}"; then
|
||||
echo "Failed to create Keycloak client"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Add audience mapper to Keycloak client
|
||||
echo "Adding audience mapper to Keycloak client..."
|
||||
just keycloak::add-audience-mapper "${client_id}"
|
||||
|
||||
# Generate cookie secret
|
||||
cookie_secret=$(just utils::random-password 32)
|
||||
|
||||
# Create namespace if it doesn't exist
|
||||
kubectl get namespace {{ app_namespace }} &>/dev/null || \
|
||||
kubectl create namespace {{ app_namespace }}
|
||||
|
||||
# Store secrets
|
||||
if helm status external-secrets -n ${EXTERNAL_SECRETS_NAMESPACE} &>/dev/null; then
|
||||
echo "External Secrets Operator detected. Storing credentials in Vault..."
|
||||
|
||||
just vault::put "oauth2-proxy/{{ app_name }}" \
|
||||
client_id="${client_id}" \
|
||||
client_secret="${client_secret}" \
|
||||
cookie_secret="${cookie_secret}"
|
||||
|
||||
# Create ExternalSecret
|
||||
export APP_NAME="{{ app_name }}"
|
||||
export APP_HOST="{{ app_host }}"
|
||||
export APP_NAMESPACE="{{ app_namespace }}"
|
||||
gomplate -f oauth2-proxy-external-secret.gomplate.yaml | kubectl apply -f -
|
||||
|
||||
echo "Waiting for ExternalSecret to sync..."
|
||||
kubectl wait --for=condition=Ready externalsecret/oauth2-proxy-{{ app_name }}-config \
|
||||
-n {{ app_namespace }} --timeout=60s
|
||||
else
|
||||
echo "Creating Kubernetes secret directly..."
|
||||
kubectl create secret generic oauth2-proxy-{{ app_name }}-config \
|
||||
-n {{ app_namespace }} \
|
||||
--from-literal=client_id="${client_id}" \
|
||||
--from-literal=client_secret="${client_secret}" \
|
||||
--from-literal=cookie_secret="${cookie_secret}" \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
fi
|
||||
|
||||
# Set upstream service (default to static response if not provided)
|
||||
if [ -z "{{ upstream_service }}" ]; then
|
||||
upstream_service="static://202"
|
||||
else
|
||||
upstream_service="{{ upstream_service }}"
|
||||
fi
|
||||
|
||||
# Deploy OAuth2 Proxy
|
||||
export APP_NAME="{{ app_name }}"
|
||||
export APP_HOST="{{ app_host }}"
|
||||
export APP_NAMESPACE="{{ app_namespace }}"
|
||||
export UPSTREAM_SERVICE="${upstream_service}"
|
||||
|
||||
gomplate -f oauth2-proxy-deployment.gomplate.yaml | kubectl apply -f -
|
||||
gomplate -f oauth2-proxy-service.gomplate.yaml | kubectl apply -f -
|
||||
gomplate -f oauth2-proxy-ingressroute.gomplate.yaml | kubectl apply -f -
|
||||
|
||||
echo "Waiting for OAuth2 Proxy to be ready..."
|
||||
kubectl wait --for=condition=Available deployment/oauth2-proxy-{{ app_name }} \
|
||||
-n {{ app_namespace }} --timeout=120s
|
||||
|
||||
echo "OAuth2 Proxy setup completed for {{ app_name }}"
|
||||
echo "Access URL: https://{{ app_host }}/oauth2/sign_in"
|
||||
|
||||
# Remove OAuth2 Proxy for an application
|
||||
remove-for-app app_name app_namespace="default":
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Removing OAuth2 Proxy for {{ app_name }}"
|
||||
|
||||
# Delete Kubernetes resources
|
||||
kubectl delete ingressroute oauth2-proxy-{{ app_name }} -n {{ app_namespace }} --ignore-not-found
|
||||
kubectl delete service oauth2-proxy-{{ app_name }} -n {{ app_namespace }} --ignore-not-found
|
||||
kubectl delete deployment oauth2-proxy-{{ app_name }} -n {{ app_namespace }} --ignore-not-found
|
||||
kubectl delete configmap oauth2-proxy-{{ app_name }}-config -n {{ app_namespace }} --ignore-not-found
|
||||
kubectl delete secret oauth2-proxy-{{ app_name }}-config -n {{ app_namespace }} --ignore-not-found
|
||||
kubectl delete externalsecret oauth2-proxy-{{ app_name }}-config -n {{ app_namespace }} --ignore-not-found
|
||||
|
||||
# Remove Vault secrets if External Secrets is available
|
||||
if helm status external-secrets -n ${EXTERNAL_SECRETS_NAMESPACE} &>/dev/null; then
|
||||
just vault::delete "oauth2-proxy/{{ app_name }}"
|
||||
fi
|
||||
|
||||
# Delete Keycloak client
|
||||
client_id="{{ app_name }}-oauth2-proxy"
|
||||
just keycloak::delete-client "${KEYCLOAK_REALM}" "${client_id}"
|
||||
|
||||
echo "OAuth2 Proxy removed for {{ app_name }}"
|
||||
|
||||
# List OAuth2 Proxy deployments
|
||||
list:
|
||||
@echo "OAuth2 Proxy deployments:"
|
||||
@kubectl get deployments -A -l app.kubernetes.io/component=oauth2-proxy
|
||||
|
||||
# Show OAuth2 Proxy status for an application
|
||||
status app_name app_namespace="default":
|
||||
@echo "OAuth2 Proxy status for {{ app_name }}:"
|
||||
@kubectl get deployment,service,ingressroute -n {{ app_namespace }} -l app={{ app_name }}-oauth2-proxy
|
||||
78
oauth2-proxy/oauth2-proxy-deployment.gomplate.yaml
Normal file
78
oauth2-proxy/oauth2-proxy-deployment.gomplate.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}-config
|
||||
namespace: {{ .Env.APP_NAMESPACE }}
|
||||
data:
|
||||
config.cfg: |
|
||||
http_address = "0.0.0.0:4180"
|
||||
provider = "keycloak-oidc"
|
||||
oidc_issuer_url = "https://{{ .Env.KEYCLOAK_HOST }}/realms/{{ .Env.KEYCLOAK_REALM }}"
|
||||
redirect_url = "https://{{ .Env.APP_HOST }}/oauth2/callback"
|
||||
email_domains = "*"
|
||||
reverse_proxy = true
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}
|
||||
namespace: {{ .Env.APP_NAMESPACE }}
|
||||
labels:
|
||||
app: {{ .Env.APP_NAME }}-oauth2-proxy
|
||||
app.kubernetes.io/component: oauth2-proxy
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Env.APP_NAME }}-oauth2-proxy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Env.APP_NAME }}-oauth2-proxy
|
||||
app.kubernetes.io/component: oauth2-proxy
|
||||
spec:
|
||||
containers:
|
||||
- name: oauth2-proxy
|
||||
image: quay.io/oauth2-proxy/oauth2-proxy:v7.6.0
|
||||
args:
|
||||
- --config=/etc/oauth2-proxy/config.cfg
|
||||
- --upstream=http://{{ .Env.UPSTREAM_SERVICE }}
|
||||
env:
|
||||
- name: OAUTH2_PROXY_CLIENT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}-config
|
||||
key: client_id
|
||||
- name: OAUTH2_PROXY_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}-config
|
||||
key: client_secret
|
||||
- name: OAUTH2_PROXY_COOKIE_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}-config
|
||||
key: cookie_secret
|
||||
ports:
|
||||
- containerPort: 4180
|
||||
name: http
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/oauth2-proxy/
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 4180
|
||||
initialDelaySeconds: 3
|
||||
timeoutSeconds: 1
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: 4180
|
||||
initialDelaySeconds: 3
|
||||
timeoutSeconds: 1
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}-config
|
||||
32
oauth2-proxy/oauth2-proxy-external-secret.gomplate.yaml
Normal file
32
oauth2-proxy/oauth2-proxy-external-secret.gomplate.yaml
Normal file
@@ -0,0 +1,32 @@
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}-config
|
||||
namespace: {{ .Env.APP_NAMESPACE }}
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
name: vault-secret-store
|
||||
kind: ClusterSecretStore
|
||||
target:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}-config
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
type: Opaque
|
||||
data:
|
||||
client_id: "{{ `{{ .client_id }}` }}"
|
||||
client_secret: "{{ `{{ .client_secret }}` }}"
|
||||
cookie_secret: "{{ `{{ .cookie_secret }}` }}"
|
||||
data:
|
||||
- secretKey: client_id
|
||||
remoteRef:
|
||||
key: oauth2-proxy/{{ .Env.APP_NAME }}
|
||||
property: client_id
|
||||
- secretKey: client_secret
|
||||
remoteRef:
|
||||
key: oauth2-proxy/{{ .Env.APP_NAME }}
|
||||
property: client_secret
|
||||
- secretKey: cookie_secret
|
||||
remoteRef:
|
||||
key: oauth2-proxy/{{ .Env.APP_NAME }}
|
||||
property: cookie_secret
|
||||
36
oauth2-proxy/oauth2-proxy-ingressroute.gomplate.yaml
Normal file
36
oauth2-proxy/oauth2-proxy-ingressroute.gomplate.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: {{ .Env.APP_NAME }}-auth-headers
|
||||
namespace: {{ .Env.APP_NAMESPACE }}
|
||||
spec:
|
||||
headers:
|
||||
sslRedirect: true
|
||||
stsSeconds: 315360000
|
||||
browserXssFilter: true
|
||||
contentTypeNosniff: true
|
||||
forceSTSHeader: true
|
||||
sslHost: {{ .Env.APP_HOST }}
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
frameDeny: true
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}
|
||||
namespace: {{ .Env.APP_NAMESPACE }}
|
||||
labels:
|
||||
app: {{ .Env.APP_NAME }}-oauth2-proxy
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: "Host(`{{ .Env.APP_HOST }}`)"
|
||||
kind: Rule
|
||||
services:
|
||||
- name: oauth2-proxy-{{ .Env.APP_NAME }}
|
||||
port: 80
|
||||
middlewares:
|
||||
- name: {{ .Env.APP_NAME }}-auth-headers
|
||||
15
oauth2-proxy/oauth2-proxy-service.gomplate.yaml
Normal file
15
oauth2-proxy/oauth2-proxy-service.gomplate.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: oauth2-proxy-{{ .Env.APP_NAME }}
|
||||
namespace: {{ .Env.APP_NAMESPACE }}
|
||||
labels:
|
||||
app: {{ .Env.APP_NAME }}-oauth2-proxy
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 4180
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: {{ .Env.APP_NAME }}-oauth2-proxy
|
||||
Reference in New Issue
Block a user