mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-18 13:41:21 +01:00
Remove the readonly filter (#10944)
Remove the readonly filter as we have introduced readonly middleware Signed-off-by: Wenkai Yin <yinw@vmware.com>
This commit is contained in:
parent
d21318dfcf
commit
c8ca6a5ccf
@ -1,95 +0,0 @@
|
|||||||
// Copyright 2018 Project Harbor Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package filter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"regexp"
|
|
||||||
|
|
||||||
"github.com/astaxie/beego/context"
|
|
||||||
"github.com/goharbor/harbor/src/common/utils/log"
|
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
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 deletion or creation (e.g. retag) of repo/tag requests and returns 503.
|
|
||||||
func ReadonlyFilter(ctx *context.Context) {
|
|
||||||
filter(ctx.Request, ctx.ResponseWriter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func filter(req *http.Request, resp http.ResponseWriter) {
|
|
||||||
if !config.ReadOnly() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
log.Errorf("failed to write response body: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
re := regexp.MustCompile(tagURL)
|
|
||||||
s := re.FindStringSubmatch(req.URL.Path)
|
|
||||||
if len(s) == 3 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
re = regexp.MustCompile(repoURL)
|
|
||||||
s = re.FindStringSubmatch(req.URL.Path)
|
|
||||||
if len(s) == 2 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if len(s) == 3 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
// Copyright 2018 Project Harbor Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package filter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
|
||||||
"github.com/goharbor/harbor/src/core/config"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestReadonlyFilter(t *testing.T) {
|
|
||||||
|
|
||||||
var defaultConfig = map[string]interface{}{
|
|
||||||
common.ReadOnly: true,
|
|
||||||
}
|
|
||||||
config.Upload(defaultConfig)
|
|
||||||
|
|
||||||
assert := assert.New(t)
|
|
||||||
req1, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/ubuntu", nil)
|
|
||||||
rec := httptest.NewRecorder()
|
|
||||||
filter(req1, rec)
|
|
||||||
assert.Equal(http.StatusServiceUnavailable, rec.Code)
|
|
||||||
|
|
||||||
req2, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/hello-world", nil)
|
|
||||||
rec = httptest.NewRecorder()
|
|
||||||
filter(req2, rec)
|
|
||||||
assert.Equal(http.StatusServiceUnavailable, rec.Code)
|
|
||||||
|
|
||||||
req3, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags/14.04", nil)
|
|
||||||
rec = httptest.NewRecorder()
|
|
||||||
filter(req3, rec)
|
|
||||||
assert.Equal(http.StatusServiceUnavailable, rec.Code)
|
|
||||||
|
|
||||||
req4, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/hello-world/tags/latest", nil)
|
|
||||||
rec = httptest.NewRecorder()
|
|
||||||
filter(req4, rec)
|
|
||||||
assert.Equal(http.StatusServiceUnavailable, rec.Code)
|
|
||||||
|
|
||||||
req5, _ := http.NewRequest("DELETE", "http://127.0.0.1:5000/api/repositories/library/vmware/hello-world", nil)
|
|
||||||
rec = httptest.NewRecorder()
|
|
||||||
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))
|
|
||||||
}
|
|
@ -21,7 +21,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
beegoctx "github.com/astaxie/beego/context"
|
beegoctx "github.com/astaxie/beego/context"
|
||||||
"github.com/docker/distribution/reference"
|
|
||||||
"github.com/goharbor/harbor/src/common"
|
"github.com/goharbor/harbor/src/common"
|
||||||
"github.com/goharbor/harbor/src/common/api"
|
"github.com/goharbor/harbor/src/common/api"
|
||||||
"github.com/goharbor/harbor/src/common/dao"
|
"github.com/goharbor/harbor/src/common/dao"
|
||||||
@ -60,30 +59,6 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
reqCtxModifiers []ReqCtxModifier
|
reqCtxModifiers []ReqCtxModifier
|
||||||
// basic auth request context modifier only takes effect on the patterns
|
|
||||||
// in the slice
|
|
||||||
basicAuthReqPatterns = []*pathMethod{
|
|
||||||
// create project
|
|
||||||
{
|
|
||||||
path: "/api/projects",
|
|
||||||
method: http.MethodPost,
|
|
||||||
},
|
|
||||||
// token service
|
|
||||||
{
|
|
||||||
path: "/service/token",
|
|
||||||
method: http.MethodGet,
|
|
||||||
},
|
|
||||||
// delete repository
|
|
||||||
{
|
|
||||||
path: "/api/repositories/" + reference.NameRegexp.String(),
|
|
||||||
method: http.MethodDelete,
|
|
||||||
},
|
|
||||||
// delete tag
|
|
||||||
{
|
|
||||||
path: "/api/repositories/" + reference.NameRegexp.String() + "/tags/" + reference.TagRegexp.String(),
|
|
||||||
method: http.MethodDelete,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Init ReqCtxMofiers list
|
// Init ReqCtxMofiers list
|
||||||
|
@ -229,7 +229,6 @@ func main() {
|
|||||||
filter.Init()
|
filter.Init()
|
||||||
beego.InsertFilter("/api/*", beego.BeforeStatic, filter.SessionCheck)
|
beego.InsertFilter("/api/*", beego.BeforeStatic, filter.SessionCheck)
|
||||||
beego.InsertFilter("/*", beego.BeforeRouter, filter.SecurityFilter)
|
beego.InsertFilter("/*", beego.BeforeRouter, filter.SecurityFilter)
|
||||||
beego.InsertFilter("/*", beego.BeforeRouter, filter.ReadonlyFilter)
|
|
||||||
|
|
||||||
server.RegisterRoutes()
|
server.RegisterRoutes()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user