mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +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 is used to impersonate a particular user during an API server request
|
||||||
ImpersonateUserHeader = "Impersonate-User"
|
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.
|
// ImpersonateGroupHeader is used to impersonate a particular group during an API server request.
|
||||||
// It can be repeated multiplied times for multiple groups.
|
// It can be repeated multiplied times for multiple groups.
|
||||||
ImpersonateGroupHeader = "Impersonate-Group"
|
ImpersonateGroupHeader = "Impersonate-Group"
|
||||||
|
@ -202,6 +202,8 @@ func (c *Config) String() string {
|
|||||||
type ImpersonationConfig struct {
|
type ImpersonationConfig struct {
|
||||||
// UserName is the username to impersonate on each request.
|
// UserName is the username to impersonate on each request.
|
||||||
UserName string
|
UserName string
|
||||||
|
// UID is a unique value that identifies the user.
|
||||||
|
UID string
|
||||||
// Groups are the groups to impersonate on each request.
|
// Groups are the groups to impersonate on each request.
|
||||||
Groups []string
|
Groups []string
|
||||||
// Extra is a free-form field which can be used to link some authentication information
|
// 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,
|
BearerToken: config.BearerToken,
|
||||||
BearerTokenFile: config.BearerTokenFile,
|
BearerTokenFile: config.BearerTokenFile,
|
||||||
Impersonate: ImpersonationConfig{
|
Impersonate: ImpersonationConfig{
|
||||||
|
UserName: config.Impersonate.UserName,
|
||||||
|
UID: config.Impersonate.UID,
|
||||||
Groups: config.Impersonate.Groups,
|
Groups: config.Impersonate.Groups,
|
||||||
Extra: config.Impersonate.Extra,
|
Extra: config.Impersonate.Extra,
|
||||||
UserName: config.Impersonate.UserName,
|
|
||||||
},
|
},
|
||||||
AuthProvider: config.AuthProvider,
|
AuthProvider: config.AuthProvider,
|
||||||
AuthConfigPersister: config.AuthConfigPersister,
|
AuthConfigPersister: config.AuthConfigPersister,
|
||||||
|
@ -594,6 +594,7 @@ func TestConfigSprint(t *testing.T) {
|
|||||||
BearerToken: "1234567890",
|
BearerToken: "1234567890",
|
||||||
Impersonate: ImpersonationConfig{
|
Impersonate: ImpersonationConfig{
|
||||||
UserName: "gopher2",
|
UserName: "gopher2",
|
||||||
|
UID: "uid123",
|
||||||
},
|
},
|
||||||
AuthProvider: &clientcmdapi.AuthProviderConfig{
|
AuthProvider: &clientcmdapi.AuthProviderConfig{
|
||||||
Name: "gopher",
|
Name: "gopher",
|
||||||
@ -626,7 +627,7 @@ func TestConfigSprint(t *testing.T) {
|
|||||||
Proxy: fakeProxyFunc,
|
Proxy: fakeProxyFunc,
|
||||||
}
|
}
|
||||||
want := fmt.Sprintf(
|
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,
|
c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc, fakeProxyFunc,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
|
|||||||
BearerTokenFile: c.BearerTokenFile,
|
BearerTokenFile: c.BearerTokenFile,
|
||||||
Impersonate: transport.ImpersonationConfig{
|
Impersonate: transport.ImpersonationConfig{
|
||||||
UserName: c.Impersonate.UserName,
|
UserName: c.Impersonate.UserName,
|
||||||
|
UID: c.Impersonate.UID,
|
||||||
Groups: c.Impersonate.Groups,
|
Groups: c.Impersonate.Groups,
|
||||||
Extra: c.Impersonate.Extra,
|
Extra: c.Impersonate.Extra,
|
||||||
},
|
},
|
||||||
|
@ -82,6 +82,8 @@ type Config struct {
|
|||||||
type ImpersonationConfig struct {
|
type ImpersonationConfig struct {
|
||||||
// UserName matches user.Info.GetName()
|
// UserName matches user.Info.GetName()
|
||||||
UserName string
|
UserName string
|
||||||
|
// UID matches user.Info.GetUID()
|
||||||
|
UID string
|
||||||
// Groups matches user.Info.GetGroups()
|
// Groups matches user.Info.GetGroups()
|
||||||
Groups []string
|
Groups []string
|
||||||
// Extra matches user.Info.GetExtra()
|
// Extra matches user.Info.GetExtra()
|
||||||
|
@ -57,6 +57,7 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip
|
|||||||
rt = NewUserAgentRoundTripper(config.UserAgent, rt)
|
rt = NewUserAgentRoundTripper(config.UserAgent, rt)
|
||||||
}
|
}
|
||||||
if len(config.Impersonate.UserName) > 0 ||
|
if len(config.Impersonate.UserName) > 0 ||
|
||||||
|
len(config.Impersonate.UID) > 0 ||
|
||||||
len(config.Impersonate.Groups) > 0 ||
|
len(config.Impersonate.Groups) > 0 ||
|
||||||
len(config.Impersonate.Extra) > 0 {
|
len(config.Impersonate.Extra) > 0 {
|
||||||
rt = NewImpersonatingRoundTripper(config.Impersonate, rt)
|
rt = NewImpersonatingRoundTripper(config.Impersonate, rt)
|
||||||
@ -199,6 +200,9 @@ const (
|
|||||||
// ImpersonateUserHeader is used to impersonate a particular user during an API server request
|
// ImpersonateUserHeader is used to impersonate a particular user during an API server request
|
||||||
ImpersonateUserHeader = "Impersonate-User"
|
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.
|
// ImpersonateGroupHeader is used to impersonate a particular group during an API server request.
|
||||||
// It can be repeated multiplied times for multiple groups.
|
// It can be repeated multiplied times for multiple groups.
|
||||||
ImpersonateGroupHeader = "Impersonate-Group"
|
ImpersonateGroupHeader = "Impersonate-Group"
|
||||||
@ -230,7 +234,9 @@ func (rt *impersonatingRoundTripper) RoundTrip(req *http.Request) (*http.Respons
|
|||||||
}
|
}
|
||||||
req = utilnet.CloneRequest(req)
|
req = utilnet.CloneRequest(req)
|
||||||
req.Header.Set(ImpersonateUserHeader, rt.impersonate.UserName)
|
req.Header.Set(ImpersonateUserHeader, rt.impersonate.UserName)
|
||||||
|
if rt.impersonate.UID != "" {
|
||||||
|
req.Header.Set(ImpersonateUIDHeader, rt.impersonate.UID)
|
||||||
|
}
|
||||||
for _, group := range rt.impersonate.Groups {
|
for _, group := range rt.impersonate.Groups {
|
||||||
req.Header.Add(ImpersonateGroupHeader, group)
|
req.Header.Add(ImpersonateGroupHeader, group)
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,25 @@ func TestImpersonationRoundTripper(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "all",
|
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{
|
impersonationConfig: ImpersonationConfig{
|
||||||
UserName: "user",
|
UserName: "user",
|
||||||
Groups: []string{"one", "two"},
|
Groups: []string{"one", "two"},
|
||||||
@ -215,6 +234,17 @@ func TestImpersonationRoundTripper(t *testing.T) {
|
|||||||
ImpersonateUserExtraHeaderPrefix + "Second": {"B", "b"},
|
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",
|
name: "escape handling",
|
||||||
impersonationConfig: ImpersonationConfig{
|
impersonationConfig: ImpersonationConfig{
|
||||||
|
@ -72,12 +72,6 @@ import (
|
|||||||
"k8s.io/kubernetes/test/integration/framework"
|
"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 (
|
const (
|
||||||
AliceToken string = "abc123" // username: alice. Present in token file.
|
AliceToken string = "abc123" // username: alice. Present in token file.
|
||||||
BobToken string = "xyz987" // username: bob. 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)
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
||||||
t.Cleanup(cancel)
|
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) {
|
t.Run("impersonation with uid header", func(t *testing.T) {
|
||||||
adminClient := clientset.NewForConfigOrDie(server.ClientConfig)
|
adminClient := clientset.NewForConfigOrDie(server.ClientConfig)
|
||||||
|
|
||||||
@ -933,8 +920,8 @@ func TestImpersonateWithUID(t *testing.T) {
|
|||||||
clientConfig := rest.CopyConfig(server.ClientConfig)
|
clientConfig := rest.CopyConfig(server.ClientConfig)
|
||||||
clientConfig.Impersonate = rest.ImpersonationConfig{
|
clientConfig.Impersonate = rest.ImpersonationConfig{
|
||||||
UserName: "alice",
|
UserName: "alice",
|
||||||
|
UID: "1234",
|
||||||
}
|
}
|
||||||
clientConfig.Wrap(setUIDWrapper)
|
|
||||||
|
|
||||||
client := clientset.NewForConfigOrDie(clientConfig)
|
client := clientset.NewForConfigOrDie(clientConfig)
|
||||||
createdCsr, err := client.CertificatesV1().CertificateSigningRequests().Create(
|
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) {
|
t.Run("impersonation with only UID fails", func(t *testing.T) {
|
||||||
clientConfig := rest.CopyConfig(server.ClientConfig)
|
clientConfig := rest.CopyConfig(server.ClientConfig)
|
||||||
clientConfig.Wrap(setUIDWrapper)
|
clientConfig.Impersonate = rest.ImpersonationConfig{
|
||||||
|
UID: "1234",
|
||||||
|
}
|
||||||
|
|
||||||
client := clientset.NewForConfigOrDie(clientConfig)
|
client := clientset.NewForConfigOrDie(clientConfig)
|
||||||
_, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
|
_, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
|
||||||
@ -1007,8 +996,8 @@ func TestImpersonateWithUID(t *testing.T) {
|
|||||||
clientConfig := rest.AnonymousClientConfig(server.ClientConfig)
|
clientConfig := rest.AnonymousClientConfig(server.ClientConfig)
|
||||||
clientConfig.Impersonate = rest.ImpersonationConfig{
|
clientConfig.Impersonate = rest.ImpersonationConfig{
|
||||||
UserName: "some-user-anonymous-can-impersonate",
|
UserName: "some-user-anonymous-can-impersonate",
|
||||||
|
UID: "1234",
|
||||||
}
|
}
|
||||||
clientConfig.Wrap(setUIDWrapper)
|
|
||||||
|
|
||||||
client := clientset.NewForConfigOrDie(clientConfig)
|
client := clientset.NewForConfigOrDie(clientConfig)
|
||||||
_, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
|
_, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
|
||||||
|
Loading…
Reference in New Issue
Block a user