Hello! > Yep, we need to support back to Python 3.6.8 (which I've tested your > patch with locally).
That's indeed good to know, there's a way to know which is the oldest version of Python used? What do you think about adding this information on top of the `oauth_server.py` file? Just as a reminder because I suspected that the lower version will be 3.9 not 3.6 > Comments: > > > Signed-off-by: Jonathan Gonzalez V. <[email protected]> > > Note that we have no signoff convention here, at least that I'm aware > of. It's not a problem to include it, but this will be replaced by an > `Author:` line in the final commit. Submissions here fall under the > Archive Policy: > > https://www.postgresql.org/about/policies/archives/ Yes, I'm fully aware of this, it's just my default git configuration to add the sign-off, I'll check to remove that, thanks for the point! > > > +my $certdir = dirname(__FILE__) . "/../../../ssl/ssl"; > > +$ENV{PGOAUTHCAFILE} = "$certdir/root+server_ca.crt"; > > I think we should inject this directory from the build system, > instead > of walking across the tree. I wasn't aware of this, will keep in mind for hte future > > > - my $issuer = "http://127.0.0.1:$port"; > > + my $issuer = "https://localhost:$port"; > > Unfortunately this will cause strange bugs [1]: we aren't listening > on > IPv6, but Curl will attempt to contact a DNS hostname on both IPv4 > and > IPv6 simultaneously, leading to intermittent failures. So we'll need > to get the certificate's SANs working... I wasn't aware of this, good learning! I'll keep it in mind for the future, and probably we can work on IPv6 support later. > > server-ip-in-dnsname \ > > + server-ip-localhost \ > > server-single-alt-name \ > > nitpick: Though the naming scheme is not really all that well > defined, > I think this probably belongs with the other -alt-name certs rather > than the -ip- certs. I had my questions if add it to the alt-name too, and now I'm thinking in other test ideas due to this, like using multiple alternative names for the host and issuer, but for sure, for the future not here! > -- > > To avoid making you play fetch-a-rock, I've attached these comments > in > code form as v1.1-0002 (but you're under no obligation to take them > if > you'd prefer to do it a different way :D). I also ran oauth_server.py > through Black. I did the same! just added the comments I mentioned, besides that, all good on my side. Thank you! --- Jonathan Gonzalez V. <[email protected]> EnterpriseDB
From 28e724e4733699d04d515e4eaff80d9b3d16d963 Mon Sep 17 00:00:00 2001 From: "Jonathan Gonzalez V." <[email protected]> Date: Sat, 28 Feb 2026 06:07:11 +0100 Subject: [PATCH v1.2 3/3] squash! add a few comments to the scripts --- src/test/modules/oauth_validator/t/oauth_server.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/modules/oauth_validator/t/oauth_server.py b/src/test/modules/oauth_validator/t/oauth_server.py index 973d99b93f2..60a754ac197 100755 --- a/src/test/modules/oauth_validator/t/oauth_server.py +++ b/src/test/modules/oauth_validator/t/oauth_server.py @@ -5,6 +5,8 @@ # so that the Perl tests can contact it) and runs as a daemon until it is # signaled. # +# This Python script must be tested and work with Python 3.6.8 and above. +# import base64 import functools @@ -417,6 +419,8 @@ def main(): ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ssl_context.load_cert_chain(ssl_cert, ssl_key) + # The server is listening exclusively on 127.0.0.1 to avoid problems with IPv6 + # in some systems during the tests s = http.server.HTTPServer(("127.0.0.1", 0), OAuthHandler) s.socket = ssl_context.wrap_socket(s.socket, server_side=True) -- 2.51.0
From 29dc27b5437965631e78195b81f3c313fa03d381 Mon Sep 17 00:00:00 2001 From: Jacob Champion <[email protected]> Date: Fri, 27 Feb 2026 15:36:17 -0800 Subject: [PATCH v1.2 2/3] squash! Add TLS support for the OAuth tests - inject certificate directory from build system - keep the HTTP test, add a failed-HTTPS-verification test - switch back to IPv4-only Issuer [1] - blacken oauth_server.py - add req_extensions = v3_req to get SANs working - move `localhost` DNS name to SANs - rename server-ip-localhost -> server-localhost-alt-names [1] https://postgr.es/m/CAOYmi%2Bn4EDOOUL27_OqYT2-F2rS6S%2B3mK-ppWb2Ec92UEoUbYA%40mail.gmail.com Signed-off-by: Jonathan Gonzalez V. <[email protected]> --- src/test/modules/oauth_validator/Makefile | 1 + src/test/modules/oauth_validator/meson.build | 1 + .../modules/oauth_validator/t/001_server.pl | 50 ++++++++++++++++--- .../modules/oauth_validator/t/OAuth/Server.pm | 6 +-- .../modules/oauth_validator/t/oauth_server.py | 12 ++--- ...nfig => server-localhost-alt-names.config} | 4 +- src/test/ssl/ssl/server-ip-localhost.crt | 20 -------- src/test/ssl/ssl/server-ip-localhost.key | 28 ----------- .../ssl/ssl/server-localhost-alt-names.crt | 20 ++++++++ .../ssl/ssl/server-localhost-alt-names.key | 28 +++++++++++ src/test/ssl/sslfiles.mk | 2 +- 11 files changed, 106 insertions(+), 66 deletions(-) rename src/test/ssl/conf/{server-ip-localhost.config => server-localhost-alt-names.config} (73%) delete mode 100644 src/test/ssl/ssl/server-ip-localhost.crt delete mode 100644 src/test/ssl/ssl/server-ip-localhost.key create mode 100644 src/test/ssl/ssl/server-localhost-alt-names.crt create mode 100644 src/test/ssl/ssl/server-localhost-alt-names.key diff --git a/src/test/modules/oauth_validator/Makefile b/src/test/modules/oauth_validator/Makefile index cb64f0f1437..0b39a88fd9f 100644 --- a/src/test/modules/oauth_validator/Makefile +++ b/src/test/modules/oauth_validator/Makefile @@ -36,5 +36,6 @@ include $(top_srcdir)/contrib/contrib-global.mk export PYTHON export with_libcurl export with_python +export cert_dir=$(top_srcdir)/src/test/ssl/ssl endif diff --git a/src/test/modules/oauth_validator/meson.build b/src/test/modules/oauth_validator/meson.build index c4b73e05297..506a9894b8d 100644 --- a/src/test/modules/oauth_validator/meson.build +++ b/src/test/modules/oauth_validator/meson.build @@ -80,6 +80,7 @@ tests += { 'PYTHON': python.full_path(), 'with_libcurl': oauth_flow_supported ? 'yes' : 'no', 'with_python': 'yes', + 'cert_dir': meson.project_source_root() / 'src/test/ssl/ssl', }, 'deps': [oauth_hook_client], }, diff --git a/src/test/modules/oauth_validator/t/001_server.pl b/src/test/modules/oauth_validator/t/001_server.pl index 12da59109bf..cdad2ae8011 100644 --- a/src/test/modules/oauth_validator/t/001_server.pl +++ b/src/test/modules/oauth_validator/t/001_server.pl @@ -14,7 +14,6 @@ use MIME::Base64 qw(encode_base64); use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More; -use File::Basename; use FindBin; use lib $FindBin::RealBin; @@ -72,8 +71,30 @@ END $? = $exit_code; } +# To test against HTTPS with our custom CA, we need to enable PGOAUTHDEBUG and +# PGOAUTHCAFILE. But first, check to make sure the client refuses HTTP and +# untrusted HTTPS connections by default. my $port = $webserver->port(); -my $issuer = "https://localhost:$port"; +my $issuer = "http://127.0.0.1:$port"; + +unlink($node->data_dir . '/pg_hba.conf'); +$node->append_conf( + 'pg_hba.conf', qq{ +local all test oauth issuer="$issuer" scope="openid postgres" +}); +$node->reload; + +my $log_start = $node->wait_for_log(qr/reloading configuration files/); + +$node->connect_fails( + "user=test dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0635", + "HTTPS is required without debug mode", + expected_stderr => + qr@OAuth discovery URI "\Q$issuer\E/.well-known/openid-configuration" must use HTTPS@ +); + +# Switch to HTTPS. +$issuer = "https://127.0.0.1:$port"; unlink($node->data_dir . '/pg_hba.conf'); $node->append_conf( @@ -84,7 +105,8 @@ local all testparam oauth issuer="$issuer/param" scope="openid postgres" }); $node->reload; -my $log_start = $node->wait_for_log(qr/reloading configuration files/); +$log_start = + $node->wait_for_log(qr/reloading configuration files/, $log_start); # Check pg_hba_file_rules() support. my $contents = $bgconn->query_safe( @@ -97,12 +119,26 @@ is( $contents, 3|oauth|\{issuer=$issuer/param,"scope=openid postgres",validator=validator\}}, "pg_hba_file_rules recreates OAuth HBA settings"); -# To test against HTTPS, we need to enable PGOAUTHDEBUG and provide the right -# CA to be able to verify the server certificate +# Make sure PGOAUTHDEBUG=UNSAFE doesn't disable certificate verification. $ENV{PGOAUTHDEBUG} = "UNSAFE"; -my $certdir = dirname(__FILE__) . "/../../../ssl/ssl"; -$ENV{PGOAUTHCAFILE} = "$certdir/root+server_ca.crt"; +$node->connect_fails( + "user=test dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0635", + "HTTPS trusts only system CA roots by default", + # Note that the latter half of this error message comes from Curl, which has + # had a few variants since 7.61: + # + # - SSL peer certificate or SSH remote key was not OK + # - Peer certificate cannot be authenticated with given CA certificates + # - Issuer check against peer certificate failed + # + # Key off of the "peer certificate" portion, since that seems to have + # remained constant over a long period of time. + 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"; my $user = "test"; $node->connect_ok( diff --git a/src/test/modules/oauth_validator/t/OAuth/Server.pm b/src/test/modules/oauth_validator/t/OAuth/Server.pm index 84c62f38c6b..d923d4c5eb2 100644 --- a/src/test/modules/oauth_validator/t/OAuth/Server.pm +++ b/src/test/modules/oauth_validator/t/OAuth/Server.pm @@ -15,7 +15,7 @@ OAuth::Server - runs a mock OAuth authorization server for testing $server->run; my $port = $server->port; - my $issuer = "https://localhost:$port"; + my $issuer = "https://127.0.0.1:$port"; # test against $issuer... @@ -27,8 +27,8 @@ This is glue API between the Perl tests and the Python authorization server 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 runs over TLS, so libpq will need to set -PGOAUTHDEBUG=UNSAFE and PGOAUTHCAFILE with the right CA. +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. =cut diff --git a/src/test/modules/oauth_validator/t/oauth_server.py b/src/test/modules/oauth_validator/t/oauth_server.py index d7de984535b..973d99b93f2 100755 --- a/src/test/modules/oauth_validator/t/oauth_server.py +++ b/src/test/modules/oauth_validator/t/oauth_server.py @@ -18,10 +18,10 @@ import urllib.parse from collections import defaultdict from typing import Dict -oauth_server_dir = os.path.dirname(os.path.realpath(__file__)) -ssl_dir = os.path.join(oauth_server_dir, "../../../ssl/ssl") -ssl_cert = ssl_dir + "/server-ip-localhost.crt" -ssl_key = ssl_dir + "/server-ip-localhost.key" +ssl_dir = os.getenv("cert_dir") +ssl_cert = ssl_dir + "/server-localhost-alt-names.crt" +ssl_key = ssl_dir + "/server-localhost-alt-names.key" + class OAuthHandler(http.server.BaseHTTPRequestHandler): """ @@ -300,7 +300,7 @@ class OAuthHandler(http.server.BaseHTTPRequestHandler): def config(self) -> JsonObject: port = self.server.socket.getsockname()[1] - issuer = f"https://localhost:{port}" + issuer = f"https://127.0.0.1:{port}" if self._alt_issuer: issuer += "/alternate" elif self._parameterized: @@ -414,7 +414,7 @@ def main(): be printed to stdout. """ # To be able to listen over SSL we create the context and load the certificates - ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ssl_context.load_cert_chain(ssl_cert, ssl_key) s = http.server.HTTPServer(("127.0.0.1", 0), OAuthHandler) diff --git a/src/test/ssl/conf/server-ip-localhost.config b/src/test/ssl/conf/server-localhost-alt-names.config similarity index 73% rename from src/test/ssl/conf/server-ip-localhost.config rename to src/test/ssl/conf/server-localhost-alt-names.config index 61f10af0619..1c41c1ecb78 100644 --- a/src/test/ssl/conf/server-ip-localhost.config +++ b/src/test/ssl/conf/server-localhost-alt-names.config @@ -1,12 +1,13 @@ # An OpenSSL format CSR config file for creating a server certificate. # +# This certificate contains SANs for localhost (DNS, IPv4, and IPv6). [ req ] distinguished_name = req_distinguished_name +req_extensions = v3_req prompt = no [ req_distinguished_name ] -CN = localhost OU = PostgreSQL test suite # For Subject Alternative Names @@ -14,5 +15,6 @@ OU = PostgreSQL test suite subjectAltName = @alt_names [ alt_names ] +DNS.1 = localhost IP.1 = 127.0.0.1 IP.2 = ::1 diff --git a/src/test/ssl/ssl/server-ip-localhost.crt b/src/test/ssl/ssl/server-ip-localhost.crt deleted file mode 100644 index c6e4dadcef8..00000000000 --- a/src/test/ssl/ssl/server-ip-localhost.crt +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDPDCCAiSgAwIBAgIIICYCJxcTNAAwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UE -Aww3VGVzdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNl -cnZlciBjZXJ0czAgFw0yNjAyMjcxNjEzMzRaGA8yMDUzMDcxNTE2MTMzNFowNDEe -MBwGA1UECwwVUG9zdGdyZVNRTCB0ZXN0IHN1aXRlMRIwEAYDVQQDDAlsb2NhbGhv -c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDC3bW97F0OjQR1E+CW -TLqPsBngwy02IBbcPZNr19HYodby82mKavwvU7IDkTCCNTU89zlHbzlF4NsnWuvA -zQlPk/kfg9OeDj2G3PKYvHe2G9xkuBEgusKsq+FkQcx3o4S93fAZKyCztgBVptfm -/q8CeJ4nsNlQI7uAYZ9s3R36PrX92qS50NvOOIm5zxRtCXX51gMVqWS7t/iyvXU2 -EVGyXLRyOArCoVkfY9AbBAJJzftzEQjqUVS7W2INUr8KLkRRJOKrzkBLg8T2+YmR -PNKdqhVMhn5uozjTgA0viQN1rVJZvxahR8zAhhqChO7+84xWEcZTl9Lc5uWDV+O4 -w4bPAgMBAAGjQjBAMB0GA1UdDgQWBBQWXk4bIK8qHwQpAQTmvNX4B/h5ozAfBgNV -HSMEGDAWgBTyjzpmQFBkSCLXLoL/Vp7Cs+50CjANBgkqhkiG9w0BAQsFAAOCAQEA -Kh0pDHJfQ/5daLpmLiOwZD3KqN8FQd5QNVJjlGD3Qh3QblVRGNl1csLSWb4BnENO -3Kq6zKkNBSoAA6bFqVhXcdL1utrnGk2fLpInGMA5B+r+BpZkUVSKu6BPn/HNnImt -PCZ3sjQyTZ1QXjDjxiOynGkDl9G+3RKDsDhdOHxoqCKUDlHkC9oi3FxkNgvvwpVu -ybio+haTmoRbOr7U5MFNbtQOglgjV4/QeFjzSm3mrSuCEJVTI4/CovbboM5xZ144 -FNunyccqdaFA9O3lKsmj03zEIkI9kNRL8IRNVf/xoVRdivNMdHhG+KrO9pqIYuYM -3Vsjv+Men06w2B0StaazZg== ------END CERTIFICATE----- diff --git a/src/test/ssl/ssl/server-ip-localhost.key b/src/test/ssl/ssl/server-ip-localhost.key deleted file mode 100644 index 748cf7a59b4..00000000000 --- a/src/test/ssl/ssl/server-ip-localhost.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDC3bW97F0OjQR1 -E+CWTLqPsBngwy02IBbcPZNr19HYodby82mKavwvU7IDkTCCNTU89zlHbzlF4Nsn -WuvAzQlPk/kfg9OeDj2G3PKYvHe2G9xkuBEgusKsq+FkQcx3o4S93fAZKyCztgBV -ptfm/q8CeJ4nsNlQI7uAYZ9s3R36PrX92qS50NvOOIm5zxRtCXX51gMVqWS7t/iy -vXU2EVGyXLRyOArCoVkfY9AbBAJJzftzEQjqUVS7W2INUr8KLkRRJOKrzkBLg8T2 -+YmRPNKdqhVMhn5uozjTgA0viQN1rVJZvxahR8zAhhqChO7+84xWEcZTl9Lc5uWD -V+O4w4bPAgMBAAECggEAAnJfIopWH08bU/A54+4bAzuZgPlu7UPFqpR/FvbYfPh9 -Ka+9TsuPZ3WBz+ZksPJfhUzRd7ayOBO5rtBHEyPzaSFdXXyRR/xRvs0slz3jnZvp -Hxz6upkx5t/2Z00wz9BdsccjpS5M3OIFV1vkchCRmGCAFAzaOHckewW7/tV7Ugfa -pNuYz0WjJT7N2sU362QINABgWtD1a3vPOyspsrSb8Gox1fzeGAgCZi8oKiI3QYuk -ooyqwbs/lkXKHRZXmLc7lXMr1wWLLnMUUUGXZ/eC4vpPJ9rH33ymxShAfrUGWvXQ -fXnuabLyMT0a/uhaJZh0/jAk1BOnUnjHCHuBpyHGrQKBgQD5mpYbpAChs9vN6yFP -RPjc2q9xuJbLzsi9vTgTxRiYVUcYWJ2Lcz2wSg9nyBsHBZVFqH9uVGTCImOIDbba -8lIZmvC4nNiMxhsD1jQZQBCOU03t0Ga5ImxUYexxq/lP809qJcGKdgVSGA93ufkC -7FXJH6iWnwTk/jiM/YyaJhX/QwKBgQDH3Ap4aGf7CmkULvKei9435lt1a3YkmXmt -NOBBimLUyXJtq1m//Thx/Gp9CVa38M3fd0NH6ZZvRJBPwO+43A3EqXv/qXn9dNcA -xY2bYtRrm3LGilNuG2C/8T+cZ+T9hX4dSIwefJMYGPDkZNwgwl/tjRjbZZE+KWv8 -VGQ87HVjhQKBgQCMZW5vh7UvP1qwncQzsUkF+R/cKIbxhpOVXhxvylpGPRlrUVT0 -flLBmTbHGmBRd8t5zgg3h9LQ+8TeX1BuIQUbD/K89MQ9kqTZaKAPX+CwHZ1k2ecd -1YX3hMkZOzFVzjbqLuiJOE9P2ObCYmH1SfgK0/rhFfsLzw8CBxASGMAgvwKBgBr6 -wlMUzQyfkCXQXKI4gWwMZcZJFm7EZR+Tpr5SPxs4goD5g6keNtN0Xq+4ZgN4t2H2 -SJfZmZw1pkGN6w6KbjVhJ8MQjs4/SpLpGD+krMZF+s2AeNjBS2M93vdvMwNiVO8B -DyFCcdzr6QD7+JdXhfmcdYGQiXXZw6ERh2KODR69AoGBAPQ4IRTIh3jfBj2xZenI -MDegCiOIAO516GTX0C1aB3jFhH4pcdbiN2R/2dffdvZvuQmJ4kCmyggvyx9QjUHL -/Kxk0jacA14LFOmrnlihwET4gfeL8S3vzhJcNcv0GXOgWe4hP9LH+eaidICx/JlD -hYX8HYSKQxIFmt5IUAxlUvoj ------END PRIVATE KEY----- diff --git a/src/test/ssl/ssl/server-localhost-alt-names.crt b/src/test/ssl/ssl/server-localhost-alt-names.crt new file mode 100644 index 00000000000..106dc34d7bb --- /dev/null +++ b/src/test/ssl/ssl/server-localhost-alt-names.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDVjCCAj6gAwIBAgIIICYCJxRTBwAwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UE +Aww3VGVzdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNl +cnZlciBjZXJ0czAgFw0yNjAyMjcyMjUzMDdaGA8yMDUzMDcxNTIyNTMwN1owIDEe +MBwGA1UECwwVUG9zdGdyZVNRTCB0ZXN0IHN1aXRlMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEA3k/aT/OV8sbJrvhtSgz5eNMCuv7RKdUQw+f52DpZTs85 +lTXIRs+l3mXoKRjN1gqzqlHInnJlhxQipqGiJfz4Li8L6jma2yZztFHH+f+YF8Ke +5fCYP1qMxbghqeIRkKgrCEjHUnOhbN5oMi/Ndt9AXWGG/39uk5Xec/Y/J5aZkPVV +blqWYyQQ+4U783lwZs1EUWdfiTVRp8fYADT/2lHjaZaX08vAE5VvCbBv6mPhPfno +F9FIaW+CRuwORisFK8Bd1q/0r5aPZGPi0lokCdaB/cRUHwJK1/HHgyB3N+Lk4swf +z+MfSqj4IaNPW7zn3EV9hgpVwSmB5ES8rzojiGtMDQIDAQABo3AwbjAsBgNVHREE +JTAjgglsb2NhbGhvc3SHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwHQYDVR0OBBYE +FOZ8KClKVbeYecn8lvAldBXOjQz6MB8GA1UdIwQYMBaAFPKPOmZAUGRIItcugv9W +nsKz7nQKMA0GCSqGSIb3DQEBCwUAA4IBAQDE1FGw20H0Flo3gAGN0ND9G/6wDxWM +MldbXRjqc1E0/+7+Zs6v1jPrNUNEvxy5kHWevUJCIt6y4SYt01JxE4wqEPJ3UBAv +cM0p08mohmN/CHc/lswXx12MZMfaLA1/WRPqvtiGFOrOOPvaRKHO4ORiT1KWmtOO +FgcW9E1Q1iJFK28xdz9NEEBWEurEIr5KGAsCwf9DfQxPJXiS9n98BDI8gPwlse7t +VqyhGVSj+EPbdY2kqkSuPXacdnUGfO6EWo9PFKqhxWMxABLuK0UZzH6/1lMOh1m9 +Mm+gtwO5RLBX22V+KIs1uuDTNcveQ2DsZnMZh7lGD05eHYG9hwnC6GNZ +-----END CERTIFICATE----- diff --git a/src/test/ssl/ssl/server-localhost-alt-names.key b/src/test/ssl/ssl/server-localhost-alt-names.key new file mode 100644 index 00000000000..4499a11928d --- /dev/null +++ b/src/test/ssl/ssl/server-localhost-alt-names.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDeT9pP85Xyxsmu ++G1KDPl40wK6/tEp1RDD5/nYOllOzzmVNchGz6XeZegpGM3WCrOqUciecmWHFCKm +oaIl/PguLwvqOZrbJnO0Ucf5/5gXwp7l8Jg/WozFuCGp4hGQqCsISMdSc6Fs3mgy +L81230BdYYb/f26Tld5z9j8nlpmQ9VVuWpZjJBD7hTvzeXBmzURRZ1+JNVGnx9gA +NP/aUeNplpfTy8ATlW8JsG/qY+E9+egX0Uhpb4JG7A5GKwUrwF3Wr/Svlo9kY+LS +WiQJ1oH9xFQfAkrX8ceDIHc34uTizB/P4x9KqPgho09bvOfcRX2GClXBKYHkRLyv +OiOIa0wNAgMBAAECggEAFchiPkJCV4r12RCbeM2DpjyawGLWcNBhN6jjuLWi6Y9x +d3bRHGsdOAjpMhmtlYLv7sjbrPbNjupAqO4eerVqRfAzLSyeyUlfvfPjcdIC/5UA +x8wGxvJi576ugbxWd0ObD9E9woz07LtwHzbC3ZprbprvRNqiJZDiPp+KuaDOhD7u +6XAM8JilFqfiDN8+xbH2dWdVkdt2OD5wctJbqy6moH9VFVsWsMQr3/vJkSdUPLxa +8ATUubFhO/sqE+KsMZESq5W1Xbj3NwMkvnA92yG9+ED60NPjFzgheZZWSmXe1B/c +XB3G/upvCoHEgKbrnYt05b/ryUbXAZkvi5oL4fp9OwKBgQD4d+Qm4GiKEWvjZ5II +ROfHEyoWOHw9z8ydJIrtOL8ICh5RH8D/v2IaMAacWV5eLoJ7aYC6yIYuWdHQljAi +zltNFrsLFmWXLy91IWfUzIGnFLWeqOmI50vlM8xU54rD/cZ3qtvr2Qk9HHs0dsyB +6cGRf0BPJi04aAEqSZqc8HCXAwKBgQDlDP0MW57bHpqQROQDLIgEX9/rzUNo48Z/ +1f27bCkKP+CpizE9eWvGs5rQmUxCNzWULFxIuBbgsubuVP7jO3piY6bRGnvSE6nD +mW0V1mSypVO22Ci/Q8ekkY2+0ZVp3qLPO/cwtI/Ye8kp4xu41I2XgJE8Mo0hEEyJ +N1/1vUJbrwKBgBp3gukVPG2An5JwpOCWnm3ZP8FwMOPQr8YJb3cHdWng0gvoKwHT +HBsYBIxBBMlZgPKucVT0KT7kuHHUnboHazhR9Iig0R+CmjaK4WmMgz8N+K625XF8 +2dvHYbulkmWAMdTrcVO1IcPNtd4HzY8FHGZoPKxxr51zjrQ3dO3EuumLAoGATho2 +sx8OtPLji2wiP77QhoVWqmYspTh9+Bs00NLZz6fmaImQ+cBMcs3NbXHIYg/HUkYq +FZXIH0iBnCUZYMxoN+J5AHZCYGjaC1tmqfqYDZ54RDHC+y0Wh1QmfDmk9Bu5cmal +LFN1dUEIYCMT0duQiGeLnnYyT2LqZiOesgGd/fsCgYEA2GbKteq+io6HAEt2/yry +xZGaRR8Twg0B8XtD9NHCbgizmZiD/mADgyhkgjUsDIkcMzEt+sA4IK9ORgIYqS+/ +q2eY1QRKpoZgJJfE8dU88B35YGqdZuXENR4I7w+JrKCCCk5jSiwylvsBsi1HX8Qu +EdQBBRiwkRnxQ83hqRI3ymw= +-----END PRIVATE KEY----- diff --git a/src/test/ssl/sslfiles.mk b/src/test/ssl/sslfiles.mk index 68fc0366df3..f32c53a76a1 100644 --- a/src/test/ssl/sslfiles.mk +++ b/src/test/ssl/sslfiles.mk @@ -29,9 +29,9 @@ SERVERS := server-cn-and-alt-names \ server-ip-cn-and-alt-names \ server-ip-cn-and-dns-alt-names \ server-ip-in-dnsname \ - server-ip-localhost \ server-single-alt-name \ server-multiple-alt-names \ + server-localhost-alt-names \ server-no-names \ server-revoked CLIENTS := client client-dn client-revoked client_ext client-long \ -- 2.51.0
From 3c1eddf0734c6c6ce7e0f91a5e991a95692f5f20 Mon Sep 17 00:00:00 2001 From: "Jonathan Gonzalez V." <[email protected]> Date: Fri, 27 Feb 2026 17:19:42 +0100 Subject: [PATCH v1.2 1/3] Add TLS support for the OAuth tests To be able to properly test some options related to certificates we require to be able to run the test over TLS, this patch provides the required infrastructure for this. The certificates are generated in `src/tests/ssl/` directory since all the configurations related to generate certificates and CA are present in that directory. Signed-off-by: Jonathan Gonzalez V. <[email protected]> --- .../modules/oauth_validator/t/001_server.pl | 17 +++++------ .../modules/oauth_validator/t/OAuth/Server.pm | 7 ++--- .../modules/oauth_validator/t/oauth_server.py | 11 +++++++- src/test/ssl/conf/server-ip-localhost.config | 18 ++++++++++++ src/test/ssl/ssl/server-ip-localhost.crt | 20 +++++++++++++ src/test/ssl/ssl/server-ip-localhost.key | 28 +++++++++++++++++++ src/test/ssl/sslfiles.mk | 1 + 7 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 src/test/ssl/conf/server-ip-localhost.config create mode 100644 src/test/ssl/ssl/server-ip-localhost.crt create mode 100644 src/test/ssl/ssl/server-ip-localhost.key diff --git a/src/test/modules/oauth_validator/t/001_server.pl b/src/test/modules/oauth_validator/t/001_server.pl index 6b649c0b06f..12da59109bf 100644 --- a/src/test/modules/oauth_validator/t/001_server.pl +++ b/src/test/modules/oauth_validator/t/001_server.pl @@ -14,6 +14,7 @@ use MIME::Base64 qw(encode_base64); use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More; +use File::Basename; use FindBin; use lib $FindBin::RealBin; @@ -72,7 +73,7 @@ END } my $port = $webserver->port(); -my $issuer = "http://127.0.0.1:$port"; +my $issuer = "https://localhost:$port"; unlink($node->data_dir . '/pg_hba.conf'); $node->append_conf( @@ -96,17 +97,13 @@ is( $contents, 3|oauth|\{issuer=$issuer/param,"scope=openid postgres",validator=validator\}}, "pg_hba_file_rules recreates OAuth HBA settings"); -# To test against HTTP rather than HTTPS, we need to enable PGOAUTHDEBUG. But -# first, check to make sure the client refuses such connections by default. -$node->connect_fails( - "user=test dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0635", - "HTTPS is required without debug mode", - expected_stderr => - qr@OAuth discovery URI "\Q$issuer\E/.well-known/openid-configuration" must use HTTPS@ -); - +# To test against HTTPS, we need to enable PGOAUTHDEBUG and provide the right +# CA to be able to verify the server certificate $ENV{PGOAUTHDEBUG} = "UNSAFE"; +my $certdir = dirname(__FILE__) . "/../../../ssl/ssl"; +$ENV{PGOAUTHCAFILE} = "$certdir/root+server_ca.crt"; + my $user = "test"; $node->connect_ok( "user=$user dbname=postgres oauth_issuer=$issuer oauth_client_id=f02c6361-0635", diff --git a/src/test/modules/oauth_validator/t/OAuth/Server.pm b/src/test/modules/oauth_validator/t/OAuth/Server.pm index 89ea7ab4a4c..84c62f38c6b 100644 --- a/src/test/modules/oauth_validator/t/OAuth/Server.pm +++ b/src/test/modules/oauth_validator/t/OAuth/Server.pm @@ -15,7 +15,7 @@ OAuth::Server - runs a mock OAuth authorization server for testing $server->run; my $port = $server->port; - my $issuer = "http://127.0.0.1:$port"; + my $issuer = "https://localhost:$port"; # test against $issuer... @@ -27,9 +27,8 @@ This is glue API between the Perl tests and the Python authorization server 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 does not use TLS (it implements a nonstandard, unsafe -issuer at "http://127.0.0.1:<port>"), so libpq in particular will need to set -PGOAUTHDEBUG=UNSAFE to be able to talk to it. +This authorization server runs over TLS, so libpq will need to set +PGOAUTHDEBUG=UNSAFE and PGOAUTHCAFILE with the right CA. =cut diff --git a/src/test/modules/oauth_validator/t/oauth_server.py b/src/test/modules/oauth_validator/t/oauth_server.py index c70783ecbe4..d7de984535b 100755 --- a/src/test/modules/oauth_validator/t/oauth_server.py +++ b/src/test/modules/oauth_validator/t/oauth_server.py @@ -11,12 +11,17 @@ import functools import http.server import json import os +import ssl import sys import time import urllib.parse from collections import defaultdict from typing import Dict +oauth_server_dir = os.path.dirname(os.path.realpath(__file__)) +ssl_dir = os.path.join(oauth_server_dir, "../../../ssl/ssl") +ssl_cert = ssl_dir + "/server-ip-localhost.crt" +ssl_key = ssl_dir + "/server-ip-localhost.key" class OAuthHandler(http.server.BaseHTTPRequestHandler): """ @@ -295,7 +300,7 @@ class OAuthHandler(http.server.BaseHTTPRequestHandler): def config(self) -> JsonObject: port = self.server.socket.getsockname()[1] - issuer = f"http://127.0.0.1:{port}" + issuer = f"https://localhost:{port}" if self._alt_issuer: issuer += "/alternate" elif self._parameterized: @@ -408,8 +413,12 @@ def main(): Starts the authorization server on localhost. The ephemeral port in use will be printed to stdout. """ + # To be able to listen over SSL we create the context and load the certificates + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + ssl_context.load_cert_chain(ssl_cert, ssl_key) s = http.server.HTTPServer(("127.0.0.1", 0), OAuthHandler) + s.socket = ssl_context.wrap_socket(s.socket, server_side=True) # Attach a "cache" dictionary to the server to allow the OAuthHandlers to # track state across token requests. The use of defaultdict ensures that new diff --git a/src/test/ssl/conf/server-ip-localhost.config b/src/test/ssl/conf/server-ip-localhost.config new file mode 100644 index 00000000000..61f10af0619 --- /dev/null +++ b/src/test/ssl/conf/server-ip-localhost.config @@ -0,0 +1,18 @@ +# An OpenSSL format CSR config file for creating a server certificate. +# + +[ req ] +distinguished_name = req_distinguished_name +prompt = no + +[ req_distinguished_name ] +CN = localhost +OU = PostgreSQL test suite + +# For Subject Alternative Names +[ v3_req ] +subjectAltName = @alt_names + +[ alt_names ] +IP.1 = 127.0.0.1 +IP.2 = ::1 diff --git a/src/test/ssl/ssl/server-ip-localhost.crt b/src/test/ssl/ssl/server-ip-localhost.crt new file mode 100644 index 00000000000..c6e4dadcef8 --- /dev/null +++ b/src/test/ssl/ssl/server-ip-localhost.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDPDCCAiSgAwIBAgIIICYCJxcTNAAwDQYJKoZIhvcNAQELBQAwQjFAMD4GA1UE +Aww3VGVzdCBDQSBmb3IgUG9zdGdyZVNRTCBTU0wgcmVncmVzc2lvbiB0ZXN0IHNl +cnZlciBjZXJ0czAgFw0yNjAyMjcxNjEzMzRaGA8yMDUzMDcxNTE2MTMzNFowNDEe +MBwGA1UECwwVUG9zdGdyZVNRTCB0ZXN0IHN1aXRlMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDC3bW97F0OjQR1E+CW +TLqPsBngwy02IBbcPZNr19HYodby82mKavwvU7IDkTCCNTU89zlHbzlF4NsnWuvA +zQlPk/kfg9OeDj2G3PKYvHe2G9xkuBEgusKsq+FkQcx3o4S93fAZKyCztgBVptfm +/q8CeJ4nsNlQI7uAYZ9s3R36PrX92qS50NvOOIm5zxRtCXX51gMVqWS7t/iyvXU2 +EVGyXLRyOArCoVkfY9AbBAJJzftzEQjqUVS7W2INUr8KLkRRJOKrzkBLg8T2+YmR +PNKdqhVMhn5uozjTgA0viQN1rVJZvxahR8zAhhqChO7+84xWEcZTl9Lc5uWDV+O4 +w4bPAgMBAAGjQjBAMB0GA1UdDgQWBBQWXk4bIK8qHwQpAQTmvNX4B/h5ozAfBgNV +HSMEGDAWgBTyjzpmQFBkSCLXLoL/Vp7Cs+50CjANBgkqhkiG9w0BAQsFAAOCAQEA +Kh0pDHJfQ/5daLpmLiOwZD3KqN8FQd5QNVJjlGD3Qh3QblVRGNl1csLSWb4BnENO +3Kq6zKkNBSoAA6bFqVhXcdL1utrnGk2fLpInGMA5B+r+BpZkUVSKu6BPn/HNnImt +PCZ3sjQyTZ1QXjDjxiOynGkDl9G+3RKDsDhdOHxoqCKUDlHkC9oi3FxkNgvvwpVu +ybio+haTmoRbOr7U5MFNbtQOglgjV4/QeFjzSm3mrSuCEJVTI4/CovbboM5xZ144 +FNunyccqdaFA9O3lKsmj03zEIkI9kNRL8IRNVf/xoVRdivNMdHhG+KrO9pqIYuYM +3Vsjv+Men06w2B0StaazZg== +-----END CERTIFICATE----- diff --git a/src/test/ssl/ssl/server-ip-localhost.key b/src/test/ssl/ssl/server-ip-localhost.key new file mode 100644 index 00000000000..748cf7a59b4 --- /dev/null +++ b/src/test/ssl/ssl/server-ip-localhost.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDC3bW97F0OjQR1 +E+CWTLqPsBngwy02IBbcPZNr19HYodby82mKavwvU7IDkTCCNTU89zlHbzlF4Nsn +WuvAzQlPk/kfg9OeDj2G3PKYvHe2G9xkuBEgusKsq+FkQcx3o4S93fAZKyCztgBV +ptfm/q8CeJ4nsNlQI7uAYZ9s3R36PrX92qS50NvOOIm5zxRtCXX51gMVqWS7t/iy +vXU2EVGyXLRyOArCoVkfY9AbBAJJzftzEQjqUVS7W2INUr8KLkRRJOKrzkBLg8T2 ++YmRPNKdqhVMhn5uozjTgA0viQN1rVJZvxahR8zAhhqChO7+84xWEcZTl9Lc5uWD +V+O4w4bPAgMBAAECggEAAnJfIopWH08bU/A54+4bAzuZgPlu7UPFqpR/FvbYfPh9 +Ka+9TsuPZ3WBz+ZksPJfhUzRd7ayOBO5rtBHEyPzaSFdXXyRR/xRvs0slz3jnZvp +Hxz6upkx5t/2Z00wz9BdsccjpS5M3OIFV1vkchCRmGCAFAzaOHckewW7/tV7Ugfa +pNuYz0WjJT7N2sU362QINABgWtD1a3vPOyspsrSb8Gox1fzeGAgCZi8oKiI3QYuk +ooyqwbs/lkXKHRZXmLc7lXMr1wWLLnMUUUGXZ/eC4vpPJ9rH33ymxShAfrUGWvXQ +fXnuabLyMT0a/uhaJZh0/jAk1BOnUnjHCHuBpyHGrQKBgQD5mpYbpAChs9vN6yFP +RPjc2q9xuJbLzsi9vTgTxRiYVUcYWJ2Lcz2wSg9nyBsHBZVFqH9uVGTCImOIDbba +8lIZmvC4nNiMxhsD1jQZQBCOU03t0Ga5ImxUYexxq/lP809qJcGKdgVSGA93ufkC +7FXJH6iWnwTk/jiM/YyaJhX/QwKBgQDH3Ap4aGf7CmkULvKei9435lt1a3YkmXmt +NOBBimLUyXJtq1m//Thx/Gp9CVa38M3fd0NH6ZZvRJBPwO+43A3EqXv/qXn9dNcA +xY2bYtRrm3LGilNuG2C/8T+cZ+T9hX4dSIwefJMYGPDkZNwgwl/tjRjbZZE+KWv8 +VGQ87HVjhQKBgQCMZW5vh7UvP1qwncQzsUkF+R/cKIbxhpOVXhxvylpGPRlrUVT0 +flLBmTbHGmBRd8t5zgg3h9LQ+8TeX1BuIQUbD/K89MQ9kqTZaKAPX+CwHZ1k2ecd +1YX3hMkZOzFVzjbqLuiJOE9P2ObCYmH1SfgK0/rhFfsLzw8CBxASGMAgvwKBgBr6 +wlMUzQyfkCXQXKI4gWwMZcZJFm7EZR+Tpr5SPxs4goD5g6keNtN0Xq+4ZgN4t2H2 +SJfZmZw1pkGN6w6KbjVhJ8MQjs4/SpLpGD+krMZF+s2AeNjBS2M93vdvMwNiVO8B +DyFCcdzr6QD7+JdXhfmcdYGQiXXZw6ERh2KODR69AoGBAPQ4IRTIh3jfBj2xZenI +MDegCiOIAO516GTX0C1aB3jFhH4pcdbiN2R/2dffdvZvuQmJ4kCmyggvyx9QjUHL +/Kxk0jacA14LFOmrnlihwET4gfeL8S3vzhJcNcv0GXOgWe4hP9LH+eaidICx/JlD +hYX8HYSKQxIFmt5IUAxlUvoj +-----END PRIVATE KEY----- diff --git a/src/test/ssl/sslfiles.mk b/src/test/ssl/sslfiles.mk index ecb40588c87..68fc0366df3 100644 --- a/src/test/ssl/sslfiles.mk +++ b/src/test/ssl/sslfiles.mk @@ -29,6 +29,7 @@ SERVERS := server-cn-and-alt-names \ server-ip-cn-and-alt-names \ server-ip-cn-and-dns-alt-names \ server-ip-in-dnsname \ + server-ip-localhost \ server-single-alt-name \ server-multiple-alt-names \ server-no-names \ -- 2.51.0
signature.asc
Description: This is a digitally signed message part
