Skip to main content
Version: Next

Quickstart

Two ways to try tresor-server in your own infrastructure:

  • The demo - one docker compose up on a laptop or a VM: the service, Keycloak with a realm, people, a service and a sample secret. Five minutes, nothing to register.
  • Your own identity provider - one docker run against Entra ID or Keycloak, your people and roles.

Either way you are done when DuckDB answers:

INSTALL tresor FROM community;
LOAD tresor;
ATTACH 'tresor:<host>' AS corp;
FROM corp.whoami();

The demo: five minutes with Keycloak​

What you need first​

WhatExampleNotes
Docker with ComposeDocker Desktop, or Docker Engine 24+docker compose version
Free ports on 127.0.0.18443 (the service), 18480 (Keycloak)the demo listens on this machine's loopback only
DuckDB 2.0.0duckdb CLI, Python duckdb==2.0.0tresor installs from DuckDB's community extensions
The repositorygit clone https://github.com/hugr-lab/tresor-serverthe recipe is deploy/docker

The demo serves plain http on loopback, as the service allows only there: run DuckDB on the same machine. On a VM, reach it through an SSH tunnel (ssh -L 8443:127.0.0.1:8443 -L 18480:127.0.0.1:18480 <vm>); for a host other machines reach, use your own identity provider or Docker on one host, with TLS.

Steps​

cd tresor-server/deploy/docker
docker compose up -d # the service, Keycloak, a realm; the first run makes every password
docker compose --profile seed run --rm seed # a sample secret (s3, fake values) granted to the analysts
docker compose run --rm init passwords # who signs in with what
  • anna is an administrator (secrets_admin): she manages secrets and grants in the console, http://127.0.0.1:8443/ui/.
  • jonas is an analyst (analysts): his DuckDB receives what is granted to the analysts.
  • etl and node are services (client credentials).

Nothing is a default anyone else knows: the passwords, the KEK and the services' secrets are made on the first run, in a Docker volume. docker compose --profile seed down -v removes everything.

You're done when​

DuckDB, signed in as jonas (a browser opens, or a device code is shown; Keycloak asks once to grant access to duckdb):

INSTALL tresor FROM community;
LOAD tresor;
ATTACH 'tresor:127.0.0.1:8443' AS corp (INSECURE_HTTP true);
FROM corp.whoami(); -- jonas, role:analysts
FROM corp.secrets(); -- lake, warehouse
FROM duckdb_secrets(); -- lake's secret value redacted

INSECURE_HTTP is for this loopback demo only. A service signs in with its client's credentials:

CREATE SECRET node_login (TYPE tresor, SCOPE 'tresor:127.0.0.1:8443', FLOW 'client_credentials',
CLIENT_ID 'node', CLIENT_SECRET '<node's secret>', ISSUER 'http://127.0.0.1:18480/realms/tresor');
ATTACH 'tresor:127.0.0.1:8443' AS corp (INSECURE_HTTP true);

Then, in the console as anna: create a secret, grant it to role:analysts, and see it in jonas's corp.secrets() (the console).

Your own identity provider​

One container, your identity provider, SQLite and a local key - a pilot for a team. For production, the same service with its state in PostgreSQL and its key in a KMS: Docker on one host, Kubernetes, Azure, AWS, GCP.

What you need first​

WhatExampleWhere to find it
The issuerhttps://login.microsoftonline.com/<tenant>/v2.0; https://sso.corp.example/realms/corpEntra: the tenant id (Overview); Keycloak: the realm's URL
The service's API (its tokens' audience)Entra: the API app's client id; Keycloak: duckdb-secretsEntra: register an app for the service, Expose an API with a scope access_as_user, its manifest requestedAccessTokenVersion: 2. Keycloak: an audience mapper adding duckdb-secrets to the people's client's tokens
The people's public clientEntra: a public client app id; Keycloak: duckdba public client (no secret), redirect http://127.0.0.1 (loopback, any port, for the CLI), device code allowed
Roles in the tokenEntra: app roles - claim roles; Keycloak: realm roles - realm_access.rolesthe role that makes an administrator, e.g. secrets_admin
How a service's token is told apartEntra: idtyp equals app; Keycloak: the client_id claimfor services that use DuckDB too (client credentials)
The host name and TLSsecrets.corp.example, a certificate DuckDB trustsa certificate and key in files, or TLS at a reverse proxy in front (tls.offload)
Where state livesa SQLite file in a volumePostgreSQL or SQL Server for several replicas: State
The key (KEK)a 32-byte file for a pilota KMS for production: Encryption
A vault, optionallyref+vault://…, ref+azkv://…to leave values where they are: References

tresor's Entra guide walks through the Entra registrations.

Steps​

mkdir tresor && cd tresor
openssl rand -base64 32 > kek # back it up: the state is sealed under it
cp /path/to/secrets.corp.example.crt tls.crt && cp /path/to/secrets.corp.example.key tls.key
sudo chown 65532:65532 kek tls.key && sudo chmod 400 kek tls.key # the service's user (nonroot) reads them, no one else
docker volume create tresor-state
docker run --rm -v tresor-state:/state alpine chown 65532 /state # the service runs as nonroot

docker run -d --name tresor-server --restart unless-stopped -p 443:8443 \
-v tresor-state:/var/lib/tresor -v "$PWD":/etc/tresor:ro \
-e TRESOR_LISTEN=0.0.0.0:8443 \
-e TRESOR_PUBLIC_URL=https://secrets.corp.example \
-e TRESOR_TLS__CERT=/etc/tresor/tls.crt -e TRESOR_TLS__KEY=/etc/tresor/tls.key \
-e TRESOR_STATE__KIND=sqlite -e TRESOR_STATE__PATH=/var/lib/tresor/tresor.db \
-e TRESOR_KEYS__KIND=local -e TRESOR_KEYS__KEY_FILE=/etc/tresor/kek \
-e TRESOR_ISSUERS="[{issuer: 'https://login.microsoftonline.com/<tenant>/v2.0', audience: '<api client id>',
client_id: '<public client id>', scopes: [openid, offline_access, 'api://<api client id>/access_as_user'],
human_flows: [authorization_code, device_code], service_flows: [client_credentials],
roles_claim: roles, service: {claim: idtyp, equals: app, client_claim: azp}}]" \
-e TRESOR_POLICY__ADMINS='[role:secrets_admin]' \
ghcr.io/hugr-lab/tresor-server:v0.8.2

For Keycloak, the issuer is the realm's URL and:

-e TRESOR_ISSUERS="[{issuer: 'https://sso.corp.example/realms/corp', audience: duckdb-secrets, client_id: duckdb,
scopes: [openid], human_flows: [authorization_code, device_code], service_flows: [client_credentials],
roles_claim: realm_access.roles, ignore_roles: [offline_access, uma_authorization, 'default-roles-*'],
service: {claim: client_id}}]" \
  • Check it: curl https://secrets.corp.example/readyz - every check ok (the state, the key, the issuer); docker logs tresor-server names a failing one, never a value.
  • A reverse proxy in front (TLS there): -e TRESOR_TLS__OFFLOAD=true, no TLS__CERT/TLS__KEY, publish -p 127.0.0.1:8080:8443 to the proxy only. Docker on one host has one with Caddy.
  • Every setting can come from the environment or a file: Configuration.

You're done when​

INSTALL tresor FROM community;
LOAD tresor;
ATTACH 'tresor:secrets.corp.example' AS corp; -- your company login
FROM corp.whoami(); -- you, and your roles

An administrator then creates the first secret - in the console at https://secrets.corp.example/ui/, or from DuckDB - and grants it to a role:

CREATE PERSISTENT SECRET lake IN corp (TYPE s3, KEY_ID 'AKIA…', SECRET '…', SCOPE 's3://lake');
CALL corp.grant_secret('lake', 'role:analysts', ['use']);