mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-30 12:38:17 +01:00
resolve robot authgen password format issue (#17134)
In some cases, the robot automatically generates passwords that do not meet confidentiality requirements. The fix adds retry for auto generating passwords, and the timeout is 1 minute. The requirement: the secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number Signed-off-by: Wang Yan <wangyan@vmware.com>
This commit is contained in:
parent
360689b1b9
commit
bd8d66c68d
@ -3,6 +3,7 @@ package robot
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
"github.com/goharbor/harbor/src/lib/errors"
|
"github.com/goharbor/harbor/src/lib/errors"
|
||||||
"github.com/goharbor/harbor/src/lib/log"
|
"github.com/goharbor/harbor/src/lib/log"
|
||||||
"github.com/goharbor/harbor/src/lib/q"
|
"github.com/goharbor/harbor/src/lib/q"
|
||||||
|
"github.com/goharbor/harbor/src/lib/retry"
|
||||||
"github.com/goharbor/harbor/src/pkg"
|
"github.com/goharbor/harbor/src/pkg"
|
||||||
"github.com/goharbor/harbor/src/pkg/permission/types"
|
"github.com/goharbor/harbor/src/pkg/permission/types"
|
||||||
"github.com/goharbor/harbor/src/pkg/project"
|
"github.com/goharbor/harbor/src/pkg/project"
|
||||||
@ -99,9 +101,10 @@ func (d *controller) Create(ctx context.Context, r *Robot) (int64, string, error
|
|||||||
expiresAt = time.Now().AddDate(0, 0, duration).Unix()
|
expiresAt = time.Now().AddDate(0, 0, duration).Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
pwd := utils.GenerateRandomString()
|
secret, pwd, salt, err := CreateSec()
|
||||||
salt := utils.GenerateRandomString()
|
if err != nil {
|
||||||
secret := utils.Encrypt(pwd, salt, utils.SHA256)
|
return 0, "", err
|
||||||
|
}
|
||||||
|
|
||||||
name := r.Name
|
name := r.Name
|
||||||
// for the project level robot, set the name pattern as projectname+robotname, and + is a illegal character.
|
// for the project level robot, set the name pattern as projectname+robotname, and + is a illegal character.
|
||||||
@ -360,3 +363,44 @@ func (d *controller) toScope(ctx context.Context, p *Permission) (string, error)
|
|||||||
}
|
}
|
||||||
return "", errors.New(nil).WithMessage("unknown robot kind").WithCode(errors.BadRequestCode)
|
return "", errors.New(nil).WithMessage("unknown robot kind").WithCode(errors.BadRequestCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateSec(salt ...string) (string, string, string, error) {
|
||||||
|
var secret, pwd string
|
||||||
|
options := []retry.Option{
|
||||||
|
retry.InitialInterval(time.Millisecond * 500),
|
||||||
|
retry.MaxInterval(time.Second * 10),
|
||||||
|
retry.Timeout(time.Minute),
|
||||||
|
retry.Callback(func(err error, sleep time.Duration) {
|
||||||
|
log.Debugf("failed to generate secret for robot, retry after %s : %v", sleep, err)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := retry.Retry(func() error {
|
||||||
|
pwd = utils.GenerateRandomString()
|
||||||
|
if !IsValidSec(pwd) {
|
||||||
|
return errors.New(nil).WithMessage("invalid secret format")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}, options...); err != nil {
|
||||||
|
return "", "", "", errors.Wrap(err, "failed to generate an valid random secret for robot in one minute, please try again")
|
||||||
|
}
|
||||||
|
|
||||||
|
var saltTmp string
|
||||||
|
if len(salt) != 0 {
|
||||||
|
saltTmp = salt[0]
|
||||||
|
} else {
|
||||||
|
saltTmp = utils.GenerateRandomString()
|
||||||
|
}
|
||||||
|
secret = utils.Encrypt(pwd, saltTmp, utils.SHA256)
|
||||||
|
return secret, pwd, saltTmp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsValidSec(secret string) bool {
|
||||||
|
hasLower := regexp.MustCompile(`[a-z]`)
|
||||||
|
hasUpper := regexp.MustCompile(`[A-Z]`)
|
||||||
|
hasNumber := regexp.MustCompile(`\d`)
|
||||||
|
if len(secret) >= 8 && hasLower.MatchString(secret) && hasUpper.MatchString(secret) && hasNumber.MatchString(secret) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -292,6 +292,20 @@ func (suite *ControllerTestSuite) TestToScope() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *ControllerTestSuite) TestIsValidSec() {
|
||||||
|
sec := "1234abcdABCD"
|
||||||
|
suite.True(IsValidSec(sec))
|
||||||
|
sec = "1234abcd"
|
||||||
|
suite.False(IsValidSec(sec))
|
||||||
|
sec = "123abc"
|
||||||
|
suite.False(IsValidSec(sec))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ControllerTestSuite) TestCreateSec() {
|
||||||
|
_, pwd, _, err := CreateSec()
|
||||||
|
suite.Nil(err)
|
||||||
|
suite.True(IsValidSec(pwd))
|
||||||
|
}
|
||||||
func TestControllerTestSuite(t *testing.T) {
|
func TestControllerTestSuite(t *testing.T) {
|
||||||
suite.Run(t, &ControllerTestSuite{})
|
suite.Run(t, &ControllerTestSuite{})
|
||||||
}
|
}
|
||||||
|
@ -239,14 +239,17 @@ func (rAPI *robotAPI) RefreshSec(ctx context.Context, params operation.RefreshSe
|
|||||||
var secret string
|
var secret string
|
||||||
robotSec := &models.RobotSec{}
|
robotSec := &models.RobotSec{}
|
||||||
if params.RobotSec.Secret != "" {
|
if params.RobotSec.Secret != "" {
|
||||||
if !isValidSec(params.RobotSec.Secret) {
|
if !robot.IsValidSec(params.RobotSec.Secret) {
|
||||||
return rAPI.SendError(ctx, errors.New("the secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number").WithCode(errors.BadRequestCode))
|
return rAPI.SendError(ctx, errors.New("the secret must longer than 8 chars with at least 1 uppercase letter, 1 lowercase letter and 1 number").WithCode(errors.BadRequestCode))
|
||||||
}
|
}
|
||||||
secret = utils.Encrypt(params.RobotSec.Secret, r.Salt, utils.SHA256)
|
secret = utils.Encrypt(params.RobotSec.Secret, r.Salt, utils.SHA256)
|
||||||
robotSec.Secret = ""
|
robotSec.Secret = ""
|
||||||
} else {
|
} else {
|
||||||
pwd := utils.GenerateRandomString()
|
sec, pwd, _, err := robot.CreateSec(r.Salt)
|
||||||
secret = utils.Encrypt(pwd, r.Salt, utils.SHA256)
|
if err != nil {
|
||||||
|
return rAPI.SendError(ctx, err)
|
||||||
|
}
|
||||||
|
secret = sec
|
||||||
robotSec.Secret = pwd
|
robotSec.Secret = pwd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,16 +351,6 @@ func isValidDuration(d int64) bool {
|
|||||||
return d >= int64(-1) && d < math.MaxInt32
|
return d >= int64(-1) && d < math.MaxInt32
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValidSec(sec string) bool {
|
|
||||||
hasLower := regexp.MustCompile(`[a-z]`)
|
|
||||||
hasUpper := regexp.MustCompile(`[A-Z]`)
|
|
||||||
hasNumber := regexp.MustCompile(`\d`)
|
|
||||||
if len(sec) >= 8 && hasLower.MatchString(sec) && hasUpper.MatchString(sec) && hasNumber.MatchString(sec) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateName validates the robot name, especially '+' cannot be a valid character
|
// validateName validates the robot name, especially '+' cannot be a valid character
|
||||||
func validateName(name string) error {
|
func validateName(name string) error {
|
||||||
robotNameReg := `^[a-z0-9]+(?:[._-][a-z0-9]+)*$`
|
robotNameReg := `^[a-z0-9]+(?:[._-][a-z0-9]+)*$`
|
||||||
|
Loading…
Reference in New Issue
Block a user