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

Add the static-roles feature for MSSQL #9062

Merged
merged 1 commit into from
Jun 20, 2020
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
69 changes: 66 additions & 3 deletions plugins/database/mssql/mssql.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ func (m *MSSQL) RotateRootCredentials(ctx context.Context, statements []string)

rotateStatents := statements
if len(rotateStatents) == 0 {
rotateStatents = []string{rotateRootCredentialsSQL}
rotateStatents = []string{alterLoginSQL}
}

db, err := m.getConnection(ctx)
Expand Down Expand Up @@ -357,6 +357,70 @@ func (m *MSSQL) RotateRootCredentials(ctx context.Context, statements []string)
return m.RawConfig, nil
}

func (m *MSSQL) SetCredentials(ctx context.Context, statements dbplugin.Statements, staticUser dbplugin.StaticUserConfig) (username, password string, err error) {
if len(statements.Rotation) == 0 {
statements.Rotation = []string{alterLoginSQL}
}

username = staticUser.Username
password = staticUser.Password

if username == "" || password == "" {
return "", "", errors.New("must provide both username and password")
}

m.Lock()
defer m.Unlock()

db, err := m.getConnection(ctx)
if err != nil {
return "", "", err
}

var exists bool

err = db.QueryRowContext(ctx, "SELECT 1 FROM master.sys.server_principals where name = N'$1'", username).Scan(&exists)

if err != nil && err != sql.ErrNoRows {
return "", "", err
}

stmts := statements.Rotation

// Start a transaction
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return "", "", err
}
defer func() {
_ = tx.Rollback()
}()

for _, stmt := range stmts {
for _, query := range strutil.ParseArbitraryStringSlice(stmt, ";") {
query = strings.TrimSpace(query)
if len(query) == 0 {
continue
}

m := map[string]string{
"name": username,
"username": username,
"password": password,
}
if err := dbtxn.ExecuteTxQuery(ctx, tx, m, query); err != nil {
return "", "", err
}
}
}

if err := tx.Commit(); err != nil {
return "", "", err
}

return username, password, nil
}

const dropUserSQL = `
USE [%s]
IF EXISTS
Expand All @@ -377,7 +441,6 @@ BEGIN
DROP LOGIN [%s]
END
`

const rotateRootCredentialsSQL = `
const alterLoginSQL = `
ALTER LOGIN [{{username}}] WITH PASSWORD = '{{password}}'
`
168 changes: 168 additions & 0 deletions plugins/database/mssql/mssql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

mssqlhelper "github.com/hashicorp/vault/helper/testhelpers/mssql"
"github.com/hashicorp/vault/sdk/database/dbplugin"
"github.com/hashicorp/vault/sdk/helper/dbtxn"
)

func TestMSSQL_Initialize(t *testing.T) {
Expand Down Expand Up @@ -123,6 +124,138 @@ func TestMSSQL_RotateRootCredentials(t *testing.T) {
}
}

func TestMSSQL_SetCredentials_missingArgs(t *testing.T) {
type testCase struct {
statements dbplugin.Statements
userConfig dbplugin.StaticUserConfig
}

tests := map[string]testCase{
"empty rotation statements": {
statements: dbplugin.Statements{
Rotation: nil,
},
userConfig: dbplugin.StaticUserConfig{
Username: "testuser",
Password: "password",
},
},
"empty username": {
statements: dbplugin.Statements{
Rotation: []string{`
ALTER LOGIN [{{username}}] WITH PASSWORD = '{{password}}';`,
},
},
userConfig: dbplugin.StaticUserConfig{
Username: "",
Password: "password",
},
},
"empty password": {
statements: dbplugin.Statements{
Rotation: []string{`
ALTER LOGIN [{{username}}] WITH PASSWORD = '{{password}}';`,
},
},
userConfig: dbplugin.StaticUserConfig{
Username: "testuser",
Password: "",
},
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
db := new()

username, password, err := db.SetCredentials(context.Background(), test.statements, test.userConfig)
if err == nil {
t.Fatalf("expected err, got nil")
}
if username != "" {
t.Fatalf("expected empty username, got [%s]", username)
}
if password != "" {
t.Fatalf("expected empty password, got [%s]", password)
}
})
}
}

func TestMSSQL_SetCredentials(t *testing.T) {
type testCase struct {
rotationStmts []string
}

tests := map[string]testCase{
"empty rotation statements": {
rotationStmts: []string{},
}, "username rotation": {
rotationStmts: []string{`
ALTER LOGIN [{{username}}] WITH PASSWORD = '{{password}}';`,
},
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
defer cleanup()

connectionDetails := map[string]interface{}{
"connection_url": connURL,
}

db := new()

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)
}

dbUser := "vaultstatictest"
initPassword := "p4$sw0rd"
createTestMSSQLUser(t, connURL, dbUser, initPassword, testMSSQLLogin)

if err := testCredsExist(t, connURL, dbUser, initPassword); err != nil {
t.Fatalf("Could not connect with initial credentials: %s", err)
}

statements := dbplugin.Statements{
Rotation: test.rotationStmts,
}

newPassword, err := db.GenerateCredentials(context.Background())
if err != nil {
t.Fatal(err)
}

usernameConfig := dbplugin.StaticUserConfig{
Username: dbUser,
Password: newPassword,
}

username, password, err := db.SetCredentials(ctx, statements, usernameConfig)
if err != nil {
t.Fatalf("err: %s", err)
}

if err := testCredsExist(t, connURL, username, password); err != nil {
t.Fatalf("Could not connect with new credentials: %s", err)
}

if err := testCredsExist(t, connURL, username, initPassword); err == nil {
t.Fatalf("Should not be able to connect with initial credentials")
}

})
}

}

func TestMSSQL_RevokeUser(t *testing.T) {
cleanup, connURL := mssqlhelper.PrepareMSSQLTestContainer(t)
defer cleanup()
Expand Down Expand Up @@ -198,6 +331,37 @@ func testCredsExist(t testing.TB, connURL, username, password string) error {
return db.Ping()
}

func createTestMSSQLUser(t *testing.T, connURL string, username, password, query string) {

db, err := sql.Open("mssql", connURL)
defer db.Close()
if err != nil {
t.Fatal(err)
}

// Start a transaction
ctx := context.Background()
tx, err := db.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
defer func() {
_ = tx.Rollback()
}()

m := map[string]string{
"name": username,
"password": password,
}
if err := dbtxn.ExecuteTxQuery(ctx, tx, m, query); err != nil {
t.Fatal(err)
}
// Commit the transaction
if err := tx.Commit(); err != nil {
t.Fatal(err)
}
}

const testMSSQLRole = `
CREATE LOGIN [{{name}}] WITH PASSWORD = '{{password}}';
CREATE USER [{{name}}] FOR LOGIN [{{name}}];
Expand All @@ -207,3 +371,7 @@ const testMSSQLDrop = `
DROP USER [{{name}}];
DROP LOGIN [{{name}}];
`

const testMSSQLLogin = `
CREATE LOGIN [{{name}}] WITH PASSWORD = '{{password}}';
`
2 changes: 1 addition & 1 deletion website/pages/docs/secrets/databases/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ the proper permission, it can generate credentials.
| [InfluxDB](/docs/secrets/databases/influxdb) | Yes | Yes | No |
| [MongoDB](/docs/secrets/databases/mongodb) | No | Yes | Yes |
| [MongoDB Atlas](/docs/secrets/databases/mongodbatlas) | No | Yes | Yes |
| [MSSQL](/docs/secrets/databases/mssql) | Yes | Yes | No |
| [MSSQL](/docs/secrets/databases/mssql) | Yes | Yes | Yes |
| [MySQL/MariaDB](/docs/secrets/databases/mysql-maria) | Yes | Yes | Yes |
| [Oracle](/docs/secrets/databases/oracle) | Yes | Yes | Yes |
| [PostgreSQL](/docs/secrets/databases/postgresql) | Yes | Yes | Yes |
Expand Down
2 changes: 1 addition & 1 deletion website/pages/docs/secrets/databases/mssql.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ more information about setting up the database secrets engine.

| Plugin Name | Root Credential Rotation | Dynamic Roles | Static Roles |
| ----------------------- | ------------------------ | ------------- | ------------ |
| `mssql-database-plugin` | Yes | Yes | No |
| `mssql-database-plugin` | Yes | Yes | Yes |

## Setup

Expand Down