Thread: allowing "map" for password auth methods with clientcert=verify-full

allowing "map" for password auth methods with clientcert=verify-full

From
"Jonathan S. Katz"
Date:
Hi,

Since PostgreSQL 12 (0516c61b756e39) we have allowed for the ability to 
set "clientcert=verify-full" against various HBA authentication methods. 
This provides the ability to provide "multi-factor authentication" e.g. 
a client must provide both a valid certificate with a CN (or DN) that 
matches the user account, as well as a separate authentication challenge 
(e.g. a password).

With certificate-based authentication methods and other methods, we 
allow for users to specify a mapping in pg_ident, e.g. if one needs to 
perform a rewrite on the CN to match the username that is specified 
within PostgreSQL.

It seems logical that we should allow for something like:

    hostssl all all all scram-sha-256 clientcert=verify-full map=map

so we can accept certificates that may have CNs that can be mapped to a 
PostgreSQL user name.

Currently we can't do this, as one will get the error:

 > authentication option "map" is only valid for authentication methods
 > ident, peer, gssapi, sspi, and cert

I propose the below patch to add the currently supported password 
methods, scram-sha-256 + md5 to allow for the "map" parameter to be 
used. I hesitate to add md5 given we're trying to phase it out, so open 
to debate there.

With my testing, this does work when you specify clientcert=verify-full: 
PostgreSQL will correctly map the certificate. If you do not have 
clientcert=verify-full, the mapping appears to do nothing.

If this seems acceptable/valid, I'll add the appropriate documentation 
and whatever else may be required.

Thanks,

Jonathan

Attachment

Re: allowing "map" for password auth methods with clientcert=verify-full

From
Tom Lane
Date:
"Jonathan S. Katz" <jkatz@postgresql.org> writes:
> With certificate-based authentication methods and other methods, we 
> allow for users to specify a mapping in pg_ident, e.g. if one needs to 
> perform a rewrite on the CN to match the username that is specified 
> within PostgreSQL.

> It seems logical that we should allow for something like:
>     hostssl all all all scram-sha-256 clientcert=verify-full map=map
> so we can accept certificates that may have CNs that can be mapped to a 
> PostgreSQL user name.

I think this is conflating two different things: a mapping from the
username given in the startup packet, and a mapping from the TLS
certificate CN.  Using the same keyword and terminology for both
is going to lead to pain.  I'm on board with the idea if we can
disentangle that, though.

            regards, tom lane



Re: allowing "map" for password auth methods with clientcert=verify-full

From
"Jonathan S. Katz"
Date:
On 10/26/21 3:26 PM, Tom Lane wrote:
> "Jonathan S. Katz" <jkatz@postgresql.org> writes:
>> With certificate-based authentication methods and other methods, we
>> allow for users to specify a mapping in pg_ident, e.g. if one needs to
>> perform a rewrite on the CN to match the username that is specified
>> within PostgreSQL.
> 
>> It seems logical that we should allow for something like:
>>     hostssl all all all scram-sha-256 clientcert=verify-full map=map
>> so we can accept certificates that may have CNs that can be mapped to a
>> PostgreSQL user name.
> 
> I think this is conflating two different things: a mapping from the
> username given in the startup packet, and a mapping from the TLS
> certificate CN.  Using the same keyword and terminology for both
> is going to lead to pain.  I'm on board with the idea if we can
> disentangle that, though.

Hm, don't we already have that already when using "cert" combined with 
the "map" parameter? This is the main reason I "stumbled" upon this 
recommendation.

Based on what you say and if we're continuing with this functionality, 
would solving the conflation be a matter of primarily documentation?

Thanks,

Jonathan

Attachment

Re: allowing "map" for password auth methods with clientcert=verify-full

From
Tom Lane
Date:
"Jonathan S. Katz" <jkatz@postgresql.org> writes:
> On 10/26/21 3:26 PM, Tom Lane wrote:
>> I think this is conflating two different things: a mapping from the
>> username given in the startup packet, and a mapping from the TLS
>> certificate CN.  Using the same keyword and terminology for both
>> is going to lead to pain.  I'm on board with the idea if we can
>> disentangle that, though.

> Hm, don't we already have that already when using "cert" combined with 
> the "map" parameter? This is the main reason I "stumbled" upon this 
> recommendation.

I'm not exactly convinced that the existing design is any good.
I'm suggesting that we stop and think about it before propagating
it to a bunch of other use-cases.

Per "21.2. User Name Maps", I think that the map parameter is supposed
to translate from the startup packet's user name to the SQL role name.
ISTM that what is in the cert CN might be different from either
(particularly by perhaps having a domain name attached).  So I'd be
happier if there were a separate mapping available for the CN.

            regards, tom lane



