Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fb2ace4
wip(deployment): add lldap + Authelia config skeleton
theosanderson May 13, 2026
7232000
wip(deployment): wire Authelia/lldap into all helm templates
theosanderson May 13, 2026
d1e59aa
wip(deployment): add registration-service skeleton + delete Keycloakify
theosanderson May 13, 2026
2ec685a
wip(website): switch OIDC client from Keycloak to Authelia
theosanderson May 13, 2026
126b7c2
wip(backend): swap Keycloak admin client for an LDAP user directory
theosanderson May 13, 2026
9481313
wip(cli): switch auth to OIDC device-code flow against Authelia
theosanderson May 13, 2026
e8933a9
wip(integration-tests): rewire auth.page.ts to Authelia + registratio…
theosanderson May 13, 2026
4ae2202
docs(architecture): describe Authelia + lldap migration
theosanderson May 13, 2026
4bfe33b
style(integration-tests): prettier auth.page.ts
theosanderson May 13, 2026
54e44d3
fix(deployment): Authelia entrypoint + lldap login field name
theosanderson May 13, 2026
b98cbf5
feat(backend): service-to-service auth via static X-Service-Token
theosanderson May 13, 2026
93cafe6
style: fix lint/format from first CI run
theosanderson May 13, 2026
ecc7e16
fix(deployment): unblock authelia config rendering for dev mode
theosanderson May 13, 2026
404b443
fix(deployment+website): Authelia HTTPS via traefik on 8443, hardcode…
theosanderson May 13, 2026
1b2cb0a
chore(website): touch to force fresh image build (cache collision wor…
theosanderson May 13, 2026
9a24091
fix(deployment): guard splitList against an unset $.Values.host
theosanderson May 13, 2026
dcbc620
Merge remote-tracking branch 'origin/main' into authelia-exp
theosanderson May 13, 2026
0b0ca50
style: fix CI lint failures from the merged-main CI run
theosanderson May 13, 2026
5c82427
fix(backend): disable CSRF so service-token POSTs aren't rejected
theosanderson May 13, 2026
b2a7c1b
fix(backend): use stateless session policy + update auth-failure stat…
theosanderson May 13, 2026
bf339d2
ci(codeql): exclude java/spring-disabled-csrf-protection
theosanderson May 13, 2026
0dd8559
wip: deeper Authelia integration — PKCE, fixed callback path, dev domain
theosanderson May 13, 2026
c94940c
wip: confidential OIDC client + plaintext secret round-tripped to web…
theosanderson May 13, 2026
9c7c05e
Fix Authelia integration auth flow
theosanderson May 13, 2026
68f8415
Route Authelia ingress explicitly through Traefik
theosanderson May 13, 2026
6eb9626
Fix CLI local DNS helper checks
theosanderson May 13, 2026
447b628
Fix auth routing and integration checks
theosanderson May 13, 2026
6697a48
Fix CLI test keyring interpreter selection
theosanderson May 13, 2026
2e5c9f7
Fix Authelia preview cookie domain
theosanderson May 13, 2026
4160174
Roll Authelia pods when config changes
theosanderson May 13, 2026
809c77a
Add registration nav link
theosanderson May 13, 2026
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: 10 additions & 0 deletions .github/codeql-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: Loculus CodeQL config

query-filters:
# Loculus is a stateless API: every request authenticates from scratch with
# either a bearer JWT or X-Service-Token header, no session cookies. Spring's
# CSRF protection is intentionally disabled in
# backend/src/main/kotlin/org/loculus/backend/config/SecurityConfig.kt and
# the corresponding alert is not actionable.
- exclude:
id: java/spring-disabled-csrf-protection
11 changes: 1 addition & 10 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,11 @@ updates:
patch:
update-types:
- "patch"
- package-ecosystem: npm
directory: keycloak/keycloakify
schedule:
interval: monthly
groups:
minorAndPatch:
update-types:
- "minor"
- "patch"
- package-ecosystem: docker
directories:
- website
- backend
- keycloak/keycloakify
- registration-service
- preprocessing/nextclade
- preprocessing/dummy
- ingest
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build-arm-images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ jobs:
uses: ./.github/workflows/ena-submission-flyway-image.yaml
with:
build_arm: true
trigger-keycloakify:
trigger-registration-service:
needs: should-build
if: needs.should-build.outputs.should_run == 'true'
uses: ./.github/workflows/keycloakify-image.yml
uses: ./.github/workflows/registration-service-image.yml
with:
build_arm: true
trigger-preprocessing-nextclade:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ jobs:
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
config-file: .github/codeql-config.yml
- if: matrix.language == 'java-kotlin' && matrix.build-mode == 'manual'
name: Set up JDK
uses: actions/setup-java@v5
Expand Down
45 changes: 0 additions & 45 deletions .github/workflows/keycloakify-test.yml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: keycloakify-image
name: registration-service-image
on:
pull_request:
push:
Expand All @@ -19,36 +19,29 @@ on:
default: false
required: false
env:
DOCKER_IMAGE_NAME: ghcr.io/loculus-project/keycloakify
DOCKER_IMAGE_NAME: ghcr.io/loculus-project/registration-service
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
BUILD_ARM: ${{ github.event.inputs.build_arm || inputs.build_arm || github.ref == 'refs/heads/main' }}
sha: ${{ github.event.pull_request.head.sha || github.sha }}
concurrency:
group: ci-${{ github.ref == 'refs/heads/main' && github.run_id || github.ref }}-keycloak-buil-${{github.event.inputs.build_arm}}d
group: ci-${{ github.ref == 'refs/heads/main' && github.run_id || github.ref }}-registration-service-${{github.event.inputs.build_arm}}
cancel-in-progress: true
jobs:
keycloakify-image:
name: Build keycloakify Docker Image # Don't change: Referenced by .github/workflows/update-argocd-metadata.yml
registration-service-image:
name: Registration service docker image build # Don't change: Referenced by .github/workflows/update-argocd-metadata.yml
runs-on: ubuntu-latest
timeout-minutes: 30
timeout-minutes: 15
permissions:
contents: read
packages: write
checks: read
steps:
- name: Shorten sha
run: echo "sha=${sha::7}" >> $GITHUB_ENV
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Generate files hash
id: files-hash
run: |
DIR_HASH=$(echo -n ${{ hashFiles('./keycloak/keycloakify/**','.github/workflows/keycloakify-image.yml', './website/.nvmrc' ) }})
DIR_HASH=$(echo -n ${{ hashFiles('registration-service/**', '.github/workflows/registration-service-image.yml') }})
echo "DIR_HASH=$DIR_HASH${{ env.BUILD_ARM == 'true' && '-arm' || '' }}" >> $GITHUB_ENV
- name: Setup Docker metadata
id: dockerMetadata
Expand All @@ -61,30 +54,29 @@ jobs:
type=raw,value=${{ env.BRANCH_NAME }}
type=raw,value=commit-${{ env.sha }}
type=raw,value=${{ env.BRANCH_NAME }}-arm,enable=${{ env.BUILD_ARM }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Check if image exists
id: check-image
run: |
EXISTS=$(docker manifest inspect ${{ env.DOCKER_IMAGE_NAME }}:${{ env.DIR_HASH }} > /dev/null 2>&1 && echo "true" || echo "false")
echo "CACHE_HIT=$EXISTS" >> $GITHUB_ENV
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Get node version build arg
id: get-node-version
if: env.CACHE_HIT == 'false'
run: |
NODE_VERSION=$(grep -v "^[[:space:]]*#" website/.nvmrc | tr -d 'v')
echo "NODE_VERSION=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Build and push image
if: env.CACHE_HIT == 'false'
uses: docker/build-push-action@v7
with:
context: ./keycloak/keycloakify
context: ./registration-service
push: true
tags: ${{ steps.dockerMetadata.outputs.tags }}
cache-from: type=gha,scope=keycloakify-${{ github.ref }}
cache-to: type=gha,mode=max,scope=keycloakify-${{ github.ref }}
cache-from: type=gha,scope=registration-service-${{ github.ref }}
cache-to: type=gha,mode=max,scope=registration-service-${{ github.ref }}
platforms: ${{ env.BUILD_ARM == 'true' && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
build-args: NODE_VERSION=${{ steps.get-node-version.outputs.NODE_VERSION }}
- name: Tag and push image if cache hit
if: env.CACHE_HIT == 'true'
run: |
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/update-argocd-metadata.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ jobs:
check-name: Build ingest Docker Image
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 2
- name: Wait for Keycloakify Docker Image
- name: Wait for Registration Service Docker Image
uses: lewagon/wait-on-check-action@v1.7.0
with:
ref: ${{ github.sha }}
check-name: Build keycloakify Docker Image
check-name: Registration service docker image build
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 2
- name: Wait for ENA Submission Docker Image
Expand Down
4 changes: 2 additions & 2 deletions architecture_docs/05_building_block_view.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ and how they interact with each other and external participants.
* use the website to browse the data and download sequences
* or use LAPIS directly to query the data (e.g. for automated analysis).
* Submitters can
* log in via Keycloak
* log in via Authelia
* submit new sequence data via the website
* or use the API directly to automate their submission process.
* The backend infrastructure stores and processes the data.
* LAPIS / SILO provides the query engine for the sequence data that is stored in the backend infrastructure.
* The backend infrastructure also fetches sequence data from / uploads sequence data to INSDC services.
* The website and the backend infrastructure use Keycloak to verify the identity of users.
* The website and the backend infrastructure use Authelia to verify the identity of users.

## LAPIS / SILO

Expand Down
4 changes: 2 additions & 2 deletions architecture_docs/07_deployment_view.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ We configured Traefik to expose the relevant services to the public:
* the website,
* the backend,
* LAPIS,
* Keycloak.
* Authelia + lldap.

We only need a single instance of the website, the backend and keycloak (and their respective databases).
We only need a single instance of the website, the backend and authelia (and their respective databases).
The other services (LAPIS, SILO, preprocessing pipeline, ingest and ENA deposition) have to be configured
and deployed per organism that the Loculus instance supports.
We utilize Helm to generate those multiple service instances.
18 changes: 18 additions & 0 deletions architecture_docs/09_architecture_decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,21 @@ Some relevant discussions:
#### Decision

We implemented the building blocks as described in this documentation.

## Authentication: Authelia + lldap (2026)

The original Keycloak deployment was replaced with Authelia for OIDC and lldap
as the user directory. Rationale:

- Lighter footprint (Authelia and lldap together are smaller than Keycloak
alone) and a simpler operational model for self-hosted installations.
- LDAP backend is pluggable: bundled lldap for self-hosted, or operators can
point Authelia at an existing enterprise LDAP/AD by setting
`auth.bundledLdap.enabled=false` and configuring `auth.ldap.*`.
- Self-registration moves into a small dedicated `registration-service` that
writes new users into lldap via its GraphQL admin API; in BYO-LDAP mode this
service is not deployed and registration is managed out-of-band.
- ORCID social login is dropped; can be added later in the registration
service.
- CLI authentication moves from ROPC (unsupported in Authelia) to the OIDC
device-code flow.
8 changes: 4 additions & 4 deletions architecture_docs/plantuml/05_level_1.puml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ frame Loculus as loculus {
component "Loculus Website" as website
component "Loculus Backend Infrastructure" as backend
component "LAPIS" as lapis
component "Keycloak" as keycloak
component "Authelia + lldap" as authelia
}

submitter --> website
submitter -right-> backend
submitter --> keycloak
submitter --> authelia

user --> website
user --> lapis
Expand All @@ -26,7 +26,7 @@ lapis --> backend

backend --> insdc

backend -left-> keycloak
website -left-> keycloak
backend -left-> authelia
website -left-> authelia

@enduml
8 changes: 4 additions & 4 deletions architecture_docs/plantuml/07_cluster_details.puml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ node "Kubernetes Cluster" as loculus {

component "Loculus Website" as website
component "Loculus Backend" as backend
component "Keycloak" as keycloak
component "Authelia + lldap" as authelia

node "One instance per organism" {
component "Processing Pipeline" as processing
Expand All @@ -21,17 +21,17 @@ node "Kubernetes Cluster" as loculus {
}

database "Loculus Database" as db
database "Keycloak Database" as kc_db
database "Authelia + lldap Database" as ldap_data

" " --> traefik : HTTP
traefik --> website
traefik --> backend
traefik --> lapis
traefik --> keycloak
traefik --> authelia

backend --> db
deposition --> db
keycloak --> kc_db
authelia --> ldap_data


@enduml
4 changes: 2 additions & 2 deletions architecture_docs/plantuml/07_deployment_overview.puml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ node "Kubernetes Cluster" as loculus {
}

database "Loculus Database" as db
database "Keycloak Database" as kc_db
database "Authelia + lldap Database" as ldap_data

services --> db
services --> kc_db
services --> ldap_data

@enduml
3 changes: 2 additions & 1 deletion backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ dependencies {
implementation "org.jetbrains.exposed:exposed-kotlin-datetime"
implementation "org.jetbrains.kotlinx:kotlinx-datetime:0.7.1-0.6.x-compat"
implementation "org.hibernate.validator:hibernate-validator"
implementation "org.keycloak:keycloak-admin-client:26.0.9"
implementation "org.springframework.boot:spring-boot-starter-data-ldap"
implementation("io.minio:minio:9.0.0")
implementation("software.amazon.awssdk:s3:2.44.2")

Expand All @@ -82,6 +82,7 @@ dependencies {
testImplementation "org.testcontainers:testcontainers-minio:2.0.5"
testImplementation "org.awaitility:awaitility:4.3.0"
testImplementation "org.junit.platform:junit-platform-launcher"
testImplementation "org.apache.httpcomponents:httpclient:4.5.14"
ktlint("com.pinterest.ktlint:ktlint-cli:1.8.0") {
attributes {
attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL))
Expand Down
Loading
Loading