mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 02:57:59 +00:00
Add mongodbatlas static roles support (#8987)
* Refactor PG container creation. * Rework rotation tests to use shorter sleeps. * Refactor rotation tests. * Add a static role rotation test for MongoDB Atlas.
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-test/deep"
|
"github.com/go-test/deep"
|
||||||
|
"github.com/hashicorp/vault-plugin-database-mongodbatlas"
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
"github.com/hashicorp/vault/helper/namespace"
|
"github.com/hashicorp/vault/helper/namespace"
|
||||||
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
||||||
@@ -104,6 +105,7 @@ func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
|
|||||||
sys := vault.TestDynamicSystemView(cores[0].Core)
|
sys := vault.TestDynamicSystemView(cores[0].Core)
|
||||||
vault.TestAddTestPlugin(t, cores[0].Core, "postgresql-database-plugin", consts.PluginTypeDatabase, "TestBackend_PluginMain_Postgres", []string{}, "")
|
vault.TestAddTestPlugin(t, cores[0].Core, "postgresql-database-plugin", consts.PluginTypeDatabase, "TestBackend_PluginMain_Postgres", []string{}, "")
|
||||||
vault.TestAddTestPlugin(t, cores[0].Core, "mongodb-database-plugin", consts.PluginTypeDatabase, "TestBackend_PluginMain_Mongo", []string{}, "")
|
vault.TestAddTestPlugin(t, cores[0].Core, "mongodb-database-plugin", consts.PluginTypeDatabase, "TestBackend_PluginMain_Mongo", []string{}, "")
|
||||||
|
vault.TestAddTestPlugin(t, cores[0].Core, "mongodbatlas-database-plugin", consts.PluginTypeDatabase, "TestBackend_PluginMain_MongoAtlas", []string{}, "")
|
||||||
|
|
||||||
return cluster, sys
|
return cluster, sys
|
||||||
}
|
}
|
||||||
@@ -149,6 +151,28 @@ func TestBackend_PluginMain_Mongo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBackend_PluginMain_MongoAtlas(t *testing.T) {
|
||||||
|
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv)
|
||||||
|
if caPEM == "" {
|
||||||
|
t.Fatal("CA cert not passed in")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{"--ca-cert=" + caPEM}
|
||||||
|
|
||||||
|
apiClientMeta := &api.PluginAPIClientMeta{}
|
||||||
|
flags := apiClientMeta.FlagSet()
|
||||||
|
flags.Parse(args)
|
||||||
|
|
||||||
|
err := mongodbatlas.Run(apiClientMeta.GetTLSConfig())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBackend_RoleUpgrade(t *testing.T) {
|
func TestBackend_RoleUpgrade(t *testing.T) {
|
||||||
|
|
||||||
storage := &logical.InmemStorage{}
|
storage := &logical.InmemStorage{}
|
||||||
|
|||||||
@@ -3,18 +3,22 @@ package database
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/Sectorbob/mlab-ns2/gae/ns/digest"
|
||||||
"github.com/hashicorp/vault/helper/namespace"
|
"github.com/hashicorp/vault/helper/namespace"
|
||||||
"github.com/hashicorp/vault/helper/testhelpers/mongodb"
|
"github.com/hashicorp/vault/helper/testhelpers/mongodb"
|
||||||
|
postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql"
|
||||||
"github.com/hashicorp/vault/sdk/framework"
|
"github.com/hashicorp/vault/sdk/framework"
|
||||||
"github.com/hashicorp/vault/sdk/helper/dbtxn"
|
"github.com/hashicorp/vault/sdk/helper/dbtxn"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
|
mongodbatlasapi "github.com/mongodb/go-client-mongodb-atlas/mongodbatlas"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
)
|
)
|
||||||
@@ -44,7 +48,7 @@ func TestBackend_StaticRole_Rotate_basic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer b.Cleanup(context.Background())
|
defer b.Cleanup(context.Background())
|
||||||
|
|
||||||
cleanup, connURL := preparePostgresTestContainer(t, config.StorageView, b)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// create the database user
|
// create the database user
|
||||||
@@ -192,7 +196,7 @@ func TestBackend_StaticRole_Rotate_NonStaticError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer b.Cleanup(context.Background())
|
defer b.Cleanup(context.Background())
|
||||||
|
|
||||||
cleanup, connURL := preparePostgresTestContainer(t, config.StorageView, b)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// create the database user
|
// create the database user
|
||||||
@@ -296,7 +300,7 @@ func TestBackend_StaticRole_Revoke_user(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer b.Cleanup(context.Background())
|
defer b.Cleanup(context.Background())
|
||||||
|
|
||||||
cleanup, connURL := preparePostgresTestContainer(t, config.StorageView, b)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// create the database user
|
// create the database user
|
||||||
@@ -521,7 +525,7 @@ func TestBackend_Static_QueueWAL_discard_role_newer_rotation_date(t *testing.T)
|
|||||||
t.Fatal("could not convert to db backend")
|
t.Fatal("could not convert to db backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup, connURL := preparePostgresTestContainer(t, config.StorageView, b)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// create the database user
|
// create the database user
|
||||||
@@ -685,143 +689,83 @@ func assertWALCount(t *testing.T, s logical.Storage, expected int, key string) {
|
|||||||
// End WAL testing
|
// End WAL testing
|
||||||
//
|
//
|
||||||
|
|
||||||
|
type userCreator func(t *testing.T, username, password string)
|
||||||
|
|
||||||
func TestBackend_StaticRole_Rotations_PostgreSQL(t *testing.T) {
|
func TestBackend_StaticRole_Rotations_PostgreSQL(t *testing.T) {
|
||||||
cluster, sys := getCluster(t)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "latest")
|
||||||
defer cluster.Cleanup()
|
|
||||||
|
|
||||||
config := logical.TestBackendConfig()
|
|
||||||
config.StorageView = &logical.InmemStorage{}
|
|
||||||
config.System = sys
|
|
||||||
|
|
||||||
b, err := Factory(context.Background(), config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer b.Cleanup(context.Background())
|
|
||||||
|
|
||||||
bd := b.(*databaseBackend)
|
|
||||||
if bd.credRotationQueue == nil {
|
|
||||||
t.Fatal("database backend had no credential rotation queue")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure backend, add item and confirm length
|
|
||||||
cleanup, connURL := preparePostgresTestContainer(t, config.StorageView, b)
|
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
testCases := []string{"65", "130", "5400"}
|
uc := userCreator(func(t *testing.T, username, password string) {
|
||||||
// Create database users ahead
|
createTestPGUser(t, connURL, username, password, testRoleStaticCreate)
|
||||||
for _, tc := range testCases {
|
})
|
||||||
createTestPGUser(t, connURL, dbUser+tc, dbUserDefaultPassword, testRoleStaticCreate)
|
testBackend_StaticRole_Rotations(t, uc, map[string]interface{}{
|
||||||
}
|
"connection_url": connURL,
|
||||||
|
"plugin_name": "postgresql-database-plugin",
|
||||||
// Configure a connection
|
})
|
||||||
data := map[string]interface{}{
|
|
||||||
"connection_url": connURL,
|
|
||||||
"plugin_name": "postgresql-database-plugin",
|
|
||||||
"verify_connection": false,
|
|
||||||
"allowed_roles": []string{"*"},
|
|
||||||
"name": "plugin-test",
|
|
||||||
}
|
|
||||||
req := &logical.Request{
|
|
||||||
Operation: logical.UpdateOperation,
|
|
||||||
Path: "config/plugin-test",
|
|
||||||
Storage: config.StorageView,
|
|
||||||
Data: data,
|
|
||||||
}
|
|
||||||
resp, err := b.HandleRequest(namespace.RootContext(nil), req)
|
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
|
||||||
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create three static roles with different rotation periods
|
|
||||||
for _, tc := range testCases {
|
|
||||||
roleName := "plugin-static-role-" + tc
|
|
||||||
data = map[string]interface{}{
|
|
||||||
"name": roleName,
|
|
||||||
"db_name": "plugin-test",
|
|
||||||
"rotation_statements": testRoleStaticUpdate,
|
|
||||||
"username": dbUser + tc,
|
|
||||||
"rotation_period": tc,
|
|
||||||
}
|
|
||||||
|
|
||||||
req = &logical.Request{
|
|
||||||
Operation: logical.CreateOperation,
|
|
||||||
Path: "static-roles/" + roleName,
|
|
||||||
Storage: config.StorageView,
|
|
||||||
Data: data,
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err = b.HandleRequest(namespace.RootContext(nil), req)
|
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
|
||||||
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify the queue has 3 items in it
|
|
||||||
if bd.credRotationQueue.Len() != 3 {
|
|
||||||
t.Fatalf("expected 3 items in the rotation queue, got: (%d)", bd.credRotationQueue.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
// List the roles
|
|
||||||
data = map[string]interface{}{}
|
|
||||||
req = &logical.Request{
|
|
||||||
Operation: logical.ListOperation,
|
|
||||||
Path: "static-roles/",
|
|
||||||
Storage: config.StorageView,
|
|
||||||
Data: data,
|
|
||||||
}
|
|
||||||
resp, err = b.HandleRequest(namespace.RootContext(nil), req)
|
|
||||||
if err != nil || (resp != nil && resp.IsError()) {
|
|
||||||
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
keys := resp.Data["keys"].([]string)
|
|
||||||
if len(keys) != 3 {
|
|
||||||
t.Fatalf("expected 3 roles, got: (%d)", len(keys))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capture initial passwords, before the periodic function is triggered
|
|
||||||
pws := make(map[string][]string, 0)
|
|
||||||
pws = capturePasswords(t, b, config, testCases, pws)
|
|
||||||
|
|
||||||
// Sleep to make sure the 65s role will be up for rotation by the time the
|
|
||||||
// periodic function ticks
|
|
||||||
time.Sleep(7 * time.Second)
|
|
||||||
|
|
||||||
// Sleep 75 to make sure the periodic func has time to actually run
|
|
||||||
time.Sleep(75 * time.Second)
|
|
||||||
pws = capturePasswords(t, b, config, testCases, pws)
|
|
||||||
|
|
||||||
// Sleep more, this should allow both sr65 and sr130 to rotate
|
|
||||||
time.Sleep(140 * time.Second)
|
|
||||||
pws = capturePasswords(t, b, config, testCases, pws)
|
|
||||||
|
|
||||||
// Verify all pws are as they should
|
|
||||||
pass := true
|
|
||||||
for k, v := range pws {
|
|
||||||
switch {
|
|
||||||
case k == "plugin-static-role-65":
|
|
||||||
// expect all passwords to be different
|
|
||||||
if v[0] == v[1] || v[1] == v[2] || v[0] == v[2] {
|
|
||||||
pass = false
|
|
||||||
}
|
|
||||||
case k == "plugin-static-role-130":
|
|
||||||
// expect the first two to be equal, but different from the third
|
|
||||||
if v[0] != v[1] || v[0] == v[2] {
|
|
||||||
pass = false
|
|
||||||
}
|
|
||||||
case k == "plugin-static-role-5400":
|
|
||||||
// expect all passwords to be equal
|
|
||||||
if v[0] != v[1] || v[1] != v[2] {
|
|
||||||
pass = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !pass {
|
|
||||||
t.Fatalf("password rotations did not match expected: %#v", pws)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBackend_StaticRole_Rotations_MongoDB(t *testing.T) {
|
func TestBackend_StaticRole_Rotations_MongoDB(t *testing.T) {
|
||||||
|
cleanup, connURL := mongodb.PrepareTestContainerWithDatabase(t, "latest", "vaulttestdb")
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
uc := userCreator(func(t *testing.T, username, password string) {
|
||||||
|
testCreateDBUser(t, connURL, "vaulttestdb", username, password)
|
||||||
|
})
|
||||||
|
testBackend_StaticRole_Rotations(t, uc, map[string]interface{}{
|
||||||
|
"connection_url": connURL,
|
||||||
|
"plugin_name": "mongodb-database-plugin",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBackend_StaticRole_Rotations_MongoDBAtlas(t *testing.T) {
|
||||||
|
// To get the project ID, connect to cloud.mongodb.com, go to the vault-test project and
|
||||||
|
// look at Project Settings.
|
||||||
|
projID := os.Getenv("VAULT_MONGODBATLAS_PROJECT_ID")
|
||||||
|
// For the private and public key, go to Organization Access Manager on cloud.mongodb.com,
|
||||||
|
// choose Create API Key, then create one using the defaults. Then go back to the vault-test
|
||||||
|
// project and add the API key to it, with permissions "Project Owner".
|
||||||
|
privKey := os.Getenv("VAULT_MONGODBATLAS_PRIVATE_KEY")
|
||||||
|
pubKey := os.Getenv("VAULT_MONGODBATLAS_PUBLIC_KEY")
|
||||||
|
if projID == "" {
|
||||||
|
t.Logf("Skipping MongoDB Atlas test because VAULT_MONGODBATLAS_PROJECT_ID not set")
|
||||||
|
t.SkipNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := digest.NewTransport(pubKey, privKey)
|
||||||
|
cl, err := transport.Client()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
api, err := mongodbatlasapi.New(cl)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
uc := userCreator(func(t *testing.T, username, password string) {
|
||||||
|
// Delete the user in case it's still there from an earlier run, ignore
|
||||||
|
// errors in case it's not.
|
||||||
|
_, _ = api.DatabaseUsers.Delete(context.Background(), projID, username)
|
||||||
|
|
||||||
|
req := &mongodbatlasapi.DatabaseUser{
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
DatabaseName: "admin",
|
||||||
|
Roles: []mongodbatlasapi.Role{{RoleName: "atlasAdmin", DatabaseName: "admin"}},
|
||||||
|
}
|
||||||
|
_, _, err := api.DatabaseUsers.Create(context.Background(), projID, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
testBackend_StaticRole_Rotations(t, uc, map[string]interface{}{
|
||||||
|
"plugin_name": "mongodbatlas-database-plugin",
|
||||||
|
"project_id": projID,
|
||||||
|
"private_key": privKey,
|
||||||
|
"public_key": pubKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBackend_StaticRole_Rotations(t *testing.T, createUser userCreator, opts map[string]interface{}) {
|
||||||
cluster, sys := getCluster(t)
|
cluster, sys := getCluster(t)
|
||||||
defer cluster.Cleanup()
|
defer cluster.Cleanup()
|
||||||
|
|
||||||
@@ -829,6 +773,7 @@ func TestBackend_StaticRole_Rotations_MongoDB(t *testing.T) {
|
|||||||
config.StorageView = &logical.InmemStorage{}
|
config.StorageView = &logical.InmemStorage{}
|
||||||
config.System = sys
|
config.System = sys
|
||||||
|
|
||||||
|
// Rotation ticker starts running in Factory call
|
||||||
b, err := Factory(context.Background(), config)
|
b, err := Factory(context.Background(), config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -841,27 +786,18 @@ func TestBackend_StaticRole_Rotations_MongoDB(t *testing.T) {
|
|||||||
t.Fatal("database backend had no credential rotation queue")
|
t.Fatal("database backend had no credential rotation queue")
|
||||||
}
|
}
|
||||||
|
|
||||||
// configure backend, add item and confirm length
|
|
||||||
cleanup, connURL := mongodb.PrepareTestContainerWithDatabase(t, "latest", "vaulttestdb")
|
|
||||||
defer cleanup()
|
|
||||||
testCases := []string{"65", "130", "5400"}
|
|
||||||
// Create database users ahead
|
|
||||||
for _, tc := range testCases {
|
|
||||||
testCreateDBUser(t, connURL, "vaulttestdb", "statictestMongo"+tc, "test")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure a connection
|
// Configure a connection
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"connection_url": connURL,
|
|
||||||
"plugin_name": "mongodb-database-plugin",
|
|
||||||
"verify_connection": false,
|
"verify_connection": false,
|
||||||
"allowed_roles": []string{"*"},
|
"allowed_roles": []string{"*"},
|
||||||
"name": "plugin-mongo-test",
|
}
|
||||||
|
for k, v := range opts {
|
||||||
|
data[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &logical.Request{
|
req := &logical.Request{
|
||||||
Operation: logical.UpdateOperation,
|
Operation: logical.UpdateOperation,
|
||||||
Path: "config/plugin-mongo-test",
|
Path: "config/plugin-test",
|
||||||
Storage: config.StorageView,
|
Storage: config.StorageView,
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
@@ -870,13 +806,19 @@ func TestBackend_StaticRole_Rotations_MongoDB(t *testing.T) {
|
|||||||
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
t.Fatalf("err:%s resp:%#v\n", err, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testCases := []string{"10", "20", "100"}
|
||||||
|
// Create database users ahead
|
||||||
|
for _, tc := range testCases {
|
||||||
|
createUser(t, "statictest"+tc, "test")
|
||||||
|
}
|
||||||
|
|
||||||
// create three static roles with different rotation periods
|
// create three static roles with different rotation periods
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
roleName := "plugin-static-role-" + tc
|
roleName := "plugin-static-role-" + tc
|
||||||
data = map[string]interface{}{
|
data = map[string]interface{}{
|
||||||
"name": roleName,
|
"name": roleName,
|
||||||
"db_name": "plugin-mongo-test",
|
"db_name": "plugin-test",
|
||||||
"username": "statictestMongo" + tc,
|
"username": "statictest" + tc,
|
||||||
"rotation_period": tc,
|
"rotation_period": tc,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -920,16 +862,12 @@ func TestBackend_StaticRole_Rotations_MongoDB(t *testing.T) {
|
|||||||
pws := make(map[string][]string, 0)
|
pws := make(map[string][]string, 0)
|
||||||
pws = capturePasswords(t, b, config, testCases, pws)
|
pws = capturePasswords(t, b, config, testCases, pws)
|
||||||
|
|
||||||
// sleep to make sure the 65s role will be up for rotation by the time the
|
// sleep to make sure the periodic func has time to actually run
|
||||||
// periodic function ticks
|
time.Sleep(15 * time.Second)
|
||||||
time.Sleep(7 * time.Second)
|
|
||||||
|
|
||||||
// sleep 75 to make sure the periodic func has time to actually run
|
|
||||||
time.Sleep(75 * time.Second)
|
|
||||||
pws = capturePasswords(t, b, config, testCases, pws)
|
pws = capturePasswords(t, b, config, testCases, pws)
|
||||||
|
|
||||||
// sleep more, this should allow both sr65 and sr130 to rotate
|
// sleep more, this should allow both sr10 and sr20 to rotate
|
||||||
time.Sleep(140 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
pws = capturePasswords(t, b, config, testCases, pws)
|
pws = capturePasswords(t, b, config, testCases, pws)
|
||||||
|
|
||||||
// verify all pws are as they should
|
// verify all pws are as they should
|
||||||
@@ -939,21 +877,23 @@ func TestBackend_StaticRole_Rotations_MongoDB(t *testing.T) {
|
|||||||
t.Fatalf("expected to find 3 passwords for (%s), only found (%d)", k, len(v))
|
t.Fatalf("expected to find 3 passwords for (%s), only found (%d)", k, len(v))
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case k == "plugin-static-role-65":
|
case k == "plugin-static-role-10":
|
||||||
// expect all passwords to be different
|
// expect all passwords to be different
|
||||||
if v[0] == v[1] || v[1] == v[2] || v[0] == v[2] {
|
if v[0] == v[1] || v[1] == v[2] || v[0] == v[2] {
|
||||||
pass = false
|
pass = false
|
||||||
}
|
}
|
||||||
case k == "plugin-static-role-130":
|
case k == "plugin-static-role-20":
|
||||||
// expect the first two to be equal, but different from the third
|
// expect the first two to be equal, but different from the third
|
||||||
if v[0] != v[1] || v[0] == v[2] {
|
if v[0] != v[1] || v[0] == v[2] {
|
||||||
pass = false
|
pass = false
|
||||||
}
|
}
|
||||||
case k == "plugin-static-role-5400":
|
case k == "plugin-static-role-100":
|
||||||
// expect all passwords to be equal
|
// expect all passwords to be equal
|
||||||
if v[0] != v[1] || v[1] != v[2] {
|
if v[0] != v[1] || v[1] != v[2] {
|
||||||
pass = false
|
pass = false
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
t.Fatalf("unexpected password key: %v", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !pass {
|
if !pass {
|
||||||
@@ -1004,7 +944,7 @@ func TestBackend_StaticRole_LockRegression(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer b.Cleanup(context.Background())
|
defer b.Cleanup(context.Background())
|
||||||
|
|
||||||
cleanup, connURL := preparePostgresTestContainer(t, config.StorageView, b)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// Configure a connection
|
// Configure a connection
|
||||||
@@ -1073,7 +1013,7 @@ func TestBackend_StaticRole_Rotate_Invalid_Role(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer b.Cleanup(context.Background())
|
defer b.Cleanup(context.Background())
|
||||||
|
|
||||||
cleanup, connURL := preparePostgresTestContainer(t, config.StorageView, b)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// create the database user
|
// create the database user
|
||||||
|
|||||||
@@ -6,58 +6,17 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
|
||||||
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
|
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
|
||||||
|
postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/ory/dockertest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func prepareTestContainer(t *testing.T) (cleanup func(), retURL string) {
|
|
||||||
if os.Getenv("PG_URL") != "" {
|
|
||||||
return func() {}, os.Getenv("PG_URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
pool, err := dockertest.NewPool("")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to connect to docker: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resource, err := pool.Run("postgres", "latest", []string{"POSTGRES_PASSWORD=secret", "POSTGRES_DB=database"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Could not start local PostgreSQL docker container: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup = func() {
|
|
||||||
docker.CleanupResource(t, pool, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
retURL = fmt.Sprintf("postgres://postgres:secret@localhost:%s/database?sslmode=disable", resource.GetPort("5432/tcp"))
|
|
||||||
|
|
||||||
// exponential backoff-retry
|
|
||||||
if err = pool.Retry(func() error {
|
|
||||||
var err error
|
|
||||||
var db *sql.DB
|
|
||||||
db, err = sql.Open("postgres", retURL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
return db.Ping()
|
|
||||||
}); err != nil {
|
|
||||||
cleanup()
|
|
||||||
t.Fatalf("Could not connect to PostgreSQL docker container: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBackend_config_connection(t *testing.T) {
|
func TestBackend_config_connection(t *testing.T) {
|
||||||
var resp *logical.Response
|
var resp *logical.Response
|
||||||
var err error
|
var err error
|
||||||
@@ -107,7 +66,7 @@ func TestBackend_basic(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup, connURL := prepareTestContainer(t)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
connData := map[string]interface{}{
|
connData := map[string]interface{}{
|
||||||
@@ -131,7 +90,7 @@ func TestBackend_roleCrud(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup, connURL := prepareTestContainer(t)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
connData := map[string]interface{}{
|
connData := map[string]interface{}{
|
||||||
@@ -157,7 +116,7 @@ func TestBackend_BlockStatements(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup, connURL := prepareTestContainer(t)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
connData := map[string]interface{}{
|
connData := map[string]interface{}{
|
||||||
@@ -187,7 +146,7 @@ func TestBackend_roleReadOnly(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup, connURL := prepareTestContainer(t)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
connData := map[string]interface{}{
|
connData := map[string]interface{}{
|
||||||
@@ -218,7 +177,7 @@ func TestBackend_roleReadOnly_revocationSQL(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup, connURL := prepareTestContainer(t)
|
cleanup, connURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
connData := map[string]interface{}{
|
connData := map[string]interface{}{
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -15,6 +15,7 @@ require (
|
|||||||
github.com/DataDog/zstd v1.4.4 // indirect
|
github.com/DataDog/zstd v1.4.4 // indirect
|
||||||
github.com/NYTimes/gziphandler v1.1.1
|
github.com/NYTimes/gziphandler v1.1.1
|
||||||
github.com/SAP/go-hdb v0.14.1
|
github.com/SAP/go-hdb v0.14.1
|
||||||
|
github.com/Sectorbob/mlab-ns2 v0.0.0-20171030222938-d3aa0c295a8a
|
||||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
|
||||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f
|
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190620160927-9418d7b0cd0f
|
||||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5
|
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5
|
||||||
@@ -81,7 +82,7 @@ require (
|
|||||||
github.com/hashicorp/vault-plugin-auth-kubernetes v0.6.1
|
github.com/hashicorp/vault-plugin-auth-kubernetes v0.6.1
|
||||||
github.com/hashicorp/vault-plugin-auth-oci v0.5.4
|
github.com/hashicorp/vault-plugin-auth-oci v0.5.4
|
||||||
github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4
|
github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4
|
||||||
github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.1
|
github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.0-beta1.0.20200521152755-9cf156a44f9c
|
||||||
github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e
|
github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e
|
||||||
github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.5
|
github.com/hashicorp/vault-plugin-secrets-alicloud v0.5.5
|
||||||
github.com/hashicorp/vault-plugin-secrets-azure v0.5.6
|
github.com/hashicorp/vault-plugin-secrets-azure v0.5.6
|
||||||
@@ -111,6 +112,7 @@ require (
|
|||||||
github.com/mitchellh/gox v1.0.1
|
github.com/mitchellh/gox v1.0.1
|
||||||
github.com/mitchellh/mapstructure v1.2.2
|
github.com/mitchellh/mapstructure v1.2.2
|
||||||
github.com/mitchellh/reflectwalk v1.0.1
|
github.com/mitchellh/reflectwalk v1.0.1
|
||||||
|
github.com/mongodb/go-client-mongodb-atlas v0.1.2
|
||||||
github.com/natefinch/atomic v0.0.0-20150920032501-a62ce929ffcc
|
github.com/natefinch/atomic v0.0.0-20150920032501-a62ce929ffcc
|
||||||
github.com/ncw/swift v1.0.47
|
github.com/ncw/swift v1.0.47
|
||||||
github.com/nwaples/rardecode v1.0.0 // indirect
|
github.com/nwaples/rardecode v1.0.0 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -496,6 +496,8 @@ github.com/hashicorp/vault-plugin-auth-oci v0.5.4 h1:Hoauxh1V8Lusf7BRs+yXfoDTFQz
|
|||||||
github.com/hashicorp/vault-plugin-auth-oci v0.5.4/go.mod h1:j05O2b9fw2Q82NxDPhHMYVfHKvitUYGWfmqmpBdqmmc=
|
github.com/hashicorp/vault-plugin-auth-oci v0.5.4/go.mod h1:j05O2b9fw2Q82NxDPhHMYVfHKvitUYGWfmqmpBdqmmc=
|
||||||
github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4 h1:YE4qndazWmYGpVOoZI7nDGG+gwTZKzL1Ou4WZQ+Tdxk=
|
github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4 h1:YE4qndazWmYGpVOoZI7nDGG+gwTZKzL1Ou4WZQ+Tdxk=
|
||||||
github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4/go.mod h1:QjGrrxcRXv/4XkEZAlM0VMZEa3uxKAICFqDj27FP/48=
|
github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4/go.mod h1:QjGrrxcRXv/4XkEZAlM0VMZEa3uxKAICFqDj27FP/48=
|
||||||
|
github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.0-beta1.0.20200521152755-9cf156a44f9c h1:9pXwe7sEVhZ5C3U6egIrKaZBb5lD0FvLIjISEvpbQQA=
|
||||||
|
github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.0-beta1.0.20200521152755-9cf156a44f9c/go.mod h1:HTXNzFr/SAVtJOs7jz0XxZ69jlKtaceEwp37l86UAQ0=
|
||||||
github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.1 h1:fA6cFH8lIPH2M4KNTEzf1bpc6Tbyy5ZvoYP8H/TI9ts=
|
github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.1 h1:fA6cFH8lIPH2M4KNTEzf1bpc6Tbyy5ZvoYP8H/TI9ts=
|
||||||
github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.1/go.mod h1:MP3kfr0N+7miOTZFwKv952b9VkXM4S2Q6YtQCiNKWq8=
|
github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.1/go.mod h1:MP3kfr0N+7miOTZFwKv952b9VkXM4S2Q6YtQCiNKWq8=
|
||||||
github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e h1:0GK1BNBfglD2sydZ4XXMjJElhY8bC2TDdc0vk1Q9zbA=
|
github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e h1:0GK1BNBfglD2sydZ4XXMjJElhY8bC2TDdc0vk1Q9zbA=
|
||||||
|
|||||||
53
helper/testhelpers/postgresql/postgreshelper.go
Normal file
53
helper/testhelpers/postgresql/postgreshelper.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package postgresql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
||||||
|
"github.com/ory/dockertest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrepareTestContainer(t *testing.T, version string) (cleanup func(), retURL string) {
|
||||||
|
if os.Getenv("PG_URL") != "" {
|
||||||
|
return func() {}, os.Getenv("PG_URL")
|
||||||
|
}
|
||||||
|
if version == "" {
|
||||||
|
version = "latest"
|
||||||
|
}
|
||||||
|
|
||||||
|
pool, err := dockertest.NewPool("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to connect to docker: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resource, err := pool.Run("postgres", "latest", []string{"POSTGRES_PASSWORD=secret", "POSTGRES_DB=database"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not start local PostgreSQL docker container: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup = func() {
|
||||||
|
docker.CleanupResource(t, pool, resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
retURL = fmt.Sprintf("postgres://postgres:secret@localhost:%s/database?sslmode=disable", resource.GetPort("5432/tcp"))
|
||||||
|
|
||||||
|
// exponential backoff-retry
|
||||||
|
if err = pool.Retry(func() error {
|
||||||
|
var err error
|
||||||
|
var db *sql.DB
|
||||||
|
db, err = sql.Open("postgres", retURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
return db.Ping()
|
||||||
|
}); err != nil {
|
||||||
|
cleanup()
|
||||||
|
t.Fatalf("Could not connect to PostgreSQL docker container: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -7,10 +7,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/hashicorp/go-hclog"
|
log "github.com/hashicorp/go-hclog"
|
||||||
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
"github.com/hashicorp/vault/helper/testhelpers/postgresql"
|
||||||
"github.com/hashicorp/vault/sdk/helper/logging"
|
"github.com/hashicorp/vault/sdk/helper/logging"
|
||||||
"github.com/hashicorp/vault/sdk/physical"
|
"github.com/hashicorp/vault/sdk/physical"
|
||||||
"github.com/ory/dockertest"
|
|
||||||
|
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
@@ -19,11 +18,11 @@ func TestPostgreSQLBackend(t *testing.T) {
|
|||||||
logger := logging.NewVaultLogger(log.Debug)
|
logger := logging.NewVaultLogger(log.Debug)
|
||||||
|
|
||||||
// Use docker as pg backend if no url is provided via environment variables
|
// Use docker as pg backend if no url is provided via environment variables
|
||||||
var cleanup func()
|
|
||||||
connURL := os.Getenv("PGURL")
|
connURL := os.Getenv("PGURL")
|
||||||
if connURL == "" {
|
if connURL == "" {
|
||||||
cleanup, connURL = prepareTestContainer(t, logger)
|
cleanup, u := postgresql.PrepareTestContainer(t, "11.1")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
connURL = u
|
||||||
}
|
}
|
||||||
|
|
||||||
table := os.Getenv("PGTABLE")
|
table := os.Getenv("PGTABLE")
|
||||||
@@ -361,47 +360,6 @@ func testPostgresSQLLockRenewal(t *testing.T, ha physical.HABackend) {
|
|||||||
newLock.Unlock()
|
newLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareTestContainer(t *testing.T, logger log.Logger) (cleanup func(), retConnString string) {
|
|
||||||
// If environment variable is set, use this connectionstring without starting docker container
|
|
||||||
if os.Getenv("PGURL") != "" {
|
|
||||||
return func() {}, os.Getenv("PGURL")
|
|
||||||
}
|
|
||||||
|
|
||||||
pool, err := dockertest.NewPool("")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to connect to docker: %s", err)
|
|
||||||
}
|
|
||||||
// using 11.1 which is currently latest, use hard version for stability of tests
|
|
||||||
resource, err := pool.Run("postgres", "11.1", []string{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Could not start docker Postgres: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
retConnString = fmt.Sprintf("postgres://postgres@localhost:%v/postgres?sslmode=disable", resource.GetPort("5432/tcp"))
|
|
||||||
|
|
||||||
cleanup = func() {
|
|
||||||
docker.CleanupResource(t, pool, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provide a test function to the pool to test if docker instance service is up.
|
|
||||||
// We try to setup a pg backend as test for successful connect
|
|
||||||
// exponential backoff-retry, because the dockerinstance may not be able to accept
|
|
||||||
// connections yet, test by trying to setup a postgres backend, max-timeout is 60s
|
|
||||||
if err := pool.Retry(func() error {
|
|
||||||
var err error
|
|
||||||
_, err = NewPostgreSQLBackend(map[string]string{
|
|
||||||
"connection_url": retConnString,
|
|
||||||
}, logger)
|
|
||||||
return err
|
|
||||||
|
|
||||||
}); err != nil {
|
|
||||||
cleanup()
|
|
||||||
t.Fatalf("Could not connect to docker: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cleanup, retConnString
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupDatabaseObjects(t *testing.T, logger log.Logger, pg *PostgreSQLBackend) {
|
func setupDatabaseObjects(t *testing.T, logger log.Logger, pg *PostgreSQLBackend) {
|
||||||
var err error
|
var err error
|
||||||
// Setup tables and indexes if not exists.
|
// Setup tables and indexes if not exists.
|
||||||
|
|||||||
@@ -4,64 +4,24 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
"github.com/hashicorp/vault/helper/testhelpers/postgresql"
|
||||||
"github.com/hashicorp/vault/sdk/database/dbplugin"
|
"github.com/hashicorp/vault/sdk/database/dbplugin"
|
||||||
"github.com/hashicorp/vault/sdk/helper/dbtxn"
|
"github.com/hashicorp/vault/sdk/helper/dbtxn"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
"github.com/ory/dockertest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func preparePostgresTestContainer(t *testing.T) (cleanup func(), retURL string) {
|
func getPostgreSQL(t *testing.T, options map[string]interface{}) (*PostgreSQL, func()) {
|
||||||
if os.Getenv("PG_URL") != "" {
|
cleanup, connURL := postgresql.PrepareTestContainer(t, "latest")
|
||||||
return func() {}, os.Getenv("PG_URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
pool, err := dockertest.NewPool("")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to connect to docker: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resource, err := pool.Run("postgres", "latest", []string{"POSTGRES_PASSWORD=secret", "POSTGRES_DB=database"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Could not start local PostgreSQL docker container: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup = func() {
|
|
||||||
docker.CleanupResource(t, pool, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
retURL = fmt.Sprintf("postgres://postgres:secret@localhost:%s/database?sslmode=disable", resource.GetPort("5432/tcp"))
|
|
||||||
|
|
||||||
// exponential backoff-retry
|
|
||||||
if err = pool.Retry(func() error {
|
|
||||||
var err error
|
|
||||||
var db *sql.DB
|
|
||||||
db, err = sql.Open("postgres", retURL)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
return db.Ping()
|
|
||||||
}); err != nil {
|
|
||||||
cleanup()
|
|
||||||
t.Fatalf("Could not connect to PostgreSQL docker container: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostgreSQL_Initialize(t *testing.T) {
|
|
||||||
cleanup, connURL := preparePostgresTestContainer(t)
|
|
||||||
defer cleanup()
|
|
||||||
|
|
||||||
connectionDetails := map[string]interface{}{
|
connectionDetails := map[string]interface{}{
|
||||||
"connection_url": connURL,
|
"connection_url": connURL,
|
||||||
"max_open_connections": 5,
|
}
|
||||||
|
for k, v := range options {
|
||||||
|
connectionDetails[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
db := new()
|
db := new()
|
||||||
@@ -73,23 +33,29 @@ func TestPostgreSQL_Initialize(t *testing.T) {
|
|||||||
if !db.Initialized {
|
if !db.Initialized {
|
||||||
t.Fatal("Database should be initialized")
|
t.Fatal("Database should be initialized")
|
||||||
}
|
}
|
||||||
|
return db, cleanup
|
||||||
|
}
|
||||||
|
|
||||||
err = db.Close()
|
func TestPostgreSQL_Initialize(t *testing.T) {
|
||||||
if err != nil {
|
db, cleanup := getPostgreSQL(t, map[string]interface{}{
|
||||||
|
"max_open_connections": 5,
|
||||||
|
})
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
if err := db.Close(); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Test decoding a string value for max_open_connections
|
func TestPostgreSQL_InitializeWithStringVals(t *testing.T) {
|
||||||
connectionDetails = map[string]interface{}{
|
db, cleanup := getPostgreSQL(t, map[string]interface{}{
|
||||||
"connection_url": connURL,
|
|
||||||
"max_open_connections": "5",
|
"max_open_connections": "5",
|
||||||
}
|
})
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
_, err = db.Init(context.Background(), connectionDetails, true)
|
if err := db.Close(); err != nil {
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPostgreSQL_CreateUser_missingArgs(t *testing.T) {
|
func TestPostgreSQL_CreateUser_missingArgs(t *testing.T) {
|
||||||
@@ -180,19 +146,9 @@ func TestPostgreSQL_CreateUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shared test container for speed - there should not be any overlap between the tests
|
// Shared test container for speed - there should not be any overlap between the tests
|
||||||
cleanup, connURL := preparePostgresTestContainer(t)
|
db, cleanup := getPostgreSQL(t, nil)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
connectionDetails := map[string]interface{}{
|
|
||||||
"connection_url": connURL,
|
|
||||||
}
|
|
||||||
|
|
||||||
db := new()
|
|
||||||
_, err := db.Init(context.Background(), connectionDetails, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
usernameConfig := dbplugin.UsernameConfig{
|
usernameConfig := dbplugin.UsernameConfig{
|
||||||
@@ -218,14 +174,14 @@ func TestPostgreSQL_CreateUser(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = testCredsExist(t, connURL, username, password); err != nil {
|
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
|
||||||
t.Fatalf("Could not connect with new credentials: %s", err)
|
t.Fatalf("Could not connect with new credentials: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the role doesn't expire immediately
|
// Ensure that the role doesn't expire immediately
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
if err = testCredsExist(t, connURL, username, password); err != nil {
|
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
|
||||||
t.Fatalf("Could not connect with new credentials: %s", err)
|
t.Fatalf("Could not connect with new credentials: %s", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -252,24 +208,9 @@ func TestPostgreSQL_RenewUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shared test container for speed - there should not be any overlap between the tests
|
// Shared test container for speed - there should not be any overlap between the tests
|
||||||
cleanup, connURL := preparePostgresTestContainer(t)
|
db, cleanup := getPostgreSQL(t, nil)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
connectionDetails := map[string]interface{}{
|
|
||||||
"connection_url": connURL,
|
|
||||||
}
|
|
||||||
|
|
||||||
db := new()
|
|
||||||
|
|
||||||
// Give a timeout just in case the test decides to be problematic
|
|
||||||
initCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err := db.Init(initCtx, connectionDetails, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
statements := dbplugin.Statements{
|
statements := dbplugin.Statements{
|
||||||
@@ -291,7 +232,7 @@ func TestPostgreSQL_RenewUser(t *testing.T) {
|
|||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = testCredsExist(t, connURL, username, password); err != nil {
|
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
|
||||||
t.Fatalf("Could not connect with new credentials: %s", err)
|
t.Fatalf("Could not connect with new credentials: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +244,7 @@ func TestPostgreSQL_RenewUser(t *testing.T) {
|
|||||||
// Sleep longer than the initial expiration time
|
// Sleep longer than the initial expiration time
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
if err = testCredsExist(t, connURL, username, password); err != nil {
|
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
|
||||||
t.Fatalf("Could not connect with new credentials: %s", err)
|
t.Fatalf("Could not connect with new credentials: %s", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -331,9 +272,8 @@ func TestPostgreSQL_RotateRootCredentials(t *testing.T) {
|
|||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
cleanup, connURL := preparePostgresTestContainer(t)
|
cleanup, connURL := postgresql.PrepareTestContainer(t, "latest")
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
connURL = strings.Replace(connURL, "postgres:secret", `{{username}}:{{password}}`, -1)
|
connURL = strings.Replace(connURL, "postgres:secret", `{{username}}:{{password}}`, -1)
|
||||||
|
|
||||||
connectionDetails := map[string]interface{}{
|
connectionDetails := map[string]interface{}{
|
||||||
@@ -344,7 +284,6 @@ func TestPostgreSQL_RotateRootCredentials(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
db := new()
|
db := new()
|
||||||
|
|
||||||
connProducer := db.SQLConnectionProducer
|
connProducer := db.SQLConnectionProducer
|
||||||
|
|
||||||
// Give a timeout just in case the test decides to be problematic
|
// Give a timeout just in case the test decides to be problematic
|
||||||
@@ -400,24 +339,9 @@ func TestPostgreSQL_RevokeUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shared test container for speed - there should not be any overlap between the tests
|
// Shared test container for speed - there should not be any overlap between the tests
|
||||||
cleanup, connURL := preparePostgresTestContainer(t)
|
db, cleanup := getPostgreSQL(t, nil)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
connectionDetails := map[string]interface{}{
|
|
||||||
"connection_url": connURL,
|
|
||||||
}
|
|
||||||
|
|
||||||
db := new()
|
|
||||||
|
|
||||||
// Give a timeout just in case the test decides to be problematic
|
|
||||||
initCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err := db.Init(initCtx, connectionDetails, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
statements := dbplugin.Statements{
|
statements := dbplugin.Statements{
|
||||||
@@ -435,7 +359,7 @@ func TestPostgreSQL_RevokeUser(t *testing.T) {
|
|||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = testCredsExist(t, connURL, username, password); err != nil {
|
if err = testCredsExist(t, db.ConnectionURL, username, password); err != nil {
|
||||||
t.Fatalf("Could not connect with new credentials: %s", err)
|
t.Fatalf("Could not connect with new credentials: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,7 +369,7 @@ func TestPostgreSQL_RevokeUser(t *testing.T) {
|
|||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := testCredsExist(t, connURL, username, password); err == nil {
|
if err := testCredsExist(t, db.ConnectionURL, username, password); err == nil {
|
||||||
t.Fatal("Credentials were not revoked")
|
t.Fatal("Credentials were not revoked")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -530,29 +454,13 @@ func TestPostgresSQL_SetCredentials(t *testing.T) {
|
|||||||
|
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
// Shared test container for speed - there should not be any overlap between the tests
|
db, cleanup := getPostgreSQL(t, nil)
|
||||||
cleanup, connURL := preparePostgresTestContainer(t)
|
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
// create the database user
|
// create the database user
|
||||||
dbUser := "vaultstatictest"
|
dbUser := "vaultstatictest"
|
||||||
initPassword := "password"
|
initPassword := "password"
|
||||||
createTestPGUser(t, connURL, dbUser, initPassword, testRoleStaticCreate)
|
createTestPGUser(t, db.ConnectionURL, dbUser, initPassword, testRoleStaticCreate)
|
||||||
|
|
||||||
connectionDetails := map[string]interface{}{
|
|
||||||
"connection_url": connURL,
|
|
||||||
}
|
|
||||||
|
|
||||||
db := new()
|
|
||||||
|
|
||||||
// Give a timeout just in case the test decides to be problematic
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
_, err := db.Init(ctx, connectionDetails, true)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
statements := dbplugin.Statements{
|
statements := dbplugin.Statements{
|
||||||
Rotation: test.rotationStmts,
|
Rotation: test.rotationStmts,
|
||||||
@@ -568,20 +476,20 @@ func TestPostgresSQL_SetCredentials(t *testing.T) {
|
|||||||
Password: password,
|
Password: password,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := testCredsExist(t, connURL, dbUser, initPassword); err != nil {
|
if err := testCredsExist(t, db.ConnectionURL, dbUser, initPassword); err != nil {
|
||||||
t.Fatalf("Could not connect with initial credentials: %s", err)
|
t.Fatalf("Could not connect with initial credentials: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
username, password, err := db.SetCredentials(ctx, statements, usernameConfig)
|
username, password, err := db.SetCredentials(context.Background(), statements, usernameConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := testCredsExist(t, connURL, username, password); err != nil {
|
if err := testCredsExist(t, db.ConnectionURL, username, password); err != nil {
|
||||||
t.Fatalf("Could not connect with new credentials: %s", err)
|
t.Fatalf("Could not connect with new credentials: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := testCredsExist(t, connURL, username, initPassword); err == nil {
|
if err := testCredsExist(t, db.ConnectionURL, username, initPassword); err == nil {
|
||||||
t.Fatalf("Should not be able to connect with initial credentials")
|
t.Fatalf("Should not be able to connect with initial credentials")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
log "github.com/hashicorp/go-hclog"
|
log "github.com/hashicorp/go-hclog"
|
||||||
@@ -15,11 +13,9 @@ import (
|
|||||||
"github.com/hashicorp/vault/builtin/logical/pki"
|
"github.com/hashicorp/vault/builtin/logical/pki"
|
||||||
"github.com/hashicorp/vault/builtin/logical/transit"
|
"github.com/hashicorp/vault/builtin/logical/transit"
|
||||||
"github.com/hashicorp/vault/helper/builtinplugins"
|
"github.com/hashicorp/vault/helper/builtinplugins"
|
||||||
"github.com/hashicorp/vault/helper/testhelpers/docker"
|
|
||||||
"github.com/hashicorp/vault/http"
|
"github.com/hashicorp/vault/http"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
"github.com/hashicorp/vault/vault"
|
"github.com/hashicorp/vault/vault"
|
||||||
"github.com/ory/dockertest"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// testVaultServer creates a test vault cluster and returns a configured API
|
// testVaultServer creates a test vault cluster and returns a configured API
|
||||||
@@ -83,40 +79,3 @@ func testVaultServerCoreConfig(t testing.TB, coreConfig *vault.CoreConfig) (*api
|
|||||||
|
|
||||||
return client, unsealKeys, func() { defer cluster.Cleanup() }
|
return client, unsealKeys, func() { defer cluster.Cleanup() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// testPostgresDB creates a testing postgres database in a Docker container,
|
|
||||||
// returning the connection URL and the associated closer function.
|
|
||||||
func testPostgresDB(t testing.TB) (string, func()) {
|
|
||||||
pool, err := dockertest.NewPool("")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("postgresdb: failed to connect to docker: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resource, err := pool.Run("postgres", "latest", []string{
|
|
||||||
"POSTGRES_PASSWORD=secret",
|
|
||||||
"POSTGRES_DB=database",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("postgresdb: could not start container: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup := func() {
|
|
||||||
docker.CleanupResource(t, pool, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := fmt.Sprintf("postgres://postgres:secret@localhost:%s/database?sslmode=disable", resource.GetPort("5432/tcp"))
|
|
||||||
|
|
||||||
if err := pool.Retry(func() error {
|
|
||||||
db, err := sql.Open("postgres", addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
return db.Ping()
|
|
||||||
}); err != nil {
|
|
||||||
cleanup()
|
|
||||||
t.Fatalf("postgresdb: could not connect: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return addr, cleanup
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
|
postgreshelper "github.com/hashicorp/vault/helper/testhelpers/postgresql"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRenewer_Renew(t *testing.T) {
|
func TestRenewer_Renew(t *testing.T) {
|
||||||
@@ -89,8 +90,8 @@ func TestRenewer_Renew(t *testing.T) {
|
|||||||
t.Run("database", func(t *testing.T) {
|
t.Run("database", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
pgURL, pgDone := testPostgresDB(t)
|
cleanup, pgURL := postgreshelper.PrepareTestContainer(t, "")
|
||||||
defer pgDone()
|
defer cleanup()
|
||||||
|
|
||||||
if err := client.Sys().Mount("database", &api.MountInput{
|
if err := client.Sys().Mount("database", &api.MountInput{
|
||||||
Type: "database",
|
Type: "database",
|
||||||
|
|||||||
95
vendor/github.com/hashicorp/vault-plugin-database-mongodbatlas/README.md
generated
vendored
95
vendor/github.com/hashicorp/vault-plugin-database-mongodbatlas/README.md
generated
vendored
@@ -1,86 +1,25 @@
|
|||||||
# MongoDB Atlas Database Secrets Engine
|
# HashiCorp Vault Database Secrets Engine - MongoDB Atlas plugin
|
||||||
|
|
||||||
This plugin provides unique, short-lived credentials for [MongoDB Atlas](https://www.mongodb.com/cloud/atlas).
|
MongoDB Atlas is one of the supported plugins for the HashiCorp Vault Database Secrets Engine and allows for the programmatic generation of unique, ephemeral MongoDB [Database User](https://docs.atlas.mongodb.com/reference/api/database-users/) credentials in MongoDB Atlas Projects.
|
||||||
It is to be used with [Hashicorp Vault](https://www.github.com/hashicorp/vault).
|
|
||||||
|
|
||||||
**Please note**: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault, _please responsibly disclose_ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com).
|
**The plugin is included in version 1.4 of Vault.**
|
||||||
|
|
||||||
|
## Support, Bugs and Feature Requests
|
||||||
|
Support for the HashiCorp Vault Database Secrets Engine - MongoDB Atlas is provided under MongoDB Atlas support plans. Please submit support questions within the Atlas UI. Vault support is via HashiCorp.
|
||||||
|
|
||||||
|
Bugs should be filed under the Issues section of this repo.
|
||||||
|
|
||||||
|
Feature requests can be submitted in the Issues section or directly to MongoDB at https://feedback.mongodb.com/forums/924145-atlas - just select the Vault plugin as the category or vote for an already suggested feature.
|
||||||
|
|
||||||
## Quick Links
|
## Quick Links
|
||||||
|
- [Database Secrets Engine for MongoDB Atlas - Docs](https://www.vaultproject.io/docs/secrets/databases/mongodbatlas)
|
||||||
- [Database Secrets Engine for MongoDB Atlas](https://www.vaultproject.io/docs/secrets/databases/mongodbatlas.html)
|
- [Database Secrets Engine for MongoDB Atlas - API Docs](https://www.vaultproject.io/api-docs/secret/databases/mongodbatlas/)
|
||||||
- [MongoDB Atlas Website](https://www.mongodb.com/cloud/atlas)
|
- [MongoDB Atlas Website](https://www.mongodb.com/cloud/atlas)
|
||||||
- [Vault Website](https://www.vaultproject.io)
|
- [Vault Website](https://www.vaultproject.io)
|
||||||
- [Vault Github](https://www.github.com/hashicorp/vault)
|
|
||||||
|
|
||||||
## Getting Started
|
**Please note**: Hashicorp takes Vault's security and their users' trust very seriously, as does MongoDB.
|
||||||
|
|
||||||
This is a Vault plugin and is meant to work with Vault. This guide assumes you have already installed Vault
|
If you believe you have found a security issue in Vault or with this plugin, _please responsibly disclose_ by
|
||||||
and have a basic understanding of how Vault works.
|
contacting HashiCorp at [security@hashicorp.com](mailto:security@hashicorp.com) and contact MongoDB
|
||||||
|
directly via [security@mongodb.com](mailto:security@mongodb.com) or
|
||||||
Otherwise, first read this guide on how to [get started with Vault](https://www.vaultproject.io/intro/getting-started/install.html).
|
[open a ticket](https://jira.mongodb.org/plugins/servlet/samlsso?redirectTo=%2Fbrowse%2FSECURITY) (link is external).
|
||||||
|
|
||||||
To learn specifically about how plugins work, see documentation on [Vault plugins](https://www.vaultproject.io/docs/internals/plugins.html).
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
This plugin is bundled in Vault version 1.4.0 or later. It may also be built and mounted externally
|
|
||||||
with earlier versions of Vault. For details on this process please see the documentation for Vault's
|
|
||||||
[plugin system](https://www.vaultproject.io/docs/internals/plugins.html).
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
1. Enable the database secrets engine if it is not already enabled:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ vault secrets enable database
|
|
||||||
Success! Enabled the database secrets engine at: database/
|
|
||||||
```
|
|
||||||
|
|
||||||
The secrets engine will be enabled at the default path which is name of the engine. To
|
|
||||||
enable the secrets engine at a different path use the `-path` argument.
|
|
||||||
|
|
||||||
1. Configure Vault with the proper plugin and connection information:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ vault write database/config/my-mongodbatlas-database \
|
|
||||||
plugin_name=mongodbatlas-database-plugin \
|
|
||||||
allowed_roles="my-role" \
|
|
||||||
public_key="a-public-key" \
|
|
||||||
private_key="a-private-key!" \
|
|
||||||
project_id="a-project-id"
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Configure a role that maps a name in Vault to a MongoDB Atlas command that executes and
|
|
||||||
creates the Database User credential:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ vault write database/roles/my-role \
|
|
||||||
db_name=my-mongodbatlas-database \
|
|
||||||
creation_statements='{ "database_name": "admin", "roles": [{"databaseName":"admin","roleName":"atlasAdmin"}]}' \
|
|
||||||
default_ttl="1h" \
|
|
||||||
max_ttl="24h"
|
|
||||||
Success! Data written to: database/roles/my-role
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
After the secrets engine is configured and a user/machine has a Vault token with
|
|
||||||
the proper permissions, it can generate credentials.
|
|
||||||
|
|
||||||
1. Generate a new credential by reading from the `/creds` endpoint with the name
|
|
||||||
of the role:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ vault read database/creds/my-role
|
|
||||||
Key Value
|
|
||||||
--- -----
|
|
||||||
lease_id database/creds/my-role/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6
|
|
||||||
lease_duration 1h
|
|
||||||
lease_renewable true
|
|
||||||
password A1a-QwxApKgnfCp1AJYN
|
|
||||||
username v-5WFTBKdwOTLOqWLgsjvH-1565815206
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
For more details on configuring and using the plugin, refer to the [Database Secrets Engine for MongoDB Atlas](https://www.vaultproject.io/docs/secrets/databases/mongodbatlas.html)
|
|
||||||
documentation.
|
|
||||||
|
|||||||
4
vendor/github.com/hashicorp/vault-plugin-database-mongodbatlas/go.mod
generated
vendored
4
vendor/github.com/hashicorp/vault-plugin-database-mongodbatlas/go.mod
generated
vendored
@@ -6,8 +6,8 @@ require (
|
|||||||
github.com/Sectorbob/mlab-ns2 v0.0.0-20171030222938-d3aa0c295a8a
|
github.com/Sectorbob/mlab-ns2 v0.0.0-20171030222938-d3aa0c295a8a
|
||||||
github.com/go-test/deep v1.0.2 // indirect
|
github.com/go-test/deep v1.0.2 // indirect
|
||||||
github.com/hashicorp/go-version v1.2.0 // indirect
|
github.com/hashicorp/go-version v1.2.0 // indirect
|
||||||
github.com/hashicorp/vault/api v1.0.5-0.20200317185738-82f498082f02
|
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820
|
||||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200317185738-82f498082f02
|
github.com/hashicorp/vault/sdk v0.1.14-0.20200215224050-f6547fa8e820
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
|
||||||
github.com/mitchellh/mapstructure v1.1.2
|
github.com/mitchellh/mapstructure v1.1.2
|
||||||
github.com/mongodb/go-client-mongodb-atlas v0.1.2
|
github.com/mongodb/go-client-mongodb-atlas v0.1.2
|
||||||
|
|||||||
8
vendor/github.com/hashicorp/vault-plugin-database-mongodbatlas/go.sum
generated
vendored
8
vendor/github.com/hashicorp/vault-plugin-database-mongodbatlas/go.sum
generated
vendored
@@ -75,12 +75,12 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+
|
|||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/vault/api v1.0.5-0.20200317185738-82f498082f02 h1:OGEV0U0+lb8SP5aZA1m456Sr3MYxFel2awVr55QRri0=
|
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820 h1:biZidYDDEWnuOI9mXnJre8lwHKhb5ym85aSXk3oz/dc=
|
||||||
github.com/hashicorp/vault/api v1.0.5-0.20200317185738-82f498082f02/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o=
|
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o=
|
||||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200215195600-2ca765f0a500 h1:tiMX2ewq4ble+e2zENzBvaH2dMoFHe80NbnrF5Ir9Kk=
|
github.com/hashicorp/vault/sdk v0.1.14-0.20200215195600-2ca765f0a500 h1:tiMX2ewq4ble+e2zENzBvaH2dMoFHe80NbnrF5Ir9Kk=
|
||||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200215195600-2ca765f0a500/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
github.com/hashicorp/vault/sdk v0.1.14-0.20200215195600-2ca765f0a500/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
||||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200317185738-82f498082f02 h1:vVrOAVfunVvkTkE9iF3Fe1+PGPLwGIp3nP4qgHGrHFs=
|
github.com/hashicorp/vault/sdk v0.1.14-0.20200215224050-f6547fa8e820 h1:TmDZ1sS6gU0hFeFlFuyJVUwRPEzifZIHCBeS2WF2uSc=
|
||||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200317185738-82f498082f02/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
github.com/hashicorp/vault/sdk v0.1.14-0.20200215224050-f6547fa8e820/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
||||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||||
|
|||||||
10
vendor/github.com/hashicorp/vault-plugin-database-mongodbatlas/mongodbatlas.go
generated
vendored
10
vendor/github.com/hashicorp/vault-plugin-database-mongodbatlas/mongodbatlas.go
generated
vendored
@@ -35,9 +35,9 @@ func new() *MongoDBAtlas {
|
|||||||
connProducer.Type = mongoDBAtlasTypeName
|
connProducer.Type = mongoDBAtlasTypeName
|
||||||
|
|
||||||
credsProducer := &credsutil.SQLCredentialsProducer{
|
credsProducer := &credsutil.SQLCredentialsProducer{
|
||||||
DisplayNameLen: 15,
|
DisplayNameLen: credsutil.NoneLength,
|
||||||
RoleNameLen: 15,
|
RoleNameLen: 15,
|
||||||
UsernameLen: 100,
|
UsernameLen: 20,
|
||||||
Separator: "-",
|
Separator: "-",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,12 +149,6 @@ func (m *MongoDBAtlas) SetCredentials(ctx context.Context, statements dbplugin.S
|
|||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
statements = dbutil.StatementCompatibilityHelper(statements)
|
|
||||||
|
|
||||||
if len(statements.Creation) == 0 {
|
|
||||||
return "", "", dbutil.ErrEmptyCreationStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := m.getConnection(ctx)
|
client, err := m.getConnection(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
|
|||||||
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -431,7 +431,7 @@ github.com/hashicorp/vault-plugin-auth-kubernetes
|
|||||||
github.com/hashicorp/vault-plugin-auth-oci
|
github.com/hashicorp/vault-plugin-auth-oci
|
||||||
# github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4
|
# github.com/hashicorp/vault-plugin-database-elasticsearch v0.5.4
|
||||||
github.com/hashicorp/vault-plugin-database-elasticsearch
|
github.com/hashicorp/vault-plugin-database-elasticsearch
|
||||||
# github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.1
|
# github.com/hashicorp/vault-plugin-database-mongodbatlas v0.1.0-beta1.0.20200521152755-9cf156a44f9c
|
||||||
github.com/hashicorp/vault-plugin-database-mongodbatlas
|
github.com/hashicorp/vault-plugin-database-mongodbatlas
|
||||||
# github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e
|
# github.com/hashicorp/vault-plugin-secrets-ad v0.6.4-beta1.0.20200518124111-3dceeb3ce90e
|
||||||
github.com/hashicorp/vault-plugin-secrets-ad/plugin
|
github.com/hashicorp/vault-plugin-secrets-ad/plugin
|
||||||
|
|||||||
Reference in New Issue
Block a user