Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UID to client-go impersonation config #104483

Merged
merged 1 commit into from Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions pkg/apis/authentication/types.go
Expand Up @@ -25,6 +25,9 @@ const (
// ImpersonateUserHeader is used to impersonate a particular user during an API server request
ImpersonateUserHeader = "Impersonate-User"

// ImpersonateUIDHeader is used to impersonate a particular UID during an API server request.
ImpersonateUidHeader = "Impersonate-Uid"

// ImpersonateGroupHeader is used to impersonate a particular group during an API server request.
// It can be repeated multiplied times for multiple groups.
ImpersonateGroupHeader = "Impersonate-Group"
Expand Down
5 changes: 4 additions & 1 deletion staging/src/k8s.io/client-go/rest/config.go
Expand Up @@ -202,6 +202,8 @@ func (c *Config) String() string {
type ImpersonationConfig struct {
// UserName is the username to impersonate on each request.
UserName string
// UID is a unique value that identifies the user.
UID string
// Groups are the groups to impersonate on each request.
Groups []string
// Extra is a free-form field which can be used to link some authentication information
Expand Down Expand Up @@ -608,9 +610,10 @@ func CopyConfig(config *Config) *Config {
BearerToken: config.BearerToken,
BearerTokenFile: config.BearerTokenFile,
Impersonate: ImpersonationConfig{
UserName: config.Impersonate.UserName,
UID: config.Impersonate.UID,
Groups: config.Impersonate.Groups,
Extra: config.Impersonate.Extra,
UserName: config.Impersonate.UserName,
},
AuthProvider: config.AuthProvider,
AuthConfigPersister: config.AuthConfigPersister,
Expand Down
3 changes: 2 additions & 1 deletion staging/src/k8s.io/client-go/rest/config_test.go
Expand Up @@ -594,6 +594,7 @@ func TestConfigSprint(t *testing.T) {
BearerToken: "1234567890",
Impersonate: ImpersonationConfig{
UserName: "gopher2",
UID: "uid123",
},
AuthProvider: &clientcmdapi.AuthProviderConfig{
Name: "gopher",
Expand Down Expand Up @@ -626,7 +627,7 @@ func TestConfigSprint(t *testing.T) {
Proxy: fakeProxyFunc,
}
want := fmt.Sprintf(
`&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), ExecProvider:api.ExecConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: "", ProvideClusterInfo: true, Config: runtime.Object(--- REDACTED ---), StdinUnavailable: false}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil), NextProtos:[]string{"h2", "http/1.1"}}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), WarningHandler:rest.fakeWarningHandler{}, Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p), Proxy:(func(*http.Request) (*url.URL, error))(%p)}`,
`&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", UID:"uid123", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), ExecProvider:api.ExecConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: "", ProvideClusterInfo: true, Config: runtime.Object(--- REDACTED ---), StdinUnavailable: false}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil), NextProtos:[]string{"h2", "http/1.1"}}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), WarningHandler:rest.fakeWarningHandler{}, Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p), Proxy:(func(*http.Request) (*url.URL, error))(%p)}`,
c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc, fakeProxyFunc,
)

Expand Down
1 change: 1 addition & 0 deletions staging/src/k8s.io/client-go/rest/transport.go
Expand Up @@ -83,6 +83,7 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
BearerTokenFile: c.BearerTokenFile,
Impersonate: transport.ImpersonationConfig{
UserName: c.Impersonate.UserName,
UID: c.Impersonate.UID,
Groups: c.Impersonate.Groups,
Extra: c.Impersonate.Extra,
},
Expand Down
2 changes: 2 additions & 0 deletions staging/src/k8s.io/client-go/transport/config.go
Expand Up @@ -82,6 +82,8 @@ type Config struct {
type ImpersonationConfig struct {
// UserName matches user.Info.GetName()
UserName string
// UID matches user.Info.GetUID()
UID string
// Groups matches user.Info.GetGroups()
Groups []string
// Extra matches user.Info.GetExtra()
Expand Down
8 changes: 7 additions & 1 deletion staging/src/k8s.io/client-go/transport/round_trippers.go
Expand Up @@ -57,6 +57,7 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip
rt = NewUserAgentRoundTripper(config.UserAgent, rt)
}
if len(config.Impersonate.UserName) > 0 ||
len(config.Impersonate.UID) > 0 ||
len(config.Impersonate.Groups) > 0 ||
len(config.Impersonate.Extra) > 0 {
rt = NewImpersonatingRoundTripper(config.Impersonate, rt)
Expand Down Expand Up @@ -199,6 +200,9 @@ const (
// ImpersonateUserHeader is used to impersonate a particular user during an API server request
ImpersonateUserHeader = "Impersonate-User"

// ImpersonateUIDHeader is used to impersonate a particular UID during an API server request
ImpersonateUIDHeader = "Impersonate-Uid"

// ImpersonateGroupHeader is used to impersonate a particular group during an API server request.
// It can be repeated multiplied times for multiple groups.
ImpersonateGroupHeader = "Impersonate-Group"
Expand Down Expand Up @@ -230,7 +234,9 @@ func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Respons
}
req = utilnet.CloneRequest(req)
req.Header.Set(ImpersonateUserHeader, rt.impersonate.UserName)

if rt.impersonate.UID != "" {
req.Header.Set(ImpersonateUIDHeader, rt.impersonate.UID)
}
for _, group := range rt.impersonate.Groups {
req.Header.Add(ImpersonateGroupHeader, group)
}
Expand Down
30 changes: 30 additions & 0 deletions staging/src/k8s.io/client-go/transport/round_trippers_test.go
Expand Up @@ -202,6 +202,7 @@ func TestImpersonationRoundTripper(t *testing.T) {
name: "all",
impersonationConfig: ImpersonationConfig{
UserName: "user",
UID: "uid-a",
Groups: []string{"one", "two"},
Extra: map[string][]string{
"first": {"A", "a"},
Expand All @@ -210,11 +211,40 @@ func TestImpersonationRoundTripper(t *testing.T) {
},
expected: map[string][]string{
ImpersonateUserHeader: {"user"},
ImpersonateUIDHeader: {"uid-a"},
ImpersonateGroupHeader: {"one", "two"},
ImpersonateUserExtraHeaderPrefix + "First": {"A", "a"},
ImpersonateUserExtraHeaderPrefix + "Second": {"B", "b"},
},
},
{
name: "username, groups and extra",
impersonationConfig: ImpersonationConfig{
UserName: "user",
Groups: []string{"one", "two"},
Extra: map[string][]string{
"first": {"A", "a"},
"second": {"B", "b"},
},
},
expected: map[string][]string{
ImpersonateUserHeader: {"user"},
ImpersonateGroupHeader: {"one", "two"},
ImpersonateUserExtraHeaderPrefix + "First": {"A", "a"},
ImpersonateUserExtraHeaderPrefix + "Second": {"B", "b"},
},
},
{
name: "username and uid",
impersonationConfig: ImpersonationConfig{
UserName: "user",
UID: "uid-a",
},
expected: map[string][]string{
ImpersonateUserHeader: {"user"},
ImpersonateUIDHeader: {"uid-a"},
},
},
{
name: "escape handling",
impersonationConfig: ImpersonationConfig{
Expand Down
21 changes: 5 additions & 16 deletions test/integration/auth/auth_test.go
Expand Up @@ -72,12 +72,6 @@ import (
"k8s.io/kubernetes/test/integration/framework"
)

type roundTripperFunc func(*http.Request) (*http.Response, error)

func (f roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req)
}

const (
AliceToken string = "abc123" // username: alice. Present in token file.
BobToken string = "xyz987" // username: bob. Present in token file.
Expand Down Expand Up @@ -910,13 +904,6 @@ func TestImpersonateWithUID(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
t.Cleanup(cancel)

setUIDWrapper := func(rt http.RoundTripper) http.RoundTripper {
return roundTripperFunc(func(req *http.Request) (*http.Response, error) {
req.Header.Set("Impersonate-Uid", "1234")
return rt.RoundTrip(req)
})
}

t.Run("impersonation with uid header", func(t *testing.T) {
adminClient := clientset.NewForConfigOrDie(server.ClientConfig)

Expand All @@ -933,8 +920,8 @@ func TestImpersonateWithUID(t *testing.T) {
clientConfig := rest.CopyConfig(server.ClientConfig)
clientConfig.Impersonate = rest.ImpersonationConfig{
UserName: "alice",
UID: "1234",
}
clientConfig.Wrap(setUIDWrapper)

client := clientset.NewForConfigOrDie(clientConfig)
createdCsr, err := client.CertificatesV1().CertificateSigningRequests().Create(
Expand Down Expand Up @@ -974,7 +961,9 @@ func TestImpersonateWithUID(t *testing.T) {

t.Run("impersonation with only UID fails", func(t *testing.T) {
clientConfig := rest.CopyConfig(server.ClientConfig)
clientConfig.Wrap(setUIDWrapper)
clientConfig.Impersonate = rest.ImpersonationConfig{
UID: "1234",
}

client := clientset.NewForConfigOrDie(clientConfig)
_, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
Expand Down Expand Up @@ -1007,8 +996,8 @@ func TestImpersonateWithUID(t *testing.T) {
clientConfig := rest.AnonymousClientConfig(server.ClientConfig)
clientConfig.Impersonate = rest.ImpersonationConfig{
UserName: "some-user-anonymous-can-impersonate",
UID: "1234",
}
clientConfig.Wrap(setUIDWrapper)

client := clientset.NewForConfigOrDie(clientConfig)
_, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
Expand Down