Skip to content

Commit

Permalink
Implement option to specify bcrypt token hash cost (cesanta#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
andsens authored Jan 2, 2024
1 parent 15f6cd1 commit 497467b
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 61 deletions.
31 changes: 16 additions & 15 deletions auth_server/authn/github_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +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 *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"`
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 {
Expand Down Expand Up @@ -158,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 {
Expand Down
35 changes: 18 additions & 17 deletions auth_server/authn/gitlab_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,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 *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"`
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 {
Expand Down Expand Up @@ -107,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 = 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 {
Expand Down
23 changes: 12 additions & 11 deletions auth_server/authn/google_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +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"`
GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"`
RedisTokenDB *RedisStoreConfig `yaml:"redis_token_db,omitempty"`
HTTPTimeout time.Duration `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 {
Expand Down Expand Up @@ -131,17 +131,18 @@ type GoogleAuth struct {
func NewGoogleAuth(c *GoogleAuthConfig) (*GoogleAuth, 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 {
return nil, err
Expand Down
13 changes: 7 additions & 6 deletions auth_server/authn/oidc_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ type OIDCAuthConfig struct {
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"`
GCSTokenDB *GCSStoreConfig `yaml:"gcs_token_db,omitempty"`
RedisTokenDB *RedisStoreConfig `yaml:"redis_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 time.Duration `yaml:"http_timeout,omitempty"`
// the URL of the docker registry. Used to generate a full docker login command after authentication
Expand Down Expand Up @@ -96,17 +96,18 @@ Creates everything necessary for OIDC auth.
func NewOIDCAuth(c *OIDCAuthConfig) (*OIDCAuth, 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 {
Expand Down
14 changes: 10 additions & 4 deletions auth_server/authn/tokendb_gcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,26 @@ import (
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
Expand Down Expand Up @@ -77,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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions auth_server/authn/tokendb_redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
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 {
Expand All @@ -52,12 +53,17 @@ func NewRedisTokenDB(options *RedisStoreConfig) (TokenDB, error) {
} 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 {
Expand Down Expand Up @@ -95,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)
}

Expand Down
8 changes: 4 additions & 4 deletions auth_server/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func validate(c *Config) error {
}
gac.ClientSecret = strings.TrimSpace(string(contents))
}
if gac.ClientId == "" || gac.ClientSecret == "" || (gac.TokenDB == "" && (gac.GCSTokenDB == nil && gac.RedisTokenDB == nil)) {
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")
}

Expand All @@ -217,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")
}

Expand Down Expand Up @@ -245,7 +245,7 @@ func validate(c *Config) error {
}
oidc.ClientSecret = strings.TrimSpace(string(contents))
}
if oidc.ClientId == "" || oidc.ClientSecret == "" || oidc.Issuer == "" || oidc.RedirectURL == "" || (oidc.TokenDB == "" && (oidc.GCSTokenDB == nil && oidc.RedisTokenDB == nil)) {
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")
}

Expand Down Expand Up @@ -275,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")
}

Expand Down

0 comments on commit 497467b

Please sign in to comment.