mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
Introduces Impersonate-Uid to client-go.
* Updates ImpersonationConfig in rest/config.go to include UID attribute, and pass it through when copying the config * Updates ImpersonationConfig in transport/config.go to include UID attribute * In transport/round_tripper.go, Set the "Impersonate-Uid" header in requests based on the UID value in the config * Update auth_test.go integration test to specify a UID through the new rest.ImpersonationConfig field rather than manually setting the Impersonate-Uid header Signed-off-by: Margo Crawford <margaretc@vmware.com>
This commit is contained in:
parent
fffaadc013
commit
d9ddfb26e1
@ -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"
|
||||
|
@ -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
|
||||
@ -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,
|
||||
|
@ -594,6 +594,7 @@ func TestConfigSprint(t *testing.T) {
|
||||
BearerToken: "1234567890",
|
||||
Impersonate: ImpersonationConfig{
|
||||
UserName: "gopher2",
|
||||
UID: "uid123",
|
||||
},
|
||||
AuthProvider: &clientcmdapi.AuthProviderConfig{
|
||||
Name: "gopher",
|
||||
@ -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,
|
||||
)
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
@ -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"
|
||||
@ -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)
|
||||
}
|
||||
|
@ -200,6 +200,25 @@ 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"},
|
||||
"second": {"B", "b"},
|
||||
},
|
||||
},
|
||||
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"},
|
||||
@ -215,6 +234,17 @@ func TestImpersonationRoundTripper(t *testing.T) {
|
||||
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{
|
||||
|
@ -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.
|
||||
@ -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)
|
||||
|
||||
@ -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(
|
||||
@ -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{})
|
||||
@ -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{})
|
||||
|
Loading…
Reference in New Issue
Block a user