mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-25 11:31:44 +00:00
client-go: add the UID to the auth-proxy roundtripper
This commit is contained in:
parent
0409ba7ff1
commit
2cc0370169
@ -251,7 +251,7 @@ func (h *peerProxyHandler) proxyRequestToDestinationAPIServer(req *http.Request,
|
|||||||
newReq.Header.Add(PeerProxiedHeader, "true")
|
newReq.Header.Add(PeerProxiedHeader, "true")
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
|
|
||||||
proxyRoundTripper := transport.NewAuthProxyRoundTripper(user.GetName(), user.GetGroups(), user.GetExtra(), h.proxyTransport)
|
proxyRoundTripper := transport.NewAuthProxyRoundTripper(user.GetName(), user.GetUID(), user.GetGroups(), user.GetExtra(), h.proxyTransport)
|
||||||
|
|
||||||
delegate := &epmetrics.ResponseWriterDelegator{ResponseWriter: rw}
|
delegate := &epmetrics.ResponseWriterDelegator{ResponseWriter: rw}
|
||||||
w := responsewriter.WrapForHTTP1Or2(delegate)
|
w := responsewriter.WrapForHTTP1Or2(delegate)
|
||||||
|
@ -86,6 +86,7 @@ func DebugWrappers(rt http.RoundTripper) http.RoundTripper {
|
|||||||
|
|
||||||
type authProxyRoundTripper struct {
|
type authProxyRoundTripper struct {
|
||||||
username string
|
username string
|
||||||
|
uid string
|
||||||
groups []string
|
groups []string
|
||||||
extra map[string][]string
|
extra map[string][]string
|
||||||
|
|
||||||
@ -98,15 +99,17 @@ var _ utilnet.RoundTripperWrapper = &authProxyRoundTripper{}
|
|||||||
// authentication terminating proxy cases
|
// authentication terminating proxy cases
|
||||||
// assuming you pull the user from the context:
|
// assuming you pull the user from the context:
|
||||||
// username is the user.Info.GetName() of the user
|
// username is the user.Info.GetName() of the user
|
||||||
|
// uid is the user.Info.GetUID() of the user
|
||||||
// groups is the user.Info.GetGroups() of the user
|
// groups is the user.Info.GetGroups() of the user
|
||||||
// extra is the user.Info.GetExtra() of the user
|
// extra is the user.Info.GetExtra() of the user
|
||||||
// extra can contain any additional information that the authenticator
|
// extra can contain any additional information that the authenticator
|
||||||
// thought was interesting, for example authorization scopes.
|
// thought was interesting, for example authorization scopes.
|
||||||
// In order to faithfully round-trip through an impersonation flow, these keys
|
// In order to faithfully round-trip through an impersonation flow, these keys
|
||||||
// MUST be lowercase.
|
// MUST be lowercase.
|
||||||
func NewAuthProxyRoundTripper(username string, groups []string, extra map[string][]string, rt http.RoundTripper) http.RoundTripper {
|
func NewAuthProxyRoundTripper(username, uid string, groups []string, extra map[string][]string, rt http.RoundTripper) http.RoundTripper {
|
||||||
return &authProxyRoundTripper{
|
return &authProxyRoundTripper{
|
||||||
username: username,
|
username: username,
|
||||||
|
uid: uid,
|
||||||
groups: groups,
|
groups: groups,
|
||||||
extra: extra,
|
extra: extra,
|
||||||
rt: rt,
|
rt: rt,
|
||||||
@ -115,14 +118,15 @@ func NewAuthProxyRoundTripper(username string, groups []string, extra map[string
|
|||||||
|
|
||||||
func (rt *authProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
func (rt *authProxyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
req = utilnet.CloneRequest(req)
|
req = utilnet.CloneRequest(req)
|
||||||
SetAuthProxyHeaders(req, rt.username, rt.groups, rt.extra)
|
SetAuthProxyHeaders(req, rt.username, rt.uid, rt.groups, rt.extra)
|
||||||
|
|
||||||
return rt.rt.RoundTrip(req)
|
return rt.rt.RoundTrip(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetAuthProxyHeaders stomps the auth proxy header fields. It mutates its argument.
|
// SetAuthProxyHeaders stomps the auth proxy header fields. It mutates its argument.
|
||||||
func SetAuthProxyHeaders(req *http.Request, username string, groups []string, extra map[string][]string) {
|
func SetAuthProxyHeaders(req *http.Request, username, uid string, groups []string, extra map[string][]string) {
|
||||||
req.Header.Del("X-Remote-User")
|
req.Header.Del("X-Remote-User")
|
||||||
|
req.Header.Del("X-Remote-Uid")
|
||||||
req.Header.Del("X-Remote-Group")
|
req.Header.Del("X-Remote-Group")
|
||||||
for key := range req.Header {
|
for key := range req.Header {
|
||||||
if strings.HasPrefix(strings.ToLower(key), strings.ToLower("X-Remote-Extra-")) {
|
if strings.HasPrefix(strings.ToLower(key), strings.ToLower("X-Remote-Extra-")) {
|
||||||
@ -131,6 +135,9 @@ func SetAuthProxyHeaders(req *http.Request, username string, groups []string, ex
|
|||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("X-Remote-User", username)
|
req.Header.Set("X-Remote-User", username)
|
||||||
|
if len(uid) > 0 {
|
||||||
|
req.Header.Set("X-Remote-Uid", uid)
|
||||||
|
}
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
req.Header.Add("X-Remote-Group", group)
|
req.Header.Add("X-Remote-Group", group)
|
||||||
}
|
}
|
||||||
|
@ -306,12 +306,14 @@ func TestImpersonationRoundTripper(t *testing.T) {
|
|||||||
func TestAuthProxyRoundTripper(t *testing.T) {
|
func TestAuthProxyRoundTripper(t *testing.T) {
|
||||||
for n, tc := range map[string]struct {
|
for n, tc := range map[string]struct {
|
||||||
username string
|
username string
|
||||||
|
uid string
|
||||||
groups []string
|
groups []string
|
||||||
extra map[string][]string
|
extra map[string][]string
|
||||||
expectedExtra map[string][]string
|
expectedExtra map[string][]string
|
||||||
}{
|
}{
|
||||||
"allfields": {
|
"allfields": {
|
||||||
username: "user",
|
username: "user",
|
||||||
|
uid: "7db46926-e803-4337-9a29-f9c1fab7d34a",
|
||||||
groups: []string{"groupA", "groupB"},
|
groups: []string{"groupA", "groupB"},
|
||||||
extra: map[string][]string{
|
extra: map[string][]string{
|
||||||
"one": {"alpha", "bravo"},
|
"one": {"alpha", "bravo"},
|
||||||
@ -324,6 +326,7 @@ func TestAuthProxyRoundTripper(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"escaped extra": {
|
"escaped extra": {
|
||||||
username: "user",
|
username: "user",
|
||||||
|
uid: "7db46926-e803-4337-9a29-f9c1fab7d34a",
|
||||||
groups: []string{"groupA", "groupB"},
|
groups: []string{"groupA", "groupB"},
|
||||||
extra: map[string][]string{
|
extra: map[string][]string{
|
||||||
"one": {"alpha", "bravo"},
|
"one": {"alpha", "bravo"},
|
||||||
@ -336,6 +339,7 @@ func TestAuthProxyRoundTripper(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"double escaped extra": {
|
"double escaped extra": {
|
||||||
username: "user",
|
username: "user",
|
||||||
|
uid: "7db46926-e803-4337-9a29-f9c1fab7d34a",
|
||||||
groups: []string{"groupA", "groupB"},
|
groups: []string{"groupA", "groupB"},
|
||||||
extra: map[string][]string{
|
extra: map[string][]string{
|
||||||
"one": {"alpha", "bravo"},
|
"one": {"alpha", "bravo"},
|
||||||
@ -349,7 +353,7 @@ func TestAuthProxyRoundTripper(t *testing.T) {
|
|||||||
} {
|
} {
|
||||||
rt := &testRoundTripper{}
|
rt := &testRoundTripper{}
|
||||||
req := &http.Request{}
|
req := &http.Request{}
|
||||||
NewAuthProxyRoundTripper(tc.username, tc.groups, tc.extra, rt).RoundTrip(req)
|
_, _ = NewAuthProxyRoundTripper(tc.username, tc.uid, tc.groups, tc.extra, rt).RoundTrip(req)
|
||||||
if rt.Request == nil {
|
if rt.Request == nil {
|
||||||
t.Errorf("%s: unexpected nil request: %v", n, rt)
|
t.Errorf("%s: unexpected nil request: %v", n, rt)
|
||||||
continue
|
continue
|
||||||
@ -368,6 +372,15 @@ func TestAuthProxyRoundTripper(t *testing.T) {
|
|||||||
t.Errorf("%s expected %v, got %v", n, e, a)
|
t.Errorf("%s expected %v, got %v", n, e, a)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
actualUID, ok := rt.Request.Header["X-Remote-Uid"]
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("%s missing value", n)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if e, a := []string{tc.uid}, actualUID; !reflect.DeepEqual(e, a) {
|
||||||
|
t.Errorf("%s expected %v, got %v", n, e, a)
|
||||||
|
continue
|
||||||
|
}
|
||||||
actualGroups, ok := rt.Request.Header["X-Remote-Group"]
|
actualGroups, ok := rt.Request.Header["X-Remote-Group"]
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("%s missing value", n)
|
t.Errorf("%s missing value", n)
|
||||||
|
@ -159,7 +159,7 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
proxyRoundTripper := handlingInfo.proxyRoundTripper
|
proxyRoundTripper := handlingInfo.proxyRoundTripper
|
||||||
upgrade := httpstream.IsUpgradeRequest(req)
|
upgrade := httpstream.IsUpgradeRequest(req)
|
||||||
|
|
||||||
proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), user.GetGroups(), user.GetExtra(), proxyRoundTripper)
|
proxyRoundTripper = transport.NewAuthProxyRoundTripper(user.GetName(), user.GetUID(), user.GetGroups(), user.GetExtra(), proxyRoundTripper)
|
||||||
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) && !upgrade {
|
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) && !upgrade {
|
||||||
tracingWrapper := tracing.WrapperFor(r.tracerProvider)
|
tracingWrapper := tracing.WrapperFor(r.tracerProvider)
|
||||||
@ -170,7 +170,7 @@ func (r *proxyHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
// NOT use the proxyRoundTripper. It's a direct dial that bypasses the proxyRoundTripper. This means that we have to
|
// NOT use the proxyRoundTripper. It's a direct dial that bypasses the proxyRoundTripper. This means that we have to
|
||||||
// attach the "correct" user headers to the request ahead of time.
|
// attach the "correct" user headers to the request ahead of time.
|
||||||
if upgrade {
|
if upgrade {
|
||||||
transport.SetAuthProxyHeaders(newReq, user.GetName(), user.GetGroups(), user.GetExtra())
|
transport.SetAuthProxyHeaders(newReq, user.GetName(), user.GetUID(), user.GetGroups(), user.GetExtra())
|
||||||
}
|
}
|
||||||
|
|
||||||
handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w})
|
handler := proxy.NewUpgradeAwareHandler(location, proxyRoundTripper, true, upgrade, &responder{w: w})
|
||||||
|
@ -154,6 +154,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
"proxy with user, insecure": {
|
"proxy with user, insecure": {
|
||||||
user: &user.DefaultInfo{
|
user: &user.DefaultInfo{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
|
UID: "6b60d791-1af9-4513-92e5-e4252a1e0a78",
|
||||||
Groups: []string{"one", "two"},
|
Groups: []string{"one", "two"},
|
||||||
},
|
},
|
||||||
path: "/request/path",
|
path: "/request/path",
|
||||||
@ -178,6 +179,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
"X-Forwarded-Uri": {"/request/path"},
|
"X-Forwarded-Uri": {"/request/path"},
|
||||||
"X-Forwarded-For": {"127.0.0.1"},
|
"X-Forwarded-For": {"127.0.0.1"},
|
||||||
"X-Remote-User": {"username"},
|
"X-Remote-User": {"username"},
|
||||||
|
"X-Remote-Uid": {"6b60d791-1af9-4513-92e5-e4252a1e0a78"},
|
||||||
"User-Agent": {"Go-http-client/1.1"},
|
"User-Agent": {"Go-http-client/1.1"},
|
||||||
"Accept-Encoding": {"gzip"},
|
"Accept-Encoding": {"gzip"},
|
||||||
"X-Remote-Group": {"one", "two"},
|
"X-Remote-Group": {"one", "two"},
|
||||||
@ -186,6 +188,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
"proxy with user, cabundle": {
|
"proxy with user, cabundle": {
|
||||||
user: &user.DefaultInfo{
|
user: &user.DefaultInfo{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
|
UID: "6b60d791-1af9-4513-92e5-e4252a1e0a78",
|
||||||
Groups: []string{"one", "two"},
|
Groups: []string{"one", "two"},
|
||||||
},
|
},
|
||||||
path: "/request/path",
|
path: "/request/path",
|
||||||
@ -210,6 +213,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
"X-Forwarded-Uri": {"/request/path"},
|
"X-Forwarded-Uri": {"/request/path"},
|
||||||
"X-Forwarded-For": {"127.0.0.1"},
|
"X-Forwarded-For": {"127.0.0.1"},
|
||||||
"X-Remote-User": {"username"},
|
"X-Remote-User": {"username"},
|
||||||
|
"X-Remote-Uid": {"6b60d791-1af9-4513-92e5-e4252a1e0a78"},
|
||||||
"User-Agent": {"Go-http-client/1.1"},
|
"User-Agent": {"Go-http-client/1.1"},
|
||||||
"Accept-Encoding": {"gzip"},
|
"Accept-Encoding": {"gzip"},
|
||||||
"X-Remote-Group": {"one", "two"},
|
"X-Remote-Group": {"one", "two"},
|
||||||
@ -218,6 +222,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
"service unavailable": {
|
"service unavailable": {
|
||||||
user: &user.DefaultInfo{
|
user: &user.DefaultInfo{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
|
UID: "6b60d791-1af9-4513-92e5-e4252a1e0a78",
|
||||||
Groups: []string{"one", "two"},
|
Groups: []string{"one", "two"},
|
||||||
},
|
},
|
||||||
path: "/request/path",
|
path: "/request/path",
|
||||||
@ -240,6 +245,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
"service unresolveable": {
|
"service unresolveable": {
|
||||||
user: &user.DefaultInfo{
|
user: &user.DefaultInfo{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
|
UID: "6b60d791-1af9-4513-92e5-e4252a1e0a78",
|
||||||
Groups: []string{"one", "two"},
|
Groups: []string{"one", "two"},
|
||||||
},
|
},
|
||||||
path: "/request/path",
|
path: "/request/path",
|
||||||
@ -263,6 +269,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
"fail on bad serving cert": {
|
"fail on bad serving cert": {
|
||||||
user: &user.DefaultInfo{
|
user: &user.DefaultInfo{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
|
UID: "6b60d791-1af9-4513-92e5-e4252a1e0a78",
|
||||||
Groups: []string{"one", "two"},
|
Groups: []string{"one", "two"},
|
||||||
},
|
},
|
||||||
path: "/request/path",
|
path: "/request/path",
|
||||||
@ -284,6 +291,7 @@ func TestProxyHandler(t *testing.T) {
|
|||||||
"fail on bad serving cert w/o SAN and increase SAN error counter metrics": {
|
"fail on bad serving cert w/o SAN and increase SAN error counter metrics": {
|
||||||
user: &user.DefaultInfo{
|
user: &user.DefaultInfo{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
|
UID: "6b60d791-1af9-4513-92e5-e4252a1e0a78",
|
||||||
Groups: []string{"one", "two"},
|
Groups: []string{"one", "two"},
|
||||||
},
|
},
|
||||||
path: "/request/path",
|
path: "/request/path",
|
||||||
@ -425,6 +433,7 @@ func newBrokenDialerAndSelector() (*mockEgressDialer, *egressselector.EgressSele
|
|||||||
|
|
||||||
func TestProxyUpgrade(t *testing.T) {
|
func TestProxyUpgrade(t *testing.T) {
|
||||||
upgradeUser := "upgradeUser"
|
upgradeUser := "upgradeUser"
|
||||||
|
upgradeUID := "upgradeUser-UID"
|
||||||
testcases := map[string]struct {
|
testcases := map[string]struct {
|
||||||
APIService *apiregistration.APIService
|
APIService *apiregistration.APIService
|
||||||
NewEgressSelector func() (*mockEgressDialer, *egressselector.EgressSelector)
|
NewEgressSelector func() (*mockEgressDialer, *egressselector.EgressSelector)
|
||||||
@ -534,6 +543,10 @@ func TestProxyUpgrade(t *testing.T) {
|
|||||||
if user != upgradeUser {
|
if user != upgradeUser {
|
||||||
t.Errorf("expected user %q, got %q", upgradeUser, user)
|
t.Errorf("expected user %q, got %q", upgradeUser, user)
|
||||||
}
|
}
|
||||||
|
uid := req.Header.Get("X-Remote-Uid")
|
||||||
|
if uid != upgradeUID {
|
||||||
|
t.Errorf("expected UID %q, got %q", upgradeUID, uid)
|
||||||
|
}
|
||||||
body := make([]byte, 5)
|
body := make([]byte, 5)
|
||||||
ws.Read(body)
|
ws.Read(body)
|
||||||
ws.Write([]byte("hello " + string(body)))
|
ws.Write([]byte("hello " + string(body)))
|
||||||
@ -576,7 +589,7 @@ func TestProxyUpgrade(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
proxyHandler.updateAPIService(tc.APIService)
|
proxyHandler.updateAPIService(tc.APIService)
|
||||||
aggregator := httptest.NewServer(contextHandler(proxyHandler, &user.DefaultInfo{Name: upgradeUser}))
|
aggregator := httptest.NewServer(contextHandler(proxyHandler, &user.DefaultInfo{Name: upgradeUser, UID: upgradeUID}))
|
||||||
defer aggregator.Close()
|
defer aggregator.Close()
|
||||||
|
|
||||||
ws, err := websocket.Dial("ws://"+aggregator.Listener.Addr().String()+path, "", "http://127.0.0.1/")
|
ws, err := websocket.Dial("ws://"+aggregator.Listener.Addr().String()+path, "", "http://127.0.0.1/")
|
||||||
|
@ -305,7 +305,7 @@ func (c *AvailableConditionController) sync(key string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// setting the system-masters identity ensures that we will always have access rights
|
// setting the system-masters identity ensures that we will always have access rights
|
||||||
transport.SetAuthProxyHeaders(newReq, "system:kube-aggregator", []string{"system:masters"}, nil)
|
transport.SetAuthProxyHeaders(newReq, "system:kube-aggregator", "", []string{"system:masters"}, nil)
|
||||||
resp, err := discoveryClient.Do(newReq)
|
resp, err := discoveryClient.Do(newReq)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
|
Loading…
Reference in New Issue
Block a user