ProofedMesh/services/dummy-sink/internal/auth/sa_token.go

68 lines
1.6 KiB
Go
Raw Normal View History

package auth
import (
"context"
"fmt"
"os"
"strings"
"sync"
"time"
)
// ProjectedSATokenSource reads the kubelet-written projected
// ServiceAccount token from a mounted Volume (typically
// `/var/run/secrets/tokens/trust-api`) and re-reads it on each call so
// kubelet's periodic refresh is picked up without a service restart.
//
// A short in-process cache avoids a syscall on every signing-service call;
// kubelet refreshes projected tokens well before their `exp`, so a few
// seconds of staleness is fine. Set CacheTTL negative to disable.
type ProjectedSATokenSource struct {
Path string
CacheTTL time.Duration
Now func() time.Time
mu sync.Mutex
cachedToken string
cachedAt time.Time
}
// Token returns the current projected ServiceAccount token.
func (p *ProjectedSATokenSource) Token(_ context.Context) (string, error) {
if p == nil {
return "", fmt.Errorf("auth.ProjectedSATokenSource: nil receiver")
}
if p.Path == "" {
return "", fmt.Errorf("auth.ProjectedSATokenSource: Path is required")
}
p.mu.Lock()
defer p.mu.Unlock()
ttl := p.CacheTTL
if ttl == 0 {
ttl = 30 * time.Second
}
now := p.now()
if ttl > 0 && p.cachedToken != "" && now.Sub(p.cachedAt) < ttl {
return p.cachedToken, nil
}
raw, err := os.ReadFile(p.Path)
if err != nil {
return "", fmt.Errorf("read projected SA token %q: %w", p.Path, err)
}
tok := strings.TrimSpace(string(raw))
if tok == "" {
return "", fmt.Errorf("projected SA token at %q is empty", p.Path)
}
p.cachedToken = tok
p.cachedAt = now
return tok, nil
}
func (p *ProjectedSATokenSource) now() time.Time {
if p.Now != nil {
return p.Now()
}
return time.Now()
}