Skip to content

Commit

Permalink
Refactor authenticator
Browse files Browse the repository at this point in the history
Signed-off-by: hongming <[email protected]>
  • Loading branch information
wansir committed Sep 17, 2021
1 parent 83df7d1 commit 4b5b1c6
Show file tree
Hide file tree
Showing 41 changed files with 1,910 additions and 745 deletions.
5 changes: 3 additions & 2 deletions cmd/controller-manager/app/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ import (

"kubesphere.io/kubesphere/pkg/controller/storage/snapshotclass"

"kubesphere.io/kubesphere/pkg/apiserver/authentication"

iamv1alpha2 "kubesphere.io/api/iam/v1alpha2"

authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
"kubesphere.io/kubesphere/pkg/controller/certificatesigningrequest"
"kubesphere.io/kubesphere/pkg/controller/cluster"
"kubesphere.io/kubesphere/pkg/controller/clusterrolebinding"
Expand Down Expand Up @@ -62,7 +63,7 @@ func addControllers(
s3Client s3.Interface,
ldapClient ldapclient.Interface,
options *k8s.KubernetesOptions,
authenticationOptions *authoptions.AuthenticationOptions,
authenticationOptions *authentication.Options,
multiClusterOptions *multicluster.Options,
networkOptions *network.Options,
serviceMeshEnabled bool,
Expand Down
7 changes: 4 additions & 3 deletions cmd/controller-manager/app/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ import (
"strings"
"time"

"kubesphere.io/kubesphere/pkg/apiserver/authentication"

"k8s.io/apimachinery/pkg/labels"

"github.com/spf13/pflag"
"k8s.io/client-go/tools/leaderelection"
cliflag "k8s.io/component-base/cli/flag"
"k8s.io/klog"

authoptions "kubesphere.io/kubesphere/pkg/apiserver/authentication/options"
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
"kubesphere.io/kubesphere/pkg/simple/client/gateway"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
Expand All @@ -44,7 +45,7 @@ type KubeSphereControllerManagerOptions struct {
KubernetesOptions *k8s.KubernetesOptions
DevopsOptions *jenkins.Options
S3Options *s3.Options
AuthenticationOptions *authoptions.AuthenticationOptions
AuthenticationOptions *authentication.Options
LdapOptions *ldapclient.Options
OpenPitrixOptions *openpitrix.Options
NetworkOptions *network.Options
Expand Down Expand Up @@ -75,7 +76,7 @@ func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions
NetworkOptions: network.NewNetworkOptions(),
MultiClusterOptions: multicluster.NewOptions(),
ServiceMeshOptions: servicemesh.NewServiceMeshOptions(),
AuthenticationOptions: authoptions.NewAuthenticateOptions(),
AuthenticationOptions: authentication.NewOptions(),
GatewayOptions: gateway.NewGatewayOptions(),
LeaderElection: &leaderelection.LeaderElectionConfig{
LeaseDuration: 30 * time.Second,
Expand Down
7 changes: 7 additions & 0 deletions cmd/ks-apiserver/app/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"flag"
"fmt"

"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"

"k8s.io/client-go/kubernetes/scheme"
cliflag "k8s.io/component-base/cli/flag"
"k8s.io/klog"
Expand Down Expand Up @@ -238,6 +240,11 @@ func (s *ServerRunOptions) NewAPIServer(stopCh <-chan struct{}) (*apiserver.APIS
klog.Fatalf("unable to create controller runtime client: %v", err)
}

apiServer.Issuer, err = token.NewIssuer(s.AuthenticationOptions)
if err != nil {
klog.Fatalf("unable to create issuer: %v", err)
}

apiServer.Server = server

return apiServer, nil
Expand Down
66 changes: 25 additions & 41 deletions pkg/apiserver/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import (
rt "runtime"
"time"

"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"

"kubesphere.io/kubesphere/pkg/apiserver/authorization"

"kubesphere.io/api/notification/v2beta1"

openpitrixv2alpha1 "kubesphere.io/kubesphere/pkg/kapis/openpitrix/v2alpha1"
Expand All @@ -48,13 +52,12 @@ import (

audit "kubesphere.io/kubesphere/pkg/apiserver/auditing"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/basic"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/jwttoken"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/authenticators/jwt"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/request/anonymous"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/request/basictoken"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/request/bearertoken"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizer"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/authorizerfactory"
authorizationoptions "kubesphere.io/kubesphere/pkg/apiserver/authorization/options"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/path"
"kubesphere.io/kubesphere/pkg/apiserver/authorization/rbac"
unionauthorizer "kubesphere.io/kubesphere/pkg/apiserver/authorization/union"
Expand Down Expand Up @@ -107,23 +110,10 @@ import (
utilnet "kubesphere.io/kubesphere/pkg/utils/net"
)

const (
// ApiRootPath defines the root path of all KubeSphere apis.
ApiRootPath = "/kapis"

// MimeMergePatchJson is the mime header used in merge request
MimeMergePatchJson = "application/merge-patch+json"

//
MimeJsonPatchJson = "application/json-patch+json"
)

type APIServer struct {

// number of kubesphere apiserver
ServerCount int

//
Server *http.Server

Config *apiserverconfig.Config
Expand All @@ -146,13 +136,10 @@ type APIServer struct {

MetricsClient monitoring.Interface

//
LoggingClient logging.Client

//
DevopsClient devops.Interface

//
S3Client s3.Interface

SonarClient sonarqube.SonarInterface
Expand All @@ -165,6 +152,10 @@ type APIServer struct {

// controller-runtime cache
RuntimeCache runtimecache.Cache

// entity that issues tokens
Issuer token.Issuer

// controller-runtime client
RuntimeClient runtimeclient.Client
}
Expand All @@ -178,7 +169,6 @@ func (s *APIServer) PrepareRun(stopCh <-chan struct{}) error {
})

s.installKubeSphereAPIs()

s.installMetricsAPI()
s.container.Filter(monitorRequest)

Expand Down Expand Up @@ -246,19 +236,12 @@ func (s *APIServer) installKubeSphereAPIs() {
group.New(s.InformerFactory, s.KubernetesClient.KubeSphere(), s.KubernetesClient.Kubernetes()),
rbacAuthorizer))

userLister := s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister()
urlruntime.Must(oauth.AddToContainer(s.container, imOperator,
auth.NewTokenOperator(
s.CacheClient,
s.Config.AuthenticationOptions),
auth.NewPasswordAuthenticator(
s.KubernetesClient.KubeSphere(),
s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister(),
s.Config.AuthenticationOptions),
auth.NewOAuthAuthenticator(s.KubernetesClient.KubeSphere(),
s.InformerFactory.KubeSphereSharedInformerFactory(),
s.Config.AuthenticationOptions),
auth.NewLoginRecorder(s.KubernetesClient.KubeSphere(),
s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister()),
auth.NewTokenOperator(s.CacheClient, s.Issuer, s.Config.AuthenticationOptions),
auth.NewPasswordAuthenticator(userLister, s.Config.AuthenticationOptions),
auth.NewOAuthAuthenticator(userLister, s.Config.AuthenticationOptions),
auth.NewLoginRecorder(s.KubernetesClient.KubeSphere(), userLister),
s.Config.AuthenticationOptions))
urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.Config.ServiceMeshOptions, s.container, s.KubernetesClient.Kubernetes(), s.CacheClient))
urlruntime.Must(networkv1alpha2.AddToContainer(s.container, s.Config.NetworkOptions.WeaveScopeHost))
Expand Down Expand Up @@ -330,13 +313,13 @@ func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) {
var authorizers authorizer.Authorizer

switch s.Config.AuthorizationOptions.Mode {
case authorizationoptions.AlwaysAllow:
case authorization.AlwaysAllow:
authorizers = authorizerfactory.NewAlwaysAllowAuthorizer()
case authorizationoptions.AlwaysDeny:
case authorization.AlwaysDeny:
authorizers = authorizerfactory.NewAlwaysDenyAuthorizer()
default:
fallthrough
case authorizationoptions.RBAC:
case authorization.RBAC:
excludedPaths := []string{"/oauth/*", "/kapis/config.kubesphere.io/*", "/kapis/version", "/kapis/metrics"}
pathAuthorizer, _ := path.NewAuthorizer(excludedPaths)
amOperator := am.NewReadOnlyOperator(s.InformerFactory)
Expand All @@ -349,16 +332,17 @@ func (s *APIServer) buildHandlerChain(stopCh <-chan struct{}) {
handler = filters.WithMultipleClusterDispatcher(handler, clusterDispatcher)
}

loginRecorder := auth.NewLoginRecorder(s.KubernetesClient.KubeSphere(),
s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister())
userLister := s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister()
loginRecorder := auth.NewLoginRecorder(s.KubernetesClient.KubeSphere(), userLister)

// authenticators are unordered
authn := unionauth.New(anonymous.NewAuthenticator(),
basictoken.New(basic.NewBasicAuthenticator(auth.NewPasswordAuthenticator(s.KubernetesClient.KubeSphere(),
s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister(),
s.Config.AuthenticationOptions), loginRecorder)),
bearertoken.New(jwttoken.NewTokenAuthenticator(auth.NewTokenOperator(s.CacheClient,
basictoken.New(basic.NewBasicAuthenticator(auth.NewPasswordAuthenticator(userLister,
s.Config.AuthenticationOptions),
s.InformerFactory.KubeSphereSharedInformerFactory().Iam().V1alpha2().Users().Lister())))
loginRecorder)),
bearertoken.New(jwt.NewTokenAuthenticator(
auth.NewTokenOperator(s.CacheClient, s.Issuer, s.Config.AuthenticationOptions),
userLister)))
handler = filters.WithAuthentication(handler, authn)
handler = filters.WithRequestInfo(handler, requestInfoResolver)

Expand Down
2 changes: 1 addition & 1 deletion pkg/apiserver/authentication/authenticators/basic/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func NewBasicAuthenticator(authenticator auth.PasswordAuthenticator, loginRecord
}

func (t *basicAuthenticator) AuthenticatePassword(ctx context.Context, username, password string) (*authenticator.Response, bool, error) {
authenticated, provider, err := t.authenticator.Authenticate(username, password)
authenticated, provider, err := t.authenticator.Authenticate(ctx, username, password)
if err != nil {
if t.loginRecorder != nil && err == auth.IncorrectPasswordError {
var sourceIP, userAgent string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package jwttoken
package jwt

import (
"context"
Expand Down Expand Up @@ -48,31 +48,27 @@ func NewTokenAuthenticator(tokenOperator auth.TokenManagementInterface, userList
}

func (t *tokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
providedUser, err := t.tokenOperator.Verify(token)
verified, err := t.tokenOperator.Verify(token)
if err != nil {
klog.Warning(err)
return nil, false, err
}

if providedUser.GetName() == iamv1alpha2.PreRegistrationUser {
if verified.User.GetName() == iamv1alpha2.PreRegistrationUser {
return &authenticator.Response{
User: &user.DefaultInfo{
Name: providedUser.GetName(),
Extra: providedUser.GetExtra(),
Groups: providedUser.GetGroups(),
},
User: verified.User,
}, true, nil
}

dbUser, err := t.userLister.Get(providedUser.GetName())
u, err := t.userLister.Get(verified.User.GetName())
if err != nil {
return nil, false, err
}

return &authenticator.Response{
User: &user.DefaultInfo{
Name: dbUser.GetName(),
Groups: append(dbUser.Spec.Groups, user.AllAuthenticated),
Name: u.GetName(),
Groups: append(u.Spec.Groups, user.AllAuthenticated),
},
}, true, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ limitations under the License.
package aliyunidaas

import (
"context"
"encoding/json"
"errors"
"io/ioutil"
"net/http"

"github.com/mitchellh/mapstructure"

Expand Down Expand Up @@ -120,13 +120,16 @@ func (a idaasIdentity) GetEmail() string {
return a.Email
}

func (a *aliyunIDaaS) IdentityExchange(code string) (identityprovider.Identity, error) {
token, err := a.Config.Exchange(context.TODO(), code)
func (a *aliyunIDaaS) IdentityExchangeCallback(req *http.Request) (identityprovider.Identity, error) {
// OAuth2 callback, see also https://tools.ietf.org/html/rfc6749#section-4.1.2
code := req.URL.Query().Get("code")
ctx := req.Context()
token, err := a.Config.Exchange(ctx, code)
if err != nil {
return nil, err
}

resp, err := oauth2.NewClient(context.TODO(), oauth2.StaticTokenSource(token)).Get(a.Endpoint.UserInfoURL)
resp, err := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token)).Get(a.Endpoint.UserInfoURL)
if err != nil {
return nil, err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ endpoint:
userInfoUrl: "https://xxxxx.login.aliyunidaas.com/api/bff/v1.2/oauth2/userinfo"
authURL: "https://xxxx.login.aliyunidaas.com/oauth/authorize"
tokenURL: "https://xxxx.login.aliyunidaas.com/oauth/token"
redirectURL: "http://ks-console/oauth/redirect"
redirectURL: "https://console.kubesphere.io/oauth/redirect/idaas"
scopes:
- read
`)},
Expand All @@ -65,7 +65,7 @@ scopes:
TokenURL: "https://xxxx.login.aliyunidaas.com/oauth/token",
UserInfoURL: "https://xxxxx.login.aliyunidaas.com/api/bff/v1.2/oauth2/userinfo",
},
RedirectURL: "http://ks-console/oauth/redirect",
RedirectURL: "https://console.kubesphere.io/oauth/redirect/idaas",
Scopes: []string{"read"},
Config: &oauth2.Config{
ClientID: "xxxx",
Expand All @@ -75,7 +75,7 @@ scopes:
TokenURL: "https://xxxx.login.aliyunidaas.com/oauth/token",
AuthStyle: oauth2.AuthStyleAutoDetect,
},
RedirectURL: "http://ks-console/oauth/redirect",
RedirectURL: "https://console.kubesphere.io/oauth/redirect/idaas",
Scopes: []string{"read"},
},
},
Expand Down
6 changes: 4 additions & 2 deletions pkg/apiserver/authentication/identityprovider/cas/cas.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@ func (f casProviderFactory) Create(options oauth.DynamicOptions) (identityprovid
return &cas, nil
}

func (c cas) IdentityExchange(ticket string) (identityprovider.Identity, error) {
func (c cas) IdentityExchangeCallback(req *http.Request) (identityprovider.Identity, error) {
// CAS callback, see also https://apereo.github.io/cas/6.3.x/protocol/CAS-Protocol-V2-Specification.html#25-servicevalidate-cas-20
ticket := req.URL.Query().Get("ticket")
resp, err := c.client.ValidateServiceTicket(gocas.ServiceTicket(ticket))
if err != nil {
return nil, fmt.Errorf("cas validate service ticket failed: %v", err)
return nil, fmt.Errorf("cas: failed to validate service ticket : %v", err)
}
return &casIdentity{User: resp.User}, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,10 @@ func (g githubIdentity) GetEmail() string {
return g.Email
}

func (g *github) IdentityExchange(code string) (identityprovider.Identity, error) {
ctx := context.TODO()
func (g *github) IdentityExchangeCallback(req *http.Request) (identityprovider.Identity, error) {
// OAuth2 callback, see also https://tools.ietf.org/html/rfc6749#section-4.1.2
code := req.URL.Query().Get("code")
ctx := req.Context()
if g.InsecureSkipVerify {
client := &http.Client{
Transport: &http.Transport{
Expand Down
Loading

0 comments on commit 4b5b1c6

Please sign in to comment.