mirror of
https://github.com/kubernetes/client-go.git
synced 2025-08-16 22:36:48 +00:00
--as-uid flag in kubectl and kubeconfigs.
This corresponds to previous work to allow impersonating UIDs: * Introduce Impersonate-UID header: #99961 * Add UID to client-go impersonation config #104483 Signed-off-by: Margo Crawford <margaretc@vmware.com> Kubernetes-commit: 7e079f5144474cae05802771f604df2b748d781f
This commit is contained in:
parent
9b0b23a8ad
commit
5fca705b7d
@ -124,7 +124,10 @@ type AuthInfo struct {
|
||||
// Impersonate is the username to act-as.
|
||||
// +optional
|
||||
Impersonate string `json:"act-as,omitempty"`
|
||||
// ImpersonateGroups is the groups to imperonate.
|
||||
// ImpersonateUID is the uid to impersonate.
|
||||
// +optional
|
||||
ImpersonateUID string `json:"act-as-uid,omitempty"`
|
||||
// ImpersonateGroups is the groups to impersonate.
|
||||
// +optional
|
||||
ImpersonateGroups []string `json:"act-as-groups,omitempty"`
|
||||
// ImpersonateUserExtra contains additional information for impersonated user.
|
||||
|
@ -111,10 +111,13 @@ type AuthInfo struct {
|
||||
// TokenFile is a pointer to a file that contains a bearer token (as described above). If both Token and TokenFile are present, Token takes precedence.
|
||||
// +optional
|
||||
TokenFile string `json:"tokenFile,omitempty"`
|
||||
// Impersonate is the username to imperonate. The name matches the flag.
|
||||
// Impersonate is the username to impersonate. The name matches the flag.
|
||||
// +optional
|
||||
Impersonate string `json:"as,omitempty"`
|
||||
// ImpersonateGroups is the groups to imperonate.
|
||||
// ImpersonateUID is the uid to impersonate.
|
||||
// +optional
|
||||
ImpersonateUID string `json:"as-uid,omitempty"`
|
||||
// ImpersonateGroups is the groups to impersonate.
|
||||
// +optional
|
||||
ImpersonateGroups []string `json:"as-groups,omitempty"`
|
||||
// ImpersonateUserExtra contains additional information for impersonated user.
|
||||
|
@ -167,6 +167,7 @@ func autoConvert_v1_AuthInfo_To_api_AuthInfo(in *AuthInfo, out *api.AuthInfo, s
|
||||
out.Token = in.Token
|
||||
out.TokenFile = in.TokenFile
|
||||
out.Impersonate = in.Impersonate
|
||||
out.ImpersonateUID = in.ImpersonateUID
|
||||
out.ImpersonateGroups = *(*[]string)(unsafe.Pointer(&in.ImpersonateGroups))
|
||||
out.ImpersonateUserExtra = *(*map[string][]string)(unsafe.Pointer(&in.ImpersonateUserExtra))
|
||||
out.Username = in.Username
|
||||
@ -201,6 +202,7 @@ func autoConvert_api_AuthInfo_To_v1_AuthInfo(in *api.AuthInfo, out *AuthInfo, s
|
||||
out.Token = in.Token
|
||||
out.TokenFile = in.TokenFile
|
||||
out.Impersonate = in.Impersonate
|
||||
out.ImpersonateUID = in.ImpersonateUID
|
||||
out.ImpersonateGroups = *(*[]string)(unsafe.Pointer(&in.ImpersonateGroups))
|
||||
out.ImpersonateUserExtra = *(*map[string][]string)(unsafe.Pointer(&in.ImpersonateUserExtra))
|
||||
out.Username = in.Username
|
||||
|
@ -181,6 +181,7 @@ func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
|
||||
if len(configAuthInfo.Impersonate) > 0 {
|
||||
clientConfig.Impersonate = restclient.ImpersonationConfig{
|
||||
UserName: configAuthInfo.Impersonate,
|
||||
UID: configAuthInfo.ImpersonateUID,
|
||||
Groups: configAuthInfo.ImpersonateGroups,
|
||||
Extra: configAuthInfo.ImpersonateUserExtra,
|
||||
}
|
||||
@ -255,6 +256,7 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
|
||||
if len(configAuthInfo.Impersonate) > 0 {
|
||||
mergedConfig.Impersonate = restclient.ImpersonationConfig{
|
||||
UserName: configAuthInfo.Impersonate,
|
||||
UID: configAuthInfo.ImpersonateUID,
|
||||
Groups: configAuthInfo.ImpersonateGroups,
|
||||
Extra: configAuthInfo.ImpersonateUserExtra,
|
||||
}
|
||||
|
@ -217,6 +217,43 @@ func TestTLSServerNameClearsWhenServerNameSet(t *testing.T) {
|
||||
matchStringArg("", actualCfg.ServerName, t)
|
||||
}
|
||||
|
||||
func TestFullImpersonateConfig(t *testing.T) {
|
||||
config := createValidTestConfig()
|
||||
config.Clusters["clean"] = &clientcmdapi.Cluster{
|
||||
Server: "https://localhost:8443",
|
||||
}
|
||||
config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{
|
||||
Impersonate: "alice",
|
||||
ImpersonateUID: "abc123",
|
||||
ImpersonateGroups: []string{"group-1"},
|
||||
ImpersonateUserExtra: map[string][]string{"some-key": {"some-value"}},
|
||||
}
|
||||
config.Contexts["clean"] = &clientcmdapi.Context{
|
||||
Cluster: "clean",
|
||||
AuthInfo: "clean",
|
||||
}
|
||||
config.CurrentContext = "clean"
|
||||
|
||||
clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{
|
||||
ClusterInfo: clientcmdapi.Cluster{
|
||||
Server: "http://something",
|
||||
},
|
||||
}, nil)
|
||||
|
||||
actualCfg, err := clientBuilder.ClientConfig()
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
matchStringArg("alice", actualCfg.Impersonate.UserName, t)
|
||||
matchStringArg("abc123", actualCfg.Impersonate.UID, t)
|
||||
matchIntArg(1, len(actualCfg.Impersonate.Groups), t)
|
||||
matchStringArg("group-1", actualCfg.Impersonate.Groups[0], t)
|
||||
matchIntArg(1, len(actualCfg.Impersonate.Extra), t)
|
||||
matchIntArg(1, len(actualCfg.Impersonate.Extra["some-key"]), t)
|
||||
matchStringArg("some-value", actualCfg.Impersonate.Extra["some-key"][0], t)
|
||||
}
|
||||
|
||||
func TestMergeContext(t *testing.T) {
|
||||
const namespace = "overridden-namespace"
|
||||
|
||||
@ -808,6 +845,12 @@ func matchByteArg(expected, got []byte, t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func matchIntArg(expected, got int, t *testing.T) {
|
||||
if expected != got {
|
||||
t.Errorf("Expected %d, got %d", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNamespaceOverride(t *testing.T) {
|
||||
config := &DirectClientConfig{
|
||||
overrides: &ConfigOverrides{
|
||||
|
@ -53,6 +53,7 @@ type AuthOverrideFlags struct {
|
||||
ClientKey FlagInfo
|
||||
Token FlagInfo
|
||||
Impersonate FlagInfo
|
||||
ImpersonateUID FlagInfo
|
||||
ImpersonateGroups FlagInfo
|
||||
Username FlagInfo
|
||||
Password FlagInfo
|
||||
@ -154,6 +155,7 @@ const (
|
||||
FlagEmbedCerts = "embed-certs"
|
||||
FlagBearerToken = "token"
|
||||
FlagImpersonate = "as"
|
||||
FlagImpersonateUID = "as-uid"
|
||||
FlagImpersonateGroup = "as-group"
|
||||
FlagUsername = "username"
|
||||
FlagPassword = "password"
|
||||
@ -179,6 +181,7 @@ func RecommendedAuthOverrideFlags(prefix string) AuthOverrideFlags {
|
||||
ClientKey: FlagInfo{prefix + FlagKeyFile, "", "", "Path to a client key file for TLS"},
|
||||
Token: FlagInfo{prefix + FlagBearerToken, "", "", "Bearer token for authentication to the API server"},
|
||||
Impersonate: FlagInfo{prefix + FlagImpersonate, "", "", "Username to impersonate for the operation"},
|
||||
ImpersonateUID: FlagInfo{prefix + FlagImpersonateUID, "", "", "UID to impersonate for the operation"},
|
||||
ImpersonateGroups: FlagInfo{prefix + FlagImpersonateGroup, "", "", "Group to impersonate for the operation, this flag can be repeated to specify multiple groups."},
|
||||
Username: FlagInfo{prefix + FlagUsername, "", "", "Username for basic authentication to the API server"},
|
||||
Password: FlagInfo{prefix + FlagPassword, "", "", "Password for basic authentication to the API server"},
|
||||
@ -219,6 +222,7 @@ func BindAuthInfoFlags(authInfo *clientcmdapi.AuthInfo, flags *pflag.FlagSet, fl
|
||||
flagNames.ClientKey.BindStringFlag(flags, &authInfo.ClientKey).AddSecretAnnotation(flags)
|
||||
flagNames.Token.BindStringFlag(flags, &authInfo.Token).AddSecretAnnotation(flags)
|
||||
flagNames.Impersonate.BindStringFlag(flags, &authInfo.Impersonate).AddSecretAnnotation(flags)
|
||||
flagNames.ImpersonateUID.BindStringFlag(flags, &authInfo.ImpersonateUID).AddSecretAnnotation(flags)
|
||||
flagNames.ImpersonateGroups.BindStringArrayFlag(flags, &authInfo.ImpersonateGroups).AddSecretAnnotation(flags)
|
||||
flagNames.Username.BindStringFlag(flags, &authInfo.Username).AddSecretAnnotation(flags)
|
||||
flagNames.Password.BindStringFlag(flags, &authInfo.Password).AddSecretAnnotation(flags)
|
||||
|
@ -323,9 +323,9 @@ func validateAuthInfo(authInfoName string, authInfo clientcmdapi.AuthInfo) []err
|
||||
validationErrors = append(validationErrors, fmt.Errorf("more than one authentication method found for %v; found %v, only one is allowed", authInfoName, methods))
|
||||
}
|
||||
|
||||
// ImpersonateGroups or ImpersonateUserExtra should be requested with a user
|
||||
if (len(authInfo.ImpersonateGroups) > 0 || len(authInfo.ImpersonateUserExtra) > 0) && (len(authInfo.Impersonate) == 0) {
|
||||
validationErrors = append(validationErrors, fmt.Errorf("requesting groups or user-extra for %v without impersonating a user", authInfoName))
|
||||
// ImpersonateUID, ImpersonateGroups or ImpersonateUserExtra should be requested with a user
|
||||
if (len(authInfo.ImpersonateUID) > 0 || len(authInfo.ImpersonateGroups) > 0 || len(authInfo.ImpersonateUserExtra) > 0) && (len(authInfo.Impersonate) == 0) {
|
||||
validationErrors = append(validationErrors, fmt.Errorf("requesting uid, groups or user-extra for %v without impersonating a user", authInfoName))
|
||||
}
|
||||
return validationErrors
|
||||
}
|
||||
|
@ -508,6 +508,78 @@ func TestValidateAuthInfoExecInteractiveModeInvalid(t *testing.T) {
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoImpersonateUser(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
Impersonate: "user",
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
}
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoImpersonateEverything(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
Impersonate: "user",
|
||||
ImpersonateUID: "abc123",
|
||||
ImpersonateGroups: []string{"group-1", "group-2"},
|
||||
ImpersonateUserExtra: map[string][]string{"key": {"val1", "val2"}},
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
}
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoImpersonateGroupsWithoutUserInvalid(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
ImpersonateGroups: []string{"group-1", "group-2"},
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{
|
||||
`requesting uid, groups or user-extra for user without impersonating a user`,
|
||||
},
|
||||
}
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoImpersonateExtraWithoutUserInvalid(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
ImpersonateUserExtra: map[string][]string{"key": {"val1", "val2"}},
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{
|
||||
`requesting uid, groups or user-extra for user without impersonating a user`,
|
||||
},
|
||||
}
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
func TestValidateAuthInfoImpersonateUIDWithoutUserInvalid(t *testing.T) {
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.AuthInfos["user"] = &clientcmdapi.AuthInfo{
|
||||
ImpersonateUID: "abc123",
|
||||
}
|
||||
test := configValidationTest{
|
||||
config: config,
|
||||
expectedErrorSubstring: []string{
|
||||
`requesting uid, groups or user-extra for user without impersonating a user`,
|
||||
},
|
||||
}
|
||||
test.testAuthInfo("user", t)
|
||||
test.testConfig(t)
|
||||
}
|
||||
|
||||
type configValidationTest struct {
|
||||
config *clientcmdapi.Config
|
||||
expectedErrorSubstring []string
|
||||
|
Loading…
Reference in New Issue
Block a user