mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00: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:
		 John-Michael Faircloth
					John-Michael Faircloth
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						GitHub
					
				
			
						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" | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user