Hello! I'm attaching the patch rebased and with some fixes noted by Zsolts.
Regards! -- Jonathan Gonzalez V. EDB: https://www.enterprisedb.com
From bd44a4e28b5317886aa291302e7375bb17aab6e2 Mon Sep 17 00:00:00 2001 From: "Jonathan Gonzalez V." <[email protected]> Date: Wed, 29 Oct 2025 16:54:42 +0100 Subject: [PATCH v4 1/1] libpq-oauth: allow changing the CA when not in debug mode Allowing to set a CA enables users environment like companies with internal CA or developers working on their own local system while using a self-signed CA and don't need to see all the debug messages while testing inside an internal environment. Reviewed-by: Zsolt Parragi <zsolt,[email protected]> Signed-off-by: Jonathan Gonzalez V. <[email protected]> --- doc/src/sgml/libpq.sgml | 23 ++++++++--- src/interfaces/libpq-oauth/oauth-curl.c | 26 ++++++------ src/interfaces/libpq/fe-connect.c | 4 ++ src/interfaces/libpq/libpq-int.h | 1 + .../modules/oauth_validator/t/001_server.pl | 40 ++++++++++++++++++- .../modules/oauth_validator/t/OAuth/Server.pm | 2 +- 6 files changed, 74 insertions(+), 22 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 6db823808fc..24fda826dd1 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -10620,12 +10620,6 @@ typedef struct permits the use of unencrypted HTTP during the OAuth provider exchange </para> </listitem> - <listitem> - <para> - allows the system's trusted CA list to be completely replaced using the - <envar>PGOAUTHCAFILE</envar> environment variable - </para> - </listitem> <listitem> <para> prints HTTP traffic (containing several critical secrets) to standard @@ -10647,6 +10641,23 @@ typedef struct </para> </warning> </sect2> + <sect2 id="libpq-oauth-environment"> + <title>Environment variables</title> + <para> + The behavior of the OAuth calls may be affected by the following variables: + <variablelist> + <varlistentry> + <term><envar>PGOAUTHCAFILE</envar></term> + <listitem> + <para> + Allows to specify the path to a CA file that will be used by the client + to verify the certificate from the OAuth server side. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> + </sect2> </sect1> diff --git a/src/interfaces/libpq-oauth/oauth-curl.c b/src/interfaces/libpq-oauth/oauth-curl.c index 052ecd32da2..cd33115b6f3 100644 --- a/src/interfaces/libpq-oauth/oauth-curl.c +++ b/src/interfaces/libpq-oauth/oauth-curl.c @@ -17,6 +17,7 @@ #include <curl/curl.h> #include <math.h> +#include <string.h> #include <unistd.h> #if defined(HAVE_SYS_EPOLL_H) @@ -216,6 +217,7 @@ struct async_ctx /* relevant connection options cached from the PGconn */ char *client_id; /* oauth_client_id */ char *client_secret; /* oauth_client_secret (may be NULL) */ + char *ca_file; /* oauth_ca_file */ /* options cached from the PGoauthBearerRequest (we don't own these) */ const char *discovery_uri; @@ -1834,20 +1836,12 @@ setup_curl_handles(struct async_ctx *actx) } /* - * If we're in debug mode, allow the developer to change the trusted CA - * list. For now, this is not something we expose outside of the UNSAFE - * mode, because it's not clear that it's useful in production: both libpq - * and the user's browser must trust the same authorization servers for - * the flow to work at all, so any changes to the roots are likely to be - * done system-wide. + * Allow to set the CA even if we're not in debug mode, this would make it + * easy to work on environments where the CA could be internal and + * available on every system, like big companies with airgap systems. */ - if (actx->debugging) - { - const char *env; - - if ((env = getenv("PGOAUTHCAFILE")) != NULL) - CHECK_SETOPT(actx, CURLOPT_CAINFO, env, return false); - } + if (actx->ca_file != NULL) + CHECK_SETOPT(actx, CURLOPT_CAINFO, actx->ca_file, return false); /* * Suppress the Accept header to make our request as minimal as possible. @@ -3125,6 +3119,12 @@ pg_start_oauthbearer(PGconn *conn, PGoauthBearerRequestV2 *request) if (!actx->client_secret) goto oom; } + else if (strcmp(opt->keyword, "oauth_ca_file") == 0) + { + actx->ca_file = strdup(opt->val); + if (!actx->ca_file) + goto oom; + } } PQconninfoFree(conninfo); diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index db9b4c8edbf..6d66fcd3eea 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -413,6 +413,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "OAuth-Scope", "", 15, offsetof(struct pg_conn, oauth_scope)}, + {"oauth_ca_file", "PGOAUTHCAFILE", NULL, NULL, + "OAuth-CA-File", "", 64, + offsetof(struct pg_conn, oauth_ca_file)}, + {"sslkeylogfile", NULL, NULL, NULL, "SSL-Key-Log-File", "D", 64, offsetof(struct pg_conn, sslkeylogfile)}, diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index bd7eb59f5f8..1f1fb89e02f 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -444,6 +444,7 @@ struct pg_conn char *oauth_client_secret; /* client secret */ char *oauth_scope; /* access token scope */ char *oauth_token; /* access token */ + char *oauth_ca_file; /* CA file path */ bool oauth_want_retry; /* should we retry on failure? */ /* Optional file to write trace info to */ diff --git a/src/test/modules/oauth_validator/t/001_server.pl b/src/test/modules/oauth_validator/t/001_server.pl index cdad2ae8011..b66d99dd4bb 100644 --- a/src/test/modules/oauth_validator/t/001_server.pl +++ b/src/test/modules/oauth_validator/t/001_server.pl @@ -137,10 +137,46 @@ $node->connect_fails( expected_stderr => qr/failed to fetch OpenID discovery document:.*peer certificate/i); -# Now we can use our alternative CA. -$ENV{PGOAUTHCAFILE} = "$ENV{cert_dir}/root+server_ca.crt"; +# Make sure that PGOAUTHDEBUG is not required to specify the certificate +delete $ENV{PGOAUTHDEBUG}; +# The alternative CA path to use during the tests +my $alternative_ca = "$ENV{cert_dir}/root+server_ca.crt"; + +# Make sure we can use oauth_ca_file option to specify the alternative CA path my $user = "test"; +$node->connect_ok( + "user=$user dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0635 oauth_ca_file=$alternative_ca", + "connect as test", + expected_stderr => + qr@Visit https://example\.com/ and enter the code: postgresuser@, + log_like => [ + qr/oauth_validator: token="9243959234", role="$user"/, + qr/oauth_validator: issuer="\Q$issuer\E", scope="openid postgres"/, + qr/connection authenticated: identity="test" method=oauth/, + qr/connection authorized/, + ]); + +# Make sure that we can use the environment variable without the PGOAUTHDEBUG +# and use it for the rest of the tests +$ENV{PGOAUTHCAFILE} = $alternative_ca; + +$node->connect_ok( + "user=$user dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0635", + "connect as test", + expected_stderr => + qr@Visit https://example\.com/ and enter the code: postgresuser@, + log_like => [ + qr/oauth_validator: token="9243959234", role="$user"/, + qr/oauth_validator: issuer="\Q$issuer\E", scope="openid postgres"/, + qr/connection authenticated: identity="test" method=oauth/, + qr/connection authorized/, + ]); + +# Enable PGOAUTHDEBUG=UNSAFE to have the proper count later with the `[libpq] total number of polls` messages +$ENV{PGOAUTHDEBUG} = "UNSAFE"; + +$user = "test"; $node->connect_ok( "user=$user dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0635", "connect as test", diff --git a/src/test/modules/oauth_validator/t/OAuth/Server.pm b/src/test/modules/oauth_validator/t/OAuth/Server.pm index d923d4c5eb2..62a29c283df 100644 --- a/src/test/modules/oauth_validator/t/OAuth/Server.pm +++ b/src/test/modules/oauth_validator/t/OAuth/Server.pm @@ -28,7 +28,7 @@ daemon implemented in t/oauth_server.py. (Python has a fairly usable HTTP server in its standard library, so the implementation was ported from Perl.) This authorization server serves HTTPS on 127.0.0.1 (IPv4 only). libpq will need -to set PGOAUTHDEBUG=UNSAFE and PGOAUTHCAFILE with the right CA. +to set PGOAUTHCAFILE with the right CA. =cut -- 2.51.0
signature.asc
Description: This is a digitally signed message part
