mirror of https://github.com/dexidp/dex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
2.9 KiB
121 lines
2.9 KiB
package kubernetes |
|
|
|
import ( |
|
"net/http" |
|
"os" |
|
"sync" |
|
"time" |
|
|
|
"github.com/dexidp/dex/storage/kubernetes/k8sapi" |
|
) |
|
|
|
// transport is a simple http.Transport wrapper |
|
type transport struct { |
|
updateReq func(r *http.Request) |
|
base http.RoundTripper |
|
} |
|
|
|
func (t transport) RoundTrip(r *http.Request) (*http.Response, error) { |
|
// shallow copy of the struct |
|
r2 := new(http.Request) |
|
*r2 = *r |
|
// deep copy of the Header |
|
r2.Header = make(http.Header, len(r.Header)) |
|
for k, s := range r.Header { |
|
r2.Header[k] = append([]string(nil), s...) |
|
} |
|
t.updateReq(r2) |
|
return t.base.RoundTrip(r2) |
|
} |
|
|
|
func wrapRoundTripper(base http.RoundTripper, user k8sapi.AuthInfo, inCluster bool) http.RoundTripper { |
|
if inCluster { |
|
inClusterTransportHelper := newInClusterTransportHelper(user) |
|
return transport{ |
|
updateReq: func(r *http.Request) { |
|
inClusterTransportHelper.UpdateToken() |
|
r.Header.Set("Authorization", "Bearer "+inClusterTransportHelper.GetToken()) |
|
}, |
|
base: base, |
|
} |
|
} |
|
|
|
if user.Token != "" { |
|
return transport{ |
|
updateReq: func(r *http.Request) { |
|
r.Header.Set("Authorization", "Bearer "+user.Token) |
|
}, |
|
base: base, |
|
} |
|
} |
|
|
|
if user.Username != "" && user.Password != "" { |
|
return transport{ |
|
updateReq: func(r *http.Request) { |
|
r.SetBasicAuth(user.Username, user.Password) |
|
}, |
|
base: base, |
|
} |
|
} |
|
|
|
return base |
|
} |
|
|
|
// renewTokenPeriod is the interval after which dex will read the token from a well-known file. |
|
// By Kubernetes documentation, this interval should be at least one minute long. |
|
// Kubernetes client-go v0.15+ uses 10 seconds long interval. |
|
// Dex uses the reasonable value between these two. |
|
const renewTokenPeriod = 30 * time.Second |
|
|
|
// inClusterTransportHelper is capable of safely updating the user token. |
|
// BoundServiceAccountTokenVolume feature is enabled in Kubernetes >=1.21 by default. |
|
// With this feature, the service account token in the pod becomes periodically updated. |
|
// Therefore, Dex needs to re-read the token from the disk after some time to be sure that it uses the valid token. |
|
type inClusterTransportHelper struct { |
|
mu sync.RWMutex |
|
info k8sapi.AuthInfo |
|
|
|
expiry time.Time |
|
now func() time.Time |
|
|
|
tokenLocation string |
|
} |
|
|
|
func newInClusterTransportHelper(info k8sapi.AuthInfo) *inClusterTransportHelper { |
|
user := &inClusterTransportHelper{ |
|
info: info, |
|
now: time.Now, |
|
tokenLocation: "/var/run/secrets/kubernetes.io/serviceaccount/token", |
|
} |
|
|
|
user.UpdateToken() |
|
|
|
return user |
|
} |
|
|
|
func (c *inClusterTransportHelper) UpdateToken() { |
|
c.mu.RLock() |
|
exp := c.expiry |
|
c.mu.RUnlock() |
|
|
|
if !c.now().After(exp) { |
|
// Do not need to update token yet |
|
return |
|
} |
|
|
|
token, err := os.ReadFile(c.tokenLocation) |
|
if err != nil { |
|
return |
|
} |
|
|
|
c.mu.Lock() |
|
defer c.mu.Unlock() |
|
c.info.Token = string(token) |
|
c.expiry = c.now().Add(renewTokenPeriod) |
|
} |
|
|
|
func (c *inClusterTransportHelper) GetToken() string { |
|
c.mu.RLock() |
|
defer c.mu.RUnlock() |
|
return c.info.Token |
|
}
|
|
|