A Postgres database driver that supports credential rotation.
The following example demonstrates a simple CredentialsProvider
implementation, configured with a fixed list of passwords. The implementation
cycles though the fixed list of passwords, trying each in turn, until it is able
to establish a connection successfully.
In actual usage, it is likely that a CredentialsProvider implementation would
source the dynamic components of the data source name (e.g. the password) from
either the environment of the process or a service such as AWS SecretsManager
or HashiCorp Vault.
package main import ( "database/sql" conn "github.com/progbits/pg-secrets-driver" "log" "net/url" ) type TestCredentialsProvider struct { count int passwords []string } func (p *TestCredentialsProvider) GetDataSourceName() (string, error) { withPassword := func(password string) string { dsn, _ := url.Parse("postgresql://localhost/postgres") query := dsn.Query() query.Add("user", "postgres") query.Add("password", password) query.Add("sslmode", "disable") dsn.RawQuery = query.Encode() return dsn.String() } password := p.passwords[p.count] p.count++ fmt.Printf("Returning data source name with password: %s\n", password) return withPassword(password), nil } func (p *TestCredentialsProvider) Retries() int { return len(p.passwords) } func main() { passwords := []string{"foo", "bar", "baz", "password", "pa$$w0rd", "p@ssw0rd"} provider := &TestCredentialsProvider{ passwords: passwords, } connector := conn.NewPgSecretsConnector(provider) db := sql.OpenDB(connector) _, err := db.Exec("SELECT 1") if err != nil { log.Print(err) } }
The example code above can be found in the example directory.
To run the example, start a new Postgres instance with the superuser
password pa$$w0rd, the 5th value in the list held by TestCredentialsProvider:
docker run -e 'POSTGRES_PASSWORD=pa$$w0rd' -d -p 5432:5432 postgres:14.3The example can then be run using make:
make run-example