diff --git a/.github/workflows/docker-build-publish.yaml b/.github/workflows/docker-build-publish.yaml index 1bd75dd..25a42cc 100644 --- a/.github/workflows/docker-build-publish.yaml +++ b/.github/workflows/docker-build-publish.yaml @@ -137,7 +137,7 @@ jobs: echo ${{ toJSON(steps.sysdig.outputs.violation_report) }} | \ jq -r . echo ${{ toJSON(steps.sysdig.outputs.violation_report) }} | \ - jq -r .cis_docker_benchmark_violation_report[].violations[] | \ + jq -r '.cis_docker_benchmark_violation_report[] | select(true) | .violations[]' | \ wc -l | \ xargs -I% test 0 -eq % @@ -226,4 +226,4 @@ jobs: name: Test Docker image id: test_docker run: | - docker run --rm ${{ fromJSON(steps.meta.outputs.json).tags[0] }} --version + docker run --rm ${{ fromJSON(steps.meta.outputs.json).tags[0] }} -version diff --git a/.gitignore b/.gitignore index 88e622f..f8e7aba 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,10 @@ *.so *.dylib +# IDE +.idea* +.vscode* + # Test binary, build with `go test -c` *.test diff --git a/Dockerfile b/Dockerfile index 39c0fe6..e712844 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -FROM golang:1.18-alpine AS base +FROM golang:1.20-alpine AS base RUN set -eux \ && apk --no-cache add ca-certificates \ - && apk --no-cache add --virtual build-dependencies cmake g++ make unzip curl upx git + && apk --no-cache add --virtual build-dependencies cmake g++ make unzip curl git libcap WORKDIR ${GOPATH}/src/github.com/AthenZ/garm @@ -30,7 +30,10 @@ RUN BUILD_TIME=$(date -u +%Y%m%d-%H%M%S) \ GOARCH=$(go env GOARCH) \ GO111MODULE=on \ go build -ldflags "-s -w -linkmode 'external' -extldflags '-static -fPIC -m64 -pthread -std=c++11 -lstdc++' -X 'main.Version=${APP_VERSION} at ${BUILD_TIME} by ${GO_VERSION}'" -a -tags "cgo netgo" -installsuffix "cgo netgo" -o "${APP_NAME}" \ - && upx --best -o "/usr/bin/${APP_NAME}" "${APP_NAME}" + && mv "${APP_NAME}" "/usr/bin/${APP_NAME}" + +# allow well-known port binding +RUN setcap 'cap_net_bind_service=+ep' "/usr/bin/${APP_NAME}" RUN apk del build-dependencies --purge \ && rm -rf "${GOPATH}" diff --git a/MAINTAINERS b/MAINTAINERS index 82e37cd..a555451 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17,3 +17,7 @@ # Seitaro Suno # Yahoo Japan Corporation # @ssunorz + +# Jeongwoo Kim +# Yahoo Japan Corporation +# @mlajkim diff --git a/config/config.go b/config/config.go index f8a5faa..4ba5fb2 100755 --- a/config/config.go +++ b/config/config.go @@ -21,6 +21,8 @@ import ( "regexp" "strings" "sync" + "github.com/kpango/glg" + assertion "github.com/AthenZ/athenz-authorizer/v5/policy" "github.com/pkg/errors" webhook "github.com/yahoo/k8s-athenz-webhook" @@ -29,7 +31,13 @@ import ( const ( // currentVersion represents the configuration version. - currentVersion = "v2.0.0" + currentVersion string = "v2.0.0" + // delimiter represents delimiter used to serialize RequestInfo. Must NOT use valid characters allowed in the all the fields of RequestInfo. + // Choose the delimiter that RequestInfo's verb, namespace, API Group, Resource and Name CANNOT use. + // i.e) If end user can set its resource name with hyphens, we cannot use hyphen as delimiter. + // This will wrongfully grant access to privileged actions like DELETE or POST + // for resources with hyphens in their names when minimum access rights such as GET is given. + delimiter string = "," ) // Config represents an application configuration content (config.yaml). @@ -256,11 +264,9 @@ type RequestInfo struct { once *sync.Once } -// Serialize returns RequestInfo in string format. -// 1. replacedAPIGroup = replace `. => _` in r.APIGroup -// 2. output format: `${r.Verb}-${r.Namespace}-${replacedAPIGroup}-${r.Resource}-${r.Name}` +// Serialize returns RequestInfo in string, separated by the delimiter. func (r *RequestInfo) Serialize() string { - return strings.Join([]string{r.Verb, r.Namespace, strings.Replace(r.APIGroup, ".", "_", -1), r.Resource, r.Name}, "-") + return strings.Join([]string{r.Verb, r.Namespace, r.APIGroup, r.Resource, r.Name}, delimiter) } // Match checks if the given RequestInfo matches with the regular expression in this RequestInfo. @@ -273,10 +279,16 @@ func (r *RequestInfo) Match(req RequestInfo) bool { r.once = new(sync.Once) } r.once.Do(func() { - r.reg = regexp.MustCompile(strings.Replace(strings.Replace(r.Serialize(), "*", ".*", -1), "..*", ".*", -1)) + ass, err := assertion.NewAssertion("", ":"+r.Serialize(), "") + if err != nil { + glg.Error(errors.Wrap(err, "regex creation error: invalid blacklist/whitelist config")) + r.reg = regexp.MustCompile("") + } else { + r.reg = ass.ResourceRegexp + } }) - return r.reg.Copy().MatchString(req.Serialize()) + return r.reg.Copy().MatchString(strings.ToLower(req.Serialize())) } // New returns the decoded configuration YAML file as *Config struct. Returns non-nil error if any. diff --git a/config/config_test.go b/config/config_test.go index 563599c..bb40c23 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -46,10 +46,10 @@ func Test_requestInfo_Serialize(t *testing.T) { Name: "dummyName", }, }, - want: "dummyVerb-dummyNamespace-dummyAPIGroup-dummyResource-dummyName", + want: "dummyVerb,dummyNamespace,dummyAPIGroup,dummyResource,dummyName", }, { - name: "Check serialize with replace API group", + name: "Check serialize with API group containing period", fields: fields{ req: RequestInfo{ Verb: "dummyVerb", @@ -59,7 +59,7 @@ func Test_requestInfo_Serialize(t *testing.T) { Name: "dummyName", }, }, - want: "dummyVerb-dummyNamespace-dummy_APIGroup-dummyResource-dummyName", + want: "dummyVerb,dummyNamespace,dummy.APIGroup,dummyResource,dummyName", }, } for _, tt := range tests { @@ -95,11 +95,6 @@ func Test_requestInfo_Match(t *testing.T) { APIGroup: "dummyAPIGroup", Resource: "dummyResource", Name: "dummyName", - - /*reg: func() *regexp.Regexp { - reg, _ := regexp.Compile("dummy") - return reg - }(),*/ }, }, args: args{ @@ -122,11 +117,6 @@ func Test_requestInfo_Match(t *testing.T) { APIGroup: "dummyAPIGroup", Resource: "dummyResource", Name: "dummyName", - - /*reg: func() *regexp.Regexp { - reg, _ := regexp.Compile("dummy") - return reg - }(),*/ }, }, args: args{ @@ -149,11 +139,6 @@ func Test_requestInfo_Match(t *testing.T) { APIGroup: "dummyAPIGroup", Resource: "dummyResource", Name: "*", - - /*reg: func() *regexp.Regexp { - reg, _ := regexp.Compile("dummy") - return reg - }(),*/ }, }, args: args{ @@ -177,11 +162,6 @@ func Test_requestInfo_Match(t *testing.T) { APIGroup: "dummyAPIGroup", Resource: "*", Name: "*", - - /*reg: func() *regexp.Regexp { - reg, _ := regexp.Compile("dummy") - return reg - }(),*/ }, }, args: args{ @@ -195,6 +175,28 @@ func Test_requestInfo_Match(t *testing.T) { }, want: true, }, + { + name: "Check malicious resource name not match", + fields: fields{ + req: RequestInfo{ + Verb: "get", + Namespace: "kube-system", + APIGroup: "garm", + Resource: "pods", + Name: "*", + }, + }, + args: args{ + req: RequestInfo{ + Verb: "create", + Namespace: "kube-system", + APIGroup: "garm", + Resource: "pods", + Name: "get-kube-system-garm-pods-test", + }, + }, + want: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/go.mod b/go.mod index 510e266..62f9470 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,17 @@ module github.com/AthenZ/garm/v2 -go 1.18 +go 1.20 replace ( - github.com/AthenZ/athenz => github.com/AthenZ/athenz v1.11.23 + github.com/AthenZ/athenz => github.com/AthenZ/athenz v1.11.26 golang.org/x/net => golang.org/x/net v0.7.0 k8s.io/client-go => k8s.io/client-go v0.26.0 ) require ( - github.com/AthenZ/athenz v1.11.23 - github.com/kpango/glg v1.6.14 + github.com/AthenZ/athenz v1.11.26 + github.com/AthenZ/athenz-authorizer/v5 v5.5.1 + github.com/kpango/glg v1.6.15 github.com/pkg/errors v0.9.1 github.com/yahoo/k8s-athenz-webhook v0.1.5-0.20230310225932-073f1a05c41a gopkg.in/yaml.v2 v2.4.0 @@ -27,17 +28,21 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kpango/fastime v1.1.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/kpango/fastime v1.1.9 // indirect + github.com/kpango/gache v1.2.8 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/yahoo/athenz v1.9.31 // indirect github.com/yahoo/k8s-athenz-syncer v0.1.8 // indirect - golang.org/x/net v0.7.0 // indirect + github.com/zeebo/xxh3 v1.0.2 // indirect + golang.org/x/net v0.9.0 // indirect golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/term v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.1 // indirect diff --git a/go.sum b/go.sum index 4284518..c34ce90 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AthenZ/athenz v1.11.23 h1:Iqw46nJHVhDXcnVbXvtl7b7s9tU8xkWHALJ0IKXfOcg= -github.com/AthenZ/athenz v1.11.23/go.mod h1:d2da1Gn5JLLrV48feSAKYJAJ2xCQPESvHpN5hnseB10= +github.com/AthenZ/athenz v1.11.26 h1:j6AZhjT2DuR/ZN3OQm5XW3ri6TS4Wf97ePcFcqBy/f0= +github.com/AthenZ/athenz v1.11.26/go.mod h1:hz8WrHkj4KOOaejllzTJIoXBCtptWV279CtEAUDuxis= +github.com/AthenZ/athenz-authorizer/v5 v5.5.1 h1:okVP8IVuYnQaJG8CfSOwDbyTKyVRBhL0ldZ5XZBpMNg= +github.com/AthenZ/athenz-authorizer/v5 v5.5.1/go.mod h1:Y4AYjbSeqaK6KdOjfGDbk1yvlVJ4Fm37+3ujnthE/1M= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -194,11 +196,15 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kpango/fastime v1.1.6 h1:lAw1Tiwnlbsx1xZs6W9eM7/8niwabknewbmLkh/yTVo= -github.com/kpango/fastime v1.1.6/go.mod h1:tTNDbIo5qL6D7g5vh2YbkyUbOVP2kD/we3rSjN22PMY= -github.com/kpango/glg v1.6.14 h1:Ss3ZvTQ23blUCDYizSAijiFTZsgGeYr/lanUGgQ10rY= -github.com/kpango/glg v1.6.14/go.mod h1:2djk7Zr4zKIYPHlORH8tJVlhCEh+XXW8W4K3qJyNXMI= +github.com/kpango/fastime v1.1.9 h1:xVQHcqyPt5M69DyFH7g1EPRns1YQNap9d5eLhl/Jy84= +github.com/kpango/fastime v1.1.9/go.mod h1:vyD7FnUn08zxY4b/QFBZVG+9EWMYsNl+QF0uE46urD4= +github.com/kpango/gache v1.2.8 h1:+OjREOmuWO4qrJksDhzWJq80o9iwHiezdVmMR1jtCG0= +github.com/kpango/gache v1.2.8/go.mod h1:UyBo0IoPFDSJypK2haDXeV6PwHEmBcXQA0BLuOYEvWg= +github.com/kpango/glg v1.6.15 h1:nw0xSxpSyrDIWHeb3dvnE08PW+SCbK+aYFETT75IeLA= +github.com/kpango/glg v1.6.15/go.mod h1:cmsc7Yeu8AS3wHLmN7bhwENXOpxfq+QoqxCIk2FneRk= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -290,6 +296,9 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -297,7 +306,7 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -357,6 +366,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -395,16 +406,19 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -413,8 +427,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/service/resolver.go b/service/resolver.go index a785578..9f0e8de 100755 --- a/service/resolver.go +++ b/service/resolver.go @@ -18,6 +18,7 @@ package service import ( "strings" + "github.com/kpango/glg" "github.com/AthenZ/garm/v2/config" ) @@ -311,29 +312,24 @@ func (r *resolve) TrimResource(res string) string { // returns false, only if inside blacklist // i.e. return (in whitelist || not in blacklist) func (r *resolve) IsAllowed(verb, namespace, apiGroup, resource, name string) bool { - var ok bool + ri := config.RequestInfo{ + Verb: verb, + Namespace: namespace, + APIGroup: apiGroup, + Resource: resource, + Name: name, + } + for _, white := range r.cfg.WhiteList { - ok = white.Match(config.RequestInfo{ - Verb: verb, - Namespace: namespace, - APIGroup: apiGroup, - Resource: resource, - Name: name, - }) - if ok { + if white.Match(ri) { + glg.Debugf("⏩ Excluded from blacklist with whitelist \"%v\" matches \"%v\"\n", white, ri) return true } } for _, black := range r.cfg.BlackList { - ok = black.Match(config.RequestInfo{ - Verb: verb, - Namespace: namespace, - APIGroup: apiGroup, - Resource: resource, - Name: name, - }) - if ok { + if black.Match(ri) { + glg.Debugf("❌ Explicitly denied by blacklist with blacklist \"%v\" matches \"%v\"\n", black, ri) return false } } @@ -343,16 +339,14 @@ func (r *resolve) IsAllowed(verb, namespace, apiGroup, resource, name string) bo // IsAdminAccess returns true, if any admin access in config match func (r *resolve) IsAdminAccess(verb, namespace, apiGroup, resource, name string) bool { - var ok bool for _, admin := range r.cfg.AdminAccessList { - ok = admin.Match(config.RequestInfo{ + if admin.Match(config.RequestInfo{ Verb: verb, Namespace: namespace, APIGroup: apiGroup, Resource: resource, Name: name, - }) - if ok { + }) { return true } } diff --git a/service/resolver_test.go b/service/resolver_test.go index 3f60574..338fd04 100644 --- a/service/resolver_test.go +++ b/service/resolver_test.go @@ -1461,7 +1461,7 @@ func Test_resolve_IsAdminAccess(t *testing.T) { want: true, }, { - name: "Check resolve IsAdminAccess regex match", + name: "Check resource with asterisk", fields: fields{ cfg: config.Platform{ AdminAccessList: []*config.RequestInfo{ @@ -1469,7 +1469,7 @@ func Test_resolve_IsAdminAccess(t *testing.T) { Verb: "verb-461", Namespace: "namespace-462", APIGroup: "apiGroup-463", - Resource: "resource-.*", + Resource: "resource-*", Name: "name-465", }, }, @@ -1509,7 +1509,7 @@ func Test_resolve_IsAdminAccess(t *testing.T) { want: false, }, { - name: "Check resolve IsAdminAccess regex match success after APIGroup replace", + name: "Check period in APIGroup gets escaped successfully", fields: fields{ cfg: config.Platform{ AdminAccessList: []*config.RequestInfo{ @@ -1526,7 +1526,7 @@ func Test_resolve_IsAdminAccess(t *testing.T) { args: args{ verb: "verb-509", namespace: "namespace-510", - apiGroup: "apiGroup-_______________", + apiGroup: "apiGroup-._______________", resource: "resource-512", name: "name-513", }, diff --git a/service/resource_mapper.go b/service/resource_mapper.go index 9428828..80ccc12 100755 --- a/service/resource_mapper.go +++ b/service/resource_mapper.go @@ -95,7 +95,7 @@ func (m *resourceMapper) MapResource(ctx context.Context, spec authz.SubjectAcce case !m.res.IsAllowed(verb, namespace, group, resource, name): // Not Allowed return "", nil, fmt.Errorf( - "----%s's request is not allowed----\nVerb:\t%s\nNamespaceb:\t%s\nAPI Group:\t%s\nResource:\t%s\nResource Name:\t%s\n", + "❌ %s's request is explicitly denied by the blacklist! Verb: \"%s\", Namespace: \"%s\", API Group: \"%s\", Resource: \"%s\", Resource Name: \"%s\"", identity, verb, namespace, group, resource, name) case m.res.IsAdminAccess(verb, namespace, group, resource, name): return identity, m.createAdminAccessCheck( diff --git a/service/resource_mapper_test.go b/service/resource_mapper_test.go index 8c8ea12..46198f2 100644 --- a/service/resource_mapper_test.go +++ b/service/resource_mapper_test.go @@ -438,7 +438,7 @@ func Test_resourceMapper_MapResource(t *testing.T) { wantIdentity: "", wantAthenzAccessChecks: nil, wantError: fmt.Errorf( - "----user-322's request is not allowed----\nVerb:\tverb-317\nNamespaceb:\tnamespace-316\nAPI Group:\tgroup-320\nResource:\tresource-318.sub-resource-319\nResource Name:\tname-315\n"), + "❌ user-322's request is explicitly denied by the blacklist! Verb: \"verb-317\", Namespace: \"namespace-316\", API Group: \"group-320\", Resource: \"resource-318.sub-resource-319\", Resource Name: \"name-315\""), }, } for _, tt := range tests {