[PoC] Delegating pg_ident to a third party - Mailing list pgsql-hackers
From | Jacob Champion |
---|---|
Subject | [PoC] Delegating pg_ident to a third party |
Date | |
Msg-id | 4d04f1e68aabb70000aa9a3c7e6a00d438c0a511.camel@vmware.com Whole thread Raw |
Responses |
Re: [PoC] Delegating pg_ident to a third party
|
List | pgsql-hackers |
Hi all, In keeping with my theme of expanding the authentication/authorization options for the server, attached is an experimental patchset that lets Postgres determine an authenticated user's allowed roles by querying an LDAP server, and enables SASL binding for those queries. This lets you delegate pieces of pg_ident.conf to a central server, so that you don't have to run any synchronization scripts (or deal with associated staleness problems, repeated load on the LDAP deployment, etc.). And it lets you make those queries with a client certificate instead of a bind password, or at the very least protect your bind password with some SCRAM crypto. You don't have to use the LDAP auth method for this to work; you can combine it with Kerberos or certs or any auth method that already supports pg_ident. The target users, in my mind, are admins who are already using an auth method with user maps, but have many deployments and want easier control over granting and revoking database access from one location. This won't help you so much if you need to have exactly one role per user -- there's no logic to automatically create roles, so it can't fully replace the existing synchronization scripts that are out there. But if all you need is "X, Y, and Z are allowed to log in as guest, and A and B may connect as admins", then this is meant to simplify your life. This is a smaller step than my previous proof-of-concept, which handled fully federated authentication and authorization via an OAuth provider [1], and it should be a nice companion to my patch that adds user mappings to the LDAP auth method [2], though I haven't tried them together yet. (I've also been thinking about pulling group membership information out of Kerberos authorization data, for those of you using Active Directory. Things for later.) = How-To = If you want to try it out -- on a non-production system please -- take a look at the test suite in src/test/ldap, which has been filled out with some example usage. The core features are the "ldapmap" HBA option (which you would use instead of "map" in your existing HBA) and the "ldapsaslmechs" HBA option, which you can set to a list of SASL mechanisms that you will accept. (The list of supported mechanisms is determined by both systems' LDAP and SASL libraries, not by Postgres.) The tricky part is writing the pg_ident line correctly, because it's currently not a very good user experience. The query is in the form of an LDAP URL. It needs to return exactly one entry for the user being authorized; the attribute values contained in that entry will be interpreted as the list of roles that the user is allowed to connect as. Regex matching and substitution are supported as they are for regular maps. Here's a sample: pg_ident.conf: myldapmap /^(.*)$ ldap://example.com/dc=example,dc=com?postgresRole?sub?(uid=\1) pg_hba.conf: hostssl all all all cert ldapmap=myldapmap ldaptls=1 ldapsaslmechs=scram-sha-1 ldapbinddn=admin ldapbindpasswd=secret This particular setup can be described as follows: - Clients must use client certificates to authenticate to Postgres. - Once the certificate is verified, Postgres will connect to the LDAP server at example.com, issue StartTLS, and begin a SCRAM-SHA-1 exchange using the bind username and password (admin/secret). - Once that completes, Postgres will issue a query for the LDAP user that has a uid matching the CN of the client certificate. (If more than one user matches, authorization fails.) - The client's PGUSER will be compared with the list of postgresRole attributes belonging to that LDAP user, and if one matches, authorization succeeds. = Areas for Improvement = I think it would be nice to support LDAP group membership in addition to object attributes. Settings for the LDAP connection are currently spread between pg_hba, pg_ident, and environment variables like LDAPTLS_CERT. I made the situation worse by allowing the pg_ident query to contain a scheme, host, and port. That makes it seem like you could send different users to different LDAP servers, but since they would all have to share exactly the same TLS settings anyway, I think this was a mistake on my part. That mistake aside, I think the current URL query syntax is powerful but unintuitive. I would rather see that as an option for power users, and let other people just specify the user filter and role attribute separately. And there needs to be more logging around the feature, to help debug problems. Regex substitution of user-controlled data into an LDAP query is perilous, and I don't like it. For now I have restricted the allowed characters as a first mitigation. Is it safe to use listen_addresses in the test suite, as I have done, as long as the HBA requires authentication? Or is that reopening a security hole? I seem to recall discussion on this but my search-fu has failed me. There's a lot of code duplication in the current patchset that would need to be undone. ...and more; see TODOs in the patches if you're interested. = Patch Roadmap = - 0001 fixes error messages that are printed when ldap_url_parse() fails. Since the pg_ident queries use LDAP URLs, and it's easy to get them wrong, that fix is particularly important for this patchset. But I think it could potentially be applied separately. - 0002 implements the "ldapmap" HBA option and enables the ldaptls, ldapbinddn, and ldapbindpasswd options for it. It also adds corresponding tests to the LDAP suite. - 0003 tests the use of client certificates via LDAP environment variables. (This is already supported today but I didn't see any coverage, which will be important for the last patch.) - 0004 implements the "ldapsaslmechs" HBA option and adds enough SASL support for at least the EXTERNAL and SCRAM-* mechanisms. Others may work but I haven't tested them. This feature is available only if you have the <sasl/sasl.h> header on your system at build time. WDYT? (My responses here will be slower than usual. Hope you all have a great end to the year!) --Jacob [1] https://www.postgresql.org/message-id/flat/d1b467a78e0e36ed85a09adf979d04cf124a9d4b.camel@vmware.com [2] https://www.postgresql.org/message-id/flat/1a61806047c536e7528b943d0cfe12608118ca31.camel@vmware.com
Attachment
pgsql-hackers by date: