Merge pull request #13724 from reasonerjt/http-auth-admin-grp

Support admin group in http authproxy
This commit is contained in:
Wenkai Yin(尹文开) 2020-12-11 13:06:26 +08:00 committed by GitHub
commit 43104ab0b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 109 additions and 25 deletions

View File

@ -129,6 +129,7 @@ var (
{Name: common.HTTPAuthProxyEndpoint, 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.HTTPAuthProxyVerifyCert, Scope: UserScope, Group: HTTPAuthGroup, DefaultValue: "true", ItemType: &BoolType{}},
{Name: common.HTTPAuthProxySkipSearch, Scope: UserScope, Group: HTTPAuthGroup, DefaultValue: "false", ItemType: &BoolType{}},
{Name: common.HTTPAuthProxyServerCertificate, Scope: UserScope, Group: HTTPAuthGroup, ItemType: &StringType{}},

View File

@ -96,6 +96,7 @@ const (
UAAVerifyCert = "uaa_verify_cert"
HTTPAuthProxyEndpoint = "http_authproxy_endpoint"
HTTPAuthProxyTokenReviewEndpoint = "http_authproxy_tokenreview_endpoint"
HTTPAuthProxyAdminGroups = "http_authproxy_admin_groups"
HTTPAuthProxyVerifyCert = "http_authproxy_verify_cert"
HTTPAuthProxySkipSearch = "http_authproxy_skip_search"
HTTPAuthProxyServerCertificate = "http_authproxy_server_certificate"

View File

@ -69,11 +69,12 @@ type Email struct {
// HTTPAuthProxy wraps the settings for HTTP auth proxy
type HTTPAuthProxy struct {
Endpoint string `json:"endpoint"`
TokenReviewEndpoint string `json:"tokenreivew_endpoint"`
VerifyCert bool `json:"verify_cert"`
SkipSearch bool `json:"skip_search"`
ServerCertificate string `json:"server_certificate"`
Endpoint string `json:"endpoint"`
TokenReviewEndpoint string `json:"tokenreivew_endpoint"`
AdminGroups []string `json:"admin_groups"`
VerifyCert bool `json:"verify_cert"`
SkipSearch bool `json:"skip_search"`
ServerCertificate string `json:"server_certificate"`
}
// OIDCSetting wraps the settings for OIDC auth endpoint

View File

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

View File

@ -391,6 +391,7 @@ func HTTPAuthProxySetting() (*models.HTTPAuthProxy, error) {
return &models.HTTPAuthProxy{
Endpoint: cfgMgr.Get(common.HTTPAuthProxyEndpoint).GetString(),
TokenReviewEndpoint: cfgMgr.Get(common.HTTPAuthProxyTokenReviewEndpoint).GetString(),
AdminGroups: splitAndTrim(cfgMgr.Get(common.HTTPAuthProxyAdminGroups).GetString(), ","),
VerifyCert: cfgMgr.Get(common.HTTPAuthProxyVerifyCert).GetBool(),
SkipSearch: cfgMgr.Get(common.HTTPAuthProxySkipSearch).GetBool(),
ServerCertificate: cfgMgr.Get(common.HTTPAuthProxyServerCertificate).GetString(),
@ -405,11 +406,7 @@ func OIDCSetting() (*models.OIDCSetting, error) {
}
scopeStr := cfgMgr.Get(common.OIDCScope).GetString()
extEndpoint := strings.TrimSuffix(cfgMgr.Get(common.ExtEndpoint).GetString(), "/")
scope := []string{}
for _, s := range strings.Split(scopeStr, ",") {
scope = append(scope, strings.TrimSpace(s))
}
scope := splitAndTrim(scopeStr, ",")
return &models.OIDCSetting{
Name: cfgMgr.Get(common.OIDCName).GetString(),
Endpoint: cfgMgr.Get(common.OIDCEndpoint).GetString(),
@ -479,3 +476,13 @@ func Metric() *models.Metric {
Path: cfgMgr.Get(common.MetricPath).GetString(),
}
}
func splitAndTrim(s, sep string) []string {
res := make([]string, 0)
for _, s := range strings.Split(s, sep) {
if e := strings.TrimSpace(s); len(e) > 0 {
res = append(res, e)
}
}
return res
}

View File

@ -238,6 +238,7 @@ y1bQusZMygQezfCuEzsewF+OpANFovCTUEs6s5vyoVNP8lk=
m := map[string]interface{}{
common.HTTPAuthProxySkipSearch: "true",
common.HTTPAuthProxyVerifyCert: "true",
common.HTTPAuthProxyAdminGroups: "group1, group2",
common.HTTPAuthProxyEndpoint: "https://auth.proxy/suffix",
common.HTTPAuthProxyServerCertificate: certificate,
}
@ -246,6 +247,7 @@ y1bQusZMygQezfCuEzsewF+OpANFovCTUEs6s5vyoVNP8lk=
assert.Nil(t, e)
assert.Equal(t, *v, models.HTTPAuthProxy{
Endpoint: "https://auth.proxy/suffix",
AdminGroups: []string{"group1", "group2"},
SkipSearch: true,
VerifyCert: true,
ServerCertificate: certificate,
@ -279,3 +281,35 @@ func TestOIDCSetting(t *testing.T) {
assert.ElementsMatch(t, []string{"openid", "profile"}, v.Scope)
assert.Equal(t, "username", v.UserClaim)
}
func TestSplitAndTrim(t *testing.T) {
cases := []struct {
s string
sep string
expect []string
}{
{
s: "abc",
sep: ",",
expect: []string{"abc"},
},
{
s: "a, b, c",
sep: ",",
expect: []string{"a", "b", "c"},
},
{
s: "a,b,c ",
sep: ".",
expect: []string{"a,b,c"},
},
{
s: "",
sep: ",",
expect: []string{},
},
}
for _, c := range cases {
assert.Equal(t, c.expect, splitAndTrim(c.s, c.sep))
}
}

View File

@ -79,7 +79,8 @@ func getTLSConfig(config *models.HTTPAuthProxy) rest.TLSClientConfig {
// UserFromReviewStatus transform a review status to a user model.
// Group entries will be populated if needed.
func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus) (*models.User, error) {
func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus, adminGroups []string) (*models.User, error) {
if !status.Authenticated {
return nil, fmt.Errorf("failed to authenticate the token, error in status: %s", status.Error)
}
@ -94,6 +95,18 @@ func UserFromReviewStatus(status k8s_api_v1beta1.TokenReviewStatus) (*models.Use
}
log.Debugf("current user's group ID list is %+v", groupIDList)
user.GroupIDs = groupIDList
if len(adminGroups) > 0 {
agm := make(map[string]struct{})
for _, ag := range adminGroups {
agm[ag] = struct{}{}
}
for _, ug := range status.User.Groups {
if _, ok := agm[ug]; ok {
user.AdminRoleInAuth = true
break
}
}
}
}
return user, nil

View File

@ -21,19 +21,22 @@ func TestMain(m *testing.M) {
func TestUserFromReviewStatus(t *testing.T) {
type result struct {
hasErr bool
username string
groupLen int
hasErr bool
username string
groupLen int
adminInAuth bool
}
cases := []struct {
input v1beta1.TokenReviewStatus
expect result
input v1beta1.TokenReviewStatus
adminGroups []string
expect result
}{
{
input: v1beta1.TokenReviewStatus{
Authenticated: false,
Error: "connection error",
},
adminGroups: []string{"admin"},
expect: result{
hasErr: true,
},
@ -46,10 +49,12 @@ func TestUserFromReviewStatus(t *testing.T) {
UID: "u-1",
},
},
adminGroups: []string{"admin"},
expect: result{
hasErr: false,
username: "jack",
groupLen: 0,
hasErr: false,
username: "jack",
groupLen: 0,
adminInAuth: false,
},
},
{
@ -61,21 +66,41 @@ func TestUserFromReviewStatus(t *testing.T) {
},
Error: "",
},
adminGroups: []string{"group2", "admin"},
expect: result{
hasErr: false,
username: "daniel",
groupLen: 2,
hasErr: false,
username: "daniel",
groupLen: 2,
adminInAuth: true,
},
},
{
input: v1beta1.TokenReviewStatus{
Authenticated: true,
User: v1beta1.UserInfo{
Username: "daniel",
Groups: []string{"group1", "group2"},
},
Error: "",
},
adminGroups: []string{},
expect: result{
hasErr: false,
username: "daniel",
groupLen: 2,
adminInAuth: false,
},
},
}
for _, c := range cases {
u, err := UserFromReviewStatus(c.input)
u, err := UserFromReviewStatus(c.input, c.adminGroups)
if c.expect.hasErr == true {
assert.NotNil(t, err)
} else {
assert.Nil(t, err)
assert.Equal(t, c.expect.username, u.Username)
assert.Equal(t, c.expect.groupLen, len(u.GroupIDs))
assert.Equal(t, c.expect.adminInAuth, u.AdminRoleInAuth)
}
if u != nil {
for _, gid := range u.GroupIDs {

View File

@ -86,12 +86,13 @@ func (a *authProxy) Generate(req *http.Request) security.Context {
return nil
}
}
u2, err := authproxy.UserFromReviewStatus(tokenReviewStatus)
u2, err := authproxy.UserFromReviewStatus(tokenReviewStatus, httpAuthProxyConf.AdminGroups)
if err != nil {
log.Errorf("failed to get user information from token review status: %v", err)
return nil
}
user.GroupIDs = u2.GroupIDs
user.AdminRoleInAuth = u2.AdminRoleInAuth
log.Debugf("an auth proxy security context generated for request %s %s", req.Method, req.URL.Path)
return local.NewSecurityContext(user)
}

View File

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