mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-06 14:47:01 +02:00
database/mssql: set default root rotation stmt for contained db (#29399)
* database/mssql: set default root rotation stmt for contained db * changelog * add rotate root test * fix test * update passwords to make mssql happy * create admin user * update contained user create query * remove test
This commit is contained in:
parent
9d31bb8586
commit
04e75372fb
3
changelog/29399.txt
Normal file
3
changelog/29399.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
```release-note:bug
|
||||||
|
database/mssql: Fix a bug where contained databases would silently fail root rotation if a custom root rotation statement was not provided.
|
||||||
|
```
|
@ -345,8 +345,11 @@ func (m *MSSQL) UpdateUser(ctx context.Context, req dbplugin.UpdateUserRequest)
|
|||||||
|
|
||||||
func (m *MSSQL) updateUserPass(ctx context.Context, username string, changePass *dbplugin.ChangePassword) error {
|
func (m *MSSQL) updateUserPass(ctx context.Context, username string, changePass *dbplugin.ChangePassword) error {
|
||||||
stmts := changePass.Statements.Commands
|
stmts := changePass.Statements.Commands
|
||||||
if len(stmts) == 0 && !m.containedDB {
|
if len(stmts) == 0 {
|
||||||
stmts = []string{alterLoginSQL}
|
stmts = []string{alterLoginSQL}
|
||||||
|
if m.containedDB {
|
||||||
|
stmts = []string{alterUserContainedSQL}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
password := changePass.NewPassword
|
password := changePass.NewPassword
|
||||||
@ -384,6 +387,11 @@ func (m *MSSQL) updateUserPass(ctx context.Context, username string, changePass
|
|||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if len(stmts) == 0 {
|
||||||
|
// should not happen, but guard against it anyway
|
||||||
|
return errors.New("no statement provided")
|
||||||
|
}
|
||||||
|
|
||||||
for _, stmt := range stmts {
|
for _, stmt := range stmts {
|
||||||
for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") {
|
for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") {
|
||||||
query = strings.TrimSpace(query)
|
query = strings.TrimSpace(query)
|
||||||
@ -431,3 +439,7 @@ EXEC (@stmt)`
|
|||||||
const alterLoginSQL = `
|
const alterLoginSQL = `
|
||||||
ALTER LOGIN [{{username}}] WITH PASSWORD = '{{password}}'
|
ALTER LOGIN [{{username}}] WITH PASSWORD = '{{password}}'
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const alterUserContainedSQL = `
|
||||||
|
ALTER USER [{{username}}] WITH PASSWORD = '{{password}}'
|
||||||
|
`
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInitialize(t *testing.T) {
|
func TestMSSQLInitialize(t *testing.T) {
|
||||||
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ func TestInitialize(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewUser(t *testing.T) {
|
func TestMSSQLNewUser(t *testing.T) {
|
||||||
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ func TestNewUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateUser_password(t *testing.T) {
|
func TestMSSQLUpdateUser_password(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
req dbplugin.UpdateUserRequest
|
req dbplugin.UpdateUserRequest
|
||||||
expectErr bool
|
expectErr bool
|
||||||
@ -312,7 +312,7 @@ func TestUpdateUser_password(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteUser(t *testing.T) {
|
func TestMSSQLDeleteUser(t *testing.T) {
|
||||||
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@ -358,7 +358,7 @@ func TestDeleteUser(t *testing.T) {
|
|||||||
assertCredsDoNotExist(t, connURL, dbUser, initPassword)
|
assertCredsDoNotExist(t, connURL, dbUser, initPassword)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteUserContainedDB(t *testing.T) {
|
func TestMSSQLDeleteUserContainedDB(t *testing.T) {
|
||||||
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@ -405,7 +405,7 @@ func TestDeleteUserContainedDB(t *testing.T) {
|
|||||||
assertContainedDBCredsDoNotExist(t, connURL, dbUser)
|
assertContainedDBCredsDoNotExist(t, connURL, dbUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainedDBSQLSanitization(t *testing.T) {
|
func TestMSSQLContainedDBSQLSanitization(t *testing.T) {
|
||||||
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@ -443,7 +443,7 @@ func TestContainedDBSQLSanitization(t *testing.T) {
|
|||||||
assert.EqualError(t, err, "mssql: Cannot alter the login 'vaultuser]', because it does not exist or you do not have permission.")
|
assert.EqualError(t, err, "mssql: Cannot alter the login 'vaultuser]', because it does not exist or you do not have permission.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSQLSanitization(t *testing.T) {
|
func TestMSSQLSanitization(t *testing.T) {
|
||||||
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
@ -576,3 +576,11 @@ const testMSSQLContainedLogin = `
|
|||||||
CREATE LOGIN [{{name}}] WITH PASSWORD = '{{password}}';
|
CREATE LOGIN [{{name}}] WITH PASSWORD = '{{password}}';
|
||||||
CREATE USER [{{name}}] FOR LOGIN [{{name}}];
|
CREATE USER [{{name}}] FOR LOGIN [{{name}}];
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const testMSSQLContainedLoginAdmin = `
|
||||||
|
CREATE USER [{{name}}] WITH PASSWORD = '{{password}}';
|
||||||
|
|
||||||
|
ALTER ROLE db_datareader ADD MEMBER [{{name}}];
|
||||||
|
ALTER ROLE db_datawriter ADD MEMBER [{{name}}];
|
||||||
|
ALTER ROLE db_owner ADD MEMBER [{{name}}];
|
||||||
|
`
|
||||||
|
@ -115,13 +115,23 @@ the proper permission, it can generate credentials.
|
|||||||
|
|
||||||
## Example for Azure SQL database
|
## Example for Azure SQL database
|
||||||
|
|
||||||
Here is a complete example using Azure SQL Database. Note that databases in Azure SQL Database are [contained databases](https://docs.microsoft.com/en-us/sql/relational-databases/databases/contained-databases) and that we do not create a login for the user; instead, we associate the password directly with the user itself. Also note that you will need a separate connection and role for each Azure SQL database for which you want to generate dynamic credentials. You can use a single database backend mount for all these databases or use a separate mount for each of them. In this example, we use a custom path for the database backend.
|
Here is a complete example using Azure SQL Database. Note that databases in
|
||||||
|
Azure SQL Database are [contained databases](https://docs.microsoft.com/en-us/sql/relational-databases/databases/contained-databases)
|
||||||
|
and that we do not create a login for the user; instead, we associate the
|
||||||
|
password directly with the user itself. Also note that you will need a separate
|
||||||
|
connection and role for each Azure SQL database for which you want to generate
|
||||||
|
dynamic credentials. You can use a single database backend mount for all these
|
||||||
|
databases or use a separate mount for each of them. In this example, we use a
|
||||||
|
custom path for the database backend.
|
||||||
|
|
||||||
<Note>
|
<Note>
|
||||||
Azure SQL databases may use different authentication mechanism that are configured on the SQL server. Vault only supports SQL authentication. Azure AD authentication is not supported.
|
Azure SQL databases may use different authentication mechanism that are configured on the SQL server. Vault only supports SQL authentication. Azure AD authentication is not supported.
|
||||||
</Note>
|
</Note>
|
||||||
|
|
||||||
First, we mount a database backend at the azuresql path with `vault secrets enable -path=azuresql database`. Then we configure a connection called "testvault" to connect to a database called "test-vault", using "azuresql" at the beginning of our path:
|
First, we mount a database backend at the azuresql path with `vault secrets
|
||||||
|
enable -path=azuresql database`. Then we configure a connection called
|
||||||
|
"testvault" to connect to a database called "test-vault", using "azuresql" at
|
||||||
|
the beginning of our path and set the `contained_db` field:
|
||||||
|
|
||||||
~> Note: If you are using a windows vault client with cmd.exe, change the single quotes to double quotes in the connection string. Windows cmd.exe does not interpret single quotes as a continous string
|
~> Note: If you are using a windows vault client with cmd.exe, change the single quotes to double quotes in the connection string. Windows cmd.exe does not interpret single quotes as a continous string
|
||||||
|
|
||||||
@ -129,6 +139,7 @@ First, we mount a database backend at the azuresql path with `vault secrets enab
|
|||||||
$ vault write azuresql/config/testvault \
|
$ vault write azuresql/config/testvault \
|
||||||
plugin_name=mssql-database-plugin \
|
plugin_name=mssql-database-plugin \
|
||||||
connection_url='server=hashisqlserver.database.windows.net;port=1433;user id=admin;password=pAssw0rd;database=test-vault;app name=vault;' \
|
connection_url='server=hashisqlserver.database.windows.net;port=1433;user id=admin;password=pAssw0rd;database=test-vault;app name=vault;' \
|
||||||
|
contained_db=true \
|
||||||
allowed_roles="test"
|
allowed_roles="test"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user