Skip to content

ak config setup fails with UnknownIssuer for private CA due to reqwest 0.12 rustls-tls #1533

Description

@clementcohen

ak config setup fails with UnknownIssuer for private CA because device flow uses reqwest 0.12 rustls-tls

Description

ak config setup fails to connect to an authentik instance whose HTTPS certificate is issued by a private CA installed in the Linux system trust store.

The same certificate chain is successfully validated by:

  • OpenSSL
  • curl
  • reqwest 0.13 using rustls-platform-verifier

However, the OAuth device flow used by ak fails with:

invalid peer certificate: UnknownIssuer

I was able to reproduce the issue independently of authentik using reqwest 0.12.28 with the rustls-tls feature.

Environment

  • authentik-cli: 0.62.0
  • OS: Ubuntu 26.04
  • authentik URL: https://auth.example.internal
  • Private PKI with:
    • private Root CA installed in the system trust store
    • intermediate issuing CA
    • server certificate issued by the intermediate
  • Server sends the leaf + intermediate certificates.

The private Root CA is correctly installed in /etc/ssl/certs and /etc/ssl/certs/ca-certificates.crt.

authentik CLI error

ak -v config setup \
  --authentik-url https://auth.example.internal \
  --profile test

Result:

Error running command: device flow setup failed

Caused by:
  0: Network error: error sending request for url (https://auth.example.internal/application/o/device/)
  1: error sending request for url (https://auth.example.internal/application/o/device/)
  2: client error (Connect)
  3: invalid peer certificate: UnknownIssuer

Location:
    ak-cli/src/setup/mod.rs:88:25

Setting SSL_CERT_FILE does not help:

SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt \
ak -v config setup \
  --authentik-url https://auth.example.internal \
  --profile test

It produces the same UnknownIssuer error.

System TLS validation succeeds

OpenSSL successfully validates the server:

openssl s_client \
  -connect auth.example.internal:443 \
  -servername auth.example.internal

Result:

Verify return code: 0 (ok)

curl also successfully connects to the authentik API over HTTPS.

Minimal reqwest 0.13 test succeeds

The following application succeeds using reqwest 0.13:

[package]
name = "rustls-test"
version = "0.1.0"
edition = "2024"

[dependencies]
reqwest = { version = "0.13", default-features = false, features = ["rustls"] }
tokio = { version = "1", features = ["full"] }
#[tokio::main]
async fn main() {
    let client = reqwest::Client::new();

    match client
        .get("https://auth.example.internal/application/o/device/")
        .send()
        .await
    {
        Ok(r) => println!("OK: {}", r.status()),
        Err(e) => {
            println!("ERROR: {e:#}");
            println!("DEBUG: {e:?}");
        }
    }
}

Result with:

reqwest 0.13.5
rustls-platform-verifier 0.7.0
rustls-webpki 0.103.15

is:

OK: 405 Method Not Allowed

The HTTP 405 is expected because the device endpoint requires POST. The important point is that TLS validation succeeds.

Minimal reqwest 0.12.28 reproduction

Changing only reqwest to:

reqwest = {
    version = "=0.12.28",
    default-features = false,
    features = ["rustls-tls"]
}

reproduces the exact TLS failure:

ERROR: error sending request for url (https://auth.example.internal/application/o/device/)

DEBUG: reqwest::Error {
    kind: Request,
    ...
    source: hyper_util::client::legacy::Error(
        Connect,
        ...
        InvalidCertificate(UnknownIssuer)
    )
}

During compilation this configuration pulls in webpki-roots.

Suspected cause

The OAuth device flow used by ak 0.62.0 depends on oauth-device-flows, which uses reqwest 0.12.x.

With reqwest 0.12, the rustls-tls feature uses the WebPKI root set rather than the operating system's native CA store.

Consequently, public CAs work, but private enterprise/internal CAs installed in the Linux system trust store are not trusted by this HTTP client.

This also explains why setting:

SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt

does not resolve the issue.

Expected behavior

ak should honor the operating system's trusted CA store, so an authentik instance using a private enterprise CA trusted by the host can be used with:

ak config setup --authentik-url https://auth.example.internal

Possible fix

For the reqwest 0.12 dependency used by the device flow, using native roots instead of WebPKI roots should allow private system CAs, for example:

features = ["rustls-tls-native-roots"]

Alternatively, migrating the device-flow HTTP client to reqwest 0.13 / rustls-platform-verifier should provide system trust-store integration.

The reqwest 0.13 test above confirms that the same private CA and server certificate work correctly through that stack.

Additional validation

The server certificate chain has also been explicitly verified with OpenSSL using the private Root CA and intermediate:

openssl verify \
  -verbose \
  -purpose sslserver \
  -CAfile root-ca.pem \
  -untrusted intermediate-ca.pem \
  server.pem

Result:

server.pem: OK

Therefore the issue does not appear to be caused by an invalid server certificate chain.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions