From 0819a17da8863cd8bd6819dfed246d6101f67086 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 27 Jan 2024 08:42:48 -0600 Subject: [PATCH] Remove openssl from TLS test setup TLS setup and tests were rather finicky. It seems that openssl 3 encrypts certificates differently than older openssl and it does it in a way Go and/or pgx ssl handling code can't handle. It appears that this related to the use of a deprecated client certificate encryption system. This caused CI to be stuck on Ubuntu 20.04 and recently caused the contributing guide to fail to work on MacOS. Remove openssl from the test setup and replace it with a Go program that generates the certificates. --- .github/workflows/ci.yml | 5 +- CONTRIBUTING.md | 18 +--- ci/setup_test.bash | 15 +-- testsetup/ca.cnf | 6 -- testsetup/generate_certs.go | 187 ++++++++++++++++++++++++++++++++++++ testsetup/localhost.cnf | 13 --- testsetup/pgx_sslcert.cnf | 9 -- 7 files changed, 192 insertions(+), 61 deletions(-) delete mode 100644 testsetup/ca.cnf create mode 100644 testsetup/generate_certs.go delete mode 100644 testsetup/localhost.cnf delete mode 100644 testsetup/pgx_sslcert.cnf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1bf91c32..f45c3045c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,10 +9,7 @@ on: jobs: test: name: Test - # Note: The TLS tests are rather finicky. It seems that openssl 3 encrypts certificates differently than older - # openssl and it does it in a way Go and/or pgx ssl handling code can't handle. So stick with Ubuntu 20.04 until - # that is figured out. - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3eb0da5b9..6ed3205ce 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -79,20 +79,11 @@ echo "listen_addresses = '127.0.0.1'" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql echo "port = $PGPORT" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf cat testsetup/postgresql_ssl.conf >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf cp testsetup/pg_hba.conf .testdb/$POSTGRESQL_DATA_DIR/pg_hba.conf -cp testsetup/ca.cnf .testdb -cp testsetup/localhost.cnf .testdb -cp testsetup/pgx_sslcert.cnf .testdb cd .testdb -# Generate a CA public / private key pair. -openssl genrsa -out ca.key 4096 -openssl req -x509 -config ca.cnf -new -nodes -key ca.key -sha256 -days 365 -subj '/O=pgx-test-root' -out ca.pem - -# Generate the certificate for localhost (the server). -openssl genrsa -out localhost.key 2048 -openssl req -new -config localhost.cnf -key localhost.key -out localhost.csr -openssl x509 -req -in localhost.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out localhost.crt -days 364 -sha256 -extfile localhost.cnf -extensions v3_req +# Generate CA, server, and encrypted client certificates. +go run ../testsetup/generate_certs.go # Copy certificates to server directory and set permissions. cp ca.pem $POSTGRESQL_DATA_DIR/root.crt @@ -100,11 +91,6 @@ cp localhost.key $POSTGRESQL_DATA_DIR/server.key chmod 600 $POSTGRESQL_DATA_DIR/server.key cp localhost.crt $POSTGRESQL_DATA_DIR/server.crt -# Generate the certificate for client authentication. -openssl genrsa -des3 -out pgx_sslcert.key -passout pass:certpw 2048 -openssl req -new -config pgx_sslcert.cnf -key pgx_sslcert.key -passin pass:certpw -out pgx_sslcert.csr -openssl x509 -req -in pgx_sslcert.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out pgx_sslcert.crt -days 363 -sha256 -extfile pgx_sslcert.cnf -extensions v3_req - cd .. ``` diff --git a/ci/setup_test.bash b/ci/setup_test.bash index f96d2b720..66ba07d4d 100755 --- a/ci/setup_test.bash +++ b/ci/setup_test.bash @@ -16,14 +16,8 @@ then cd testsetup - # Generate a CA public / private key pair. - openssl genrsa -out ca.key 4096 - openssl req -x509 -config ca.cnf -new -nodes -key ca.key -sha256 -days 365 -subj '/O=pgx-test-root' -out ca.pem - - # Generate the certificate for localhost (the server). - openssl genrsa -out localhost.key 2048 - openssl req -new -config localhost.cnf -key localhost.key -out localhost.csr - openssl x509 -req -in localhost.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out localhost.crt -days 364 -sha256 -extfile localhost.cnf -extensions v3_req + # Generate CA, server, and encrypted client certificates. + go run generate_certs.go # Copy certificates to server directory and set permissions. sudo cp ca.pem /var/lib/postgresql/$PGVERSION/main/root.crt @@ -34,11 +28,6 @@ then sudo cp localhost.crt /var/lib/postgresql/$PGVERSION/main/server.crt sudo chown postgres:postgres /var/lib/postgresql/$PGVERSION/main/server.crt - # Generate the certificate for client authentication. - openssl genrsa -des3 -out pgx_sslcert.key -passout pass:certpw 2048 - openssl req -new -config pgx_sslcert.cnf -key pgx_sslcert.key -passin pass:certpw -out pgx_sslcert.csr - openssl x509 -req -in pgx_sslcert.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out pgx_sslcert.crt -days 363 -sha256 -extfile pgx_sslcert.cnf -extensions v3_req - cp ca.pem /tmp cp pgx_sslcert.key /tmp cp pgx_sslcert.crt /tmp diff --git a/testsetup/ca.cnf b/testsetup/ca.cnf deleted file mode 100644 index bd0180375..000000000 --- a/testsetup/ca.cnf +++ /dev/null @@ -1,6 +0,0 @@ -[ req ] -distinguished_name = dn -[ dn ] -commonName = ca -[ ext ] -basicConstraints =CA:TRUE,pathlen:0 diff --git a/testsetup/generate_certs.go b/testsetup/generate_certs.go new file mode 100644 index 000000000..945c6c5e2 --- /dev/null +++ b/testsetup/generate_certs.go @@ -0,0 +1,187 @@ +// Generates a CA, server certificate, and encrypted client certificate for testing pgx. + +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "net" + "os" + "time" +) + +func main() { + // Create the CA + ca := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "pgx-root-ca", + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(20, 0, 0), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + + caKey, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + panic(err) + } + + caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caKey.PublicKey, caKey) + if err != nil { + panic(err) + } + + err = writePrivateKey("ca.key", caKey) + if err != nil { + panic(err) + } + + err = writeCertificate("ca.pem", caBytes) + if err != nil { + panic(err) + } + + // Create a server certificate signed by the CA for localhost. + serverCert := &x509.Certificate{ + SerialNumber: big.NewInt(2), + Subject: pkix.Name{ + CommonName: "localhost", + }, + DNSNames: []string{"localhost"}, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback}, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(20, 0, 0), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } + + serverCertPrivKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + panic(err) + } + + serverBytes, err := x509.CreateCertificate(rand.Reader, serverCert, ca, &serverCertPrivKey.PublicKey, caKey) + if err != nil { + panic(err) + } + + err = writePrivateKey("localhost.key", serverCertPrivKey) + if err != nil { + panic(err) + } + + err = writeCertificate("localhost.crt", serverBytes) + if err != nil { + panic(err) + } + + // Create a client certificate signed by the CA and encrypted. + clientCert := &x509.Certificate{ + SerialNumber: big.NewInt(3), + Subject: pkix.Name{ + CommonName: "pgx_sslcert", + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(20, 0, 0), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } + + clientCertPrivKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + panic(err) + } + + clientBytes, err := x509.CreateCertificate(rand.Reader, clientCert, ca, &clientCertPrivKey.PublicKey, caKey) + if err != nil { + panic(err) + } + + writeEncryptedPrivateKey("pgx_sslcert.key", clientCertPrivKey, "certpw") + if err != nil { + panic(err) + } + + writeCertificate("pgx_sslcert.crt", clientBytes) + if err != nil { + panic(err) + } +} + +func writePrivateKey(path string, privateKey *rsa.PrivateKey) error { + file, err := os.Create(path) + if err != nil { + return fmt.Errorf("writePrivateKey: %w", err) + } + + err = pem.Encode(file, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(privateKey), + }) + if err != nil { + return fmt.Errorf("writePrivateKey: %w", err) + } + + err = file.Close() + if err != nil { + return fmt.Errorf("writePrivateKey: %w", err) + } + + return nil +} + +func writeEncryptedPrivateKey(path string, privateKey *rsa.PrivateKey, password string) error { + file, err := os.Create(path) + if err != nil { + return fmt.Errorf("writeEncryptedPrivateKey: %w", err) + } + + block, err := x509.EncryptPEMBlock(rand.Reader, "CERTIFICATE", x509.MarshalPKCS1PrivateKey(privateKey), []byte(password), x509.PEMCipher3DES) + if err != nil { + return fmt.Errorf("writeEncryptedPrivateKey: %w", err) + } + + err = pem.Encode(file, block) + if err != nil { + return fmt.Errorf("writeEncryptedPrivateKey: %w", err) + } + + err = file.Close() + if err != nil { + return fmt.Errorf("writeEncryptedPrivateKey: %w", err) + } + + return nil + +} + +func writeCertificate(path string, certBytes []byte) error { + file, err := os.Create(path) + if err != nil { + return fmt.Errorf("writeCertificate: %w", err) + } + + err = pem.Encode(file, &pem.Block{ + Type: "CERTIFICATE", + Bytes: certBytes, + }) + if err != nil { + return fmt.Errorf("writeCertificate: %w", err) + } + + err = file.Close() + if err != nil { + return fmt.Errorf("writeCertificate: %w", err) + } + + return nil +} diff --git a/testsetup/localhost.cnf b/testsetup/localhost.cnf deleted file mode 100644 index 14dcd57f4..000000000 --- a/testsetup/localhost.cnf +++ /dev/null @@ -1,13 +0,0 @@ -[ req ] -default_bits = 2048 -distinguished_name = dn -req_extensions = v3_req -prompt = no -[ dn ] -commonName = localhost -[ v3_req ] -subjectAltName = @alt_names -keyUsage = digitalSignature -extendedKeyUsage = serverAuth -[alt_names] -DNS.1 = localhost diff --git a/testsetup/pgx_sslcert.cnf b/testsetup/pgx_sslcert.cnf deleted file mode 100644 index 2d5d0ff7f..000000000 --- a/testsetup/pgx_sslcert.cnf +++ /dev/null @@ -1,9 +0,0 @@ -[ req ] -default_bits = 2048 -distinguished_name = dn -req_extensions = v3_req -prompt = no -[ dn ] -commonName = pgx_sslcert -[ v3_req ] -keyUsage = digitalSignature