mirror of
https://github.com/project-zot/zot.git
synced 2024-12-30 22:34:13 -05:00
fix: call notation-go libs instead of using notation binary (#1104)
fix: add loading notation path Signed-off-by: Andreea-Lupu <andreealupu1470@yahoo.com> Co-authored-by: Roxana Nemulescu <roxana.nemulescu@gmail.com>
This commit is contained in:
parent
2377d62344
commit
ee95ab0ffc
21 changed files with 1731 additions and 333 deletions
7
Makefile
7
Makefile
|
@ -12,6 +12,7 @@ STACKER := $(shell which stacker)
|
||||||
GOLINTER := $(TOOLSDIR)/bin/golangci-lint
|
GOLINTER := $(TOOLSDIR)/bin/golangci-lint
|
||||||
GOLINTER_VERSION := v1.49.0
|
GOLINTER_VERSION := v1.49.0
|
||||||
NOTATION := $(TOOLSDIR)/bin/notation
|
NOTATION := $(TOOLSDIR)/bin/notation
|
||||||
|
NOTATION_VERSION := 1.0.0-rc.1
|
||||||
COSIGN := $(TOOLSDIR)/bin/cosign
|
COSIGN := $(TOOLSDIR)/bin/cosign
|
||||||
HELM := $(TOOLSDIR)/bin/helm
|
HELM := $(TOOLSDIR)/bin/helm
|
||||||
ORAS := $(TOOLSDIR)/bin/oras
|
ORAS := $(TOOLSDIR)/bin/oras
|
||||||
|
@ -85,7 +86,7 @@ exporter-minimal: modcheck build-metadata
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test: $(if $(findstring ui,$(EXTENSIONS)), ui)
|
test: $(if $(findstring ui,$(EXTENSIONS)), ui)
|
||||||
test: check-skopeo $(TESTDATA) $(NOTATION) $(ORAS)
|
test: check-skopeo $(TESTDATA) $(ORAS)
|
||||||
go test -failfast -tags $(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-extended.txt -covermode=atomic ./...
|
go test -failfast -tags $(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-extended.txt -covermode=atomic ./...
|
||||||
go test -failfast -tags containers_image_openpgp -v -trimpath -race -cover -coverpkg ./... -coverprofile=coverage-minimal.txt -covermode=atomic ./...
|
go test -failfast -tags containers_image_openpgp -v -trimpath -race -cover -coverpkg ./... -coverprofile=coverage-minimal.txt -covermode=atomic ./...
|
||||||
# development-mode unit tests possibly using failure injection
|
# development-mode unit tests possibly using failure injection
|
||||||
|
@ -95,7 +96,7 @@ test: check-skopeo $(TESTDATA) $(NOTATION) $(ORAS)
|
||||||
|
|
||||||
.PHONY: privileged-test
|
.PHONY: privileged-test
|
||||||
privileged-test: $(if $(findstring ui,$(EXTENSIONS)), ui)
|
privileged-test: $(if $(findstring ui,$(EXTENSIONS)), ui)
|
||||||
privileged-test: check-skopeo $(TESTDATA) $(NOTATION)
|
privileged-test: check-skopeo $(TESTDATA)
|
||||||
go test -failfast -tags needprivileges,$(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-dev-needprivileges.txt -covermode=atomic ./pkg/storage/... ./pkg/cli/... -run ^TestElevatedPrivileges
|
go test -failfast -tags needprivileges,$(EXTENSIONS),containers_image_openpgp -v -trimpath -race -timeout 15m -cover -coverpkg ./... -coverprofile=coverage-dev-needprivileges.txt -covermode=atomic ./pkg/storage/... ./pkg/cli/... -run ^TestElevatedPrivileges
|
||||||
|
|
||||||
$(TESTDATA): check-skopeo
|
$(TESTDATA): check-skopeo
|
||||||
|
@ -115,7 +116,7 @@ check-skopeo:
|
||||||
|
|
||||||
$(NOTATION):
|
$(NOTATION):
|
||||||
mkdir -p $(TOOLSDIR)/bin
|
mkdir -p $(TOOLSDIR)/bin
|
||||||
curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v0.7.1-alpha.1/notation_0.7.1-alpha.1_linux_amd64.tar.gz
|
curl -Lo notation.tar.gz https://github.com/notaryproject/notation/releases/download/v$(NOTATION_VERSION)/notation_$(NOTATION_VERSION)_linux_amd64.tar.gz
|
||||||
tar xvzf notation.tar.gz -C $(TOOLSDIR)/bin notation
|
tar xvzf notation.tar.gz -C $(TOOLSDIR)/bin notation
|
||||||
rm notation.tar.gz
|
rm notation.tar.gz
|
||||||
|
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -55,10 +55,11 @@ require (
|
||||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.18.1
|
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.18.1
|
||||||
github.com/containers/image/v5 v5.23.0
|
github.com/containers/image/v5 v5.23.0
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/notaryproject/notation-go v0.12.0-beta.1
|
github.com/notaryproject/notation-go v1.0.0-rc.1
|
||||||
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20230117141039-067a0f5b0e25
|
github.com/opencontainers/distribution-spec/specs-go v0.0.0-20230117141039-067a0f5b0e25
|
||||||
github.com/sigstore/cosign v1.13.1
|
github.com/sigstore/cosign v1.13.1
|
||||||
github.com/swaggo/http-swagger v1.3.3
|
github.com/swaggo/http-swagger v1.3.3
|
||||||
|
oras.land/oras-go/v2 v2.0.0-rc.5
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
@ -376,7 +377,7 @@ require (
|
||||||
github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect
|
github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect
|
||||||
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect
|
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect
|
||||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 // indirect
|
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 // indirect
|
||||||
github.com/notaryproject/notation-core-go v0.2.0-beta.1 // indirect
|
github.com/notaryproject/notation-core-go v1.0.0-rc.1
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/open-policy-agent/opa v0.45.0 // indirect
|
github.com/open-policy-agent/opa v0.45.0 // indirect
|
||||||
github.com/opencontainers/runc v1.1.4 // indirect
|
github.com/opencontainers/runc v1.1.4 // indirect
|
||||||
|
@ -495,7 +496,6 @@ require (
|
||||||
k8s.io/klog/v2 v2.70.1 // indirect
|
k8s.io/klog/v2 v2.70.1 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
|
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
|
||||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
|
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
|
||||||
oras.land/oras-go/v2 v2.0.0-rc.3 // indirect
|
|
||||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||||
sigs.k8s.io/release-utils v0.7.3 // indirect
|
sigs.k8s.io/release-utils v0.7.3 // indirect
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||||
|
|
15
go.sum
15
go.sum
|
@ -839,6 +839,7 @@ github.com/fullstorydev/grpcurl v1.8.1/go.mod h1:3BWhvHZwNO7iLXaQlojdg5NA6SxUDeP
|
||||||
github.com/fullstorydev/grpcurl v1.8.6/go.mod h1:WhP7fRQdhxz2TkL97u+TCb505sxfH78W1usyoB3tepw=
|
github.com/fullstorydev/grpcurl v1.8.6/go.mod h1:WhP7fRQdhxz2TkL97u+TCb505sxfH78W1usyoB3tepw=
|
||||||
github.com/fullstorydev/grpcurl v1.8.7 h1:xJWosq3BQovQ4QrdPO72OrPiWuGgEsxY8ldYsJbPrqI=
|
github.com/fullstorydev/grpcurl v1.8.7 h1:xJWosq3BQovQ4QrdPO72OrPiWuGgEsxY8ldYsJbPrqI=
|
||||||
github.com/fullstorydev/grpcurl v1.8.7/go.mod h1:pVtM4qe3CMoLaIzYS8uvTuDj2jVYmXqMUkZeijnXp/E=
|
github.com/fullstorydev/grpcurl v1.8.7/go.mod h1:pVtM4qe3CMoLaIzYS8uvTuDj2jVYmXqMUkZeijnXp/E=
|
||||||
|
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
|
||||||
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
|
||||||
github.com/getlantern/deepcopy v0.0.0-20160317154340-7f45deb8130a h1:yU/FENpkHYISWsQrbr3pcZOBj0EuRjPzNc1+dTCLu44=
|
github.com/getlantern/deepcopy v0.0.0-20160317154340-7f45deb8130a h1:yU/FENpkHYISWsQrbr3pcZOBj0EuRjPzNc1+dTCLu44=
|
||||||
github.com/getlantern/deepcopy v0.0.0-20160317154340-7f45deb8130a/go.mod h1:AEugkNu3BjBxyz958nJ5holD9PRjta6iprcoUauDbU4=
|
github.com/getlantern/deepcopy v0.0.0-20160317154340-7f45deb8130a/go.mod h1:AEugkNu3BjBxyz958nJ5holD9PRjta6iprcoUauDbU4=
|
||||||
|
@ -1632,10 +1633,10 @@ github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484 h1:D9EvfGQvlkKaD
|
||||||
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484/go.mod h1:O1EljZ+oHprtxDDPHiMWVo/5dBT6PlvWX5PSwj80aBA=
|
github.com/nmcclain/asn1-ber v0.0.0-20170104154839-2661553a0484/go.mod h1:O1EljZ+oHprtxDDPHiMWVo/5dBT6PlvWX5PSwj80aBA=
|
||||||
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba h1:DO8NFYdcRv1dnyAINJIBm6Bw2XibtLvQniNFGzf2W8E=
|
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba h1:DO8NFYdcRv1dnyAINJIBm6Bw2XibtLvQniNFGzf2W8E=
|
||||||
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba/go.mod h1:4S0XndRL8HNOaQBfdViJ2F/GPCgL524xlXRuXFH12/U=
|
github.com/nmcclain/ldap v0.0.0-20210720162743-7f8d1e44eeba/go.mod h1:4S0XndRL8HNOaQBfdViJ2F/GPCgL524xlXRuXFH12/U=
|
||||||
github.com/notaryproject/notation-core-go v0.2.0-beta.1 h1:8tFxNycWCcPLti9ZYST5kjkX2wMXtX9YPvMjiBAQ1tA=
|
github.com/notaryproject/notation-core-go v1.0.0-rc.1 h1:ACi0gr6mD1bzp9+gu3P0meJ/N6iWHlyM9zgtdnooNAA=
|
||||||
github.com/notaryproject/notation-core-go v0.2.0-beta.1/go.mod h1:s8DZptmN1rZS0tBLTPt/w+d4o6eAcGWTYYJlXaJhQ4U=
|
github.com/notaryproject/notation-core-go v1.0.0-rc.1/go.mod h1:n8Gbvl9sKa00KptkKEL5XKUyMTIALe74QipKauE2rj4=
|
||||||
github.com/notaryproject/notation-go v0.12.0-beta.1 h1:LATXX7gt/Y7a+vqLVN4Ydmd6GfaPAFRdKgUEjaEYhUM=
|
github.com/notaryproject/notation-go v1.0.0-rc.1 h1:WobIGCUPcPUDCD2qGMCccTfLm2J5y1bsh4SFVsyMIaA=
|
||||||
github.com/notaryproject/notation-go v0.12.0-beta.1/go.mod h1:sfOLDfdt7IXtzU9tyGwhsWDYY357+OWr1ktCfHfLdOk=
|
github.com/notaryproject/notation-go v1.0.0-rc.1/go.mod h1:xk4q0GXqGfEiy7WmyHi3Om3OM2dgHk0OHL6NIiJv5vA=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
@ -2089,6 +2090,7 @@ github.com/vbauerster/mpb/v7 v7.5.3 h1:BkGfmb6nMrrBQDFECR/Q7RkKCw7ylMetCb4079CGs
|
||||||
github.com/vbauerster/mpb/v7 v7.5.3/go.mod h1:i+h4QY6lmLvBNK2ah1fSreiw3ajskRlBp9AhY/PnuOE=
|
github.com/vbauerster/mpb/v7 v7.5.3/go.mod h1:i+h4QY6lmLvBNK2ah1fSreiw3ajskRlBp9AhY/PnuOE=
|
||||||
github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4=
|
github.com/vektah/gqlparser/v2 v2.5.1 h1:ZGu+bquAY23jsxDRcYpWjttRZrUz07LbiY77gUOHcr4=
|
||||||
github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs=
|
github.com/vektah/gqlparser/v2 v2.5.1/go.mod h1:mPgqFBu/woKTVYWyNk8cO3kh4S/f4aRFZrvOnp3hmCs=
|
||||||
|
github.com/veraison/go-cose v1.0.0-rc.2 h1:zH3QmP4N5kwpdGauceIT3aJm8iUyV9OqpUOb+7CF7rQ=
|
||||||
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||||
|
@ -2106,6 +2108,7 @@ github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
|
||||||
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
|
||||||
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||||
github.com/xanzy/go-gitlab v0.73.1 h1:UMagqUZLJdjss1SovIC+kJCH4k2AZWXl58gJd38Y/hI=
|
github.com/xanzy/go-gitlab v0.73.1 h1:UMagqUZLJdjss1SovIC+kJCH4k2AZWXl58gJd38Y/hI=
|
||||||
github.com/xanzy/go-gitlab v0.73.1/go.mod h1:d/a0vswScO7Agg1CZNz15Ic6SSvBG9vfw8egL99t4kA=
|
github.com/xanzy/go-gitlab v0.73.1/go.mod h1:d/a0vswScO7Agg1CZNz15Ic6SSvBG9vfw8egL99t4kA=
|
||||||
|
@ -3194,8 +3197,8 @@ modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
|
||||||
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=
|
||||||
oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4=
|
oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4=
|
||||||
oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc=
|
oras.land/oras-go v1.2.0/go.mod h1:pFNs7oHp2dYsYMSS82HaX5l4mpnGO7hbpPN6EWH2ltc=
|
||||||
oras.land/oras-go/v2 v2.0.0-rc.3 h1:O4GeIwJ9Ge7rbCkqa/M7DLrL55ww+ZEc+Rhc63OYitU=
|
oras.land/oras-go/v2 v2.0.0-rc.5 h1:enT2ZMNo383bH3INm1/+mw4d09AaMbqx0BMhsgEDUSg=
|
||||||
oras.land/oras-go/v2 v2.0.0-rc.3/go.mod h1:PrY+cCglzK/DrQoJUtxbYVbL94ZHecVS3eJR01RglpE=
|
oras.land/oras-go/v2 v2.0.0-rc.5/go.mod h1:YGHvWBGuqRlZgUyXUIoKsR3lcuCOb3DAtG0SEsEw1iY=
|
||||||
pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
|
pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -3964,53 +3963,28 @@ func TestImageSignatures(t *testing.T) {
|
||||||
tdir := t.TempDir()
|
tdir := t.TempDir()
|
||||||
_ = os.Chdir(tdir)
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
// "notation" (notaryv2) doesn't yet support exported apis, so use the binary instead
|
test.NotationPathLock.Lock()
|
||||||
notPath, err := exec.LookPath("notation")
|
defer test.NotationPathLock.Unlock()
|
||||||
So(notPath, ShouldNotBeNil)
|
|
||||||
|
test.LoadNotationPath(tdir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(tdir, "good")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
os.Setenv("XDG_CONFIG_HOME", tdir)
|
err = test.GenerateNotationCerts(tdir, "bad")
|
||||||
|
|
||||||
// generate a keypair
|
|
||||||
cmd := exec.Command("notation", "cert", "generate-test", "--trust", "good")
|
|
||||||
err = cmd.Run()
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
// generate another keypair
|
|
||||||
cmd = exec.Command("notation", "cert", "generate-test", "--trust", "bad")
|
|
||||||
err = cmd.Run()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
// sign the image
|
|
||||||
image := fmt.Sprintf("localhost:%s/%s:%s", port, repoName, "1.0")
|
image := fmt.Sprintf("localhost:%s/%s:%s", port, repoName, "1.0")
|
||||||
cmd = exec.Command("notation", "sign", "--key", "good", "--plain-http", image)
|
err = test.SignWithNotation("good", image, tdir)
|
||||||
err = cmd.Run()
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
// verify the image
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
msg := string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
|
||||||
|
|
||||||
// check list
|
// check list
|
||||||
cmd = exec.Command("notation", "list", "--plain-http", image)
|
sigs, err := test.ListNotarySignatures(image, tdir)
|
||||||
out, err = cmd.CombinedOutput()
|
So(len(sigs), ShouldEqual, 1)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
msg = strings.TrimSuffix(string(out), "\n")
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
_, err = godigest.Parse(msg)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
// verify the image with incorrect key
|
|
||||||
cmd = exec.Command("notation", "verify", "--cert", "bad", "--plain-http", image)
|
|
||||||
out, err = cmd.CombinedOutput()
|
|
||||||
So(err, ShouldNotBeNil)
|
|
||||||
msg = string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
So(strings.Contains(msg, "verification failure"), ShouldBeTrue)
|
|
||||||
|
|
||||||
// check unsupported manifest media type
|
// check unsupported manifest media type
|
||||||
resp, err := resty.R().SetHeader("Content-Type", "application/vnd.unsupported.image.manifest.v1+json").
|
resp, err := resty.R().SetHeader("Content-Type", "application/vnd.unsupported.image.manifest.v1+json").
|
||||||
|
@ -4027,49 +4001,45 @@ func TestImageSignatures(t *testing.T) {
|
||||||
Convey("Validate corrupted signature", func() {
|
Convey("Validate corrupted signature", func() {
|
||||||
// verify with corrupted signature
|
// verify with corrupted signature
|
||||||
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
||||||
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, repoName, digest.String()))
|
fmt.Sprintf("%s/v2/%s/referrers/%s", baseURL, repoName, digest.String()))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||||
var refs api.ReferenceList
|
var refs ispec.Index
|
||||||
err = json.Unmarshal(resp.Body(), &refs)
|
err = json.Unmarshal(resp.Body(), &refs)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(refs.References), ShouldEqual, 1)
|
So(len(refs.Manifests), ShouldEqual, 1)
|
||||||
err = os.WriteFile(path.Join(dir, repoName, "blobs",
|
err = os.WriteFile(path.Join(dir, repoName, "blobs",
|
||||||
strings.ReplaceAll(refs.References[0].Digest.String(), ":", "/")), []byte("corrupt"), 0o600)
|
strings.ReplaceAll(refs.Manifests[0].Digest.String(), ":", "/")), []byte("corrupt"), 0o600)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
||||||
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, repoName, digest.String()))
|
fmt.Sprintf("%s/v2/%s/referrers/%s", baseURL, repoName, digest.String()))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
So(resp.StatusCode(), ShouldEqual, http.StatusInternalServerError)
|
||||||
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
|
||||||
out, err = cmd.CombinedOutput()
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
msg = string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Validate deleted signature", func() {
|
Convey("Validate deleted signature", func() {
|
||||||
// verify with corrupted signature
|
// verify with corrupted signature
|
||||||
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
||||||
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, repoName, digest.String()))
|
fmt.Sprintf("%s/v2/%s/referrers/%s", baseURL, repoName, digest.String()))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||||
var refs api.ReferenceList
|
var refs ispec.Index
|
||||||
err = json.Unmarshal(resp.Body(), &refs)
|
err = json.Unmarshal(resp.Body(), &refs)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(refs.References), ShouldEqual, 1)
|
So(len(refs.Manifests), ShouldEqual, 1)
|
||||||
err = os.Remove(path.Join(dir, repoName, "blobs",
|
err = os.Remove(path.Join(dir, repoName, "blobs",
|
||||||
strings.ReplaceAll(refs.References[0].Digest.String(), ":", "/")))
|
strings.ReplaceAll(refs.Manifests[0].Digest.String(), ":", "/")))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
||||||
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, repoName, digest.String()))
|
fmt.Sprintf("%s/v2/%s/referrers/%s", baseURL, repoName, digest.String()))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
||||||
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
|
||||||
out, err = cmd.CombinedOutput()
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
msg = string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -5192,7 +5162,7 @@ func TestManifestImageIndex(t *testing.T) {
|
||||||
Layers: layers,
|
Layers: layers,
|
||||||
Manifest: manifest,
|
Manifest: manifest,
|
||||||
}, baseURL, repoName)
|
}, baseURL, repoName)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
content, err = json.Marshal(manifest)
|
content, err = json.Marshal(manifest)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -5217,7 +5187,7 @@ func TestManifestImageIndex(t *testing.T) {
|
||||||
Layers: layers,
|
Layers: layers,
|
||||||
Manifest: manifest,
|
Manifest: manifest,
|
||||||
}, baseURL, repoName)
|
}, baseURL, repoName)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
content, err = json.Marshal(manifest)
|
content, err = json.Marshal(manifest)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -5273,7 +5243,7 @@ func TestManifestImageIndex(t *testing.T) {
|
||||||
Layers: layers,
|
Layers: layers,
|
||||||
Manifest: manifest,
|
Manifest: manifest,
|
||||||
}, baseURL, repoName)
|
}, baseURL, repoName)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
content, err = json.Marshal(manifest)
|
content, err = json.Marshal(manifest)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
digest = godigest.FromBytes(content)
|
digest = godigest.FromBytes(content)
|
||||||
|
@ -5446,7 +5416,7 @@ func TestManifestImageIndex(t *testing.T) {
|
||||||
Layers: layers,
|
Layers: layers,
|
||||||
Manifest: manifest,
|
Manifest: manifest,
|
||||||
}, baseURL, repoName)
|
}, baseURL, repoName)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
content, err = json.Marshal(manifest)
|
content, err = json.Marshal(manifest)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
digest = godigest.FromBytes(content)
|
digest = godigest.FromBytes(content)
|
||||||
|
@ -6200,23 +6170,17 @@ func TestGCSignaturesAndUntaggedManifests(t *testing.T) {
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
// "notation" (notaryv2) doesn't yet support exported apis, so use the binary instead
|
test.NotationPathLock.Lock()
|
||||||
notPath, err := exec.LookPath("notation")
|
defer test.NotationPathLock.Unlock()
|
||||||
So(notPath, ShouldNotBeNil)
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
os.Setenv("XDG_CONFIG_HOME", tdir)
|
test.LoadNotationPath(tdir)
|
||||||
|
|
||||||
// generate a keypair
|
// generate a keypair
|
||||||
cmd := exec.Command("notation", "cert", "generate-test", "--trust", "good")
|
err = test.GenerateNotationCerts(tdir, "good")
|
||||||
output, err := cmd.CombinedOutput()
|
|
||||||
t.Log(string(output))
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
// sign the image
|
// sign the image
|
||||||
cmd = exec.Command("notation", "sign", "--key", "good", "--plain-http", image)
|
err = test.SignWithNotation("good", image, tdir)
|
||||||
output, err = cmd.CombinedOutput()
|
|
||||||
t.Log(string(output))
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
// get cosign signature manifest
|
// get cosign signature manifest
|
||||||
|
@ -6228,15 +6192,21 @@ func TestGCSignaturesAndUntaggedManifests(t *testing.T) {
|
||||||
|
|
||||||
// get notation signature manifest
|
// get notation signature manifest
|
||||||
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
||||||
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, repoName, digest.String()))
|
fmt.Sprintf("%s/v2/%s/referrers/%s", baseURL, repoName, digest.String()))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||||
|
|
||||||
|
var index ispec.Index
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &index)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(len(index.Manifests), ShouldEqual, 1)
|
||||||
|
|
||||||
Convey("Trigger gcNotationSignatures() error", func() {
|
Convey("Trigger gcNotationSignatures() error", func() {
|
||||||
var refs api.ReferenceList
|
var refs ispec.Index
|
||||||
err = json.Unmarshal(resp.Body(), &refs)
|
err = json.Unmarshal(resp.Body(), &refs)
|
||||||
|
|
||||||
err := os.Chmod(path.Join(dir, repoName, "blobs", "sha256", refs.References[0].Digest.Encoded()), 0o000)
|
err := os.Chmod(path.Join(dir, repoName, "blobs", "sha256", refs.Manifests[0].Digest.Encoded()), 0o000)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
// trigger gc
|
// trigger gc
|
||||||
|
@ -6250,9 +6220,26 @@ func TestGCSignaturesAndUntaggedManifests(t *testing.T) {
|
||||||
Manifest: manifest,
|
Manifest: manifest,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
}, baseURL, repoName)
|
}, baseURL, repoName)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = os.Chmod(path.Join(dir, repoName, "blobs", "sha256", refs.Manifests[0].Digest.Encoded()), 0o755)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
err = os.Chmod(path.Join(dir, repoName, "blobs", "sha256", refs.References[0].Digest.Encoded()), 0o755)
|
content, err := os.ReadFile(path.Join(dir, repoName, "blobs", "sha256", refs.Manifests[0].Digest.Encoded()))
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.WriteFile(path.Join(dir, repoName, "blobs", "sha256", refs.Manifests[0].Digest.Encoded()), []byte("corrupt"), 0o600) //nolint:lll
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.UploadImage(
|
||||||
|
test.Image{
|
||||||
|
Config: cfg,
|
||||||
|
Layers: layers,
|
||||||
|
Manifest: manifest,
|
||||||
|
Tag: tag,
|
||||||
|
}, baseURL, repoName)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = os.WriteFile(path.Join(dir, repoName, "blobs", "sha256", refs.Manifests[0].Digest.Encoded()), content, 0o600)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -6296,14 +6283,22 @@ func TestGCSignaturesAndUntaggedManifests(t *testing.T) {
|
||||||
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
||||||
|
|
||||||
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
||||||
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, repoName, digest.String()))
|
fmt.Sprintf("%s/v2/%s/referrers/%s", baseURL, repoName, digest.String()))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &index)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(len(index.Manifests), ShouldEqual, 0)
|
||||||
|
|
||||||
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
|
||||||
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, repoName, newManifestDigest.String()))
|
fmt.Sprintf("%s/v2/%s/referrers/%s", baseURL, repoName, newManifestDigest.String()))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &index)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(len(index.Manifests), ShouldEqual, 0)
|
||||||
|
|
||||||
// untagged image should also be gc'ed
|
// untagged image should also be gc'ed
|
||||||
resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/%s/manifests/%s", repoName, untaggedManifestDigest))
|
resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/%s/manifests/%s", repoName, untaggedManifestDigest))
|
||||||
|
|
|
@ -431,7 +431,7 @@ func getReferrers(ctx context.Context, routeHandler *RouteHandler,
|
||||||
artifactTypes []string,
|
artifactTypes []string,
|
||||||
) (ispec.Index, error) {
|
) (ispec.Index, error) {
|
||||||
references, err := imgStore.GetReferrers(name, digest, artifactTypes)
|
references, err := imgStore.GetReferrers(name, digest, artifactTypes)
|
||||||
if err != nil {
|
if err != nil || len(references.Manifests) == 0 {
|
||||||
if routeHandler.c.Config.Extensions != nil &&
|
if routeHandler.c.Config.Extensions != nil &&
|
||||||
routeHandler.c.Config.Extensions.Sync != nil &&
|
routeHandler.c.Config.Extensions.Sync != nil &&
|
||||||
*routeHandler.c.Config.Extensions.Sync.Enable {
|
*routeHandler.c.Config.Extensions.Sync.Enable {
|
||||||
|
@ -495,11 +495,11 @@ func (rh *RouteHandler) GetReferrers(response http.ResponseWriter, request *http
|
||||||
if errors.Is(err, zerr.ErrManifestNotFound) || errors.Is(err, zerr.ErrRepoNotFound) {
|
if errors.Is(err, zerr.ErrManifestNotFound) || errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("manifest not found")
|
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("manifest not found")
|
||||||
response.WriteHeader(http.StatusNotFound)
|
response.WriteHeader(http.StatusNotFound)
|
||||||
|
} else {
|
||||||
|
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("unable to get references")
|
||||||
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("unable to get references")
|
|
||||||
response.WriteHeader(http.StatusInternalServerError)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1662,7 +1662,7 @@ func getOrasReferrers(ctx context.Context, routeHandler *RouteHandler,
|
||||||
if routeHandler.c.Config.Extensions != nil &&
|
if routeHandler.c.Config.Extensions != nil &&
|
||||||
routeHandler.c.Config.Extensions.Sync != nil &&
|
routeHandler.c.Config.Extensions.Sync != nil &&
|
||||||
*routeHandler.c.Config.Extensions.Sync.Enable {
|
*routeHandler.c.Config.Extensions.Sync.Enable {
|
||||||
routeHandler.c.Log.Info().Msgf("signature not found, trying to get signature %s:%s by syncing on demand",
|
routeHandler.c.Log.Info().Msgf("artifact not found, trying to get artifact %s:%s by syncing on demand",
|
||||||
name, digest.String())
|
name, digest.String())
|
||||||
|
|
||||||
errSync := ext.SyncOneImage(ctx, routeHandler.c.Config, routeHandler.c.StoreController,
|
errSync := ext.SyncOneImage(ctx, routeHandler.c.Config, routeHandler.c.StoreController,
|
||||||
|
@ -1736,8 +1736,13 @@ func (rh *RouteHandler) GetOrasReferrers(response http.ResponseWriter, request *
|
||||||
|
|
||||||
refs, err := getOrasReferrers(request.Context(), rh, imgStore, name, digest, artifactType) //nolint:contextcheck
|
refs, err := getOrasReferrers(request.Context(), rh, imgStore, name, digest, artifactType) //nolint:contextcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("unable to get references")
|
if errors.Is(err, zerr.ErrManifestNotFound) || errors.Is(err, zerr.ErrRepoNotFound) {
|
||||||
response.WriteHeader(http.StatusNotFound)
|
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("manifest not found")
|
||||||
|
response.WriteHeader(http.StatusNotFound)
|
||||||
|
} else {
|
||||||
|
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("unable to get references")
|
||||||
|
response.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
notreg "github.com/notaryproject/notation-go/registry"
|
notreg "github.com/notaryproject/notation-go/registry"
|
||||||
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/sigstore/cosign/pkg/oci/remote"
|
"github.com/sigstore/cosign/pkg/oci/remote"
|
||||||
|
|
||||||
zotErrors "zotregistry.io/zot/errors"
|
zotErrors "zotregistry.io/zot/errors"
|
||||||
"zotregistry.io/zot/pkg/api"
|
|
||||||
"zotregistry.io/zot/pkg/common"
|
"zotregistry.io/zot/pkg/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -236,14 +236,14 @@ func (p *requestsPool) doJob(ctx context.Context, job *manifestJob) {
|
||||||
isSigned = true
|
isSigned = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var referrers api.ReferenceList
|
var referrers ispec.Index
|
||||||
|
|
||||||
if !isSigned {
|
if !isSigned {
|
||||||
_, err = makeGETRequest(ctx, fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers?artifactType=%s",
|
_, err = makeGETRequest(ctx, fmt.Sprintf("%s/v2/%s/referrers/%s?artifactType=%s",
|
||||||
*job.config.servURL, job.imageName, digestStr, notreg.ArtifactTypeNotation), job.username, job.password,
|
*job.config.servURL, job.imageName, digestStr, notreg.ArtifactTypeNotation), job.username, job.password,
|
||||||
*job.config.verifyTLS, *job.config.debug, &referrers, job.config.resultWriter)
|
*job.config.verifyTLS, *job.config.debug, &referrers, job.config.resultWriter)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, reference := range referrers.References {
|
for _, reference := range referrers.Manifests {
|
||||||
if reference.ArtifactType == notreg.ArtifactTypeNotation {
|
if reference.ArtifactType == notreg.ArtifactTypeNotation {
|
||||||
isSigned = true
|
isSigned = true
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -254,46 +253,6 @@ func TestSearchImageCmd(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func SignImageUsingNotary(repoTag, port string) error {
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() { _ = os.Chdir(cwd) }()
|
|
||||||
|
|
||||||
tdir, err := os.MkdirTemp("", "notation")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer os.RemoveAll(tdir)
|
|
||||||
|
|
||||||
_ = os.Chdir(tdir)
|
|
||||||
|
|
||||||
_, err = exec.LookPath("notation")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Setenv("XDG_CONFIG_HOME", tdir)
|
|
||||||
|
|
||||||
// generate a keypair
|
|
||||||
cmd := exec.Command("notation", "cert", "generate-test", "--trust", "notation-sign-test")
|
|
||||||
|
|
||||||
err = cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// sign the image
|
|
||||||
image := fmt.Sprintf("localhost:%s/%s", port, repoTag)
|
|
||||||
|
|
||||||
cmd = exec.Command("notation", "sign", "--key", "notation-sign-test", "--plain-http", image)
|
|
||||||
|
|
||||||
return cmd.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSignature(t *testing.T) {
|
func TestSignature(t *testing.T) {
|
||||||
Convey("Test from real server", t, func() {
|
Convey("Test from real server", t, func() {
|
||||||
currentWorkingDir, err := os.Getwd()
|
currentWorkingDir, err := os.Getwd()
|
||||||
|
@ -427,7 +386,7 @@ func TestSignature(t *testing.T) {
|
||||||
digest := godigest.FromBytes(content)
|
digest := godigest.FromBytes(content)
|
||||||
So(digest, ShouldNotBeNil)
|
So(digest, ShouldNotBeNil)
|
||||||
|
|
||||||
err = SignImageUsingNotary("repo7:0.0.1", port)
|
err = test.SignImageUsingNotary("repo7:0.0.1", port)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
|
t.Logf("%s", ctlr.Config.Storage.RootDirectory)
|
||||||
|
|
|
@ -24,7 +24,6 @@ import (
|
||||||
godigest "github.com/opencontainers/go-digest"
|
godigest "github.com/opencontainers/go-digest"
|
||||||
"github.com/opencontainers/image-spec/specs-go"
|
"github.com/opencontainers/image-spec/specs-go"
|
||||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
"gopkg.in/resty.v1"
|
"gopkg.in/resty.v1"
|
||||||
|
|
||||||
|
@ -4085,7 +4084,7 @@ func TestRepoDBWhenPushingImages(t *testing.T) {
|
||||||
baseURL,
|
baseURL,
|
||||||
"repo1",
|
"repo1",
|
||||||
)
|
)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("SetManifestMeta succeeds but SetRepoTag fails", func() {
|
Convey("SetManifestMeta succeeds but SetRepoTag fails", func() {
|
||||||
|
@ -4458,10 +4457,10 @@ func TestRepoDBWhenDeletingImages(t *testing.T) {
|
||||||
|
|
||||||
signatureReference := ""
|
signatureReference := ""
|
||||||
|
|
||||||
var sigManifestContent artifactspec.Manifest
|
var sigManifestContent ispec.Artifact
|
||||||
|
|
||||||
for _, manifest := range indexContent.Manifests {
|
for _, manifest := range indexContent.Manifests {
|
||||||
if manifest.MediaType == artifactspec.MediaTypeArtifactManifest {
|
if manifest.MediaType == ispec.MediaTypeArtifactManifest {
|
||||||
signatureReference = manifest.Digest.String()
|
signatureReference = manifest.Digest.String()
|
||||||
manifestBlob, _, _, err := storage.GetImageManifest(repo, signatureReference)
|
manifestBlob, _, _, err := storage.GetImageManifest(repo, signatureReference)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -4854,6 +4853,82 @@ func TestBaseOciLayoutUtils(t *testing.T) {
|
||||||
_, err := olu.GetImageInfo("", "")
|
_, err := olu.GetImageInfo("", "")
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("CheckManifestSignature: notation", t, func() {
|
||||||
|
// GetReferrers - fails => checkNotarySignature returns false
|
||||||
|
mockStoreController := mocks.MockedImageStore{
|
||||||
|
GetImageManifestFn: func(name, reference string) ([]byte, godigest.Digest, string, error) {
|
||||||
|
return []byte{}, "", "", zerr.ErrRepoNotFound
|
||||||
|
},
|
||||||
|
GetReferrersFn: func(name string, digest godigest.Digest, mediaTypes []string) (ispec.Index, error) {
|
||||||
|
return ispec.Index{}, ErrTestError
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
storeController := storage.StoreController{DefaultStore: mockStoreController}
|
||||||
|
olu := common.NewBaseOciLayoutUtils(storeController, log.NewLogger("debug", ""))
|
||||||
|
|
||||||
|
check := olu.CheckManifestSignature("rep", godigest.FromString(""))
|
||||||
|
So(check, ShouldBeFalse)
|
||||||
|
|
||||||
|
// checkNotarySignature -> true
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
port := GetFreePort()
|
||||||
|
baseURL := GetBaseURL(port)
|
||||||
|
conf := config.New()
|
||||||
|
conf.HTTP.Port = port
|
||||||
|
conf.Storage.RootDirectory = dir
|
||||||
|
defaultVal := true
|
||||||
|
conf.Extensions = &extconf.ExtensionConfig{
|
||||||
|
Search: &extconf.SearchConfig{BaseConfig: extconf.BaseConfig{Enable: &defaultVal}},
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.Extensions.Search.CVE = nil
|
||||||
|
|
||||||
|
ctlr := api.NewController(conf)
|
||||||
|
|
||||||
|
ctlrManager := NewControllerManager(ctlr)
|
||||||
|
ctlrManager.StartAndWait(port)
|
||||||
|
defer ctlrManager.StopServer()
|
||||||
|
|
||||||
|
// push test image to repo
|
||||||
|
config, layers, manifest, err := GetImageComponents(100)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
layersSize1 := 0
|
||||||
|
for _, l := range layers {
|
||||||
|
layersSize1 += len(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo := "repo"
|
||||||
|
tag := "1.0.1"
|
||||||
|
err = UploadImage(
|
||||||
|
Image{
|
||||||
|
Manifest: manifest,
|
||||||
|
Config: config,
|
||||||
|
Layers: layers,
|
||||||
|
Tag: tag,
|
||||||
|
},
|
||||||
|
baseURL,
|
||||||
|
repo,
|
||||||
|
)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
olu = common.NewBaseOciLayoutUtils(ctlr.StoreController, log.NewLogger("debug", ""))
|
||||||
|
manifestList, err := olu.GetImageManifests(repo)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(len(manifestList), ShouldEqual, 1)
|
||||||
|
|
||||||
|
isSigned := olu.CheckManifestSignature(repo, manifestList[0].Digest)
|
||||||
|
So(isSigned, ShouldBeFalse)
|
||||||
|
|
||||||
|
err = SignImageUsingNotary(fmt.Sprintf("%s:%s", repo, tag), port)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
isSigned = olu.CheckManifestSignature(repo, manifestList[0].Digest)
|
||||||
|
So(isSigned, ShouldBeTrue)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearchSize(t *testing.T) {
|
func TestSearchSize(t *testing.T) {
|
||||||
|
|
|
@ -218,7 +218,7 @@ func (olu BaseOciLayoutUtils) checkNotarySignature(name string, digest godigest.
|
||||||
imageStore := olu.StoreController.GetImageStore(name)
|
imageStore := olu.StoreController.GetImageStore(name)
|
||||||
mediaType := notreg.ArtifactTypeNotation
|
mediaType := notreg.ArtifactTypeNotation
|
||||||
|
|
||||||
_, err := imageStore.GetOrasReferrers(name, digest, mediaType)
|
referrers, err := imageStore.GetReferrers(name, digest, []string{mediaType})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
olu.Log.Info().Err(err).Str("repo", name).Str("digest",
|
olu.Log.Info().Err(err).Str("repo", name).Str("digest",
|
||||||
digest.String()).Str("mediatype", mediaType).Msg("invalid notary signature")
|
digest.String()).Str("mediatype", mediaType).Msg("invalid notary signature")
|
||||||
|
@ -226,6 +226,10 @@ func (olu BaseOciLayoutUtils) checkNotarySignature(name string, digest godigest.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(referrers.Manifests) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -300,14 +300,14 @@ func syncRun(regCfg RegistryConfig,
|
||||||
Err(err).Msgf("couldn't get upstream image %s cosign manifest", upstreamImageRef.DockerReference())
|
Err(err).Msgf("couldn't get upstream image %s cosign manifest", upstreamImageRef.DockerReference())
|
||||||
}
|
}
|
||||||
|
|
||||||
refs, err := sig.getNotaryRefs(upstreamRepo, upstreamImageDigest.String())
|
index, err := sig.getOCIRefs(upstreamRepo, upstreamImageDigest.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Str("errorType", common.TypeOf(err)).
|
log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msgf("couldn't get upstream image %s notary references", upstreamImageRef.DockerReference())
|
Err(err).Msgf("couldn't get upstream image %s OCI references", upstreamImageRef.DockerReference())
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if upstream image is signed
|
// check if upstream image is signed
|
||||||
if cosignManifest == nil && len(refs.References) == 0 {
|
if cosignManifest == nil && len(getNotationManifestsFromOCIRefs(index)) == 0 {
|
||||||
// upstream image not signed
|
// upstream image not signed
|
||||||
if regCfg.OnlySigned != nil && *regCfg.OnlySigned {
|
if regCfg.OnlySigned != nil && *regCfg.OnlySigned {
|
||||||
// skip unsigned images
|
// skip unsigned images
|
||||||
|
@ -355,12 +355,6 @@ func syncRun(regCfg RegistryConfig,
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
index, err := sig.getOCIRefs(upstreamRepo, upstreamImageDigest.String())
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Str("errorType", common.TypeOf(err)).
|
|
||||||
Err(err).Msgf("couldn't get upstream image %s oci references", upstreamImageRef.DockerReference())
|
|
||||||
}
|
|
||||||
|
|
||||||
err = sig.syncOCIRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), index)
|
err = sig.syncOCIRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -374,10 +368,16 @@ func syncRun(regCfg RegistryConfig,
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sig.syncNotaryRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), refs)
|
refs, err := sig.getORASRefs(upstreamRepo, upstreamImageDigest.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Str("errorType", common.TypeOf(err)).
|
log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msgf("couldn't copy image notary signature %s/%s:%s", utils.upstreamAddr, upstreamRepo, reference)
|
Err(err).Msgf("couldn't get upstream image %s ORAS references", upstreamImageRef.DockerReference())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sig.syncORASRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), refs)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
|
Err(err).Msgf("couldn't copy image ORAS references %s/%s:%s", utils.upstreamAddr, upstreamRepo, reference)
|
||||||
|
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -409,27 +409,28 @@ func syncSignaturesArtifacts(sig *signaturesCopier, localRepo, upstreamRepo, ref
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case artifactType == OrasArtifact:
|
case artifactType == OrasArtifact:
|
||||||
// is notary signature
|
// is oras artifact
|
||||||
refs, err := sig.getNotaryRefs(upstreamRepo, reference)
|
refs, err := sig.getORASRefs(upstreamRepo, reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msgf("couldn't get upstream image %s/%s:%s notary references", upstreamURL, upstreamRepo, reference)
|
Err(err).Msgf("couldn't get upstream image %s/%s:%s ORAS references", upstreamURL, upstreamRepo, reference)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sig.syncNotaryRefs(localRepo, upstreamRepo, reference, refs)
|
err = sig.syncORASRefs(localRepo, upstreamRepo, reference, refs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msgf("couldn't copy image signature %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
Err(err).Msgf("couldn't copy image ORAS references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case artifactType == OCIReference:
|
case artifactType == OCIReference:
|
||||||
|
// this contains notary signatures
|
||||||
index, err := sig.getOCIRefs(upstreamRepo, reference)
|
index, err := sig.getOCIRefs(upstreamRepo, reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msgf("couldn't get oci references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
Err(err).Msgf("couldn't get OCI references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -437,7 +438,7 @@ func syncSignaturesArtifacts(sig *signaturesCopier, localRepo, upstreamRepo, ref
|
||||||
err = sig.syncOCIRefs(localRepo, upstreamRepo, reference, index)
|
err = sig.syncOCIRefs(localRepo, upstreamRepo, reference, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msgf("couldn't copy oci references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
Err(err).Msgf("couldn't copy OCI references %s/%s:%s", upstreamURL, upstreamRepo, reference)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ func (sig *signaturesCopier) getCosignManifest(repo, digestStr string) (*ispec.M
|
||||||
return &cosignManifest, nil
|
return &cosignManifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sig *signaturesCopier) getNotaryRefs(repo, digestStr string) (ReferenceList, error) {
|
func (sig *signaturesCopier) getORASRefs(repo, digestStr string) (ReferenceList, error) {
|
||||||
var referrers ReferenceList
|
var referrers ReferenceList
|
||||||
|
|
||||||
getReferrersURL := sig.upstreamURL
|
getReferrersURL := sig.upstreamURL
|
||||||
|
@ -89,12 +89,12 @@ func (sig *signaturesCopier) getNotaryRefs(repo, digestStr string) (ReferenceLis
|
||||||
getReferrersURL.String(), "application/json", sig.log)
|
getReferrersURL.String(), "application/json", sig.log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if statusCode == http.StatusNotFound {
|
if statusCode == http.StatusNotFound {
|
||||||
sig.log.Info().Err(err).Msg("couldn't find any notary signatures/oras artifacts")
|
sig.log.Info().Err(err).Msg("couldn't find any ORAS artifact")
|
||||||
|
|
||||||
return referrers, zerr.ErrSyncReferrerNotFound
|
return referrers, zerr.ErrSyncReferrerNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
sig.log.Error().Err(err).Msg("couldn't get notary signatures/oras artifacts")
|
sig.log.Error().Err(err).Msg("couldn't get ORAS artifacts")
|
||||||
|
|
||||||
return referrers, err
|
return referrers, err
|
||||||
}
|
}
|
||||||
|
@ -188,25 +188,25 @@ func (sig *signaturesCopier) syncCosignSignature(localRepo, remoteRepo, digestSt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sig *signaturesCopier) syncNotaryRefs(localRepo, remoteRepo, digestStr string, referrers ReferenceList,
|
func (sig *signaturesCopier) syncORASRefs(localRepo, remoteRepo, digestStr string, referrers ReferenceList,
|
||||||
) error {
|
) error {
|
||||||
if len(referrers.References) == 0 {
|
if len(referrers.References) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
skipNotarySig, err := sig.canSkipNotaryRefs(localRepo, digestStr, referrers)
|
skipORASRefs, err := sig.canSkipORASRefs(localRepo, digestStr, referrers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sig.log.Error().Err(err).Msgf("couldn't check if the upstream image %s:%s notary signature can be skipped",
|
sig.log.Error().Err(err).Msgf("couldn't check if the upstream image %s:%s ORAS artifact can be skipped",
|
||||||
remoteRepo, digestStr)
|
remoteRepo, digestStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if skipNotarySig {
|
if skipORASRefs {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
imageStore := sig.storeController.GetImageStore(localRepo)
|
imageStore := sig.storeController.GetImageStore(localRepo)
|
||||||
|
|
||||||
sig.log.Info().Msg("syncing notary signatures")
|
sig.log.Info().Msg("syncing ORAS artifacts")
|
||||||
|
|
||||||
for _, ref := range referrers.References {
|
for _, ref := range referrers.References {
|
||||||
// get referrer manifest
|
// get referrer manifest
|
||||||
|
@ -222,13 +222,13 @@ func (sig *signaturesCopier) syncNotaryRefs(localRepo, remoteRepo, digestStr str
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if statusCode == http.StatusNotFound {
|
if statusCode == http.StatusNotFound {
|
||||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msgf("couldn't find any notary manifest: %s", getRefManifestURL.String())
|
Err(err).Msgf("couldn't find any ORAS manifest: %s", getRefManifestURL.String())
|
||||||
|
|
||||||
return zerr.ErrSyncReferrerNotFound
|
return zerr.ErrSyncReferrerNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msgf("couldn't get notary manifest: %s", getRefManifestURL.String())
|
Err(err).Msgf("couldn't get ORAS manifest: %s", getRefManifestURL.String())
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -243,13 +243,13 @@ func (sig *signaturesCopier) syncNotaryRefs(localRepo, remoteRepo, digestStr str
|
||||||
oras.MediaTypeArtifactManifest, body)
|
oras.MediaTypeArtifactManifest, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msg("couldn't upload notary sig manifest")
|
Err(err).Msg("couldn't upload ORAS manifest")
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sig.log.Info().Msgf("successfully synced notary signature for repo %s digest %s", localRepo, digestStr)
|
sig.log.Info().Msgf("successfully synced ORAS artifacts for repo %s digest %s", localRepo, digestStr)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -409,33 +409,33 @@ func (sig *signaturesCopier) syncOCIArtifact(localRepo, remoteRepo, reference st
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sig *signaturesCopier) canSkipNotaryRefs(localRepo, digestStr string, refs ReferenceList,
|
func (sig *signaturesCopier) canSkipORASRefs(localRepo, digestStr string, refs ReferenceList,
|
||||||
) (bool, error) {
|
) (bool, error) {
|
||||||
imageStore := sig.storeController.GetImageStore(localRepo)
|
imageStore := sig.storeController.GetImageStore(localRepo)
|
||||||
digest := godigest.Digest(digestStr)
|
digest := godigest.Digest(digestStr)
|
||||||
|
|
||||||
// check notary signature already synced
|
// check oras artifacts already synced
|
||||||
if len(refs.References) > 0 {
|
if len(refs.References) > 0 {
|
||||||
localRefs, err := imageStore.GetOrasReferrers(localRepo, digest, notreg.ArtifactTypeNotation)
|
localRefs, err := imageStore.GetOrasReferrers(localRepo, digest, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, zerr.ErrManifestNotFound) {
|
if errors.Is(err, zerr.ErrManifestNotFound) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
sig.log.Error().Str("errorType", common.TypeOf(err)).
|
||||||
Err(err).Msgf("couldn't get local notary signature %s:%s manifest", localRepo, digestStr)
|
Err(err).Msgf("couldn't get local ORAS artifact %s:%s manifest", localRepo, digestStr)
|
||||||
|
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !artifactDescriptorsEqual(localRefs, refs.References) {
|
if !artifactDescriptorsEqual(localRefs, refs.References) {
|
||||||
sig.log.Info().Msgf("upstream notary signatures %s:%s changed, syncing again", localRepo, digestStr)
|
sig.log.Info().Msgf("upstream ORAS artifacts %s:%s changed, syncing again", localRepo, digestStr)
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sig.log.Info().Msgf("skipping notary signature %s:%s, already synced", localRepo, digestStr)
|
sig.log.Info().Msgf("skipping ORAS artifact %s:%s, already synced", localRepo, digestStr)
|
||||||
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -608,3 +608,15 @@ func getCosignTagFromImageDigest(digestStr string) string {
|
||||||
|
|
||||||
return digestStr
|
return digestStr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getNotationManifestsFromOCIRefs(ociRefs ispec.Index) []ispec.Descriptor {
|
||||||
|
notaryManifests := []ispec.Descriptor{}
|
||||||
|
|
||||||
|
for _, ref := range ociRefs.Manifests {
|
||||||
|
if ref.ArtifactType == notreg.ArtifactTypeNotation {
|
||||||
|
notaryManifests = append(notaryManifests, ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return notaryManifests
|
||||||
|
}
|
||||||
|
|
|
@ -334,15 +334,15 @@ func syncRegistry(ctx context.Context, regCfg RegistryConfig,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
refs, err := sig.getNotaryRefs(upstreamRepo, upstreamImageDigest.String())
|
index, err := sig.getOCIRefs(upstreamRepo, upstreamImageDigest.String())
|
||||||
if err != nil && !errors.Is(err, zerr.ErrSyncReferrerNotFound) {
|
if err != nil && !errors.Is(err, zerr.ErrSyncReferrerNotFound) {
|
||||||
log.Error().Err(err).Msgf("couldn't get upstream image %s notary references", upstreamImageRef.DockerReference())
|
log.Error().Err(err).Msgf("couldn't get upstream image %s OCI references", upstreamImageRef.DockerReference())
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if upstream image is signed
|
// check if upstream image is signed
|
||||||
if cosignManifest == nil && len(refs.References) == 0 {
|
if cosignManifest == nil && len(getNotationManifestsFromOCIRefs(index)) == 0 {
|
||||||
// upstream image not signed
|
// upstream image not signed
|
||||||
if regCfg.OnlySigned != nil && *regCfg.OnlySigned {
|
if regCfg.OnlySigned != nil && *regCfg.OnlySigned {
|
||||||
// skip unsigned images
|
// skip unsigned images
|
||||||
|
@ -399,17 +399,17 @@ func syncRegistry(ctx context.Context, regCfg RegistryConfig,
|
||||||
|
|
||||||
// sync signatures
|
// sync signatures
|
||||||
if err = retry.RetryIfNecessary(ctx, func() error {
|
if err = retry.RetryIfNecessary(ctx, func() error {
|
||||||
index, err := sig.getOCIRefs(upstreamRepo, upstreamImageDigest.String())
|
|
||||||
if err != nil && !errors.Is(err, zerr.ErrSyncReferrerNotFound) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = sig.syncOCIRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), index)
|
err = sig.syncOCIRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sig.syncNotaryRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), refs)
|
refs, err := sig.getORASRefs(upstreamRepo, upstreamImageDigest.String())
|
||||||
|
if err != nil && !errors.Is(err, zerr.ErrSyncReferrerNotFound) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sig.syncORASRefs(localRepo, upstreamRepo, upstreamImageDigest.String(), refs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -358,8 +358,10 @@ func TestSyncInternal(t *testing.T) {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(regURL, ShouldNotBeNil)
|
So(regURL, ShouldNotBeNil)
|
||||||
|
|
||||||
ref := artifactspec.Descriptor{
|
ref := ispec.Descriptor{
|
||||||
Digest: "fakeDigest",
|
MediaType: ispec.MediaTypeArtifactManifest,
|
||||||
|
Digest: "fakeDigest",
|
||||||
|
ArtifactType: "application/vnd.cncf.notary.signature",
|
||||||
}
|
}
|
||||||
|
|
||||||
desc := ispec.Descriptor{
|
desc := ispec.Descriptor{
|
||||||
|
@ -383,7 +385,7 @@ func TestSyncInternal(t *testing.T) {
|
||||||
err = sig.syncCosignSignature(testImage, testImage, testImageTag, &manifest)
|
err = sig.syncCosignSignature(testImage, testImage, testImageTag, &manifest)
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
err = sig.syncNotaryRefs(testImage, testImage, "invalidDigest", ReferenceList{[]artifactspec.Descriptor{ref}})
|
err = sig.syncOCIRefs(testImage, testImage, "invalidDigest", ispec.Index{Manifests: []ispec.Descriptor{ref}})
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -398,9 +400,11 @@ func TestSyncInternal(t *testing.T) {
|
||||||
imageStore := local.NewImageStore(storageDir, false, storage.DefaultGCDelay,
|
imageStore := local.NewImageStore(storageDir, false, storage.DefaultGCDelay,
|
||||||
false, false, log, metrics, nil, nil)
|
false, false, log, metrics, nil, nil)
|
||||||
|
|
||||||
refs := ReferenceList{[]artifactspec.Descriptor{
|
refs := ispec.Index{Manifests: []ispec.Descriptor{
|
||||||
{
|
{
|
||||||
Digest: "fakeDigest",
|
MediaType: ispec.MediaTypeArtifactManifest,
|
||||||
|
Digest: "fakeDigest",
|
||||||
|
ArtifactType: "application/vnd.cncf.notary.signature",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
@ -425,14 +429,34 @@ func TestSyncInternal(t *testing.T) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
sig := newSignaturesCopier(client, Credentials{}, *regURL, storage.StoreController{DefaultStore: imageStore}, log)
|
sig := newSignaturesCopier(client, Credentials{}, *regURL, storage.StoreController{DefaultStore: imageStore}, log)
|
||||||
|
|
||||||
canBeSkipped, err = sig.canSkipNotaryRefs(testImage, testImageManifestDigest.String(), refs)
|
canBeSkipped, err = sig.canSkipOCIRefs(testImage, testImageManifestDigest.String(), refs)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(canBeSkipped, ShouldBeFalse)
|
||||||
|
|
||||||
|
var index ispec.Index
|
||||||
|
indexPath := path.Join(imageStore.RootDir(), testImage, "index.json")
|
||||||
|
buf, err := os.ReadFile(indexPath)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = json.Unmarshal(buf, &index)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
index.Manifests = append(index.Manifests, ispec.Descriptor{
|
||||||
|
MediaType: ispec.MediaTypeArtifactManifest,
|
||||||
|
Digest: godigest.FromString(""),
|
||||||
|
ArtifactType: "application/vnd.cncf.notary.signature",
|
||||||
|
})
|
||||||
|
indexBuf, err := json.Marshal(index)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.WriteFile(indexPath, indexBuf, 0o600)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
canBeSkipped, err = sig.canSkipOCIRefs(testImage, testImageManifestDigest.String(), refs)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(canBeSkipped, ShouldBeFalse)
|
So(canBeSkipped, ShouldBeFalse)
|
||||||
|
|
||||||
err = os.Chmod(path.Join(imageStore.RootDir(), testImage, "index.json"), 0o000)
|
err = os.Chmod(path.Join(imageStore.RootDir(), testImage, "index.json"), 0o000)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
canBeSkipped, err = sig.canSkipNotaryRefs(testImage, testImageManifestDigest.String(), refs)
|
canBeSkipped, err = sig.canSkipOCIRefs(testImage, testImageManifestDigest.String(), refs)
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
So(canBeSkipped, ShouldBeFalse)
|
So(canBeSkipped, ShouldBeFalse)
|
||||||
|
|
||||||
|
@ -457,6 +481,65 @@ func TestSyncInternal(t *testing.T) {
|
||||||
canBeSkipped, err = sig.canSkipCosignSignature(testImage, testImageManifestDigest.String(), &cosignManifest)
|
canBeSkipped, err = sig.canSkipCosignSignature(testImage, testImageManifestDigest.String(), &cosignManifest)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(canBeSkipped, ShouldBeFalse)
|
So(canBeSkipped, ShouldBeFalse)
|
||||||
|
|
||||||
|
// test canSkipOrasRefs()
|
||||||
|
refList := ReferenceList{}
|
||||||
|
canBeSkipped, err = sig.canSkipORASRefs(testImage, testImageManifestDigest.String(), refList)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(canBeSkipped, ShouldBeTrue)
|
||||||
|
|
||||||
|
refList.References = append(refList.References, artifactspec.Descriptor{
|
||||||
|
MediaType: artifactspec.MediaTypeArtifactManifest,
|
||||||
|
Digest: godigest.FromString(""),
|
||||||
|
ArtifactType: "application/vnd.oras.artifact.ref",
|
||||||
|
})
|
||||||
|
canBeSkipped, err = sig.canSkipORASRefs(testImage, testImageManifestDigest.String(), refList)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(canBeSkipped, ShouldBeFalse)
|
||||||
|
|
||||||
|
err = sig.syncORASRefs(testImage, testImage, testImageManifestDigest.String(), refList)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
|
buf, err = os.ReadFile(indexPath)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = json.Unmarshal(buf, &index)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
blobs := []artifactspec.Descriptor{}
|
||||||
|
manifest := artifactspec.Manifest{
|
||||||
|
MediaType: artifactspec.MediaTypeArtifactManifest,
|
||||||
|
Blobs: blobs,
|
||||||
|
Subject: &artifactspec.Descriptor{Digest: testImageManifestDigest},
|
||||||
|
}
|
||||||
|
manifestBuf, err := json.Marshal(manifest)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
index.Manifests = append(index.Manifests, ispec.Descriptor{
|
||||||
|
MediaType: artifactspec.MediaTypeArtifactManifest,
|
||||||
|
Digest: godigest.FromBytes(manifestBuf),
|
||||||
|
ArtifactType: "application/vnd.oras.artifact",
|
||||||
|
})
|
||||||
|
indexBuf, err = json.Marshal(index)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.WriteFile(indexPath, indexBuf, 0o600)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = os.Chmod(path.Join(imageStore.RootDir(), testImage, "index.json"), 0o000)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
canBeSkipped, err = sig.canSkipORASRefs(testImage, testImageManifestDigest.String(), refList)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
So(canBeSkipped, ShouldBeFalse)
|
||||||
|
|
||||||
|
err = os.Chmod(path.Join(imageStore.RootDir(), testImage, "index.json"), 0o755)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = os.WriteFile(path.Join(imageStore.RootDir(), testImage,
|
||||||
|
"blobs", "sha256", godigest.FromBytes(manifestBuf).Encoded()),
|
||||||
|
manifestBuf, 0o600)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
canBeSkipped, err = sig.canSkipORASRefs(testImage, testImageManifestDigest.String(), refList)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(canBeSkipped, ShouldBeFalse)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Test filterRepos()", t, func() {
|
Convey("Test filterRepos()", t, func() {
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -22,11 +21,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/notaryproject/notation-go"
|
|
||||||
notreg "github.com/notaryproject/notation-go/registry"
|
notreg "github.com/notaryproject/notation-go/registry"
|
||||||
godigest "github.com/opencontainers/go-digest"
|
godigest "github.com/opencontainers/go-digest"
|
||||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
oraspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
||||||
perr "github.com/pkg/errors"
|
perr "github.com/pkg/errors"
|
||||||
"github.com/sigstore/cosign/cmd/cosign/cli/generate"
|
"github.com/sigstore/cosign/cmd/cosign/cli/generate"
|
||||||
"github.com/sigstore/cosign/cmd/cosign/cli/options"
|
"github.com/sigstore/cosign/cmd/cosign/cli/options"
|
||||||
|
@ -74,33 +72,13 @@ type TagsList struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReferenceList struct {
|
type ReferenceList struct {
|
||||||
References []notation.Descriptor `json:"references"`
|
References []ispec.Descriptor `json:"references"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type catalog struct {
|
type catalog struct {
|
||||||
Repositories []string `json:"repositories"`
|
Repositories []string `json:"repositories"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyFile(sourceFilePath, destFilePath string) error {
|
|
||||||
destFile, err := os.Create(destFilePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer destFile.Close()
|
|
||||||
|
|
||||||
sourceFile, err := os.Open(sourceFilePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer sourceFile.Close()
|
|
||||||
|
|
||||||
if _, err = io.Copy(destFile, sourceFile); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func startUpstreamServer(
|
func startUpstreamServer(
|
||||||
t *testing.T, secure, basicAuth bool,
|
t *testing.T, secure, basicAuth bool,
|
||||||
) (*api.Controller, string, string, string, *resty.Client) {
|
) (*api.Controller, string, string, string, *resty.Client) {
|
||||||
|
@ -324,6 +302,180 @@ func TestORAS(t *testing.T) {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(string(output), ShouldContainSubstring, "helloworld")
|
So(string(output), ShouldContainSubstring, "helloworld")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Verify get and sync oras refs", t, func() {
|
||||||
|
updateDuration, _ := time.ParseDuration("30m")
|
||||||
|
|
||||||
|
sctlr, srcBaseURL, srcDir, _, _ := startUpstreamServer(t, false, false)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
sctlr.Shutdown()
|
||||||
|
}()
|
||||||
|
|
||||||
|
repoName := testImage
|
||||||
|
var digest godigest.Digest
|
||||||
|
So(func() { digest = pushRepo(srcBaseURL, repoName) }, ShouldNotPanic)
|
||||||
|
|
||||||
|
regex := ".*"
|
||||||
|
var semver bool
|
||||||
|
var tlsVerify bool
|
||||||
|
|
||||||
|
syncRegistryConfig := sync.RegistryConfig{
|
||||||
|
Content: []sync.Content{
|
||||||
|
{
|
||||||
|
Prefix: repoName,
|
||||||
|
Tags: &sync.Tags{
|
||||||
|
Regex: ®ex,
|
||||||
|
Semver: &semver,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
URLs: []string{srcBaseURL},
|
||||||
|
PollInterval: updateDuration,
|
||||||
|
TLSVerify: &tlsVerify,
|
||||||
|
CertDir: "",
|
||||||
|
OnDemand: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultVal := true
|
||||||
|
syncConfig := &sync.Config{
|
||||||
|
Enable: &defaultVal,
|
||||||
|
Registries: []sync.RegistryConfig{syncRegistryConfig},
|
||||||
|
}
|
||||||
|
|
||||||
|
dctlr, destBaseURL, destDir, destClient := startDownstreamServer(t, false, syncConfig)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
dctlr.Shutdown()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// wait for sync
|
||||||
|
var destTagsList TagsList
|
||||||
|
|
||||||
|
for {
|
||||||
|
resp, err := destClient.R().Get(destBaseURL + "/v2/" + repoName + "/tags/list")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &destTagsList)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(destTagsList.Tags) > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
// get oras refs from downstream, should be synced
|
||||||
|
getORASReferrersURL := destBaseURL + path.Join("/oras/artifacts/v1/", repoName, "manifests", digest.String(), "referrers") //nolint:lll
|
||||||
|
|
||||||
|
resp, err := resty.R().Get(getORASReferrersURL)
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp, ShouldNotBeEmpty)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
||||||
|
|
||||||
|
err = os.Chmod(path.Join(destDir, testImage, "index.json"), 0o000)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
resp, err = resty.R().Get(getORASReferrersURL)
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp, ShouldNotBeEmpty)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, http.StatusInternalServerError)
|
||||||
|
|
||||||
|
err = os.Chmod(path.Join(destDir, testImage, "index.json"), 0o755)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
// get manifest digest from source
|
||||||
|
resp, err = destClient.R().Get(srcBaseURL + "/v2/" + testImage + "/manifests/" + digest.String())
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
|
digest = godigest.FromBytes(resp.Body())
|
||||||
|
|
||||||
|
content := []byte("blob content")
|
||||||
|
adigest := pushBlob(srcBaseURL, repoName, content)
|
||||||
|
|
||||||
|
artifactManifest := ispec.Artifact{
|
||||||
|
MediaType: artifactspec.MediaTypeArtifactManifest,
|
||||||
|
ArtifactType: "application/vnd.oras.artifact",
|
||||||
|
Blobs: []ispec.Descriptor{
|
||||||
|
{
|
||||||
|
MediaType: "application/octet-stream",
|
||||||
|
Digest: adigest,
|
||||||
|
Size: int64(len(content)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Subject: &ispec.Descriptor{
|
||||||
|
MediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||||
|
Digest: digest,
|
||||||
|
Size: int64(len(resp.Body())),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err = json.Marshal(artifactManifest)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
adigest = godigest.FromBytes(content)
|
||||||
|
|
||||||
|
// put OCI reference artifact mediaType artifact
|
||||||
|
_, err = resty.R().SetHeader("Content-Type", artifactspec.MediaTypeArtifactManifest).
|
||||||
|
SetBody(content).Put(srcBaseURL + fmt.Sprintf("/v2/%s/manifests/%s", repoName, adigest.String()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Chmod(path.Join(destDir, testImage, "index.json"), 0o000)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
resp, err = resty.R().Get(getORASReferrersURL)
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp, ShouldNotBeEmpty)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, http.StatusInternalServerError)
|
||||||
|
|
||||||
|
err = os.Chmod(path.Join(destDir, testImage, "index.json"), 0o755)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
resp, err = resty.R().Get(getORASReferrersURL)
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp, ShouldNotBeEmpty)
|
||||||
|
|
||||||
|
var refs ReferenceList
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &refs)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
So(len(refs.References), ShouldEqual, 1)
|
||||||
|
|
||||||
|
err = os.RemoveAll(path.Join(destDir, repoName))
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = os.WriteFile(path.Join(srcDir, repoName, "blobs", "sha256", adigest.Encoded()), []byte("wrong content"), 0o600)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
_, err = resty.R().SetHeader("Content-Type", artifactspec.MediaTypeArtifactManifest).
|
||||||
|
SetBody(content).Put(srcBaseURL + fmt.Sprintf("/v2/%s/manifests/%s", repoName, adigest.String()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = resty.R().Get(getORASReferrersURL)
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp, ShouldNotBeEmpty)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOnDemand(t *testing.T) {
|
func TestOnDemand(t *testing.T) {
|
||||||
|
@ -994,19 +1146,19 @@ func TestTLS(t *testing.T) {
|
||||||
destClientCertDir := t.TempDir()
|
destClientCertDir := t.TempDir()
|
||||||
|
|
||||||
destFilePath := path.Join(destClientCertDir, "ca.crt")
|
destFilePath := path.Join(destClientCertDir, "ca.crt")
|
||||||
err = copyFile(CACert, destFilePath)
|
err = test.CopyFile(CACert, destFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
destFilePath = path.Join(destClientCertDir, "client.cert")
|
destFilePath = path.Join(destClientCertDir, "client.cert")
|
||||||
err = copyFile(ClientCert, destFilePath)
|
err = test.CopyFile(ClientCert, destFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
destFilePath = path.Join(destClientCertDir, "client.key")
|
destFilePath = path.Join(destClientCertDir, "client.key")
|
||||||
err = copyFile(ClientKey, destFilePath)
|
err = test.CopyFile(ClientKey, destFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -1664,7 +1816,7 @@ func TestInvalidCerts(t *testing.T) {
|
||||||
clientCertDir := t.TempDir()
|
clientCertDir := t.TempDir()
|
||||||
|
|
||||||
destFilePath := path.Join(clientCertDir, "ca.crt")
|
destFilePath := path.Join(clientCertDir, "ca.crt")
|
||||||
err := copyFile(CACert, destFilePath)
|
err := test.CopyFile(CACert, destFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -1681,13 +1833,13 @@ func TestInvalidCerts(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
destFilePath = path.Join(clientCertDir, "client.cert")
|
destFilePath = path.Join(clientCertDir, "client.cert")
|
||||||
err = copyFile(ClientCert, destFilePath)
|
err = test.CopyFile(ClientCert, destFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
destFilePath = path.Join(clientCertDir, "client.key")
|
destFilePath = path.Join(clientCertDir, "client.key")
|
||||||
err = copyFile(ClientKey, destFilePath)
|
err = test.CopyFile(ClientKey, destFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -1739,7 +1891,7 @@ func TestCertsWithWrongPerms(t *testing.T) {
|
||||||
clientCertDir := t.TempDir()
|
clientCertDir := t.TempDir()
|
||||||
|
|
||||||
destFilePath := path.Join(clientCertDir, "ca.crt")
|
destFilePath := path.Join(clientCertDir, "ca.crt")
|
||||||
err := copyFile(CACert, destFilePath)
|
err := test.CopyFile(CACert, destFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -1748,13 +1900,13 @@ func TestCertsWithWrongPerms(t *testing.T) {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
destFilePath = path.Join(clientCertDir, "client.cert")
|
destFilePath = path.Join(clientCertDir, "client.cert")
|
||||||
err = copyFile(ClientCert, destFilePath)
|
err = test.CopyFile(ClientCert, destFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
destFilePath = path.Join(clientCertDir, "client.key")
|
destFilePath = path.Join(clientCertDir, "client.key")
|
||||||
err = copyFile(ClientKey, destFilePath)
|
err = test.CopyFile(ClientKey, destFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -2439,25 +2591,25 @@ func TestPeriodicallySignaturesErr(t *testing.T) {
|
||||||
|
|
||||||
Convey("Trigger error on notary signature", func() {
|
Convey("Trigger error on notary signature", func() {
|
||||||
// trigger permission error on notary signature on upstream
|
// trigger permission error on notary signature on upstream
|
||||||
notaryURLPath := path.Join("/oras/artifacts/v1/", repoName, "manifests", imageManifestDigest.String(), "referrers")
|
notaryURLPath := path.Join("/v2/", repoName, "referrers", imageManifestDigest.String())
|
||||||
|
|
||||||
// based on image manifest digest get referrers
|
// based on image manifest digest get referrers
|
||||||
resp, err := resty.R().
|
resp, err := resty.R().
|
||||||
SetHeader("Content-Type", "application/json").
|
SetHeader("Content-Type", "application/json").
|
||||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.v2.signature").
|
SetQueryParam("artifactType", "application/vnd.cncf.notary.signature").
|
||||||
Get(srcBaseURL + notaryURLPath)
|
Get(srcBaseURL + notaryURLPath)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp, ShouldNotBeEmpty)
|
So(resp, ShouldNotBeEmpty)
|
||||||
|
|
||||||
var referrers ReferenceList
|
var referrers ispec.Index
|
||||||
|
|
||||||
err = json.Unmarshal(resp.Body(), &referrers)
|
err = json.Unmarshal(resp.Body(), &referrers)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
// read manifest
|
// read manifest
|
||||||
var artifactManifest oraspec.Manifest
|
var artifactManifest ispec.Artifact
|
||||||
for _, ref := range referrers.References {
|
for _, ref := range referrers.Manifests {
|
||||||
refPath := path.Join(srcDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
refPath := path.Join(srcDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
||||||
body, err := os.ReadFile(refPath)
|
body, err := os.ReadFile(refPath)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -2481,10 +2633,17 @@ func TestPeriodicallySignaturesErr(t *testing.T) {
|
||||||
|
|
||||||
// should not be synced nor sync on demand
|
// should not be synced nor sync on demand
|
||||||
resp, err = resty.R().SetHeader("Content-Type", "application/json").
|
resp, err = resty.R().SetHeader("Content-Type", "application/json").
|
||||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.v2.signature").
|
SetQueryParam("artifactType", "application/vnd.cncf.notary.signature").
|
||||||
Get(destBaseURL + notaryURLPath)
|
Get(destBaseURL + notaryURLPath)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp.StatusCode(), ShouldEqual, 404)
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
|
var index ispec.Index
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &index)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
So(len(index.Manifests), ShouldEqual, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Trigger error on artifact references", func() {
|
Convey("Trigger error on artifact references", func() {
|
||||||
|
@ -2600,6 +2759,7 @@ func TestSignatures(t *testing.T) {
|
||||||
defer func() { _ = os.Chdir(cwd) }()
|
defer func() { _ = os.Chdir(cwd) }()
|
||||||
tdir := t.TempDir()
|
tdir := t.TempDir()
|
||||||
_ = os.Chdir(tdir)
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
generateKeyPairs(tdir)
|
generateKeyPairs(tdir)
|
||||||
|
|
||||||
So(func() { signImage(tdir, srcPort, repoName, digest) }, ShouldNotPanic)
|
So(func() { signImage(tdir, srcPort, repoName, digest) }, ShouldNotPanic)
|
||||||
|
@ -2671,13 +2831,9 @@ func TestSignatures(t *testing.T) {
|
||||||
|
|
||||||
// notation verify the image
|
// notation verify the image
|
||||||
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||||
cmd := exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
msg := string(out)
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
So(msg, ShouldNotBeEmpty)
|
So(err, ShouldBeNil)
|
||||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
|
||||||
|
|
||||||
// cosign verify the image
|
// cosign verify the image
|
||||||
vrfy := verify.VerifyCommand{
|
vrfy := verify.VerifyCommand{
|
||||||
|
@ -2703,23 +2859,23 @@ func TestSignatures(t *testing.T) {
|
||||||
err = json.Unmarshal(resp.Body(), &index)
|
err = json.Unmarshal(resp.Body(), &index)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
So(len(index.Manifests), ShouldEqual, 2)
|
So(len(index.Manifests), ShouldEqual, 3)
|
||||||
|
|
||||||
// test negative cases (trigger errors)
|
// test negative cases (trigger errors)
|
||||||
// test notary signatures errors
|
// test notary signatures errors
|
||||||
|
|
||||||
// based on manifest digest get referrers
|
// based on manifest digest get referrers
|
||||||
getReferrersURL := srcBaseURL + path.Join("/oras/artifacts/v1/", repoName, "manifests", digest.String(), "referrers")
|
getReferrersURL := srcBaseURL + path.Join("/v2/", repoName, "referrers", digest.String())
|
||||||
|
|
||||||
resp, err = resty.R().
|
resp, err = resty.R().
|
||||||
SetHeader("Content-Type", "application/json").
|
SetHeader("Content-Type", "application/json").
|
||||||
SetQueryParam("artifactType", "application/vnd.cncf.notary.v2.signature").
|
SetQueryParam("artifactType", "application/vnd.cncf.notary.signature").
|
||||||
Get(getReferrersURL)
|
Get(getReferrersURL)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(resp, ShouldNotBeEmpty)
|
So(resp, ShouldNotBeEmpty)
|
||||||
|
|
||||||
var referrers ReferenceList
|
var referrers ispec.Index
|
||||||
|
|
||||||
err = json.Unmarshal(resp.Body(), &referrers)
|
err = json.Unmarshal(resp.Body(), &referrers)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -2728,8 +2884,8 @@ func TestSignatures(t *testing.T) {
|
||||||
err = os.RemoveAll(path.Join(destDir, repoName))
|
err = os.RemoveAll(path.Join(destDir, repoName))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
var artifactManifest oraspec.Manifest
|
var artifactManifest ispec.Artifact
|
||||||
for _, ref := range referrers.References {
|
for _, ref := range referrers.Manifests {
|
||||||
refPath := path.Join(srcDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
refPath := path.Join(srcDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
||||||
body, err := os.ReadFile(refPath)
|
body, err := os.ReadFile(refPath)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -2757,7 +2913,7 @@ func TestSignatures(t *testing.T) {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
// triggers perm denied on notary manifest on downstream
|
// triggers perm denied on notary manifest on downstream
|
||||||
for _, ref := range referrers.References {
|
for _, ref := range referrers.Manifests {
|
||||||
refPath := path.Join(destDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
refPath := path.Join(destDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
||||||
err := os.MkdirAll(refPath, 0o755)
|
err := os.MkdirAll(refPath, 0o755)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -3498,12 +3654,8 @@ func TestSignaturesOnDemand(t *testing.T) {
|
||||||
|
|
||||||
// notation verify the synced image
|
// notation verify the synced image
|
||||||
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||||
cmd := exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
msg := string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
|
||||||
|
|
||||||
// cosign verify the synced image
|
// cosign verify the synced image
|
||||||
vrfy := verify.VerifyCommand{
|
vrfy := verify.VerifyCommand{
|
||||||
|
@ -3568,6 +3720,107 @@ func TestSignaturesOnDemand(t *testing.T) {
|
||||||
err = os.Chmod(srcSignatureBlobPath, 0o755)
|
err = os.Chmod(srcSignatureBlobPath, 0o755)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Verify sync signatures on demand feature: notation - negative cases", t, func() {
|
||||||
|
sctlr, srcBaseURL, srcDir, _, _ := startUpstreamServer(t, false, false)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
sctlr.Shutdown()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// create repo, push and sign it
|
||||||
|
repoName := testSignedImage
|
||||||
|
var digest godigest.Digest
|
||||||
|
So(func() { digest = pushRepo(srcBaseURL, repoName) }, ShouldNotPanic)
|
||||||
|
|
||||||
|
splittedURL := strings.SplitAfter(srcBaseURL, ":")
|
||||||
|
srcPort := splittedURL[len(splittedURL)-1]
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
defer func() { _ = os.Chdir(cwd) }()
|
||||||
|
tdir := t.TempDir()
|
||||||
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
|
generateKeyPairs(tdir)
|
||||||
|
|
||||||
|
So(func() { signImage(tdir, srcPort, repoName, digest) }, ShouldNotPanic)
|
||||||
|
|
||||||
|
var tlsVerify bool
|
||||||
|
|
||||||
|
syncRegistryConfig := sync.RegistryConfig{
|
||||||
|
URLs: []string{srcBaseURL},
|
||||||
|
TLSVerify: &tlsVerify,
|
||||||
|
CertDir: "",
|
||||||
|
OnDemand: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultVal := true
|
||||||
|
syncConfig := &sync.Config{
|
||||||
|
Enable: &defaultVal,
|
||||||
|
Registries: []sync.RegistryConfig{syncRegistryConfig},
|
||||||
|
}
|
||||||
|
|
||||||
|
destPort := test.GetFreePort()
|
||||||
|
destConfig := config.New()
|
||||||
|
destBaseURL := test.GetBaseURL(destPort)
|
||||||
|
destConfig.HTTP.Port = destPort
|
||||||
|
|
||||||
|
destDir := t.TempDir()
|
||||||
|
|
||||||
|
destConfig.Storage.RootDirectory = destDir
|
||||||
|
destConfig.Storage.Dedupe = false
|
||||||
|
destConfig.Storage.GC = false
|
||||||
|
|
||||||
|
destConfig.Extensions = &extconf.ExtensionConfig{}
|
||||||
|
destConfig.Extensions.Search = nil
|
||||||
|
destConfig.Extensions.Sync = syncConfig
|
||||||
|
destConfig.Log.Output = path.Join(destDir, "sync.log")
|
||||||
|
|
||||||
|
dctlr := api.NewController(destConfig)
|
||||||
|
dcm := test.NewControllerManager(dctlr)
|
||||||
|
|
||||||
|
dcm.StartAndWait(destPort)
|
||||||
|
|
||||||
|
defer dcm.StopServer()
|
||||||
|
|
||||||
|
// trigger getOCIRefs error
|
||||||
|
getReferrersURL := srcBaseURL + path.Join("/v2/", repoName, "referrers", digest.String())
|
||||||
|
|
||||||
|
resp, err := resty.R().
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetQueryParam("artifactType", "application/vnd.cncf.notary.signature").
|
||||||
|
Get(getReferrersURL)
|
||||||
|
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp, ShouldNotBeEmpty)
|
||||||
|
|
||||||
|
var referrers ispec.Index
|
||||||
|
|
||||||
|
err = json.Unmarshal(resp.Body(), &referrers)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
for _, ref := range referrers.Manifests {
|
||||||
|
refPath := path.Join(srcDir, repoName, "blobs", string(ref.Digest.Algorithm()), ref.Digest.Encoded())
|
||||||
|
err := os.Remove(refPath)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = resty.R().Get(destBaseURL + "/v2/" + testSignedImage + "/manifests/1.0")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(resp.StatusCode(), ShouldEqual, 200)
|
||||||
|
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
body, err := os.ReadFile(path.Join(destDir, "sync.log"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("unable to read file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
So(string(body), ShouldContainSubstring, "couldn't find any oci reference")
|
||||||
|
So(string(body), ShouldContainSubstring, "couldn't find upstream referrer")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOnlySignaturesOnDemand(t *testing.T) {
|
func TestOnlySignaturesOnDemand(t *testing.T) {
|
||||||
|
@ -3641,12 +3894,8 @@ func TestOnlySignaturesOnDemand(t *testing.T) {
|
||||||
|
|
||||||
// sync signature on demand when upstream doesn't have the signature
|
// sync signature on demand when upstream doesn't have the signature
|
||||||
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||||
cmd := exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
msg := string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
So(strings.Contains(msg, "signature failure"), ShouldBeTrue)
|
|
||||||
|
|
||||||
// cosign verify the synced image
|
// cosign verify the synced image
|
||||||
vrfy := verify.VerifyCommand{
|
vrfy := verify.VerifyCommand{
|
||||||
|
@ -3664,12 +3913,8 @@ func TestOnlySignaturesOnDemand(t *testing.T) {
|
||||||
|
|
||||||
// now it should sync signatures on demand, even if we already have the image
|
// now it should sync signatures on demand, even if we already have the image
|
||||||
image = fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
image = fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||||
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
out, err = cmd.CombinedOutput()
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
msg = string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
|
||||||
|
|
||||||
// cosign verify the synced image
|
// cosign verify the synced image
|
||||||
vrfy = verify.VerifyCommand{
|
vrfy = verify.VerifyCommand{
|
||||||
|
@ -4002,14 +4247,9 @@ func TestSyncSignaturesDiff(t *testing.T) {
|
||||||
|
|
||||||
// notation verify the image
|
// notation verify the image
|
||||||
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
image := fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||||
cmd := exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
msg := string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
|
||||||
|
|
||||||
// cosign verify the image
|
// cosign verify the image
|
||||||
vrfy := verify.VerifyCommand{
|
vrfy := verify.VerifyCommand{
|
||||||
RegistryOptions: options.RegistryOptions{AllowInsecure: true},
|
RegistryOptions: options.RegistryOptions{AllowInsecure: true},
|
||||||
|
@ -4033,14 +4273,9 @@ func TestSyncSignaturesDiff(t *testing.T) {
|
||||||
|
|
||||||
// notation verify the image
|
// notation verify the image
|
||||||
image = fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
image = fmt.Sprintf("localhost:%s/%s:%s", destPort, repoName, "1.0")
|
||||||
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
out, err = cmd.CombinedOutput()
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
msg = string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
|
||||||
|
|
||||||
// cosign verify the image
|
// cosign verify the image
|
||||||
vrfy = verify.VerifyCommand{
|
vrfy = verify.VerifyCommand{
|
||||||
RegistryOptions: options.RegistryOptions{AllowInsecure: true},
|
RegistryOptions: options.RegistryOptions{AllowInsecure: true},
|
||||||
|
@ -4721,18 +4956,12 @@ func generateKeyPairs(tdir string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// "notation" (notaryv2) doesn't yet support exported apis, so use the binary instead
|
test.NotationPathLock.Lock()
|
||||||
_, err := exec.LookPath("notation")
|
defer test.NotationPathLock.Unlock()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Setenv("XDG_CONFIG_HOME", tdir)
|
test.LoadNotationPath(tdir)
|
||||||
|
|
||||||
// generate a keypair
|
err := test.GenerateNotationCerts(tdir, "good")
|
||||||
cmd := exec.Command("notation", "cert", "generate-test", "--trust", "good")
|
|
||||||
|
|
||||||
err = cmd.Run()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -4771,23 +5000,23 @@ func signImage(tdir, port, repoName string, digest godigest.Digest) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tdir)
|
||||||
|
|
||||||
// sign the image
|
// sign the image
|
||||||
image := fmt.Sprintf("localhost:%s/%s:%s", port, repoName, "1.0")
|
image := fmt.Sprintf("localhost:%s/%s:%s", port, repoName, "1.0")
|
||||||
cmd := exec.Command("notation", "sign", "--key", "good", "--plain-http", image)
|
|
||||||
|
|
||||||
err = cmd.Run()
|
err = test.SignWithNotation("good", image, tdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify the image
|
err = test.VerifyWithNotation(image, tdir)
|
||||||
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
|
if err != nil {
|
||||||
out, err := cmd.CombinedOutput()
|
panic(err)
|
||||||
So(err, ShouldBeNil)
|
}
|
||||||
|
|
||||||
msg := string(out)
|
|
||||||
So(msg, ShouldNotBeEmpty)
|
|
||||||
So(strings.Contains(msg, "verification failure"), ShouldBeFalse)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pushRepo(url, repoName string) godigest.Digest {
|
func pushRepo(url, repoName string) godigest.Digest {
|
||||||
|
|
|
@ -3,13 +3,10 @@ package storage
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/distribution/registry/storage/driver"
|
|
||||||
"github.com/gobwas/glob"
|
"github.com/gobwas/glob"
|
||||||
"github.com/notaryproject/notation-go"
|
|
||||||
godigest "github.com/opencontainers/go-digest"
|
godigest "github.com/opencontainers/go-digest"
|
||||||
imeta "github.com/opencontainers/image-spec/specs-go"
|
imeta "github.com/opencontainers/image-spec/specs-go"
|
||||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
@ -95,7 +92,7 @@ func ValidateManifest(imgStore ImageStore, repo, reference, mediaType string, bo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case oras.MediaTypeArtifactManifest:
|
case oras.MediaTypeArtifactManifest:
|
||||||
var m notation.Descriptor
|
var m oras.Descriptor
|
||||||
if err := json.Unmarshal(body, &m); err != nil {
|
if err := json.Unmarshal(body, &m); err != nil {
|
||||||
log.Error().Err(err).Msg("unable to unmarshal JSON")
|
log.Error().Err(err).Msg("unable to unmarshal JSON")
|
||||||
|
|
||||||
|
@ -580,7 +577,7 @@ func GetReferrers(imgStore ImageStore, repo string, gdigest godigest.Digest, art
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Str("blob", imgStore.BlobPath(repo, manifest.Digest)).Msg("failed to read manifest")
|
log.Error().Err(err).Str("blob", imgStore.BlobPath(repo, manifest.Digest)).Msg("failed to read manifest")
|
||||||
|
|
||||||
if os.IsNotExist(err) || errors.Is(err, driver.PathNotFoundError{}) {
|
if errors.Is(err, zerr.ErrBlobNotFound) {
|
||||||
return nilIndex, zerr.ErrManifestNotFound
|
return nilIndex, zerr.ErrManifestNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,7 +688,7 @@ func GetOrasManifestByDigest(imgStore ImageStore, repo string, digest godigest.D
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Str("blob", blobPath).Msg("failed to read manifest")
|
log.Error().Err(err).Str("blob", blobPath).Msg("failed to read manifest")
|
||||||
|
|
||||||
if os.IsNotExist(err) || errors.Is(err, driver.PathNotFoundError{}) {
|
if errors.Is(err, zerr.ErrBlobNotFound) {
|
||||||
return artManifest, zerr.ErrManifestNotFound
|
return artManifest, zerr.ErrManifestNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/distribution/registry/storage/driver"
|
|
||||||
godigest "github.com/opencontainers/go-digest"
|
godigest "github.com/opencontainers/go-digest"
|
||||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
||||||
|
@ -169,7 +168,7 @@ func TestGetReferrersErrors(t *testing.T) {
|
||||||
return indexBuf, nil
|
return indexBuf, nil
|
||||||
},
|
},
|
||||||
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
||||||
return []byte{}, driver.PathNotFoundError{}
|
return []byte{}, errors.ErrBlobNotFound
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +194,10 @@ func TestGetReferrersErrors(t *testing.T) {
|
||||||
_, err = storage.GetReferrers(imgStore, "zot-test", validDigest,
|
_, err = storage.GetReferrers(imgStore, "zot-test", validDigest,
|
||||||
[]string{artifactType}, log.With().Caller().Logger())
|
[]string{artifactType}, log.With().Caller().Logger())
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
|
_, err = storage.GetOrasReferrers(imgStore, "zot-test", validDigest,
|
||||||
|
artifactType, log.With().Caller().Logger())
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Trigger continue on different artifactType", func(c C) {
|
Convey("Trigger continue on different artifactType", func(c C) {
|
||||||
|
@ -226,6 +229,20 @@ func TestGetReferrersErrors(t *testing.T) {
|
||||||
So(err, ShouldNotBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Convey("Unmarshal oras artifact error", func(c C) {
|
||||||
|
imgStore = &mocks.MockedImageStore{
|
||||||
|
GetIndexContentFn: func(repo string) ([]byte, error) {
|
||||||
|
return indexBuf, nil
|
||||||
|
},
|
||||||
|
GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) {
|
||||||
|
return []byte("wrong content"), nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = storage.GetOrasReferrers(imgStore, "zot-test", validDigest, artifactType, log.With().Caller().Logger())
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
Convey("Trigger unmarshal error on manifest image mediaType", func(c C) {
|
Convey("Trigger unmarshal error on manifest image mediaType", func(c C) {
|
||||||
index = ispec.Index{
|
index = ispec.Index{
|
||||||
Manifests: []ispec.Descriptor{
|
Manifests: []ispec.Descriptor{
|
||||||
|
|
|
@ -1476,7 +1476,7 @@ func (is *ImageStoreLocal) garbageCollect(dir string, repo string) error {
|
||||||
cosignDescriptors = append(cosignDescriptors, desc)
|
cosignDescriptors = append(cosignDescriptors, desc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case oras.MediaTypeArtifactManifest:
|
case ispec.MediaTypeArtifactManifest:
|
||||||
notationDescriptors = append(notationDescriptors, desc)
|
notationDescriptors = append(notationDescriptors, desc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1615,7 +1615,9 @@ func gcNotationSignatures(imgStore *ImageStoreLocal, oci casext.Engine, index *i
|
||||||
for _, notationDesc := range notationDescriptors {
|
for _, notationDesc := range notationDescriptors {
|
||||||
foundSubject := false
|
foundSubject := false
|
||||||
// check if we can find the manifest which the signature points to
|
// check if we can find the manifest which the signature points to
|
||||||
artManifest, err := storage.GetOrasManifestByDigest(imgStore, repo, notationDesc.Digest, imgStore.log)
|
var artManifest ispec.Artifact
|
||||||
|
|
||||||
|
buf, err := imgStore.GetBlobContent(repo, notationDesc.Digest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
imgStore.log.Error().Err(err).Str("repo", repo).Str("digest", notationDesc.Digest.String()).
|
imgStore.log.Error().Err(err).Str("repo", repo).Str("digest", notationDesc.Digest.String()).
|
||||||
Msg("gc: failed to get oras artifact manifest")
|
Msg("gc: failed to get oras artifact manifest")
|
||||||
|
@ -1623,7 +1625,14 @@ func gcNotationSignatures(imgStore *ImageStoreLocal, oci casext.Engine, index *i
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip oras artifacts which are not signatures
|
if err := json.Unmarshal(buf, &artManifest); err != nil {
|
||||||
|
imgStore.log.Error().Err(err).Str("repo", repo).Str("digest", notationDesc.Digest.String()).
|
||||||
|
Msg("gc: failed to get oras artifact manifest")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip oci artifacts which are not signatures
|
||||||
if artManifest.ArtifactType != notreg.ArtifactTypeNotation {
|
if artManifest.ArtifactType != notreg.ArtifactTypeNotation {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,34 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/notaryproject/notation-core-go/signature/jws"
|
||||||
|
"github.com/notaryproject/notation-core-go/testhelper"
|
||||||
|
"github.com/notaryproject/notation-go"
|
||||||
|
notconfig "github.com/notaryproject/notation-go/config"
|
||||||
|
"github.com/notaryproject/notation-go/dir"
|
||||||
|
notreg "github.com/notaryproject/notation-go/registry"
|
||||||
|
"github.com/notaryproject/notation-go/signer"
|
||||||
|
"github.com/notaryproject/notation-go/verifier"
|
||||||
godigest "github.com/opencontainers/go-digest"
|
godigest "github.com/opencontainers/go-digest"
|
||||||
"github.com/opencontainers/image-spec/specs-go"
|
"github.com/opencontainers/image-spec/specs-go"
|
||||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
@ -27,6 +41,9 @@ import (
|
||||||
"github.com/sigstore/cosign/cmd/cosign/cli/options"
|
"github.com/sigstore/cosign/cmd/cosign/cli/options"
|
||||||
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
|
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
|
||||||
"gopkg.in/resty.v1"
|
"gopkg.in/resty.v1"
|
||||||
|
"oras.land/oras-go/v2/registry"
|
||||||
|
"oras.land/oras-go/v2/registry/remote"
|
||||||
|
"oras.land/oras-go/v2/registry/remote/auth"
|
||||||
|
|
||||||
"zotregistry.io/zot/pkg/storage"
|
"zotregistry.io/zot/pkg/storage"
|
||||||
)
|
)
|
||||||
|
@ -37,6 +54,8 @@ const (
|
||||||
SleepTime = 100 * time.Millisecond
|
SleepTime = 100 * time.Millisecond
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var NotationPathLock = new(sync.Mutex) //nolint: gochecknoglobals
|
||||||
|
|
||||||
// which: manifest, config, layer
|
// which: manifest, config, layer
|
||||||
func GetTestBlobDigest(image, which string) godigest.Digest {
|
func GetTestBlobDigest(image, which string) godigest.Digest {
|
||||||
prePath := "../test/data"
|
prePath := "../test/data"
|
||||||
|
@ -61,8 +80,11 @@ func GetTestBlobDigest(image, which string) godigest.Digest {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrPostBlob = errors.New("can't post blob")
|
ErrPostBlob = errors.New("can't post blob")
|
||||||
ErrPutBlob = errors.New("can't put blob")
|
ErrPutBlob = errors.New("can't put blob")
|
||||||
|
ErrAlreadyExists = errors.New("already exists")
|
||||||
|
ErrKeyNotFound = errors.New("key not found")
|
||||||
|
ErrSignatureVerification = errors.New("signature verification failed")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Image struct {
|
type Image struct {
|
||||||
|
@ -667,11 +689,15 @@ func UploadImage(img Image, baseURL, repo string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = resty.R().
|
resp, err = resty.R().
|
||||||
SetHeader("Content-type", "application/vnd.oci.image.manifest.v1+json").
|
SetHeader("Content-type", "application/vnd.oci.image.manifest.v1+json").
|
||||||
SetBody(manifestBlob).
|
SetBody(manifestBlob).
|
||||||
Put(baseURL + "/v2/" + repo + "/manifests/" + img.Tag)
|
Put(baseURL + "/v2/" + repo + "/manifests/" + img.Tag)
|
||||||
|
|
||||||
|
if ErrStatusCode(resp.StatusCode()) != http.StatusCreated {
|
||||||
|
return ErrPutBlob
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,6 +771,469 @@ func ReadLogFileAndSearchString(logPath string, stringToMatch string, timeout ti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CopyFile(sourceFilePath, destFilePath string) error {
|
||||||
|
destFile, err := os.Create(destFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer destFile.Close()
|
||||||
|
|
||||||
|
sourceFile, err := os.Open(sourceFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
if _, err = io.Copy(destFile, sourceFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadNotationPath(tdir string) {
|
||||||
|
dir.UserConfigDir = filepath.Join(tdir, "notation")
|
||||||
|
|
||||||
|
// set user libexec
|
||||||
|
dir.UserLibexecDir = dir.UserConfigDir
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateNotationCerts(tdir string, certName string) error {
|
||||||
|
// generate RSA private key
|
||||||
|
bits := 2048
|
||||||
|
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, bits)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyBytes, err := x509.MarshalPKCS8PrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})
|
||||||
|
|
||||||
|
rsaCertTuple := testhelper.GetRSASelfSignedCertTupleWithPK(key, "cert")
|
||||||
|
|
||||||
|
certBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rsaCertTuple.Cert.Raw})
|
||||||
|
|
||||||
|
// write private key
|
||||||
|
relativeKeyPath, relativeCertPath := dir.LocalKeyPath(certName)
|
||||||
|
|
||||||
|
configFS := dir.ConfigFS()
|
||||||
|
|
||||||
|
keyPath, err := configFS.SysPath(relativeKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
certPath, err := configFS.SysPath(relativeCertPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := WriteFileWithPermission(keyPath, keyPEM, 0o600, false); err != nil { //nolint:gomnd
|
||||||
|
return fmt.Errorf("failed to write key file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// write self-signed certificate
|
||||||
|
if err := WriteFileWithPermission(certPath, certBytes, 0o644, false); err != nil { //nolint:gomnd
|
||||||
|
return fmt.Errorf("failed to write certificate file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signingKeys, err := notconfig.LoadSigningKeys()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
keySuite := notconfig.KeySuite{
|
||||||
|
Name: certName,
|
||||||
|
X509KeyPair: ¬config.X509KeyPair{
|
||||||
|
KeyPath: keyPath,
|
||||||
|
CertificatePath: certPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// addKeyToSigningKeys
|
||||||
|
if Contains(signingKeys.Keys, keySuite.Name) {
|
||||||
|
return ErrAlreadyExists
|
||||||
|
}
|
||||||
|
|
||||||
|
signingKeys.Keys = append(signingKeys.Keys, keySuite)
|
||||||
|
|
||||||
|
// Add to the trust store
|
||||||
|
trustStorePath := path.Join(tdir, fmt.Sprintf("notation/truststore/x509/ca/%s", certName))
|
||||||
|
|
||||||
|
if _, err := os.Stat(filepath.Join(trustStorePath, filepath.Base(certPath))); err == nil {
|
||||||
|
return ErrAlreadyExists
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(trustStorePath, 0o755); err != nil { //nolint:gomnd
|
||||||
|
return fmt.Errorf("GenerateNotationCerts os.MkdirAll failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
trustCertPath := path.Join(trustStorePath, fmt.Sprintf("%s%s", certName, dir.LocalCertificateExtension))
|
||||||
|
|
||||||
|
err = CopyFile(certPath, trustCertPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save to the SigningKeys.json
|
||||||
|
if err := signingKeys.Save(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignWithNotation(keyName string, reference string, tdir string) error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
// getSigner
|
||||||
|
var newSigner notation.Signer
|
||||||
|
|
||||||
|
mediaType := jws.MediaTypeEnvelope
|
||||||
|
|
||||||
|
// ResolveKey
|
||||||
|
signingKeys, err := LoadNotationSigningkeys(tdir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := Index(signingKeys.Keys, keyName)
|
||||||
|
if idx < 0 {
|
||||||
|
return ErrKeyNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
key := signingKeys.Keys[idx]
|
||||||
|
|
||||||
|
if key.X509KeyPair != nil {
|
||||||
|
newSigner, err = signer.NewFromFiles(key.X509KeyPair.KeyPath, key.X509KeyPair.CertificatePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareSigningContent
|
||||||
|
// getRepositoryClient
|
||||||
|
authClient := &auth.Client{
|
||||||
|
Credential: func(ctx context.Context, reg string) (auth.Credential, error) {
|
||||||
|
return auth.EmptyCredential, nil
|
||||||
|
},
|
||||||
|
Cache: auth.NewCache(),
|
||||||
|
ClientID: "notation",
|
||||||
|
}
|
||||||
|
|
||||||
|
authClient.SetUserAgent("notation/zot_tests")
|
||||||
|
|
||||||
|
plainHTTP := true
|
||||||
|
|
||||||
|
// Resolve referance
|
||||||
|
ref, err := registry.ParseReference(reference)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteRepo := &remote.Repository{
|
||||||
|
Client: authClient,
|
||||||
|
Reference: ref,
|
||||||
|
PlainHTTP: plainHTTP,
|
||||||
|
}
|
||||||
|
|
||||||
|
sigRepo := notreg.NewRepository(remoteRepo)
|
||||||
|
|
||||||
|
sigOpts := notation.SignOptions{
|
||||||
|
ArtifactReference: ref.String(),
|
||||||
|
SignatureMediaType: mediaType,
|
||||||
|
PluginConfig: map[string]string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = notation.Sign(ctx, newSigner, sigRepo, sigOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifyWithNotation(reference string, tdir string) error {
|
||||||
|
// check if trustpolicy.json exists
|
||||||
|
trustpolicyPath := path.Join(tdir, "notation/trustpolicy.json")
|
||||||
|
|
||||||
|
if _, err := os.Stat(trustpolicyPath); errors.Is(err, os.ErrNotExist) {
|
||||||
|
trustPolicy := `
|
||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"trustPolicies": [
|
||||||
|
{
|
||||||
|
"name": "good",
|
||||||
|
"registryScopes": [ "*" ],
|
||||||
|
"signatureVerification": {
|
||||||
|
"level" : "audit"
|
||||||
|
},
|
||||||
|
"trustStores": ["ca:good"],
|
||||||
|
"trustedIdentities": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
|
||||||
|
file, err := os.Create(trustpolicyPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = file.WriteString(trustPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start verifying signatures
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
// getRepositoryClient
|
||||||
|
authClient := &auth.Client{
|
||||||
|
Credential: func(ctx context.Context, reg string) (auth.Credential, error) {
|
||||||
|
return auth.EmptyCredential, nil
|
||||||
|
},
|
||||||
|
Cache: auth.NewCache(),
|
||||||
|
ClientID: "notation",
|
||||||
|
}
|
||||||
|
|
||||||
|
authClient.SetUserAgent("notation/zot_tests")
|
||||||
|
|
||||||
|
plainHTTP := true
|
||||||
|
|
||||||
|
// Resolve referance
|
||||||
|
ref, err := registry.ParseReference(reference)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteRepo := &remote.Repository{
|
||||||
|
Client: authClient,
|
||||||
|
Reference: ref,
|
||||||
|
PlainHTTP: plainHTTP,
|
||||||
|
}
|
||||||
|
|
||||||
|
repo := notreg.NewRepository(remoteRepo)
|
||||||
|
|
||||||
|
manifestDesc, err := repo.Resolve(ctx, ref.Reference)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ref.ValidateReferenceAsDigest(); err != nil {
|
||||||
|
ref.Reference = manifestDesc.Digest.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getVerifier
|
||||||
|
newVerifier, err := verifier.NewFromConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteRepo = &remote.Repository{
|
||||||
|
Client: authClient,
|
||||||
|
Reference: ref,
|
||||||
|
PlainHTTP: plainHTTP,
|
||||||
|
}
|
||||||
|
|
||||||
|
repo = notreg.NewRepository(remoteRepo)
|
||||||
|
|
||||||
|
configs := map[string]string{}
|
||||||
|
|
||||||
|
verifyOpts := notation.RemoteVerifyOptions{
|
||||||
|
ArtifactReference: ref.String(),
|
||||||
|
PluginConfig: configs,
|
||||||
|
MaxSignatureAttempts: math.MaxInt64,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, outcomes, err := notation.Verify(ctx, newVerifier, repo, verifyOpts)
|
||||||
|
if err != nil || len(outcomes) == 0 {
|
||||||
|
return ErrSignatureVerification
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListNotarySignatures(reference string, tdir string) ([]godigest.Digest, error) {
|
||||||
|
signatures := []godigest.Digest{}
|
||||||
|
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
// getSignatureRepository
|
||||||
|
ref, err := registry.ParseReference(reference)
|
||||||
|
if err != nil {
|
||||||
|
return signatures, err
|
||||||
|
}
|
||||||
|
|
||||||
|
plainHTTP := true
|
||||||
|
|
||||||
|
// getRepositoryClient
|
||||||
|
authClient := &auth.Client{
|
||||||
|
Credential: func(ctx context.Context, registry string) (auth.Credential, error) {
|
||||||
|
return auth.EmptyCredential, nil
|
||||||
|
},
|
||||||
|
Cache: auth.NewCache(),
|
||||||
|
ClientID: "notation",
|
||||||
|
}
|
||||||
|
|
||||||
|
authClient.SetUserAgent("notation/zot_tests")
|
||||||
|
|
||||||
|
remoteRepo := &remote.Repository{
|
||||||
|
Client: authClient,
|
||||||
|
Reference: ref,
|
||||||
|
PlainHTTP: plainHTTP,
|
||||||
|
}
|
||||||
|
|
||||||
|
sigRepo := notreg.NewRepository(remoteRepo)
|
||||||
|
|
||||||
|
artifectDesc, err := sigRepo.Resolve(ctx, reference)
|
||||||
|
if err != nil {
|
||||||
|
return signatures, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sigRepo.ListSignatures(ctx, artifectDesc, func(signatureManifests []ispec.Descriptor) error {
|
||||||
|
for _, sigManifestDesc := range signatureManifests {
|
||||||
|
signatures = append(signatures, sigManifestDesc.Digest)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return signatures, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadNotationSigningkeys(tdir string) (*notconfig.SigningKeys, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var signingKeysInfo *notconfig.SigningKeys
|
||||||
|
|
||||||
|
filePath := path.Join(tdir, "notation/signingkeys.json")
|
||||||
|
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
// create file
|
||||||
|
newSigningKeys := notconfig.NewSigningKeys()
|
||||||
|
|
||||||
|
newFile, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return newSigningKeys, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer newFile.Close()
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(newFile)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
|
||||||
|
err = encoder.Encode(newSigningKeys)
|
||||||
|
|
||||||
|
return newSigningKeys, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
err = json.NewDecoder(file).Decode(&signingKeysInfo)
|
||||||
|
|
||||||
|
return signingKeysInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadNotationConfig(tdir string) (*notconfig.Config, error) {
|
||||||
|
var configInfo *notconfig.Config
|
||||||
|
|
||||||
|
filePath := path.Join(tdir, "notation/signingkeys.json")
|
||||||
|
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return configInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
err = json.NewDecoder(file).Decode(&configInfo)
|
||||||
|
if err != nil {
|
||||||
|
return configInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default value
|
||||||
|
configInfo.SignatureFormat = strings.ToLower(configInfo.SignatureFormat)
|
||||||
|
if configInfo.SignatureFormat == "" {
|
||||||
|
configInfo.SignatureFormat = "jws"
|
||||||
|
}
|
||||||
|
|
||||||
|
return configInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteFileWithPermission(path string, data []byte, perm fs.FileMode, overwrite bool) error {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
flag := os.O_WRONLY | os.O_CREATE
|
||||||
|
|
||||||
|
if overwrite {
|
||||||
|
flag |= os.O_TRUNC
|
||||||
|
} else {
|
||||||
|
flag |= os.O_EXCL
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.OpenFile(path, flag, perm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = file.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
file.Close()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsDigestReference(ref string) bool {
|
||||||
|
parts := strings.SplitN(ref, "/", 2) //nolint:gomnd
|
||||||
|
if len(parts) == 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
index := strings.Index(parts[1], "@")
|
||||||
|
|
||||||
|
return index != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
type isser interface {
|
||||||
|
Is(string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index returns the index of the first occurrence of name in s,
|
||||||
|
// or -1 if not present.
|
||||||
|
func Index[E isser](s []E, name string) int {
|
||||||
|
for i, v := range s {
|
||||||
|
if v.Is(name) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains reports whether name is present in s.
|
||||||
|
func Contains[E isser](s []E, name string) bool {
|
||||||
|
return Index(s, name) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
func UploadImageWithBasicAuth(img Image, baseURL, repo, user, password string) error {
|
func UploadImageWithBasicAuth(img Image, baseURL, repo, user, password string) error {
|
||||||
for _, blob := range img.Layers {
|
for _, blob := range img.Layers {
|
||||||
resp, err := resty.R().
|
resp, err := resty.R().
|
||||||
|
@ -883,17 +1372,13 @@ func SignImageUsingNotary(repoTag, port string) error {
|
||||||
|
|
||||||
_ = os.Chdir(tdir)
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
_, err = exec.LookPath("notation")
|
NotationPathLock.Lock()
|
||||||
if err != nil {
|
defer NotationPathLock.Unlock()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Setenv("XDG_CONFIG_HOME", tdir)
|
LoadNotationPath(tdir)
|
||||||
|
|
||||||
// generate a keypair
|
// generate a keypair
|
||||||
cmd := exec.Command("notation", "cert", "generate-test", "--trust", "notation-sign-test")
|
err = GenerateNotationCerts(tdir, "notation-sign-test")
|
||||||
|
|
||||||
err = cmd.Run()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -901,7 +1386,7 @@ func SignImageUsingNotary(repoTag, port string) error {
|
||||||
// sign the image
|
// sign the image
|
||||||
image := fmt.Sprintf("localhost:%s/%s", port, repoTag)
|
image := fmt.Sprintf("localhost:%s/%s", port, repoTag)
|
||||||
|
|
||||||
cmd = exec.Command("notation", "sign", "--key", "notation-sign-test", "--plain-http", image)
|
err = SignWithNotation("notation-sign-test", image, tdir)
|
||||||
|
|
||||||
return cmd.Run()
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
notconfig "github.com/notaryproject/notation-go/config"
|
||||||
godigest "github.com/opencontainers/go-digest"
|
godigest "github.com/opencontainers/go-digest"
|
||||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
@ -453,7 +454,7 @@ func TestUploadImage(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err := test.UploadImage(img, baseURL, "test")
|
err := test.UploadImage(img, baseURL, "test")
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Upload image with authentification", t, func() {
|
Convey("Upload image with authentification", t, func() {
|
||||||
|
@ -792,3 +793,481 @@ func TestInjectUploadImageWithBasicAuth(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCopyFile(t *testing.T) {
|
||||||
|
Convey("destFilePath does not exist", t, func() {
|
||||||
|
err := test.CopyFile("/path/to/srcFile", "~/path/to/some/unexisting/destDir/file")
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("sourceFile does not exist", t, func() {
|
||||||
|
err := test.CopyFile("/path/to/some/unexisting/file", path.Join(t.TempDir(), "destFile.txt"))
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsDigestReference(t *testing.T) {
|
||||||
|
Convey("not digest reference", t, func() {
|
||||||
|
res := test.IsDigestReference("notDigestReference/input")
|
||||||
|
So(res, ShouldBeFalse)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("wrong input format", t, func() {
|
||||||
|
res := test.IsDigestReference("wrongInput")
|
||||||
|
So(res, ShouldBeFalse)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadNotationSigningkeys(t *testing.T) {
|
||||||
|
Convey("notation directory doesn't exist", t, func() {
|
||||||
|
_, err := test.LoadNotationSigningkeys(t.TempDir())
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("wrong content of signingkeys.json", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
dir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(dir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
filePath := path.Join(dir, "signingkeys.json")
|
||||||
|
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
_, err = test.LoadNotationSigningkeys(tempDir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("not enough permissions to access signingkeys.json", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
dir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(dir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
filePath := path.Join(dir, "signingkeys.json")
|
||||||
|
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o300) //nolint: gosec
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
_, err = test.LoadNotationSigningkeys(tempDir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("signingkeys.json not exists so it is created successfully", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
dir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(dir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
_, err = test.LoadNotationSigningkeys(tempDir)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("signingkeys.json not exists - error trying to create it", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
dir := path.Join(tempDir, "notation")
|
||||||
|
// create notation directory without write permissions
|
||||||
|
err := os.Mkdir(dir, 0o555)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
_, err = test.LoadNotationSigningkeys(tempDir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadNotationConfig(t *testing.T) {
|
||||||
|
Convey("directory doesn't exist", t, func() {
|
||||||
|
_, err := test.LoadNotationConfig(t.TempDir())
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("wrong content of signingkeys.json", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
dir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(dir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
filePath := path.Join(dir, "signingkeys.json")
|
||||||
|
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
_, err = test.LoadNotationConfig(tempDir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("check default value of signature format", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
dir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(dir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
filePath := path.Join(dir, "signingkeys.json")
|
||||||
|
err = os.WriteFile(filePath, []byte("{\"SignatureFormat\": \"\"}"), 0o666) //nolint: gosec
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
configInfo, err := test.LoadNotationConfig(tempDir)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(configInfo.SignatureFormat, ShouldEqual, "jws")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignWithNotation(t *testing.T) {
|
||||||
|
Convey("notation directory doesn't exist", t, func() {
|
||||||
|
err := test.SignWithNotation("key", "reference", t.TempDir())
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("key not found", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
dir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(dir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
filePath := path.Join(dir, "signingkeys.json")
|
||||||
|
err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.SignWithNotation("key", "reference", tempDir)
|
||||||
|
So(err, ShouldEqual, test.ErrKeyNotFound)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("not enough permissions to access notation/localkeys dir", t, func() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
defer func() { _ = os.Chdir(cwd) }()
|
||||||
|
tdir := t.TempDir()
|
||||||
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tdir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(tdir, "key")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = os.Chmod(path.Join(tdir, "notation", "localkeys"), 0o000)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.SignWithNotation("key", "reference", tdir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = os.Chmod(path.Join(tdir, "notation", "localkeys"), 0o755)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("error parsing reference", t, func() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
defer func() { _ = os.Chdir(cwd) }()
|
||||||
|
tdir := t.TempDir()
|
||||||
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tdir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(tdir, "key")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.SignWithNotation("key", "invalidReference", tdir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("error signing", t, func() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
defer func() { _ = os.Chdir(cwd) }()
|
||||||
|
tdir := t.TempDir()
|
||||||
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tdir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(tdir, "key")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.SignWithNotation("key", "localhost:8080/invalidreference:1.0", tdir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyWithNotation(t *testing.T) {
|
||||||
|
Convey("notation directory doesn't exist", t, func() {
|
||||||
|
err := test.VerifyWithNotation("reference", t.TempDir())
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("error parsing reference", t, func() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
defer func() { _ = os.Chdir(cwd) }()
|
||||||
|
tdir := t.TempDir()
|
||||||
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tdir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(tdir, "key")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.VerifyWithNotation("invalidReference", tdir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("error trying to get manifest", t, func() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
defer func() { _ = os.Chdir(cwd) }()
|
||||||
|
tdir := t.TempDir()
|
||||||
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tdir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(tdir, "key")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.VerifyWithNotation("localhost:8080/invalidreference:1.0", tdir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("invalid content of trustpolicy.json", t, func() {
|
||||||
|
// start a new server
|
||||||
|
port := test.GetFreePort()
|
||||||
|
baseURL := test.GetBaseURL(port)
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
conf := config.New()
|
||||||
|
conf.HTTP.Port = port
|
||||||
|
conf.Storage.RootDirectory = dir
|
||||||
|
|
||||||
|
ctlr := api.NewController(conf)
|
||||||
|
cm := test.NewControllerManager(ctlr)
|
||||||
|
// this blocks
|
||||||
|
cm.StartAndWait(port)
|
||||||
|
defer cm.StopServer()
|
||||||
|
|
||||||
|
repoName := "signed-repo"
|
||||||
|
tag := "1.0"
|
||||||
|
cfg, layers, manifest, err := test.GetImageComponents(2)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.UploadImage(
|
||||||
|
test.Image{
|
||||||
|
Config: cfg,
|
||||||
|
Layers: layers,
|
||||||
|
Manifest: manifest,
|
||||||
|
Tag: tag,
|
||||||
|
}, baseURL, repoName)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
content, err := json.Marshal(manifest)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
digest := godigest.FromBytes(content)
|
||||||
|
So(digest, ShouldNotBeNil)
|
||||||
|
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
notationDir := path.Join(tempDir, "notation")
|
||||||
|
err = os.Mkdir(notationDir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
filePath := path.Join(notationDir, "trustpolicy.json")
|
||||||
|
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tempDir)
|
||||||
|
|
||||||
|
err = test.VerifyWithNotation(fmt.Sprintf("localhost:%s/%s:%s", port, repoName, tag), tempDir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListNotarySignatures(t *testing.T) {
|
||||||
|
Convey("error parsing reference", t, func() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
defer func() { _ = os.Chdir(cwd) }()
|
||||||
|
tdir := t.TempDir()
|
||||||
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
|
_, err = test.ListNotarySignatures("invalidReference", tdir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("error trying to get manifest", t, func() {
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
defer func() { _ = os.Chdir(cwd) }()
|
||||||
|
tdir := t.TempDir()
|
||||||
|
_ = os.Chdir(tdir)
|
||||||
|
|
||||||
|
_, err = test.ListNotarySignatures("localhost:8080/invalidreference:1.0", tdir)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateNotationCerts(t *testing.T) {
|
||||||
|
Convey("write key file with permission", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
notationDir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(notationDir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
filePath := path.Join(notationDir, "localkeys")
|
||||||
|
err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tempDir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(t.TempDir(), "cert")
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("write cert file with permission", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
notationDir := path.Join(tempDir, "notation", "localkeys")
|
||||||
|
err := os.MkdirAll(notationDir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
filePath := path.Join(notationDir, "cert.crt")
|
||||||
|
err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = os.Chmod(filePath, 0o000)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tempDir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(t.TempDir(), "cert")
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = os.Chmod(filePath, 0o755)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("signingkeys.json file - not enough permission", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
notationDir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(notationDir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
filePath := path.Join(notationDir, "signingkeys.json")
|
||||||
|
_, err = os.Create(filePath) //nolint: gosec
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.Chmod(filePath, 0o000)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tempDir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(t.TempDir(), "cert")
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = os.Remove(filePath)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.RemoveAll(path.Join(notationDir, "localkeys"))
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
signingKeysBuf, err := json.Marshal(notconfig.SigningKeys{})
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.WriteFile(filePath, signingKeysBuf, 0o555)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = test.GenerateNotationCerts(t.TempDir(), "cert")
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
Convey("keysuite already exists in signingkeys.json", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
notationDir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(notationDir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
certName := "cert-test"
|
||||||
|
filePath := path.Join(notationDir, "signingkeys.json")
|
||||||
|
keyPath := path.Join(notationDir, "localkeys", certName+".key")
|
||||||
|
certPath := path.Join(notationDir, "localkeys", certName+".crt")
|
||||||
|
signingKeys := notconfig.SigningKeys{}
|
||||||
|
keySuite := notconfig.KeySuite{
|
||||||
|
Name: certName,
|
||||||
|
X509KeyPair: ¬config.X509KeyPair{
|
||||||
|
KeyPath: keyPath,
|
||||||
|
CertificatePath: certPath,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
signingKeys.Keys = []notconfig.KeySuite{keySuite}
|
||||||
|
signingKeysBuf, err := json.Marshal(signingKeys)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.WriteFile(filePath, signingKeysBuf, 0o600)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tempDir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(t.TempDir(), certName)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
Convey("truststore files", t, func() {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
notationDir := path.Join(tempDir, "notation")
|
||||||
|
err := os.Mkdir(notationDir, 0o777)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
certName := "cert-test"
|
||||||
|
trustStorePath := path.Join(notationDir, fmt.Sprintf("truststore/x509/ca/%s", certName))
|
||||||
|
err = os.MkdirAll(trustStorePath, 0o755)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.Chmod(path.Join(notationDir, "truststore/x509"), 0o000)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
test.NotationPathLock.Lock()
|
||||||
|
defer test.NotationPathLock.Unlock()
|
||||||
|
|
||||||
|
test.LoadNotationPath(tempDir)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(tempDir, certName)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = os.RemoveAll(path.Join(notationDir, "localkeys"))
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.Chmod(path.Join(notationDir, "truststore/x509"), 0o755)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
_, err = os.Create(path.Join(trustStorePath, "cert-test.crt"))
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(tempDir, certName)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
|
||||||
|
err = os.RemoveAll(path.Join(notationDir, "localkeys"))
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.Remove(path.Join(trustStorePath, "cert-test.crt"))
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
err = os.Chmod(path.Join(notationDir, "truststore/x509/ca", certName), 0o555)
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
err = test.GenerateNotationCerts(tempDir, certName)
|
||||||
|
So(err, ShouldNotBeNil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -119,11 +119,33 @@ function teardown_file() {
|
||||||
[ $(echo "${lines[-1]}" | jq '.data.ImageList[0].RepoName') = '"annotations"' ]
|
[ $(echo "${lines[-1]}" | jq '.data.ImageList[0].RepoName') = '"annotations"' ]
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
run notation cert generate-test --trust "notation-sign-test"
|
run notation cert generate-test "notation-sign-test"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
local trust_policy_file=${HOME}/.config/notation/trustpolicy.json
|
||||||
|
|
||||||
|
cat >${trust_policy_file} <<EOF
|
||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"trustPolicies": [
|
||||||
|
{
|
||||||
|
"name": "notation-sign-test",
|
||||||
|
"registryScopes": [ "*" ],
|
||||||
|
"signatureVerification": {
|
||||||
|
"level" : "strict"
|
||||||
|
},
|
||||||
|
"trustStores": [ "ca:notation-sign-test" ],
|
||||||
|
"trustedIdentities": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
run notation sign --key "notation-sign-test" --plain-http localhost:8080/annotations:latest
|
run notation sign --key "notation-sign-test" --plain-http localhost:8080/annotations:latest
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run notation verify --cert "notation-sign-test" --plain-http localhost:8080/annotations:latest
|
run notation verify --plain-http localhost:8080/annotations:latest
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run notation list --plain-http localhost:8080/annotations:latest
|
run notation list --plain-http localhost:8080/annotations:latest
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
|
@ -248,11 +248,33 @@ function teardown_file() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "sign/verify with notation" {
|
@test "sign/verify with notation" {
|
||||||
run notation cert generate-test --trust "notation-sign-sync-test"
|
run notation cert generate-test "notation-sign-sync-test"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
local trust_policy_file=${HOME}/.config/notation/trustpolicy.json
|
||||||
|
|
||||||
|
cat >${trust_policy_file} <<EOF
|
||||||
|
{
|
||||||
|
"version": "1.0",
|
||||||
|
"trustPolicies": [
|
||||||
|
{
|
||||||
|
"name": "notation-sign-sync-test",
|
||||||
|
"registryScopes": [ "*" ],
|
||||||
|
"signatureVerification": {
|
||||||
|
"level" : "strict"
|
||||||
|
},
|
||||||
|
"trustStores": [ "ca:notation-sign-sync-test" ],
|
||||||
|
"trustedIdentities": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
run notation sign --key "notation-sign-sync-test" --plain-http localhost:9000/golang:1.19
|
run notation sign --key "notation-sign-sync-test" --plain-http localhost:9000/golang:1.19
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run notation verify --cert "notation-sign-sync-test" --plain-http localhost:9000/golang:1.19
|
run notation verify --plain-http localhost:9000/golang:1.19
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run notation list --plain-http localhost:9000/golang:1.19
|
run notation list --plain-http localhost:9000/golang:1.19
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
@ -262,7 +284,7 @@ function teardown_file() {
|
||||||
# wait for signatures to be copied
|
# wait for signatures to be copied
|
||||||
run sleep 5s
|
run sleep 5s
|
||||||
|
|
||||||
run notation verify --cert "notation-sign-sync-test" --plain-http localhost:8081/golang:1.19
|
run notation verify --plain-http localhost:8081/golang:1.19
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
run cosign verify --key cosign.pub localhost:8081/golang:1.19
|
run cosign verify --key cosign.pub localhost:8081/golang:1.19
|
||||||
|
@ -270,7 +292,7 @@ function teardown_file() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "sync signatures ondemand" {
|
@test "sync signatures ondemand" {
|
||||||
run notation verify --cert "notation-sign-sync-test" --plain-http localhost:8082/golang:1.19
|
run notation verify --plain-http localhost:8082/golang:1.19
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
run cosign verify --key cosign.pub localhost:8082/golang:1.19
|
run cosign verify --key cosign.pub localhost:8082/golang:1.19
|
||||||
|
|
Loading…
Reference in a new issue