mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
auth: support a read-only mode
This is useful if we want to roll out experimental versions of zot pointing to some storage shared with another zot instance. Also, when under storage full conditions, will be useful to turn on this flag to prevent further writes.
This commit is contained in:
parent
74f48e6ad3
commit
78be4cbe3c
3 changed files with 91 additions and 2 deletions
|
@ -91,6 +91,12 @@ func basicAuthHandler(c *Controller) mux.MiddlewareFunc {
|
||||||
authFail(w, realm, 5)
|
authFail(w, realm, 5)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (r.Method != http.MethodGet && r.Method != http.MethodHead) && c.Config.HTTP.ReadOnly {
|
||||||
|
// Reject modification requests in read-only mode
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
// Process request
|
// Process request
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
|
@ -175,6 +181,12 @@ func basicAuthHandler(c *Controller) mux.MiddlewareFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (r.Method != http.MethodGet && r.Method != http.MethodHead) && c.Config.HTTP.ReadOnly {
|
||||||
|
// Reject modification requests in read-only mode
|
||||||
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
basicAuth := r.Header.Get("Authorization")
|
basicAuth := r.Header.Get("Authorization")
|
||||||
if basicAuth == "" {
|
if basicAuth == "" {
|
||||||
authFail(w, realm, delay)
|
authFail(w, realm, delay)
|
||||||
|
|
|
@ -46,6 +46,7 @@ type HTTPConfig struct {
|
||||||
Auth *AuthConfig
|
Auth *AuthConfig
|
||||||
Realm string
|
Realm string
|
||||||
AllowReadAccess bool `mapstructure:",omitempty"`
|
AllowReadAccess bool `mapstructure:",omitempty"`
|
||||||
|
ReadOnly bool `mapstructure:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LDAPConfig struct {
|
type LDAPConfig struct {
|
||||||
|
|
|
@ -33,10 +33,12 @@ const (
|
||||||
BaseURL1 = "http://127.0.0.1:8081"
|
BaseURL1 = "http://127.0.0.1:8081"
|
||||||
BaseURL2 = "http://127.0.0.1:8082"
|
BaseURL2 = "http://127.0.0.1:8082"
|
||||||
BaseURL3 = "http://127.0.0.1:8083"
|
BaseURL3 = "http://127.0.0.1:8083"
|
||||||
|
BaseURL4 = "http://127.0.0.1:8084"
|
||||||
BaseSecureURL2 = "https://127.0.0.1:8082"
|
BaseSecureURL2 = "https://127.0.0.1:8082"
|
||||||
SecurePort1 = "8081"
|
SecurePort1 = "8081"
|
||||||
SecurePort2 = "8082"
|
SecurePort2 = "8082"
|
||||||
SecurePort3 = "8083"
|
SecurePort3 = "8083"
|
||||||
|
SecurePort4 = "8084"
|
||||||
username = "test"
|
username = "test"
|
||||||
passphrase = "test"
|
passphrase = "test"
|
||||||
ServerCert = "../../test/data/server.cert"
|
ServerCert = "../../test/data/server.cert"
|
||||||
|
@ -44,6 +46,7 @@ const (
|
||||||
CACert = "../../test/data/ca.crt"
|
CACert = "../../test/data/ca.crt"
|
||||||
AuthorizedNamespace = "everyone/isallowed"
|
AuthorizedNamespace = "everyone/isallowed"
|
||||||
UnauthorizedNamespace = "fortknox/notallowed"
|
UnauthorizedNamespace = "fortknox/notallowed"
|
||||||
|
ALICE = "alice"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
@ -109,8 +112,8 @@ func TestNew(t *testing.T) {
|
||||||
func TestHtpasswdSingleCred(t *testing.T) {
|
func TestHtpasswdSingleCred(t *testing.T) {
|
||||||
Convey("Single cred", t, func() {
|
Convey("Single cred", t, func() {
|
||||||
singleCredtests := []string{}
|
singleCredtests := []string{}
|
||||||
user := "alice"
|
user := ALICE
|
||||||
password := "alice"
|
password := ALICE
|
||||||
singleCredtests = append(singleCredtests, getCredString(user, password))
|
singleCredtests = append(singleCredtests, getCredString(user, password))
|
||||||
singleCredtests = append(singleCredtests, getCredString(user, password)+"\n")
|
singleCredtests = append(singleCredtests, getCredString(user, password)+"\n")
|
||||||
|
|
||||||
|
@ -1439,3 +1442,76 @@ func parseBearerAuthHeader(authHeaderRaw string) *authHeader {
|
||||||
|
|
||||||
return &h
|
return &h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPReadOnly(t *testing.T) {
|
||||||
|
Convey("Single cred", t, func() {
|
||||||
|
singleCredtests := []string{}
|
||||||
|
user := ALICE
|
||||||
|
password := ALICE
|
||||||
|
singleCredtests = append(singleCredtests, getCredString(user, password))
|
||||||
|
singleCredtests = append(singleCredtests, getCredString(user, password)+"\n")
|
||||||
|
|
||||||
|
for _, testString := range singleCredtests {
|
||||||
|
func() {
|
||||||
|
config := api.NewConfig()
|
||||||
|
config.HTTP.Port = SecurePort4
|
||||||
|
// enable read-only mode
|
||||||
|
config.HTTP.ReadOnly = true
|
||||||
|
|
||||||
|
htpasswdPath := makeHtpasswdFileFromString(testString)
|
||||||
|
defer os.Remove(htpasswdPath)
|
||||||
|
config.HTTP.Auth = &api.AuthConfig{
|
||||||
|
HTPasswd: api.AuthHTPasswd{
|
||||||
|
Path: htpasswdPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c := api.NewController(config)
|
||||||
|
dir, err := ioutil.TempDir("", "oci-repo-test")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
c.Config.Storage.RootDirectory = dir
|
||||||
|
go func(controller *api.Controller) {
|
||||||
|
// this blocks
|
||||||
|
if err := controller.Run(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(c)
|
||||||
|
// wait till ready
|
||||||
|
for {
|
||||||
|
_, err := resty.R().Get(BaseURL4)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
defer func(controller *api.Controller) {
|
||||||
|
ctx := context.Background()
|
||||||
|
_ = controller.Server.Shutdown(ctx)
|
||||||
|
}(c)
|
||||||
|
// with creds, should get expected status code
|
||||||
|
resp, _ := resty.R().SetBasicAuth(user, password).Get(BaseURL4 + "/v2/")
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
|
// with creds, should get expected status code
|
||||||
|
resp, _ = resty.R().SetBasicAuth(user, password).Get(BaseURL4 + "/v2/")
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
|
// with creds, any modifications should still fail on read-only mode
|
||||||
|
resp, err = resty.R().SetBasicAuth(user, password).
|
||||||
|
Post(BaseURL4 + "/v2/" + AuthorizedNamespace + "/blobs/uploads/")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 405)
|
||||||
|
|
||||||
|
//with invalid creds, it should fail
|
||||||
|
resp, _ = resty.R().SetBasicAuth("chuck", "chuck").Get(BaseURL4 + "/v2/")
|
||||||
|
So(resp, ShouldNotBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 401)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue