diff --git a/auth_server/authn/github_auth.go b/auth_server/authn/github_auth.go index 7787739e..9c04a046 100644 --- a/auth_server/authn/github_auth.go +++ b/auth_server/authn/github_auth.go @@ -29,7 +29,6 @@ import ( "time" "github.com/cesanta/glog" - "github.com/go-redis/redis" "github.com/cesanta/docker_auth/auth_server/api" ) @@ -57,28 +56,18 @@ type ParentGitHubTeam struct { } type GitHubAuthConfig struct { - Organization string `yaml:"organization,omitempty"` - ClientId string `yaml:"client_id,omitempty"` - ClientSecret string `yaml:"client_secret,omitempty"` - ClientSecretFile string `yaml:"client_secret_file,omitempty"` - TokenDB string `yaml:"token_db,omitempty"` - GCSTokenDB *GitHubGCSStoreConfig `yaml:"gcs_token_db,omitempty"` - RedisTokenDB *GitHubRedisStoreConfig `yaml:"redis_token_db,omitempty"` - HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"` - RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"` - GithubWebUri string `yaml:"github_web_uri,omitempty"` - GithubApiUri string `yaml:"github_api_uri,omitempty"` - RegistryUrl string `yaml:"registry_url,omitempty"` -} - -type GitHubGCSStoreConfig struct { - Bucket string `yaml:"bucket,omitempty"` - ClientSecretFile string `yaml:"client_secret_file,omitempty"` -} - -type GitHubRedisStoreConfig struct { - ClientOptions *redis.Options `yaml:"redis_options,omitempty"` - ClusterOptions *redis.ClusterOptions `yaml:"redis_cluster_options,omitempty"` + Organization string `yaml:"organization,omitempty"` + ClientId string `yaml:"client_id,omitempty"` + ClientSecret string `yaml:"client_secret,omitempty"` + ClientSecretFile string `yaml:"client_secret_file,omitempty"` + LevelTokenDB *LevelDBStoreConfig `yaml:"level_token_db,omitempty"` + GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"` + RedisTokenDB *RedisStoreConfig `yaml:"redis_token_db,omitempty"` + HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"` + RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"` + GithubWebUri string `yaml:"github_web_uri,omitempty"` + GithubApiUri string `yaml:"github_api_uri,omitempty"` + RegistryUrl string `yaml:"registry_url,omitempty"` } type GitHubAuthRequest struct { @@ -169,17 +158,18 @@ func parseLinkHeader(linkLines []string) (linkHeader, error) { func NewGitHubAuth(c *GitHubAuthConfig) (*GitHubAuth, error) { var db TokenDB var err error - dbName := c.TokenDB + var dbName string switch { case c.GCSTokenDB != nil: - db, err = NewGCSTokenDB(c.GCSTokenDB.Bucket, c.GCSTokenDB.ClientSecretFile) + db, err = NewGCSTokenDB(c.GCSTokenDB) dbName = "GCS: " + c.GCSTokenDB.Bucket case c.RedisTokenDB != nil: db, err = NewRedisTokenDB(c.RedisTokenDB) dbName = db.(*redisTokenDB).String() default: - db, err = NewTokenDB(c.TokenDB) + db, err = NewTokenDB(c.LevelTokenDB) + dbName = c.LevelTokenDB.Path } if err != nil { @@ -191,7 +181,7 @@ func NewGitHubAuth(c *GitHubAuthConfig) (*GitHubAuth, error) { return &GitHubAuth{ config: c, db: db, - client: &http.Client{Timeout: 10 * time.Second}, + client: &http.Client{Timeout: c.HTTPTimeout}, tmpl: template.Must(template.New("github_auth").Parse(string(github_auth))), tmplResult: template.Must(template.New("github_auth_result").Parse(string(github_auth_result))), }, nil diff --git a/auth_server/authn/gitlab_auth.go b/auth_server/authn/gitlab_auth.go index 9866fb6d..689e56da 100644 --- a/auth_server/authn/gitlab_auth.go +++ b/auth_server/authn/gitlab_auth.go @@ -29,7 +29,6 @@ import ( "time" "github.com/cesanta/glog" - "github.com/go-redis/redis" "github.com/cesanta/docker_auth/auth_server/api" ) @@ -57,20 +56,20 @@ type ParentGitlabTeam struct { } type GitlabAuthConfig struct { - Organization string `yaml:"organization,omitempty"` - ClientId string `yaml:"client_id,omitempty"` - ClientSecret string `yaml:"client_secret,omitempty"` - ClientSecretFile string `yaml:"client_secret_file,omitempty"` - TokenDB string `yaml:"token_db,omitempty"` - GCSTokenDB *GitlabGCSStoreConfig `yaml:"gcs_token_db,omitempty"` - RedisTokenDB *GitlabRedisStoreConfig `yaml:"redis_token_db,omitempty"` - HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"` - RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"` - GitlabWebUri string `yaml:"gitlab_web_uri,omitempty"` - GitlabApiUri string `yaml:"gitlab_api_uri,omitempty"` - RegistryUrl string `yaml:"registry_url,omitempty"` - GrantType string `yaml:"grant_type,omitempty"` - RedirectUri string `yaml:"redirect_uri,omitempty"` + Organization string `yaml:"organization,omitempty"` + ClientId string `yaml:"client_id,omitempty"` + ClientSecret string `yaml:"client_secret,omitempty"` + ClientSecretFile string `yaml:"client_secret_file,omitempty"` + LevelTokenDB *LevelDBStoreConfig `yaml:"level_token_db,omitempty"` + GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"` + RedisTokenDB *RedisStoreConfig `yaml:"redis_token_db,omitempty"` + HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"` + RevalidateAfter time.Duration `yaml:"revalidate_after,omitempty"` + GitlabWebUri string `yaml:"gitlab_web_uri,omitempty"` + GitlabApiUri string `yaml:"gitlab_api_uri,omitempty"` + RegistryUrl string `yaml:"registry_url,omitempty"` + GrantType string `yaml:"grant_type,omitempty"` + RedirectUri string `yaml:"redirect_uri,omitempty"` } type CodeToGitlabTokenResponse struct { @@ -85,16 +84,6 @@ type CodeToGitlabTokenResponse struct { ErrorDescription string `json:"error_description,omitempty"` } -type GitlabGCSStoreConfig struct { - Bucket string `yaml:"bucket,omitempty"` - ClientSecretFile string `yaml:"client_secret_file,omitempty"` -} - -type GitlabRedisStoreConfig struct { - ClientOptions *redis.Options `yaml:"redis_options,omitempty"` - ClusterOptions *redis.ClusterOptions `yaml:"redis_cluster_options,omitempty"` -} - type GitlabAuthRequest struct { Action string `json:"action,omitempty"` Code string `json:"code,omitempty"` @@ -118,17 +107,18 @@ type GitlabAuth struct { func NewGitlabAuth(c *GitlabAuthConfig) (*GitlabAuth, error) { var db TokenDB var err error - dbName := c.TokenDB + var dbName string switch { case c.GCSTokenDB != nil: - db, err = NewGCSTokenDB(c.GCSTokenDB.Bucket, c.GCSTokenDB.ClientSecretFile) + db, err = NewGCSTokenDB(c.GCSTokenDB) dbName = "GCS: " + c.GCSTokenDB.Bucket case c.RedisTokenDB != nil: - db, err = NewRedisGitlabTokenDB(c.RedisTokenDB) + db, err = NewRedisTokenDB(c.RedisTokenDB) dbName = db.(*redisTokenDB).String() default: - db, err = NewTokenDB(c.TokenDB) + db, err = NewTokenDB(c.LevelTokenDB) + dbName = c.LevelTokenDB.Path } if err != nil { @@ -140,7 +130,7 @@ func NewGitlabAuth(c *GitlabAuthConfig) (*GitlabAuth, error) { return &GitlabAuth{ config: c, db: db, - client: &http.Client{Timeout: 10 * time.Second}, + client: &http.Client{Timeout: c.HTTPTimeout}, tmpl: template.Must(template.New("gitlab_auth").Parse(string(gitlab_auth))), tmplResult: template.Must(template.New("gitlab_auth_result").Parse(string(gitlab_auth_result))), }, nil diff --git a/auth_server/authn/google_auth.go b/auth_server/authn/google_auth.go index a717839b..93b080c0 100644 --- a/auth_server/authn/google_auth.go +++ b/auth_server/authn/google_auth.go @@ -33,12 +33,14 @@ import ( ) type GoogleAuthConfig struct { - Domain string `yaml:"domain,omitempty"` - ClientId string `yaml:"client_id,omitempty"` - ClientSecret string `yaml:"client_secret,omitempty"` - ClientSecretFile string `yaml:"client_secret_file,omitempty"` - TokenDB string `yaml:"token_db,omitempty"` - HTTPTimeout int `yaml:"http_timeout,omitempty"` + Domain string `yaml:"domain,omitempty"` + ClientId string `yaml:"client_id,omitempty"` + ClientSecret string `yaml:"client_secret,omitempty"` + ClientSecretFile string `yaml:"client_secret_file,omitempty"` + LevelTokenDB *LevelDBStoreConfig `yaml:"level_token_db,omitempty"` + GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"` + RedisTokenDB *RedisStoreConfig `yaml:"redis_token_db,omitempty"` + HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"` } type GoogleAuthRequest struct { @@ -127,16 +129,30 @@ type GoogleAuth struct { } func NewGoogleAuth(c *GoogleAuthConfig) (*GoogleAuth, error) { - db, err := NewTokenDB(c.TokenDB) + var db TokenDB + var err error + var dbName string + + switch { + case c.GCSTokenDB != nil: + db, err = NewGCSTokenDB(c.GCSTokenDB) + dbName = "GCS: " + c.GCSTokenDB.Bucket + case c.RedisTokenDB != nil: + db, err = NewRedisTokenDB(c.RedisTokenDB) + dbName = db.(*redisTokenDB).String() + default: + db, err = NewTokenDB(c.LevelTokenDB) + dbName = c.LevelTokenDB.Path + } if err != nil { return nil, err } - glog.Infof("Google auth token DB at %s", c.TokenDB) + glog.Infof("Google auth token DB at %s", dbName) google_auth, _ := static.ReadFile("data/google_auth.tmpl") return &GoogleAuth{ config: c, db: db, - client: &http.Client{Timeout: 10 * time.Second}, + client: &http.Client{Timeout: c.HTTPTimeout}, tmpl: template.Must(template.New("google_auth").Parse(string(google_auth))), }, nil } diff --git a/auth_server/authn/oidc_auth.go b/auth_server/authn/oidc_auth.go index 624cfe9c..a52e0969 100644 --- a/auth_server/authn/oidc_auth.go +++ b/auth_server/authn/oidc_auth.go @@ -40,27 +40,29 @@ import ( type OIDCAuthConfig struct { // --- necessary --- // URL of the authentication provider. Must be able to serve the /.well-known/openid-configuration - Issuer string `yaml:"issuer,omitempty"` + Issuer string `yaml:"issuer,omitempty"` // URL of the auth server. Has to end with /oidc_auth - RedirectURL string `yaml:"redirect_url,omitempty"` + RedirectURL string `yaml:"redirect_url,omitempty"` // ID and secret, priovided by the OIDC provider after registration of the auth server - ClientId string `yaml:"client_id,omitempty"` - ClientSecret string `yaml:"client_secret,omitempty"` - ClientSecretFile string `yaml:"client_secret_file,omitempty"` + ClientId string `yaml:"client_id,omitempty"` + ClientSecret string `yaml:"client_secret,omitempty"` + ClientSecretFile string `yaml:"client_secret_file,omitempty"` // path where the tokendb should be stored within the container - TokenDB string `yaml:"token_db,omitempty"` + LevelTokenDB *LevelDBStoreConfig `yaml:"level_token_db,omitempty"` + GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"` + RedisTokenDB *RedisStoreConfig `yaml:"redis_token_db,omitempty"` // --- optional --- - HTTPTimeout int `yaml:"http_timeout,omitempty"` + HTTPTimeout time.Duration `yaml:"http_timeout,omitempty"` // the URL of the docker registry. Used to generate a full docker login command after authentication - RegistryURL string `yaml:"registry_url,omitempty"` + RegistryURL string `yaml:"registry_url,omitempty"` // --- optional --- // String claim to use for the username - UserClaim string `yaml:"user_claim,omitempty"` + UserClaim string `yaml:"user_claim,omitempty"` // --- optional --- // []string to add as labels. - LabelsClaims []string `yaml:"labels_claims,omitempty"` + LabelsClaims []string `yaml:"labels_claims,omitempty"` // --- optional --- - Scopes []string `yaml:"scopes,omitempty"` + Scopes []string `yaml:"scopes,omitempty"` } // OIDCRefreshTokenResponse is sent by OIDC provider in response to the grant_type=refresh_token request. @@ -92,11 +94,26 @@ type OIDCAuth struct { Creates everything necessary for OIDC auth. */ func NewOIDCAuth(c *OIDCAuthConfig) (*OIDCAuth, error) { - db, err := NewTokenDB(c.TokenDB) + var db TokenDB + var err error + var dbName string + + switch { + case c.GCSTokenDB != nil: + db, err = NewGCSTokenDB(c.GCSTokenDB) + dbName = "GCS: " + c.GCSTokenDB.Bucket + case c.RedisTokenDB != nil: + db, err = NewRedisTokenDB(c.RedisTokenDB) + dbName = db.(*redisTokenDB).String() + default: + db, err = NewTokenDB(c.LevelTokenDB) + dbName = c.LevelTokenDB.Path + } + if err != nil { return nil, err } - glog.Infof("OIDC auth token DB at %s", c.TokenDB) + glog.Infof("OIDC auth token DB at %s", dbName) ctx := context.Background() oidcAuth, _ := static.ReadFile("data/oidc_auth.tmpl") oidcAuthResult, _ := static.ReadFile("data/oidc_auth_result.tmpl") @@ -115,7 +132,7 @@ func NewOIDCAuth(c *OIDCAuthConfig) (*OIDCAuth, error) { return &OIDCAuth{ config: c, db: db, - client: &http.Client{Timeout: 10 * time.Second}, + client: &http.Client{Timeout: c.HTTPTimeout}, tmpl: template.Must(template.New("oidc_auth").Parse(string(oidcAuth))), tmplResult: template.Must(template.New("oidc_auth_result").Parse(string(oidcAuthResult))), ctx: ctx, diff --git a/auth_server/authn/tokendb_gcs.go b/auth_server/authn/tokendb_gcs.go index 284d1a3a..53a0d278 100644 --- a/auth_server/authn/tokendb_gcs.go +++ b/auth_server/authn/tokendb_gcs.go @@ -31,18 +31,29 @@ import ( "github.com/cesanta/docker_auth/auth_server/api" ) +type GCSStoreConfig struct { + Bucket string `yaml:"bucket,omitempty"` + ClientSecretFile string `yaml:"client_secret_file,omitempty"` + TokenHashCost int `yaml:"token_hash_cost,omitempty"` +} + // NewGCSTokenDB return a new TokenDB structure which uses Google Cloud Storage as backend. The // created DB uses file-per-user strategy and stores credentials independently for each user. // // Note: it's not recomanded bucket to be shared with other apps or services -func NewGCSTokenDB(bucket, clientSecretFile string) (TokenDB, error) { - gcs, err := storage.NewClient(context.Background(), option.WithServiceAccountFile(clientSecretFile)) - return &gcsTokenDB{gcs, bucket}, err +func NewGCSTokenDB(options *GCSStoreConfig) (TokenDB, error) { + gcs, err := storage.NewClient(context.Background(), option.WithServiceAccountFile(options.ClientSecretFile)) + tokenHashCost := options.TokenHashCost + if tokenHashCost <= 0 { + tokenHashCost = bcrypt.DefaultCost + } + return &gcsTokenDB{gcs, options.Bucket, tokenHashCost}, err } type gcsTokenDB struct { gcs *storage.Client bucket string + tokenHashCost int } // GetValue gets token value associated with the provided user. Each user @@ -72,7 +83,7 @@ func (db *gcsTokenDB) GetValue(user string) (*TokenDBValue, error) { func (db *gcsTokenDB) StoreToken(user string, v *TokenDBValue, updatePassword bool) (dp string, err error) { if updatePassword { dp = uniuri.New() - dph, _ := bcrypt.GenerateFromPassword([]byte(dp), bcrypt.DefaultCost) + dph, _ := bcrypt.GenerateFromPassword([]byte(dp), db.tokenHashCost) v.DockerPassword = string(dph) } diff --git a/auth_server/authn/tokendb.go b/auth_server/authn/tokendb_level.go similarity index 92% rename from auth_server/authn/tokendb.go rename to auth_server/authn/tokendb_level.go index 91cec5ff..66d43444 100644 --- a/auth_server/authn/tokendb.go +++ b/auth_server/authn/tokendb_level.go @@ -36,6 +36,11 @@ const ( var ExpiredToken = errors.New("expired token") +type LevelDBStoreConfig struct { + Path string `yaml:"path,omitempty"` + TokenHashCost int `yaml:"token_hash_cost,omitempty"` +} + // TokenDB stores tokens using LevelDB type TokenDB interface { // GetValue takes a username returns the corresponding token @@ -75,8 +80,12 @@ type TokenDBValue struct { } // NewTokenDB returns a new TokenDB structure -func NewTokenDB(file string) (TokenDB, error) { - db, err := leveldb.OpenFile(file, nil) +func NewTokenDB(options *LevelDBStoreConfig) (TokenDB, error) { + db, err := leveldb.OpenFile(options.Path, nil) + tokenHashCost := options.TokenHashCost + if tokenHashCost <= 0 { + tokenHashCost = bcrypt.DefaultCost + } return &TokenDBImpl{ DB: db, }, err diff --git a/auth_server/authn/tokendb_redis.go b/auth_server/authn/tokendb_redis.go index d6066e4c..39a4f10a 100644 --- a/auth_server/authn/tokendb_redis.go +++ b/auth_server/authn/tokendb_redis.go @@ -29,6 +29,12 @@ import ( "github.com/go-redis/redis" ) +type RedisStoreConfig struct { + ClientOptions *redis.Options `yaml:"redis_options,omitempty"` + ClusterOptions *redis.ClusterOptions `yaml:"redis_cluster_options,omitempty"` + TokenHashCost int `yaml:"token_hash_cost,omitempty"` +} + type RedisClient interface { Get(key string) *redis.StringCmd Set(key string, value interface{}, expiration time.Duration) *redis.StatusCmd @@ -37,7 +43,7 @@ type RedisClient interface { // NewRedisTokenDB returns a new TokenDB structure which uses Redis as the storage backend. // -func NewRedisTokenDB(options *GitHubRedisStoreConfig) (TokenDB, error) { +func NewRedisTokenDB(options *RedisStoreConfig) (TokenDB, error) { var client RedisClient if options.ClusterOptions != nil { if options.ClientOptions != nil { @@ -47,28 +53,17 @@ func NewRedisTokenDB(options *GitHubRedisStoreConfig) (TokenDB, error) { } else { client = redis.NewClient(options.ClientOptions) } - - return &redisTokenDB{client}, nil -} - -// NewRedisTokenDB returns a new TokenDB structure which uses Redis as the storage backend. -// -func NewRedisGitlabTokenDB(options *GitlabRedisStoreConfig) (TokenDB, error) { - var client RedisClient - if options.ClusterOptions != nil { - if options.ClientOptions != nil { - glog.Infof("Both redis_token_db.configs and redis_token_db.cluster_configs have been set. Only the latter will be used") - } - client = redis.NewClusterClient(options.ClusterOptions) - } else { - client = redis.NewClient(options.ClientOptions) + tokenHashCost := options.TokenHashCost + if tokenHashCost <= 0 { + tokenHashCost = bcrypt.DefaultCost } - return &redisTokenDB{client}, nil + return &redisTokenDB{client,tokenHashCost}, nil } type redisTokenDB struct { client RedisClient + tokenHashCost int } func (db *redisTokenDB) String() string { @@ -106,7 +101,7 @@ func (db *redisTokenDB) GetValue(user string) (*TokenDBValue, error) { func (db *redisTokenDB) StoreToken(user string, v *TokenDBValue, updatePassword bool) (dp string, err error) { if updatePassword { dp = uniuri.New() - dph, _ := bcrypt.GenerateFromPassword([]byte(dp), bcrypt.DefaultCost) + dph, _ := bcrypt.GenerateFromPassword([]byte(dp), db.tokenHashCost) v.DockerPassword = string(dph) } @@ -162,4 +157,4 @@ func (db *redisTokenDB) DeleteToken(user string) error { func (db *redisTokenDB) Close() error { return nil -} \ No newline at end of file +} diff --git a/auth_server/server/config.go b/auth_server/server/config.go index 9a396f71..866f65af 100644 --- a/auth_server/server/config.go +++ b/auth_server/server/config.go @@ -70,6 +70,7 @@ type ServerConfig struct { publicKey libtrust.PublicKey privateKey libtrust.PrivateKey + sigAlg string } type LetsEncryptConfig struct { @@ -86,6 +87,7 @@ type TokenConfig struct { publicKey libtrust.PublicKey privateKey libtrust.PrivateKey + sigAlg string } // TLSCipherSuitesValues maps CipherSuite names as strings to the actual values @@ -171,7 +173,7 @@ func validate(c *Config) error { return fmt.Errorf("expiration must be positive, got %d", c.Token.Expiration) } if c.Users == nil && c.ExtAuth == nil && c.GoogleAuth == nil && c.GitHubAuth == nil && c.GitlabAuth == nil && c.OIDCAuth == nil && c.LDAPAuth == nil && c.MongoAuth == nil && c.XormAuthn == nil && c.PluginAuthn == nil { - return errors.New("no auth methods are configured, this is probably a mistake. Use an empty user map if you really want to deny everyone.") + return errors.New("no auth methods are configured, this is probably a mistake. Use an empty user map if you really want to deny everyone") } if c.MongoAuth != nil { if err := c.MongoAuth.Validate("mongo_auth"); err != nil { @@ -191,11 +193,20 @@ func validate(c *Config) error { } gac.ClientSecret = strings.TrimSpace(string(contents)) } - if gac.ClientId == "" || gac.ClientSecret == "" || gac.TokenDB == "" { - return errors.New("google_auth.{client_id,client_secret,token_db} are required.") + if gac.ClientId == "" || gac.ClientSecret == "" || (gac.LevelTokenDB != nil && (gac.GCSTokenDB == nil && gac.RedisTokenDB == nil)) { + return errors.New("google_auth.{client_id,client_secret,token_db} are required") } + + if gac.ClientId == "" || gac.ClientSecret == "" || (gac.GCSTokenDB != nil && (gac.GCSTokenDB.Bucket == "" || gac.GCSTokenDB.ClientSecretFile == "")) { + return errors.New("google_auth.{client_id,client_secret,gcs_token_db{bucket,client_secret_file}} are required") + } + + if gac.ClientId == "" || gac.ClientSecret == "" || (gac.RedisTokenDB != nil && gac.RedisTokenDB.ClientOptions == nil && gac.RedisTokenDB.ClusterOptions == nil) { + return errors.New("google_auth.{client_id,client_secret,redis_token_db.{redis_options,redis_cluster_options}} are required") + } + if gac.HTTPTimeout <= 0 { - gac.HTTPTimeout = 10 + gac.HTTPTimeout = time.Duration(10 * time.Second) } } if ghac := c.GitHubAuth; ghac != nil { @@ -206,7 +217,7 @@ func validate(c *Config) error { } ghac.ClientSecret = strings.TrimSpace(string(contents)) } - if ghac.ClientId == "" || ghac.ClientSecret == "" || (ghac.TokenDB == "" && (ghac.GCSTokenDB == nil && ghac.RedisTokenDB == nil)) { + if ghac.ClientId == "" || ghac.ClientSecret == "" || (ghac.LevelTokenDB != nil && (ghac.GCSTokenDB == nil && ghac.RedisTokenDB == nil)) { return errors.New("github_auth.{client_id,client_secret,token_db} are required") } @@ -234,11 +245,20 @@ func validate(c *Config) error { } oidc.ClientSecret = strings.TrimSpace(string(contents)) } - if oidc.ClientId == "" || oidc.ClientSecret == "" || oidc.TokenDB == "" || oidc.Issuer == "" || oidc.RedirectURL == "" { + if oidc.ClientId == "" || oidc.ClientSecret == "" || oidc.Issuer == "" || oidc.RedirectURL == "" || (oidc.LevelTokenDB != nil && (oidc.GCSTokenDB == nil && oidc.RedisTokenDB == nil)) { return errors.New("oidc_auth.{issuer,redirect_url,client_id,client_secret,token_db} are required") } + + if oidc.ClientId == "" || oidc.ClientSecret == "" || (oidc.GCSTokenDB != nil && (oidc.GCSTokenDB.Bucket == "" || oidc.GCSTokenDB.ClientSecretFile == "")) { + return errors.New("oidc_auth.{client_id,client_secret,gcs_token_db{bucket,client_secret_file}} are required") + } + + if oidc.ClientId == "" || oidc.ClientSecret == "" || (oidc.RedisTokenDB != nil && oidc.RedisTokenDB.ClientOptions == nil && oidc.RedisTokenDB.ClusterOptions == nil) { + return errors.New("oidc_auth.{client_id,client_secret,redis_token_db.{redis_options,redis_cluster_options}} are required") + } + if oidc.HTTPTimeout <= 0 { - oidc.HTTPTimeout = 10 + oidc.HTTPTimeout = time.Duration(10 * time.Second) } if oidc.UserClaim == "" { oidc.UserClaim = "email" @@ -255,7 +275,7 @@ func validate(c *Config) error { } glab.ClientSecret = strings.TrimSpace(string(contents)) } - if glab.ClientId == "" || glab.ClientSecret == "" || (glab.TokenDB == "" && (glab.GCSTokenDB == nil && glab.RedisTokenDB == nil)) { + if glab.ClientId == "" || glab.ClientSecret == "" || (glab.LevelTokenDB != nil && (glab.GCSTokenDB == nil && glab.RedisTokenDB == nil)) { return errors.New("gitlab_auth.{client_id,client_secret,token_db} are required") } @@ -317,7 +337,7 @@ func validate(c *Config) error { return nil } -func loadCertAndKey(certFile string, keyFile string) (pk libtrust.PublicKey, prk libtrust.PrivateKey, err error) { +func loadCertAndKey(certFile string, keyFile string) (pk libtrust.PublicKey, prk libtrust.PrivateKey, sigAlg string, err error) { cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return @@ -331,6 +351,11 @@ func loadCertAndKey(certFile string, keyFile string) (pk libtrust.PublicKey, prk return } prk, err = libtrust.FromCryptoPrivateKey(cert.PrivateKey) + _, sigAlg, errStr := prk.Sign(strings.NewReader("dummy"), 0) + if errStr != nil { + err = fmt.Errorf("failed to sign: %s", errStr) + return + } return } @@ -352,7 +377,7 @@ func LoadConfig(fileName string) (*Config, error) { if c.Server.CertFile == "" || c.Server.KeyFile == "" { return nil, fmt.Errorf("failed to load server cert and key: both were not provided") } - c.Server.publicKey, c.Server.privateKey, err = loadCertAndKey(c.Server.CertFile, c.Server.KeyFile) + c.Server.publicKey, c.Server.privateKey, c.Server.sigAlg, err = loadCertAndKey(c.Server.CertFile, c.Server.KeyFile) if err != nil { return nil, fmt.Errorf("failed to load server cert and key: %s", err) } @@ -364,7 +389,7 @@ func LoadConfig(fileName string) (*Config, error) { if c.Token.CertFile == "" || c.Token.KeyFile == "" { return nil, fmt.Errorf("failed to load token cert and key: both were not provided") } - c.Token.publicKey, c.Token.privateKey, err = loadCertAndKey(c.Token.CertFile, c.Token.KeyFile) + c.Token.publicKey, c.Token.privateKey, c.Token.sigAlg, err = loadCertAndKey(c.Token.CertFile, c.Token.KeyFile) if err != nil { return nil, fmt.Errorf("failed to load token cert and key: %s", err) } @@ -372,7 +397,7 @@ func LoadConfig(fileName string) (*Config, error) { } if serverConfigured && !tokenConfigured { - c.Token.publicKey, c.Token.privateKey = c.Server.publicKey, c.Server.privateKey + c.Token.publicKey, c.Token.privateKey, c.Token.sigAlg = c.Server.publicKey, c.Server.privateKey, c.Server.sigAlg tokenConfigured = true } diff --git a/auth_server/server/server.go b/auth_server/server/server.go index 3ca0c367..2592b533 100644 --- a/auth_server/server/server.go +++ b/auth_server/server/server.go @@ -378,14 +378,9 @@ func (as *AuthServer) CreateToken(ar *authRequest, ares []authzResult) (string, now := time.Now().Unix() tc := &as.config.Token - // Sign something dummy to find out which algorithm is used. - _, sigAlg, err := tc.privateKey.Sign(strings.NewReader("dummy"), 0) - if err != nil { - return "", fmt.Errorf("failed to sign: %s", err) - } header := token.Header{ Type: "JWT", - SigningAlg: sigAlg, + SigningAlg: tc.sigAlg, KeyID: tc.publicKey.KeyID(), } headerJSON, err := json.Marshal(header) @@ -423,7 +418,7 @@ func (as *AuthServer) CreateToken(ar *authRequest, ares []authzResult) (string, payload := fmt.Sprintf("%s%s%s", joseBase64UrlEncode(headerJSON), token.TokenSeparator, joseBase64UrlEncode(claimsJSON)) sig, sigAlg2, err := tc.privateKey.Sign(strings.NewReader(payload), 0) - if err != nil || sigAlg2 != sigAlg { + if err != nil || sigAlg2 != tc.sigAlg { return "", fmt.Errorf("failed to sign token: %s", err) } glog.Infof("New token for %s %+v: %s", *ar, ar.Labels, claimsJSON)