Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

database/mssql: set default root rotation stmt for contained db #29399

Merged
merged 9 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/29399.txt
Original file line number Diff line number Diff line change
@@ -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.
```
14 changes: 13 additions & 1 deletion plugins/database/mssql/mssql.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
stmts := changePass.Statements.Commands
if len(stmts) == 0 && !m.containedDB {
if len(stmts) == 0 {
stmts = []string{alterLoginSQL}
if m.containedDB {
stmts = []string{alterUserContainedSQL}
}
}

password := changePass.NewPassword
Expand Down Expand Up @@ -384,6 +387,11 @@ func (m *MSSQL) updateUserPass(ctx context.Context, username string, changePass
_ = 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 _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") {
query = strings.TrimSpace(query)
Expand Down Expand Up @@ -431,3 +439,7 @@ EXEC (@stmt)`
const alterLoginSQL = `
ALTER LOGIN [{{username}}] WITH PASSWORD = '{{password}}'
`

const alterUserContainedSQL = `
ALTER USER [{{username}}] WITH PASSWORD = '{{password}}'
`
22 changes: 15 additions & 7 deletions plugins/database/mssql/mssql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"github.com/stretchr/testify/assert"
)

func TestInitialize(t *testing.T) {
func TestMSSQLInitialize(t *testing.T) {

Check failure on line 23 in plugins/database/mssql/mssql_test.go

View workflow job for this annotation

GitHub Actions / Code checks

Test TestMSSQLInitialize is missing a go doc
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
defer cleanup()

Expand Down Expand Up @@ -79,7 +79,7 @@
}
}

func TestNewUser(t *testing.T) {
func TestMSSQLNewUser(t *testing.T) {

Check failure on line 82 in plugins/database/mssql/mssql_test.go

View workflow job for this annotation

GitHub Actions / Code checks

Test TestMSSQLNewUser is missing a go doc
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
defer cleanup()

Expand Down Expand Up @@ -185,7 +185,7 @@
}
}

func TestUpdateUser_password(t *testing.T) {
func TestMSSQLUpdateUser_password(t *testing.T) {

Check failure on line 188 in plugins/database/mssql/mssql_test.go

View workflow job for this annotation

GitHub Actions / Code checks

Test TestMSSQLUpdateUser_password is missing a go doc
type testCase struct {
req dbplugin.UpdateUserRequest
expectErr bool
Expand Down Expand Up @@ -312,7 +312,7 @@
}
}

func TestDeleteUser(t *testing.T) {
func TestMSSQLDeleteUser(t *testing.T) {

Check failure on line 315 in plugins/database/mssql/mssql_test.go

View workflow job for this annotation

GitHub Actions / Code checks

Test TestMSSQLDeleteUser is missing a go doc
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
defer cleanup()

Expand Down Expand Up @@ -358,7 +358,7 @@
assertCredsDoNotExist(t, connURL, dbUser, initPassword)
}

func TestDeleteUserContainedDB(t *testing.T) {
func TestMSSQLDeleteUserContainedDB(t *testing.T) {

Check failure on line 361 in plugins/database/mssql/mssql_test.go

View workflow job for this annotation

GitHub Actions / Code checks

Test TestMSSQLDeleteUserContainedDB is missing a go doc
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
defer cleanup()

Expand Down Expand Up @@ -405,7 +405,7 @@
assertContainedDBCredsDoNotExist(t, connURL, dbUser)
}

func TestContainedDBSQLSanitization(t *testing.T) {
func TestMSSQLContainedDBSQLSanitization(t *testing.T) {

Check failure on line 408 in plugins/database/mssql/mssql_test.go

View workflow job for this annotation

GitHub Actions / Code checks

Test TestMSSQLContainedDBSQLSanitization is missing a go doc
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
defer cleanup()

Expand Down Expand Up @@ -443,7 +443,7 @@
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) {

Check failure on line 446 in plugins/database/mssql/mssql_test.go

View workflow job for this annotation

GitHub Actions / Code checks

Test TestMSSQLSanitization is missing a go doc
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
defer cleanup()

Expand Down Expand Up @@ -576,3 +576,11 @@
CREATE LOGIN [{{name}}] WITH PASSWORD = '{{password}}';
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}}];
`
15 changes: 13 additions & 2 deletions website/content/docs/secrets/databases/mssql.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,31 @@ the proper permission, it can generate credentials.

## 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>
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>

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

```shell-session
$ vault write azuresql/config/testvault \
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;' \
contained_db=true \
allowed_roles="test"
```

Expand Down
Loading