route/vendor/github.com/lib/pq/ssl_test.go

280 lines
8.0 KiB
Go

package pq
// This file contains SSL tests
import (
_ "crypto/sha256"
"crypto/x509"
"database/sql"
"os"
"path/filepath"
"testing"
)
func maybeSkipSSLTests(t *testing.T) {
// Require some special variables for testing certificates
if os.Getenv("PQSSLCERTTEST_PATH") == "" {
t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests")
}
value := os.Getenv("PQGOSSLTESTS")
if value == "" || value == "0" {
t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests")
} else if value != "1" {
t.Fatalf("unexpected value %q for PQGOSSLTESTS", value)
}
}
func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) {
db, err := openTestConnConninfo(conninfo)
if err != nil {
// should never fail
t.Fatal(err)
}
// Do something with the connection to see whether it's working or not.
tx, err := db.Begin()
if err == nil {
return db, tx.Rollback()
}
_ = db.Close()
return nil, err
}
func checkSSLSetup(t *testing.T, conninfo string) {
_, err := openSSLConn(t, conninfo)
if pge, ok := err.(*Error); ok {
if pge.Code.Name() != "invalid_authorization_specification" {
t.Fatalf("unexpected error code '%s'", pge.Code.Name())
}
} else {
t.Fatalf("expected %T, got %v", (*Error)(nil), err)
}
}
// Connect over SSL and run a simple query to test the basics
func TestSSLConnection(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
db, err := openSSLConn(t, "sslmode=require user=pqgossltest")
if err != nil {
t.Fatal(err)
}
rows, err := db.Query("SELECT 1")
if err != nil {
t.Fatal(err)
}
rows.Close()
}
// Test sslmode=verify-full
func TestSSLVerifyFull(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
// Not OK according to the system CA
_, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest")
if err == nil {
t.Fatal("expected error")
}
_, ok := err.(x509.UnknownAuthorityError)
if !ok {
t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
}
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
rootCert := "sslrootcert=" + rootCertPath + " "
// No match on Common Name
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest")
if err == nil {
t.Fatal("expected error")
}
_, ok = err.(x509.HostnameError)
if !ok {
t.Fatalf("expected x509.HostnameError, got %#+v", err)
}
// OK
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest")
if err != nil {
t.Fatal(err)
}
}
// Test sslmode=require sslrootcert=rootCertPath
func TestSSLRequireWithRootCert(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
bogusRootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "bogus_root.crt")
bogusRootCert := "sslrootcert=" + bogusRootCertPath + " "
// Not OK according to the bogus CA
_, err := openSSLConn(t, bogusRootCert+"host=postgres sslmode=require user=pqgossltest")
if err == nil {
t.Fatal("expected error")
}
_, ok := err.(x509.UnknownAuthorityError)
if !ok {
t.Fatalf("expected x509.UnknownAuthorityError, got %s, %#+v", err, err)
}
nonExistentCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "non_existent.crt")
nonExistentCert := "sslrootcert=" + nonExistentCertPath + " "
// No match on Common Name, but that's OK because we're not validating anything.
_, err = openSSLConn(t, nonExistentCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
if err != nil {
t.Fatal(err)
}
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
rootCert := "sslrootcert=" + rootCertPath + " "
// No match on Common Name, but that's OK because we're not validating the CN.
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
if err != nil {
t.Fatal(err)
}
// Everything OK
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=require user=pqgossltest")
if err != nil {
t.Fatal(err)
}
}
// Test sslmode=verify-ca
func TestSSLVerifyCA(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
// Not OK according to the system CA
{
_, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest")
if _, ok := err.(x509.UnknownAuthorityError); !ok {
t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err)
}
}
// Still not OK according to the system CA; empty sslrootcert is treated as unspecified.
{
_, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest sslrootcert=''")
if _, ok := err.(x509.UnknownAuthorityError); !ok {
t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err)
}
}
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
rootCert := "sslrootcert=" + rootCertPath + " "
// No match on Common Name, but that's OK
if _, err := openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest"); err != nil {
t.Fatal(err)
}
// Everything OK
if _, err := openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest"); err != nil {
t.Fatal(err)
}
}
// Authenticate over SSL using client certificates
func TestSSLClientCertificates(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
const baseinfo = "sslmode=require user=pqgosslcert"
// Certificate not specified, should fail
{
_, err := openSSLConn(t, baseinfo)
if pge, ok := err.(*Error); ok {
if pge.Code.Name() != "invalid_authorization_specification" {
t.Fatalf("unexpected error code '%s'", pge.Code.Name())
}
} else {
t.Fatalf("expected %T, got %v", (*Error)(nil), err)
}
}
// Empty certificate specified, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert=''")
if pge, ok := err.(*Error); ok {
if pge.Code.Name() != "invalid_authorization_specification" {
t.Fatalf("unexpected error code '%s'", pge.Code.Name())
}
} else {
t.Fatalf("expected %T, got %v", (*Error)(nil), err)
}
}
// Non-existent certificate specified, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert=/tmp/filedoesnotexist")
if pge, ok := err.(*Error); ok {
if pge.Code.Name() != "invalid_authorization_specification" {
t.Fatalf("unexpected error code '%s'", pge.Code.Name())
}
} else {
t.Fatalf("expected %T, got %v", (*Error)(nil), err)
}
}
certpath, ok := os.LookupEnv("PQSSLCERTTEST_PATH")
if !ok {
t.Fatalf("PQSSLCERTTEST_PATH not present in environment")
}
sslcert := filepath.Join(certpath, "postgresql.crt")
// Cert present, key not specified, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert="+sslcert)
if _, ok := err.(*os.PathError); !ok {
t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
}
}
// Cert present, empty key specified, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=''")
if _, ok := err.(*os.PathError); !ok {
t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
}
}
// Cert present, non-existent key, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=/tmp/filedoesnotexist")
if _, ok := err.(*os.PathError); !ok {
t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
}
}
// Key has wrong permissions (passing the cert as the key), should fail
if _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslcert); err != ErrSSLKeyHasWorldPermissions {
t.Fatalf("expected %s, got %#+v", ErrSSLKeyHasWorldPermissions, err)
}
sslkey := filepath.Join(certpath, "postgresql.key")
// Should work
if db, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslkey); err != nil {
t.Fatal(err)
} else {
rows, err := db.Query("SELECT 1")
if err != nil {
t.Fatal(err)
}
if err := rows.Close(); err != nil {
t.Fatal(err)
}
if err := db.Close(); err != nil {
t.Fatal(err)
}
}
}