diff --git a/CHANGELOG.md b/CHANGELOG.md index d0dbc188..3dacadbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added support for annotations on the PgSTAC bootstrap job via `pgstacBootstrap.jobAnnotations` in values.yaml [#381](https://github.com/developmentseed/eoapi-k8s/pull/381) +- Added auth support to STAC Browser [#376](https://github.com/developmentseed/eoapi-k8s/pull/376) ### Fixed diff --git a/charts/eoapi/templates/networking/ingress-browser.yaml b/charts/eoapi/templates/networking/ingress-no-prefix.yaml similarity index 56% rename from charts/eoapi/templates/networking/ingress-browser.yaml rename to charts/eoapi/templates/networking/ingress-no-prefix.yaml index 35eccc23..048b4580 100644 --- a/charts/eoapi/templates/networking/ingress-browser.yaml +++ b/charts/eoapi/templates/networking/ingress-no-prefix.yaml @@ -1,5 +1,4 @@ -# We need a separate ingress because browser has the prefix /browser hardcoded in the code -{{- if and .Values.browser.enabled .Values.ingress.enabled (or (not (hasKey .Values.browser "ingress")) .Values.browser.ingress.enabled) }} +{{- if and .Values.ingress.enabled (or (and .Values.stac.enabled (or (not (hasKey .Values.stac "ingress")) .Values.stac.ingress.enabled)) (and .Values.browser.enabled (or (not (hasKey .Values.browser "ingress")) .Values.browser.ingress.enabled))) }} {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion }} apiVersion: networking.k8s.io/v1 {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion }} @@ -9,21 +8,20 @@ apiVersion: extensions/v1beta1 {{- end }} kind: Ingress metadata: - name: {{ .Release.Name }}-ingress-browser + name: {{ .Release.Name }}-ingress-no-prefix labels: - app: {{ .Release.Name }}-ingress-browser + app: {{ .Release.Name }}-ingress-no-prefix annotations: {{- if .Values.ingress.annotations }} {{ toYaml .Values.ingress.annotations | indent 4 }} {{- end }} - {{- if eq .Values.ingress.className "nginx" }} - nginx.ingress.kubernetes.io/rewrite-target: /browser/$2 - nginx.ingress.kubernetes.io/use-regex: "true" - {{- end }} - # Temporary annotations for Traefik until uvicorn support real prefix in ASGI: https://github.com/encode/uvicorn/discussions/2490 + # Services handle their own path manipulation - no stripPrefix middleware {{- if eq .Values.ingress.className "traefik" }} traefik.ingress.kubernetes.io/router.entrypoints: web - traefik.ingress.kubernetes.io/router.middlewares: {{ $.Release.Namespace }}-{{ $.Release.Name }}-strip-prefix-middleware@kubernetescrd + {{- end }} + {{- if eq .Values.ingress.className "nginx" }} + nginx.ingress.kubernetes.io/rewrite-target: /$2 + nginx.ingress.kubernetes.io/use-regex: "true" {{- end }} spec: {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} @@ -35,12 +33,26 @@ spec: - host: {{ . }} http: paths: + {{- if and $.Values.stac.enabled (or (not (hasKey $.Values.stac "ingress")) $.Values.stac.ingress.enabled) }} + - pathType: {{ if eq $.Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }} + 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 }} + {{- if and $.Values.browser.enabled (or (not (hasKey $.Values.browser "ingress")) $.Values.browser.ingress.enabled) }} - pathType: {{ if eq $.Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }} path: "/browser{{ if eq $.Values.ingress.className "nginx" }}(/|$)(.*){{ end }}" backend: service: - name: {{ .Release.Name }}-browser + name: {{ $.Release.Name }}-browser port: number: 8080 {{- end }} @@ -51,6 +63,20 @@ spec: {{- end }} http: paths: + {{- if and .Values.stac.enabled (or (not (hasKey .Values.stac "ingress")) .Values.stac.ingress.enabled) }} + - pathType: {{ if eq .Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }} + 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 }} + {{- if and .Values.browser.enabled (or (not (hasKey .Values.browser "ingress")) .Values.browser.ingress.enabled) }} - pathType: {{ if eq .Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }} path: "/browser{{ if eq .Values.ingress.className "nginx" }}(/|$)(.*){{ end }}" diff --git a/charts/eoapi/templates/networking/ingress.yaml b/charts/eoapi/templates/networking/ingress.yaml index dc49bb70..d6afb3bc 100644 --- a/charts/eoapi/templates/networking/ingress.yaml +++ b/charts/eoapi/templates/networking/ingress.yaml @@ -19,7 +19,7 @@ metadata: {{- if .Values.ingress.annotations }} {{ toYaml .Values.ingress.annotations | indent 4 }} {{- end }} - # Temporary annotations for Traefik until uvicorn support real prefix in ASGI: https://github.com/encode/uvicorn/discussions/2490 + # Traefik stripPrefix middleware for services that need path stripping (excludes STAC) {{- if eq .Values.ingress.className "traefik" }} traefik.ingress.kubernetes.io/router.entrypoints: web traefik.ingress.kubernetes.io/router.middlewares: {{ $.Release.Namespace }}-{{ $.Release.Name }}-strip-prefix-middleware@kubernetescrd @@ -44,19 +44,7 @@ spec: number: {{ $.Values.service.port }} {{- end }} - {{- if and $.Values.stac.enabled (or (not (hasKey $.Values.stac "ingress")) $.Values.stac.ingress.enabled) }} - - pathType: {{ if eq $.Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }} - 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 }} + {{- if and $.Values.vector.enabled (or (not (hasKey $.Values.vector "ingress")) $.Values.vector.ingress.enabled) }} - pathType: {{ if eq $.Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }} @@ -114,19 +102,7 @@ spec: number: {{ .Values.service.port }} {{- end }} - {{- if and .Values.stac.enabled (or (not (hasKey .Values.stac "ingress")) .Values.stac.ingress.enabled) }} - - pathType: {{ if eq .Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }} - 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 }} + {{- if and .Values.vector.enabled (or (not (hasKey .Values.vector "ingress")) .Values.vector.ingress.enabled) }} - pathType: {{ if eq .Values.ingress.className "nginx" }}ImplementationSpecific{{ else }}Prefix{{ end }} diff --git a/charts/eoapi/templates/services/browser/deployment.yaml b/charts/eoapi/templates/services/browser/deployment.yaml index 08e143a3..87505a28 100644 --- a/charts/eoapi/templates/services/browser/deployment.yaml +++ b/charts/eoapi/templates/services/browser/deployment.yaml @@ -7,7 +7,7 @@ metadata: app: {{ .Release.Name }}-browser gitsha: {{ .Values.gitSha }} spec: - replicas: {{.Values.browser.replicaCount}} + replicas: {{ .Values.browser.replicaCount }} selector: matchLabels: app: {{ .Release.Name }}-browser @@ -23,5 +23,20 @@ spec: - containerPort: 8080 env: - name: SB_catalogUrl - value: "{{ .Values.stac.ingress.path }}" + value: "http://{{ .Values.ingress.host }}{{ .Values.stac.ingress.path }}" + {{- if index .Values "stac-auth-proxy" "enabled" }} + - name: SB_authConfig + {{- if .Values.browser.authConfig }} + value: {{ .Values.browser.authConfig }} + {{- else }} + value: | + { + "type": "openIdConnect", + "openIdConnectUrl": "{{ index .Values "stac-auth-proxy" "env" "OIDC_DISCOVERY_URL" }}", + "oidcOptions": { + "client_id": "{{ .Values.browser.oidcClientId | default "test-client" }}" + } + } + {{- end }} + {{- end }} {{- end }} diff --git a/charts/eoapi/tests/ingress_tests.yaml b/charts/eoapi/tests/ingress_tests.yaml index 11363982..4d0b3477 100644 --- a/charts/eoapi/tests/ingress_tests.yaml +++ b/charts/eoapi/tests/ingress_tests.yaml @@ -1,6 +1,6 @@ suite: unified ingress tests templates: - - templates/services/ingress.yaml + - templates/networking/ingress.yaml tests: - it: "vector ingress with nginx controller" set: @@ -33,13 +33,13 @@ tests: path: spec.ingressClassName value: "nginx" - - it: "stac ingress with traefik controller" + - it: "raster ingress with traefik controller" set: ingress.className: "traefik" ingress.pathType: "Prefix" ingress.host: "eoapi.local" - raster.enabled: false - stac.enabled: true + raster.enabled: true + stac.enabled: false vector.enabled: false multidim.enabled: false browser.enabled: false @@ -48,7 +48,7 @@ tests: of: Ingress - equal: path: spec.rules[0].http.paths[0].path - value: "/stac" + value: "/raster" - equal: path: spec.rules[0].http.paths[0].pathType value: "Prefix" @@ -95,8 +95,7 @@ tests: ingress.className: "nginx" raster.enabled: true raster.ingress.path: "/titiler" - stac.enabled: true - stac.ingress.path: "/api" + stac.enabled: false vector.enabled: true vector.ingress.path: "/features" multidim.enabled: false @@ -109,9 +108,6 @@ tests: value: "/titiler(/|$)(.*)" - equal: path: spec.rules[0].http.paths[1].path - value: "/api(/|$)(.*)" - - equal: - path: spec.rules[0].http.paths[2].path value: "/features(/|$)(.*)" - equal: path: spec.rules[0].http.paths[0].pathType @@ -119,9 +115,6 @@ tests: - equal: path: spec.rules[0].http.paths[1].pathType value: "ImplementationSpecific" - - equal: - path: spec.rules[0].http.paths[2].pathType - value: "ImplementationSpecific" - equal: path: metadata.annotations value: @@ -131,9 +124,9 @@ tests: - it: "custom paths with traefik controller" set: ingress.className: "traefik" - raster.enabled: false - stac.enabled: true - stac.ingress.path: "/api" + raster.enabled: true + raster.ingress.path: "/titiler" + stac.enabled: false vector.enabled: false multidim.enabled: false browser.enabled: false @@ -142,7 +135,7 @@ tests: of: Ingress - equal: path: spec.rules[0].http.paths[0].path - value: "/api" + value: "/titiler" - equal: path: spec.rules[0].http.paths[0].pathType value: "Prefix" @@ -159,8 +152,8 @@ tests: - "2.eoapi.dev" - "1.eoapi.dev" raster.enabled: true - stac.enabled: true - vector.enabled: false + stac.enabled: false + vector.enabled: true multidim.enabled: false browser.enabled: false asserts: @@ -177,13 +170,13 @@ tests: value: "/raster(/|$)(.*)" - equal: path: spec.rules[0].http.paths[1].path - value: "/stac(/|$)(.*)" + value: "/vector(/|$)(.*)" - equal: path: spec.rules[1].http.paths[0].path value: "/raster(/|$)(.*)" - equal: path: spec.rules[1].http.paths[1].path - value: "/stac(/|$)(.*)" + value: "/vector(/|$)(.*)" - it: "multiple hosts with traefik controller" set: @@ -191,8 +184,8 @@ tests: ingress.hosts: - "2.eoapi.dev" - "1.eoapi.dev" - raster.enabled: false - stac.enabled: true + raster.enabled: true + stac.enabled: false vector.enabled: false multidim.enabled: false browser.enabled: false @@ -207,13 +200,13 @@ tests: value: "1.eoapi.dev" - equal: path: spec.rules[0].http.paths[0].path - value: "/stac" + value: "/raster" - equal: path: spec.rules[0].http.paths[0].pathType value: "Prefix" - equal: path: spec.rules[1].http.paths[0].path - value: "/stac" + value: "/raster" - equal: path: spec.rules[1].http.paths[0].pathType value: "Prefix" @@ -226,8 +219,8 @@ tests: - "1.eoapi.dev" ingress.tls.enabled: true ingress.tls.secretName: "eoapi-tls" - raster.enabled: false - stac.enabled: true + raster.enabled: true + stac.enabled: false vector.enabled: false multidim.enabled: false browser.enabled: false @@ -250,8 +243,8 @@ tests: ingress.host: "1.eoapi.dev" ingress.tls.enabled: true ingress.tls.secretName: "eoapi-tls" - raster.enabled: false - stac.enabled: true + raster.enabled: true + stac.enabled: false vector.enabled: false multidim.enabled: false browser.enabled: false @@ -274,8 +267,8 @@ tests: ingress.host: "should-be-ignored.com" ingress.hosts: - "1.eoapi.dev" - raster.enabled: false - stac.enabled: true + raster.enabled: true + stac.enabled: false vector.enabled: false multidim.enabled: false browser.enabled: false diff --git a/charts/eoapi/tests/stac-auth-proxy-ingress_test.yaml b/charts/eoapi/tests/stac-auth-proxy-ingress_test.yaml index c4007016..0851ce94 100644 --- a/charts/eoapi/tests/stac-auth-proxy-ingress_test.yaml +++ b/charts/eoapi/tests/stac-auth-proxy-ingress_test.yaml @@ -1,6 +1,6 @@ -suite: test stac-auth-proxy ingress routing +suite: test ingress routing without stripPrefix middleware templates: - - networking/ingress.yaml + - networking/ingress-no-prefix.yaml tests: - it: should route ingress to stac-auth-proxy when enabled @@ -23,7 +23,7 @@ tests: name: RELEASE-NAME-stac-auth-proxy port: number: 8080 - template: networking/ingress.yaml + template: networking/ingress-no-prefix.yaml - it: should route ingress directly to stac when auth-proxy is disabled set: @@ -45,20 +45,18 @@ tests: name: RELEASE-NAME-stac port: number: 8080 - template: networking/ingress.yaml + template: networking/ingress-no-prefix.yaml - - it: should not create stac routes when stac is disabled + - it: should not create ingress when both stac and browser are disabled set: ingress.enabled: true stac.enabled: false + browser.enabled: false stac-auth-proxy.enabled: true asserts: - - notContains: - path: spec.rules[0].http.paths - any: true - content: - path: /stac(/|$)(.*) - template: networking/ingress.yaml + - hasDocuments: + count: 0 + template: networking/ingress-no-prefix.yaml - it: should route correctly with experimental profile values: @@ -76,4 +74,57 @@ tests: name: RELEASE-NAME-stac-auth-proxy port: number: 8080 - template: networking/ingress.yaml + template: networking/ingress-no-prefix.yaml + + - it: should route ingress to browser + set: + ingress.enabled: true + ingress.className: nginx + browser.enabled: true + stac.enabled: false + asserts: + - contains: + path: spec.rules[0].http.paths + content: + pathType: ImplementationSpecific + path: /browser(/|$)(.*) + backend: + service: + name: RELEASE-NAME-browser + port: + number: 8080 + template: networking/ingress-no-prefix.yaml + + - it: should include both stac and browser when both enabled + set: + ingress.enabled: true + ingress.className: nginx + stac.enabled: true + stac.ingress.enabled: true + stac.ingress.path: "/stac" + stac-auth-proxy.enabled: true + browser.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-no-prefix.yaml + - contains: + path: spec.rules[0].http.paths + content: + pathType: ImplementationSpecific + path: /browser(/|$)(.*) + backend: + service: + name: RELEASE-NAME-browser + port: + number: 8080 + template: networking/ingress-no-prefix.yaml diff --git a/charts/eoapi/tests/stac_browser_tests.yaml b/charts/eoapi/tests/stac_browser_tests.yaml index d195a601..0a74bd89 100644 --- a/charts/eoapi/tests/stac_browser_tests.yaml +++ b/charts/eoapi/tests/stac_browser_tests.yaml @@ -51,3 +51,91 @@ tests: - equal: path: metadata.annotations.annotation2 value: world + - it: "stac browser deployment with auth enabled" + set: + raster.enabled: false + stac.enabled: true + vector.enabled: false + multidim.enabled: false + browser.enabled: true + stac-auth-proxy.enabled: true + stac-auth-proxy.env.OIDC_DISCOVERY_URL: "http://localhost/mock-oidc/.well-known/openid-configuration" + ingress.host: "localhost" + stac.ingress.path: "/stac" + mockOidcServer.ingress.path: "/mock-oidc" + browser.oidcClientId: "test-client" + gitSha: "ABC123" + template: templates/services/browser/deployment.yaml + asserts: + - isKind: + of: Deployment + - contains: + path: spec.template.spec.containers[0].env + content: + name: SB_catalogUrl + value: "http://localhost/stac" + - contains: + path: spec.template.spec.containers[0].env + content: + name: SB_authConfig + value: | + { + "type": "openIdConnect", + "openIdConnectUrl": "http://localhost/mock-oidc/.well-known/openid-configuration", + "oidcOptions": { + "client_id": "test-client" + } + } + - it: "stac browser deployment with custom OIDC_DISCOVERY_URL" + set: + raster.enabled: false + stac.enabled: true + vector.enabled: false + multidim.enabled: false + browser.enabled: true + stac-auth-proxy.enabled: true + stac-auth-proxy.env.OIDC_DISCOVERY_URL: "https://auth.example.com/.well-known/openid-configuration" + ingress.host: "localhost" + stac.ingress.path: "/stac" + browser.oidcClientId: "test-client" + gitSha: "ABC123" + template: templates/services/browser/deployment.yaml + asserts: + - isKind: + of: Deployment + - contains: + path: spec.template.spec.containers[0].env + content: + name: SB_authConfig + value: | + { + "type": "openIdConnect", + "openIdConnectUrl": "https://auth.example.com/.well-known/openid-configuration", + "oidcOptions": { + "client_id": "test-client" + } + } + - it: "stac browser deployment without auth" + set: + raster.enabled: false + stac.enabled: true + vector.enabled: false + multidim.enabled: false + browser.enabled: true + stac-auth-proxy.enabled: false + ingress.host: "localhost" + stac.ingress.path: "/stac" + gitSha: "ABC123" + template: templates/services/browser/deployment.yaml + asserts: + - isKind: + of: Deployment + - contains: + path: spec.template.spec.containers[0].env + content: + name: SB_catalogUrl + value: "http://localhost/stac" + - notContains: + path: spec.template.spec.containers[0].env + content: + name: SB_authConfig diff --git a/charts/eoapi/values.yaml b/charts/eoapi/values.yaml index 62608f00..c1651f00 100644 --- a/charts/eoapi/values.yaml +++ b/charts/eoapi/values.yaml @@ -415,7 +415,11 @@ stac: # STAC Auth Proxy - authentication layer for STAC API stac-auth-proxy: enabled: false + image: + tag: "v0.10.2-rc2" env: + ROOT_PATH: "/stac" + OVERRIDE_HOST: "false" DEFAULT_PUBLIC: "true" # UPSTREAM_URL will be set dynamically in template to point to stac service # OIDC_DISCOVERY_URL must be configured when enabling auth @@ -492,6 +496,8 @@ browser: tag: 3.3.4 ingress: enabled: true # Control ingress specifically for browser service + # OAuth2 client ID for browser (frontend app). Reads OIDC_DISCOVERY_URL from stac-auth-proxy.env + oidcClientId: "some-client-id" docServer: enabled: true