Add attribute admin username for authproxy

This commit adds the attribute "http_authproxy_admin_usernames", which
is string that contains usernames separated by comma, when a user logs
in and the username in the tokenreview status matches the setting of
this attribute, the user will have administrator permission.

Signed-off-by: Daniel Jiang <jiangd@vmware.com>
This commit is contained in:
Daniel Jiang 2021-04-07 14:11:39 +08:00
parent 85c08d62a4
commit ad8eee8623
10 changed files with 32 additions and 15 deletions

View File

@ -130,6 +130,7 @@ var (
{Name: common.HTTPAuthProxyEndpoint, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}}, {Name: common.HTTPAuthProxyEndpoint, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}},
{Name: common.HTTPAuthProxyTokenReviewEndpoint, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}}, {Name: common.HTTPAuthProxyTokenReviewEndpoint, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}},
{Name: common.HTTPAuthProxyAdminGroups, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}}, {Name: common.HTTPAuthProxyAdminGroups, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}},
{Name: common.HTTPAuthProxyAdminUsernames, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}},
{Name: common.HTTPAuthProxyVerifyCert, Scope: UserScope, Group: HTTPAuthGroup, DefaultValue: "true", ItemType: &BoolType{}}, {Name: common.HTTPAuthProxyVerifyCert, Scope: UserScope, Group: HTTPAuthGroup, DefaultValue: "true", ItemType: &BoolType{}},
{Name: common.HTTPAuthProxySkipSearch, Scope: UserScope, Group: HTTPAuthGroup, DefaultValue: "false", ItemType: &BoolType{}}, {Name: common.HTTPAuthProxySkipSearch, Scope: UserScope, Group: HTTPAuthGroup, DefaultValue: "false", ItemType: &BoolType{}},
{Name: common.HTTPAuthProxyServerCertificate, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}}, {Name: common.HTTPAuthProxyServerCertificate, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}},

View File

