Skip to content

Conversation

@staaldraad
Copy link
Member

What kind of change does this PR introduce?

Feature

Introduces support for Just-in-time (JIT) access via a Personal Access Token (PAT) or JWT, which is validated against a remote API. This is implemented as a separate tenant feature, since it is not a standard postgres feature. The upstream tenant must also be configured for JIT, which requires a PAM to be installed and configured in the pg_hba.

With JIT in use, a disconnect happens between what the server expects for auth (pg_hba.conf) and what pg_authid stores for user auth. In most cases pg_authid will have the credentials saved as scram-sha-256, but with the use of PAM for authentication, the database uses AuthenticationCleartext_password. For this reason, the Supavisor must be made aware of the tenants changed auth, and we use a new use_jit configuration value for the tenant. When this is active, the pooler will use AuthenticationCleartext_password, and support either logging in with the valid user password (which will be checked against the scram-sha-256 retrieved from pg_authid) or a PAT/JWT that is validated against the upstream API server configured via jit_api_url.

Cached credentials keep working as before, except in the case of the PAT/JWT, the check is always performed against the upstream API server. This ensures that any revocation of JIT access is respected, alongside the expiration/revocation of the auth tokens, something that happens outside of the database.

What is the current behavior?

Doesn't support JIT.

What is the new behavior?

Supports JIT, allowing login with:

JWT:

psql 'postgresql://postgres.dev_tenant2:eyJhbGciOiJSUzI1NiIsImtpZCI6IjcyYjY2NjA1IiwidHlwIjoiSldUIn0.eyJhYWwiOiJhYWwyIiwiYW1yIjpbeyJtZXRob2QiOiJ0b3RwIiwidGltZXN0YW1wIjOnt9fQ.XbOq_XWg@localhost:6543/postgres'

PAT:

psql 'postgresql://postgres.dev_tenant2:sbp_39wBdIEXAMPLESdMIg@localhost:6543/postgres'

Password:

psql 'postgresql://postgres.dev_tenant2:ExAmPlePA$$@localhost:6543/postgres'

Additional context

In the case of JWT/PAT, there is double lookup to the API that happens on first connection (new pool). First by pooler, and then by the upstream tenant. After that, the API lookup only happens in pooler for every new attempt to join the pool with a PAT/JWT.

The API server is expected to respond with 200 OK and a list of roles the token is allowed to assume, or return permission denied if the user is not authorized for JIT or there are any upstream errors.

Screen.Recording.2025-08-12.at.17.11.12.mov

@staaldraad staaldraad changed the title Etienne/sec 488 support jit access in supavisor WIP feat: sec 488 support jit access in supavisor Aug 12, 2025
@staaldraad staaldraad changed the title WIP feat: sec 488 support jit access in supavisor feat: sec 488 support jit access in supavisor Aug 19, 2025
@staaldraad staaldraad marked this pull request as ready for review August 19, 2025 08:04
@staaldraad staaldraad requested a review from a team as a code owner August 19, 2025 08:04
@staaldraad staaldraad force-pushed the etienne/sec-488-support-jit-access-in-supavisor branch 2 times, most recently from d409995 to c76bc91 Compare August 22, 2025 19:03
@staaldraad staaldraad force-pushed the etienne/sec-488-support-jit-access-in-supavisor branch from c76bc91 to d03457e Compare August 23, 2025 09:59
@staaldraad staaldraad force-pushed the etienne/sec-488-support-jit-access-in-supavisor branch 2 times, most recently from 4ac614d to c1f6cfe Compare September 15, 2025 13:41
@staaldraad staaldraad force-pushed the etienne/sec-488-support-jit-access-in-supavisor branch from c1f6cfe to 709b81a Compare December 4, 2025 11:33
@staaldraad
Copy link
Member Author

@v0idpwn 👋🏼 I've come back to this project. The PR has been rebased and updated to work with the latest changes, what do you need from me to move this forward?

@v0idpwn
Copy link
Member

v0idpwn commented Dec 4, 2025

Hi, @staaldraad. I'd say this was blocked by #744, but now that it is in, we can explore it again.

@staaldraad
Copy link
Member Author

Hi, @staaldraad. I'd say this was blocked by #744, but now that it is in, we can explore it again.

amazing 🙏🏼 yeah it took a bit to get there pieces back into the right place and I'm sure there are some optimisations to be made. But the changes from #744 are great and definitely simplify and improve things 💪🏼

Only using data.auth.password would reuse the password for the
is_manager user when trying to authenticate against the upstream.

The first request, from the is_manager to the upstream should use the
password, while the subsequent connection with the authenticating user
should use secret.

feat: support e2e password flow

chore: check password against auth_query

chore: set client_key

Sets the client_key when using password auth (jit) and the incomming
password is a valid password that matches the scram-sha-256. This allows
supporting the case where the tenant has switched back to scram-sha-256
but pooler is still checking for cleartext password.

chore: todo - api implementation

feat: support api lookup

chore: ensure user secret caching works with jit

chore: add jit expire checks

chore: cleanup credo warnings

chore: store client_key for cleartext auth exchange

If the cleartext auth exchange was for a normal password (not PAT/JWT),
we will have a valid client_key that was calculated for the
scram-sha-256. Ensure this is always present in secrets, along with the
clear password if we have it. This allows for the case where the
upstream server either stops using JIT and reverts to scram-sha-256. Or
has a pg_hba entry for the specific user to use scram-sha-256 rather
than pam. This allows both JIT users and non-JIT users to exist in the
same tenant.

chore: support md5 on JIT, lazy eval secrets

fix: migration

fix: terminate does not need additional message

Update lib/supavisor/helpers.ex

Co-authored-by: felipe stival <14948182+v0idpwn@users.noreply.github.com>

chore: start adding more tests

test: add jit api tester

chore: use simplified API with role, rhost
@staaldraad staaldraad force-pushed the etienne/sec-488-support-jit-access-in-supavisor branch from 709b81a to 541c5ca Compare December 5, 2025 07:15
Comment on lines +626 to +627
{:ok, {ip, _port}} = :inet.peername(socket)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this won't work with ssl sockets, but we have a function in the codebase that handles both

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants