open-vault/builtin/logical/database/secret_creds.go

168 lines
4.8 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
2016-12-19 18:15:58 +00:00
package database
import (
"context"
2016-12-19 18:15:58 +00:00
"fmt"
"time"
2016-12-19 18:15:58 +00:00
v4 "github.com/hashicorp/vault/sdk/database/dbplugin"
v5 "github.com/hashicorp/vault/sdk/database/dbplugin/v5"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
2016-12-19 18:15:58 +00:00
)
const SecretCredsType = "creds"
func secretCreds(b *databaseBackend) *framework.Secret {
return &framework.Secret{
2017-03-08 22:46:53 +00:00
Type: SecretCredsType,
Fields: map[string]*framework.FieldSchema{},
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
Renew: b.secretCredsRenew(),
Revoke: b.secretCredsRevoke(),
2016-12-19 18:15:58 +00:00
}
}
2017-04-11 18:50:34 +00:00
func (b *databaseBackend) secretCredsRenew() framework.OperationFunc {
return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2017-04-11 18:50:34 +00:00
// Get the username from the internal data
usernameRaw, ok := req.Secret.InternalData["username"]
if !ok {
return nil, fmt.Errorf("secret is missing username internal data")
}
username, ok := usernameRaw.(string)
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
roleNameRaw, ok := req.Secret.InternalData["role"]
if !ok {
return nil, fmt.Errorf("could not find role with name: %q", req.Secret.InternalData["role"])
2017-04-11 18:50:34 +00:00
}
2016-12-19 18:15:58 +00:00
role, err := b.Role(ctx, req.Storage, roleNameRaw.(string))
2017-04-11 18:50:34 +00:00
if err != nil {
return nil, err
}
if role == nil {
return nil, fmt.Errorf("error during renew: could not find role with name %q", req.Secret.InternalData["role"])
2017-04-11 18:50:34 +00:00
}
2016-12-19 18:15:58 +00:00
2017-04-26 22:23:14 +00:00
// Get the Database object
dbi, err := b.GetConnection(ctx, req.Storage, role.DBName)
Database Root Credential Rotation (#3976) * redoing connection handling * a little more cleanup * empty implementation of rotation * updating rotate signature * signature update * updating interfaces again :( * changing back to interface * adding templated url support and rotation for postgres * adding correct username * return updates * updating statements to be a list * adding error sanitizing middleware * fixing log sanitizier * adding postgres rotate test * removing conf from rotate * adding rotate command * adding mysql rotate * finishing up the endpoint in the db backend for rotate * no more structs, just store raw config * fixing tests * adding db instance lock * adding support for statement list in cassandra * wip redoing interface to support BC * adding falllback for Initialize implementation * adding backwards compat for statements * fix tests * fix more tests * fixing up tests, switching to new fields in statements * fixing more tests * adding mssql and mysql * wrapping all the things in middleware, implementing templating for mongodb * wrapping all db servers with error santizer * fixing test * store the name with the db instance * adding rotate to cassandra * adding compatibility translation to both server and plugin * reordering a few things * store the name with the db instance * reordering * adding a few more tests * switch secret values from slice to map * addressing some feedback * reinstate execute plugin after resetting connection * set database connection to closed * switching secret values func to map[string]interface for potential future uses * addressing feedback
2018-03-21 19:05:56 +00:00
if err != nil {
return nil, err
2016-12-19 18:15:58 +00:00
}
dbi.RLock()
defer dbi.RUnlock()
Database Root Credential Rotation (#3976) * redoing connection handling * a little more cleanup * empty implementation of rotation * updating rotate signature * signature update * updating interfaces again :( * changing back to interface * adding templated url support and rotation for postgres * adding correct username * return updates * updating statements to be a list * adding error sanitizing middleware * fixing log sanitizier * adding postgres rotate test * removing conf from rotate * adding rotate command * adding mysql rotate * finishing up the endpoint in the db backend for rotate * no more structs, just store raw config * fixing tests * adding db instance lock * adding support for statement list in cassandra * wip redoing interface to support BC * adding falllback for Initialize implementation * adding backwards compat for statements * fix tests * fix more tests * fixing up tests, switching to new fields in statements * fixing more tests * adding mssql and mysql * wrapping all the things in middleware, implementing templating for mongodb * wrapping all db servers with error santizer * fixing test * store the name with the db instance * adding rotate to cassandra * adding compatibility translation to both server and plugin * reordering a few things * store the name with the db instance * reordering * adding a few more tests * switch secret values from slice to map * addressing some feedback * reinstate execute plugin after resetting connection * set database connection to closed * switching secret values func to map[string]interface for potential future uses * addressing feedback
2018-03-21 19:05:56 +00:00
2017-04-11 18:50:34 +00:00
// Make sure we increase the VALID UNTIL endpoint for this user.
ttl, _, err := framework.CalculateTTL(b.System(), req.Secret.Increment, role.DefaultTTL, 0, role.MaxTTL, 0, req.Secret.IssueTime)
if err != nil {
return nil, err
}
if ttl > 0 {
expireTime := time.Now().Add(ttl)
// Adding a small buffer since the TTL will be calculated again after this call
// to ensure the database credential does not expire before the lease
expireTime = expireTime.Add(5 * time.Second)
updateReq := v5.UpdateUserRequest{
Username: username,
Expiration: &v5.ChangeExpiration{
NewExpiration: expireTime,
Statements: v5.Statements{
Commands: role.Statements.Renewal,
},
},
}
_, err := dbi.database.UpdateUser(ctx, updateReq, false)
2017-04-11 18:50:34 +00:00
if err != nil {
b.CloseIfShutdown(dbi, err)
2017-04-11 18:50:34 +00:00
return nil, err
}
}
resp := &logical.Response{Secret: req.Secret}
resp.Secret.TTL = role.DefaultTTL
resp.Secret.MaxTTL = role.MaxTTL
2017-04-11 18:50:34 +00:00
return resp, nil
2016-12-19 18:15:58 +00:00
}
2017-04-11 18:50:34 +00:00
}
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
func (b *databaseBackend) secretCredsRevoke() framework.OperationFunc {
return func(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
2017-04-11 18:50:34 +00:00
// Get the username from the internal data
usernameRaw, ok := req.Secret.InternalData["username"]
if !ok {
return nil, fmt.Errorf("secret is missing username internal data")
}
username, ok := usernameRaw.(string)
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
var resp *logical.Response
2016-12-19 18:15:58 +00:00
2017-04-11 18:50:34 +00:00
roleNameRaw, ok := req.Secret.InternalData["role"]
if !ok {
return nil, fmt.Errorf("no role name was provided")
}
2016-12-19 18:15:58 +00:00
var dbName string
var statements v4.Statements
role, err := b.Role(ctx, req.Storage, roleNameRaw.(string))
2016-12-19 18:15:58 +00:00
if err != nil {
return nil, err
}
if role != nil {
dbName = role.DBName
statements = role.Statements
} else {
dbNameRaw, ok := req.Secret.InternalData["db_name"]
if !ok {
return nil, fmt.Errorf("error during revoke: could not find role with name %q or embedded revocation db name data", req.Secret.InternalData["role"])
}
dbName = dbNameRaw.(string)
statementsRaw, ok := req.Secret.InternalData["revocation_statements"]
if !ok {
return nil, fmt.Errorf("error during revoke: could not find role with name %q or embedded revocation statement data", req.Secret.InternalData["role"])
}
// If we don't actually have any statements, because none were
// set in the role, we'll end up with an empty one and the
// default for the db type will be attempted
if statementsRaw != nil {
statementsSlice, ok := statementsRaw.([]interface{})
if !ok {
return nil, fmt.Errorf("error during revoke: could not find role with name %q and embedded reovcation data could not be read", req.Secret.InternalData["role"])
}
for _, v := range statementsSlice {
statements.Revocation = append(statements.Revocation, v.(string))
}
}
2016-12-19 18:15:58 +00:00
}
2017-04-11 18:50:34 +00:00
// Get our connection
dbi, err := b.GetConnection(ctx, req.Storage, dbName)
Database Root Credential Rotation (#3976) * redoing connection handling * a little more cleanup * empty implementation of rotation * updating rotate signature * signature update * updating interfaces again :( * changing back to interface * adding templated url support and rotation for postgres * adding correct username * return updates * updating statements to be a list * adding error sanitizing middleware * fixing log sanitizier * adding postgres rotate test * removing conf from rotate * adding rotate command * adding mysql rotate * finishing up the endpoint in the db backend for rotate * no more structs, just store raw config * fixing tests * adding db instance lock * adding support for statement list in cassandra * wip redoing interface to support BC * adding falllback for Initialize implementation * adding backwards compat for statements * fix tests * fix more tests * fixing up tests, switching to new fields in statements * fixing more tests * adding mssql and mysql * wrapping all the things in middleware, implementing templating for mongodb * wrapping all db servers with error santizer * fixing test * store the name with the db instance * adding rotate to cassandra * adding compatibility translation to both server and plugin * reordering a few things * store the name with the db instance * reordering * adding a few more tests * switch secret values from slice to map * addressing some feedback * reinstate execute plugin after resetting connection * set database connection to closed * switching secret values func to map[string]interface for potential future uses * addressing feedback
2018-03-21 19:05:56 +00:00
if err != nil {
return nil, err
2017-04-11 18:50:34 +00:00
}
2016-12-19 18:15:58 +00:00
dbi.RLock()
defer dbi.RUnlock()
Database Root Credential Rotation (#3976) * redoing connection handling * a little more cleanup * empty implementation of rotation * updating rotate signature * signature update * updating interfaces again :( * changing back to interface * adding templated url support and rotation for postgres * adding correct username * return updates * updating statements to be a list * adding error sanitizing middleware * fixing log sanitizier * adding postgres rotate test * removing conf from rotate * adding rotate command * adding mysql rotate * finishing up the endpoint in the db backend for rotate * no more structs, just store raw config * fixing tests * adding db instance lock * adding support for statement list in cassandra * wip redoing interface to support BC * adding falllback for Initialize implementation * adding backwards compat for statements * fix tests * fix more tests * fixing up tests, switching to new fields in statements * fixing more tests * adding mssql and mysql * wrapping all the things in middleware, implementing templating for mongodb * wrapping all db servers with error santizer * fixing test * store the name with the db instance * adding rotate to cassandra * adding compatibility translation to both server and plugin * reordering a few things * store the name with the db instance * reordering * adding a few more tests * switch secret values from slice to map * addressing some feedback * reinstate execute plugin after resetting connection * set database connection to closed * switching secret values func to map[string]interface for potential future uses * addressing feedback
2018-03-21 19:05:56 +00:00
deleteReq := v5.DeleteUserRequest{
Username: username,
Statements: v5.Statements{
Commands: statements.Revocation,
},
}
_, err = dbi.database.DeleteUser(ctx, deleteReq)
if err != nil {
b.CloseIfShutdown(dbi, err)
2017-04-11 18:50:34 +00:00
return nil, err
}
return resp, nil
}
2016-12-19 18:15:58 +00:00
}