Skip to content
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
6 changes: 4 additions & 2 deletions cmd/secrets/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/spf13/cobra"
)

const daemonRestartTimeout = 30 * time.Second

var daemonCmd = &cobra.Command{
Use: "daemon",
Short: "Manage the supervised agent-secrets daemon",
Expand Down Expand Up @@ -44,7 +46,7 @@ var daemonRestartCmd = &cobra.Command{
return output.PrintFail(output.Error(commandName, fmt.Errorf("daemon did not accept restart")))
}

deadline := time.Now().Add(15 * time.Second)
deadline := time.Now().Add(daemonRestartTimeout)
for time.Now().Before(deadline) {
time.Sleep(150 * time.Millisecond)
after, statusErr := daemonStatus()
Expand All @@ -63,7 +65,7 @@ var daemonRestartCmd = &cobra.Command{

return output.PrintFail(output.ErrorWithFix(
commandName,
fmt.Errorf("restart was accepted but no fresh daemon became healthy within 15s"),
fmt.Errorf("restart was accepted but no fresh daemon became healthy within %s", daemonRestartTimeout),
"Use the host's break-glass service restart",
))
},
Expand Down
27 changes: 14 additions & 13 deletions internal/lease/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Manager struct {
leases map[string]*types.Lease
cfg *config.Config
auditLogger *audit.Logger
logAudit func(*types.AuditEntry) error
otelEmitter *otel.Emitter

// Cleanup loop control
Expand All @@ -33,6 +34,7 @@ func NewManager(cfg *config.Config, auditLogger *audit.Logger, otelEmitter ...*o
leases: make(map[string]*types.Lease),
cfg: cfg,
auditLogger: auditLogger,
logAudit: auditLogger.Log,
cleanupDone: make(chan struct{}),
cleanupStop: make(chan struct{}),
}
Expand Down Expand Up @@ -230,25 +232,24 @@ func (m *Manager) List() []*types.Lease {
// CleanupExpired removes expired leases and logs expirations.
func (m *Manager) CleanupExpired() {
m.mu.Lock()
var expired []string
expired := make([]*types.Lease, 0)
for id, lease := range m.leases {
if !lease.Revoked && IsExpired(lease) {
expired = append(expired, id)

entry := audit.NewEntry(types.ActionLeaseExpire, true).
WithSecret(lease.SecretName).
WithClient(lease.ClientID).
WithLease(id).
Build()
_ = m.auditLogger.Log(entry)
leaseCopy := *lease
expired = append(expired, &leaseCopy)
delete(m.leases, id)
}
}
m.mu.Unlock()

// Remove expired leases
for _, id := range expired {
delete(m.leases, id)
for _, lease := range expired {
entry := audit.NewEntry(types.ActionLeaseExpire, true).
WithSecret(lease.SecretName).
WithClient(lease.ClientID).
WithLease(lease.ID).
Build()
_ = m.logAudit(entry)
}
m.mu.Unlock()

if len(expired) > 0 {
_ = m.Save()
Expand Down
51 changes: 51 additions & 0 deletions internal/lease/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,57 @@ func TestCleanupExpired(t *testing.T) {
}
}

func TestCleanupExpiredDoesNotHoldLeaseLockDuringAudit(t *testing.T) {
mgr, _ := setupTestManager(t)

_, err := mgr.Acquire("test-secret", "client-1", 1*time.Millisecond)
if err != nil {
t.Fatalf("Acquire() failed: %v", err)
}
time.Sleep(10 * time.Millisecond)

logStarted := make(chan struct{})
releaseLog := make(chan struct{})
mgr.logAudit = func(*types.AuditEntry) error {
close(logStarted)
<-releaseLog
return nil
}

cleanupDone := make(chan struct{})
go func() {
mgr.CleanupExpired()
close(cleanupDone)
}()

select {
case <-logStarted:
case <-time.After(time.Second):
t.Fatal("cleanup did not reach audit logging")
}

listDone := make(chan []*types.Lease, 1)
go func() {
listDone <- mgr.List()
}()

select {
case active := <-listDone:
if len(active) != 0 {
t.Fatalf("List() returned %d leases during audit, want 0", len(active))
}
case <-time.After(100 * time.Millisecond):
t.Fatal("List() blocked behind expiration audit logging")
}

close(releaseLog)
select {
case <-cleanupDone:
case <-time.After(time.Second):
t.Fatal("cleanup did not finish after audit logging resumed")
}
}

func TestSaveLoad(t *testing.T) {
mgr, tmpDir := setupTestManager(t)

Expand Down
Loading