Skip to content

Commit

Permalink
fix: metrics should be protected behind authZ
Browse files Browse the repository at this point in the history
Signed-off-by: Alexei Dodon <[email protected]>
  • Loading branch information
adodon2go committed Oct 18, 2023
1 parent 7f6534a commit 6b5fac6
Show file tree
Hide file tree
Showing 28 changed files with 963 additions and 532 deletions.
41 changes: 20 additions & 21 deletions THIRD-PARTY-LICENSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,28 +65,28 @@ github.com/aquasecurity/tml|https://github.com/aquasecurity/tml/blob/v0.6.1/LICE
github.com/asaskevich/govalidator|https://github.com/asaskevich/govalidator/blob/a9d515a09cc2/LICENSE|MIT
github.com/aws/aws-sdk-go-v2/config|https://github.com/aws/aws-sdk-go-v2/blob/config/v1.18.44/config/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/credentials|https://github.com/aws/aws-sdk-go-v2/blob/credentials/v1.13.42/credentials/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue|https://github.com/aws/aws-sdk-go-v2/blob/feature/dynamodb/attributevalue/v1.10.41/feature/dynamodb/attributevalue/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue|https://github.com/aws/aws-sdk-go-v2/blob/feature/dynamodb/attributevalue/v1.10.42/feature/dynamodb/attributevalue/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/feature/ec2/imds|https://github.com/aws/aws-sdk-go-v2/blob/feature/ec2/imds/v1.13.12/feature/ec2/imds/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/internal/configsources|https://github.com/aws/aws-sdk-go-v2/blob/internal/configsources/v1.1.42/internal/configsources/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2|https://github.com/aws/aws-sdk-go-v2/blob/internal/endpoints/v2.4.36/internal/endpoints/v2/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/internal/configsources|https://github.com/aws/aws-sdk-go-v2/blob/internal/configsources/v1.1.43/internal/configsources/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2|https://github.com/aws/aws-sdk-go-v2/blob/internal/endpoints/v2.4.37/internal/endpoints/v2/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/internal/ini|https://github.com/aws/aws-sdk-go-v2/blob/internal/ini/v1.3.44/internal/ini/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/internal/sync/singleflight|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.1/internal/sync/singleflight/LICENSE|BSD-3-Clause
github.com/aws/aws-sdk-go-v2/service/dynamodb/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodb/v1.22.1/service/dynamodb/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodbstreams/v1.15.6/service/dynamodbstreams/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodbstreams/v1.15.6/service/dynamodbstreams/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/dynamodb|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodb/v1.22.1/service/dynamodb/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/internal/sync/singleflight|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.2/internal/sync/singleflight/LICENSE|BSD-3-Clause
github.com/aws/aws-sdk-go-v2/service/dynamodb/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodb/v1.22.2/service/dynamodb/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams/types|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodbstreams/v1.15.7/service/dynamodbstreams/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodbstreams/v1.15.7/service/dynamodbstreams/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/dynamodb|https://github.com/aws/aws-sdk-go-v2/blob/service/dynamodb/v1.22.2/service/dynamodb/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/ebs|https://github.com/aws/aws-sdk-go-v2/blob/service/ebs/v1.18.1/service/ebs/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/ec2|https://github.com/aws/aws-sdk-go-v2/blob/service/ec2/v1.98.0/service/ec2/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/ecrpublic|https://github.com/aws/aws-sdk-go-v2/blob/service/ecrpublic/v1.12.0/service/ecrpublic/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/ecr|https://github.com/aws/aws-sdk-go-v2/blob/service/ecr/v1.17.18/service/ecr/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/accept-encoding/v1.9.15/service/internal/accept-encoding/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/endpoint-discovery/v1.7.36/service/internal/endpoint-discovery/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/endpoint-discovery/v1.7.37/service/internal/endpoint-discovery/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url|https://github.com/aws/aws-sdk-go-v2/blob/service/internal/presigned-url/v1.9.36/service/internal/presigned-url/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/secretsmanager|https://github.com/aws/aws-sdk-go-v2/blob/service/secretsmanager/v1.21.4/service/secretsmanager/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/secretsmanager|https://github.com/aws/aws-sdk-go-v2/blob/service/secretsmanager/v1.21.5/service/secretsmanager/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/ssooidc|https://github.com/aws/aws-sdk-go-v2/blob/service/ssooidc/v1.17.2/service/ssooidc/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/sso|https://github.com/aws/aws-sdk-go-v2/blob/service/sso/v1.15.1/service/sso/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2/service/sts|https://github.com/aws/aws-sdk-go-v2/blob/service/sts/v1.23.1/service/sts/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.1/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go-v2|https://github.com/aws/aws-sdk-go-v2/blob/v1.21.2/LICENSE.txt|Apache-2.0
github.com/aws/aws-sdk-go/internal/sync/singleflight|https://github.com/aws/aws-sdk-go/blob/v1.45.24/internal/sync/singleflight/LICENSE|BSD-3-Clause
github.com/aws/aws-sdk-go|https://github.com/aws/aws-sdk-go/blob/v1.45.24/LICENSE.txt|Apache-2.0
github.com/aws/smithy-go/internal/sync/singleflight|https://github.com/aws/smithy-go/blob/v1.15.0/internal/sync/singleflight/LICENSE|BSD-3-Clause
Expand Down Expand Up @@ -192,7 +192,7 @@ github.com/golang/protobuf|https://github.com/golang/protobuf/blob/v1.5.3/LICENS
github.com/golang/snappy|https://github.com/golang/snappy/blob/v0.0.4/LICENSE|BSD-3-Clause
github.com/google/btree|https://github.com/google/btree/blob/v1.1.2/LICENSE|Apache-2.0
github.com/google/certificate-transparency-go|https://github.com/google/certificate-transparency-go/blob/v1.1.6/LICENSE|Apache-2.0
github.com/google/go-cmp/cmp|https://github.com/google/go-cmp/blob/v0.5.9/LICENSE|BSD-3-Clause
github.com/google/go-cmp/cmp|https://github.com/google/go-cmp/blob/v0.6.0/LICENSE|BSD-3-Clause
github.com/google/go-querystring/query|https://github.com/google/go-querystring/blob/v1.1.0/LICENSE|BSD-3-Clause
github.com/google/gofuzz|https://github.com/google/gofuzz/blob/v1.2.0/LICENSE|Apache-2.0
github.com/google/licenseclassifier/v2|https://github.com/google/licenseclassifier/blob/v2.0.0/v2/LICENSE|Apache-2.0
Expand Down Expand Up @@ -391,7 +391,7 @@ github.com/yashtewari/glob-intersection|https://github.com/yashtewari/glob-inter
github.com/zclconf/go-cty-yaml|https://github.com/zclconf/go-cty-yaml/blob/v1.0.3/LICENSE|Apache-2.0
github.com/zclconf/go-cty/cty|https://github.com/zclconf/go-cty/blob/v1.13.0/LICENSE|MIT
github.com/zeebo/errs|https://github.com/zeebo/errs/blob/v1.3.0/LICENSE|MIT
github.com/zitadel/oidc|https://github.com/zitadel/oidc/blob/v1.13.4/LICENSE|Apache-2.0
github.com/zitadel/oidc|https://github.com/zitadel/oidc/blob/v1.13.5/LICENSE|Apache-2.0
go.etcd.io/bbolt|https://github.com/etcd-io/bbolt/blob/v1.3.7/LICENSE|MIT
go.mongodb.org/mongo-driver|https://github.com/mongodb/mongo-go-driver/blob/v1.11.3/LICENSE|Apache-2.0
go.mozilla.org/pkcs7|https://github.com/mozilla-services/pkcs7/blob/33d05740a352/LICENSE|MIT
Expand All @@ -412,13 +412,12 @@ golang.org/x/crypto/sha3|https://cs.opensource.google/go/x/crypto/+/v0.14.0:LICE
golang.org/x/crypto|https://cs.opensource.google/go/x/crypto/+/v0.14.0:LICENSE|BSD-3-Clause
golang.org/x/exp/constraints|https://cs.opensource.google/go/x/exp/+/92128663:LICENSE|BSD-3-Clause
golang.org/x/exp|https://cs.opensource.google/go/x/exp/+/92128663:LICENSE|BSD-3-Clause
golang.org/x/mod/semver|https://cs.opensource.google/go/x/mod/+/v0.12.0:LICENSE|BSD-3-Clause
golang.org/x/mod|https://cs.opensource.google/go/x/mod/+/v0.12.0:LICENSE|BSD-3-Clause
golang.org/x/net/context|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause
golang.org/x/net/html|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause
golang.org/x/net/publicsuffix|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause
golang.org/x/net/webdav|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause
golang.org/x/net|https://cs.opensource.google/go/x/net/+/v0.16.0:LICENSE|BSD-3-Clause
golang.org/x/mod/semver|https://cs.opensource.google/go/x/mod/+/v0.13.0:LICENSE|BSD-3-Clause
golang.org/x/mod|https://cs.opensource.google/go/x/mod/+/v0.13.0:LICENSE|BSD-3-Clause
golang.org/x/net/html|https://cs.opensource.google/go/x/net/+/v0.17.0:LICENSE|BSD-3-Clause
golang.org/x/net/publicsuffix|https://cs.opensource.google/go/x/net/+/v0.17.0:LICENSE|BSD-3-Clause
golang.org/x/net/webdav|https://cs.opensource.google/go/x/net/+/v0.17.0:LICENSE|BSD-3-Clause
golang.org/x/net|https://cs.opensource.google/go/x/net/+/v0.17.0:LICENSE|BSD-3-Clause
golang.org/x/oauth2|https://cs.opensource.google/go/x/oauth2/+/v0.13.0:LICENSE|BSD-3-Clause
golang.org/x/sync/errgroup|https://cs.opensource.google/go/x/sync/+/v0.3.0:LICENSE|BSD-3-Clause
golang.org/x/sync/semaphore|https://cs.opensource.google/go/x/sync/+/v0.3.0:LICENSE|BSD-3-Clause
Expand All @@ -435,7 +434,7 @@ golang.org/x/xerrors|https://cs.opensource.google/go/x/xerrors/+/04be3eba:LICENS
google.golang.org/api/googleapi|https://github.com/googleapis/google-api-go-client/blob/v0.143.0/LICENSE|BSD-3-Clause
google.golang.org/api/internal/third_party/uritemplates|https://github.com/googleapis/google-api-go-client/blob/v0.143.0/internal/third_party/uritemplates/LICENSE|BSD-3-Clause
google.golang.org/api|https://github.com/googleapis/google-api-go-client/blob/v0.143.0/LICENSE|BSD-3-Clause
google.golang.org/appengine|https://github.com/golang/appengine/blob/v1.6.7/LICENSE|Apache-2.0
google.golang.org/appengine|https://github.com/golang/appengine/blob/v1.6.8/LICENSE|Apache-2.0
google.golang.org/genproto/googleapis/api|https://github.com/googleapis/go-genproto/blob/007df8e322eb/googleapis/api/LICENSE|Apache-2.0
google.golang.org/genproto/googleapis/rpc/status|https://github.com/googleapis/go-genproto/blob/e6e6cdab5c13/googleapis/rpc/LICENSE|Apache-2.0
google.golang.org/genproto/googleapis/rpc|https://github.com/googleapis/go-genproto/blob/e6e6cdab5c13/googleapis/rpc/LICENSE|Apache-2.0
Expand Down
26 changes: 26 additions & 0 deletions examples/config-metrics-authn.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"distSpecVersion": "1.1.0-dev",
"storage": {
"rootDirectory": "/tmp/zot"
},
"http": {
"address": "127.0.0.1",
"port": "8080",
"auth": {
"htpasswd": {
"path": "test/data/htpasswd"
}
}
},
"log": {
"level": "debug"
},
"extensions": {
"metrics": {
"enable": true,
"prometheus": {
"path": "/metrics"
}
}
}
}
4 changes: 2 additions & 2 deletions pkg/api/authn.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func AuthHandler(ctlr *Controller) mux.MiddlewareFunc {
return bearerAuthHandler(ctlr)
}

