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
10 changes: 6 additions & 4 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,11 @@ jobs:
# Application (deploy/auth.project.yml). Each with the numeric user it
# runs as, read back from what was pushed rather than assumed
# (deploy-kit spec/v1/20-resolved-deployment.md#the-images-lock). An
# image that names no user runs as root, 0, as both do today, which the
# shared resolve-image-lock step refuses, so this lock stays here. Each
# entry's version is its own release's (JorisJonkers-dev/workspace#140).
# image that names no user runs as root, and a root image is refused
# here: deploy-kit 0.31 renders its uid 0 beside runAsNonRoot, which the
# kubelet refuses at pod start, so a lock naming one would take sign-in
# down on the estate path. Each entry's version is its own release's
# (JorisJonkers-dev/workspace#140).
env:
GH_TOKEN: ${{ github.token }}
API_IMAGE: ghcr.io/jorisjonkers-dev/auth-api/auth-api
Expand All @@ -123,7 +125,7 @@ jobs:
named="$(docker buildx imagetools inspect "$1" --format '{{ json .Image }}' \
| jq -r 'if has("linux/amd64") then .["linux/amd64"] else . end | .config.User // ""')"
case "$named" in
""|root|0) echo "0:0" ;;
""|root|0|0:*) echo "::error::$1 runs as root; the estate path runs only a non-root image" >&2; return 1 ;;
*[!0-9:]*) echo "::error::$1 runs as '${named}', not a numeric uid[:gid]" >&2; return 1 ;;
*:*) echo "$named" ;;
*) echo "${named}:${named}" ;;
Expand Down
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,10 @@ COPY --from=build /app/api/build/libs/*.jar app.jar
COPY --from=otel /otel-javaagent.jar otel-javaagent.jar
COPY entrypoint.sh /app/entrypoint.sh
EXPOSE 8081
# A numeric user, so the image runs as non-root wherever it runs: the estate path's
# restricted posture sets runAsNonRoot and runs the uid the images lock reads from
# here, and a root image there is refused at pod start
# (deploy-kit spec/v1/20-resolved-deployment.md#the-images-lock). The JVM writes only
# /tmp, which the render mounts writable.
USER 65532:65532
ENTRYPOINT ["/app/entrypoint.sh", "-javaagent:otel-javaagent.jar", "-jar", "app.jar"]
25 changes: 13 additions & 12 deletions deploy/auth.project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
# product (deploy-kit spec/v1/examples/auth/auth.project.yml).
#
# The Project is `auth`, so the namespace derives as `auth-system`, where both already run.
# It is still `legacy` (JorisJonkers-dev/estate platform.intent.yml): fleet-infra delivers it,
# and the estate composes this file without delivering anything for it. Its fragment exists so
# a Project on the estate path can sign in through auth and name it in `dependsOn`
# (deploy-kit spec/v1/60-setup.md#handing-over-one-project-at-a-time). The handover revisits
# every field here against the rendered diff.
#
# Each value below is read from the live objects. No env file: the old path still configures
# both Processes, and nothing on the estate path renders them yet.
# It moves from fleet-infra to the estate path in the three changes of deploy-kit
# spec/v1/60-setup.md#handing-over-one-project-at-a-time (#80). Each value below is read from
# the live objects, and each Process's environment from the old path's Deployment, in
# env/<process>/base.env beside this file.

apiVersion: intent.jorisjonkers.dev/v1
kind: Project
Expand Down Expand Up @@ -107,6 +103,10 @@ applications:
access: read
delivery: env
rotation: { tolerates: restart }
# Granted on the derived role, auth-system-auth-api, but not yet read through it:
# auth-api also reads transit/keys/auth-api-jwt for its JWKS, which no transit
# operation derives, so it logs in as the old path's role until deploy-kit can
# declare that read (env/auth-api/base.env, VAULT_KUBERNETES_ROLE).
- engine: transit
key: auth-api-jwt
operations: [sign]
Expand All @@ -119,17 +119,18 @@ applications:
runtime: static

provides:
http: 80
http: 8080 # the unprivileged nginx: no capability to bind a port below 1024

placement:
memory: 192Mi
cpu: 100m

writablePaths: [/var/cache/nginx, /var/run]
# The unprivileged nginx keeps its pid and temp files under /tmp.
writablePaths: [/tmp]

probes:
readiness: { path: /, port: 80 }
liveness: { path: /, port: 80 }
readiness: { path: /, port: 8080 }
liveness: { path: /, port: 8080 }

startupBudget: 30s

Expand Down
92 changes: 92 additions & 0 deletions deploy/env/auth-api/base.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# The Process scope of auth-api (deploy-kit spec/v1, chapter 10, Configuration).
#
# Every variable the old path's Deployment sets, carried over as it is today
# (fleet-infra cluster/flux/apps/stateless/auth-api/deployment.yaml), except:
# - the coordinates of what auth-api depends on and its own public address,
# which are placeholders now, so they follow the composition;
# - the values the Vault Agent Injector templated into /vault/secrets/auth-api.env,
# which are ${secret:...} placeholders now, synced by VSO into Secrets;
# - DEPLOYMENT_ENVIRONMENT, OTEL_SERVICE_NAME and OTEL_EXPORTER_OTLP_ENDPOINT, which
# the jvm Runtime Profile injects;
# - the PYROSCOPE_* variables: their agent came from an init container the render
# has no counterpart for, so nothing would read them.
SERVER_PORT=8081
SPRING_PROFILES_ACTIVE=prod

# The old path's java flags: ZGC, and a heap of 55% of the limit, which leaves room
# under it for the OpenTelemetry agent's native memory during the cold start.
JAVA_TOOL_OPTIONS=-XX:+UseZGC -XX:MaxRAMPercentage=55

# Spring Cloud Vault, `delivery: self`: the transit key the tokens are signed with,
# and the dynamic database login that takes precedence over DB_USER below.
#
# It logs in as the old path's role, auth-api, not as the role the render derives
# (auth-system-auth-api), on purpose. The token's JWKS is read from
# transit/keys/auth-api-jwt, and deploy-kit 0.31 can declare a transit operation
# (sign, verify, encrypt, decrypt, rotate) but not a read of the key, so the derived
# policy cannot cover what auth-api reads at startup. The old role still binds this
# ServiceAccount (auth-api in auth-system), and the old path's Vault bootstrap still
# writes it, with database/creds/auth-api beside the transit grants.
SPRING_CONFIG_IMPORT=vault://
VAULT_ENABLED=true
VAULT_ADDR=http://vault.data-system.svc.cluster.local:8200
VAULT_AUTHENTICATION=KUBERNETES
VAULT_KUBERNETES_ROLE=auth-api
VAULT_DB_ENABLED=true
AUTH_TRANSIT_ENABLED=true
AUTH_TRANSIT_KEY_NAME=auth-api-jwt

DB_HOST=${dependency:platform-postgres.host}
DB_PORT=${dependency:platform-postgres.port}
DB_USER=${secret:secret/data/platform/postgres#auth.user}
DB_PASSWORD=${secret:secret/data/platform/postgres#auth.password}

VALKEY_HOST=${dependency:platform-valkey.host}
VALKEY_PORT=${dependency:platform-valkey.port}

# The static broker login: a dynamic one was revoked under a running pod once.
SPRING_RABBITMQ_HOST=${dependency:platform-rabbitmq.host}
SPRING_RABBITMQ_PORT=${dependency:platform-rabbitmq.port}
SPRING_RABBITMQ_USERNAME=${secret:secret/data/platform/rabbitmq#rabbitmq.user}
SPRING_RABBITMQ_PASSWORD=${secret:secret/data/platform/rabbitmq#rabbitmq.password}

# The submission account stalwart provisions; its password has one source,
# secret/platform/mail.
MAIL_HOST=${dependency:stalwart.host}
MAIL_PORT=${dependency:stalwart.port}
MAIL_USERNAME=auth@jorisjonkers.dev
MAIL_PASSWORD=${secret:secret/data/platform/mail#account.password}

AUTH_ISSUER=${exposure:auth.public#url}
AUTH_LOGIN_URL=${exposure:auth.public#url}/login
CONFIRMATION_URL=${exposure:auth.public#url}/confirm-email
PASSWORD_RESET_URL=${exposure:auth.public#url}/reset-password
SESSION_COOKIE_DOMAIN=jorisjonkers.dev
SESSION_COOKIE_SECURE=true

# Every forward-auth host belongs here, whether or not it uses WebSockets today:
# a missing host breaks only a WebSocket handshake, with a 403 from Spring's CORS
# filter that logs nothing (the workspace's agent contract records the Overleaf case).
AUTH_CORS_ALLOWED_ORIGINS=https://jorisjonkers.dev,https://auth.jorisjonkers.dev,https://agents.jorisjonkers.dev,https://vault.jorisjonkers.dev,https://n8n.jorisjonkers.dev,https://grafana.jorisjonkers.dev,https://dashboard.jorisjonkers.dev,https://rabbitmq.jorisjonkers.dev,https://status.jorisjonkers.dev,https://overleaf.jorisjonkers.dev,https://notes.jorisjonkers.dev,https://hindsight.jorisjonkers.dev,https://hindsight-api.jorisjonkers.dev,https://hindsight-private.jorisjonkers.dev,https://karakeep.jorisjonkers.dev,https://tribelt.jorisjonkers.dev,https://grimoire.jorisjonkers.dev

# Each OIDC client's secret, one key of secret/auth-api each.
AUTH_CLIENTS_ESTATE_DASHBOARD_SECRET=${secret:secret/data/auth-api#auth.clients.estate-dashboard.secret}
AUTH_CLIENTS_FINDABILITY_EXPLORER_SECRET=${secret:secret/data/auth-api#auth.clients.findability-explorer.secret}
AUTH_CLIENTS_GRAFANA_SECRET=${secret:secret/data/auth-api#auth.clients.grafana.secret}
AUTH_CLIENTS_GRIMOIRE_SECRET=${secret:secret/data/auth-api#auth.clients.grimoire.secret}
AUTH_CLIENTS_N8N_SECRET=${secret:secret/data/auth-api#auth.clients.n8n.secret}
AUTH_CLIENTS_OUTLINE_SECRET=${secret:secret/data/auth-api#auth.clients.outline.secret}
AUTH_CLIENTS_TRIBELT_SECRET=${secret:secret/data/auth-api#auth.clients.tribelt.secret}
AUTH_CLIENTS_VAULT_SECRET=${secret:secret/data/auth-api#auth.clients.vault.secret}

# The trace exporter's tuning from the old path.
OTEL_LOGS_EXPORTER=none
OTEL_METRICS_EXPORTER=none
OTEL_TRACES_EXPORTER=otlp
OTEL_EXPORTER_OTLP_TIMEOUT=5000
OTEL_BSP_SCHEDULE_DELAY=5000
OTEL_BSP_MAX_QUEUE_SIZE=16384
OTEL_BSP_MAX_EXPORT_BATCH_SIZE=2048
# The profile's endpoint is the collector's otlp surface, 4317, which speaks gRPC; the
# agent's default protocol, http/protobuf, is the old path's port 4318.
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
5 changes: 5 additions & 0 deletions deploy/env/auth-ui/base.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# The Process scope of auth-ui (deploy-kit spec/v1, chapter 10, Configuration).
#
# Empty on purpose: auth-ui is nginx serving the built sign-in UI, and the old path's
# Deployment sets no variable for it. The UI reaches auth-api on the same host, under
# /api/, so it needs no address either.
Loading