Skip to content
Merged
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 @@ -17,6 +17,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.2.0
repository: https://grafana.github.io/helm-charts
condition: observability.grafana.enabled
- name: stac-auth-proxy
version: "0.1.1"
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
42 changes: 42 additions & 0 deletions charts/eoapi/profiles/experimental.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,48 @@ ingress:
tls:
enabled: false

stac-auth-proxy:
enabled: true
service:
port: 8080
# For testing this will be set dynamically; for production, point to your OIDC server
# env:
# OIDC_DISCOVERY_URL: "http://eoapi-mock-oidc-server.eoapi.svc.cluster.local:8080/.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
ingress:
enabled: true
path: "/mock-oidc"
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
nodeSelector: {}
tolerations: []
affinity: {}
imagePullSecrets: []
extraEnv: []

######################
# SERVICE
######################
Expand Down
16 changes: 16 additions & 0 deletions charts/eoapi/templates/_helpers/validation.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,19 @@ 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
Ensures stac-auth-proxy cannot be enabled when stac is disabled
*/}}
{{- define "eoapi.validateStacAuthProxy" -}}
{{- if index .Values "stac-auth-proxy" "enabled" }}
{{- if not .Values.stac.enabled }}
{{- fail "stac-auth-proxy cannot be enabled when stac.enabled is false. Enable stac first or disable stac-auth-proxy." }}
{{- end }}
{{- 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 -}}
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" . }}
84 changes: 84 additions & 0 deletions charts/eoapi/templates/mock-oidc/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{{- 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
startupProbe:
httpGet:
path: /.well-known/openid-configuration
port: http
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 12
timeoutSeconds: 3
livenessProbe:
httpGet:
path: /.well-known/openid-configuration
port: http
initialDelaySeconds: 30
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /.well-known/openid-configuration
port: http
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
timeoutSeconds: 3
{{- 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 }}
28 changes: 28 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 All @@ -74,6 +78,16 @@ spec:
number: {{ $.Values.service.port }}
{{- end }}

{{- if and $.Values.mockOidcServer.enabled $.Values.mockOidcServer.ingress.enabled }}
- pathType: {{ if eq $.Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }}
path: {{ $.Values.mockOidcServer.ingress.path }}{{ if eq $.Values.ingress.className "nginx" }}(/|$)(.*){{ end }}
backend:
service:
name: {{ $.Release.Name }}-mock-oidc-server
port:
number: {{ $.Values.mockOidcServer.service.port | default 8080 }}
{{- end }}

{{- if $.Values.docServer.enabled }}
- pathType: Prefix
path: "/{{ $.Values.ingress.rootPath | default "" }}"
Expand Down Expand Up @@ -105,7 +119,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 All @@ -130,6 +148,16 @@ spec:
number: {{ .Values.service.port }}
{{- end }}

{{- if and .Values.mockOidcServer.enabled .Values.mockOidcServer.ingress.enabled }}
- pathType: {{ if eq .Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }}
path: {{ .Values.mockOidcServer.ingress.path }}{{ if eq .Values.ingress.className "nginx" }}(/|$)(.*){{ end }}
backend:
service:
name: {{ .Release.Name }}-mock-oidc-server
port:
number: {{ .Values.mockOidcServer.service.port | default 8080 }}
{{- end }}

{{- if .Values.docServer.enabled }}
- pathType: Prefix
path: "/{{ $.Values.ingress.rootPath | default "" }}"
Expand Down
3 changes: 3 additions & 0 deletions charts/eoapi/templates/networking/traefik-middleware.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ spec:
{{- if .Values.multidim.enabled }}
- {{ .Values.multidim.ingress.path }}
{{- end }}
{{- if and .Values.mockOidcServer.enabled .Values.mockOidcServer.ingress.enabled }}
- {{ .Values.mockOidcServer.ingress.path }}
{{- end }}
{{- end }}
79 changes: 79 additions & 0 deletions charts/eoapi/tests/stac-auth-proxy-ingress_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
suite: test stac-auth-proxy ingress routing
templates:
- networking/ingress.yaml

tests:
- it: should route ingress to stac-auth-proxy when enabled
set:
ingress.enabled: true
ingress.className: nginx
stac.enabled: true
stac.ingress.enabled: true
stac.ingress.path: "/stac"
stac-auth-proxy.enabled: true
service.port: 8080
asserts:
- contains:
path: spec.rules[0].http.paths
content:
pathType: ImplementationSpecific
path: /stac(/|$)(.*)
backend:
service:
name: RELEASE-NAME-stac-auth-proxy
port:
number: 8080
template: networking/ingress.yaml

- it: should route ingress directly to stac when auth-proxy is disabled
set:
ingress.enabled: true
ingress.className: nginx
stac.enabled: true
stac.ingress.enabled: true
stac.ingress.path: "/stac"
stac-auth-proxy.enabled: false
service.port: 8080
asserts:
- contains:
path: spec.rules[0].http.paths
content:
pathType: ImplementationSpecific
path: /stac(/|$)(.*)
backend:
service:
name: RELEASE-NAME-stac
port:
number: 8080
template: networking/ingress.yaml

- it: should not create stac routes when stac is disabled
set:
ingress.enabled: true
stac.enabled: false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ever valid to have stac disabled but stac-auth-proxy enabled? Should that rather be a validation check that stac-auth-proxy cannot be enabled is stac.enabled is set to false ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, indeed stac-auth-proxy without stac doesn't make sense. Will adjust in a bit.

On a side note: does anybody deploy eoAPI without the stac service at all?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a side note: does anybody deploy eoAPI without the stac service at all?

Not that I know of. But it seems like it makes sense to leave the option in there for consistency? I think it can sometimes be convenient if one wants to temporarily remove a service or so.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a simple test to the helpers.

stac-auth-proxy.enabled: true
asserts:
- notContains:
path: spec.rules[0].http.paths
any: true
content:
path: /stac(/|$)(.*)
template: networking/ingress.yaml

- it: should route correctly with experimental profile
values:
- ../profiles/experimental.yaml
set:
ingress.enabled: true
asserts:
- contains:
path: spec.rules[0].http.paths
content:
pathType: ImplementationSpecific
path: /stac(/|$)(.*)
backend:
service:
name: RELEASE-NAME-stac-auth-proxy
port:
number: 8080
template: networking/ingress.yaml
Loading