VAULT-13614 Support SCRAM-SHA-256 encrypted passwords for PostgreSQL (#19616)
This commit is contained in:
parent
427b4dbd49
commit
96e966e9ef
|
@ -11,5 +11,6 @@ project {
|
||||||
"builtin/credential/aws/pkcs7/**",
|
"builtin/credential/aws/pkcs7/**",
|
||||||
"ui/node_modules/**",
|
"ui/node_modules/**",
|
||||||
"enos/modules/k8s_deploy_vault/raft-config.hcl",
|
"enos/modules/k8s_deploy_vault/raft-config.hcl",
|
||||||
|
"plugins/database/postgresql/scram/**"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -246,6 +246,7 @@ test_packages[13]+=" $base/command/server"
|
||||||
test_packages[13]+=" $base/physical/aerospike"
|
test_packages[13]+=" $base/physical/aerospike"
|
||||||
test_packages[13]+=" $base/physical/cockroachdb"
|
test_packages[13]+=" $base/physical/cockroachdb"
|
||||||
test_packages[13]+=" $base/plugins/database/postgresql"
|
test_packages[13]+=" $base/plugins/database/postgresql"
|
||||||
|
test_packages[13]+=" $base/plugins/database/postgresql/scram"
|
||||||
if [ "${ENTERPRISE:+x}" == "x" ] ; then
|
if [ "${ENTERPRISE:+x}" == "x" ] ; then
|
||||||
test_packages[13]+=" $base/vault/external_tests/filteredpathsext"
|
test_packages[13]+=" $base/vault/external_tests/filteredpathsext"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
secrets/postgresql: Add configuration to scram-sha-256 encrypt passwords on Vault before sending them to PostgreSQL
|
||||||
|
```
|
|
@ -0,0 +1,25 @@
|
||||||
|
package postgresql
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// passwordAuthentication determines whether to send passwords in plaintext (password) or hashed (scram-sha-256).
|
||||||
|
type passwordAuthentication string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// passwordAuthenticationPassword is the default. If set, passwords will be sent to PostgreSQL in plain text.
|
||||||
|
passwordAuthenticationPassword passwordAuthentication = "password"
|
||||||
|
passwordAuthenticationSCRAMSHA256 passwordAuthentication = "scram-sha-256"
|
||||||
|
)
|
||||||
|
|
||||||
|
var passwordAuthentications = map[passwordAuthentication]struct{}{
|
||||||
|
passwordAuthenticationSCRAMSHA256: {},
|
||||||
|
passwordAuthenticationPassword: {},
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePasswordAuthentication(s string) (passwordAuthentication, error) {
|
||||||
|
if _, ok := passwordAuthentications[passwordAuthentication(s)]; !ok {
|
||||||
|
return "", fmt.Errorf("'%s' is not a valid password authentication type", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return passwordAuthentication(s), nil
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/go-secure-stdlib/strutil"
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
||||||
|
"github.com/hashicorp/vault/plugins/database/postgresql/scram"
|
||||||
"github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
"github.com/hashicorp/vault/sdk/database/dbplugin/v5"
|
||||||
"github.com/hashicorp/vault/sdk/database/helper/connutil"
|
"github.com/hashicorp/vault/sdk/database/helper/connutil"
|
||||||
"github.com/hashicorp/vault/sdk/database/helper/dbutil"
|
"github.com/hashicorp/vault/sdk/database/helper/dbutil"
|
||||||
|
@ -68,7 +69,8 @@ func new() *PostgreSQL {
|
||||||
connProducer.Type = postgreSQLTypeName
|
connProducer.Type = postgreSQLTypeName
|
||||||
|
|
||||||
db := &PostgreSQL{
|
db := &PostgreSQL{
|
||||||
SQLConnectionProducer: connProducer,
|
SQLConnectionProducer: connProducer,
|
||||||
|
passwordAuthentication: passwordAuthenticationPassword,
|
||||||
}
|
}
|
||||||
|
|
||||||
return db
|
return db
|
||||||
|
@ -77,7 +79,8 @@ func new() *PostgreSQL {
|
||||||
type PostgreSQL struct {
|
type PostgreSQL struct {
|
||||||
*connutil.SQLConnectionProducer
|
*connutil.SQLConnectionProducer
|
||||||
|
|
||||||
usernameProducer template.StringTemplate
|
usernameProducer template.StringTemplate
|
||||||
|
passwordAuthentication passwordAuthentication
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PostgreSQL) Initialize(ctx context.Context, req dbplugin.InitializeRequest) (dbplugin.InitializeResponse, error) {
|
func (p *PostgreSQL) Initialize(ctx context.Context, req dbplugin.InitializeRequest) (dbplugin.InitializeResponse, error) {
|
||||||
|
@ -105,6 +108,20 @@ func (p *PostgreSQL) Initialize(ctx context.Context, req dbplugin.InitializeRequ
|
||||||
return dbplugin.InitializeResponse{}, fmt.Errorf("invalid username template: %w", err)
|
return dbplugin.InitializeResponse{}, fmt.Errorf("invalid username template: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
passwordAuthenticationRaw, err := strutil.GetString(req.Config, "password_authentication")
|
||||||
|
if err != nil {
|
||||||
|
return dbplugin.InitializeResponse{}, fmt.Errorf("failed to retrieve password_authentication: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if passwordAuthenticationRaw != "" {
|
||||||
|
pwAuthentication, err := parsePasswordAuthentication(passwordAuthenticationRaw)
|
||||||
|
if err != nil {
|
||||||
|
return dbplugin.InitializeResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.passwordAuthentication = pwAuthentication
|
||||||
|
}
|
||||||
|
|
||||||
resp := dbplugin.InitializeResponse{
|
resp := dbplugin.InitializeResponse{
|
||||||
Config: newConf,
|
Config: newConf,
|
||||||
}
|
}
|
||||||
|
@ -188,6 +205,15 @@ func (p *PostgreSQL) changeUserPassword(ctx context.Context, username string, ch
|
||||||
"username": username,
|
"username": username,
|
||||||
"password": password,
|
"password": password,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.passwordAuthentication == passwordAuthenticationSCRAMSHA256 {
|
||||||
|
hashedPassword, err := scram.Hash(password)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to scram-sha256 password: %w", err)
|
||||||
|
}
|
||||||
|
m["password"] = hashedPassword
|
||||||
|
}
|
||||||
|
|
||||||
if err := dbtxn.ExecuteTxQueryDirect(ctx, tx, m, query); err != nil {
|
if err := dbtxn.ExecuteTxQueryDirect(ctx, tx, m, query); err != nil {
|
||||||
return fmt.Errorf("failed to execute query: %w", err)
|
return fmt.Errorf("failed to execute query: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -272,15 +298,24 @@ func (p *PostgreSQL) NewUser(ctx context.Context, req dbplugin.NewUserRequest) (
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
m := map[string]string{
|
||||||
|
"name": username,
|
||||||
|
"username": username,
|
||||||
|
"password": req.Password,
|
||||||
|
"expiration": expirationStr,
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.passwordAuthentication == passwordAuthenticationSCRAMSHA256 {
|
||||||
|
hashedPassword, err := scram.Hash(req.Password)
|
||||||
|
if err != nil {
|
||||||
|
return dbplugin.NewUserResponse{}, fmt.Errorf("unable to scram-sha256 password: %w", err)
|
||||||
|
}
|
||||||
|
m["password"] = hashedPassword
|
||||||
|
}
|
||||||
|
|
||||||
for _, stmt := range req.Statements.Commands {
|
for _, stmt := range req.Statements.Commands {
|
||||||
if containsMultilineStatement(stmt) {
|
if containsMultilineStatement(stmt) {
|
||||||
// Execute it as-is.
|
// Execute it as-is.
|
||||||
m := map[string]string{
|
|
||||||
"name": username,
|
|
||||||
"username": username,
|
|
||||||
"password": req.Password,
|
|
||||||
"expiration": expirationStr,
|
|
||||||
}
|
|
||||||
if err := dbtxn.ExecuteTxQueryDirect(ctx, tx, m, stmt); err != nil {
|
if err := dbtxn.ExecuteTxQueryDirect(ctx, tx, m, stmt); err != nil {
|
||||||
return dbplugin.NewUserResponse{}, fmt.Errorf("failed to execute query: %w", err)
|
return dbplugin.NewUserResponse{}, fmt.Errorf("failed to execute query: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -293,12 +328,6 @@ func (p *PostgreSQL) NewUser(ctx context.Context, req dbplugin.NewUserRequest) (
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
m := map[string]string{
|
|
||||||
"name": username,
|
|
||||||
"username": username,
|
|
||||||
"password": req.Password,
|
|
||||||
"expiration": expirationStr,
|
|
||||||
}
|
|
||||||
if err := dbtxn.ExecuteTxQueryDirect(ctx, tx, m, query); err != nil {
|
if err := dbtxn.ExecuteTxQueryDirect(ctx, tx, m, query); err != nil {
|
||||||
return dbplugin.NewUserResponse{}, fmt.Errorf("failed to execute query: %w", err)
|
return dbplugin.NewUserResponse{}, fmt.Errorf("failed to execute query: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
dbtesting "github.com/hashicorp/vault/sdk/database/dbplugin/v5/testing"
|
dbtesting "github.com/hashicorp/vault/sdk/database/dbplugin/v5/testing"
|
||||||
"github.com/hashicorp/vault/sdk/database/helper/dbutil"
|
"github.com/hashicorp/vault/sdk/database/helper/dbutil"
|
||||||
"github.com/hashicorp/vault/sdk/helper/template"
|
"github.com/hashicorp/vault/sdk/helper/template"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -93,6 +94,97 @@ func TestPostgreSQL_Initialize_ConnURLWithDSNFormat(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPostgreSQL_PasswordAuthentication tests that the default "password_authentication" is "none", and that
|
||||||
|
// an error is returned if an invalid "password_authentication" is provided.
|
||||||
|
func TestPostgreSQL_PasswordAuthentication(t *testing.T) {
|
||||||
|
cleanup, connURL := postgresql.PrepareTestContainer(t, "13.4-buster")
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
dsnConnURL, err := dbutil.ParseURL(connURL)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
db := new()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
t.Run("invalid-password-authentication", func(t *testing.T) {
|
||||||
|
connectionDetails := map[string]interface{}{
|
||||||
|
"connection_url": dsnConnURL,
|
||||||
|
"password_authentication": "invalid-password-authentication",
|
||||||
|
}
|
||||||
|
|
||||||
|
req := dbplugin.InitializeRequest{
|
||||||
|
Config: connectionDetails,
|
||||||
|
VerifyConnection: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := db.Initialize(ctx, req)
|
||||||
|
assert.EqualError(t, err, "'invalid-password-authentication' is not a valid password authentication type")
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("default-is-none", func(t *testing.T) {
|
||||||
|
connectionDetails := map[string]interface{}{
|
||||||
|
"connection_url": dsnConnURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
req := dbplugin.InitializeRequest{
|
||||||
|
Config: connectionDetails,
|
||||||
|
VerifyConnection: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = dbtesting.AssertInitialize(t, db, req)
|
||||||
|
assert.Equal(t, passwordAuthenticationPassword, db.passwordAuthentication)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPostgreSQL_PasswordAuthentication_SCRAMSHA256 tests that password_authentication works when set to scram-sha-256.
|
||||||
|
// When sending an encrypted password, the raw password should still successfully authenticate the user.
|
||||||
|
func TestPostgreSQL_PasswordAuthentication_SCRAMSHA256(t *testing.T) {
|
||||||
|
cleanup, connURL := postgresql.PrepareTestContainer(t, "13.4-buster")
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
dsnConnURL, err := dbutil.ParseURL(connURL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionDetails := map[string]interface{}{
|
||||||
|
"connection_url": dsnConnURL,
|
||||||
|
"password_authentication": string(passwordAuthenticationSCRAMSHA256),
|
||||||
|
}
|
||||||
|
|
||||||
|
req := dbplugin.InitializeRequest{
|
||||||
|
Config: connectionDetails,
|
||||||
|
VerifyConnection: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
db := new()
|
||||||
|
resp := dbtesting.AssertInitialize(t, db, req)
|
||||||
|
assert.Equal(t, string(passwordAuthenticationSCRAMSHA256), resp.Config["password_authentication"])
|
||||||
|
|
||||||
|
if !db.Initialized {
|
||||||
|
t.Fatal("Database should be initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
newUserRequest := dbplugin.NewUserRequest{
|
||||||
|
Statements: dbplugin.Statements{
|
||||||
|
Commands: []string{
|
||||||
|
`
|
||||||
|
CREATE ROLE "{{name}}" WITH
|
||||||
|
LOGIN
|
||||||
|
PASSWORD '{{password}}'
|
||||||
|
VALID UNTIL '{{expiration}}';
|
||||||
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "{{name}}";`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Password: "somesecurepassword",
|
||||||
|
Expiration: time.Now().Add(1 * time.Minute),
|
||||||
|
}
|
||||||
|
newUserResponse, err := db.NewUser(ctx, newUserRequest)
|
||||||
|
|
||||||
|
assertCredsExist(t, db.ConnectionURL, newUserResponse.Username, newUserRequest.Password)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPostgreSQL_NewUser(t *testing.T) {
|
func TestPostgreSQL_NewUser(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
req dbplugin.NewUserRequest
|
req dbplugin.NewUserRequest
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Taishi Kasuga
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,86 @@
|
||||||
|
package scram
|
||||||
|
|
||||||
|
//
|
||||||
|
// @see https://github.com/postgres/postgres/blob/c30f54ad732ca5c8762bb68bbe0f51de9137dd72/src/interfaces/libpq/fe-auth.c#L1167-L1285
|
||||||
|
// @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/interfaces/libpq/fe-auth-scram.c#L868-L905
|
||||||
|
// @see https://github.com/postgres/postgres/blob/c30f54ad732ca5c8762bb68bbe0f51de9137dd72/src/port/pg_strong_random.c#L66-L96
|
||||||
|
// @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/common/scram-common.c#L160-L274
|
||||||
|
// @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/common/scram-common.c#L27-L85
|
||||||
|
|
||||||
|
// Implementation from https://github.com/supercaracal/scram-sha-256/blob/d3c05cd927770a11c6e12de3e3a99c3446a1f78d/main.go
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/include/common/scram-common.h#L36-L41
|
||||||
|
saltSize = 16
|
||||||
|
|
||||||
|
// @see https://github.com/postgres/postgres/blob/c30f54ad732ca5c8762bb68bbe0f51de9137dd72/src/include/common/sha2.h#L22
|
||||||
|
digestLen = 32
|
||||||
|
|
||||||
|
// @see https://github.com/postgres/postgres/blob/e6bdfd9700ebfc7df811c97c2fc46d7e94e329a2/src/include/common/scram-common.h#L43-L47
|
||||||
|
iterationCnt = 4096
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
clientRawKey = []byte("Client Key")
|
||||||
|
serverRawKey = []byte("Server Key")
|
||||||
|
)
|
||||||
|
|
||||||
|
func genSalt(size int) ([]byte, error) {
|
||||||
|
salt := make([]byte, size)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return salt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeB64(src []byte) (dst []byte) {
|
||||||
|
dst = make([]byte, base64.StdEncoding.EncodedLen(len(src)))
|
||||||
|
base64.StdEncoding.Encode(dst, src)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHMACSum(key, msg []byte) []byte {
|
||||||
|
h := hmac.New(sha256.New, key)
|
||||||
|
_, _ = h.Write(msg)
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSHA256Sum(key []byte) []byte {
|
||||||
|
h := sha256.New()
|
||||||
|
_, _ = h.Write(key)
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashPassword(rawPassword, salt []byte, iter, keyLen int) string {
|
||||||
|
digestKey := pbkdf2.Key(rawPassword, salt, iter, keyLen, sha256.New)
|
||||||
|
clientKey := getHMACSum(digestKey, clientRawKey)
|
||||||
|
storedKey := getSHA256Sum(clientKey)
|
||||||
|
serverKey := getHMACSum(digestKey, serverRawKey)
|
||||||
|
|
||||||
|
return fmt.Sprintf("SCRAM-SHA-256$%d:%s$%s:%s",
|
||||||
|
iter,
|
||||||
|
string(encodeB64(salt)),
|
||||||
|
string(encodeB64(storedKey)),
|
||||||
|
string(encodeB64(serverKey)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Hash(password string) (string, error) {
|
||||||
|
salt, err := genSalt(saltSize)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
hashedPassword := hashPassword([]byte(password), salt, iterationCnt, digestLen)
|
||||||
|
return hashedPassword, nil
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package scram
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestScram tests the Hash method. The hashed password string should have a SCRAM-SHA-256 prefix.
|
||||||
|
func TestScram(t *testing.T) {
|
||||||
|
tcs := map[string]struct {
|
||||||
|
Password string
|
||||||
|
}{
|
||||||
|
"empty-password": {Password: ""},
|
||||||
|
"simple-password": {Password: "password"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range tcs {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
got, err := Hash(tc.Password)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, strings.HasPrefix(got, "SCRAM-SHA-256$4096:"))
|
||||||
|
assert.Len(t, got, 133)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -55,6 +55,12 @@ has a number of parameters to further configure a connection.
|
||||||
and password fields. See the [databases secrets engine docs](/vault/docs/secrets/databases#disable-character-escaping)
|
and password fields. See the [databases secrets engine docs](/vault/docs/secrets/databases#disable-character-escaping)
|
||||||
for more information. Defaults to `false`.
|
for more information. Defaults to `false`.
|
||||||
|
|
||||||
|
- `password_authentication` `(string: "password")` - When set to "scram-sha-256", passwords will be hashed by Vault and stored as-is by PostgreSQL.
|
||||||
|
Using "scram-sha-256" requires a minimum version of PostgreSQL 10. Available options are "scram-sha-256" and "password". The default is "password".
|
||||||
|
When set to "password", passwords will be sent to PostgresSQL in plaintext format and may appear in PostgreSQL logs as-is.
|
||||||
|
For more information, please refer to the [https://www.postgresql.org/docs/current/sql-createrole.html#password](PostgreSQL documentation).
|
||||||
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Default Username Template</b></summary>
|
<summary><b>Default Username Template</b></summary>
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,8 @@ options, including SSL options, can be found in the [pgx][pgxlib] and
|
||||||
allowed_roles="my-role" \
|
allowed_roles="my-role" \
|
||||||
connection_url="postgresql://{{username}}:{{password}}@localhost:5432/database-name" \
|
connection_url="postgresql://{{username}}:{{password}}@localhost:5432/database-name" \
|
||||||
username="vaultuser" \
|
username="vaultuser" \
|
||||||
password="vaultpass"
|
password="vaultpass" \
|
||||||
|
password_authentication="scram-sha-256"
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Configure a role that maps a name in Vault to an SQL statement to execute to
|
1. Configure a role that maps a name in Vault to an SQL statement to execute to
|
||||||
|
|
Loading…
Reference in New Issue