diff options
author | Aria <me@aria.rip> | 2023-09-25 00:12:03 +0100 |
---|---|---|
committer | Aria <me@aria.rip> | 2023-09-25 00:12:03 +0100 |
commit | 0bd62b1d8b13ad1d38f61a6388c1f2e292b191a5 (patch) | |
tree | 68cf96bbcd113061daab8adabdfc8cb4fccde27f /path_rotate.go |
fockin BOOOILLEEERPLAAATEEE
Diffstat (limited to 'path_rotate.go')
-rw-r--r-- | path_rotate.go | 172 |
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) +} |