mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-23 11:41:24 +01:00
* license: update headers to IBM Corp. * `make proto` * update offset because source file changed Signed-off-by: Ryan Cragun <me@ryan.ec> Co-authored-by: Ryan Cragun <me@ryan.ec>
172 lines
5.2 KiB
Go
172 lines
5.2 KiB
Go
// Copyright IBM Corp. 2016, 2025
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package database
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/vault/helper/namespace"
|
|
postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
// TestRotateRootPassword_Postgres tests database secrets root rotation for password credentials with Postgres.
|
|
func TestRotateRootPassword_Postgres(t *testing.T) {
|
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t)
|
|
defer cleanup()
|
|
|
|
cluster, sys := getClusterWithFactory(t, Factory)
|
|
defer cluster.Cleanup()
|
|
|
|
connURL = strings.Replace(connURL, "postgres:secret", "{{username}}:{{password}}", 1)
|
|
|
|
testRotateRoot(t, sys, true, connURL, "postgresql-database-plugin", "postgres", "secret")
|
|
}
|
|
|
|
// TestRotateRootKeypair_Snowflake_Acc tests database secrets root rotation for private key credentials with Snowflake.
|
|
func TestRotateRootKeypair_Snowflake_Acc(t *testing.T) {
|
|
// SNOWFLAKE_PRIVATE_KEY is the path to the private key file.
|
|
privateKeyPath, ok := os.LookupEnv("VAULT_SNOWFLAKE_PRIVATE_KEY")
|
|
if !ok {
|
|
t.Skip("VAULT_SNOWFLAKE_PRIVATE_KEY not set, skipping test")
|
|
}
|
|
|
|
keyFile, err := os.ReadFile(privateKeyPath)
|
|
if err != nil {
|
|
t.Fatalf("failed to read private key file: %s", err)
|
|
}
|
|
|
|
connURL, ok := os.LookupEnv("VAULT_SNOWFLAKE_CONNECTION_URL")
|
|
if !ok {
|
|
t.Skip("VAULT_SNOWFLAKE_CONNECTION_URL not set, skipping test")
|
|
}
|
|
|
|
username, ok := os.LookupEnv("VAULT_SNOWFLAKE_USERNAME")
|
|
if !ok {
|
|
t.Skip("VAULT_SNOWFLAKE_USERNAME not set, skipping test")
|
|
}
|
|
|
|
cluster, sys := getClusterWithFactory(t, Factory)
|
|
defer cluster.Cleanup()
|
|
|
|
testRotateRoot(t, sys, false, connURL, "snowflake-database-plugin", username, string(keyFile))
|
|
}
|
|
|
|
// Helper function to run rotate root tests.
|
|
func testRotateRoot(t *testing.T, sys logical.SystemView, isPassword bool, connURL string, pluginName, username, credential string) {
|
|
config := logical.TestBackendConfig()
|
|
config.StorageView = &logical.InmemStorage{}
|
|
config.System = sys
|
|
|
|
lb, err := Factory(context.Background(), config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, ok := lb.(*databaseBackend)
|
|
if !ok {
|
|
t.Fatal("could not convert to db backend")
|
|
}
|
|
defer b.Cleanup(context.Background())
|
|
|
|
// Configure a connection
|
|
configData := map[string]any{
|
|
"connection_url": connURL,
|
|
"plugin_name": pluginName,
|
|
"verify_connection": false,
|
|
"allowed_roles": []string{"*"},
|
|
"name": "plugin-test",
|
|
"username": username,
|
|
}
|
|
if isPassword {
|
|
configData["password"] = credential
|
|
} else {
|
|
configData["private_key"] = credential
|
|
}
|
|
req := &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "config/plugin-test",
|
|
Storage: config.StorageView,
|
|
Data: configData,
|
|
}
|
|
resp, err := b.HandleRequest(namespace.RootContext(context.Background()), req)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
|
}
|
|
|
|
// Create a dynamic role
|
|
req = &logical.Request{
|
|
Operation: logical.CreateOperation,
|
|
Path: "roles/test",
|
|
Storage: config.StorageView,
|
|
Data: map[string]interface{}{
|
|
"db_name": "plugin-test",
|
|
"creation_statements": `CREATE USER "{{name}}"`,
|
|
},
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), req)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
|
}
|
|
|
|
// Read some creds to validate the connection
|
|
req = &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "creds/test",
|
|
Storage: config.StorageView,
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), req)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
|
}
|
|
|
|
// Rotate the root credentials
|
|
req = &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "rotate-root/plugin-test",
|
|
Storage: config.StorageView,
|
|
}
|
|
resp, err = b.HandleRequest(namespace.RootContext(context.Background()), req)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
|
}
|
|
|
|
// Read creds a second time to validate the root rotation still works
|
|
req = &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "creds/test",
|
|
Storage: config.StorageView,
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), req)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
|
}
|
|
|
|
// Write back the original credential to ensure it no longer works
|
|
req = &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "config/plugin-test",
|
|
Storage: config.StorageView,
|
|
Data: configData,
|
|
}
|
|
resp, err = b.HandleRequest(namespace.RootContext(context.Background()), req)
|
|
if err != nil || (resp != nil && resp.IsError()) {
|
|
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
|
}
|
|
|
|
// Read some more creds again and expect an error]
|
|
// Note: For Snowflake, this step may fail if you are using the account's 2nd private key
|
|
req = &logical.Request{
|
|
Operation: logical.ReadOperation,
|
|
Path: "creds/test",
|
|
Storage: config.StorageView,
|
|
}
|
|
resp, err = b.HandleRequest(context.Background(), req)
|
|
if err == nil || (resp != nil && !resp.IsError()) {
|
|
t.Fatalf("expected authentication error but did not receive an error, resp:%#v\n", resp)
|
|
}
|
|
}
|