summaryrefslogtreecommitdiff
path: root/path_rotate.go
diff options
context:
space:
mode:
Diffstat (limited to 'path_rotate.go')
-rw-r--r--path_rotate.go172
1 files changed, 172 insertions, 0 deletions
diff --git a/path_rotate.go b/path_rotate.go
new file mode 100644
index 0000000..d06c7ac
--- /dev/null
+++ b/path_rotate.go
@@ -0,0 +1,172 @@
+package secretsengine
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ "git.tardisproject.uk/tcmal/vault-plugin-kerberos-secrets/config"
+ "git.tardisproject.uk/tcmal/vault-plugin-kerberos-secrets/password"
+ "github.com/hashicorp/vault/sdk/framework"
+ "github.com/hashicorp/vault/sdk/logical"
+)
+
+const (
+ rotateRootPath = "rotate-root"
+ rotateRolePath = "rotate-static-role/"
+)
+
+func pathRotateCredentials(b *krbBackend) []*framework.Path {
+ return []*framework.Path{
+ {
+ Pattern: rotateRootPath,
+ Fields: map[string]*framework.FieldSchema{},
+ Operations: map[logical.Operation]framework.OperationHandler{
+ logical.UpdateOperation: &framework.PathOperation{
+ Callback: b.pathRotateRootCredentialsUpdate,
+ ForwardPerformanceStandby: true,
+ ForwardPerformanceSecondary: true,
+ },
+ },
+ HelpSynopsis: "Request to rotate the root credentials Vault uses for the kerberos administrator account.",
+ HelpDescription: "This path attempts to rotate the root credentials of the administrator account " +
+ "used by Vault to manage credentials.",
+ },
+ {
+ Pattern: rotateRolePath + framework.GenericNameRegex("name"),
+ Fields: map[string]*framework.FieldSchema{
+ "name": {
+ Type: framework.TypeString,
+ Description: "Name of the static role",
+ },
+ },
+ Operations: map[logical.Operation]framework.OperationHandler{
+ logical.UpdateOperation: &framework.PathOperation{
+ Callback: b.pathRotateRoleCredentialsUpdate,
+ ForwardPerformanceStandby: true,
+ ForwardPerformanceSecondary: true,
+ },
+ },
+ HelpSynopsis: "Request to rotate the credentials for a static user account.",
+ HelpDescription: "This path attempts to rotate the credentials for the given static role.",
+ },
+ }
+}
+
+func (b *krbBackend) pathRotateRootCredentialsUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
+ if _, hasTimeout := ctx.Deadline(); !hasTimeout {
+ var cancel func()
+ ctx, cancel = context.WithTimeout(ctx, defaultCtxTimeout)
+ defer cancel()
+ }
+
+ config, err := getConfig(ctx, req.Storage)
+ if err != nil {
+ return nil, err
+ }
+ if config == nil {
+ return nil, errors.New("the config is currently unset")
+ }
+
+ client, err := b.getClient(ctx, req.Storage)
+ if err != nil {
+ return nil, err
+ }
+
+ newPassword := password.Generate()
+
+ // Update the password remotely.
+ if err := (*client).SetPassword(config.Username, newPassword); err != nil {
+ return nil, err
+ }
+
+ // Update the password locally.
+ config.Password = newPassword
+ if pwdStoringErr := storePassword(ctx, req.Storage, config); pwdStoringErr != nil {
+ return nil, fmt.Errorf("unable to update password due to storage err: %s", pwdStoringErr)
+ // TODO: deal with this more gracefully
+ }
+
+ b.reset()
+
+ // Respond with a 204.
+ return nil, nil
+}
+
+func (b *krbBackend) pathRotateRoleCredentialsUpdate(ctx context.Context, req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
+ name := data.Get("name").(string)
+ if name == "" {
+ return logical.ErrorResponse("empty role name attribute given"), nil
+ }
+
+ ictx, _ := context.WithTimeout(context.Background(), time.Minute*60)
+ go b.doRotation(ictx, name, req.Storage)
+ return nil, nil
+}
+
+func (b *krbBackend) doRotation(ctx context.Context, name string, storage logical.Storage) {
+ log := b.Logger().With("role", name)
+ log.Debug("starting to rotate role")
+
+ for {
+ err, retry := b.attemptRotation(ctx, name, storage) // TODO
+ if err == nil {
+ log.Debug("credentials rotated")
+ return
+ }
+
+ log.Error("error rotating credentials", "error", err)
+ if !retry {
+ log.Error("unrecoverable error rotating credentials")
+ }
+
+ timer := time.NewTimer(30 * time.Second)
+ select {
+ case <-timer.C:
+ continue
+ case <-ctx.Done():
+ log.Error("timeout rotating credentials")
+ return
+ }
+ }
+}
+
+func (b *krbBackend) attemptRotation(ctx context.Context, name string, storage logical.Storage) (error, bool) {
+ role, err := b.getRole(ctx, storage, name)
+ if err != nil {
+ return fmt.Errorf("error fetching role from storage: %e", err), true
+ }
+ if role == nil {
+ return fmt.Errorf("role does not exist: %s", name), false
+ }
+
+ c, err := b.getClient(ctx, storage)
+ if err != nil {
+ return fmt.Errorf("error getting client: %e", err), true
+ }
+
+ newPassword := password.Generate()
+ err = (*c).SetPassword(role.Principal, newPassword)
+ if err != nil {
+ return fmt.Errorf("error setting password: %e", err), true
+ }
+
+ role.Password = newPassword
+ role.LastVaultRotation = time.Now()
+ err = setRole(ctx, storage, name, role)
+ if err != nil {
+ return fmt.Errorf("rotated password but could not save back to storage"), true
+ // TODO: deal with this more gracefully
+ }
+
+ return nil, false
+}
+
+func storePassword(ctx context.Context, s logical.Storage, cfg *config.Config) error {
+ entry, err := logical.StorageEntryJSON(configStoragePath, cfg)
+ if err != nil {
+ return err
+ }
+ return s.Put(ctx, entry)
+}