Skip to content

Commit

Permalink
feat(auth): cherry pick industry to master (#2084)
Browse files Browse the repository at this point in the history
* feat(auth): support cloud industry oidc and accounts (#1878)

Co-authored-by: caryxychen <caryxychen@tencent.com>

* fix(auth): authz bug fix (#1916)

Co-authored-by: caryxychen <caryxychen@tencent.com>

* fix(auth): fix auth redirect bug (#1925)

* fix(auth): authz bug fix

* fix(auth): fix auth redirect bug

Co-authored-by: caryxychen <caryxychen@tencent.com>

* feat(gateway): logout redirect (#1933)

* fix(auth): authz bug fix

* fix(auth): fix auth redirect bug

* feat(gateway): logout redirect

Co-authored-by: caryxychen <caryxychen@tencent.com>

* fix(registry): fix registry login bug  (#1971)

* fix(auth): authz bug fix

* fix(auth): fix auth redirect bug

* feat(gateway): logout redirect

* fix(registry): fix registry login bug

Co-authored-by: caryxychen <caryxychen@tencent.com>

* fix(auth): apiKey authn failed (#2006)

* fix(auth): authz bug fix

* fix(auth): fix auth redirect bug

* feat(gateway): logout redirect

* fix(registry): fix registry login bug

* fix(auth): apiKey authn failed

Co-authored-by: caryxychen <caryxychen@tencent.com>

* feat(cloudindustry): support searchKey and limit (#2076)

* feat(cloudindustry): support searchKey and limit

* feat(cloudindustry): support searchKey and limit

Co-authored-by: caryxychen <caryxychen@tencent.com>

* fix(auth): fix pmd

* fix(auth): fix authz pmd

Co-authored-by: caryxychen <caryxychen@tencent.com>
  • Loading branch information
caryxychen and caryxychen authored Oct 12, 2022
1 parent 917006a commit 8978358
Show file tree
Hide file tree
Showing 29 changed files with 620 additions and 136 deletions.
50 changes: 40 additions & 10 deletions cmd/tke-auth-api/app/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"regexp"
"strings"
"time"
"tkestack.io/tke/pkg/auth/authentication/oidc/identityprovider/cloudindustry"

"github.com/casbin/casbin/v2"
casbinlog "github.com/casbin/casbin/v2/log"
Expand Down Expand Up @@ -185,6 +186,11 @@ func CreateConfigFromOptions(serverName string, opts *options.Options) (*Config,
if err != nil {
return nil, err
}
case cloudindustry.ConnectorType:
err = setupCloudIndustryConnector(opts.Auth)
if err != nil {
return nil, err
}
default:
log.Warn("Unknown init tenant type", log.String("type", opts.Auth.InitTenantType))
}
Expand Down Expand Up @@ -362,11 +368,30 @@ func setupLDAPConnector(auth *options.AuthOptions) error {
if err != nil {
return err
}
identityprovider.SetIdentityProvider(auth.InitTenantID, idp)
return nil
}

func setupCloudIndustryConnector(auth *options.AuthOptions) error {
log.Info("setup cloud industry connector", log.Any("tenantID", auth.InitTenantID))
const errFmt = "failed to load cloud industry config file %s, error %v"
// compute absolute path based on current working dir
cloudIndustryConfigFile, err := filepath.Abs(auth.CloudIndustryConfigFile)
if err != nil {
return fmt.Errorf(errFmt, cloudIndustryConfigFile, err)
}

if _, ok := identityprovider.GetIdentityProvider(auth.InitTenantID); !ok {
identityprovider.SetIdentityProvider(auth.InitTenantID, idp)
bytes, err := ioutil.ReadFile(cloudIndustryConfigFile)
var sdkConfig cloudindustry.SDKConfig
if err := json.Unmarshal(bytes, &sdkConfig); err != nil {
return fmt.Errorf(errFmt, cloudIndustryConfigFile, err)
}

idp, err := cloudindustry.NewCloudIndustryIdentityProvider(auth.InitTenantID, auth.InitIDPAdmins, &sdkConfig)
if err != nil {
return err
}
identityprovider.SetIdentityProvider(auth.InitTenantID, idp)
return nil
}

Expand All @@ -383,20 +408,25 @@ func setupDefaultClient(store dexstorage.Storage, auth *options.AuthOptions) err
continue
}
}
cli := dexstorage.Client{
ID: auth.InitClientID,
Secret: auth.InitClientSecret,
Name: auth.InitClientID,
RedirectURIs: auth.InitClientRedirectUris,
}
if !exists {
cli := dexstorage.Client{
ID: auth.InitClientID,
Secret: auth.InitClientSecret,
Name: auth.InitClientID,
RedirectURIs: auth.InitClientRedirectUris,
}

// Create a default connector
if err = store.CreateClient(cli); err != nil && err != dexstorage.ErrAlreadyExists {
return err
}
} else {
// Update default connector
if err = store.UpdateClient(auth.InitClientID, func(old dexstorage.Client) (dexstorage.Client, error) {
return cli, nil
}); err != nil {
return err
}
}

return nil
}

Expand Down
76 changes: 45 additions & 31 deletions cmd/tke-auth-api/app/options/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package options
import (
"fmt"
"time"
"tkestack.io/tke/pkg/auth/authentication/oidc/identityprovider/cloudindustry"

"tkestack.io/tke/pkg/auth/authentication/oidc/identityprovider/ldap"
"tkestack.io/tke/pkg/auth/authentication/oidc/identityprovider/local"
Expand All @@ -30,43 +31,46 @@ import (
)

const (
flagAuthAssetsPath = "assets-path"
flagAuthIDTokenTimeout = "id-token-timeout"
flagAuthInitTenantType = "init-tenant-type"
flagAuthInitTenantID = "init-tenant-id"
flagAuthInitIDPAdmins = "init-idp-administrators"
flagAuthLDAPConfigFile = "ldap-config-file"
flagAuthInitClientID = "init-client-id"
flagAuthInitClientSecret = "init-client-secret"
flagAuthInitClientRedirectUris = "init-client-redirect-uris"
flagAuthPasswordGrantConnID = "password-grant-conn-id"
flagAuthAssetsPath = "assets-path"
flagAuthIDTokenTimeout = "id-token-timeout"
flagAuthInitTenantType = "init-tenant-type"
flagAuthInitTenantID = "init-tenant-id"
flagAuthInitIDPAdmins = "init-idp-administrators"
flagAuthLDAPConfigFile = "ldap-config-file"
flagAuthCloudIndustryConfigFile = "cloudindustry-config-file"
flagAuthInitClientID = "init-client-id"
flagAuthInitClientSecret = "init-client-secret"
flagAuthInitClientRedirectUris = "init-client-redirect-uris"
flagAuthPasswordGrantConnID = "password-grant-conn-id"
)

const (
configAuthAssetsPath = "auth.assets_path"
configAuthIDTokenTimeout = "auth.id_token_timeout"
configAuthInitTenantType = "auth.init_tenant_type"
configAuthInitTenantID = "auth.init_tenant_id"
configAuthInitIDPAdmins = "auth.init_idp_administrators"
configAuthLDAPConfigFile = "auth.ldap_config_file"
configAuthInitClientID = "auth.init_client_id"
configAuthInitClientSecret = "auth.init_client_secret"
configAuthInitClientRedirectUris = "auth.init_client_redirect_uris"
configAuthPasswordGrantConnID = "auth.password_grant_conn_id"
configAuthAssetsPath = "auth.assets_path"
configAuthIDTokenTimeout = "auth.id_token_timeout"
configAuthInitTenantType = "auth.init_tenant_type"
configAuthInitTenantID = "auth.init_tenant_id"
configAuthInitIDPAdmins = "auth.init_idp_administrators"
configAuthLDAPConfigFile = "auth.ldap_config_file"
configAuthCloudIndustryConfigFile = "auth.cloudindustry_config_file"
configAuthInitClientID = "auth.init_client_id"
configAuthInitClientSecret = "auth.init_client_secret"
configAuthInitClientRedirectUris = "auth.init_client_redirect_uris"
configAuthPasswordGrantConnID = "auth.password_grant_conn_id"
)

// AuthOptions contains configuration items related to auth attributes.
type AuthOptions struct {
AssetsPath string
IDTokenTimeout time.Duration
InitTenantType string
InitTenantID string
InitIDPAdmins []string
LdapConfigFile string
InitClientID string
InitClientSecret string
InitClientRedirectUris []string
PasswordGrantConnID string
AssetsPath string
IDTokenTimeout time.Duration
InitTenantType string
InitTenantID string
InitIDPAdmins []string
LdapConfigFile string
CloudIndustryConfigFile string
InitClientID string
InitClientSecret string
InitClientRedirectUris []string
PasswordGrantConnID string
}

// NewAuthOptions creates a AuthOptions object with default parameters.
Expand Down Expand Up @@ -102,9 +106,13 @@ func (o *AuthOptions) AddFlags(fs *pflag.FlagSet) {
_ = viper.BindPFlag(configAuthInitIDPAdmins, fs.Lookup(flagAuthInitIDPAdmins))

fs.String(flagAuthLDAPConfigFile, o.LdapConfigFile,
"Config file path for ldap ldap, must specify if init-tenant-type is ldap.")
"Config file path for ldap identity provider, must specify if init-tenant-type is ldap.")
_ = viper.BindPFlag(configAuthLDAPConfigFile, fs.Lookup(flagAuthLDAPConfigFile))

fs.String(flagAuthCloudIndustryConfigFile, o.CloudIndustryConfigFile,
"Config file path for cloud industry identity provider, must specify if init-tenant-type is cloudindustry.")
_ = viper.BindPFlag(configAuthCloudIndustryConfigFile, fs.Lookup(flagAuthCloudIndustryConfigFile))

fs.String(flagAuthInitClientID, o.InitClientID,
"Default client id will be created when started.")
_ = viper.BindPFlag(configAuthInitClientID, fs.Lookup(flagAuthInitClientID))
Expand Down Expand Up @@ -140,6 +148,12 @@ func (o *AuthOptions) ApplyFlags() []error {
if o.InitTenantType == ldap.ConnectorType && o.LdapConfigFile == "" {
errs = append(errs, fmt.Errorf("--%s must be specified for ldap type tenant", flagAuthLDAPConfigFile))
}

o.CloudIndustryConfigFile = viper.GetString(configAuthCloudIndustryConfigFile)
if o.InitTenantType == cloudindustry.ConnectorType && o.CloudIndustryConfigFile == "" {
errs = append(errs, fmt.Errorf("--%s must be specified for ldap type tenant", flagAuthCloudIndustryConfigFile))
}

o.InitTenantID = viper.GetString(configAuthInitTenantID)
if len(o.InitTenantID) == 0 {
errs = append(errs, fmt.Errorf("--%s must be specified", flagAuthInitTenantID))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ data:
username_prefix ="-"
username_claim = "name"
tenantid_claim = "federated_claims"
token_review_path = "/auth/authn"
token_review_path = "https://tke-auth-api/auth/authn"
{{- else }}
client_id = {{ .Values.api.oIDCClientID }}
issuer_url = {{ .Values.api.oIDCIssuerURL }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ data:
username_prefix ="-"
username_claim = "name"
tenantid_claim = "federated_claims"
token_review_path = "/auth/authn"
token_review_path = "https://tke-auth-api/auth/authn"
{{- else }}
client_id = {{ .OIDCClientID }}
issuer_url = {{ .OIDCIssuerURL }}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require (
github.com/aws/aws-sdk-go v1.40.37
github.com/bitly/go-simplejson v0.5.0
github.com/caddyserver/caddy v1.0.5
github.com/caryxychen/cloudindustry-sdk-go v1.0.1
github.com/casbin/casbin/v2 v2.2.1
github.com/chartmuseum/helm-push v0.9.0
github.com/chartmuseum/storage v0.11.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3k
github.com/caddyserver/caddy v1.0.5 h1:5B1Hs0UF2x2tggr2X9jL2qOZtDXbIWQb9YLbmlxHSuM=
github.com/caddyserver/caddy v1.0.5/go.mod h1:AnFHB+/MrgRC+mJAvuAgQ38ePzw+wKeW0wzENpdQQKY=
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
github.com/caryxychen/cloudindustry-sdk-go v1.0.1 h1:hHJ8G9nZLl4bLiTfpdvGycc9u2nXGlKx4iWKedAmj0U=
github.com/caryxychen/cloudindustry-sdk-go v1.0.1/go.mod h1:ZqQXwtco2MGa2s6MAP6snl5bg/M+0rufb7E8zDuCn1M=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/casbin/casbin/v2 v2.2.1 h1:ijrSMfBfbQlDc4LnMTGtGYWmhKuuR6RLSQRj8vHrMzc=
github.com/casbin/casbin/v2 v2.2.1/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
Expand Down
25 changes: 8 additions & 17 deletions pkg/apiserver/authentication/authenticator/apikey/apikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,46 +21,37 @@ package apikey
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/url"

jsoniter "github.com/json-iterator/go"
"io/ioutil"
v1 "k8s.io/api/authentication/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
"net/http"
"tkestack.io/tke/pkg/util/log"
"tkestack.io/tke/pkg/util/transport"
)

var json = jsoniter.ConfigCompatibleWithStandardLibrary

type Options struct {
OIDCIssuerURL string
OIDCCAFile string
TokenReviewPath string
AdminUsername string
AdminPassword string
TokenReviewCAFile string
TokenReviewURL string
AdminUsername string
AdminPassword string
}

// NewAPIKeyAuthenticator creates a request auth authenticator and returns it.
func NewAPIKeyAuthenticator(opts *Options) (authenticator.Request, error) {
issuerURL, err := url.Parse(opts.OIDCIssuerURL)
if err != nil {
return nil, err
}
issuerURL.Path = opts.TokenReviewPath

tr, err := transport.NewOneWayTLSTransport(opts.OIDCCAFile, true)
tr, err := transport.NewOneWayTLSTransport(opts.TokenReviewCAFile, true)
if err != nil {
return nil, err
}

return &Authenticator{
adminUsername: opts.AdminUsername,
adminPassword: opts.AdminPassword,
tokenReviewURL: issuerURL.String(),
tokenReviewURL: opts.TokenReviewURL,
tokenReviewTransport: tr,
}, nil
}
Expand Down
19 changes: 19 additions & 0 deletions pkg/apiserver/authentication/authenticator/oidc/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,25 @@ type ProviderJSON struct {
UserInfoURL string `json:"userinfo_endpoint"`
}

// staticKeySet implements oidc.KeySet.
type staticKeySet struct {}

func parseJWT(p string) ([]byte, error) {
parts := strings.Split(p, ".")
if len(parts) < 2 {
return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts))
}
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err)
}
return payload, nil
}

func (s *staticKeySet) VerifySignature(ctx context.Context, jwt string) (payload []byte, err error) {
return parseJWT(jwt)
}

// NewIDTokenVerifier uses the OpenID Connect discovery mechanism to construct a verifier manually from a issuer URL.
// The issuer is the URL identifier for the service. For example: "https://accounts.google.com"
// or "https://login.salesforce.com".
Expand Down
10 changes: 10 additions & 0 deletions pkg/apiserver/filter/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,16 @@ func GroupWithProject(project string) string {
return fmt.Sprintf("project:%s", project)
}

func FindValueFromGroups(groups []string, key string) (bool, string) {
prefix := fmt.Sprintf("%s:", key)
for _, group := range groups {
if strings.HasPrefix(group, prefix) {
return true, strings.TrimPrefix(group, prefix)
}
}
return false, ""
}

func GetValueFromGroups(groups []string, key string) string {
prefix := fmt.Sprintf("%s:", key)
for _, group := range groups {
Expand Down
4 changes: 3 additions & 1 deletion pkg/auth/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"net/http"
"time"
"tkestack.io/tke/pkg/auth/authentication/oidc/identityprovider/cloudindustry"

"tkestack.io/tke/api/auth"
"tkestack.io/tke/pkg/auth/authentication/oidc/identityprovider"
Expand Down Expand Up @@ -228,11 +229,12 @@ func (c completedConfig) registerHooks(dexHandler *identityprovider.DexHander, s

localIdpHook := local.NewLocalHookHandler(authClient)
ldapIdpHook := ldap.NewLdapHookHandler(authClient)
cloudIndustryIdpHook := cloudindustry.NewCloudIndustryHookHandler(authClient)

authVersionedClient := versionedclientset.NewForConfigOrDie(s.LoopbackClientConfig)
adapterHook := local2.NewAdapterHookHandler(authVersionedClient, c.ExtraConfig.CasbinEnforcer, c.ExtraConfig.VersionedInformers, c.ExtraConfig.CasbinReloadInterval)

return []genericapiserver.PostStartHookProvider{dexHook, apiSigningKeyHook, localIdpHook, ldapIdpHook, adapterHook}
return []genericapiserver.PostStartHookProvider{dexHook, apiSigningKeyHook, localIdpHook, ldapIdpHook, cloudIndustryIdpHook, adapterHook}
}

// installCasbinPreStopHook is used to register preStop hook to stop casbin enforcer sync.
Expand Down
17 changes: 8 additions & 9 deletions pkg/auth/authentication/authenticator/apikey.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,14 @@ func (h *APIKeyAuthenticator) AuthenticateToken(ctx context.Context, token strin

user, err := util.GetUserByName(ctx, h.authClient, tokenInfo.TenantID, info.Name)
if err != nil {
log.Error("Get localIdentity failed", log.String("localIdentity", info.Name), log.Err(err))
return nil, false, err
}

info.UID = user.ObjectMeta.Name
groups, err := util.GetGroupsForUser(ctx, h.authClient, user.ObjectMeta.Name)
if err == nil {
for _, g := range groups.Items {
info.Groups = append(info.Groups, g.ObjectMeta.Name)
log.Warnf("Get localIdentity failed", log.String("localIdentity", info.Name), log.Err(err))
} else {
info.UID = user.ObjectMeta.Name
groups, err := util.GetGroupsForUser(ctx, h.authClient, user.ObjectMeta.Name)
if err == nil {
for _, g := range groups.Items {
info.Groups = append(info.Groups, g.ObjectMeta.Name)
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion pkg/auth/authentication/oidc/claims/claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type IDTokenClaims struct {

// FederatedIDClaims represents the extension struct of claims.
type FederatedIDClaims struct {
// 租户ID
ConnectorID string `json:"connector_id,omitempty"`
UserID string `json:"user_id,omitempty"`
// 用户ID
UserID string `json:"user_id,omitempty"`
}
Loading

0 comments on commit 8978358

Please sign in to comment.