mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 12:58:02 +08:00
fix: add support for sha256 and sha512 in htpasswd (#3497)
feat: add support for sha256 and sha512 htpasswd formats Fixes issue #3495 We currently support only bcrypt htpasswd hashes, however bcrypt is not FIPS-140 approved since it uses Blowfish. This PR adds support for sha256 and sha512 formats and enforces that bcrypt be disabled when fips140 mode is enabled. Signed-off-by: Ramkumar Chinchani <rchincha.dev@gmail.com>
This commit is contained in:
committed by
GitHub
parent
aaba362b4f
commit
04ae0a9409
@@ -99,7 +99,7 @@ func TestAPIKeys(t *testing.T) {
|
||||
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -871,7 +871,7 @@ func TestAPIKeysOpenDBError(t *testing.T) {
|
||||
conf := config.New()
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -1154,7 +1154,7 @@ func TestCookieSecureFlag(t *testing.T) {
|
||||
|
||||
username, _ := test.GenerateRandomString()
|
||||
password, _ := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
|
||||
+86
-78
@@ -719,54 +719,62 @@ func TestHtpasswdSingleCred(t *testing.T) {
|
||||
Convey("Single cred", t, func() {
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
singleCredtests := []string{}
|
||||
user, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
singleCredtests = append(singleCredtests, test.GetCredString(user, password))
|
||||
singleCredtests = append(singleCredtests, test.GetCredString(user, password))
|
||||
credFuncs := []func(string, string) string{
|
||||
test.GetBcryptCredString,
|
||||
test.GetSHA256CredString,
|
||||
test.GetSHA512CredString,
|
||||
}
|
||||
|
||||
for _, testString := range singleCredtests {
|
||||
func() {
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
for _, credFunc := range credFuncs {
|
||||
singleCredtests := []string{}
|
||||
user, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
singleCredtests = append(singleCredtests, credFunc(user, password))
|
||||
singleCredtests = append(singleCredtests, credFunc(user, password))
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(testString)
|
||||
defer os.Remove(htpasswdPath)
|
||||
conf.HTTP.Auth = &config.AuthConfig{
|
||||
HTPasswd: config.AuthHTPasswd{
|
||||
Path: htpasswdPath,
|
||||
},
|
||||
}
|
||||
for _, testString := range singleCredtests {
|
||||
func() {
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
|
||||
conf.HTTP.AllowOrigin = conf.HTTP.Address
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(testString)
|
||||
defer os.Remove(htpasswdPath)
|
||||
conf.HTTP.Auth = &config.AuthConfig{
|
||||
HTPasswd: config.AuthHTPasswd{
|
||||
Path: htpasswdPath,
|
||||
},
|
||||
}
|
||||
|
||||
ctlr := makeController(conf, t.TempDir())
|
||||
ctlr.Log.Info().Int64("seedUser", seedUser).Int64("seedPass", seedPass).Msg("random seed for username & password")
|
||||
conf.HTTP.AllowOrigin = conf.HTTP.Address
|
||||
|
||||
cm := test.NewControllerManager(ctlr)
|
||||
cm.StartAndWait(port)
|
||||
ctlr := makeController(conf, t.TempDir())
|
||||
ctlr.Log.Info().Int64("seedUser", seedUser).Int64("seedPass", seedPass).Msg("random seed for username & password")
|
||||
|
||||
defer cm.StopServer()
|
||||
cm := test.NewControllerManager(ctlr)
|
||||
cm.StartAndWait(port)
|
||||
|
||||
// with creds, should get expected status code
|
||||
resp, _ := resty.R().SetBasicAuth(user, password).Get(baseURL + "/v2/")
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
defer cm.StopServer()
|
||||
|
||||
header := []string{"Authorization,content-type," + constants.SessionClientHeaderName}
|
||||
// with creds, should get expected status code
|
||||
resp, _ := resty.R().SetBasicAuth(user, password).Get(baseURL + "/v2/")
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
|
||||
resp, _ = resty.R().SetBasicAuth(user, password).Options(baseURL + "/v2/")
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusNoContent)
|
||||
So(len(resp.Header()), ShouldEqual, 5)
|
||||
So(resp.Header()["Access-Control-Allow-Headers"], ShouldResemble, header)
|
||||
So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,OPTIONS")
|
||||
header := []string{"Authorization,content-type," + constants.SessionClientHeaderName}
|
||||
|
||||
// with invalid creds, it should fail
|
||||
resp, _ = resty.R().SetBasicAuth("chuck", "chuck").Get(baseURL + "/v2/")
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
|
||||
}()
|
||||
resp, _ = resty.R().SetBasicAuth(user, password).Options(baseURL + "/v2/")
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusNoContent)
|
||||
So(len(resp.Header()), ShouldEqual, 5)
|
||||
So(resp.Header()["Access-Control-Allow-Headers"], ShouldResemble, header)
|
||||
So(resp.Header().Get("Access-Control-Allow-Methods"), ShouldResemble, "GET,OPTIONS")
|
||||
|
||||
// with invalid creds, it should fail
|
||||
resp, _ = resty.R().SetBasicAuth("chuck", "chuck").Get(baseURL + "/v2/")
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
|
||||
}()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -783,7 +791,7 @@ func TestAllowMethodsHeader(t *testing.T) {
|
||||
|
||||
simpleUser := "simpleUser"
|
||||
simpleUserPassword := "simpleUserPass"
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(simpleUser, simpleUserPassword))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(simpleUser, simpleUserPassword))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -860,14 +868,14 @@ func TestHtpasswdTwoCreds(t *testing.T) {
|
||||
password1 := "aliciapassword"
|
||||
user2 := "bob"
|
||||
password2 := "robert"
|
||||
twoCredTests = append(twoCredTests, test.GetCredString(user1, password1)+"\n"+
|
||||
test.GetCredString(user2, password2))
|
||||
twoCredTests = append(twoCredTests, test.GetBcryptCredString(user1, password1)+"\n"+
|
||||
test.GetBcryptCredString(user2, password2))
|
||||
|
||||
twoCredTests = append(twoCredTests, test.GetCredString(user1, password1)+"\n"+
|
||||
test.GetCredString(user2, password2)+"\n")
|
||||
twoCredTests = append(twoCredTests, test.GetBcryptCredString(user1, password1)+"\n"+
|
||||
test.GetBcryptCredString(user2, password2)+"\n")
|
||||
|
||||
twoCredTests = append(twoCredTests, test.GetCredString(user1, password1)+"\n\n"+
|
||||
test.GetCredString(user2, password2)+"\n\n")
|
||||
twoCredTests = append(twoCredTests, test.GetBcryptCredString(user1, password1)+"\n\n"+
|
||||
test.GetBcryptCredString(user2, password2)+"\n\n")
|
||||
|
||||
for _, testString := range twoCredTests {
|
||||
func() {
|
||||
@@ -920,7 +928,7 @@ func TestHtpasswdFiveCreds(t *testing.T) {
|
||||
credString := strings.Builder{}
|
||||
|
||||
for key, val := range tests {
|
||||
credString.WriteString(test.GetCredString(key, val) + "\n")
|
||||
credString.WriteString(test.GetBcryptCredString(key, val) + "\n")
|
||||
}
|
||||
|
||||
func() {
|
||||
@@ -1074,7 +1082,7 @@ func TestBasicAuth(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -1363,7 +1371,7 @@ func TestScaleOutRequestProxy(t *testing.T) {
|
||||
|
||||
username, _ := test.GenerateRandomString()
|
||||
password, _ := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -1935,7 +1943,7 @@ func TestMultipleInstance(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -1979,7 +1987,7 @@ func TestMultipleInstance(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -2029,7 +2037,7 @@ func TestMultipleInstance(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -2083,7 +2091,7 @@ func TestTLSWithBasicAuth(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -2154,7 +2162,7 @@ func TestTLSWithBasicAuthAllowReadAccess(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -2826,7 +2834,7 @@ func TestTLSMutualAndBasicAuth(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -2913,7 +2921,7 @@ func TestTLSMutualAndBasicAuthAllowReadAccess(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -4579,7 +4587,7 @@ func TestOpenIDMiddleware(t *testing.T) {
|
||||
// need a username different than ldap one, to test both logic
|
||||
htpasswdUsername, seedUser := test.GenerateRandomString()
|
||||
htpasswdPassword, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(htpasswdUsername, htpasswdPassword))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(htpasswdUsername, htpasswdPassword))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -4995,7 +5003,7 @@ func TestOpenIDMiddlewareWithRedisSessionDriver(t *testing.T) {
|
||||
// need a username different than ldap one, to test both logic
|
||||
htpasswdUsername, seedUser := test.GenerateRandomString()
|
||||
htpasswdPassword, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(htpasswdUsername, htpasswdPassword))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(htpasswdUsername, htpasswdPassword))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -5481,7 +5489,7 @@ func TestAuthnSessionErrors(t *testing.T) {
|
||||
htpasswdUsername, seedUser := test.GenerateRandomString()
|
||||
htpasswdPassword, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(htpasswdUsername, htpasswdPassword))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(htpasswdUsername, htpasswdPassword))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -5886,7 +5894,7 @@ func TestAuthnMetaDBErrors(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -5999,7 +6007,7 @@ func TestAuthorization(t *testing.T) {
|
||||
conf.HTTP.Port = port
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -6119,7 +6127,7 @@ func TestGetUsername(t *testing.T) {
|
||||
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -6190,7 +6198,7 @@ func TestAuthorizationMountBlob(t *testing.T) {
|
||||
username1 = strings.ToLower(username1)
|
||||
username2 = strings.ToLower(username2)
|
||||
|
||||
content := test.GetCredString(username1, password1) + test.GetCredString(username2, password2)
|
||||
content := test.GetBcryptCredString(username1, password1) + test.GetBcryptCredString(username2, password2)
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(content)
|
||||
|
||||
@@ -6548,7 +6556,7 @@ func TestAuthorizationWithAnonymousPolicyBasicAuthAndSessionHeader(t *testing.T)
|
||||
badpassphrase := "bad"
|
||||
htpasswdUsername, seedUser := test.GenerateRandomString()
|
||||
htpasswdPassword, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(htpasswdUsername, htpasswdPassword))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(htpasswdUsername, htpasswdPassword))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -6758,7 +6766,7 @@ func TestAuthorizationWithMultiplePolicies(t *testing.T) {
|
||||
password1, seedPass1 := test.GenerateRandomString()
|
||||
username2, seedUser2 := test.GenerateRandomString()
|
||||
password2, seedPass2 := test.GenerateRandomString()
|
||||
content := test.GetCredString(username1, password1) + test.GetCredString(username2, password2)
|
||||
content := test.GetBcryptCredString(username1, password1) + test.GetBcryptCredString(username2, password2)
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(content)
|
||||
|
||||
@@ -6917,7 +6925,7 @@ func TestInvalidCases(t *testing.T) {
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -6977,8 +6985,8 @@ func TestHTTPReadOnly(t *testing.T) {
|
||||
singleCredtests := []string{}
|
||||
user, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
singleCredtests = append(singleCredtests, test.GetCredString(user, password))
|
||||
singleCredtests = append(singleCredtests, test.GetCredString(user, password)+"\n")
|
||||
singleCredtests = append(singleCredtests, test.GetBcryptCredString(user, password))
|
||||
singleCredtests = append(singleCredtests, test.GetBcryptCredString(user, password)+"\n")
|
||||
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
@@ -7048,7 +7056,7 @@ func TestCrossRepoMount(t *testing.T) {
|
||||
conf.HTTP.Port = port
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -7263,7 +7271,7 @@ func TestCrossRepoMount(t *testing.T) {
|
||||
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -7407,7 +7415,7 @@ func TestParallelRequests(t *testing.T) {
|
||||
conf.HTTP.Port = port
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
t.Cleanup(func() {
|
||||
os.Remove(htpasswdPath)
|
||||
@@ -8971,7 +8979,7 @@ func TestPagedRepositoriesWithAuthorization(t *testing.T) {
|
||||
conf.HTTP.Port = port
|
||||
username, _ := test.GenerateRandomString()
|
||||
password, _ := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -12380,7 +12388,7 @@ func TestSearchRoutes(t *testing.T) {
|
||||
|
||||
user1 := "test"
|
||||
password1 := "test"
|
||||
testString1 := test.GetCredString(user1, password1)
|
||||
testString1 := test.GetBcryptCredString(user1, password1)
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(testString1)
|
||||
|
||||
@@ -12523,7 +12531,7 @@ func TestSearchRoutes(t *testing.T) {
|
||||
user1 := "test1"
|
||||
password1 := "test1"
|
||||
group1 := "testgroup3"
|
||||
testString1 := test.GetCredString(user1, password1)
|
||||
testString1 := test.GetBcryptCredString(user1, password1)
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(testString1)
|
||||
|
||||
@@ -12611,7 +12619,7 @@ func TestSearchRoutes(t *testing.T) {
|
||||
password1 := "test2"
|
||||
group1 := "testgroup1"
|
||||
group2 := "secondtestgroup"
|
||||
testString1 := test.GetCredString(user1, password1)
|
||||
testString1 := test.GetBcryptCredString(user1, password1)
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(testString1)
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
@@ -12681,7 +12689,7 @@ func TestSearchRoutes(t *testing.T) {
|
||||
user1 := "test3"
|
||||
password1 := "test3"
|
||||
group1 := "testgroup"
|
||||
testString1 := test.GetCredString(user1, password1)
|
||||
testString1 := test.GetBcryptCredString(user1, password1)
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(testString1)
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
@@ -12751,7 +12759,7 @@ func TestSearchRoutes(t *testing.T) {
|
||||
user1 := "test4"
|
||||
password1 := "test4"
|
||||
group1 := "testgroup1"
|
||||
testString1 := test.GetCredString(user1, password1)
|
||||
testString1 := test.GetBcryptCredString(user1, password1)
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(testString1)
|
||||
|
||||
@@ -12823,7 +12831,7 @@ func TestSearchRoutes(t *testing.T) {
|
||||
user1 := "test5"
|
||||
password1 := "test5"
|
||||
group1 := "testgroup2"
|
||||
testString1 := test.GetCredString(user1, password1)
|
||||
testString1 := test.GetBcryptCredString(user1, password1)
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(testString1)
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
@@ -12883,7 +12891,7 @@ func TestSearchRoutes(t *testing.T) {
|
||||
user1, seedUser1 := test.GenerateRandomString()
|
||||
password1, seedPass1 := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(user1, password1))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(user1, password1))
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
conf.HTTP.Auth = &config.AuthConfig{
|
||||
|
||||
+44
-6
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/fips140"
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
cpass "github.com/nathanaelle/password"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"zotregistry.dev/zot/v2/pkg/log"
|
||||
@@ -87,15 +89,51 @@ func (s *HTPasswd) Authenticate(username, passphrase string) (ok, present bool)
|
||||
return false, false
|
||||
}
|
||||
|
||||
err := bcrypt.CompareHashAndPassword([]byte(passphraseHash), []byte(passphrase))
|
||||
ok = err == nil
|
||||
// first try bcrypt (although disabled if fips140 mode is enabled)
|
||||
if strings.HasPrefix(passphraseHash, "$2a$") || strings.HasPrefix(passphraseHash, "$2b$") ||
|
||||
strings.HasPrefix(passphraseHash, "$2y$") {
|
||||
if fips140.Enabled() {
|
||||
s.log.Warn().Str("username", username).Msg("htpasswd bcrypt failed since fips140 is enabled")
|
||||
|
||||
if err != nil && !errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
|
||||
// Log that user's hash has unsupported format. Better than silently return 401.
|
||||
s.log.Warn().Err(err).Str("username", username).Msg("htpasswd bcrypt compare failed")
|
||||
return false, present
|
||||
}
|
||||
|
||||
err := bcrypt.CompareHashAndPassword([]byte(passphraseHash), []byte(passphrase))
|
||||
if err != nil {
|
||||
// Log that user's hash has unsupported format. Better than silently return 401.
|
||||
s.log.Warn().Err(err).Str("username", username).Msg("htpasswd bcrypt compare failed")
|
||||
|
||||
return false, present
|
||||
}
|
||||
|
||||
return true, present // success: bcrypt
|
||||
}
|
||||
|
||||
return
|
||||
var crypter cpass.Crypter
|
||||
|
||||
if strings.HasPrefix(passphraseHash, "$5$") { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
|
||||
crypter, ok = cpass.SHA256.CrypterFound(passphraseHash)
|
||||
} else if strings.HasPrefix(passphraseHash, "$6$") {
|
||||
crypter, ok = cpass.SHA512.CrypterFound(passphraseHash)
|
||||
} else {
|
||||
s.log.Warn().Str("username", username).Msg("htpasswd entry has unsupported hash type")
|
||||
|
||||
return false, present
|
||||
}
|
||||
|
||||
if !ok {
|
||||
s.log.Warn().Str("username", username).Msg("htpasswd entry parsing failed")
|
||||
|
||||
return false, present
|
||||
}
|
||||
|
||||
if !crypter.Verify([]byte(passphrase)) {
|
||||
s.log.Warn().Str("username", username).Msg("htpasswd sha compare failed")
|
||||
|
||||
return false, present
|
||||
}
|
||||
|
||||
return true, present // success: sha
|
||||
}
|
||||
|
||||
// HTPasswdWatcher helper which triggers htpasswd reload on file change event.
|
||||
|
||||
+15
-14
@@ -19,7 +19,7 @@ func TestHTPasswdWatcherOriginal(t *testing.T) {
|
||||
username, _ := test.GenerateRandomString()
|
||||
password1, _ := test.GenerateRandomString()
|
||||
password2, _ := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password1))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password1))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestHTPasswdWatcherOriginal(t *testing.T) {
|
||||
So(present, ShouldBeTrue)
|
||||
|
||||
// 2. Change file
|
||||
err = os.WriteFile(htpasswdPath, []byte(test.GetCredString(username, password2)), 0o600)
|
||||
err = os.WriteFile(htpasswdPath, []byte(test.GetBcryptCredString(username, password2)), 0o600)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// 3. Give some time for the background task
|
||||
@@ -98,8 +98,8 @@ func TestHTPasswdWatcher(t *testing.T) {
|
||||
username2, _ := test.GenerateRandomString()
|
||||
password2, _ := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath1 := test.MakeHtpasswdFileFromString(test.GetCredString(username1, password1))
|
||||
htpasswdPath2 := test.MakeHtpasswdFileFromString(test.GetCredString(username2, password2))
|
||||
htpasswdPath1 := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username1, password1))
|
||||
htpasswdPath2 := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username2, password2))
|
||||
|
||||
defer os.Remove(htpasswdPath1)
|
||||
defer os.Remove(htpasswdPath2)
|
||||
@@ -150,7 +150,7 @@ func TestHTPasswdWatcher(t *testing.T) {
|
||||
So(present, ShouldBeTrue)
|
||||
|
||||
// Change file content and verify automatic reload
|
||||
err = os.WriteFile(htpasswdPath1, []byte(test.GetCredString(username1, password2)), 0o600)
|
||||
err = os.WriteFile(htpasswdPath1, []byte(test.GetBcryptCredString(username1, password2)), 0o600)
|
||||
So(err, ShouldBeNil)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
ok, present = htp.Authenticate(username1, password2)
|
||||
@@ -158,7 +158,8 @@ func TestHTPasswdWatcher(t *testing.T) {
|
||||
So(present, ShouldBeTrue)
|
||||
|
||||
// Test multiple users
|
||||
multiUserContent := test.GetCredString(username1, password1) + "\n" + test.GetCredString(username2, password2)
|
||||
multiUserContent := test.GetBcryptCredString(username1, password1) +
|
||||
"\n" + test.GetBcryptCredString(username2, password2)
|
||||
err = os.WriteFile(htpasswdPath1, []byte(multiUserContent), 0o600)
|
||||
So(err, ShouldBeNil)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -194,8 +195,8 @@ func TestHTPasswdWatcher(t *testing.T) {
|
||||
username2, _ := test.GenerateRandomString()
|
||||
password2, _ := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath1 := test.MakeHtpasswdFileFromString(test.GetCredString(username1, password1))
|
||||
htpasswdPath2 := test.MakeHtpasswdFileFromString(test.GetCredString(username2, password2))
|
||||
htpasswdPath1 := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username1, password1))
|
||||
htpasswdPath2 := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username2, password2))
|
||||
|
||||
defer os.Remove(htpasswdPath1)
|
||||
defer os.Remove(htpasswdPath2)
|
||||
@@ -236,7 +237,7 @@ func TestHTPasswdWatcher(t *testing.T) {
|
||||
So(present, ShouldBeTrue)
|
||||
|
||||
// Test file rename (should not trigger reload)
|
||||
htpasswdPath3 := test.MakeHtpasswdFileFromString(test.GetCredString(username1, password1))
|
||||
htpasswdPath3 := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username1, password1))
|
||||
defer os.Remove(htpasswdPath3)
|
||||
err = htw.ChangeFile(htpasswdPath3)
|
||||
So(err, ShouldBeNil)
|
||||
@@ -302,8 +303,8 @@ func TestHTPasswdWatcher(t *testing.T) {
|
||||
username2, _ := test.GenerateRandomString()
|
||||
password2, _ := test.GenerateRandomString()
|
||||
|
||||
htpasswdPath1 := test.MakeHtpasswdFileFromString(test.GetCredString(username1, password1))
|
||||
htpasswdPath2 := test.MakeHtpasswdFileFromString(test.GetCredString(username2, password2))
|
||||
htpasswdPath1 := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username1, password1))
|
||||
htpasswdPath2 := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username2, password2))
|
||||
|
||||
defer os.Remove(htpasswdPath1)
|
||||
defer os.Remove(htpasswdPath2)
|
||||
@@ -408,7 +409,7 @@ func TestHTPasswdWatcher(t *testing.T) {
|
||||
// Test 2: File watching with fsnotify resources cleanup
|
||||
username, _ := test.GenerateRandomString()
|
||||
password, _ := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
@@ -490,7 +491,7 @@ func TestHTPasswdWatcher(t *testing.T) {
|
||||
So(test.WaitForLogMessages(logBuffer, "htpasswd watcher terminating...", 1, 5*time.Second), ShouldBeTrue)
|
||||
|
||||
// Test file with empty lines and comments
|
||||
content := "\n\n" + test.GetCredString(username, password) + "\n# comment\n"
|
||||
content := "\n\n" + test.GetBcryptCredString(username, password) + "\n# comment\n"
|
||||
commentedPath := test.MakeHtpasswdFileFromString(content)
|
||||
|
||||
defer os.Remove(commentedPath)
|
||||
@@ -520,7 +521,7 @@ func TestHTPasswdWatcher(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// Load some initial data
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
// Load initial file (this will populate the store)
|
||||
|
||||
@@ -46,7 +46,7 @@ func TestRoutes(t *testing.T) {
|
||||
|
||||
username, seedUser := test.GenerateRandomString()
|
||||
password, seedPass := test.GenerateRandomString()
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
|
||||
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetBcryptCredString(username, password))
|
||||
|
||||
defer os.Remove(htpasswdPath)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user