Skip to content

[BUG] OIDC with on-prem Identity Provider fails due to self-signed certificate despite trust-configuration #243

@Visparu

Description

@Visparu

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

I successfully set up a basic installation inside a Docker Swarm with a single replica (for testing purposes) and no user federation or external authentication. There were no issues at this point and I was able to log in and use the application in every intended way.

However, my department uses Keycloak as a federation system to provide Single Sign-On. So I created a client within Keycloak and configured it as described in the https://www.bookstackapp.com/docs/admin/oidc-auth/ guide.

When trying to log in, the "Single Sign-On" button was there as expected. When clicking on it though, I received an error:

OIDC Discovery Error: HTTP request failed during discovery with error:
cURL error 60: SSL certificate problem: unable to get local issuer certificate
(see https://curl.haxx.se/libcurl/c/libcurl-errors.html)
for https://keycloak.example.com/auth/realms/example-realm/.well-known/openid-configuration

Of course. We use internal certificates with no public trust. Usually not a hard thing to fix, so I tried around for two hours but I just got absolutely nowhere. The application log doesn't say anything.

I tried the following approaches:

  1. Copying our root and intermediate CA certs into the container (at /usr/local/share/ca-certificates), exec-ing into it and running update-ca-certificates (No error)
  2. Injecting the CA file as a volume and pointing the CURL_CA_BUNDLE in the docker-compose.yml towards it
  3. Same thing with the SSL_CERT_PATH and SSL_CERT_DIR variables (using the parent dir for SSL_CERT_DIR, of course)
  4. Desperately trying REQUESTS_CA_BUNDLE, even though it's Python-specific
  5. Repeating approach 1 but in the Dockerfile and building my own image from there
  6. Setting all of the mentioned env variables in the Dockerfile and rebuilding again
  7. Manually appending the PEM certificate of the root CA to the /etc/ssl/certs/ca-certificates.crt file

After most of these options (pretty much just excluding the REQUESTS_CA_BUNDLE attempt), a normal curl from within the container can reach and successfully verify the /.well-known/openid-configuration endpoint of my Keycloak realm, but Bookstack still fails.

I even thought to just not use the OIDC discovery method but naturally, that doesn't work either because while this skips step one of the OIDC communication, there are like 7 more steps that will all fail for the same reason.

So, with all that said, I am really at the end of my wisdom here. I hope I'm just not seeing some wildly obvious thing here. Please help me.

Expected Behavior

I would like to be able to tell Bookstack to trust my internal, private CA. That way, I would be able to use an OIDC Identity Provider which uses said private, untrusted certificate.

Steps To Reproduce

  1. Environment: Docker Swarm OR single-host docker compose, with OIDC configuration towards an identity provider offering a private certificate.
  2. Config: Following https://www.bookstackapp.com/docs/admin/oidc-auth/ with OIDC_ISSUER_DISCOVER=true. I cannot show the specific Keycloak URIs for compliance reasons, unfortunately. However, judging by the fact that the TLS handshake does happen, I assume everything until then is set up correctly.
  3. Run: The container, try to log in via SSO.
  4. See error:
    OIDC Discovery Error: HTTP request failed during discovery with error:
    cURL error 60: SSL certificate problem: unable to get local issuer certificate
    (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)
    for https://keycloak.example.com/auth/realms/example-realm/.well-known/openid-configuration
    

Environment

- OS: Ubuntu 22.04 (Swarm mode) OR Ubuntu 24.04 (WSL/Docker-Compose) - both fail the same way
- How docker service was installed: In both cases with `apt`, pretty much following https://docs.docker.com/engine/install/ubuntu/. There are some additional setup steps like proxies and private certs, but nothing that should impact a running container.

CPU architecture

x86-64

Docker creation

services:
  ui:
    image: linuxserver/bookstack:latest

    deploy:
      mode: replicated
      replicas: 1
      restart_policy:
        max_attempts: 3

    environment:
      TZ: Europe/Berlin

      APP_URL: https://bookstack-test.example.com
      APP_KEY: ${BOOKSTACK_APP_KEY}

      AUTH_METHOD: oidc
      AUTH_AUTO_INITIATE: "false"
      OIDC_NAME: Example SSO
      OIDC_DISPLAY_NAME_CLAIMS: given_name|family_name
      OIDC_CLIENT_ID: example-bookstack-test
      OIDC_CLIENT_SECRET: ${BOOKSTACK_CLIENT_SECRET}
      OIDC_ISSUER: https://keycloak.example.com/auth/realms/example-realm
      OIDC_END_SESSION_ENDPOINT: "true"
      OIDC_ISSUER_DISCOVER: "true"

      DB_HOST: mariadb.example.com
      DB_PORT: 3306
      DB_USERNAME: bookstack_example_test
      DB_PASSWORD: ${DB_PASS}
      DB_DATABASE: bookstack_example_test

    volumes:
      - data:/config

    hostname: bookstack-example-test
    networks:
      rev-proxy:
        aliases:
          - bookstack-example-test
    ports:
      - "6875:80"

volumes:
  data:
    driver_opts:
      type: nfs
      o: "addr=nfs.example.com,nolock,soft,rw"
      device: ":/nfs/bookstack-example/test"

networks:
  rev-proxy:
    name: rev-proxy
    external: true

OR Docker-Compose with and without customized Dockerfile:

services:
  ui:
    image: linuxserver/bookstack:latest

    environment:
      TZ: Europe/Berlin

      APP_URL: http://localhost:8080
      APP_KEY: ${BOOKSTACK_APP_KEY}

      AUTH_METHOD: oidc
      AUTH_AUTO_INITIATE: "false"
      OIDC_NAME: KURZ SSO
      OIDC_DISPLAY_NAME_CLAIMS: given_name|family_name
      OIDC_CLIENT_ID: example-bookstack-test
      OIDC_CLIENT_SECRET: ${BOOKSTACK_CLIENT_SECRET}
      OIDC_ISSUER: https://keycloak.example.com/auth/realms/example-realm
      OIDC_END_SESSION_ENDPOINT: "true"
      OIDC_ISSUER_DISCOVER: "true"

      DB_HOST: mariadb.example.com
      DB_PORT: 3306
      DB_USERNAME: bookstack_example_test
      DB_PASSWORD: ${DB_PASS}
      DB_DATABASE: bookstack_example_test

    volumes:
      - ./config:/config

    ports:
      - "8080:80"

This is the addition to the Dockerfile I used in some of my attempts:

COPY example-ca.crt /usr/local/share/ca-certificates/example-ca.crt
RUN update-ca-certificates && \
    cat /usr/local/share/ca-certificates/example-ca.crt >> /etc/ssl/certs/ca-certificates.crt

Although I also used only the update-ca-certificates line, and also only the manual addition to /etc/ssl/certs/ca-certificates.crt, basically all permutations.

I had to redact a lot of things in here. If anything is not entirely congruent, I'm sorry. I'll rectify it as soon as possible.

Container logs

ui-1  | [migrations] started
ui-1  | [migrations] 01-nginx-site-confs-default: skipped
ui-1  | [migrations] 02-default-location: skipped
ui-1  | [migrations] done
ui-1  | usermod: no changes
ui-1  | ───────────────────────────────────────
ui-1  |   _____ __ __ _____ _____ _____ _____
ui-1  |  |     |  |  |   __|_   _|     |     |
ui-1  |  |   --|  |  |__   | | | |  |  | | | |
ui-1  |  |_____|_____|_____| |_| |_____|_|_|_|
ui-1  |        _____ __ __ _ __    ____
ui-1  |       | __  |  |  | |  |  |    \
ui-1  |       | __ -|  |  | |  |__|  |  |
ui-1  |       |_____|_____|_|_____|____/
ui-1  |
ui-1  |   Based on images from linuxserver.io
ui-1  | ───────────────────────────────────────
ui-1  |
ui-1  | To support LSIO projects visit:
ui-1  | https://www.linuxserver.io/donate/
ui-1  |
ui-1  | ───────────────────────────────────────
ui-1  | GID/UID
ui-1  | ───────────────────────────────────────
ui-1  |
ui-1  | User UID:    911
ui-1  | User GID:    911
ui-1  | ───────────────────────────────────────
ui-1  | Linuxserver.io version:
ui-1  | Build-date:
ui-1  | ───────────────────────────────────────
ui-1  |
ui-1  | using keys found in /config/keys
ui-1  | Waiting for DB to be available
ui-1  |
ui-1  |    INFO  Nothing to migrate.
ui-1  |
ui-1  | [custom-init] No custom files found, skipping...
ui-1  | [ls.io-init] done.

(My own addition: This really is all there is. When trying to use the SSO, there are no additional logs.)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions