68 lines
1.6 KiB
Go
68 lines
1.6 KiB
Go
|
|
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()
|
||
|
|
}
|