diff options
| author | James Elliott <james-d-elliott@users.noreply.github.com> | 2024-12-30 17:59:36 +1100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-12-30 06:59:36 +0000 |
| commit | 31565e447ba1e357828c99db5410de879bfd7669 (patch) | |
| tree | 7683ef81e1372f5cc686677670dfe5d8814951e0 /internal/storage | |
| parent | 29b582e1735aff6baabf503c0a2dee533fc41ebe (diff) | |
fix(configuration): allow unix socket ports (#8520)
This allows unix sockets to include ports in the address URL. In addition allows for a absolute path for the PostgreSQL socket type. Both options are only used by PostgreSQL but other unix sockets will not expressly error if this is included.
Fixes #8509
Diffstat (limited to 'internal/storage')
| -rw-r--r-- | internal/storage/const.go | 3 | ||||
| -rw-r--r-- | internal/storage/sql_provider_backend_postgres.go | 55 | ||||
| -rw-r--r-- | internal/storage/sql_provider_backend_postgres_test.go | 305 |
3 files changed, 349 insertions, 14 deletions
diff --git a/internal/storage/const.go b/internal/storage/const.go index 907842b89..84066fa23 100644 --- a/internal/storage/const.go +++ b/internal/storage/const.go @@ -70,7 +70,8 @@ const ( ) var ( - reMigration = regexp.MustCompile(`^V(?P<Version>\d{4})\.(?P<Name>[^.]+)\.(?P<Direction>(up|down))\.sql$`) + reMigration = regexp.MustCompile(`^V(?P<Version>\d{4})\.(?P<Name>[^.]+)\.(?P<Direction>(up|down))\.sql$`) + rePostgreSQLUnixDomainSocket = regexp.MustCompile(`^\.s\.PGSQL\.(\d+)$`) ) const ( diff --git a/internal/storage/sql_provider_backend_postgres.go b/internal/storage/sql_provider_backend_postgres.go index 41dd83db0..2886d387e 100644 --- a/internal/storage/sql_provider_backend_postgres.go +++ b/internal/storage/sql_provider_backend_postgres.go @@ -3,9 +3,12 @@ package storage import ( "crypto/tls" "crypto/x509" + "encoding/pem" "errors" "fmt" "os" + "path/filepath" + "strconv" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/stdlib" @@ -153,8 +156,7 @@ func NewPostgreSQLProvider(config *schema.Configuration, caCertPool *x509.CertPo func dsnPostgreSQL(config *schema.StoragePostgreSQL, globalCACertPool *x509.CertPool) (dsn string) { dsnConfig, _ := pgx.ParseConfig("") - dsnConfig.Host = config.Address.SocketHostname() - dsnConfig.Port = config.Address.Port() + dsnConfig.Host, dsnConfig.Port = dsnPostgreSQLHostPort(config.Address) dsnConfig.Database = config.Database dsnConfig.User = config.Username dsnConfig.Password = config.Password @@ -164,11 +166,34 @@ func dsnPostgreSQL(config *schema.StoragePostgreSQL, globalCACertPool *x509.Cert "search_path": config.Schema, } - if dsnConfig.Port == 0 && config.Address.IsUnixDomainSocket() { - dsnConfig.Port = 5432 + return stdlib.RegisterConnConfig(dsnConfig) +} + +func dsnPostgreSQLHostPort(address *schema.AddressTCP) (host string, port uint16) { + if !address.IsUnixDomainSocket() { + return address.SocketHostname(), address.Port() } - return stdlib.RegisterConnConfig(dsnConfig) + host, port = address.SocketHostname(), address.Port() + + if port == 0 { + port = 5432 + } + + dir, base := filepath.Dir(host), filepath.Base(host) + + matches := rePostgreSQLUnixDomainSocket.FindStringSubmatch(base) + + if len(matches) != 2 { + return host, port + } + + if raw, err := strconv.ParseUint(matches[1], 10, 16); err == nil { + host = dir + port = uint16(raw) + } + + return host, port } func loadPostgreSQLTLSConfig(config *schema.StoragePostgreSQL, globalCACertPool *x509.CertPool) (tlsConfig *tls.Config) { @@ -231,34 +256,38 @@ func loadPostgreSQLLegacyTLSConfigFiles(config *schema.StoragePostgreSQL) (ca *x ) if config.SSL.RootCertificate != "" { - var caPEMBlock []byte + var ( + data []byte + block *pem.Block + ) - if caPEMBlock, err = os.ReadFile(config.SSL.RootCertificate); err != nil { + if data, err = os.ReadFile(config.SSL.RootCertificate); err != nil { return nil, nil } - if ca, err = x509.ParseCertificate(caPEMBlock); err != nil { + block, _ = pem.Decode(data) + + if ca, err = x509.ParseCertificate(block.Bytes); err != nil { return nil, nil } } if config.SSL.Certificate != "" && config.SSL.Key != "" { var ( - keyPEMBlock []byte - certPEMBlock []byte + dataKey, dataCert []byte ) - if keyPEMBlock, err = os.ReadFile(config.SSL.Key); err != nil { + if dataKey, err = os.ReadFile(config.SSL.Key); err != nil { return nil, nil } - if certPEMBlock, err = os.ReadFile(config.SSL.Certificate); err != nil { + if dataCert, err = os.ReadFile(config.SSL.Certificate); err != nil { return nil, nil } var cert tls.Certificate - if cert, err = tls.X509KeyPair(certPEMBlock, keyPEMBlock); err != nil { + if cert, err = tls.X509KeyPair(dataCert, dataKey); err != nil { return nil, nil } diff --git a/internal/storage/sql_provider_backend_postgres_test.go b/internal/storage/sql_provider_backend_postgres_test.go new file mode 100644 index 000000000..4a56c25a1 --- /dev/null +++ b/internal/storage/sql_provider_backend_postgres_test.go @@ -0,0 +1,305 @@ +package storage + +import ( + "crypto/tls" + "crypto/x509" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/authelia/authelia/v4/internal/configuration/schema" +) + +func TestNewPostgreSQLProvider(t *testing.T) { + address, err := schema.NewAddress("tcp://localhost:5432") + require.NoError(t, err) + + testCases := []struct { + name string + have *schema.Configuration + }{ + { + "ShouldHandleSimple", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + }, + }, + }, + }, + { + "ShouldHandleTLS", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + TLS: &schema.TLS{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS12}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSVerifyFull", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "verify-full", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSVerifyCA", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "verify-ca", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSRequire", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "require", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSDisabled", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "disable", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSVerifyCARootCA", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "verify-ca", + RootCertificate: "../configuration/test_resources/crypto/ca.rsa.2048.crt", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSVerifyCAAllCertificates", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "verify-ca", + RootCertificate: "../configuration/test_resources/crypto/ca.rsa.2048.crt", + Certificate: "../configuration/test_resources/crypto/rsa.2048.crt", + Key: "../configuration/test_resources/crypto/rsa.2048.pem", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSVerifyCAAllCertificatesFailReadFileCA", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "verify-ca", + RootCertificate: "../configuration/test_resources/crypto/ca.rsa.2048.cert", + Certificate: "../configuration/test_resources/crypto/rsa.2048.crt", + Key: "../configuration/test_resources/crypto/rsa.2048.pem", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSVerifyCAAllCertificatesFailReadFileKey", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "verify-ca", + RootCertificate: "../configuration/test_resources/crypto/ca.rsa.2048.crt", + Certificate: "../configuration/test_resources/crypto/rsa.2048.crt", + Key: "../configuration/test_resources/crypto/rsa.2048.key", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSVerifyCAAllCertificatesFailReadFileCertificate", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "verify-ca", + RootCertificate: "../configuration/test_resources/crypto/ca.rsa.2048.crt", + Certificate: "../configuration/test_resources/crypto/rsa.2048.cert", + Key: "../configuration/test_resources/crypto/rsa.2048.pem", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSVerifyCAAllCertificatesFailPair", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "verify-ca", + RootCertificate: "../configuration/test_resources/crypto/ca.rsa.2048.crt", + Certificate: "../configuration/test_resources/crypto/rsa.2048.crt", + Key: "../configuration/test_resources/crypto/rsa.4096.pem", + }, + }, + }, + }, + }, + { + "ShouldHandleLegacyTLSVerifyCAAllCertificatesFailReadCACertificateFromPrivateKey", + &schema.Configuration{ + Storage: schema.Storage{ + PostgreSQL: &schema.StoragePostgreSQL{ + StorageSQL: schema.StorageSQL{ + Address: &schema.AddressTCP{Address: *address}, + }, + SSL: &schema.StoragePostgreSQLSSL{ + Mode: "verify-ca", + RootCertificate: "../configuration/test_resources/crypto/ca.rsa.2048.pem", + Certificate: "../configuration/test_resources/crypto/rsa.2048.crt", + Key: "../configuration/test_resources/crypto/rsa.2048.pem", + }, + }, + }, + }, + }, + } + + t.Parallel() + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + provider := NewPostgreSQLProvider(tc.have, x509.NewCertPool()) + + assert.NotNil(t, provider) + }) + } +} + +func TestDSNConfigPostgreSQLHostPort(t *testing.T) { + testCases := []struct { + name string + have string + hexpected string + pexpected uint16 + }{ + { + "ShouldParseDirectoryDefaultPort", + "unix:///tmp", + "/tmp", + 5432, + }, + { + "ShouldParseURLPort", + "unix://:255/tmp", + "/tmp", + 255, + }, + { + "ShouldParseAbsolutePort", + "unix:///tmp/.s.PGSQL.25432", + "/tmp", + 25432, + }, + { + "ShouldParseAbsolutePortWithURLPort", + "unix://:2455/tmp/.s.PGSQL.25432", + "/tmp", + 25432, + }, + { + "ShouldParseAbsolutePortInvalidWithURLPort", + "unix://:2455/tmp/.s.PGSQL.233335432", + "/tmp/.s.PGSQL.233335432", + 2455, + }, + { + "ShouldParseAbsolutePortInvalid", + "unix:///tmp/.s.PGSQL.233335432", + "/tmp/.s.PGSQL.233335432", + 5432, + }, + } + + t.Parallel() + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + address, err := schema.NewAddress(tc.have) + require.NotNil(t, address) + require.NoError(t, err) + + host, port := dsnPostgreSQLHostPort(&schema.AddressTCP{Address: *address}) + assert.Equal(t, tc.hexpected, host) + assert.Equal(t, tc.pexpected, port) + }) + } +} |
