diff --git a/Makefile b/Makefile index 6b0d2258c..01fea1ba0 100644 --- a/Makefile +++ b/Makefile @@ -408,7 +408,8 @@ run-blackbox-ci: check-blackbox-prerequisites binary binary-minimal cli $(BATS) $(BATS_FLAGS) test/blackbox/sync_replica_cluster.bats && \ $(BATS) $(BATS_FLAGS) test/blackbox/scrub.bats && \ $(BATS) $(BATS_FLAGS) test/blackbox/garbage_collect.bats && \ - $(BATS) $(BATS_FLAGS) test/blackbox/metrics.bats + $(BATS) $(BATS_FLAGS) test/blackbox/metrics.bats && \ + $(BATS) $(BATS_FLAGS) test/blackbox/metrics_minimal.bats .PHONY: run-blackbox-cloud-ci run-blackbox-cloud-ci: check-blackbox-prerequisites check-awslocal binary $(BATS) diff --git a/examples/config-policy.json b/examples/config-policy.json index eaf45e28a..231abbdc0 100644 --- a/examples/config-policy.json +++ b/examples/config-policy.json @@ -60,7 +60,7 @@ } ], "defaultPolicy": ["read"] - } + } }, "adminPolicy": { "users": ["admin"], diff --git a/pkg/api/controller_test.go b/pkg/api/controller_test.go index 41f395dbf..a0e06319d 100644 --- a/pkg/api/controller_test.go +++ b/pkg/api/controller_test.go @@ -2195,7 +2195,7 @@ func TestBearerAuth(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader := authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader := authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -2225,7 +2225,7 @@ func TestBearerAuth(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -2254,7 +2254,7 @@ func TestBearerAuth(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -2283,7 +2283,7 @@ func TestBearerAuth(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -2307,7 +2307,7 @@ func TestBearerAuth(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -2392,7 +2392,7 @@ func TestBearerAuthWithAllowReadAccess(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader := authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader := authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -2416,7 +2416,7 @@ func TestBearerAuthWithAllowReadAccess(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -2445,7 +2445,7 @@ func TestBearerAuthWithAllowReadAccess(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -2474,7 +2474,7 @@ func TestBearerAuthWithAllowReadAccess(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -2498,7 +2498,7 @@ func TestBearerAuthWithAllowReadAccess(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). diff --git a/pkg/api/errors/errors.go b/pkg/api/errors/errors.go index d6278fcaa..588293444 100644 --- a/pkg/api/errors/errors.go +++ b/pkg/api/errors/errors.go @@ -120,8 +120,8 @@ func NewError(code ErrorCode) *Error { UNAUTHORIZED: { Message: "authentication required", - Description: "The access controller was unable to authenticate the client." + - "Often this will be accompanied by a Www-Authenticate HTTP response header " + + Description: "The access controller was unable to authenticate the client. " + + "Often this will be accompanied by a WWW-Authenticate HTTP response header " + "indicating how to authenticate.", }, diff --git a/pkg/extensions/extension_metrics_disabled.go b/pkg/extensions/extension_metrics_disabled.go index 41cdccb8d..6d280b9ba 100644 --- a/pkg/extensions/extension_metrics_disabled.go +++ b/pkg/extensions/extension_metrics_disabled.go @@ -29,5 +29,6 @@ func SetupMetricsRoutes(conf *config.Config, router *mux.Router, zcommon.WriteJSON(w, http.StatusOK, m) } + router.Use(authFunc) router.HandleFunc("/metrics", getMetrics).Methods("GET") } diff --git a/pkg/extensions/extensions_test.go b/pkg/extensions/extensions_test.go index 04034b72c..9f1063675 100644 --- a/pkg/extensions/extensions_test.go +++ b/pkg/extensions/extensions_test.go @@ -799,7 +799,7 @@ func TestMgmtWithBearer(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader := authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader := authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -829,7 +829,7 @@ func TestMgmtWithBearer(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). @@ -853,7 +853,7 @@ func TestMgmtWithBearer(t *testing.T) { So(resp, ShouldNotBeNil) So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized) - authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("Www-Authenticate")) + authorizationHeader = authutils.ParseBearerAuthHeader(resp.Header().Get("WWW-Authenticate")) resp, err = resty.R(). SetQueryParam("service", authorizationHeader.Service). SetQueryParam("scope", authorizationHeader.Scope). diff --git a/test/blackbox/anonymous_policy.bats b/test/blackbox/anonymous_policy.bats index 577e6c867..305562f4c 100644 --- a/test/blackbox/anonymous_policy.bats +++ b/test/blackbox/anonymous_policy.bats @@ -5,6 +5,11 @@ load helpers_zot function verify_prerequisites { + if [ ! $(command -v htpasswd) ]; then + echo "you need to install htpasswd as a prerequisite to running the tests" >&3 + return 1 + fi + return 0 } @@ -15,15 +20,15 @@ function setup_file() { fi # Download test data to folder common for the entire suite, not just this file - skopeo --insecure-policy copy --format=oci docker://ghcr.io/project-zot/golang:1.20 oci:${TEST_DATA_DIR}/golang:1.20 + skopeo --insecure-policy copy --format=oci docker://ghcr.io/project-zot/test-images/busybox:1.36 oci:${TEST_DATA_DIR}/busybox:1.36 # Setup zot server local zot_root_dir=${BATS_FILE_TMPDIR}/zot local zot_config_file=${BATS_FILE_TMPDIR}/zot_config.json local oci_data_dir=${BATS_FILE_TMPDIR}/oci - local htpasswordFile=${BATS_FILE_TMPDIR}/htpasswd + local zot_htpasswd_file=${BATS_FILE_TMPDIR}/htpasswd mkdir -p ${zot_root_dir} mkdir -p ${oci_data_dir} - echo 'test:$2a$10$EIIoeCnvsIDAJeDL4T1sEOnL2fWOvsq7ACZbs3RT40BBBXg.Ih7V.' >> ${htpasswordFile} + htpasswd -Bbn ${AUTH_USER} ${AUTH_PASS} >> ${zot_htpasswd_file} cat > ${zot_config_file}<&3 + return 1 + fi + return 0 } @@ -15,15 +20,16 @@ function setup_file() { fi # Download test data to folder common for the entire suite, not just this file - skopeo --insecure-policy copy --format=oci docker://ghcr.io/project-zot/golang:1.20 oci:${TEST_DATA_DIR}/golang:1.20 + skopeo --insecure-policy copy --format=oci docker://ghcr.io/project-zot/test-images/busybox:1.36 oci:${TEST_DATA_DIR}/busybox:1.36 + # Setup zot server local zot_root_dir=${BATS_FILE_TMPDIR}/zot local zot_config_file=${BATS_FILE_TMPDIR}/zot_config.json local oci_data_dir=${BATS_FILE_TMPDIR}/oci - local htpasswordFile=${BATS_FILE_TMPDIR}/htpasswd + local zot_htpasswd_file=${BATS_FILE_TMPDIR}/htpasswd mkdir -p ${zot_root_dir} mkdir -p ${oci_data_dir} - echo 'test:$2a$10$EIIoeCnvsIDAJeDL4T1sEOnL2fWOvsq7ACZbs3RT40BBBXg.Ih7V.' >> ${htpasswordFile} + htpasswd -Bbn ${AUTH_USER} ${AUTH_PASS} >> ${zot_htpasswd_file} cat > ${zot_config_file}<&3 + return 1 + fi + return 0 } @@ -25,15 +30,16 @@ function setup_file() { fi # Download test data to folder common for the entire suite, not just this file - skopeo --insecure-policy copy --format=oci docker://ghcr.io/project-zot/golang:1.18 oci:${TEST_DATA_DIR}/golang:1.18 + skopeo --insecure-policy copy --format=oci docker://ghcr.io/project-zot/test-images/busybox:1.36 oci:${TEST_DATA_DIR}/busybox:1.36 + # Setup zot server local zot_root_dir=${BATS_FILE_TMPDIR}/zot local zot_config_file=${BATS_FILE_TMPDIR}/zot_config.json local oci_data_dir=${BATS_FILE_TMPDIR}/oci - local htpasswordFile=${BATS_FILE_TMPDIR}/htpasswd + local zot_htpasswd_file=${BATS_FILE_TMPDIR}/htpasswd mkdir -p ${zot_root_dir} mkdir -p ${oci_data_dir} - echo 'test:$2a$10$EIIoeCnvsIDAJeDL4T1sEOnL2fWOvsq7ACZbs3RT40BBBXg.Ih7V.' >> ${htpasswordFile} + htpasswd -Bbn ${AUTH_USER} ${AUTH_PASS} >> ${zot_htpasswd_file} cat > ${zot_config_file}<&3 + return 1 + fi + return 0 } @@ -19,14 +25,14 @@ function setup_file() { exit 1 fi - # Download test data to folder common for the entire suite, not just this file - skopeo --insecure-policy copy --format=oci docker://ghcr.io/project-zot/golang:1.20 oci:${TEST_DATA_DIR}/golang:1.20 - # Setup zot server zot_root_dir=${BATS_FILE_TMPDIR}/zot - echo ${zot_root_dir} + echo ${zot_root_dir} >&3 zot_log_file=${zot_root_dir}/zot-log.json zot_config_file=${BATS_FILE_TMPDIR}/zot_config.json + zot_htpasswd_file=${BATS_FILE_TMPDIR}/zot_htpasswd + htpasswd -Bbn ${AUTH_USER} ${AUTH_PASS} >> ${zot_htpasswd_file} + mkdir -p ${zot_root_dir} touch ${zot_log_file} cat >${zot_config_file} <&3 + return 1 + fi + + if [ ! $(command -v htpasswd) ]; then + echo "you need to install htpasswd as a prerequisite to running the tests" >&3 + return 1 + fi + + return 0 +} + +function setup_file() { + # verify prerequisites are available + if ! $(verify_prerequisites); then + exit 1 + fi + + # Setup zot server + zot_root_dir=${BATS_FILE_TMPDIR}/zot + echo ${zot_root_dir} >&3 + zot_log_file=${zot_root_dir}/zot-log.json + zot_config_file=${BATS_FILE_TMPDIR}/zot_config.json + zot_htpasswd_file=${BATS_FILE_TMPDIR}/zot_htpasswd + htpasswd -Bbn ${AUTH_USER} ${AUTH_PASS} >> ${zot_htpasswd_file} + + mkdir -p ${zot_root_dir} + touch ${zot_log_file} + cat >${zot_config_file} <> ${zot_htpasswd_file} - + htpasswd -Bbn ${AUTH_USER} ${AUTH_PASS} >> ${zot_htpasswd_file} + echo ${zot_root_dir} >&3 mkdir -p ${zot_root_dir} @@ -89,14 +92,14 @@ function teardown_file() { @test "push image with regclient" { run regctl registry set localhost:8080 --tls disabled - run regctl registry login localhost:8080 -u test -p test123 + run regctl registry login localhost:8080 -u ${AUTH_USER} -p ${AUTH_PASS} [ "$status" -eq 0 ] - run regctl image copy ocidir://${TEST_DATA_DIR}/golang:1.20 localhost:8080/test-regclient + run regctl image copy ocidir://${TEST_DATA_DIR}/busybox:1.36 localhost:8080/test-regclient [ "$status" -eq 0 ] } @test "pull image with regclient" { - run regctl image copy localhost:8080/test-regclient ocidir://${TEST_DATA_DIR}/golang:1.20 + run regctl image copy localhost:8080/test-regclient ocidir://${TEST_DATA_DIR}/busybox:latest [ "$status" -eq 0 ] }