Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Changed

- Unified scripts and removed Makefile, combined all into one CLI command `eoapi-cli` [#359](https://github.com/developmentseed/eoapi-k8s/pull/359)
- Added stac-auth-proxy for authentication and authorization on the STAC API [#358](https://github.com/developmentseed/eoapi-k8s/pull/358)

## [0.8.0] - 2025-11-20

Expand Down
4 changes: 4 additions & 0 deletions charts/eoapi/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,7 @@ dependencies:
version: 10.1.5
repository: https://grafana.github.io/helm-charts
condition: observability.grafana.enabled
- name: stac-auth-proxy
version: "0.1.0"
repository: "oci://ghcr.io/developmentseed/stac-auth-proxy/charts"
condition: stac-auth-proxy.enabled
16 changes: 16 additions & 0 deletions charts/eoapi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ A Helm chart for deploying Earth Observation APIs with integrated STAC, raster,
## Features

- STAC API for metadata discovery and search
- STAC Auth Proxy for authentication/authorization (optional)
- Raster tile services (TiTiler)
- Vector tile services (TIPG)
- Multidimensional data support
Expand Down Expand Up @@ -42,6 +43,21 @@ helm install eoapi eoapi/eoapi -f profiles/experimental.yaml
- PV provisioner support
- PostgreSQL operator

## STAC Auth Proxy (Optional)

The chart includes support for [stac-auth-proxy](https://github.com/developmentseed/stac-auth-proxy) to add authentication and authorization to your STAC API. This feature is disabled by default and can be enabled, and will need a valid OIDC discovery URL.

### Configuration

```yaml
stac-auth-proxy:
enabled: true
env:
OIDC_DISCOVERY_URL: "https://your-auth-server/.well-known/openid-configuration"
```

When enabled, the ingress will automatically route STAC API requests through the auth proxy instead of directly to the STAC service.

## Quick Start with Profiles

Use pre-configured profiles for common deployment scenarios:
Expand Down
50 changes: 50 additions & 0 deletions charts/eoapi/profiles/experimental.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,56 @@ ingress:
tls:
enabled: false

stac-auth-proxy:
enabled: true
service:
port: 8080
# Wait for dependencies to be ready before starting stac-auth-proxy
initContainers:
- name: wait-for-mock-oidc
image: busybox:1.35
command: ['sh', '-c', 'until nc -z eoapi-mock-oidc-server.eoapi.svc.cluster.local 8080; do echo waiting for mock-oidc; sleep 2; done']
- name: wait-for-stac
image: busybox:1.35
command: ['sh', '-c', 'until nc -z eoapi-stac.eoapi.svc.cluster.local 8080; do echo waiting for stac service; sleep 2; done']
env:
UPSTREAM_URL: "http://eoapi-stac:8080"
# For testing one could deploy a mock OIDC server (https://github.com/alukach/mock-oidc-server)
OIDC_DISCOVERY_URL: "http://eoapi-mock-oidc-server.eoapi.svc.cluster.local:8080/.well-known/openid-configuration"
# For production, point to your actual OpenID Connect provider
# OIDC_DISCOVERY_URL: "https://your-auth-provider.com/.well-known/openid-configuration"

######################
# MOCK OIDC SERVER
######################
# Mock OIDC server for testing authentication
# WARNING: Only for development/testing, never use in production!
mockOidcServer:
enabled: true
replicaCount: 1
image:
repository: ghcr.io/alukach/mock-oidc-server
tag: latest
pullPolicy: IfNotPresent
port: 8888
clientId: "test-client"
clientSecret: "test-secret"
service:
type: ClusterIP
port: 8080
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
nodeSelector: {}
tolerations: []
affinity: {}
imagePullSecrets: []
extraEnv: []

######################
# SERVICE
######################
Expand Down
12 changes: 12 additions & 0 deletions charts/eoapi/templates/_helpers/validation.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,15 @@ so we use this helper function to check autoscaling rules
{{- end }}
{{- end }}
{{- end -}}

{{/*
Validate stac-auth-proxy configuration
Ensures OIDC_DISCOVERY_URL is set when stac-auth-proxy is enabled
*/}}
{{- define "eoapi.validateStacAuthProxy" -}}
{{- if index .Values "stac-auth-proxy" "enabled" }}
{{- if not (index .Values "stac-auth-proxy" "env" "OIDC_DISCOVERY_URL") }}
{{- fail "stac-auth-proxy.env.OIDC_DISCOVERY_URL is required when stac-auth-proxy is enabled. Set it to your OpenID Connect discovery URL (e.g., https://your-auth-server/.well-known/openid-configuration)" }}
{{- end }}
{{- end }}
{{- end -}}
34 changes: 34 additions & 0 deletions charts/eoapi/templates/core/stac-auth-proxy-patch-rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{{- if index .Values "stac-auth-proxy" "enabled" }}
{{- if index .Values "stac-auth-proxy" "initContainers" }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: {{ .Release.Name }}-stac-auth-proxy-patch
namespace: {{ .Release.Namespace }}
labels:
{{- include "eoapi.labels" . | nindent 4 }}
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "patch"]
resourceNames:
- {{ .Release.Name }}-stac-auth-proxy
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ .Release.Name }}-stac-auth-proxy-patch
namespace: {{ .Release.Namespace }}
labels:
{{- include "eoapi.labels" . | nindent 4 }}
subjects:
- kind: ServiceAccount
name: {{ include "eoapi.serviceAccountName" . }}
namespace: {{ .Release.Namespace }}
roleRef:
kind: Role
name: {{ .Release.Name }}-stac-auth-proxy-patch
apiGroup: rbac.authorization.k8s.io
{{- end }}
{{- end }}
56 changes: 56 additions & 0 deletions charts/eoapi/templates/core/stac-auth-proxy-patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{{- if index .Values "stac-auth-proxy" "enabled" }}
{{- if index .Values "stac-auth-proxy" "initContainers" }}
# NOTE: This hook patches the stac-auth-proxy deployment to add initContainers.
# Since Helm hooks run AFTER all resources (including subcharts) are installed,
# the deployment will start once without initContainers, then be patched and rolled out.
# This is a temporary workaround until upstream stac-auth-proxy chart adds native initContainers support.
apiVersion: batch/v1
kind: Job
metadata:
name: {{ .Release.Name }}-stac-auth-proxy-patch
namespace: {{ .Release.Namespace }}
labels:
{{- include "eoapi.labels" . | nindent 4 }}
annotations:
helm.sh/hook: "post-install,post-upgrade"
helm.sh/hook-weight: "5"
helm.sh/hook-delete-policy: "before-hook-creation,hook-succeeded"
spec:
template:
metadata:
labels:
{{- include "eoapi.labels" . | nindent 8 }}
spec:
restartPolicy: Never
serviceAccountName: {{ include "eoapi.serviceAccountName" . }}
containers:
- name: patch-deployment
image: bitnami/kubectl:latest
imagePullPolicy: IfNotPresent
# Use python3 for JSON manipulation (bitnami/kubectl includes python3)
command:
- /bin/bash
- -c
- |
set -e
DEPLOYMENT_NAME="{{ .Release.Name }}-stac-auth-proxy"
NAMESPACE="{{ .Release.Namespace }}"
echo "Waiting for deployment $DEPLOYMENT_NAME..."
for i in {1..30}; do
kubectl get deployment "$DEPLOYMENT_NAME" -n "$NAMESPACE" &>/dev/null && break
sleep 2
done
EXISTING_INIT=$(kubectl get deployment "$DEPLOYMENT_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.template.spec.initContainers}' 2>/dev/null || echo "")
if [ -n "$EXISTING_INIT" ] && [ "$EXISTING_INIT" != "null" ]; then
echo "Deployment already has initContainers, skipping"
exit 0
fi
echo "Patching deployment with initContainers..."
# Use Helm templating to replace service names in the JSON string
INIT_CONTAINERS_JSON='{{ index .Values "stac-auth-proxy" "initContainers" | toJson | replace "eoapi-stac.eoapi" (printf "%s-stac.%s" .Release.Name .Release.Namespace) | replace "eoapi-mock-oidc-server.eoapi" (printf "%s-mock-oidc-server.%s" .Release.Name .Release.Namespace) }}'
kubectl patch deployment "$DEPLOYMENT_NAME" -n "$NAMESPACE" --type='json' -p="[{\"op\":\"add\",\"path\":\"/spec/template/spec/initContainers\",\"value\":$INIT_CONTAINERS_JSON}]"
echo "Waiting for rollout..."
kubectl rollout status deployment/"$DEPLOYMENT_NAME" -n "$NAMESPACE" --timeout=300s
backoffLimit: 2
{{- end }}
{{- end }}
3 changes: 2 additions & 1 deletion charts/eoapi/templates/core/validation.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{{/*
This template validates the PostgreSQL configuration.
This template validates various configurations.
It doesn't create any resources but ensures configuration consistency.
*/}}
{{- include "eoapi.validatePostgresql" . }}
{{- include "eoapi.validateStacAuthProxy" . }}
72 changes: 72 additions & 0 deletions charts/eoapi/templates/mock-oidc/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{{- if .Values.mockOidcServer.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "eoapi.fullname" . }}-mock-oidc-server
namespace: {{ .Release.Namespace }}
labels:
{{- include "eoapi.labels" . | nindent 4 }}
app.kubernetes.io/component: mock-oidc-server
spec:
replicas: {{ .Values.mockOidcServer.replicaCount | default 1 }}
selector:
matchLabels:
{{- include "eoapi.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: mock-oidc-server
template:
metadata:
labels:
{{- include "eoapi.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: mock-oidc-server
spec:
{{- if .Values.mockOidcServer.imagePullSecrets }}
imagePullSecrets:
{{- toYaml .Values.mockOidcServer.imagePullSecrets | nindent 8 }}
{{- end }}
containers:
- name: mock-oidc
image: "{{ if .Values.mockOidcServer.image }}{{ .Values.mockOidcServer.image.repository | default "ghcr.io/alukach/mock-oidc-server" }}:{{ .Values.mockOidcServer.image.tag | default "latest" }}{{ else }}ghcr.io/alukach/mock-oidc-server:latest{{ end }}"
imagePullPolicy: {{ if .Values.mockOidcServer.image }}{{ .Values.mockOidcServer.image.pullPolicy | default "IfNotPresent" }}{{ else }}IfNotPresent{{ end }}
env:
- name: MOCK_OIDC_PORT
value: "{{ .Values.mockOidcServer.port | default 8888 }}"
- name: MOCK_OIDC_CLIENT_ID
value: "{{ .Values.mockOidcServer.clientId | default "test-client" }}"
- name: MOCK_OIDC_CLIENT_SECRET
value: "{{ .Values.mockOidcServer.clientSecret | default "test-secret" }}"
{{- if .Values.mockOidcServer.extraEnv }}
{{- toYaml .Values.mockOidcServer.extraEnv | nindent 8 }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.mockOidcServer.port | default 8888 }}
protocol: TCP
livenessProbe:
httpGet:
path: /.well-known/openid-configuration
port: http
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /.well-known/openid-configuration
port: http
initialDelaySeconds: 5
periodSeconds: 5
{{- if .Values.mockOidcServer.resources }}
resources:
{{- toYaml .Values.mockOidcServer.resources | nindent 10 }}
{{- end }}
{{- if .Values.mockOidcServer.nodeSelector }}
nodeSelector:
{{- toYaml .Values.mockOidcServer.nodeSelector | nindent 8 }}
{{- end }}
{{- if .Values.mockOidcServer.affinity }}
affinity:
{{- toYaml .Values.mockOidcServer.affinity | nindent 8 }}
{{- end }}
{{- if .Values.mockOidcServer.tolerations }}
tolerations:
{{- toYaml .Values.mockOidcServer.tolerations | nindent 8 }}
{{- end }}
{{- end }}
20 changes: 20 additions & 0 deletions charts/eoapi/templates/mock-oidc/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{{- if .Values.mockOidcServer.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "eoapi.fullname" . }}-mock-oidc-server
namespace: {{ .Release.Namespace }}
labels:
{{- include "eoapi.labels" . | nindent 4 }}
app.kubernetes.io/component: mock-oidc-server
spec:
type: {{ if .Values.mockOidcServer.service }}{{ .Values.mockOidcServer.service.type | default "ClusterIP" }}{{ else }}ClusterIP{{ end }}
ports:
- port: {{ if .Values.mockOidcServer.service }}{{ .Values.mockOidcServer.service.port | default 8080 }}{{ else }}8080{{ end }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "eoapi.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: mock-oidc-server
{{- end }}
8 changes: 8 additions & 0 deletions charts/eoapi/templates/networking/ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ spec:
path: {{ $.Values.stac.ingress.path }}{{ if eq $.Values.ingress.className "nginx" }}(/|$)(.*){{ end }}
backend:
service:
{{- if index $.Values "stac-auth-proxy" "enabled" }}
name: {{ $.Release.Name }}-stac-auth-proxy
{{- else }}
name: {{ $.Release.Name }}-stac
{{- end }}
port:
number: {{ $.Values.service.port }}
{{- end }}
Expand Down Expand Up @@ -105,7 +109,11 @@ spec:
path: {{ .Values.stac.ingress.path }}{{ if eq .Values.ingress.className "nginx" }}(/|$)(.*){{ end }}
backend:
service:
{{- if index .Values "stac-auth-proxy" "enabled" }}
name: {{ .Release.Name }}-stac-auth-proxy
{{- else }}
name: {{ .Release.Name }}-stac
{{- end }}
port:
number: {{ .Values.service.port }}
{{- end }}
Expand Down
Loading
Loading