- Added internal/vault/client.go for creating a Vault client with configuration settings. - Introduced internal/vault/errors.go to classify Vault API errors for better UI handling. - Created internal/vault/kv.go to manage KV secrets, including listing, reading, writing, and deleting operations. - Implemented internal/vault/mounts.go to list and describe secret engine mounts. - Developed internal/vault/service.go to provide a unified entry point for Vault operations. - Added internal/vault/kv_test.go for comprehensive testing of KV operations. - Introduced internal/ui/toast.go for transient notifications in the UI. - Added renovate.json for dependency management and updates.
111 lines
3.1 KiB
Go
111 lines
3.1 KiB
Go
package token
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/hashicorp/vault/api"
|
|
)
|
|
|
|
// RenewEvent is emitted by AutoRenewer as the watched token's lifecycle
|
|
// changes. Err set + Terminal true means the watcher has given up and the
|
|
// token will eventually expire without further notice.
|
|
type RenewEvent struct {
|
|
At time.Time
|
|
TTL time.Duration
|
|
Err error
|
|
Terminal bool
|
|
}
|
|
|
|
// AutoRenewer wraps api.LifetimeWatcher with the two special cases that
|
|
// matter for a token we did not necessarily just mint ourselves: batch
|
|
// tokens are not renewable, and root tokens have TTL == 0 (never expire).
|
|
// NewAutoRenewer returns (nil, nil) for either case — callers should treat
|
|
// a nil renewer as "nothing to do", not an error.
|
|
type AutoRenewer struct {
|
|
client *api.Client
|
|
w *api.LifetimeWatcher
|
|
events chan RenewEvent
|
|
}
|
|
|
|
// NewAutoRenewer builds a renewer from a *Info (e.g. from Lookup) rather
|
|
// than requiring the original login *api.Secret, since a token resolved
|
|
// from ~/.vault-token has no such Secret — this synthesises an equivalent
|
|
// one from the lookup data.
|
|
func NewAutoRenewer(c *api.Client, info *Info, increment time.Duration) (*AutoRenewer, error) {
|
|
if !info.Renewable || info.ExpireTime == nil {
|
|
return nil, nil
|
|
}
|
|
sec := &api.Secret{
|
|
Auth: &api.SecretAuth{
|
|
ClientToken: c.Token(),
|
|
LeaseDuration: int(info.TTL.Seconds()),
|
|
Renewable: info.Renewable,
|
|
},
|
|
}
|
|
w, err := c.NewLifetimeWatcher(&api.LifetimeWatcherInput{
|
|
Secret: sec,
|
|
Increment: int(increment.Seconds()),
|
|
RenewBuffer: api.DefaultLifetimeWatcherRenewBuffer,
|
|
RenewBehavior: api.RenewBehaviorIgnoreErrors,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating lifetime watcher: %w", err)
|
|
}
|
|
return &AutoRenewer{client: c, w: w, events: make(chan RenewEvent, 8)}, nil
|
|
}
|
|
|
|
// Start runs the watcher until ctx is cancelled or Stop is called.
|
|
func (a *AutoRenewer) Start(ctx context.Context) {
|
|
go a.w.Start()
|
|
go func() {
|
|
defer close(a.events)
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
a.w.Stop()
|
|
return
|
|
case out, ok := <-a.w.RenewCh():
|
|
if !ok {
|
|
return
|
|
}
|
|
a.emit(RenewEvent{At: time.Now(), TTL: leaseDuration(out)})
|
|
case err, ok := <-a.w.DoneCh():
|
|
if !ok {
|
|
return
|
|
}
|
|
a.emit(RenewEvent{At: time.Now(), Err: err, Terminal: true})
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (a *AutoRenewer) emit(e RenewEvent) {
|
|
select {
|
|
case a.events <- e:
|
|
default:
|
|
}
|
|
}
|
|
|
|
// Events returns the channel of renewal notifications; closed when the
|
|
// watcher stops.
|
|
func (a *AutoRenewer) Events() <-chan RenewEvent { return a.events }
|
|
|
|
// Stop releases the underlying watcher.
|
|
func (a *AutoRenewer) Stop() { a.w.Stop() }
|
|
|
|
// leaseDuration reads the renewed TTL out of a RenewOutput. Token renewals
|
|
// (our only use case) carry it on Secret.Auth, not the top-level
|
|
// Secret.LeaseDuration field that non-auth lease renewals use.
|
|
func leaseDuration(out *api.RenewOutput) time.Duration {
|
|
if out == nil || out.Secret == nil {
|
|
return 0
|
|
}
|
|
if out.Secret.Auth != nil {
|
|
return time.Duration(out.Secret.Auth.LeaseDuration) * time.Second
|
|
}
|
|
return time.Duration(out.Secret.LeaseDuration) * time.Second
|
|
}
|