Skip to content

Proposal: Supporting URI SAN in Certificate Authentication - #399

Open
pg-hub-mirror[bot] wants to merge 1 commit into
masterfrom
pg-hub/mirror-patch-24dfb73180f8adc3
Open

pg-hub-mirror[bot] wants to merge 1 commit into
masterfrom
pg-hub/mirror-patch-24dfb73180f8adc3

Conversation

@pg-hub-mirror

@pg-hub-mirror pg-hub-mirror Bot commented Sep 17, 2026

Copy link
Copy Markdown

Read-only mirror. Reply and review on pgsql-hackers; activity here is not sent upstream.

  • Original author: Florin Irion <irionr(at)gmail(dot)com>
  • Mailing list: pgsql-hackers
  • Message-ID: a3335e80-c032-493b-83f6-de1d658e6489@gmail.com
  • Original email

Patch files:


Hi
Here is the first version of the patch to support URI SAN in certificate
authentication (clientname=URI in pg_hba.conf). We can start a discussion
on the actual code now.The patch can also be reviewed on my “postgres”
project fork [1].
Together with Leonardo Cecchi, CloudNativePG (CNPG) maintainer, we also
tested the patch using the cnpg-i-spiffe plugin[2] that he wrote for the
occasion and the generated container image for CloudNativePG [3]. Everything
worked smoothly, and workloads can now authenticate natively with SPIFFE
IDs.
Given that both CloudNativePG and SPIFFE/SPIRE are CNCF projects, we
contacted
Agustín Martínez Fayó, a SPIRE maintainer, to verify the integration between
the two projects and to begin collaborating on the topic. We decided to
strictly allow only a single URI SAN per certificate. This follows the
X509-SVID spec, avoiding weird edge cases with user mapping, and making
connection logging very clear.
Cheers,
Florin
[1] - irionr#7
[2] - https://github.com/leonardoce/cnpg-i-spiffe
[3] -
https://github.com/leonardoce/postgres-trunk-containers/actions/runs/35070429177

Allow certificate authentication to use a URI subject alternative name
as the client identity via clientname=URI in pg_hba.conf.

URI SANs are extracted from the client certificate during TLS setup and
stored on the Port, alongside the existing CN and DN.  Following the
SPIFFE X.509-SVID specification, authentication requires the certificate
to carry exactly one URI SAN: the username is matched against it, and
the matched URI, rather than the Subject DN, becomes the authenticated
identity recorded in the connection log.

Client certificates with an empty subject are now accepted as long as a
URI SAN is present.clientname=CN and clientname=DN still reject such
certificates, since there is no name to compare.

The documentation describes clientname=URI, including the single URI SAN
requirement and the caveat that, for SPIFFE deployments, ssl_ca_file
should hold a single trust domain's CA bundle.

TAP coverage is added for URI SAN matching: success with a single URI
SAN (identity logged as the URI), rejection when the certificate has no
URI SAN, rejection with multiple URI SANs, and success with an
empty-subject certificate.
@pg-hub-mirror pg-hub-mirror Bot added source:pgsql-hackers Mirrored from pgsql-hackers type:patch Mail thread contains a PostgreSQL patch area:testing Tests and buildfarm area:security Authentication, authorization, or security labels Sep 17, 2026
@pg-hub-mirror pg-hub-mirror Bot locked and limited conversation to collaborators Sep 17, 2026
@pg-hub-mirror

pg-hub-mirror Bot commented Sep 18, 2026

Copy link
Copy Markdown
Author

Jacob Champion <jacob(dot)champion(at)enterprisedb(dot)com> via pgsql-hackers · original email

On Thu, Sep 17, 2026 at 3:10 AM Florin Irion <irionr(at)gmail(dot)com> wrote:

We decided to
strictly allow only a single URI SAN per certificate. This follows the
X509-SVID spec, avoiding weird edge cases with user mapping, and making
connection logging very clear.
I'd like opinions from the crowd on the use of X509-SVID as a
governing spec here. I don't mind the 1:1 requirement for now; it
simplifies things. But if someone comes forward proposing multiple-URI
support, are we going to reject it since the original use case was
SPIFFE? Also, this patch doesn't seem to perform the other required
checks for X509-SVID validation [1], so it seems a little weird to
cite SPIFFE in the comments and tests and documentation.
Partial review follows (I have not read in detail):

    • An empty subject means there is no DN to record, so leave
    • peer_dn NULL. The X.509-SVID specification allows this when a
    • (critical) URI subjectAltName is present.
      From a skim, it looks like other parts of the code assume either
      peer_dn or peer_cn is set; they'll need to be changed too.
      X.509-SVID isn't relevant in this particular part of the code, IMO.
      Empty Subjects have been permissible in general for a very long time,
      and criticality should generally be handled by the OpenSSL layer
      rather than us.
      The growing indentation in be_tls_open_server is, I think, a smell.
      We've reached rule of three (or at least 2.5 depending on your view of
      peer_dn and peer_cn) and I think this code is going to need to be
      restructured a bit in order to absorb the new use case. I don't have
      anything in particular in mind yet.
      The X.509-SVID specification
      +# requires the URI SAN to be critical when the subject is empty.
      We can cite RFC 5280 for the criticality requirement (X509-SVID defers
      to it anyway).
      +# Client certificate with an empty subject and a single, critical URI
      +# subjectAltName. Unlike the standard client certificates, this needs
      +# an explicit "-subj /" when generating the CSR, since the config file
      +# alone cannot express an empty subject.
      That's fun. Let's just pull the -subj / into a default-empty
      variable that the new CSR can tweak. That should get rid of most of
      the new copy-paste boilerplate, I think.
      For SPIFFE deployments, configure
  •   <literal>ssl_ca_file</literal> with a single trust domain's bundle;
    
  •  </para>
    

Looks like this was cut off?

  • /*
    • The SPIFFE X.509-SVID specification requires an SVID to carry
    • exactly one URI SAN, so refuse both none and more than one: the
    • authenticated identity must be unambiguous.
  • */
  • if (port->peer_uri_count != 1)
    I think it's really important that we keep unedited LLMisms out of the
    security code. (And elsewhere too.)
    Like, definitely push back if I've misattributed this, but I imagine
    that you wouldn't have willingly typed three lines of comments in
    order to say "we require exactly one URI", and I don't really want to
    read three lines of comments that say that. We need the
    signal-to-noise ratio to be very high here.
  • if (peer_username == NULL ||
  • strlen(peer_username) <= 0)
    

@pg-hub-mirror pg-hub-mirror Bot unlocked this conversation Sep 18, 2026
@pg-hub-mirror pg-hub-mirror Bot locked and limited conversation to collaborators Sep 18, 2026
@pg-hub-mirror pg-hub-mirror Bot added the area:docs Documentation label Sep 18, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area:docs Documentation area:security Authentication, authorization, or security area:testing Tests and buildfarm source:pgsql-hackers Mirrored from pgsql-hackers type:patch Mail thread contains a PostgreSQL patch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant