Block retag requests in read-only mode (#6457)

Signed-off-by: cd1989 <chende@caicloud.io>
This commit is contained in:
De Chen 2018-12-06 18:35:22 +08:00 committed by Yan
parent 657ee8a150
commit 60d65a9d86
2 changed files with 38 additions and 6 deletions

View File

@ -25,11 +25,12 @@ import (
const ( const (
repoURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)(?:[a-z0-9]+(?:[._-][a-z0-9]+)*)$` repoURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)(?:[a-z0-9]+(?:[._-][a-z0-9]+)*)$`
tagsURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)tags$`
tagURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)tags/([\w][\w.-]{0,127})$` tagURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)tags/([\w][\w.-]{0,127})$`
labelURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)tags/([\w][\w.-]{0,127})/labels/[0-9]+$` labelURL = `^/api/repositories/((?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)+)tags/([\w][\w.-]{0,127})/labels/[0-9]+$`
) )
// ReadonlyFilter filters the delete repo/tag request and returns 503. // ReadonlyFilter filters the deletion or creation (e.g. retag) of repo/tag requests and returns 503.
func ReadonlyFilter(ctx *context.Context) { func ReadonlyFilter(ctx *context.Context) {
filter(ctx.Request, ctx.ResponseWriter) filter(ctx.Request, ctx.ResponseWriter)
} }
@ -38,10 +39,8 @@ func filter(req *http.Request, resp http.ResponseWriter) {
if !config.ReadOnly() { if !config.ReadOnly() {
return return
} }
if req.Method != http.MethodDelete {
return if matchRepoTagDelete(req) || matchRetag(req) {
}
if matchRepoTagDelete(req) {
resp.WriteHeader(http.StatusServiceUnavailable) resp.WriteHeader(http.StatusServiceUnavailable)
_, err := resp.Write([]byte("The system is in read only mode. Any modification is prohibited.")) _, err := resp.Write([]byte("The system is in read only mode. Any modification is prohibited."))
if err != nil { if err != nil {
@ -50,8 +49,13 @@ func filter(req *http.Request, resp http.ResponseWriter) {
} }
} }
// Only block repository and tag deletion // matchRepoTagDelete checks whether a request is a repository or tag deletion request,
// it should be blocked in read-only mode.
func matchRepoTagDelete(req *http.Request) bool { func matchRepoTagDelete(req *http.Request) bool {
if req.Method != http.MethodDelete {
return false
}
if inWhiteList(req) { if inWhiteList(req) {
return false return false
} }
@ -71,6 +75,16 @@ func matchRepoTagDelete(req *http.Request) bool {
return false return false
} }
// matchRetag checks whether a request is a retag request, it should be blocked in read-only mode.
func matchRetag(req *http.Request) bool {
if req.Method != http.MethodPost {
return false
}
re := regexp.MustCompile(tagsURL)
return re.MatchString(req.URL.Path)
}
func inWhiteList(req *http.Request) bool { func inWhiteList(req *http.Request) bool {
re := regexp.MustCompile(labelURL) re := regexp.MustCompile(labelURL)
s := re.FindStringSubmatch(req.URL.Path) s := re.FindStringSubmatch(req.URL.Path)

View File

@ -79,4 +79,22 @@ func TestReadonlyFilter(t *testing.T) {
filter(req5, rec) filter(req5, rec)
assert.Equal(http.StatusServiceUnavailable, rec.Code) assert.Equal(http.StatusServiceUnavailable, rec.Code)
req6, _ := http.NewRequest("POST", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags", nil)
rec = httptest.NewRecorder()
filter(req6, rec)
assert.Equal(http.StatusServiceUnavailable, rec.Code)
}
func TestMatchRetag(t *testing.T) {
req1, _ := http.NewRequest("POST", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags", nil)
assert.True(t, matchRetag(req1))
req2, _ := http.NewRequest("POST", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags/v1.0", nil)
assert.False(t, matchRetag(req2))
req3, _ := http.NewRequest("GET", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags", nil)
assert.False(t, matchRetag(req3))
req4, _ := http.NewRequest("POST", "http://127.0.0.1:5000/api/repositories/library/hello-world", nil)
assert.False(t, matchRetag(req4))
} }