Re: allowing "map" for password auth methods with clientcert=verify-full

From
Andrew Dunstan
Date:
On 10/26/21 18:16, Tom Lane wrote:
> "Jonathan S. Katz" <jkatz@postgresql.org> writes:
>> On 10/26/21 3:26 PM, Tom Lane wrote:
>>> I think this is conflating two different things: a mapping from the
>>> username given in the startup packet, and a mapping from the TLS
>>> certificate CN.  Using the same keyword and terminology for both
>>> is going to lead to pain.  I'm on board with the idea if we can
>>> disentangle that, though.
>> Hm, don't we already have that already when using "cert" combined with 
>> the "map" parameter? This is the main reason I "stumbled" upon this 
>> recommendation.
> I'm not exactly convinced that the existing design is any good.
> I'm suggesting that we stop and think about it before propagating
> it to a bunch of other use-cases.
>
> Per "21.2. User Name Maps", I think that the map parameter is supposed
> to translate from the startup packet's user name to the SQL role name.
> ISTM that what is in the cert CN might be different from either
> (particularly by perhaps having a domain name attached).  So I'd be
> happier if there were a separate mapping available for the CN.
>
>             


Possibly slightly off topic, but

The cert+map pattern is very useful in conjunction with pgbouncer. Using
it with an auth query to get the password pgbouncer doesn't even need to
have a list of users, and we in effect delegate authentication to
pgbouncer.

It would be nice to have + and @ expansion for the usernames in the
ident file, like there is for pg_hba.conf.


cheers


andrew

--
Andrew Dunstan
EDB: https://www.enterprisedb.com




Re: allowing "map" for password auth methods with clientcert=verify-full

From
Jacob Champion
Date:
On Tue, 2021-10-26 at 18:16 -0400, Tom Lane wrote:
> Per "21.2. User Name Maps", I think that the map parameter is supposed
> to translate from the startup packet's user name to the SQL role name.

I may have misunderstood what you wrote, but IIUC the startup packet's
user name _is_ the SQL role name, even when using a map. The map is
just determining whether or not the authenticated ID (pulled from a
certificate, or from Kerberos, or etc.) is authorized to use that role
name. It's not a translation, because you can have a one-to-many user
mapping (where me@example.com is allowed to log in as `me` or
`postgres` or `admin` or...).

Please correct me if I've missed something -- I need to have it right
in my head, given my other patches in this area...

--Jacob

Re: allowing "map" for password auth methods with clientcert=verify-full

From
Jacob Champion
Date:
On Wed, 2021-10-27 at 10:12 -0400, Andrew Dunstan wrote:
> Possibly slightly off topic, but
> 
> The cert+map pattern is very useful in conjunction with pgbouncer. Using
> it with an auth query to get the password pgbouncer doesn't even need to
> have a list of users, and we in effect delegate authentication to
> pgbouncer.
> 
> It would be nice to have + and @ expansion for the usernames in the
> ident file, like there is for pg_hba.conf.

(Probably is off-topic :D but +1 to the concept. Combined with LDAP
mapping that could make some of the ad-hoc LDAP-to-Postgres sync
scripts a lot simpler.)

--Jacob

Re: allowing "map" for password auth methods with clientcert=verify-full

From
"Jonathan S. Katz"
Date:
On 10/27/21 12:14 PM, Jacob Champion wrote:
> On Tue, 2021-10-26 at 18:16 -0400, Tom Lane wrote:
>> Per "21.2. User Name Maps", I think that the map parameter is supposed
>> to translate from the startup packet's user name to the SQL role name.
> 
> I may have misunderstood what you wrote, but IIUC the startup packet's
> user name _is_ the SQL role name, even when using a map. The map is
> just determining whether or not the authenticated ID (pulled from a
> certificate, or from Kerberos, or etc.) is authorized to use that role
> name. It's not a translation, because you can have a one-to-many user
> mapping (where me@example.com is allowed to log in as `me` or
> `postgres` or `admin` or...).
> 
> Please correct me if I've missed something -- I need to have it right
> in my head, given my other patches in this area...

To Tom's earlier point, I understand why we may want to pause and think 
about this.

I don't know the whole history of the "pg_ident.conf" file, but judging 
by the name, my guess is that the mapping functionality started with the 
"ident" authentication support, and then it was used for other auth 
types that could benefit from mapping (cert/gssapi etc.). The 
documentation referenced also skews towards describing what the original 
functionality for ident does.

That said, the existing functionality does match what Jacob is 
describing and what my own understanding is.

The patch I propose just layers on top of the existing functionality -- 
  you could even argue that it's "fixing a bug" that we did not add the 
current "map" support for the case of "clientcert=verify-full" given we 
do introspect the certificate CN to see if it matches the SQL role name.

