mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-23 10:45:45 +01:00
Block retag requests in read-only mode (#6457)
Signed-off-by: cd1989 <chende@caicloud.io>
This commit is contained in:
parent
657ee8a150
commit
60d65a9d86
@ -25,11 +25,12 @@ import (
|
||||
|
||||
const (
|
||||
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})$`
|
||||
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) {
|
||||
filter(ctx.Request, ctx.ResponseWriter)
|
||||
}
|
||||
@ -38,10 +39,8 @@ func filter(req *http.Request, resp http.ResponseWriter) {
|
||||
if !config.ReadOnly() {
|
||||
return
|
||||
}
|
||||
if req.Method != http.MethodDelete {
|
||||
return
|
||||
}
|
||||
if matchRepoTagDelete(req) {
|
||||
|
||||
if matchRepoTagDelete(req) || matchRetag(req) {
|
||||
resp.WriteHeader(http.StatusServiceUnavailable)
|
||||
_, err := resp.Write([]byte("The system is in read only mode. Any modification is prohibited."))
|
||||
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 {
|
||||
if req.Method != http.MethodDelete {
|
||||
return false
|
||||
}
|
||||
|
||||
if inWhiteList(req) {
|
||||
return false
|
||||
}
|
||||
@ -71,6 +75,16 @@ func matchRepoTagDelete(req *http.Request) bool {
|
||||
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 {
|
||||
re := regexp.MustCompile(labelURL)
|
||||
s := re.FindStringSubmatch(req.URL.Path)
|
||||
|
@ -79,4 +79,22 @@ func TestReadonlyFilter(t *testing.T) {
|
||||
filter(req5, rec)
|
||||
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))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user