Hi!!

After some discussion about PGOAUTHCAFILE[1] variable, the need of
having the TLS support for the tests added in a different patch[2].

I'm attaching a patch that add this TLS support making use of the
already certs system in the `src/test/ssl/` directory and just making
the `oauth_server.py` script able to support TLS only, this removes the
plain HTTP support from the server.

My Python skills are old, but I tried to keep the modifications as
simple as possible, even that there's an easy way to have the context
in Python 3.14, I decided to just have a context because the function
HTTPSServer() is only available from Python 3.14 and above, which is
not so widely used yet, in the future for sure will be more simple to
use that function.


[1]
https://www.postgresql.org/message-id/flat/16a91d02795cb991963326a902afa764e4d721db.camel%40gmail.com
[2]
https://www.postgresql.org/message-id/flat/9850AB21-78A7-43CD-94C6-FA8E3BC9F1B3%40yesql.se#11e8d9febb3c3b428caa383f0c3f6acf
-- 
Jonathan Gonzalez V. <[email protected]>
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 1/1] 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

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to