From 0a07e5f652cd7682fb677c81f8e382ea65e2279d Mon Sep 17 00:00:00 2001 From: zryfish Date: Sun, 15 Mar 2020 23:26:32 +0800 Subject: [PATCH] refactor authentication (#1951) * refactor authentication * refactor authentication --- cmd/ks-apiserver/app/options/options.go | 4 +-- cmd/ks-apiserver/app/server.go | 21 ++++++------ pkg/api/iam/authenticate.go | 12 +++---- .../caddy-plugin/authenticate/auto_load.go | 2 +- pkg/apiserver/apiserver.go | 10 +++--- pkg/apiserver/config/config.go | 4 +-- pkg/apiserver/config/config_test.go | 5 ++- pkg/models/iam/im.go | 2 +- pkg/simple/client/cache/options.go | 34 +++++++++---------- pkg/simple/client/cache/redis.go | 15 ++++---- pkg/simple/client/ldap/options.go | 3 ++ 11 files changed, 60 insertions(+), 52 deletions(-) diff --git a/cmd/ks-apiserver/app/options/options.go b/cmd/ks-apiserver/app/options/options.go index 4bb580406..1e5a349ed 100644 --- a/cmd/ks-apiserver/app/options/options.go +++ b/cmd/ks-apiserver/app/options/options.go @@ -156,8 +156,8 @@ func (s *ServerRunOptions) NewAPIServer(stopCh <-chan struct{}) (*apiserver.APIS } var cacheClient cache.Interface - if s.CacheOptions.RedisURL != "" { - if s.CacheOptions.RedisURL == fakeInterface && s.DebugMode { + if s.CacheOptions.Host != "" { + if s.CacheOptions.Host == fakeInterface && s.DebugMode { apiServer.CacheClient = cache.NewSimpleCache() } else { cacheClient, err = cache.NewRedisClient(s.CacheOptions, stopCh) diff --git a/cmd/ks-apiserver/app/server.go b/cmd/ks-apiserver/app/server.go index b260ecb83..3375ced9f 100644 --- a/cmd/ks-apiserver/app/server.go +++ b/cmd/ks-apiserver/app/server.go @@ -36,16 +36,17 @@ func NewAPIServerCommand() *cobra.Command { conf, err := apiserverconfig.TryLoadFromDisk() if err == nil { s = &options.ServerRunOptions{ - KubernetesOptions: conf.KubernetesOptions, - DevopsOptions: conf.DevopsOptions, - SonarQubeOptions: conf.SonarQubeOptions, - ServiceMeshOptions: conf.ServiceMeshOptions, - MySQLOptions: conf.MySQLOptions, - MonitoringOptions: conf.MonitoringOptions, - S3Options: conf.S3Options, - OpenPitrixOptions: conf.OpenPitrixOptions, - LoggingOptions: conf.LoggingOptions, - AuthenticateOptions: conf.AuthenticateOptions, + GenericServerRunOptions: s.GenericServerRunOptions, + KubernetesOptions: conf.KubernetesOptions, + DevopsOptions: conf.DevopsOptions, + SonarQubeOptions: conf.SonarQubeOptions, + ServiceMeshOptions: conf.ServiceMeshOptions, + MySQLOptions: conf.MySQLOptions, + MonitoringOptions: conf.MonitoringOptions, + S3Options: conf.S3Options, + OpenPitrixOptions: conf.OpenPitrixOptions, + LoggingOptions: conf.LoggingOptions, + AuthenticateOptions: conf.AuthenticateOptions, } } diff --git a/pkg/api/iam/authenticate.go b/pkg/api/iam/authenticate.go index 46ecac6dc..98c14b520 100644 --- a/pkg/api/iam/authenticate.go +++ b/pkg/api/iam/authenticate.go @@ -8,21 +8,21 @@ import ( type AuthenticationOptions struct { // authenticate rate limit will - AuthenticateRateLimiterMaxTries int - AuthenticateRateLimiterDuration time.Duration + AuthenticateRateLimiterMaxTries int `json:"authenticateRateLimiterMaxTries" yaml:"authenticateRateLimiterMaxTries"` + AuthenticateRateLimiterDuration time.Duration `json:"authenticationRateLimiterDuration" yaml:"authenticationRateLimiterDuration"` // maximum retries when authenticate failed - MaxAuthenticateRetries int + MaxAuthenticateRetries int `json:"maxAuthenticateRetries" yaml:"maxAuthenticateRetries"` // token validation duration, will refresh token expiration for each user request // 0 means never expire - TokenExpiration time.Duration + TokenExpiration time.Duration `json:"tokenExpiration" yaml:"tokenExpiration"` // allow multiple users login at the same time - MultipleLogin bool + MultipleLogin bool `json:"multipleLogin" yaml:"multipleLogin"` // secret to signed jwt token - JwtSecret string + JwtSecret string `json:"jwtSecret" yaml:"jwtSecret"` } func NewAuthenticateOptions() *AuthenticationOptions { diff --git a/pkg/apigateway/caddy-plugin/authenticate/auto_load.go b/pkg/apigateway/caddy-plugin/authenticate/auto_load.go index 111293ac0..65c23d4c3 100644 --- a/pkg/apigateway/caddy-plugin/authenticate/auto_load.go +++ b/pkg/apigateway/caddy-plugin/authenticate/auto_load.go @@ -99,7 +99,7 @@ func parse(c *caddy.Controller) (*Rule, error) { return nil, c.ArgErr() } - options := &cache.Options{RedisURL: c.Val()} + options := &cache.Options{Host: c.Val()} if err := options.Validate(); len(err) > 0 { return nil, c.ArgErr() diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 82a730d3d..23209166c 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -143,9 +143,9 @@ func (s *APIServer) installKubeSphereAPIs() { urlruntime.Must(servicemeshv1alpha2.AddToContainer(s.container)) } -func (s *APIServer) Run(stopCh <-chan struct{}) error { +func (s *APIServer) Run(stopCh <-chan struct{}) (err error) { - err := s.waitForResourceSync(stopCh) + err = s.waitForResourceSync(stopCh) if err != nil { return err } @@ -160,10 +160,12 @@ func (s *APIServer) Run(stopCh <-chan struct{}) error { klog.V(0).Infof("Start listening on %s", s.Server.Addr) if s.Server.TLSConfig != nil { - return s.Server.ListenAndServeTLS("", "") + err = s.Server.ListenAndServeTLS("", "") } else { - return s.Server.ListenAndServe() + err = s.Server.ListenAndServe() } + + return err } func (s *APIServer) buildHandlerChain() { diff --git a/pkg/apiserver/config/config.go b/pkg/apiserver/config/config.go index dc243f412..4d6d8d435 100644 --- a/pkg/apiserver/config/config.go +++ b/pkg/apiserver/config/config.go @@ -78,7 +78,7 @@ type Config struct { // Options below are only loaded from configuration file, no command line flags for these options now. KubeSphereOptions *kubesphere.Options `json:"-" yaml:"kubesphere,omitempty" mapstructure:"kubesphere"` - AuthenticateOptions *iam.AuthenticationOptions `json:"authenticate,omitempty" yaml:"authenticate,omitempty" mapstructure:"authenticate"` + AuthenticateOptions *iam.AuthenticationOptions `json:"authentication,omitempty" yaml:"authenticate,omitempty" mapstructure:"authenticate"` // Options used for enabling components, not actually used now. Once we switch Alerting/Notification API to kubesphere, // we can add these options to kubesphere command lines @@ -194,7 +194,7 @@ func (conf *Config) stripEmptyOptions() { conf.MySQLOptions = nil } - if conf.RedisOptions != nil && conf.RedisOptions.RedisURL == "" { + if conf.RedisOptions != nil && conf.RedisOptions.Host == "" { conf.RedisOptions = nil } diff --git a/pkg/apiserver/config/config_test.go b/pkg/apiserver/config/config_test.go index 3f6be5020..cea9d7b77 100644 --- a/pkg/apiserver/config/config_test.go +++ b/pkg/apiserver/config/config_test.go @@ -64,7 +64,10 @@ func newTestConfig() *Config { GroupSearchBase: "ou=Groups,dc=example,dc=org", }, RedisOptions: &cache.Options{ - RedisURL: "redis://:qwerty@localhost:6379/1", + Host: "localhost:6379", + Port: 6379, + Password: "P@88w0rd", + DB: 0, }, S3Options: &s3.Options{ Endpoint: "http://minio.openpitrix-system.svc", diff --git a/pkg/models/iam/im.go b/pkg/models/iam/im.go index 010a64c9e..1f12b9b60 100644 --- a/pkg/models/iam/im.go +++ b/pkg/models/iam/im.go @@ -114,7 +114,7 @@ func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error } // TODO: I think we should come up with a better strategy to prevent multiple login. - tokenKey := tokenKeyForUsername(user.Username, "*") + tokenKey := tokenKeyForUsername(user.Username, issuedToken) if !im.authenticateOptions.MultipleLogin { // multi login not allowed, remove the previous token sessions, err := im.cacheClient.Keys(tokenKey) diff --git a/pkg/simple/client/cache/options.go b/pkg/simple/client/cache/options.go index f4b8730aa..3422c1f81 100644 --- a/pkg/simple/client/cache/options.go +++ b/pkg/simple/client/cache/options.go @@ -1,20 +1,25 @@ package cache import ( - "github.com/go-redis/redis" + "fmt" "github.com/spf13/pflag" - "kubesphere.io/kubesphere/pkg/utils/reflectutils" ) type Options struct { - RedisURL string + Host string `json:"host"` + Port int `json:"port"` + Password string `json:"password"` + DB int `json:"db"` } // NewRedisOptions returns options points to nowhere, // because redis is not required for some components func NewRedisOptions() *Options { return &Options{ - RedisURL: "", + Host: "", + Port: 0, + Password: "", + DB: 0, } } @@ -22,25 +27,20 @@ func NewRedisOptions() *Options { func (r *Options) Validate() []error { errors := make([]error, 0) - _, err := redis.ParseURL(r.RedisURL) - - if err != nil { - errors = append(errors, err) + if r.Port == 0 { + errors = append(errors, fmt.Errorf("invalid service port number")) } return errors } -// ApplyTo apply to another options if it's a enabled option(non empty host) -func (r *Options) ApplyTo(options *Options) { - if r.RedisURL != "" { - reflectutils.Override(options, r) - } -} - // AddFlags add option flags to command line flags, // if redis-host left empty, the following options will be ignored. func (r *Options) AddFlags(fs *pflag.FlagSet, s *Options) { - fs.StringVar(&r.RedisURL, "redis-url", s.RedisURL, "Redis connection URL. If left blank, means redis is unnecessary, "+ - "redis will be disabled. e.g. redis://:password@host:port/db") + fs.StringVar(&r.Host, "redis-host", s.Host, "Redis connection URL. If left blank, means redis is unnecessary, "+ + "redis will be disabled.") + + fs.IntVar(&r.Port, "redis-port", s.Port, "") + fs.StringVar(&r.Password, "redis-password", s.Password, "") + fs.IntVar(&r.DB, "redis-db", s.DB, "") } diff --git a/pkg/simple/client/cache/redis.go b/pkg/simple/client/cache/redis.go index 558b6c5c3..54db227bf 100644 --- a/pkg/simple/client/cache/redis.go +++ b/pkg/simple/client/cache/redis.go @@ -18,6 +18,7 @@ package cache import ( + "fmt" "github.com/go-redis/redis" "k8s.io/klog" "time" @@ -30,21 +31,19 @@ type Client struct { func NewRedisClient(option *Options, stopCh <-chan struct{}) (Interface, error) { var r Client - options, err := redis.ParseURL(option.RedisURL) - - if err != nil { - klog.Error(err) - return nil, err + redisOptions := &redis.Options{ + Addr: fmt.Sprintf("%s:%d", option.Host, option.Port), + Password: option.Password, + DB: option.DB, } if stopCh == nil { - klog.Warningf("no stop signal passed, may cause redis connections leaked") + klog.Fatalf("no stop channel passed, redis connections will leak.") } - r.client = redis.NewClient(options) + r.client = redis.NewClient(redisOptions) if err := r.client.Ping().Err(); err != nil { - klog.Error("unable to reach redis host", err) r.client.Close() return nil, err } diff --git a/pkg/simple/client/ldap/options.go b/pkg/simple/client/ldap/options.go index 2913cc3d7..cc631aa8e 100644 --- a/pkg/simple/client/ldap/options.go +++ b/pkg/simple/client/ldap/options.go @@ -24,6 +24,9 @@ func NewOptions() *Options { ManagerDN: "cn=admin,dc=example,dc=org", UserSearchBase: "ou=Users,dc=example,dc=org", GroupSearchBase: "ou=Groups,dc=example,dc=org", + InitialCap: 10, + MaxCap: 100, + PoolName: "ldap", } }