return authnMiddleware.TryAuthnHandlers(ctlr)
return authnMiddleware.tryAuthnHandlers(ctlr)
}

func (amw *AuthnMiddleware) sessionAuthn(ctlr *Controller, userAc *reqCtx.UserAccessControl,
Expand Down Expand Up @@ -247,7 +247,7 @@ func (amw *AuthnMiddleware) basicAuthn(ctlr *Controller, userAc *reqCtx.UserAcce
return false, nil
}

func (amw *AuthnMiddleware) TryAuthnHandlers(ctlr *Controller) mux.MiddlewareFunc { //nolint: gocyclo
func (amw *AuthnMiddleware) tryAuthnHandlers(ctlr *Controller) mux.MiddlewareFunc { //nolint: gocyclo
// no password based authN, if neither LDAP nor HTTP BASIC is enabled
if !ctlr.Config.IsBasicAuthnEnabled() {
return noPasswdAuth(ctlr)
Expand Down
22 changes: 13 additions & 9 deletions pkg/api/authn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ func TestAPIKeys(t *testing.T) {
conf := config.New()
conf.HTTP.Port = port

htpasswdPath := test.MakeHtpasswdFile()
username := test.GenerateRandomString()
password := test.GenerateRandomString()
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
defer os.Remove(htpasswdPath)

mockOIDCServer, err := authutils.MockOIDCRun()
Expand Down Expand Up @@ -145,7 +147,7 @@ func TestAPIKeys(t *testing.T) {
Convey("API key retrieved with basic auth", func() {
resp, err := resty.R().
SetBody(reqBody).
SetBasicAuth("test", "test").
SetBasicAuth(username, password).
Post(baseURL + constants.APIKeyPath)
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
Expand All @@ -162,15 +164,15 @@ func TestAPIKeys(t *testing.T) {
So(email, ShouldNotBeEmpty)

resp, err = resty.R().
SetBasicAuth("test", apiKeyResponse.APIKey).
SetBasicAuth(username, apiKeyResponse.APIKey).
Get(baseURL + "/v2/_catalog")
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)

// get API key list with basic auth
resp, err = resty.R().
SetBasicAuth("test", "test").
SetBasicAuth(username, password).
Get(baseURL + constants.APIKeyPath)
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
Expand All @@ -189,7 +191,7 @@ func TestAPIKeys(t *testing.T) {
// add another one
resp, err = resty.R().
SetBody(reqBody).
SetBasicAuth("test", "test").
SetBasicAuth(username, password).
Post(baseURL + constants.APIKeyPath)
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
Expand All @@ -199,15 +201,15 @@ func TestAPIKeys(t *testing.T) {
So(err, ShouldBeNil)

resp, err = resty.R().
SetBasicAuth("test", apiKeyResponse.APIKey).
SetBasicAuth(username, apiKeyResponse.APIKey).
Get(baseURL + "/v2/_catalog")
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)

// get API key list with api key auth
resp, err = resty.R().
SetBasicAuth("test", apiKeyResponse.APIKey).
SetBasicAuth(username, apiKeyResponse.APIKey).
Get(baseURL + constants.APIKeyPath)
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
Expand Down Expand Up @@ -600,7 +602,7 @@ func TestAPIKeys(t *testing.T) {
So(len(apiKeyListResponse.APIKeys), ShouldEqual, 0)

resp, err = client.R().
SetBasicAuth("test", "test").
SetBasicAuth(username, password).
SetQueryParam("id", apiKeyResponse.UUID).
Delete(baseURL + constants.APIKeyPath)
So(err, ShouldBeNil)
Expand Down Expand Up @@ -832,7 +834,9 @@ func TestAPIKeys(t *testing.T) {
func TestAPIKeysOpenDBError(t *testing.T) {
Convey("Test API keys - unable to create database", t, func() {
conf := config.New()
htpasswdPath := test.MakeHtpasswdFile()
username := test.GenerateRandomString()
password := test.GenerateRandomString()
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
defer os.Remove(htpasswdPath)

mockOIDCServer, err := authutils.MockOIDCRun()
Expand Down
61 changes: 44 additions & 17 deletions pkg/api/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,10 @@ func (ac *AccessController) getAuthnMiddlewareContext(authnType string, request
func (ac *AccessController) isPermitted(userGroups []string, username, action string,
policyGroup config.PolicyGroup,
) bool {
var result bool

// check repo/system based policies
for _, p := range policyGroup.Policies {
if common.Contains(p.Users, username) && common.Contains(p.Actions, action) {
result = true

return result
return true
}
}

Expand All @@ -207,30 +203,24 @@ func (ac *AccessController) isPermitted(userGroups []string, username, action st
if common.Contains(p.Actions, action) {
for _, group := range p.Groups {
if common.Contains(userGroups, group) {
result = true

return result
return true
}
}
}
}
}

// check defaultPolicy
if !result {
if common.Contains(policyGroup.DefaultPolicy, action) && username != "" {
result = true
}
if common.Contains(policyGroup.DefaultPolicy, action) && username != "" {
return true
}

// check anonymousPolicy
if !result {
if common.Contains(policyGroup.AnonymousPolicy, action) && username == "" {
result = true
}
if common.Contains(policyGroup.AnonymousPolicy, action) && username == "" {
return true
}

return result
return false
}

func BaseAuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
Expand Down Expand Up @@ -343,3 +333,40 @@ func DistSpecAuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
})
}
}

func MetricsAuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
if ctlr.Config.HTTP.AccessControl == nil {
// allow access to authenticated user as anonymous policy does not exist
next.ServeHTTP(response, request)

return
}
if len(ctlr.Config.HTTP.AccessControl.Metrics.Users) == 0 {
log := ctlr.Log
log.Warn().Msg("auth is enabled but no metrics users in accessControl: /metrics is unaccesible")
common.AuthzFail(response, request, "", ctlr.Config.HTTP.Realm, ctlr.Config.HTTP.Auth.FailDelay)

return
}

// get access control context made in authn.go
userAc, err := reqCtx.UserAcFromContext(request.Context())
if err != nil { // should never happen
common.AuthzFail(response, request, "", ctlr.Config.HTTP.Realm, ctlr.Config.HTTP.Auth.FailDelay)

return
}

username := userAc.GetUsername()
if !common.Contains(ctlr.Config.HTTP.AccessControl.Metrics.Users, username) {
common.AuthzFail(response, request, username, ctlr.Config.HTTP.Realm, ctlr.Config.HTTP.Auth.FailDelay)

return
}

next.ServeHTTP(response, request) //nolint:contextcheck
})
}
}
5 changes: 5 additions & 0 deletions pkg/api/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ type AccessControlConfig struct {
Repositories Repositories `json:"repositories" mapstructure:"repositories"`
AdminPolicy Policy
Groups Groups
Metrics Metrics
}

func (config *AccessControlConfig) AnonymousPolicyExists() bool {
Expand Down Expand Up @@ -168,6 +169,10 @@ type Policy struct {
Groups []string
}

type Metrics struct {
Users []string
}

type Config struct {
DistSpecVersion string `json:"distSpecVersion" mapstructure:"distSpecVersion"`
GoVersion string
Expand Down
Loading

0 comments on commit 6b5fac6

Please sign in to comment.