mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-05-11 21:46:40 +00:00

We removed all token validation from cloudflared and now rely on the edge to do the validation. This is better because the edge is the only thing that fully knows about token revocation. So if a user logs out or the application revokes all it's tokens cloudflared will now handle that process instead of barfing on it. When we go to fetch a token we will check for the existence of a lock file. If the lock file exists, we stop and poll every half second to see if the lock is still there. Once the lock file is removed, it will restart the function to (hopefully) go pick up the valid token that was just created.
140 lines
3.4 KiB
Go
140 lines
3.4 KiB
Go
package token
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/url"
|
|
"os"
|
|
|
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
|
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/path"
|
|
"github.com/cloudflare/cloudflared/cmd/cloudflared/transfer"
|
|
"github.com/cloudflare/cloudflared/log"
|
|
"github.com/cloudflare/cloudflared/origin"
|
|
"github.com/coreos/go-oidc/jose"
|
|
)
|
|
|
|
const (
|
|
keyName = "token"
|
|
)
|
|
|
|
var logger = log.CreateLogger()
|
|
|
|
type lock struct {
|
|
lockFilePath string
|
|
backoff *origin.BackoffHandler
|
|
}
|
|
|
|
func errDeleteTokenFailed(lockFilePath string) error {
|
|
return fmt.Errorf("failed to acquire a new Access token. Please try to delete %s", lockFilePath)
|
|
}
|
|
|
|
// newLock will get a new file lock
|
|
func newLock(path string) *lock {
|
|
lockPath := path + ".lock"
|
|
return &lock{
|
|
lockFilePath: lockPath,
|
|
backoff: &origin.BackoffHandler{MaxRetries: 7},
|
|
}
|
|
}
|
|
|
|
func (l *lock) Acquire() error {
|
|
// Check for a path.lock file
|
|
// if the lock file exists; start polling
|
|
// if not, create the lock file and go through the normal flow.
|
|
// See AUTH-1736 for the reason why we do all this
|
|
for isTokenLocked(l.lockFilePath) {
|
|
if l.backoff.Backoff(context.Background()) {
|
|
continue
|
|
} else {
|
|
return errDeleteTokenFailed(l.lockFilePath)
|
|
}
|
|
}
|
|
|
|
// Create a lock file so other processes won't also try to get the token at
|
|
// the same time
|
|
if err := ioutil.WriteFile(l.lockFilePath, []byte{}, 0600); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *lock) Release() error {
|
|
if err := os.Remove(l.lockFilePath); err != nil && !os.IsNotExist(err) {
|
|
return errDeleteTokenFailed(l.lockFilePath)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// isTokenLocked checks to see if there is another process attempting to get the token already
|
|
func isTokenLocked(lockFilePath string) bool {
|
|
exists, err := config.FileExists(lockFilePath)
|
|
return exists && err == nil
|
|
}
|
|
|
|
// FetchToken will either load a stored token or generate a new one
|
|
func FetchToken(appURL *url.URL) (string, error) {
|
|
if token, err := GetTokenIfExists(appURL); token != "" && err == nil {
|
|
return token, nil
|
|
}
|
|
|
|
path, err := path.GenerateFilePathFromURL(appURL, keyName)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
lock := newLock(path)
|
|
err = lock.Acquire()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer lock.Release()
|
|
|
|
// check to see if another process has gotten a token while we waited for the lock
|
|
if token, err := GetTokenIfExists(appURL); token != "" && err == nil {
|
|
return token, nil
|
|
}
|
|
|
|
// this weird parameter is the resource name (token) and the key/value
|
|
// we want to send to the transfer service. the key is token and the value
|
|
// is blank (basically just the id generated in the transfer service)
|
|
token, err := transfer.Run(appURL, keyName, keyName, "", path, true)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(token), nil
|
|
}
|
|
|
|
// GetTokenIfExists will return the token from local storage if it exists
|
|
func GetTokenIfExists(url *url.URL) (string, error) {
|
|
path, err := path.GenerateFilePathFromURL(url, keyName)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
content, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
token, err := jose.ParseJWT(string(content))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return token.Encode(), nil
|
|
}
|
|
|
|
// RemoveTokenIfExists removes the a token from local storage if it exists
|
|
func RemoveTokenIfExists(url *url.URL) error {
|
|
path, err := path.GenerateFilePathFromURL(url, keyName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|