-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
When defaulting to local JWT token and CA certificate in a pod, always read them from local filesystem and do not store them persistently with the config. Token will be re-read periodically to avoid using expired token. The change allows running Vault on Kubernetes 1.21 and newer, which switched to ID token that is bound to the pod and will expire. Signed-off-by: Tero Saarni <[email protected]> * review comment fix: load only token or ca cert if other is given in config * changed the reload period to 1 minute * fixed review comments * take lock also on alias lookahead path * more review fixes * proposal to fix the read/write lock issue * fixed typo * cachedFile by value to avoid mutation while not holding log * acquire lock in same place as in pathLogin * added debug log entry when local token is not found and falling back to client token Co-authored-by: Tero Saarni <[email protected]>
- Loading branch information
Showing
6 changed files
with
320 additions
and
48 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package kubeauth | ||
|
||
import ( | ||
"io/ioutil" | ||
"sync" | ||
"time" | ||
) | ||
|
||
// cachingFileReader reads a file and keeps an in-memory copy of it, until the | ||
// copy is considered stale. Next ReadFile() after expiry will re-read the file from disk. | ||
type cachingFileReader struct { | ||
// path is the file path to the cached file. | ||
path string | ||
|
||
// ttl is the time-to-live duration when cached file is considered stale | ||
ttl time.Duration | ||
|
||
// cache is the buffer holding the in-memory copy of the file. | ||
cache cachedFile | ||
|
||
l sync.RWMutex | ||
|
||
// currentTime is a function that returns the current local time. | ||
// Normally set to time.Now but it can be overwritten by test cases to manipulate time. | ||
currentTime func() time.Time | ||
} | ||
|
||
type cachedFile struct { | ||
// buf is the buffer holding the in-memory copy of the file. | ||
buf string | ||
|
||
// expiry is the time when the cached copy is considered stale and must be re-read. | ||
expiry time.Time | ||
} | ||
|
||
func newCachingFileReader(path string, ttl time.Duration, currentTime func() time.Time) *cachingFileReader { | ||
return &cachingFileReader{ | ||
path: path, | ||
ttl: ttl, | ||
currentTime: currentTime, | ||
} | ||
} | ||
|
||
func (r *cachingFileReader) ReadFile() (string, error) { | ||
// Fast path requiring read lock only: file is already in memory and not stale. | ||
r.l.RLock() | ||
now := r.currentTime() | ||
cache := r.cache | ||
r.l.RUnlock() | ||
if now.Before(cache.expiry) { | ||
return cache.buf, nil | ||
} | ||
|
||
// Slow path: read the file from disk. | ||
r.l.Lock() | ||
defer r.l.Unlock() | ||
|
||
buf, err := ioutil.ReadFile(r.path) | ||
if err != nil { | ||
return "", err | ||
} | ||
r.cache = cachedFile{ | ||
buf: string(buf), | ||
expiry: now.Add(r.ttl), | ||
} | ||
|
||
return r.cache.buf, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package kubeauth | ||
|
||
import ( | ||
"io/ioutil" | ||
"os" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestCachingFileReader(t *testing.T) { | ||
content1 := "before" | ||
content2 := "after" | ||
|
||
// Create temporary file. | ||
f, err := ioutil.TempFile("", "testfile") | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
f.Close() | ||
defer os.Remove(f.Name()) | ||
|
||
currentTime := time.Now() | ||
|
||
r := newCachingFileReader(f.Name(), 1*time.Minute, | ||
func() time.Time { | ||
return currentTime | ||
}) | ||
|
||
// Write initial content to file and check that we can read it. | ||
ioutil.WriteFile(f.Name(), []byte(content1), 0644) | ||
got, err := r.ReadFile() | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
if got != content1 { | ||
t.Errorf("got '%s', expected '%s'", got, content1) | ||
} | ||
|
||
// Write new content to the file. | ||
ioutil.WriteFile(f.Name(), []byte(content2), 0644) | ||
|
||
// Advance simulated time, but not enough for cache to expire. | ||
currentTime = currentTime.Add(30 * time.Second) | ||
|
||
// Read again and check we still got the old cached content. | ||
got, err = r.ReadFile() | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
if got != content1 { | ||
t.Errorf("got '%s', expected '%s'", got, content1) | ||
} | ||
|
||
// Advance simulated time for cache to expire. | ||
currentTime = currentTime.Add(30 * time.Second) | ||
|
||
// Read again and check that we got the new content. | ||
got, err = r.ReadFile() | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
if got != content2 { | ||
t.Errorf("got '%s', expected '%s'", got, content2) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.