@ -97,6 +97,7 @@ const (
HTTPAuthProxyEndpoint = "http_authproxy_endpoint" HTTPAuthProxyEndpoint = "http_authproxy_endpoint"
HTTPAuthProxyTokenReviewEndpoint = "http_authproxy_tokenreview_endpoint" HTTPAuthProxyTokenReviewEndpoint = "http_authproxy_tokenreview_endpoint"
HTTPAuthProxyAdminGroups = "http_authproxy_admin_groups" HTTPAuthProxyAdminGroups = "http_authproxy_admin_groups"
HTTPAuthProxyAdminUsernames = "http_authproxy_admin_usernames"
HTTPAuthProxyVerifyCert = "http_authproxy_verify_cert" HTTPAuthProxyVerifyCert = "http_authproxy_verify_cert"
HTTPAuthProxySkipSearch = "http_authproxy_skip_search" HTTPAuthProxySkipSearch = "http_authproxy_skip_search"
HTTPAuthProxyServerCertificate = "http_authproxy_server_certificate" HTTPAuthProxyServerCertificate = "http_authproxy_server_certificate"

View File

@ -72,6 +72,7 @@ type HTTPAuthProxy struct {
Endpoint string `json:"endpoint"` Endpoint string `json:"endpoint"`
TokenReviewEndpoint string `json:"tokenreivew_endpoint"` TokenReviewEndpoint string `json:"tokenreivew_endpoint"`
AdminGroups []string `json:"admin_groups"` AdminGroups []string `json:"admin_groups"`
AdminUsernames []string `json:"admin_usernames"`
VerifyCert bool `json:"verify_cert"` VerifyCert bool `json:"verify_cert"`
SkipSearch bool `json:"skip_search"` SkipSearch bool `json:"skip_search"`
ServerCertificate string `json:"server_certificate"` ServerCertificate string `json:"server_certificate"`

View File

@ -116,7 +116,7 @@ func (a *Auth) tokenReview(sessionID string) (*models.User, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
u, err := authproxy.UserFromReviewStatus(reviewStatus, httpAuthProxySetting.AdminGroups) u, err := authproxy.UserFromReviewStatus(reviewStatus, httpAuthProxySetting.AdminGroups, httpAuthProxySetting.AdminUsernames)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -393,6 +393,7 @@ func HTTPAuthProxySetting() (*models.HTTPAuthProxy, error) {
Endpoint: cfgMgr.Get(common.HTTPAuthProxyEndpoint).GetString(), Endpoint: cfgMgr.Get(common.HTTPAuthProxyEndpoint).GetString(),
TokenReviewEndpoint: cfgMgr.Get(common.HTTPAuthProxyTokenReviewEndpoint).GetString(), TokenReviewEndpoint: cfgMgr.Get(common.HTTPAuthProxyTokenReviewEndpoint).GetString(),
AdminGroups: splitAndTrim(cfgMgr.Get(common.HTTPAuthProxyAdminGroups).GetString(), ","), AdminGroups: splitAndTrim(cfgMgr.Get(common.HTTPAuthProxyAdminGroups).GetString(), ","),
AdminUsernames: splitAndTrim(cfgMgr.Get(common.HTTPAuthProxyAdminUsernames).GetString(), ","),
VerifyCert: cfgMgr.Get(common.HTTPAuthProxyVerifyCert).GetBool(), VerifyCert: cfgMgr.Get(common.HTTPAuthProxyVerifyCert).GetBool(),
SkipSearch: cfgMgr.Get(common.HTTPAuthProxySkipSearch).GetBool(), SkipSearch: cfgMgr.Get(common.HTTPAuthProxySkipSearch).GetBool(),
ServerCertificate: cfgMgr.Get(common.HTTPAuthProxyServerCertificate).GetString(), ServerCertificate: cfgMgr.Get(common.HTTPAuthProxyServerCertificate).GetString(),

View File

@ -248,6 +248,7 @@ y1bQusZMygQezfCuEzsewF+OpANFovCTUEs6s5vyoVNP8lk=
assert.Equal(t, *v, models.HTTPAuthProxy{ assert.Equal(t, *v, models.HTTPAuthProxy{
Endpoint: "https://auth.proxy/suffix", Endpoint: "https://auth.proxy/suffix",
AdminGroups: []string{"group1", "group2"}, AdminGroups: []string{"group1", "group2"},
AdminUsernames: []string{},
SkipSearch: true, SkipSearch: true,
VerifyCert: true, VerifyCert: true,
ServerCertificate: certificate, ServerCertificate: certificate,

View File

@ -78,14 +78,21 @@ func getTLSConfig(config *models.HTTPAuthProxy) rest.TLSClientConfig {
// UserFromReviewStatus transform a review status to a user model. // UserFromReviewStatus transform a review status to a user model.
// Group entries will be populated if needed. // Group entries will be populated if needed.
func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus, adminGroups []string) (*models.User, error) { func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus, adminGroups []string, adminUsernames []string) (*models.User, error) {
if !status.Authenticated { if !status.Authenticated {
return nil, fmt.Errorf("failed to authenticate the token, error in status: %s", status.Error) return nil, fmt.Errorf("failed to authenticate the token, error in status: %s", status.Error)
} }
user := &models.User{ user := &models.User{
Username: status.User.Username, Username: status.User.Username,
} }
for _, au := range adminUsernames {
if status.User.Username == au {
log.Debugf("Username: %s in the adminusers list, assigning user admin permission", au)
user.AdminRoleInAuth = true
break
}
}
if len(status.User.Groups) > 0 { if len(status.User.Groups) > 0 {
userGroups := models.UserGroupsFromName(status.User.Groups, common.HTTPGroupType) userGroups := models.UserGroupsFromName(status.User.Groups, common.HTTPGroupType)
groupIDList, err := group.PopulateGroup(userGroups) groupIDList, err := group.PopulateGroup(userGroups)
@ -94,7 +101,7 @@ func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus, adminGroups
} }
log.Debugf("current user's group ID list is %+v", groupIDList) log.Debugf("current user's group ID list is %+v", groupIDList)
user.GroupIDs = groupIDList user.GroupIDs = groupIDList
if len(adminGroups) > 0 { if len(adminGroups) > 0 && !user.AdminRoleInAuth { // skip checking admin group if user already has admin role
agm := make(map[string]struct{}) agm := make(map[string]struct{})
for _, ag := range adminGroups { for _, ag := range adminGroups {
agm[ag] = struct{}{} agm[ag] = struct{}{}
@ -108,5 +115,4 @@ func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus, adminGroups
} }
} }
return user, nil return user, nil
} }

View File

@ -27,16 +27,18 @@ func TestUserFromReviewStatus(t *testing.T) {
adminInAuth bool adminInAuth bool
} }
cases := []struct { cases := []struct {
input v1beta1.TokenReviewStatus input v1beta1.TokenReviewStatus
adminGroups []string adminGroups []string
expect result adminUsernames []string
expect result
}{ }{
{ {
input: v1beta1.TokenReviewStatus{ input: v1beta1.TokenReviewStatus{
Authenticated: false, Authenticated: false,
Error: "connection error", Error: "connection error",
}, },
adminGroups: []string{"admin"}, adminGroups: []string{"admin"},
adminUsernames: []string{},
expect: result{ expect: result{
hasErr: true, hasErr: true,
}, },
@ -49,7 +51,8 @@ func TestUserFromReviewStatus(t *testing.T) {
UID: "u-1", UID: "u-1",
}, },
}, },
adminGroups: []string{"admin"}, adminGroups: []string{"admin"},
adminUsernames: []string{},
expect: result{ expect: result{
hasErr: false, hasErr: false,
username: "jack", username: "jack",
@ -66,7 +69,8 @@ func TestUserFromReviewStatus(t *testing.T) {
}, },
Error: "", Error: "",
}, },
adminGroups: []string{"group2", "admin"}, adminGroups: []string{"group2", "admin"},
adminUsernames: []string{},
expect: result{ expect: result{
hasErr: false, hasErr: false,
username: "daniel", username: "daniel",
@ -83,17 +87,18 @@ func TestUserFromReviewStatus(t *testing.T) {
}, },
Error: "", Error: "",
}, },
adminGroups: []string{}, adminGroups: []string{},
adminUsernames: []string{"daniel", "admin"},
expect: result{ expect: result{
hasErr: false, hasErr: false,
username: "daniel", username: "daniel",
groupLen: 2, groupLen: 2,
adminInAuth: false, adminInAuth: true,
}, },
}, },
} }
for _, c := range cases { for _, c := range cases {
u, err := UserFromReviewStatus(c.input, c.adminGroups) u, err := UserFromReviewStatus(c.input, c.adminGroups, c.adminUsernames)
if c.expect.hasErr == true { if c.expect.hasErr == true {
assert.NotNil(t, err) assert.NotNil(t, err)
} else { } else {

View File

@ -86,7 +86,7 @@ func (a *authProxy) Generate(req *http.Request) security.Context {
return nil return nil
} }
} }
u2, err := authproxy.UserFromReviewStatus(tokenReviewStatus, httpAuthProxyConf.AdminGroups) u2, err := authproxy.UserFromReviewStatus(tokenReviewStatus, httpAuthProxyConf.AdminGroups, httpAuthProxyConf.AdminUsernames)
if err != nil { if err != nil {
log.Errorf("failed to get user information from token review status: %v", err) log.Errorf("failed to get user information from token review status: %v", err)
return nil return nil

View File

@ -58,6 +58,7 @@ func TestAuthProxy(t *testing.T) {
VerifyCert: false, VerifyCert: false,
TokenReviewEndpoint: server.URL, TokenReviewEndpoint: server.URL,
AdminGroups: []string{}, AdminGroups: []string{},
AdminUsernames: []string{},
}) })
// No onboard // No onboard