Zero trust databases

Passwordless access to PostgreSQL and MySQL

Octelium is a free and open source, self-hosted zero trust access platform. Its database modes authenticate to the upstream on the User's behalf, so nobody receives a password or a connection string, the upstream account is forced by configuration, and every request is authorized and recorded.

  • Free and open source
  • No passwords distributed
  • Works with ordinary clients
postgres.yaml
A PostgreSQL ServiceThe upstream password stays in the Cluster. Users connect without ever receiving it.
A PostgreSQL Service. The upstream password stays in the Cluster. Users connect without ever receiving it.
kind: Service
metadata:
  name: pg-prod
spec:
  mode: POSTGRES
  port: 5432
  config:
    upstream:
      url: postgres://10.0.4.21:5432
    postgres:
      user: analytics_ro
      database: analytics
      auth:
        password:
          fromSecret: pg-password
      sslMode: REQUIRE
A MySQL Service. The same model covers MySQL, with the upstream user and database forced by the Service.
kind: Service
metadata:
  name: mysql-prod
spec:
  mode: MYSQL
  port: 3306
  config:
    upstream:
      url: mysql://10.0.4.30:3306
    mysql:
      user: reporting
      database: sales
      auth:
        password:
          fromSecret: mysql-password
A Policy. Each connection is authorized on identity, context and the database user requested.
kind: Policy
metadata:
  name: analysts
spec:
  rules:
    - effect: ALLOW
      condition:
        all:
          of:
            - match: ctx.user.spec.type == "HUMAN"
            - match: '"analysts" in ctx.user.spec.groups'
            - match: ctx.session.status.type == "CLIENT"
pg-private.yaml
A database in a private networkA database behind NAT in any environment, reached over the tunnel through a stable route.
A database in a private network. A database behind NAT in any environment, reached over the tunnel through a stable route.
kind: Service
metadata:
  name: pg-prod
spec:
  mode: POSTGRES
  port: 5432
  config:
    upstream:
      url: postgres://10.0.4.21:5432
    postgres:
      user: analytics_ro
      database: analytics
      auth:
        password:
          fromSecret: pg-password
Served from a connected host. The upstream address is resolved from the side of a connected client, so no port is opened.
kind: Service
metadata:
  name: pg-edge
spec:
  mode: POSTGRES
  port: 5432
  config:
    upstream:
      url: postgres://pg.branch.local
      user: branch-gw
    postgres:
      user: app
      database: app
      auth:
        password:
          fromSecret: pg-edge-password
A hosted database. Protected public databases are covered too, with TLS required to the upstream.
kind: Service
metadata:
  name: pg-cloud
spec:
  mode: POSTGRES
  port: 5432
  config:
    upstream:
      url: postgres://db.provider.example:5432
    postgres:
      user: app
      database: main
      auth:
        password:
          fromSecret: pg-cloud-password
      sslMode: REQUIRE
policy-pg.yaml
PostgreSQLThe database user and database requested by the client are request attributes.
PostgreSQL. The database user and database requested by the client are request attributes.
kind: Policy
metadata:
  name: read-only-db
spec:
  rules:
    - effect: ALLOW
      condition:
        all:
          of:
            - match: ctx.request.postgres.connect.user
                == "analytics_ro"
            - match: '"analysts" in ctx.user.spec.groups'
MySQL. The same attributes exist for MySQL, including the requested database.
kind: Policy
metadata:
  name: reporting-only
spec:
  rules:
    - effect: ALLOW
      condition:
        all:
          of:
            - match: ctx.request.mysql.connect.user == "reporting"
            - match: ctx.request.mysql.connect.database == "sales"
            - match: '"analysts" in ctx.user.spec.groups'
Identity and context. Device posture, session type and group membership are attributes in the same expression.
kind: Policy
metadata:
  name: production-db
spec:
  rules:
    - effect: ALLOW
      condition:
        all:
          of:
            - match: ctx.user.spec.type == "HUMAN"
            - match: ctx.session.status.type == "CLIENT"
            - match: ctx.device.status.osType in ["MAC", "LINUX"]
            - match: '"dba" in ctx.user.spec.groups'
secret.sh
The password itselfThe password is created as a Secret and referenced by name from the Service.
The password itself. The password is created as a Secret and referenced by name from the Service.
octeliumctl create secret pg-password

# Or from a file
octeliumctl create secret \
  --file /path/to/password \
  pg-password

# Users never receive the value.
A different database per identity. Group membership selects which upstream, user, database and credential are used.
kind: Service
metadata:
  name: pg
spec:
  mode: POSTGRES
  port: 5432
  config:
    upstream:
      url: postgres://10.0.4.21:5432
    postgres:
      user: analytics_ro
      database: analytics
      auth:
        password:
          fromSecret: pg-ro-password
  dynamicConfig:
    configs:
      - name: writer
        upstream:
          url: postgres://10.0.4.21:5432
        postgres:
          user: app_rw
          database: analytics
          auth:
            password:
              fromSecret: pg-rw-password
    rules:
      - condition:
          match: '"dba" in ctx.user.spec.groups'
        configName: writer
TLS to the upstream. The connection to the database can be forced to TLS regardless of the client.
kind: Service
metadata:
  name: pg-prod
spec:
  mode: POSTGRES
  port: 5432
  config:
    upstream:
      url: postgres://10.0.4.21:5432
    postgres:
      user: analytics_ro
      database: analytics
      auth:
        password:
          fromSecret: pg-password
      sslMode: REQUIRE
idp.yaml
Secretless workload identityJobs authenticate with OIDC assertions issued by the platform running them.
Secretless workload identity. Jobs authenticate with OIDC assertions issued by the platform running them.
kind: IdentityProvider
metadata:
  name: github-actions
spec:
  oidcIdentityToken:
    issuerURL: https://token.actions.githubusercontent.com
    audience: https://example.com
A workload User. A migration job or an ETL run is a User with its own groups and audit trail.
kind: User
metadata:
  name: migrations
spec:
  type: WORKLOAD
  groups:
    - ci
An agent with read-only reach. An AI agent gets its own identity and its own upstream account, never a password.
kind: Policy
metadata:
  name: agent-read-only
spec:
  rules:
    - effect: ALLOW
      condition:
        all:
          of:
            - match: ctx.user.spec.type == "WORKLOAD"
            - match: '"agents" in ctx.user.spec.groups'
            - match: ctx.request.postgres.connect.database
                == "analytics"
Visibility and auditing

Queries attributed to a person, not an account

Every request produces an AccessLog identifying the User, Session and Device alongside the application-layer detail of the database request, emitted in real time to your OpenTelemetry OTLP receivers.

Read about visibility and auditing
AccessLogOTLP
ALLOWlinus@acme.compg-prod.dbPOSTGRESquery: select id, email from users limit 50
ALLOWci-runnerprod-k8sKUBERNETESverb=list resource=pods namespace=production
DENYagent-07tools-mcpMCPtools/call name=transfer policy=mcp-tools
ALLOWjane@acme.combastionSSHsession recorded, upstream user=deploy
ALLOWagent-07openaiLLMCHAT_COMPLETIONS model=gpt-5-mini tokens=1284
octeliumctl
octeliumctl apply ./cluster
$ octeliumctl apply ./cluster
Secret: pg-password Updated
Secret: mysql-password Updated
IdentityProvider: okta Updated
Policy: read-only-db Created
Group: analysts Created
Group: dba Updated
Service: pg-prod.db Created
Service: mysql-prod.db Created
Service: pg-cloud.db Created
Namespace: db Created
Cluster Core resources successfully applied
 7 resources created
 3 resources updated

 ClusterConfig updated
Database access

What database access has to solve, and what solves each part

Connecting to a database is easy. These are the problems that appear once more than a handful of people and jobs need to, next to the mechanism in an Octelium Cluster that handles each one.

  1. 01

    Getting the password to the right people

    Nothing is distributed. The upstream password is stored in the Cluster as a Secret and used by the identity-aware proxy to authenticate to the database once a connection has been authorized.

    Read more
  2. 02

    Taking it away again

    Revoking access is a Policy change or a Session revocation rather than rotating a password on the database and then chasing every laptop, script and connection string that held a copy of it.

    Read more
  3. 03

    Shared accounts nobody can attribute

    The Service forces the upstream database user and database regardless of what the client asks for, so a shared account stops being a gap in the audit trail: each connection is still attributed to the Octelium User behind it.

    Read more
  4. 04

    Giving different people different rights

    Dynamic configuration maps groups to different upstream accounts, databases and credentials on the same Service, so an analyst lands on a read-only account and a database administrator on a writing one.

    Read more
  5. 05

    Reaching databases that are not routable

    A Service upstream can be an address that is only reachable from the side of a connected host, so a database in another private network, a branch office or a customer environment needs no inbound port.

    Read more
  6. 06

    Hosted and serverless databases

    Protected public databases are covered by the same model, with TLS to the upstream forced by configuration, which brings managed providers under the same identity and audit story as internal ones.

    Read more
  7. 07

    Changing the client tooling

    There is nothing to change. People use psql, mysql and ordinary drivers against a stable private name, and applications use their normal connection libraries.

    Read more
  8. 08

    Being able to show what was run

    Every request produces an identity-aware AccessLog with the application-layer detail of the database request, streamed in real time over OpenTelemetry to your own log management and SIEM providers.

    Read more

A password vault shortens how long a credential lives in one place. It still hands the credential to whoever asks, and the database still sees one account. Injecting it at the proxy changes both.

Questions

Frequently asked

Which databases are supported?
PostgreSQL and MySQL have dedicated application-layer modes, which give per-request authorization, secretless upstream credentials, dynamic configuration and protocol-aware auditing. That covers wire-compatible databases and managed providers as well. Anything else that speaks a TCP protocol can be reached through the generic TCP mode, including databases protected by mutual TLS, though authorization there happens at connection time.
How do people connect?
With their normal client. The Service is reached at a stable private name over the client-based tunnel, and the proxy authenticates to the database on the User's behalf. Nothing about psql, mysql, a GUI client or an application driver changes, and no password is entered. For MySQL Services, authorized Users currently connect with an empty password value.
Can different people get different privileges on the same database?
Yes. The Service forces an upstream database user and database regardless of what the client asks for, and dynamic configuration selects which upstream, account and credential are used per request from identity and context. One Service can therefore map an analyst group to a read-only account and a database administrator group to a writing one.
What about hosted databases?
They are covered by the same model. The upstream is simply a public address, TLS to the upstream can be required with the sslMode field, and the provider password stays in the Cluster as a Secret. That brings managed and serverless providers under the same identity, policy and audit story as internal databases.
Is the traffic recorded?
Yes. Each request produces an identity-aware AccessLog with the application-layer detail of the database request, streamed in real time over OpenTelemetry to your own log management and SIEM providers, attributed to the Octelium User, Session and Device behind it rather than to a shared database account.
Is Octelium free and open source?
Yes. Octelium is free and open source and is designed for single-tenant self-hosting. There is no proprietary cloud-based control plane, and it is not a limited edition of a separate paid product. An enterprise package is available for organizations that need capabilities such as a web console, SCIM provisioning and secret encryption at rest, and it is free for personal, homelab and evaluation use.
Get started

Deploy Octelium on your own infrastructure in minutes

Free and open source. Self-hosted. No vendor lock-in.