From 34bf0a5df957597aaef6997388ce77f64b69a8d8 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 19 Feb 2022 08:00:49 -0600 Subject: [PATCH 1/9] Upgrade golang.org/x/text to v0.3.7 https://github.com/jackc/pgconn/issues/103 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 6fdd0e97..fb3ed181 100644 --- a/go.mod +++ b/go.mod @@ -11,5 +11,5 @@ require ( github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 - golang.org/x/text v0.3.6 + golang.org/x/text v0.3.7 ) diff --git a/go.sum b/go.sum index 3c77ee21..bdb5ee8c 100644 --- a/go.sum +++ b/go.sum @@ -114,6 +114,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= From ccb96b8aca08245245455a3d6c168e7768d51734 Mon Sep 17 00:00:00 2001 From: William Storey Date: Wed, 16 Feb 2022 11:34:09 -0800 Subject: [PATCH 2/9] Fix typos in comments --- pgconn.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pgconn.go b/pgconn.go index 7bf2f20e..29889a74 100644 --- a/pgconn.go +++ b/pgconn.go @@ -99,7 +99,7 @@ type PgConn struct { } // Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format) -// to provide configuration. See documention for ParseConfig for details. ctx can be used to cancel a connect attempt. +// to provide configuration. See documentation for ParseConfig for details. ctx can be used to cancel a connect attempt. func Connect(ctx context.Context, connString string) (*PgConn, error) { config, err := ParseConfig(connString) if err != nil { @@ -154,7 +154,7 @@ func ConnectConfig(ctx context.Context, config *Config) (pgConn *PgConn, err err break } else if pgerr, ok := err.(*PgError); ok { err = &connectError{config: config, msg: "server error", err: pgerr} - ERRCODE_INVALID_PASSWORD := "28P01" // worng password + ERRCODE_INVALID_PASSWORD := "28P01" // wrong password ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION := "28000" // db does not exist if pgerr.Code == ERRCODE_INVALID_PASSWORD || pgerr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION { break From ded272b1f2c31e345b3209d7ab373822bf90b761 Mon Sep 17 00:00:00 2001 From: William Storey Date: Wed, 16 Feb 2022 11:37:26 -0800 Subject: [PATCH 3/9] Remove documentation line stating only one IP is used With `expandWithIPs()` (added in #14), we try all IPs. --- config.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/config.go b/config.go index 0eab23af..5cee9297 100644 --- a/config.go +++ b/config.go @@ -176,8 +176,6 @@ func NetworkAddress(host string, port uint16) (network, address string) { // // Other known differences with libpq: // -// If a host name resolves into multiple addresses, libpq will try all addresses. pgconn will only try the first. -// // When multiple hosts are specified, libpq allows them to have different passwords set via the .pgpass file. pgconn // does not. // From b7a85d1a6fc58df695e8cf0571ebf4e7dab921d5 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 5 Mar 2022 08:23:58 -0600 Subject: [PATCH 4/9] Consider any "0A000" error a possible cached plan changed error https://github.com/jackc/pgx/issues/1162 --- stmtcache/lru.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/stmtcache/lru.go b/stmtcache/lru.go index 90fb76c2..f0fb53b9 100644 --- a/stmtcache/lru.go +++ b/stmtcache/lru.go @@ -102,10 +102,14 @@ func (c *LRU) StatementErrored(sql string, err error) { return } - isInvalidCachedPlanError := pgErr.Severity == "ERROR" && - pgErr.Code == "0A000" && - pgErr.Message == "cached plan must not change result type" - if isInvalidCachedPlanError { + // https://github.com/jackc/pgx/issues/1162 + // + // We used to look for the message "cached plan must not change result type". However, that message can be localized. + // Unfortunately, error code "0A000" - "FEATURE NOT SUPPORTED" is used for many different errors and the only way to + // tell the difference is by the message. But all that happens is we clear a statement that we otherwise wouldn't + // have so it should be safe. + possibleInvalidCachedPlanError := pgErr.Code == "0A000" + if possibleInvalidCachedPlanError { c.stmtsToClear = append(c.stmtsToClear, sql) } } From 5982e4b4f881f4a71c4cacb8f2addf3ac5386921 Mon Sep 17 00:00:00 2001 From: Matthew Gabeler-Lee Date: Mon, 4 Apr 2022 15:27:52 -0400 Subject: [PATCH 5/9] fix detection of database does not exist error during connect --- pgconn.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pgconn.go b/pgconn.go index 29889a74..9a496ed0 100644 --- a/pgconn.go +++ b/pgconn.go @@ -154,9 +154,12 @@ func ConnectConfig(ctx context.Context, config *Config) (pgConn *PgConn, err err break } else if pgerr, ok := err.(*PgError); ok { err = &connectError{config: config, msg: "server error", err: pgerr} - ERRCODE_INVALID_PASSWORD := "28P01" // wrong password - ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION := "28000" // db does not exist - if pgerr.Code == ERRCODE_INVALID_PASSWORD || pgerr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION { + const ERRCODE_INVALID_PASSWORD = "28P01" // wrong password + const ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION = "28000" // wrong password or bad pg_hba.conf settings + const ERRCODE_INVALID_CATALOG_NAME = "3D000" // db does not exist + if pgerr.Code == ERRCODE_INVALID_PASSWORD || + pgerr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION || + pgerr.Code == ERRCODE_INVALID_CATALOG_NAME { break } } From 90ef5bba3fff4eaa6e8d2faf20489bb907876b8b Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Wed, 13 Apr 2022 07:15:08 +1000 Subject: [PATCH 6/9] add GSSAPI authentication This commit adds the GSSAPI authentication to pgx. This roughly follows the lib/pq implementation: * We require registering a provider to avoid mass dependency inclusions that may not be desired (https://github.com/lib/pq/issues/971). * Requires the pgproto3 package be updated. I've included my custom fork for now. --- config.go | 2 ++ go.mod | 2 +- go.sum | 4 +-- krb5.go | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ pgconn.go | 7 ++++- 5 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 krb5.go diff --git a/config.go b/config.go index 5cee9297..6e6930ee 100644 --- a/config.go +++ b/config.go @@ -257,6 +257,8 @@ func ParseConfig(connString string) (*Config, error) { "sslkey": {}, "sslcert": {}, "sslrootcert": {}, + "krbspn": {}, + "krbsrvname": {}, "target_session_attrs": {}, "min_read_buffer_size": {}, "service": {}, diff --git a/go.mod b/go.mod index fb3ed181..2a2d6810 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/jackc/pgio v1.0.0 github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 github.com/jackc/pgpassfile v1.0.0 - github.com/jackc/pgproto3/v2 v2.1.1 + github.com/jackc/pgproto3/v2 v2.2.1-0.20220412121321-175856ffd3c8 github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 diff --git a/go.sum b/go.sum index bdb5ee8c..c558564b 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,9 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.2.1-0.20220412121321-175856ffd3c8 h1:KxsCQec+1iwJXtxnbbS/dY0EJ6rJEUlFsrJUnL5A2XI= +github.com/jackc/pgproto3/v2 v2.2.1-0.20220412121321-175856ffd3c8/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= @@ -112,7 +113,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/krb5.go b/krb5.go new file mode 100644 index 00000000..1f9ce97c --- /dev/null +++ b/krb5.go @@ -0,0 +1,94 @@ +package pgconn + +import ( + "errors" + "github.com/jackc/pgproto3/v2" +) + +// NewGSSFunc creates a GSS authentication provider, for use with +// RegisterGSSProvider. +type NewGSSFunc func() (GSS, error) + +var newGSS NewGSSFunc + +// RegisterGSSProvider registers a GSS authentication provider. For example, if +// you need to use Kerberos to authenticate with your server, add this to your +// main package: +// +// import "github.com/otan/gopgkrb5" +// +// func init() { +// pgconn.RegisterGSSProvider(func() (pgconn.GSS, error) { return gopgkrb5.NewGSS() }) +// } +func RegisterGSSProvider(newGSSArg NewGSSFunc) { + newGSS = newGSSArg +} + +// GSS provides GSSAPI authentication (e.g., Kerberos). +type GSS interface { + GetInitToken(host string, service string) ([]byte, error) + GetInitTokenFromSPN(spn string) ([]byte, error) + Continue(inToken []byte) (done bool, outToken []byte, err error) +} + +func (c *PgConn) gssAuth() error { + if newGSS == nil { + return errors.New("kerberos error: no GSSAPI provider registered, see https://github.com/otan/gopgkrb5") + } + cli, err := newGSS() + if err != nil { + return err + } + + var nextData []byte + if spn, ok := c.config.RuntimeParams["krbspn"]; ok { + // Use the supplied SPN if provided. + nextData, err = cli.GetInitTokenFromSPN(spn) + } else { + // Allow the kerberos service name to be overridden + service := "postgres" + if val, ok := c.config.RuntimeParams["krbsrvname"]; ok { + service = val + } + nextData, err = cli.GetInitToken(c.config.Host, service) + } + if err != nil { + return err + } + + for { + gssResponse := &pgproto3.GSSResponse{ + Data: nextData, + } + _, err = c.conn.Write(gssResponse.Encode(nil)) + if err != nil { + return err + } + resp, err := c.rxGSSContinue() + if err != nil { + return err + } + var done bool + done, nextData, err = cli.Continue(resp.Data) + if err != nil { + return err + } + if done { + break + } + } + return nil +} + +func (c *PgConn) rxGSSContinue() (*pgproto3.AuthenticationGSSContinue, error) { + msg, err := c.receiveMessage() + if err != nil { + return nil, err + } + gssContinue, ok := msg.(*pgproto3.AuthenticationGSSContinue) + if ok { + return gssContinue, nil + } + + return nil, errors.New("expected AuthenticationGSSContinue message but received unexpected message") +} diff --git a/pgconn.go b/pgconn.go index 9a496ed0..0d07ac57 100644 --- a/pgconn.go +++ b/pgconn.go @@ -320,7 +320,12 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig pgConn.conn.Close() return nil, &connectError{config: config, msg: "failed SASL auth", err: err} } - + case *pgproto3.AuthenticationGSS: + err = pgConn.gssAuth() + if err != nil { + pgConn.conn.Close() + return nil, &connectError{config: config, msg: "failed GSS auth", err: err} + } case *pgproto3.ReadyForQuery: pgConn.status = connStatusIdle if config.ValidateConnect != nil { From beb4e2cfbcd7f41e6389254496cc5feae7d99d9c Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 16 Apr 2022 07:24:25 -0500 Subject: [PATCH 7/9] SQLCODE 42501 is fatal connect error Don't try fallback configs. Match libpq behavior. fixes https://github.com/jackc/pgconn/issues/108 --- pgconn.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pgconn.go b/pgconn.go index 0d07ac57..f1304d08 100644 --- a/pgconn.go +++ b/pgconn.go @@ -157,9 +157,11 @@ func ConnectConfig(ctx context.Context, config *Config) (pgConn *PgConn, err err const ERRCODE_INVALID_PASSWORD = "28P01" // wrong password const ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION = "28000" // wrong password or bad pg_hba.conf settings const ERRCODE_INVALID_CATALOG_NAME = "3D000" // db does not exist + const ERRCODE_INSUFFICIENT_PRIVILEGE = "42501" // missing connect privilege if pgerr.Code == ERRCODE_INVALID_PASSWORD || pgerr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION || - pgerr.Code == ERRCODE_INVALID_CATALOG_NAME { + pgerr.Code == ERRCODE_INVALID_CATALOG_NAME || + pgerr.Code == ERRCODE_INSUFFICIENT_PRIVILEGE { break } } From 1b244eec5da4234408cc3241e412b23be8541d4b Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Thu, 21 Apr 2022 19:48:43 -0500 Subject: [PATCH 8/9] Upgrade to pgproto3 v2.3.0 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 2a2d6810..aaf7a486 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/jackc/pgio v1.0.0 github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 github.com/jackc/pgpassfile v1.0.0 - github.com/jackc/pgproto3/v2 v2.2.1-0.20220412121321-175856ffd3c8 + github.com/jackc/pgproto3/v2 v2.3.0 github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 diff --git a/go.sum b/go.sum index c558564b..a3834fd2 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.2.1-0.20220412121321-175856ffd3c8 h1:KxsCQec+1iwJXtxnbbS/dY0EJ6rJEUlFsrJUnL5A2XI= github.com/jackc/pgproto3/v2 v2.2.1-0.20220412121321-175856ffd3c8/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= +github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= From 9bb49f990f1e563b88fc0772a6a928b14ebd19f0 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Thu, 21 Apr 2022 19:49:01 -0500 Subject: [PATCH 9/9] Release v1.12.0 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a37eecfe..6df3ddcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.12.0 (April 21, 2022) + +* Add pluggable GSSAPI support (Oliver Tan) +* Fix: Consider any "0A000" error a possible cached plan changed error due to locale +* Better match psql fallback behavior with multiple hosts + # 1.11.0 (February 7, 2022) * Support port in ip from LookupFunc to override config (James Hartig)