In terms of other user mapping functionality, we have ad hoc support for 
FDWs when trying to map to a user in a different server:

https://www.postgresql.org/docs/current/sql-createusermapping.html

I'm unsure if there is anything we'd want to leverage here, as the 
overall goal of this is to provide the ability to establish a connection 
with a remote server.

I think in the context of doing any new work, I'd step back and ask what 
problem is this solving? The main one I think of is an integration with 
a SSO system has a credential with an identifier that does not match 
it's credential in PostgreSQL? (That would be the case I was working on, 
though said case was borrowed from our docs). Are there other cases?

That said, what would make it easier to manage it then? Maybe a lot of 
this is documenting and some expansion on what the pg_ident.conf file 
can do (per Andrew's suggestion). And maybe a new name for said file.

I don't know if we would want to bring any of this into the catalog or 
not -- but perhaps there may be some advantages to that from an 
administration standpoint.

Anyway, those are my initial thoughts on the challenge to think a bit 
more deeply about this. I'd still suggest considering the patch I 
propose as an "immediate fix" for existing versions as, at least to 
myself, I can argue it's a bug. We can then do some more work to make 
the overall system a bit easier/clearer to use and maintain.

Thanks,

Jonathan

Attachment

Re: allowing "map" for password auth methods with clientcert=verify-full

From
Jacob Champion
Date:
On Wed, 2021-10-27 at 12:53 -0400, Jonathan S. Katz wrote:
> The patch I propose just layers on top of the existing functionality -- 
>   you could even argue that it's "fixing a bug" that we did not add the 
> current "map" support for the case of "clientcert=verify-full" given we 
> do introspect the certificate CN to see if it matches the SQL role name.

Well, also to Tom's earlier point, though, this is a different sort of
mapping. Which "map" should we use if someone combines
clientcert=verify-full with an auth method which already uses a map
itself? Does the DBA want to map the auth name, the cert name, or both?

The current usermap support is piecemeal and I'd like to see it
completed, but I think you may be painting yourself into a corner if
you fix it in this way. (From a quick look at the patch, I'm also
worried that this happens to work by accident, but that may just be
FUD.)

> I think in the context of doing any new work, I'd step back and ask what 
> problem is this solving? The main one I think of is an integration with 
> a SSO system has a credential with an identifier that does not match 
> it's credential in PostgreSQL? (That would be the case I was working on, 
> though said case was borrowed from our docs). Are there other cases?
> 
> That said, what would make it easier to manage it then? Maybe a lot of 
> this is documenting and some expansion on what the pg_ident.conf file 
> can do (per Andrew's suggestion). And maybe a new name for said file.

I agree that the authorization system is due for a tuneup, for what
it's worth. Some of the comments from Magnus on my LDAP patch [1] kind
of hinted in that direction as well, I think, even if my approach is
rejected in the end.

--Jacob

[1] https://www.postgresql.org/message-id/flat/1a61806047c536e7528b943d0cfe12608118ca31.camel@vmware.com

Re: allowing "map" for password auth methods with clientcert=verify-full

From
Stephen Frost
Date:
Greetings,

* Jacob Champion (pchampion@vmware.com) wrote:
> On Wed, 2021-10-27 at 12:53 -0400, Jonathan S. Katz wrote:
> > The patch I propose just layers on top of the existing functionality --
> >   you could even argue that it's "fixing a bug" that we did not add the
> > current "map" support for the case of "clientcert=verify-full" given we
> > do introspect the certificate CN to see if it matches the SQL role name.
>
> Well, also to Tom's earlier point, though, this is a different sort of
> mapping. Which "map" should we use if someone combines
> clientcert=verify-full with an auth method which already uses a map
> itself? Does the DBA want to map the auth name, the cert name, or both?

My understanding of the mapping system with pg_ident has always been
that it's a mapping from the 'authenticated user' to the 'user in PG'
and the point is to check if that mapping is allowed but not to actually
change anything about who the ultimately logged in user is.

This is pretty clear and simple when the two are entirely disjoint- that
is, with 'peer' or 'gssapi' or 'cert', the authenticated user will be
whatever the authentication system thinks it is- the Unix username for
peer, the Kerberos principle for gssapi, the DN or CN for cert.

In all of the above cases, the username provided to us by the end user
is the role they're trying to log in as and the mapping is just there to
check if that's allowed based on the *authenticated username*.  Perhaps
not surprisingly, 21.2 could use some improvement on this, but it's
certainly the case that the mapping is only there as a permission check
and does not actually change who the user is logging into the system as.
Whatever username is in the startup packet is the role that the user
will be logged in as if they're allowed to log in as that role.

Now, when PG is involved in the authentication of the user, then I agree
that it gets more interesting.  That is- the user wants to log in as
user u1, they have a certificate that has a DN of u1/user/domain, and we
want to verify that they know the password and present the right
certificate- but which password do they need to know?

Today, there's really only one possible answer- the username in the
startup packet, because we don't have any concept of "authenticate to PG
as user u1 and then be logged in as user u2".  If we were to support a
mapping for scram or md5, we'd really need the user to tell us both who
to authenticate as and the user to log into PG as, and then we'd use
pg_ident.conf to ensure that such a mapping is allowed.  If we wanted to
implement something along the lines of "user authenticates as X but is
logged in as Y" automatically, that would need to be something other
than pg_ident.conf, imv.  I see that's been discussed a bit on the other
thread I was explicitly trying to ignore and glad that it's more-or-less
coming to the same conclusion.

Where does that leave us with what Jonathan is suggesting though?  For
my 2c, we shouldn't allow 'map=' to be used for scram or md5 because
it'll just confuse users, until and unless we actually do the PGAUTHUSER
thing and then we can allow 'map=' to check if that mapping is allowed
and the actual SCRAM PW check is done against PGAUTHUSER and then the
logged in user is the user as specified in the startup packet, assuming
that mapping is allowed.  For Jonathan's actual case though, we should
add a 'certmap' option instead and have that be explicitly for the case
where it's scram w/ clientcert=verify-full and then we check the mapping
of the DN/CN to the startup-packet username.  There's no reason this
couldn't also work with a 'map' specified and PGAUTHUSER set and SCRAM
used to verify against that at the same time.

> The current usermap support is piecemeal and I'd like to see it
> completed, but I think you may be painting yourself into a corner if
> you fix it in this way. (From a quick look at the patch, I'm also
> worried that this happens to work by accident, but that may just be
> FUD.)

I don't think it's an accident that it works, but a few comments and
a more explicit option for the user interface would be good.  Admins
would be confused if 'map=xyz' was accepted for SCRAM but then didn't
actually do anything.

> > I think in the context of doing any new work, I'd step back and ask what
> > problem is this solving? The main one I think of is an integration with
> > a SSO system has a credential with an identifier that does not match
> > it's credential in PostgreSQL? (That would be the case I was working on,
> > though said case was borrowed from our docs). Are there other cases?
> >
> > That said, what would make it easier to manage it then? Maybe a lot of
> > this is documenting and some expansion on what the pg_ident.conf file
> > can do (per Andrew's suggestion). And maybe a new name for said file.
>
> I agree that the authorization system is due for a tuneup, for what
> it's worth. Some of the comments from Magnus on my LDAP patch [1] kind
> of hinted in that direction as well, I think, even if my approach is
> rejected in the end.

Perhaps not a surprise, but I continue to be against the idea of adding
anything more to the insecure hack that is our LDAP auth method.  We
should be moving away from that, not adding to it.

That this would also require a new connection option / envvar to tell us
who the user wants to authenticate to LDAP as doesn't exactly make me
any more thrilled with it.

Just my 2c though and I know others don't necessarily agree with me on
this point.

Thanks,

Stephen

Attachment

Re: allowing "map" for password auth methods with clientcert=verify-full

From
Jacob Champion
Date:
On Mon, 2021-11-08 at 15:32 -0500, Stephen Frost wrote:
> Where does that leave us with what Jonathan is suggesting though?  For
> my 2c, we shouldn't allow 'map=' to be used for scram or md5 because
> it'll just confuse users, until and unless we actually do the PGAUTHUSER
> thing and then we can allow 'map=' to check if that mapping is allowed
> and the actual SCRAM PW check is done against PGAUTHUSER and then the
> logged in user is the user as specified in the startup packet, assuming
> that mapping is allowed.  For Jonathan's actual case though, we should
> add a 'certmap' option instead and have that be explicitly for the case
> where it's scram w/ clientcert=verify-full and then we check the mapping
> of the DN/CN to the startup-packet username.  There's no reason this
> couldn't also work with a 'map' specified and PGAUTHUSER set and SCRAM
> used to verify against that at the same time.

Agreed.

> Perhaps not a surprise, but I continue to be against the idea of adding
> anything more to the insecure hack that is our LDAP auth method.  We
> should be moving away from that, not adding to it.

Paraphrasing you from earlier, the "authenticate as one user and then
log in as another" use case is the one I'm trying to expand. LDAP is
just the auth method I happen to have present-day customer cases for.

> That this would also require a new connection option / envvar to tell us
> who the user wants to authenticate to LDAP as doesn't exactly make me
> any more thrilled with it.

Forgetting the LDAP part for a moment, do you have another suggestion
for how we can separate the role name from the user name? The
connection string seemed to be the most straightforward.

--Jacob