mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
feat: upload cosign public key and notation certificates to cloud
Signed-off-by: Andreea-Lupu <andreealupu1470@yahoo.com>
(cherry picked from commit 5f2f8adbc3
)
Signed-off-by: Andrei Aaron <aaaron@luxoft.com>
This commit is contained in:
parent
9bccd784a9
commit
eafcc1a213
23 changed files with 1385 additions and 413 deletions
12
go.mod
12
go.mod
|
@ -50,6 +50,8 @@ require (
|
|||
require (
|
||||
github.com/aquasecurity/trivy v0.43.1
|
||||
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.21.2
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.21.3
|
||||
github.com/aws/aws-secretsmanager-caching-go v1.1.2
|
||||
github.com/containers/image/v5 v5.25.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/google/go-github/v52 v52.0.0
|
||||
|
@ -256,14 +258,14 @@ require (
|
|||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 // indirect
|
||||
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.333
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.1
|
||||
github.com/aws/aws-sdk-go v1.44.334
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.27
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.36
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ecr v1.17.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0 // indirect
|
||||
|
@ -271,7 +273,7 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 // indirect
|
||||
github.com/aws/smithy-go v1.14.1 // indirect
|
||||
github.com/aws/smithy-go v1.14.2 // indirect
|
||||
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
|
|
25
go.sum
25
go.sum
|
@ -405,16 +405,18 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
|
|||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
github.com/aws/aws-sdk-go v1.44.333 h1:X0j5TGXtHLZzDB/uRcGKLG77ERFtxYQtXefs+Apf2PU=
|
||||
github.com/aws/aws-sdk-go v1.44.333/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go v1.44.287/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go v1.44.334 h1:h2bdbGb//fez6Sv6PaYv868s9liDeoYM6hYsAqTB4MU=
|
||||
github.com/aws/aws-sdk-go v1.44.334/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250=
|
||||
github.com/aws/aws-sdk-go-v2 v1.14.0/go.mod h1:ZA3Y8V0LrlWj63MQAnRHgKf/5QB//LSZCPNWlWrNGLU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k=
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.1/go.mod h1:JLnGeGONAyi2lWXI1p0PCIOIy333JMVK1U7Hf0aRFLw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.1 h1:rZBf5DWr7YGrnlTK4kgDQGn1ltqOg5orCYb/UhOFZkg=
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.1/go.mod h1:NU06lETsFm8fUC6ZjhgDpVBcGZTFQ6XM+LZWZxMI4ac=
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.27 h1:Az9uLwmssTE6OGTpsFqOnaGpLnKDqNYOJzWuC6UAYzA=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.27/go.mod h1:0My+YgmkGxeqjXZb5BYme5pc4drjTnM+x1GJ3zv42Nw=
|
||||
|
@ -431,15 +433,17 @@ github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfI
|
|||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.25/go.mod h1:Zb29PYkf42vVYQY6pvSyJCJcFHlPIiY+YKdPtwnvMkY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38 h1:c8ed/T9T2K5I+h/JzmF5tpI46+OODQ74dzmdo+QnaMg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.38/go.mod h1:qggunOChCMu9ZF/UkAfhTz25+U2rLVb3ya0Ua6TTfCA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.3.0/go.mod h1:miRSv9l093jX/t/j+mBCaLqFHo9xKYzJ7DGm1BsGoJM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19/go.mod h1:6Q0546uHDp421okhmmGfbxzq2hBqbXFNpi4k+Q1JnQA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32 h1:hNeAAymUY5gu11WrrmFb3CVIp9Dar9hbo44yzzcQpzA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.32/go.mod h1:0ZXSqrty4FtQ7p8TEuRde/SZm9X05KT18LAUlR40Ln0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.1.1/go.mod h1:Zy8smImhTdOETZqfyn01iNOe0CNggVbPjCajyaz6Gvg=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0=
|
||||
|
@ -466,6 +470,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EO
|
|||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 h1:bkRyG4a929RCnpVSTvLM2j/T4ls015ZhhYApbmYs15s=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU=
|
||||
github.com/aws/aws-sdk-go-v2/service/kms v1.23.0 h1:NXYeZBNg35rDBhcus60DFkIP7q6RNSkarLx+37ERX1g=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.21.3 h1:H6ZipEknzu7RkJW3w2PP75zd8XOdR35AEY5D57YrJtA=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.21.3/go.mod h1:5W2cYXDPabUmwULErlC92ffLhtTuyv4ai+5HhdbhfNo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 h1:nneMBM2p79PGWBQovYO/6Xnc2ryRMw3InnDJq1FHkSY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY=
|
||||
|
@ -474,13 +480,16 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12/go.mod h1:E4VrHCPzmVB/KFXt
|
|||
github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 h1:XFJ2Z6sNUUcAz9poj+245DMkrHE4h2j5I9/xD50RHfE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg=
|
||||
github.com/aws/aws-secretsmanager-caching-go v1.1.2 h1:tY3pRhAkaohm75KFpGHoqjWrnRpznqrc8iX/wTLVpH0=
|
||||
github.com/aws/aws-secretsmanager-caching-go v1.1.2/go.mod h1:s3Or+O0O8obPyDJz6875Rg1WApAbQ64L0WTBwYNnKLo=
|
||||
github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
|
||||
github.com/aws/smithy-go v1.11.0/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
|
||||
github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/smithy-go v1.13.4/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/smithy-go v1.14.1 h1:EFKMUmH/iHMqLiwoEDx2rRjRQpI1YCn5jTysoaDujFs=
|
||||
github.com/aws/smithy-go v1.14.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ=
|
||||
github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795 h1:IWeCJzU+IYaO2rVEBlGPTBfe90cmGXFTLdhUFlzDGsY=
|
||||
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795/go.mod h1:8vJsEZ4iRqG+Vx6pKhWK6U00qcj0KC37IsfszMkY6UE=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
|
@ -1808,6 +1817,7 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
|
|||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -2078,6 +2088,7 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
|
@ -2088,6 +2099,7 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
|||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -2105,6 +2117,7 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
|
|
@ -265,6 +265,11 @@ func (c *Controller) InitMetaDB(reloadCtx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = ext.SetupExtensions(c.Config, driver, c.Log) //nolint:contextcheck
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = driver.PatchDB()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -194,7 +194,7 @@ func (rh *RouteHandler) SetupRoutes() {
|
|||
// Preconditions for enabling the actual extension routes are part of extensions themselves
|
||||
ext.SetupSearchRoutes(rh.c.Config, prefixedRouter, rh.c.StoreController, rh.c.MetaDB, rh.c.CveInfo,
|
||||
rh.c.Log)
|
||||
ext.SetupImageTrustRoutes(rh.c.Config, prefixedRouter, rh.c.Log)
|
||||
ext.SetupImageTrustRoutes(rh.c.Config, prefixedRouter, rh.c.MetaDB, rh.c.Log)
|
||||
ext.SetupMgmtRoutes(rh.c.Config, prefixedRouter, rh.c.Log)
|
||||
ext.SetupUserPreferencesRoutes(rh.c.Config, prefixedRouter, rh.c.MetaDB, rh.c.Log)
|
||||
// last should always be UI because it will setup a http.FileServer and paths will be resolved by this FileServer.
|
||||
|
|
|
@ -21,16 +21,11 @@ import (
|
|||
"zotregistry.io/zot/pkg/scheduler"
|
||||
)
|
||||
|
||||
const (
|
||||
ConfigResource = "config"
|
||||
SignaturesResource = "signatures"
|
||||
)
|
||||
|
||||
func IsBuiltWithImageTrustExtension() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func SetupImageTrustRoutes(conf *config.Config, router *mux.Router, log log.Logger) {
|
||||
func SetupImageTrustRoutes(conf *config.Config, router *mux.Router, metaDB mTypes.MetaDB, log log.Logger) {
|
||||
if !conf.IsImageTrustEnabled() || (!conf.IsCosignEnabled() && !conf.IsNotationEnabled()) {
|
||||
log.Info().Msg("skip enabling the image trust routes as the config prerequisites are not met")
|
||||
|
||||
|
@ -39,7 +34,8 @@ func SetupImageTrustRoutes(conf *config.Config, router *mux.Router, log log.Logg
|
|||
|
||||
log.Info().Msg("setting up image trust routes")
|
||||
|
||||
trust := ImageTrust{Conf: conf, Log: log}
|
||||
imgTrustStore, _ := metaDB.ImageTrustStore().(*imagetrust.ImageTrustStore)
|
||||
trust := ImageTrust{Conf: conf, ImageTrustStore: imgTrustStore, Log: log}
|
||||
allowedMethods := zcommon.AllowedMethods(http.MethodPost)
|
||||
|
||||
if conf.IsNotationEnabled() {
|
||||
|
@ -70,8 +66,9 @@ func SetupImageTrustRoutes(conf *config.Config, router *mux.Router, log log.Logg
|
|||
}
|
||||
|
||||
type ImageTrust struct {
|
||||
Conf *config.Config
|
||||
Log log.Logger
|
||||
Conf *config.Config
|
||||
ImageTrustStore *imagetrust.ImageTrustStore
|
||||
Log log.Logger
|
||||
}
|
||||
|
||||
// Cosign handler godoc
|
||||
|
@ -93,7 +90,7 @@ func (trust *ImageTrust) HandleCosignPublicKeyUpload(response http.ResponseWrite
|
|||
return
|
||||
}
|
||||
|
||||
err = imagetrust.UploadPublicKey(body)
|
||||
err = imagetrust.UploadPublicKey(trust.ImageTrustStore.CosignStorage, body)
|
||||
if err != nil {
|
||||
if errors.Is(err, zerr.ErrInvalidPublicKeyContent) {
|
||||
response.WriteHeader(http.StatusBadRequest)
|
||||
|
@ -151,7 +148,7 @@ func (trust *ImageTrust) HandleNotationCertificateUpload(response http.ResponseW
|
|||
return
|
||||
}
|
||||
|
||||
err = imagetrust.UploadCertificate(body, truststoreType, truststoreName)
|
||||
err = imagetrust.UploadCertificate(trust.ImageTrustStore.NotationStorage, body, truststoreType, truststoreName)
|
||||
if err != nil {
|
||||
if errors.Is(err, zerr.ErrInvalidTruststoreType) ||
|
||||
errors.Is(err, zerr.ErrInvalidTruststoreName) ||
|
||||
|
@ -178,6 +175,35 @@ func EnableImageTrustVerification(conf *config.Config, taskScheduler *scheduler.
|
|||
generator := imagetrust.NewTaskGenerator(metaDB, log)
|
||||
|
||||
numberOfHours := 2
|
||||
interval := time.Duration(numberOfHours) * time.Minute
|
||||
interval := time.Duration(numberOfHours) * time.Hour
|
||||
taskScheduler.SubmitGenerator(generator, interval, scheduler.MediumPriority)
|
||||
}
|
||||
|
||||
func SetupImageTrustExtension(conf *config.Config, metaDB mTypes.MetaDB, log log.Logger) error {
|
||||
if !conf.IsImageTrustEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var imgTrustStore mTypes.ImageTrustStore
|
||||
|
||||
var err error
|
||||
|
||||
if conf.Storage.RemoteCache {
|
||||
endpoint, _ := conf.Storage.CacheDriver["endpoint"].(string)
|
||||
region, _ := conf.Storage.CacheDriver["region"].(string)
|
||||
imgTrustStore, err = imagetrust.NewCloudImageTrustStore(region, endpoint)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
imgTrustStore, err = imagetrust.NewLocalImageTrustStore(conf.Storage.RootDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
metaDB.SetImageTrustStore(imgTrustStore)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ func IsBuiltWithImageTrustExtension() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func SetupImageTrustRoutes(config *config.Config, router *mux.Router, log log.Logger) {
|
||||
func SetupImageTrustRoutes(config *config.Config, router *mux.Router, metaDB mTypes.MetaDB, log log.Logger) {
|
||||
log.Warn().Msg("skipping setting up image trust routes because given zot binary doesn't include this feature," +
|
||||
"please build a binary that does so")
|
||||
}
|
||||
|
@ -27,3 +27,10 @@ func EnableImageTrustVerification(config *config.Config, taskScheduler *schedule
|
|||
log.Warn().Msg("skipping adding to the scheduler a generator for updating signatures validity because " +
|
||||
"given binary doesn't include this feature, please build a binary that does so")
|
||||
}
|
||||
|
||||
func SetupImageTrustExtension(conf *config.Config, metaDB mTypes.MetaDB, log log.Logger) error {
|
||||
log.Warn().Msg("skipping setting up image trust because given zot binary doesn't include this feature," +
|
||||
"please build a binary that does so")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
guuid "github.com/gofrs/uuid"
|
||||
"github.com/sigstore/cosign/v2/cmd/cosign/cli/generate"
|
||||
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
|
||||
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
|
||||
|
@ -111,7 +112,49 @@ func TestSignaturesAllowedMethodsHeader(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestSignatureUploadAndVerification(t *testing.T) {
|
||||
func TestSignatureUploadAndVerificationLocal(t *testing.T) {
|
||||
Convey("test with local storage", t, func() {
|
||||
var cacheDriverParams map[string]interface{}
|
||||
|
||||
RunSignatureUploadAndVerificationTests(t, cacheDriverParams)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSignatureUploadAndVerificationAWS(t *testing.T) {
|
||||
skipIt(t)
|
||||
|
||||
Convey("test with AWS", t, func() {
|
||||
uuid, err := guuid.NewV4()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
cacheTablename := "BlobTable" + uuid.String()
|
||||
repoMetaTablename := "RepoMetadataTable" + uuid.String()
|
||||
manifestDataTablename := "ManifestDataTable" + uuid.String()
|
||||
versionTablename := "Version" + uuid.String()
|
||||
indexDataTablename := "IndexDataTable" + uuid.String()
|
||||
userDataTablename := "UserDataTable" + uuid.String()
|
||||
apiKeyTablename := "ApiKeyTable" + uuid.String()
|
||||
|
||||
cacheDriverParams := map[string]interface{}{
|
||||
"name": "dynamoDB",
|
||||
"endpoint": os.Getenv("DYNAMODBMOCK_ENDPOINT"),
|
||||
"region": "us-east-2",
|
||||
"cacheTablename": cacheTablename,
|
||||
"repoMetaTablename": repoMetaTablename,
|
||||
"manifestDataTablename": manifestDataTablename,
|
||||
"indexDataTablename": indexDataTablename,
|
||||
"userDataTablename": userDataTablename,
|
||||
"apiKeyTablename": apiKeyTablename,
|
||||
"versionTablename": versionTablename,
|
||||
}
|
||||
|
||||
t.Logf("using dynamo driver options: %v", cacheDriverParams)
|
||||
|
||||
RunSignatureUploadAndVerificationTests(t, cacheDriverParams)
|
||||
})
|
||||
}
|
||||
|
||||
func RunSignatureUploadAndVerificationTests(t *testing.T, cacheDriverParams map[string]interface{}) { //nolint: thelper
|
||||
repo := "repo"
|
||||
tag := "0.0.1"
|
||||
certName := "test"
|
||||
|
@ -128,12 +171,15 @@ func TestSignatureUploadAndVerification(t *testing.T) {
|
|||
}
|
||||
}`
|
||||
|
||||
Convey("Verify cosign public key upload without search or notation being enabled", t, func() {
|
||||
Convey("Verify cosign public key upload without search or notation being enabled", func() {
|
||||
globalDir := t.TempDir()
|
||||
port := test.GetFreePort()
|
||||
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
if cacheDriverParams != nil {
|
||||
conf.Storage.CacheDriver = cacheDriverParams
|
||||
}
|
||||
conf.Extensions = &extconf.ExtensionConfig{}
|
||||
conf.Extensions.Trust = &extconf.ImageTrustConfig{}
|
||||
conf.Extensions.Trust.Enable = &defaultValue
|
||||
|
@ -246,12 +292,15 @@ func TestSignatureUploadAndVerification(t *testing.T) {
|
|||
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
||||
})
|
||||
|
||||
Convey("Verify notation certificate upload without search or cosign being enabled", t, func() {
|
||||
Convey("Verify notation certificate upload without search or cosign being enabled", func() {
|
||||
globalDir := t.TempDir()
|
||||
port := test.GetFreePort()
|
||||
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
if cacheDriverParams != nil {
|
||||
conf.Storage.CacheDriver = cacheDriverParams
|
||||
}
|
||||
conf.Extensions = &extconf.ExtensionConfig{}
|
||||
conf.Extensions.Trust = &extconf.ImageTrustConfig{}
|
||||
conf.Extensions.Trust.Enable = &defaultValue
|
||||
|
@ -360,12 +409,15 @@ func TestSignatureUploadAndVerification(t *testing.T) {
|
|||
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
|
||||
})
|
||||
|
||||
Convey("Verify uploading notation certificates", t, func() {
|
||||
Convey("Verify uploading notation certificates", func() {
|
||||
globalDir := t.TempDir()
|
||||
port := test.GetFreePort()
|
||||
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
if cacheDriverParams != nil {
|
||||
conf.Storage.CacheDriver = cacheDriverParams
|
||||
}
|
||||
conf.Extensions = &extconf.ExtensionConfig{}
|
||||
conf.Extensions.Search = &extconf.SearchConfig{}
|
||||
conf.Extensions.Search.Enable = &defaultValue
|
||||
|
@ -533,12 +585,15 @@ func TestSignatureUploadAndVerification(t *testing.T) {
|
|||
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
|
||||
})
|
||||
|
||||
Convey("Verify uploading cosign public keys", t, func() {
|
||||
Convey("Verify uploading cosign public keys", func() {
|
||||
globalDir := t.TempDir()
|
||||
port := test.GetFreePort()
|
||||
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
if cacheDriverParams != nil {
|
||||
conf.Storage.CacheDriver = cacheDriverParams
|
||||
}
|
||||
conf.Extensions = &extconf.ExtensionConfig{}
|
||||
conf.Extensions.Search = &extconf.SearchConfig{}
|
||||
conf.Extensions.Search.Enable = &defaultValue
|
||||
|
@ -698,7 +753,7 @@ func TestSignatureUploadAndVerification(t *testing.T) {
|
|||
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
|
||||
})
|
||||
|
||||
Convey("Verify uploading cosign public keys with auth configured", t, func() {
|
||||
Convey("Verify uploading cosign public keys with auth configured", func() {
|
||||
globalDir := t.TempDir()
|
||||
port := test.GetFreePort()
|
||||
testCreds := test.GetCredString("admin", "admin") + "\n" + test.GetCredString("test", "test")
|
||||
|
@ -713,6 +768,9 @@ func TestSignatureUploadAndVerification(t *testing.T) {
|
|||
Actions: []string{},
|
||||
},
|
||||
}
|
||||
if cacheDriverParams != nil {
|
||||
conf.Storage.CacheDriver = cacheDriverParams
|
||||
}
|
||||
conf.Extensions = &extconf.ExtensionConfig{}
|
||||
conf.Extensions.Search = &extconf.SearchConfig{}
|
||||
conf.Extensions.Search.Enable = &defaultValue
|
||||
|
@ -788,12 +846,15 @@ func TestSignatureUploadAndVerification(t *testing.T) {
|
|||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
})
|
||||
|
||||
Convey("Verify signatures are read from the disk and updated in the DB when zot starts", t, func() {
|
||||
Convey("Verify signatures are read from the disk and updated in the DB when zot starts", func() {
|
||||
globalDir := t.TempDir()
|
||||
port := test.GetFreePort()
|
||||
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
if cacheDriverParams != nil {
|
||||
conf.Storage.CacheDriver = cacheDriverParams
|
||||
}
|
||||
conf.Extensions = &extconf.ExtensionConfig{}
|
||||
conf.Extensions.Search = &extconf.SearchConfig{}
|
||||
conf.Extensions.Search.Enable = &defaultValue
|
||||
|
@ -890,12 +951,15 @@ func TestSignatureUploadAndVerification(t *testing.T) {
|
|||
So(imgSummary.Manifests[0].SignatureInfo[0].Author, ShouldEqual, "")
|
||||
})
|
||||
|
||||
Convey("Verify failures when saving uploaded certificates and public keys", t, func() {
|
||||
Convey("Verify failures when saving uploaded certificates and public keys", func() {
|
||||
globalDir := t.TempDir()
|
||||
port := test.GetFreePort()
|
||||
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
if cacheDriverParams != nil {
|
||||
conf.Storage.CacheDriver = cacheDriverParams
|
||||
}
|
||||
conf.Extensions = &extconf.ExtensionConfig{}
|
||||
conf.Extensions.Search = &extconf.SearchConfig{}
|
||||
conf.Extensions.Search.Enable = &defaultValue
|
||||
|
@ -966,3 +1030,11 @@ func TestSignatureUploadAndVerification(t *testing.T) {
|
|||
So(resp.StatusCode(), ShouldEqual, http.StatusInternalServerError)
|
||||
})
|
||||
}
|
||||
|
||||
func skipIt(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
if os.Getenv("DYNAMODBMOCK_ENDPOINT") == "" {
|
||||
t.Skip("Skipping testing without AWS S3 mock server")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,3 +55,7 @@ func EnableScheduledTasks(conf *config.Config, taskScheduler *scheduler.Schedule
|
|||
) {
|
||||
EnableImageTrustVerification(conf, taskScheduler, metaDB, log)
|
||||
}
|
||||
|
||||
func SetupExtensions(conf *config.Config, metaDB mTypes.MetaDB, log log.Logger) error {
|
||||
return SetupImageTrustExtension(conf, metaDB, log)
|
||||
}
|
||||
|
|
|
@ -13,10 +13,14 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
|
||||
"github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
|
||||
"github.com/aws/aws-secretsmanager-caching-go/secretcache"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
"github.com/sigstore/cosign/v2/pkg/cosign/pkcs11key"
|
||||
sigs "github.com/sigstore/cosign/v2/pkg/signature"
|
||||
"github.com/sigstore/sigstore/pkg/cryptoutils"
|
||||
sigstoreSigs "github.com/sigstore/sigstore/pkg/signature"
|
||||
"github.com/sigstore/sigstore/pkg/signature/options"
|
||||
|
||||
zerr "zotregistry.io/zot/errors"
|
||||
|
@ -24,116 +28,242 @@ import (
|
|||
|
||||
const cosignDirRelativePath = "_cosign"
|
||||
|
||||
var cosignDir = "" //nolint:gochecknoglobals
|
||||
type PublicKeyLocalStorage struct {
|
||||
cosignDir string
|
||||
}
|
||||
|
||||
func InitCosignDir(rootDir string) error {
|
||||
type PublicKeyCloudStorage struct {
|
||||
secretsManagerClient *secretsmanager.Client
|
||||
secretsManagerCache *secretcache.Cache
|
||||
}
|
||||
|
||||
type publicKeyStorage interface {
|
||||
StorePublicKey(name godigest.Digest, publicKeyContent []byte) error
|
||||
GetPublicKeyVerifier(name string) (sigstoreSigs.Verifier, []byte, error)
|
||||
GetPublicKeys() ([]string, error)
|
||||
}
|
||||
|
||||
func NewPublicKeyLocalStorage(rootDir string) (*PublicKeyLocalStorage, error) {
|
||||
dir := path.Join(rootDir, cosignDirRelativePath)
|
||||
|
||||
_, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, defaultDirPerms)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
cosignDir = dir
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return err
|
||||
return &PublicKeyLocalStorage{
|
||||
cosignDir: dir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetCosignDirPath() (string, error) {
|
||||
if cosignDir != "" {
|
||||
return cosignDir, nil
|
||||
func NewPublicKeyCloudStorage(
|
||||
secretsManagerClient *secretsmanager.Client, secretsManagerCache *secretcache.Cache,
|
||||
) *PublicKeyCloudStorage {
|
||||
return &PublicKeyCloudStorage{
|
||||
secretsManagerClient: secretsManagerClient,
|
||||
secretsManagerCache: secretsManagerCache,
|
||||
}
|
||||
}
|
||||
|
||||
func (local *PublicKeyLocalStorage) GetCosignDirPath() (string, error) {
|
||||
if local.cosignDir != "" {
|
||||
return local.cosignDir, nil
|
||||
}
|
||||
|
||||
return "", zerr.ErrSignConfigDirNotSet
|
||||
}
|
||||
|
||||
func VerifyCosignSignature(
|
||||
repo string, digest godigest.Digest, signatureKey string, layerContent []byte,
|
||||
cosignStorage publicKeyStorage, repo string, digest godigest.Digest, signatureKey string, layerContent []byte,
|
||||
) (string, bool, error) {
|
||||
cosignDir, err := GetCosignDirPath()
|
||||
publicKeys, err := cosignStorage.GetPublicKeys()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
|
||||
files, err := os.ReadDir(cosignDir)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
for _, publicKey := range publicKeys {
|
||||
// cosign verify the image
|
||||
pubKeyVerifier, pubKeyContent, err := cosignStorage.GetPublicKeyVerifier(publicKey)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsDir() {
|
||||
// cosign verify the image
|
||||
ctx := context.Background()
|
||||
keyRef := path.Join(cosignDir, file.Name())
|
||||
hashAlgorithm := crypto.SHA256
|
||||
pkcs11Key, ok := pubKeyVerifier.(*pkcs11key.Key)
|
||||
if ok {
|
||||
defer pkcs11Key.Close()
|
||||
}
|
||||
|
||||
pubKey, err := sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, hashAlgorithm)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
verifier := pubKeyVerifier
|
||||
|
||||
pkcs11Key, ok := pubKey.(*pkcs11key.Key)
|
||||
if ok {
|
||||
defer pkcs11Key.Close()
|
||||
}
|
||||
b64sig := signatureKey
|
||||
|
||||
verifier := pubKey
|
||||
signature, err := base64.StdEncoding.DecodeString(b64sig)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
b64sig := signatureKey
|
||||
compressed := io.NopCloser(bytes.NewReader(layerContent))
|
||||
|
||||
signature, err := base64.StdEncoding.DecodeString(b64sig)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
payload, err := io.ReadAll(compressed)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
compressed := io.NopCloser(bytes.NewReader(layerContent))
|
||||
err = verifier.VerifySignature(bytes.NewReader(signature), bytes.NewReader(payload),
|
||||
options.WithContext(context.Background()))
|
||||
|
||||
payload, err := io.ReadAll(compressed)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err = verifier.VerifySignature(bytes.NewReader(signature), bytes.NewReader(payload), options.WithContext(ctx))
|
||||
|
||||
if err == nil {
|
||||
publicKey, err := os.ReadFile(keyRef)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return string(publicKey), true, nil
|
||||
}
|
||||
if err == nil {
|
||||
return string(pubKeyContent), true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
func UploadPublicKey(publicKeyContent []byte) error {
|
||||
func (local *PublicKeyLocalStorage) GetPublicKeyVerifier(fileName string) (sigstoreSigs.Verifier, []byte, error) {
|
||||
cosignDir, err := local.GetCosignDirPath()
|
||||
if err != nil {
|
||||
return nil, []byte{}, err
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
keyRef := path.Join(cosignDir, fileName)
|
||||
hashAlgorithm := crypto.SHA256
|
||||
|
||||
pubKeyContent, err := os.ReadFile(keyRef)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pubKey, err := sigs.PublicKeyFromKeyRefWithHashAlgo(ctx, keyRef, hashAlgorithm)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pubKey, pubKeyContent, nil
|
||||
}
|
||||
|
||||
func (cloud *PublicKeyCloudStorage) GetPublicKeyVerifier(secretName string) (sigstoreSigs.Verifier, []byte, error) {
|
||||
hashAlgorithm := crypto.SHA256
|
||||
|
||||
// get key
|
||||
raw, err := cloud.secretsManagerCache.GetSecretString(secretName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rawDecoded, err := base64.StdEncoding.DecodeString(raw)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// PEM encoded file.
|
||||
key, err := cryptoutils.UnmarshalPEMToPublicKey(rawDecoded)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pubKey, err := sigstoreSigs.LoadVerifier(key, hashAlgorithm)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return pubKey, rawDecoded, nil
|
||||
}
|
||||
|
||||
func (local *PublicKeyLocalStorage) GetPublicKeys() ([]string, error) {
|
||||
cosignDir, err := local.GetCosignDirPath()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
files, err := os.ReadDir(cosignDir)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
publicKeys := []string{}
|
||||
for _, file := range files {
|
||||
publicKeys = append(publicKeys, file.Name())
|
||||
}
|
||||
|
||||
return publicKeys, nil
|
||||
}
|
||||
|
||||
func (cloud *PublicKeyCloudStorage) GetPublicKeys() ([]string, error) {
|
||||
ctx := context.Background()
|
||||
listSecretsInput := secretsmanager.ListSecretsInput{
|
||||
Filters: []types.Filter{
|
||||
{
|
||||
Key: types.FilterNameStringTypeDescription,
|
||||
Values: []string{"cosign public key"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secrets, err := cloud.secretsManagerClient.ListSecrets(ctx, &listSecretsInput)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
publicKeys := []string{}
|
||||
|
||||
for _, secret := range secrets.SecretList {
|
||||
publicKeys = append(publicKeys, *(secret.Name))
|
||||
}
|
||||
|
||||
return publicKeys, nil
|
||||
}
|
||||
|
||||
func UploadPublicKey(cosignStorage publicKeyStorage, publicKeyContent []byte) error {
|
||||
// validate public key
|
||||
if ok, err := validatePublicKey(publicKeyContent); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
name := godigest.FromBytes(publicKeyContent)
|
||||
|
||||
return cosignStorage.StorePublicKey(name, publicKeyContent)
|
||||
}
|
||||
|
||||
func (local *PublicKeyLocalStorage) StorePublicKey(name godigest.Digest, publicKeyContent []byte) error {
|
||||
// add public key to "{rootDir}/_cosign/{name.pub}"
|
||||
configDir, err := GetCosignDirPath()
|
||||
cosignDir, err := local.GetCosignDirPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := godigest.FromBytes(publicKeyContent)
|
||||
|
||||
// store public key
|
||||
publicKeyPath := path.Join(configDir, name.String())
|
||||
publicKeyPath := path.Join(cosignDir, name.String())
|
||||
|
||||
return os.WriteFile(publicKeyPath, publicKeyContent, defaultFilePerms)
|
||||
}
|
||||
|
||||
func (cloud *PublicKeyCloudStorage) StorePublicKey(name godigest.Digest, publicKeyContent []byte) error {
|
||||
n := name.Encoded()
|
||||
description := "cosign public key"
|
||||
secret := base64.StdEncoding.EncodeToString(publicKeyContent)
|
||||
secretInputParam := &secretsmanager.CreateSecretInput{
|
||||
Name: &n,
|
||||
Description: &description,
|
||||
SecretString: &secret,
|
||||
}
|
||||
|
||||
_, err := cloud.secretsManagerClient.CreateSecret(context.Background(), secretInputParam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validatePublicKey(publicKeyContent []byte) (bool, error) {
|
||||
_, err := cryptoutils.UnmarshalPEMToPublicKey(publicKeyContent)
|
||||
if err != nil {
|
||||
|
|
|
@ -8,6 +8,14 @@ import (
|
|||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
|
||||
aws1 "github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
smanager "github.com/aws/aws-sdk-go/service/secretsmanager"
|
||||
"github.com/aws/aws-secretsmanager-caching-go/secretcache"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
||||
|
@ -23,18 +31,106 @@ const (
|
|||
defaultFilePerms = 0o644
|
||||
)
|
||||
|
||||
func InitCosignAndNotationDirs(rootDir string) error {
|
||||
err := InitCosignDir(rootDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = InitNotationDir(rootDir)
|
||||
|
||||
return err
|
||||
type ImageTrustStore struct {
|
||||
CosignStorage publicKeyStorage
|
||||
NotationStorage certificateStorage
|
||||
}
|
||||
|
||||
func VerifySignature(
|
||||
func NewLocalImageTrustStore(rootDir string) (*ImageTrustStore, error) {
|
||||
publicKeyStorage, err := NewPublicKeyLocalStorage(rootDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certStorage, err := NewCertificateLocalStorage(rootDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ImageTrustStore{
|
||||
CosignStorage: publicKeyStorage,
|
||||
NotationStorage: certStorage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewCloudImageTrustStore(region, endpoint string) (*ImageTrustStore, error) {
|
||||
secretsManagerClient, err := GetSecretsManagerClient(region, endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secretsManagerCache, err := GetSecretsManagerRetrieval(region, endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKeyStorage := NewPublicKeyCloudStorage(secretsManagerClient, secretsManagerCache)
|
||||
|
||||
certStorage, err := NewCertificateCloudStorage(secretsManagerClient, secretsManagerCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ImageTrustStore{
|
||||
CosignStorage: publicKeyStorage,
|
||||
NotationStorage: certStorage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetSecretsManagerClient(region, endpoint string) (*secretsmanager.Client, error) {
|
||||
customResolver := aws.EndpointResolverWithOptionsFunc(
|
||||
func(service, region string, options ...interface{}) (aws.Endpoint, error) {
|
||||
return aws.Endpoint{
|
||||
PartitionID: "aws",
|
||||
URL: endpoint,
|
||||
SigningRegion: region,
|
||||
}, nil
|
||||
})
|
||||
|
||||
// Using the SDK's default configuration, loading additional config
|
||||
// and credentials values from the environment variables, shared
|
||||
// credentials, and shared configuration files
|
||||
cfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(region),
|
||||
config.WithEndpointResolverWithOptions(customResolver))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return secretsmanager.NewFromConfig(cfg), nil
|
||||
}
|
||||
|
||||
func GetSecretsManagerRetrieval(region, endpoint string) (*secretcache.Cache, error) {
|
||||
endpointFunc := func(service, region string, optFns ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
|
||||
return endpoints.ResolvedEndpoint{
|
||||
PartitionID: "aws",
|
||||
URL: endpoint,
|
||||
SigningRegion: region,
|
||||
}, nil
|
||||
}
|
||||
customResolver := endpoints.ResolverFunc(endpointFunc)
|
||||
|
||||
cfg := aws1.NewConfig().WithRegion(region).WithEndpointResolver(customResolver)
|
||||
|
||||
newSession := session.Must(session.NewSession())
|
||||
|
||||
client := smanager.New(newSession, cfg)
|
||||
// Create a custom CacheConfig struct
|
||||
config := secretcache.CacheConfig{
|
||||
MaxCacheSize: secretcache.DefaultMaxCacheSize,
|
||||
VersionStage: secretcache.DefaultVersionStage,
|
||||
CacheItemTTL: secretcache.DefaultCacheItemTTL,
|
||||
}
|
||||
|
||||
// Instantiate the cache
|
||||
cache, _ := secretcache.New(
|
||||
func(c *secretcache.Cache) { c.CacheConfig = config },
|
||||
func(c *secretcache.Cache) { c.Client = client },
|
||||
)
|
||||
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func (imgTrustStore *ImageTrustStore) VerifySignature(
|
||||
signatureType string, rawSignature []byte, sigKey string, manifestDigest godigest.Digest, manifestContent []byte,
|
||||
repo string,
|
||||
) (string, time.Time, bool, error) {
|
||||
|
@ -55,11 +151,11 @@ func VerifySignature(
|
|||
|
||||
switch signatureType {
|
||||
case zcommon.CosignSignature:
|
||||
author, isValid, err := VerifyCosignSignature(repo, manifestDigest, sigKey, rawSignature)
|
||||
author, isValid, err := VerifyCosignSignature(imgTrustStore.CosignStorage, repo, manifestDigest, sigKey, rawSignature)
|
||||
|
||||
return author, time.Time{}, isValid, err
|
||||
case zcommon.NotationSignature:
|
||||
return VerifyNotationSignature(desc, manifestDigest.String(), rawSignature, sigKey)
|
||||
return VerifyNotationSignature(imgTrustStore.NotationStorage, desc, manifestDigest.String(), rawSignature, sigKey)
|
||||
default:
|
||||
return "", time.Time{}, false, zerr.ErrInvalidSignatureType
|
||||
}
|
||||
|
|
|
@ -9,19 +9,17 @@ import (
|
|||
godigest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
func InitCosignAndNotationDirs(rootDir string) error {
|
||||
return nil
|
||||
func NewLocalImageTrustStore(dir string) (*imageTrustDisabled, error) {
|
||||
return &imageTrustDisabled{}, nil
|
||||
}
|
||||
|
||||
func InitCosignDir(rootDir string) error {
|
||||
return nil
|
||||
func NewCloudImageTrustStore(region, endpoint string) (*imageTrustDisabled, error) {
|
||||
return &imageTrustDisabled{}, nil
|
||||
}
|
||||
|
||||
func InitNotationDir(rootDir string) error {
|
||||
return nil
|
||||
}
|
||||
type imageTrustDisabled struct{}
|
||||
|
||||
func VerifySignature(
|
||||
func (imgTrustStore *imageTrustDisabled) VerifySignature(
|
||||
signatureType string, rawSignature []byte, sigKey string, manifestDigest godigest.Digest, manifestContent []byte,
|
||||
repo string,
|
||||
) (string, time.Time, bool, error) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
package imagetrust_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
@ -10,35 +11,56 @@ import (
|
|||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"zotregistry.io/zot/pkg/extensions/imagetrust"
|
||||
"zotregistry.io/zot/pkg/test"
|
||||
)
|
||||
|
||||
func TestImageTrust(t *testing.T) {
|
||||
Convey("binary doesn't include imagetrust", t, func() {
|
||||
rootDir := t.TempDir()
|
||||
|
||||
err := imagetrust.InitCosignDir(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
cosignDir := path.Join(rootDir, "_cosign")
|
||||
_, err = os.Stat(cosignDir)
|
||||
_, err := os.Stat(cosignDir)
|
||||
So(os.IsNotExist(err), ShouldBeTrue)
|
||||
|
||||
err = imagetrust.InitNotationDir(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
notationDir := path.Join(rootDir, "_notation")
|
||||
_, err = os.Stat(notationDir)
|
||||
So(os.IsNotExist(err), ShouldBeTrue)
|
||||
|
||||
err = imagetrust.InitCosignAndNotationDirs(rootDir)
|
||||
repo := "repo"
|
||||
|
||||
image, err := test.GetRandomImage() //nolint:staticcheck
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifestContent, err := json.Marshal(image.Manifest)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifestDigest := image.Digest()
|
||||
|
||||
localImgTrustStore, err := imagetrust.NewLocalImageTrustStore(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
author, expTime, ok, err := localImgTrustStore.VerifySignature("cosign",
|
||||
[]byte(""), "", manifestDigest, manifestContent, repo,
|
||||
)
|
||||
So(author, ShouldBeEmpty)
|
||||
So(expTime, ShouldBeZeroValue)
|
||||
So(ok, ShouldBeFalse)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = os.Stat(cosignDir)
|
||||
So(os.IsNotExist(err), ShouldBeTrue)
|
||||
|
||||
_, err = os.Stat(notationDir)
|
||||
So(os.IsNotExist(err), ShouldBeTrue)
|
||||
|
||||
author, expTime, ok, err := imagetrust.VerifySignature("", []byte{}, "", "", []byte{}, "")
|
||||
cloudImgTrustStore, err := imagetrust.NewCloudImageTrustStore("region",
|
||||
"endpoint",
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
author, expTime, ok, err = cloudImgTrustStore.VerifySignature("cosign",
|
||||
[]byte(""), "", manifestDigest, manifestContent, repo,
|
||||
)
|
||||
So(author, ShouldBeEmpty)
|
||||
So(expTime, ShouldBeZeroValue)
|
||||
So(ok, ShouldBeFalse)
|
||||
|
|
|
@ -8,23 +8,30 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
guuid "github.com/gofrs/uuid"
|
||||
"github.com/notaryproject/notation-go"
|
||||
notreg "github.com/notaryproject/notation-go/registry"
|
||||
"github.com/notaryproject/notation-go/verifier/trustpolicy"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sigstore/cosign/v2/cmd/cosign/cli/generate"
|
||||
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
|
||||
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"gopkg.in/resty.v1"
|
||||
|
||||
zerr "zotregistry.io/zot/errors"
|
||||
"zotregistry.io/zot/pkg/api"
|
||||
"zotregistry.io/zot/pkg/api/config"
|
||||
"zotregistry.io/zot/pkg/api/constants"
|
||||
zcommon "zotregistry.io/zot/pkg/common"
|
||||
extconf "zotregistry.io/zot/pkg/extensions/config"
|
||||
"zotregistry.io/zot/pkg/extensions/imagetrust"
|
||||
"zotregistry.io/zot/pkg/test"
|
||||
)
|
||||
|
@ -37,16 +44,17 @@ func TestInitCosignAndNotationDirs(t *testing.T) {
|
|||
err := os.Chmod(dir, 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.InitCosignAndNotationDirs(dir)
|
||||
_, err = imagetrust.NewPublicKeyLocalStorage(dir)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(dir, 0o500)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.InitCosignAndNotationDirs(dir)
|
||||
_, err = imagetrust.NewPublicKeyLocalStorage(dir)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
cosignDir, err := imagetrust.GetCosignDirPath()
|
||||
pubKeyStorage := &imagetrust.PublicKeyLocalStorage{}
|
||||
cosignDir, err := pubKeyStorage.GetCosignDirPath()
|
||||
So(cosignDir, ShouldBeEmpty)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
|
||||
|
@ -57,22 +65,23 @@ func TestInitCosignAndNotationDirs(t *testing.T) {
|
|||
err := os.Chmod(dir, 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.InitCosignAndNotationDirs(dir)
|
||||
_, err = imagetrust.NewPublicKeyLocalStorage(dir)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.InitNotationDir(dir)
|
||||
_, err = imagetrust.NewCertificateLocalStorage(dir)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(dir, 0o500)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.InitCosignAndNotationDirs(dir)
|
||||
_, err = imagetrust.NewPublicKeyLocalStorage(dir)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.InitNotationDir(dir)
|
||||
_, err = imagetrust.NewCertificateLocalStorage(dir)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
notationDir, err := imagetrust.GetNotationDirPath()
|
||||
certStorage := &imagetrust.CertificateLocalStorage{}
|
||||
notationDir, err := certStorage.GetNotationDirPath()
|
||||
So(notationDir, ShouldBeEmpty)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
|
||||
|
@ -94,7 +103,8 @@ func TestInitCosignAndNotationDirs(t *testing.T) {
|
|||
So(err, ShouldBeNil)
|
||||
So(certificateContent, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.UploadCertificate(certificateContent, "ca", "notation-upload-test")
|
||||
certStorgae := &imagetrust.CertificateLocalStorage{}
|
||||
err = imagetrust.UploadCertificate(certStorgae, certificateContent, "ca", "notation-upload-test")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
|
||||
})
|
||||
|
@ -118,7 +128,8 @@ func TestInitCosignAndNotationDirs(t *testing.T) {
|
|||
So(err, ShouldBeNil)
|
||||
So(publicKeyContent, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.UploadPublicKey(publicKeyContent)
|
||||
pubKeyStorage := &imagetrust.PublicKeyLocalStorage{}
|
||||
err = imagetrust.UploadPublicKey(pubKeyStorage, publicKeyContent)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
|
||||
})
|
||||
|
@ -128,7 +139,8 @@ func TestVerifySignatures(t *testing.T) {
|
|||
Convey("wrong manifest content", t, func() {
|
||||
manifestContent := []byte("wrong json")
|
||||
|
||||
_, _, _, err := imagetrust.VerifySignature("", []byte(""), "", "", manifestContent, "repo")
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{}
|
||||
_, _, _, err := imgTrustStore.VerifySignature("", []byte(""), "", "", manifestContent, "repo")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
|
@ -139,7 +151,8 @@ func TestVerifySignatures(t *testing.T) {
|
|||
manifestContent, err := json.Marshal(image.Manifest)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, _, err = imagetrust.VerifySignature("", []byte(""), "", "", manifestContent, "repo")
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{}
|
||||
_, _, _, err = imgTrustStore.VerifySignature("", []byte(""), "", "", manifestContent, "repo")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrBadManifestDigest)
|
||||
})
|
||||
|
@ -153,14 +166,15 @@ func TestVerifySignatures(t *testing.T) {
|
|||
|
||||
manifestDigest := image.Digest()
|
||||
|
||||
_, _, _, err = imagetrust.VerifySignature("wrongType", []byte(""), "", manifestDigest, manifestContent, "repo")
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{}
|
||||
_, _, _, err = imgTrustStore.VerifySignature("wrongType", []byte(""), "", manifestDigest, manifestContent, "repo")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrInvalidSignatureType)
|
||||
})
|
||||
|
||||
Convey("verify cosign signature", t, func() {
|
||||
repo := "repo"
|
||||
tag := "test"
|
||||
repo := "repo" //nolint:goconst
|
||||
tag := "test" //nolint:goconst
|
||||
image, err := test.GetRandomImage() //nolint:staticcheck
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
|
@ -170,7 +184,11 @@ func TestVerifySignatures(t *testing.T) {
|
|||
manifestDigest := image.Digest()
|
||||
|
||||
Convey("cosignDir is not set", func() {
|
||||
_, _, _, err = imagetrust.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{
|
||||
CosignStorage: &imagetrust.PublicKeyLocalStorage{},
|
||||
}
|
||||
|
||||
_, _, _, err = imgTrustStore.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
|
||||
})
|
||||
|
@ -178,31 +196,40 @@ func TestVerifySignatures(t *testing.T) {
|
|||
Convey("cosignDir does not have read permissions", func() {
|
||||
dir := t.TempDir()
|
||||
|
||||
err := imagetrust.InitCosignDir(dir)
|
||||
pubKeyStorage, err := imagetrust.NewPublicKeyLocalStorage(dir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
cosignDir, err := imagetrust.GetCosignDirPath()
|
||||
cosignDir, err := pubKeyStorage.GetCosignDirPath()
|
||||
So(err, ShouldBeNil)
|
||||
err = os.Chmod(cosignDir, 0o300)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, _, err = imagetrust.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{
|
||||
CosignStorage: pubKeyStorage,
|
||||
}
|
||||
|
||||
_, _, _, err = imgTrustStore.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("no valid public key", func() {
|
||||
dir := t.TempDir()
|
||||
|
||||
err := imagetrust.InitCosignDir(dir)
|
||||
pubKeyStorage, err := imagetrust.NewPublicKeyLocalStorage(dir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
cosignDir, err := imagetrust.GetCosignDirPath()
|
||||
cosignDir, err := pubKeyStorage.GetCosignDirPath()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.WriteFileWithPermission(path.Join(cosignDir, "file"), []byte("not a public key"), 0o600, false)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, isTrusted, err := imagetrust.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{
|
||||
CosignStorage: pubKeyStorage,
|
||||
}
|
||||
|
||||
_, _, isTrusted, err := imgTrustStore.VerifySignature("cosign", []byte(""), "", manifestDigest,
|
||||
manifestContent, repo)
|
||||
So(err, ShouldBeNil)
|
||||
So(isTrusted, ShouldBeFalse)
|
||||
})
|
||||
|
@ -225,10 +252,10 @@ func TestVerifySignatures(t *testing.T) {
|
|||
err := test.UploadImage(image, baseURL, repo, tag)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.InitCosignDir(rootDir)
|
||||
pubKeyStorage, err := imagetrust.NewPublicKeyLocalStorage(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
cosignDir, err := imagetrust.GetCosignDirPath()
|
||||
cosignDir, err := pubKeyStorage.GetCosignDirPath()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
|
@ -284,8 +311,12 @@ func TestVerifySignatures(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{
|
||||
CosignStorage: pubKeyStorage,
|
||||
}
|
||||
|
||||
// signature is trusted
|
||||
author, _, isTrusted, err := imagetrust.VerifySignature("cosign", rawSignature, sigKey, manifestDigest,
|
||||
author, _, isTrusted, err := imgTrustStore.VerifySignature("cosign", rawSignature, sigKey, manifestDigest,
|
||||
manifestContent, repo)
|
||||
So(err, ShouldBeNil)
|
||||
So(isTrusted, ShouldBeTrue)
|
||||
|
@ -294,8 +325,8 @@ func TestVerifySignatures(t *testing.T) {
|
|||
})
|
||||
|
||||
Convey("verify notation signature", t, func() {
|
||||
repo := "repo"
|
||||
tag := "test"
|
||||
repo := "repo" //nolint:goconst
|
||||
tag := "test" //nolint:goconst
|
||||
image, err := test.GetRandomImage() //nolint:staticcheck
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
|
@ -305,7 +336,12 @@ func TestVerifySignatures(t *testing.T) {
|
|||
manifestDigest := image.Digest()
|
||||
|
||||
Convey("notationDir is not set", func() {
|
||||
_, _, _, err = imagetrust.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent, repo)
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{
|
||||
NotationStorage: &imagetrust.CertificateLocalStorage{},
|
||||
}
|
||||
|
||||
_, _, _, err = imgTrustStore.VerifySignature("notation", []byte("signature"), "", manifestDigest,
|
||||
manifestContent, repo)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
|
||||
})
|
||||
|
@ -313,10 +349,15 @@ func TestVerifySignatures(t *testing.T) {
|
|||
Convey("no signature provided", func() {
|
||||
dir := t.TempDir()
|
||||
|
||||
err := imagetrust.InitNotationDir(dir)
|
||||
certStorage, err := imagetrust.NewCertificateLocalStorage(dir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, isTrusted, err := imagetrust.VerifySignature("notation", []byte(""), "", manifestDigest, manifestContent, repo)
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{
|
||||
NotationStorage: certStorage,
|
||||
}
|
||||
|
||||
_, _, isTrusted, err := imgTrustStore.VerifySignature("notation", []byte(""), "", manifestDigest,
|
||||
manifestContent, repo)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(isTrusted, ShouldBeFalse)
|
||||
})
|
||||
|
@ -324,32 +365,41 @@ func TestVerifySignatures(t *testing.T) {
|
|||
Convey("trustpolicy.json does not exist", func() {
|
||||
dir := t.TempDir()
|
||||
|
||||
err := imagetrust.InitNotationDir(dir)
|
||||
certStorage, err := imagetrust.NewCertificateLocalStorage(dir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
notationDir, _ := imagetrust.GetNotationDirPath()
|
||||
notationDir, _ := certStorage.GetNotationDirPath()
|
||||
|
||||
err = os.Remove(path.Join(notationDir, "trustpolicy.json"))
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, _, err = imagetrust.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent, repo)
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{
|
||||
NotationStorage: certStorage,
|
||||
}
|
||||
|
||||
_, _, _, err = imgTrustStore.VerifySignature("notation", []byte("signature"), "", manifestDigest,
|
||||
manifestContent, repo)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("trustpolicy.json has invalid content", func() {
|
||||
dir := t.TempDir()
|
||||
|
||||
err := imagetrust.InitNotationDir(dir)
|
||||
certStorage, err := imagetrust.NewCertificateLocalStorage(dir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
notationDir, err := imagetrust.GetNotationDirPath()
|
||||
notationDir, err := certStorage.GetNotationDirPath()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.WriteFileWithPermission(path.Join(notationDir, "trustpolicy.json"), []byte("invalid content"),
|
||||
0o600, true)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, _, _, err = imagetrust.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent,
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{
|
||||
NotationStorage: certStorage,
|
||||
}
|
||||
|
||||
_, _, _, err = imgTrustStore.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent,
|
||||
repo)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
@ -372,10 +422,10 @@ func TestVerifySignatures(t *testing.T) {
|
|||
err := test.UploadImage(image, baseURL, repo, tag)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.InitNotationDir(rootDir)
|
||||
certStorage, err := imagetrust.NewCertificateLocalStorage(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
notationDir, err := imagetrust.GetNotationDirPath()
|
||||
notationDir, err := certStorage.GetNotationDirPath()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
|
@ -447,8 +497,12 @@ func TestVerifySignatures(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
imgTrustStore := &imagetrust.ImageTrustStore{
|
||||
NotationStorage: certStorage,
|
||||
}
|
||||
|
||||
// signature is trusted
|
||||
author, _, isTrusted, err := imagetrust.VerifySignature("notation", rawSignature, sigKey, manifestDigest,
|
||||
author, _, isTrusted, err := imgTrustStore.VerifySignature("notation", rawSignature, sigKey, manifestDigest,
|
||||
manifestContent, repo)
|
||||
So(err, ShouldBeNil)
|
||||
So(isTrusted, ShouldBeTrue)
|
||||
|
@ -458,7 +512,7 @@ func TestVerifySignatures(t *testing.T) {
|
|||
So(err, ShouldBeNil)
|
||||
|
||||
// signature is not trusted
|
||||
author, _, isTrusted, err = imagetrust.VerifySignature("notation", rawSignature, sigKey, manifestDigest,
|
||||
author, _, isTrusted, err = imgTrustStore.VerifySignature("notation", rawSignature, sigKey, manifestDigest,
|
||||
manifestContent, repo)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(isTrusted, ShouldBeFalse)
|
||||
|
@ -492,69 +546,7 @@ func TestCheckExpiryErr(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestUploadPublicKey(t *testing.T) {
|
||||
Convey("public key - invalid content", t, func() {
|
||||
err := imagetrust.UploadPublicKey([]byte("wrong content"))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("upload public key successfully", t, func() {
|
||||
rootDir := t.TempDir()
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_ = os.Chdir(rootDir)
|
||||
|
||||
// generate a keypair
|
||||
os.Setenv("COSIGN_PASSWORD", "")
|
||||
err = generate.GenerateKeyPairCmd(context.TODO(), "", "cosign", nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_ = os.Chdir(cwd)
|
||||
|
||||
publicKeyContent, err := os.ReadFile(path.Join(rootDir, "cosign.pub"))
|
||||
So(err, ShouldBeNil)
|
||||
So(publicKeyContent, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.InitCosignDir(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.UploadPublicKey(publicKeyContent)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestUploadCertificate(t *testing.T) {
|
||||
Convey("invalid truststore type", t, func() {
|
||||
err := imagetrust.UploadCertificate([]byte("certificate content"), "wrongType", "store")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrInvalidTruststoreType)
|
||||
})
|
||||
|
||||
Convey("invalid truststore name", t, func() {
|
||||
err := imagetrust.UploadCertificate([]byte("certificate content"), "ca", "*store?")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrInvalidTruststoreName)
|
||||
})
|
||||
|
||||
Convey("invalid certificate content", t, func() {
|
||||
err := imagetrust.UploadCertificate([]byte("invalid content"), "ca", "store")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
content := `-----BEGIN CERTIFICATE-----
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
err = imagetrust.UploadCertificate([]byte(content), "ca", "store")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
content = ``
|
||||
|
||||
err = imagetrust.UploadCertificate([]byte(content), "ca", "store")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
func TestLocalTrustStoreUploadErr(t *testing.T) {
|
||||
Convey("truststore dir can not be created", t, func() {
|
||||
rootDir := t.TempDir()
|
||||
|
||||
|
@ -571,16 +563,16 @@ func TestUploadCertificate(t *testing.T) {
|
|||
So(err, ShouldBeNil)
|
||||
So(certificateContent, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.InitNotationDir(rootDir)
|
||||
certStorage, err := imagetrust.NewCertificateLocalStorage(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
notationDir, err := imagetrust.GetNotationDirPath()
|
||||
notationDir, err := certStorage.GetNotationDirPath()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.Chmod(notationDir, 0o100)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.UploadCertificate(certificateContent, "ca", "notation-upload-test")
|
||||
err = imagetrust.UploadCertificate(certStorage, certificateContent, "ca", "notation-upload-test")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(notationDir, 0o777)
|
||||
|
@ -603,10 +595,10 @@ func TestUploadCertificate(t *testing.T) {
|
|||
So(err, ShouldBeNil)
|
||||
So(certificateContent, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.InitNotationDir(rootDir)
|
||||
certStorage, err := imagetrust.NewCertificateLocalStorage(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
notationDir, err := imagetrust.GetNotationDirPath()
|
||||
notationDir, err := certStorage.GetNotationDirPath()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.MkdirAll(path.Join(notationDir, "truststore/x509/ca/notation-upload-test"), 0o777)
|
||||
|
@ -615,7 +607,7 @@ func TestUploadCertificate(t *testing.T) {
|
|||
err = os.Chmod(path.Join(notationDir, "truststore/x509/ca/notation-upload-test"), 0o100)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.UploadCertificate(certificateContent, "ca", "notation-upload-test")
|
||||
err = imagetrust.UploadCertificate(certStorage, certificateContent, "ca", "notation-upload-test")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
|
@ -635,17 +627,17 @@ func TestUploadCertificate(t *testing.T) {
|
|||
So(err, ShouldBeNil)
|
||||
So(certificateContent, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.InitNotationDir(rootDir)
|
||||
certStorage, err := imagetrust.NewCertificateLocalStorage(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
notationDir, err := imagetrust.GetNotationDirPath()
|
||||
notationDir, err := certStorage.GetNotationDirPath()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.WriteFileWithPermission(path.Join(notationDir, "trustpolicy.json"), []byte("invalid content"),
|
||||
0o600, true)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.UploadCertificate(certificateContent, "ca", "notation-upload-test")
|
||||
err = imagetrust.UploadCertificate(certStorage, certificateContent, "ca", "notation-upload-test")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
|
@ -665,13 +657,13 @@ func TestUploadCertificate(t *testing.T) {
|
|||
So(err, ShouldBeNil)
|
||||
So(certificateContent, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.InitNotationDir(rootDir)
|
||||
certStorage, err := imagetrust.NewCertificateLocalStorage(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
notationDir, err := imagetrust.GetNotationDirPath()
|
||||
notationDir, err := certStorage.GetNotationDirPath()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
trustpolicyDoc, err := imagetrust.LoadTrustPolicyDocument(notationDir)
|
||||
trustpolicyDoc, err := certStorage.LoadTrustPolicyDocument()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
trustpolicyDoc.TrustPolicies[0].TrustStores = append(trustpolicyDoc.TrustPolicies[0].TrustStores,
|
||||
|
@ -683,30 +675,382 @@ func TestUploadCertificate(t *testing.T) {
|
|||
err = os.WriteFile(path.Join(notationDir, "trustpolicy.json"), trustpolicyDocContent, 0o400)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.UploadCertificate(certificateContent, "ca", "notation-upload-test")
|
||||
err = imagetrust.UploadCertificate(certStorage, certificateContent, "ca", "notation-upload-test")
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLocalTrustStore(t *testing.T) {
|
||||
Convey("test with local storage", t, func() {
|
||||
rootDir := t.TempDir()
|
||||
|
||||
imageTrustStore, err := imagetrust.NewLocalImageTrustStore(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
var dbDriverParams map[string]interface{}
|
||||
|
||||
RunUploadTests(t, *imageTrustStore)
|
||||
RunVerificationTests(t, dbDriverParams)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAWSTrustStore(t *testing.T) {
|
||||
skipIt(t)
|
||||
|
||||
Convey("test with AWS storage", t, func() {
|
||||
uuid, err := guuid.NewV4()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
repoMetaTablename := "RepoMetadataTable" + uuid.String()
|
||||
manifestDataTablename := "ManifestDataTable" + uuid.String()
|
||||
versionTablename := "Version" + uuid.String()
|
||||
indexDataTablename := "IndexDataTable" + uuid.String()
|
||||
userDataTablename := "UserDataTable" + uuid.String()
|
||||
apiKeyTablename := "ApiKeyTable" + uuid.String()
|
||||
|
||||
dynamoDBDriverParams := map[string]interface{}{
|
||||
"name": "dynamoDB",
|
||||
"endpoint": os.Getenv("DYNAMODBMOCK_ENDPOINT"),
|
||||
"region": "us-east-2",
|
||||
"repoMetaTablename": repoMetaTablename,
|
||||
"manifestDataTablename": manifestDataTablename,
|
||||
"indexDataTablename": indexDataTablename,
|
||||
"userDataTablename": userDataTablename,
|
||||
"apiKeyTablename": apiKeyTablename,
|
||||
"versionTablename": versionTablename,
|
||||
}
|
||||
|
||||
t.Logf("using dynamo driver options: %v", dynamoDBDriverParams)
|
||||
|
||||
imageTrustStore, err := imagetrust.NewCloudImageTrustStore(
|
||||
"us-east-2",
|
||||
os.Getenv("DYNAMODBMOCK_ENDPOINT"),
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
RunUploadTests(t, *imageTrustStore)
|
||||
RunVerificationTests(t, dynamoDBDriverParams)
|
||||
})
|
||||
}
|
||||
|
||||
func RunUploadTests(t *testing.T, imageTrustStore imagetrust.ImageTrustStore) { //nolint: thelper
|
||||
cosignStorage := imageTrustStore.CosignStorage
|
||||
notationStorage := imageTrustStore.NotationStorage
|
||||
|
||||
Convey("public key - invalid content", func() {
|
||||
err := imagetrust.UploadPublicKey(cosignStorage, []byte("wrong content"))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("upload public key successfully", func() {
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
keyDir := t.TempDir()
|
||||
_ = os.Chdir(keyDir)
|
||||
|
||||
// generate a keypair
|
||||
os.Setenv("COSIGN_PASSWORD", "")
|
||||
err = generate.GenerateKeyPairCmd(context.TODO(), "", "cosign", nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_ = os.Chdir(cwd)
|
||||
|
||||
publicKeyContent, err := os.ReadFile(path.Join(keyDir, "cosign.pub"))
|
||||
So(err, ShouldBeNil)
|
||||
So(publicKeyContent, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.UploadPublicKey(cosignStorage, publicKeyContent)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("upload certificate successfully", t, func() {
|
||||
rootDir := t.TempDir()
|
||||
Convey("invalid truststore type", func() {
|
||||
err := imagetrust.UploadCertificate(notationStorage,
|
||||
[]byte("certificate content"), "wrongType", "store",
|
||||
)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrInvalidTruststoreType)
|
||||
})
|
||||
|
||||
Convey("invalid truststore name", func() {
|
||||
err := imagetrust.UploadCertificate(notationStorage,
|
||||
[]byte("certificate content"), "ca", "*store?",
|
||||
)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, zerr.ErrInvalidTruststoreName)
|
||||
})
|
||||
|
||||
Convey("invalid certificate content", func() {
|
||||
content := "invalid content"
|
||||
|
||||
err := imagetrust.UploadCertificate(notationStorage,
|
||||
[]byte(content), "ca", "store",
|
||||
)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
content = `-----BEGIN CERTIFICATE-----
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
|
||||
err = imagetrust.UploadCertificate(notationStorage,
|
||||
[]byte(content), "ca", "store",
|
||||
)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
content = ``
|
||||
|
||||
err = imagetrust.UploadCertificate(notationStorage,
|
||||
[]byte(content), "ca", "store",
|
||||
)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("upload certificate successfully", func() {
|
||||
certDir := t.TempDir()
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(rootDir)
|
||||
test.LoadNotationPath(certDir)
|
||||
|
||||
// generate a keypair
|
||||
err := test.GenerateNotationCerts(rootDir, "notation-upload-test")
|
||||
err := test.GenerateNotationCerts(certDir, "notation-upload-test")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
certificateContent, err := os.ReadFile(path.Join(rootDir, "notation/localkeys", "notation-upload-test.crt"))
|
||||
certificateContent, err := os.ReadFile(path.Join(certDir, "notation/localkeys", "notation-upload-test.crt"))
|
||||
So(err, ShouldBeNil)
|
||||
So(certificateContent, ShouldNotBeNil)
|
||||
|
||||
err = imagetrust.InitNotationDir(rootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.UploadCertificate(certificateContent, "ca", "notation-upload-test")
|
||||
err = imagetrust.UploadCertificate(notationStorage, certificateContent, "ca", "notation-upload-test")
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func RunVerificationTests(t *testing.T, dbDriverParams map[string]interface{}) { //nolint: thelper
|
||||
Convey("verify signatures are trusted", func() {
|
||||
defaultValue := true
|
||||
rootDir := t.TempDir()
|
||||
logFile, err := os.CreateTemp(t.TempDir(), "zot-log*.txt")
|
||||
So(err, ShouldBeNil)
|
||||
logPath := logFile.Name()
|
||||
defer os.Remove(logPath)
|
||||
|
||||
writers := io.MultiWriter(os.Stdout, logFile)
|
||||
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
conf.Storage.GC = false
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
ctlr.Log.Logger = ctlr.Log.Output(writers)
|
||||
ctlr.Config.Storage.RootDirectory = rootDir
|
||||
if dbDriverParams != nil {
|
||||
conf.Storage.CacheDriver = dbDriverParams
|
||||
}
|
||||
conf.Extensions = &extconf.ExtensionConfig{}
|
||||
conf.Extensions.Trust = &extconf.ImageTrustConfig{}
|
||||
conf.Extensions.Trust.Enable = &defaultValue
|
||||
conf.Extensions.Trust.Cosign = defaultValue
|
||||
conf.Extensions.Trust.Notation = defaultValue
|
||||
|
||||
cm := test.NewControllerManager(ctlr)
|
||||
cm.StartAndWait(conf.HTTP.Port)
|
||||
defer cm.StopServer()
|
||||
|
||||
repo := "repo" //nolint:goconst
|
||||
tag := "test" //nolint:goconst
|
||||
|
||||
Convey("verify cosign signature is trusted", func() {
|
||||
image, err := test.GetRandomImage() //nolint:staticcheck
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifestContent, err := json.Marshal(image.Manifest)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifestDigest := image.Digest()
|
||||
|
||||
err = test.UploadImage(image, baseURL, repo, tag)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
keyDir := t.TempDir()
|
||||
_ = os.Chdir(keyDir)
|
||||
|
||||
// generate a keypair
|
||||
os.Setenv("COSIGN_PASSWORD", "")
|
||||
err = generate.GenerateKeyPairCmd(context.TODO(), "", "cosign", nil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_ = os.Chdir(cwd)
|
||||
|
||||
// sign the image
|
||||
err = sign.SignCmd(&options.RootOptions{Verbose: true, Timeout: 1 * time.Minute},
|
||||
options.KeyOpts{KeyRef: path.Join(keyDir, "cosign.key"), PassFunc: generate.GetPass},
|
||||
options.SignOptions{
|
||||
Registry: options.RegistryOptions{AllowInsecure: true},
|
||||
AnnotationOptions: options.AnnotationOptions{Annotations: []string{fmt.Sprintf("tag=%s", tag)}},
|
||||
Upload: true,
|
||||
},
|
||||
[]string{fmt.Sprintf("localhost:%s/%s@%s", port, repo, manifestDigest.String())})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
indexContent, err := ctlr.StoreController.DefaultStore.GetIndexContent(repo)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
var index ispec.Index
|
||||
err = json.Unmarshal(indexContent, &index)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
var rawSignature []byte
|
||||
var sigKey string
|
||||
|
||||
for _, manifest := range index.Manifests {
|
||||
if manifest.Digest != manifestDigest {
|
||||
blobContent, err := ctlr.StoreController.DefaultStore.GetBlobContent(repo, manifest.Digest)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
var cosignSig ispec.Manifest
|
||||
|
||||
err = json.Unmarshal(blobContent, &cosignSig)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
sigKey = cosignSig.Layers[0].Annotations[zcommon.CosignSigKey]
|
||||
|
||||
rawSignature, err = ctlr.StoreController.DefaultStore.GetBlobContent(repo, cosignSig.Layers[0].Digest)
|
||||
So(err, ShouldBeNil)
|
||||
}
|
||||
}
|
||||
|
||||
publicKeyContent, err := os.ReadFile(path.Join(keyDir, "cosign.pub"))
|
||||
So(err, ShouldBeNil)
|
||||
So(publicKeyContent, ShouldNotBeNil)
|
||||
|
||||
// upload the public key
|
||||
client := resty.New()
|
||||
resp, err := client.R().SetHeader("Content-type", "application/octet-stream").
|
||||
SetBody(publicKeyContent).Post(baseURL + constants.FullCosign)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
|
||||
imageTrustStore := ctlr.MetaDB.ImageTrustStore()
|
||||
|
||||
// signature is trusted
|
||||
author, _, isTrusted, err := imageTrustStore.VerifySignature("cosign", rawSignature, sigKey, manifestDigest,
|
||||
manifestContent, repo)
|
||||
So(err, ShouldBeNil)
|
||||
So(isTrusted, ShouldBeTrue)
|
||||
So(author, ShouldNotBeEmpty)
|
||||
})
|
||||
|
||||
Convey("verify notation signature is trusted", func() {
|
||||
image, err := test.GetRandomImage() //nolint:staticcheck
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifestContent, err := json.Marshal(image.Manifest)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
manifestDigest := image.Digest()
|
||||
|
||||
err = test.UploadImage(image, baseURL, repo, tag)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
notationDir := t.TempDir()
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(notationDir)
|
||||
|
||||
uuid, err := guuid.NewV4()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
certName := fmt.Sprintf("notation-sign-test-%s", uuid)
|
||||
|
||||
// generate a keypair
|
||||
err = test.GenerateNotationCerts(notationDir, certName)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
// sign the image
|
||||
imageURL := fmt.Sprintf("localhost:%s/%s", port, fmt.Sprintf("%s:%s", repo, tag))
|
||||
|
||||
err = test.SignWithNotation(certName, imageURL, notationDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
indexContent, err := ctlr.StoreController.DefaultStore.GetIndexContent(repo)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
var index ispec.Index
|
||||
err = json.Unmarshal(indexContent, &index)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
var rawSignature []byte
|
||||
var sigKey string
|
||||
|
||||
for _, manifest := range index.Manifests {
|
||||
blobContent, err := ctlr.StoreController.DefaultStore.GetBlobContent(repo, manifest.Digest)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
var notationSig ispec.Manifest
|
||||
|
||||
err = json.Unmarshal(blobContent, ¬ationSig)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
t.Logf("Processing manifest %v", notationSig)
|
||||
if notationSig.Config.MediaType != notreg.ArtifactTypeNotation ||
|
||||
notationSig.Subject.Digest != manifestDigest {
|
||||
continue
|
||||
}
|
||||
|
||||
sigKey = notationSig.Layers[0].MediaType
|
||||
|
||||
rawSignature, err = ctlr.StoreController.DefaultStore.GetBlobContent(repo, notationSig.Layers[0].Digest)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
t.Logf("Identified notation signature manifest %v", notationSig)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
So(sigKey, ShouldNotBeEmpty)
|
||||
|
||||
certificateContent, err := os.ReadFile(
|
||||
path.Join(notationDir,
|
||||
fmt.Sprintf("notation/truststore/x509/ca/%s", certName),
|
||||
fmt.Sprintf("%s.crt", certName),
|
||||
),
|
||||
)
|
||||
So(err, ShouldBeNil)
|
||||
So(certificateContent, ShouldNotBeNil)
|
||||
|
||||
client := resty.New()
|
||||
resp, err := client.R().SetHeader("Content-type", "application/octet-stream").
|
||||
SetQueryParam("truststoreName", certName).
|
||||
SetBody(certificateContent).Post(baseURL + constants.FullNotation)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
|
||||
imageTrustStore := ctlr.MetaDB.ImageTrustStore()
|
||||
|
||||
// signature is trusted
|
||||
author, _, isTrusted, err := imageTrustStore.VerifySignature("notation", rawSignature, sigKey, manifestDigest,
|
||||
manifestContent, repo)
|
||||
So(err, ShouldBeNil)
|
||||
So(isTrusted, ShouldBeTrue)
|
||||
So(author, ShouldEqual, "CN=cert,O=Notary,L=Seattle,ST=WA,C=US")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func skipIt(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
if os.Getenv("DYNAMODBMOCK_ENDPOINT") == "" {
|
||||
t.Skip("Skipping testing without AWS S3 mock server")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
package imagetrust
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
|
@ -14,9 +16,12 @@ import (
|
|||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sync"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
|
||||
"github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
|
||||
"github.com/aws/aws-secretsmanager-caching-go/secretcache"
|
||||
_ "github.com/notaryproject/notation-core-go/signature/jws"
|
||||
"github.com/notaryproject/notation-go"
|
||||
"github.com/notaryproject/notation-go/dir"
|
||||
|
@ -32,34 +37,71 @@ import (
|
|||
|
||||
const notationDirRelativePath = "_notation"
|
||||
|
||||
var (
|
||||
notationDir = "" //nolint:gochecknoglobals
|
||||
TrustpolicyLock = new(sync.Mutex) //nolint: gochecknoglobals
|
||||
)
|
||||
type CertificateLocalStorage struct {
|
||||
notationDir string
|
||||
}
|
||||
|
||||
func InitNotationDir(rootDir string) error {
|
||||
type CertificateCloudStorage struct {
|
||||
secretsManagerClient *secretsmanager.Client
|
||||
secretsManagerCache *secretcache.Cache
|
||||
}
|
||||
|
||||
type certificateStorage interface {
|
||||
LoadTrustPolicyDocument() (*trustpolicy.Document, error)
|
||||
StoreCertificate(certificateContent []byte, truststoreType, truststoreName string) error
|
||||
UpdateTrustPolicyDocument(trustpolicyDocContent []byte) error
|
||||
GetVerifier(policyDoc *trustpolicy.Document) (notation.Verifier, error)
|
||||
InitTrustpolicy(trustpolicy []byte) error
|
||||
}
|
||||
|
||||
func NewCertificateLocalStorage(rootDir string) (*CertificateLocalStorage, error) {
|
||||
dir := path.Join(rootDir, notationDirRelativePath)
|
||||
|
||||
_, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, defaultDirPerms)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
notationDir = dir
|
||||
|
||||
if _, err := LoadTrustPolicyDocument(notationDir); os.IsNotExist(err) {
|
||||
return InitTrustpolicyFile(notationDir)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return err
|
||||
certStorage := &CertificateLocalStorage{
|
||||
notationDir: dir,
|
||||
}
|
||||
|
||||
_, err = certStorage.LoadTrustPolicyDocument()
|
||||
if os.IsNotExist(err) {
|
||||
if err := InitTrustpolicyFile(certStorage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return certStorage, nil
|
||||
}
|
||||
|
||||
func InitTrustpolicyFile(configDir string) error {
|
||||
func NewCertificateCloudStorage(
|
||||
secretsManagerClient *secretsmanager.Client, secretsManagerCache *secretcache.Cache,
|
||||
) (*CertificateCloudStorage, error) {
|
||||
certStorage := &CertificateCloudStorage{
|
||||
secretsManagerClient: secretsManagerClient,
|
||||
secretsManagerCache: secretsManagerCache,
|
||||
}
|
||||
|
||||
err := InitTrustpolicyFile(certStorage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return certStorage, nil
|
||||
}
|
||||
|
||||
func InitTrustpolicyFile(notationStorage certificateStorage) error {
|
||||
// according to https://github.com/notaryproject/notation/blob/main/specs/commandline/verify.md
|
||||
// the value of signatureVerification.level field from trustpolicy.json file
|
||||
// could be one of these values: `strict`, `permissive`, `audit` or `skip`
|
||||
|
@ -68,40 +110,123 @@ func InitTrustpolicyFile(configDir string) error {
|
|||
// a certificate that verifies a signature, but that certificate has expired, then the
|
||||
// signature is not trusted; if this field were set to `permissive` then the
|
||||
// signature would be trusted)
|
||||
trustPolicy := `
|
||||
{
|
||||
"version": "1.0",
|
||||
"trustPolicies": [
|
||||
{
|
||||
"name": "default-config",
|
||||
"registryScopes": [ "*" ],
|
||||
"signatureVerification": {
|
||||
"level" : "strict"
|
||||
},
|
||||
"trustStores": [],
|
||||
"trustedIdentities": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
trustPolicy := `{
|
||||
"version": "1.0",
|
||||
"trustPolicies": [
|
||||
{
|
||||
"name": "default-config",
|
||||
"registryScopes": [ "*" ],
|
||||
"signatureVerification": {
|
||||
"level" : "strict"
|
||||
},
|
||||
"trustStores": [],
|
||||
"trustedIdentities": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
TrustpolicyLock.Lock()
|
||||
defer TrustpolicyLock.Unlock()
|
||||
|
||||
return os.WriteFile(path.Join(configDir, dir.PathTrustPolicy), []byte(trustPolicy), defaultDirPerms)
|
||||
return notationStorage.InitTrustpolicy([]byte(trustPolicy))
|
||||
}
|
||||
|
||||
func GetNotationDirPath() (string, error) {
|
||||
if notationDir != "" {
|
||||
return notationDir, nil
|
||||
func (local *CertificateLocalStorage) InitTrustpolicy(trustpolicy []byte) error {
|
||||
notationDir, err := local.GetNotationDirPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(path.Join(notationDir, dir.PathTrustPolicy), trustpolicy, defaultDirPerms)
|
||||
}
|
||||
|
||||
func (cloud *CertificateCloudStorage) InitTrustpolicy(trustpolicy []byte) error {
|
||||
name := "trustpolicy"
|
||||
description := "notation trustpolicy file"
|
||||
secret := base64.StdEncoding.EncodeToString(trustpolicy)
|
||||
secretInputParam := &secretsmanager.CreateSecretInput{
|
||||
Name: &name,
|
||||
Description: &description,
|
||||
SecretString: &secret,
|
||||
}
|
||||
|
||||
_, err := cloud.secretsManagerClient.CreateSecret(context.Background(), secretInputParam)
|
||||
if err != nil && !strings.Contains(err.Error(), "the secret trustpolicy already exists.") {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (local *CertificateLocalStorage) GetNotationDirPath() (string, error) {
|
||||
if local.notationDir != "" {
|
||||
return local.notationDir, nil
|
||||
}
|
||||
|
||||
return "", zerr.ErrSignConfigDirNotSet
|
||||
}
|
||||
|
||||
func (cloud *CertificateCloudStorage) GetCertificates(
|
||||
ctx context.Context, storeType truststore.Type, namedStore string,
|
||||
) ([]*x509.Certificate, error) {
|
||||
certificates := []*x509.Certificate{}
|
||||
|
||||
if !validateTruststoreType(string(storeType)) {
|
||||
return []*x509.Certificate{}, zerr.ErrInvalidTruststoreType
|
||||
}
|
||||
|
||||
if !validateTruststoreName(namedStore) {
|
||||
return []*x509.Certificate{}, zerr.ErrInvalidTruststoreName
|
||||
}
|
||||
|
||||
listSecretsInput := secretsmanager.ListSecretsInput{
|
||||
Filters: []types.Filter{
|
||||
{
|
||||
Key: types.FilterNameStringTypeName,
|
||||
Values: []string{path.Join(string(storeType), namedStore)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
secrets, err := cloud.secretsManagerClient.ListSecrets(ctx, &listSecretsInput)
|
||||
if err != nil {
|
||||
return []*x509.Certificate{}, err
|
||||
}
|
||||
|
||||
for _, secret := range secrets.SecretList {
|
||||
// get key
|
||||
raw, err := cloud.secretsManagerCache.GetSecretString(*(secret.Name))
|
||||
if err != nil {
|
||||
return []*x509.Certificate{}, err
|
||||
}
|
||||
|
||||
rawDecoded, err := base64.StdEncoding.DecodeString(raw)
|
||||
if err != nil {
|
||||
return []*x509.Certificate{}, err
|
||||
}
|
||||
|
||||
certs, _, err := parseAndValidateCertificateContent(rawDecoded)
|
||||
if err != nil {
|
||||
return []*x509.Certificate{}, err
|
||||
}
|
||||
|
||||
err = truststore.ValidateCertificates(certs)
|
||||
if err != nil {
|
||||
return []*x509.Certificate{}, err
|
||||
}
|
||||
|
||||
certificates = append(certificates, certs...)
|
||||
}
|
||||
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
// Equivalent function for trustpolicy.LoadDocument() but using a specific SysFS not the one returned by ConfigFS().
|
||||
func LoadTrustPolicyDocument(notationDir string) (*trustpolicy.Document, error) {
|
||||
func (local *CertificateLocalStorage) LoadTrustPolicyDocument() (*trustpolicy.Document, error) {
|
||||
notationDir, err := local.GetNotationDirPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonFile, err := dir.NewSysFS(notationDir).Open(dir.PathTrustPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -119,33 +244,66 @@ func LoadTrustPolicyDocument(notationDir string) (*trustpolicy.Document, error)
|
|||
return policyDocument, nil
|
||||
}
|
||||
|
||||
func (cloud *CertificateCloudStorage) LoadTrustPolicyDocument() (*trustpolicy.Document, error) {
|
||||
policyDocument := &trustpolicy.Document{}
|
||||
|
||||
raw, err := cloud.secretsManagerCache.GetSecretString("trustpolicy")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawDecoded, err := base64.StdEncoding.DecodeString(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
err = json.Compact(&buf, rawDecoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(buf.Bytes(), policyDocument)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return policyDocument, nil
|
||||
}
|
||||
|
||||
// NewFromConfig returns a verifier based on local file system.
|
||||
// Equivalent function for verifier.NewFromConfig()
|
||||
// but using LoadTrustPolicyDocumnt() function instead of trustpolicy.LoadDocument() function.
|
||||
func NewFromConfig() (notation.Verifier, error) {
|
||||
notationDir, err := GetNotationDirPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func NewFromConfig(notationStorage certificateStorage) (notation.Verifier, error) {
|
||||
// Load trust policy.
|
||||
TrustpolicyLock.Lock()
|
||||
defer TrustpolicyLock.Unlock()
|
||||
|
||||
policyDocument, err := LoadTrustPolicyDocument(notationDir)
|
||||
policyDocument, err := notationStorage.LoadTrustPolicyDocument()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Load trust store.
|
||||
return notationStorage.GetVerifier(policyDocument)
|
||||
}
|
||||
|
||||
func (local *CertificateLocalStorage) GetVerifier(policyDoc *trustpolicy.Document) (notation.Verifier, error) {
|
||||
notationDir, err := local.GetNotationDirPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x509TrustStore := truststore.NewX509TrustStore(dir.NewSysFS(notationDir))
|
||||
|
||||
return verifier.New(policyDocument, x509TrustStore,
|
||||
return verifier.New(policyDoc, x509TrustStore,
|
||||
plugin.NewCLIManager(dir.NewSysFS(path.Join(notationDir, dir.PathPlugins))))
|
||||
}
|
||||
|
||||
func (cloud *CertificateCloudStorage) GetVerifier(policyDoc *trustpolicy.Document) (notation.Verifier, error) {
|
||||
return verifier.New(policyDoc, cloud,
|
||||
plugin.NewCLIManager(dir.NewSysFS(path.Join(dir.PathPlugins))))
|
||||
}
|
||||
|
||||
func VerifyNotationSignature(
|
||||
artifactDescriptor ispec.Descriptor, artifactReference string, rawSignature []byte, signatureMediaType string,
|
||||
notationStorage certificateStorage, artifactDescriptor ispec.Descriptor, artifactReference string,
|
||||
rawSignature []byte, signatureMediaType string,
|
||||
) (string, time.Time, bool, error) {
|
||||
var (
|
||||
date time.Time
|
||||
|
@ -161,7 +319,7 @@ func VerifyNotationSignature(
|
|||
}
|
||||
|
||||
// Initialize verifier.
|
||||
verifier, err := NewFromConfig()
|
||||
verifier, err := NewFromConfig(notationStorage)
|
||||
if err != nil {
|
||||
return author, date, false, err
|
||||
}
|
||||
|
@ -219,7 +377,9 @@ func CheckExpiryErr(verificationResults []*notation.ValidationResult, notAfter t
|
|||
return false
|
||||
}
|
||||
|
||||
func UploadCertificate(certificateContent []byte, truststoreType, truststoreName string) error {
|
||||
func UploadCertificate(
|
||||
notationStorage certificateStorage, certificateContent []byte, truststoreType, truststoreName string,
|
||||
) error {
|
||||
// validate truststore type
|
||||
if !validateTruststoreType(truststoreType) {
|
||||
return zerr.ErrInvalidTruststoreType
|
||||
|
@ -231,35 +391,19 @@ func UploadCertificate(certificateContent []byte, truststoreType, truststoreName
|
|||
}
|
||||
|
||||
// validate certificate
|
||||
if ok, err := validateCertificate(certificateContent); !ok {
|
||||
if _, ok, err := parseAndValidateCertificateContent(certificateContent); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
// add certificate to "{rootDir}/_notation/truststore/x509/{type}/{name}/{name.crt}"
|
||||
configDir, err := GetNotationDirPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := godigest.FromBytes(certificateContent)
|
||||
|
||||
// store certificate
|
||||
truststorePath := path.Join(configDir, dir.TrustStoreDir, "x509", truststoreType, truststoreName, name.String())
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(truststorePath), defaultDirPerms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(truststorePath, certificateContent, defaultFilePerms)
|
||||
err := notationStorage.StoreCertificate(certificateContent, truststoreType, truststoreName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update "trustpolicy.json" file
|
||||
// add certificate to "trustpolicy.json"
|
||||
TrustpolicyLock.Lock()
|
||||
defer TrustpolicyLock.Unlock()
|
||||
|
||||
trustpolicyDoc, err := LoadTrustPolicyDocument(configDir)
|
||||
trustpolicyDoc, err := notationStorage.LoadTrustPolicyDocument()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -279,7 +423,67 @@ func UploadCertificate(certificateContent []byte, truststoreType, truststoreName
|
|||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(path.Join(configDir, dir.PathTrustPolicy), trustpolicyDocContent, defaultFilePerms)
|
||||
return notationStorage.UpdateTrustPolicyDocument(trustpolicyDocContent)
|
||||
}
|
||||
|
||||
func (local *CertificateLocalStorage) StoreCertificate(certificateContent []byte,
|
||||
truststoreType, truststoreName string,
|
||||
) error {
|
||||
// add certificate to "{rootDir}/_notation/truststore/x509/{type}/{name}/{name.crt}"
|
||||
configDir, err := local.GetNotationDirPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := godigest.FromBytes(certificateContent)
|
||||
|
||||
// store certificate
|
||||
truststorePath := path.Join(configDir, dir.TrustStoreDir, "x509", truststoreType, truststoreName, name.String())
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(truststorePath), defaultDirPerms); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(truststorePath, certificateContent, defaultFilePerms)
|
||||
}
|
||||
|
||||
func (local *CertificateLocalStorage) UpdateTrustPolicyDocument(content []byte) error {
|
||||
configDir, err := local.GetNotationDirPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(path.Join(configDir, dir.PathTrustPolicy), content, defaultFilePerms)
|
||||
}
|
||||
|
||||
func (cloud *CertificateCloudStorage) StoreCertificate(certificateContent []byte,
|
||||
truststoreType, truststoreName string,
|
||||
) error {
|
||||
name := path.Join(truststoreType, truststoreName, godigest.FromBytes(certificateContent).Encoded())
|
||||
description := "notation certificate"
|
||||
secret := base64.StdEncoding.EncodeToString(certificateContent)
|
||||
secretInputParam := &secretsmanager.CreateSecretInput{
|
||||
Name: &name,
|
||||
Description: &description,
|
||||
SecretString: &secret,
|
||||
}
|
||||
|
||||
_, err := cloud.secretsManagerClient.CreateSecret(context.Background(), secretInputParam)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (cloud *CertificateCloudStorage) UpdateTrustPolicyDocument(content []byte) error {
|
||||
trustpolicyName := "trustpolicy"
|
||||
trustpolicySecret := base64.StdEncoding.EncodeToString(content)
|
||||
trustpolicySecretInputParam := &secretsmanager.UpdateSecretInput{
|
||||
SecretId: &trustpolicyName,
|
||||
SecretString: &trustpolicySecret,
|
||||
}
|
||||
|
||||
_, err := cloud.secretsManagerClient.UpdateSecret(context.Background(), trustpolicySecretInputParam)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func validateTruststoreType(truststoreType string) bool {
|
||||
|
@ -293,11 +497,15 @@ func validateTruststoreType(truststoreType string) bool {
|
|||
}
|
||||
|
||||
func validateTruststoreName(truststoreName string) bool {
|
||||
if strings.Contains(truststoreName, "..") {
|
||||
return false
|
||||
}
|
||||
|
||||
return regexp.MustCompile(`^[a-zA-Z0-9_.-]+$`).MatchString(truststoreName)
|
||||
}
|
||||
|
||||
// implementation from https://github.com/notaryproject/notation-core-go/blob/main/x509/cert.go#L20
|
||||
func validateCertificate(certificateContent []byte) (bool, error) {
|
||||
func parseAndValidateCertificateContent(certificateContent []byte) ([]*x509.Certificate, bool, error) {
|
||||
var certs []*x509.Certificate
|
||||
|
||||
block, rest := pem.Decode(certificateContent)
|
||||
|
@ -305,7 +513,7 @@ func validateCertificate(certificateContent []byte) (bool, error) {
|
|||
// data may be in DER format
|
||||
derCerts, err := x509.ParseCertificates(certificateContent)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("%w: %w", zerr.ErrInvalidCertificateContent, err)
|
||||
return []*x509.Certificate{}, false, fmt.Errorf("%w: %w", zerr.ErrInvalidCertificateContent, err)
|
||||
}
|
||||
|
||||
certs = append(certs, derCerts...)
|
||||
|
@ -314,7 +522,7 @@ func validateCertificate(certificateContent []byte) (bool, error) {
|
|||
for block != nil {
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("%w: %w", zerr.ErrInvalidCertificateContent, err)
|
||||
return []*x509.Certificate{}, false, fmt.Errorf("%w: %w", zerr.ErrInvalidCertificateContent, err)
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
block, rest = pem.Decode(rest)
|
||||
|
@ -322,9 +530,9 @@ func validateCertificate(certificateContent []byte) (bool, error) {
|
|||
}
|
||||
|
||||
if len(certs) == 0 {
|
||||
return false, fmt.Errorf("%w: no valid certificates found in payload",
|
||||
return []*x509.Certificate{}, false, fmt.Errorf("%w: no valid certificates found in payload",
|
||||
zerr.ErrInvalidCertificateContent)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
return certs, true, nil
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
|
||||
zerr "zotregistry.io/zot/errors"
|
||||
zcommon "zotregistry.io/zot/pkg/common"
|
||||
"zotregistry.io/zot/pkg/extensions/imagetrust"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
"zotregistry.io/zot/pkg/meta/common"
|
||||
mTypes "zotregistry.io/zot/pkg/meta/types"
|
||||
|
@ -23,9 +22,10 @@ import (
|
|||
)
|
||||
|
||||
type BoltDB struct {
|
||||
DB *bbolt.DB
|
||||
Patches []func(DB *bbolt.DB) error
|
||||
Log log.Logger
|
||||
DB *bbolt.DB
|
||||
Patches []func(DB *bbolt.DB) error
|
||||
imgTrustStore mTypes.ImageTrustStore
|
||||
Log log.Logger
|
||||
}
|
||||
|
||||
func New(boltDB *bbolt.DB, log log.Logger) (*BoltDB, error) {
|
||||
|
@ -72,12 +72,21 @@ func New(boltDB *bbolt.DB, log log.Logger) (*BoltDB, error) {
|
|||
}
|
||||
|
||||
return &BoltDB{
|
||||
DB: boltDB,
|
||||
Patches: version.GetBoltDBPatches(),
|
||||
Log: log,
|
||||
DB: boltDB,
|
||||
Patches: version.GetBoltDBPatches(),
|
||||
imgTrustStore: nil,
|
||||
Log: log,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (bdw *BoltDB) ImageTrustStore() mTypes.ImageTrustStore {
|
||||
return bdw.imgTrustStore
|
||||
}
|
||||
|
||||
func (bdw *BoltDB) SetImageTrustStore(imgTrustStore mTypes.ImageTrustStore) {
|
||||
bdw.imgTrustStore = imgTrustStore
|
||||
}
|
||||
|
||||
func (bdw *BoltDB) SetManifestData(manifestDigest godigest.Digest, manifestData mTypes.ManifestData) error {
|
||||
err := bdw.DB.Update(func(tx *bbolt.Tx) error {
|
||||
buck := tx.Bucket([]byte(ManifestDataBucket))
|
||||
|
@ -721,6 +730,12 @@ func (bdw *BoltDB) IncrementImageDownloads(repo string, reference string) error
|
|||
|
||||
func (bdw *BoltDB) UpdateSignaturesValidity(repo string, manifestDigest godigest.Digest) error {
|
||||
err := bdw.DB.Update(func(transaction *bbolt.Tx) error {
|
||||
imgTrustStore := bdw.ImageTrustStore()
|
||||
|
||||
if imgTrustStore == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get ManifestData of signed manifest
|
||||
manifestBuck := transaction.Bucket([]byte(ManifestDataBucket))
|
||||
mdBlob := manifestBuck.Get([]byte(manifestDigest))
|
||||
|
@ -778,8 +793,8 @@ func (bdw *BoltDB) UpdateSignaturesValidity(repo string, manifestDigest godigest
|
|||
layersInfo := []mTypes.LayerInfo{}
|
||||
|
||||
for _, layerInfo := range sigInfo.LayersInfo {
|
||||
author, date, isTrusted, _ := imagetrust.VerifySignature(sigType, layerInfo.LayerContent, layerInfo.SignatureKey,
|
||||
manifestDigest, blob, repo)
|
||||
author, date, isTrusted, _ := imgTrustStore.VerifySignature(sigType, layerInfo.LayerContent,
|
||||
layerInfo.SignatureKey, manifestDigest, blob, repo)
|
||||
|
||||
if isTrusted {
|
||||
layerInfo.Signer = author
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"encoding/json"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/opencontainers/go-digest"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
|
@ -22,6 +23,15 @@ import (
|
|||
"zotregistry.io/zot/pkg/test"
|
||||
)
|
||||
|
||||
type imgTrustStore struct{}
|
||||
|
||||
func (its imgTrustStore) VerifySignature(
|
||||
signatureType string, rawSignature []byte, sigKey string, manifestDigest digest.Digest, manifestContent []byte,
|
||||
repo string,
|
||||
) (string, time.Time, bool, error) {
|
||||
return "", time.Time{}, false, nil
|
||||
}
|
||||
|
||||
func TestWrapperErrors(t *testing.T) {
|
||||
Convey("Errors", t, func() {
|
||||
tmpDir := t.TempDir()
|
||||
|
@ -35,6 +45,8 @@ func TestWrapperErrors(t *testing.T) {
|
|||
So(boltdbWrapper, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
boltdbWrapper.SetImageTrustStore(imgTrustStore{})
|
||||
|
||||
repoMeta := mTypes.RepoMetadata{
|
||||
Tags: map[string]mTypes.Descriptor{},
|
||||
Signatures: map[string]mTypes.ManifestSignatures{},
|
||||
|
|
|
@ -17,7 +17,6 @@ import (
|
|||
|
||||
zerr "zotregistry.io/zot/errors"
|
||||
zcommon "zotregistry.io/zot/pkg/common"
|
||||
"zotregistry.io/zot/pkg/extensions/imagetrust"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
"zotregistry.io/zot/pkg/meta/common"
|
||||
mTypes "zotregistry.io/zot/pkg/meta/types"
|
||||
|
@ -36,10 +35,13 @@ type DynamoDB struct {
|
|||
UserDataTablename string
|
||||
VersionTablename string
|
||||
Patches []func(client *dynamodb.Client, tableNames map[string]string) error
|
||||
imgTrustStore mTypes.ImageTrustStore
|
||||
Log log.Logger
|
||||
}
|
||||
|
||||
func New(client *dynamodb.Client, params DBDriverParameters, log log.Logger) (*DynamoDB, error) {
|
||||
func New(
|
||||
client *dynamodb.Client, params DBDriverParameters, log log.Logger,
|
||||
) (*DynamoDB, error) {
|
||||
dynamoWrapper := DynamoDB{
|
||||
Client: client,
|
||||
RepoMetaTablename: params.RepoMetaTablename,
|
||||
|
@ -49,6 +51,7 @@ func New(client *dynamodb.Client, params DBDriverParameters, log log.Logger) (*D
|
|||
UserDataTablename: params.UserDataTablename,
|
||||
APIKeyTablename: params.APIKeyTablename,
|
||||
Patches: version.GetDynamoDBPatches(),
|
||||
imgTrustStore: nil,
|
||||
Log: log,
|
||||
}
|
||||
|
||||
|
@ -86,6 +89,14 @@ func New(client *dynamodb.Client, params DBDriverParameters, log log.Logger) (*D
|
|||
return &dynamoWrapper, nil
|
||||
}
|
||||
|
||||
func (dwr *DynamoDB) ImageTrustStore() mTypes.ImageTrustStore {
|
||||
return dwr.imgTrustStore
|
||||
}
|
||||
|
||||
func (dwr *DynamoDB) SetImageTrustStore(imgTrustStore mTypes.ImageTrustStore) {
|
||||
dwr.imgTrustStore = imgTrustStore
|
||||
}
|
||||
|
||||
func (dwr *DynamoDB) SetManifestData(manifestDigest godigest.Digest, manifestData mTypes.ManifestData) error {
|
||||
mdAttributeValue, err := attributevalue.Marshal(manifestData)
|
||||
if err != nil {
|
||||
|
@ -624,6 +635,12 @@ func (dwr *DynamoDB) IncrementImageDownloads(repo string, reference string) erro
|
|||
}
|
||||
|
||||
func (dwr *DynamoDB) UpdateSignaturesValidity(repo string, manifestDigest godigest.Digest) error {
|
||||
imgTrustStore := dwr.ImageTrustStore()
|
||||
|
||||
if imgTrustStore == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get ManifestData of signed manifest
|
||||
var blob []byte
|
||||
|
||||
|
@ -658,7 +675,7 @@ func (dwr *DynamoDB) UpdateSignaturesValidity(repo string, manifestDigest godige
|
|||
layersInfo := []mTypes.LayerInfo{}
|
||||
|
||||
for _, layerInfo := range sigInfo.LayersInfo {
|
||||
author, date, isTrusted, _ := imagetrust.VerifySignature(sigType, layerInfo.LayerContent, layerInfo.SignatureKey,
|
||||
author, date, isTrusted, _ := imgTrustStore.VerifySignature(sigType, layerInfo.LayerContent, layerInfo.SignatureKey,
|
||||
manifestDigest, blob, repo)
|
||||
|
||||
if isTrusted {
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"zotregistry.io/zot/pkg/extensions/imagetrust"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
mdynamodb "zotregistry.io/zot/pkg/meta/dynamodb"
|
||||
mTypes "zotregistry.io/zot/pkg/meta/types"
|
||||
|
@ -164,9 +165,14 @@ func TestWrapperErrors(t *testing.T) {
|
|||
client, err := mdynamodb.GetDynamoClient(params) //nolint:contextcheck
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
imgTrustStore, err := imagetrust.NewCloudImageTrustStore(params.Region, params.Endpoint)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dynamoWrapper, err := mdynamodb.New(client, params, log) //nolint:contextcheck
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dynamoWrapper.SetImageTrustStore(imgTrustStore)
|
||||
|
||||
So(dynamoWrapper.ResetManifestDataTable(), ShouldBeNil) //nolint:contextcheck
|
||||
So(dynamoWrapper.ResetRepoMetaTable(), ShouldBeNil) //nolint:contextcheck
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"zotregistry.io/zot/errors"
|
||||
"zotregistry.io/zot/pkg/api/config"
|
||||
"zotregistry.io/zot/pkg/extensions/imagetrust"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
"zotregistry.io/zot/pkg/meta/boltdb"
|
||||
mdynamodb "zotregistry.io/zot/pkg/meta/dynamodb"
|
||||
|
@ -33,11 +32,6 @@ func New(storageConfig config.StorageConfig, log log.Logger) (mTypes.MetaDB, err
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = imagetrust.InitCosignAndNotationDirs(params.RootDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Create("boltdb", driver, params, log) //nolint:contextcheck
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ package meta_test
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -22,7 +23,6 @@ import (
|
|||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
|
||||
"zotregistry.io/zot/pkg/api/config"
|
||||
"zotregistry.io/zot/pkg/extensions/imagetrust"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
"zotregistry.io/zot/pkg/meta"
|
||||
|
@ -43,9 +43,12 @@ const (
|
|||
|
||||
func TestBoltDB(t *testing.T) {
|
||||
Convey("BoltDB creation", t, func() {
|
||||
boltDBParams := boltdb.DBParameters{}
|
||||
boltDBParams := boltdb.DBParameters{RootDir: t.TempDir()}
|
||||
repoDBPath := path.Join(boltDBParams.RootDir, "repo.db")
|
||||
|
||||
boltDriver, err := boltdb.GetBoltDriver(boltDBParams)
|
||||
So(err, ShouldBeNil)
|
||||
defer os.Remove(repoDBPath)
|
||||
|
||||
log := log.NewLogger("debug", "")
|
||||
|
||||
|
@ -53,27 +56,36 @@ func TestBoltDB(t *testing.T) {
|
|||
So(metaDB, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.Chmod("repo.db", 0o200)
|
||||
err = os.Chmod(repoDBPath, 0o200)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = boltdb.GetBoltDriver(boltDBParams)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod("repo.db", 0o600)
|
||||
err = os.Chmod(repoDBPath, 0o600)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
defer os.Remove("repo.db")
|
||||
})
|
||||
|
||||
Convey("BoltDB Wrapper", t, func() {
|
||||
boltDBParams := boltdb.DBParameters{}
|
||||
boltDBParams := boltdb.DBParameters{RootDir: t.TempDir()}
|
||||
boltDriver, err := boltdb.GetBoltDriver(boltDBParams)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
log := log.NewLogger("debug", "")
|
||||
|
||||
imgTrustStore, err := imagetrust.NewLocalImageTrustStore(boltDBParams.RootDir)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
boltdbWrapper, err := boltdb.New(boltDriver, log)
|
||||
defer os.Remove("repo.db")
|
||||
|
||||
boltdbWrapper.SetImageTrustStore(imgTrustStore)
|
||||
|
||||
defer func() {
|
||||
os.Remove(path.Join(boltDBParams.RootDir, "repo.db"))
|
||||
os.RemoveAll(path.Join(boltDBParams.RootDir, "_cosign"))
|
||||
os.RemoveAll(path.Join(boltDBParams.RootDir, "_notation"))
|
||||
}()
|
||||
|
||||
So(boltdbWrapper, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
|
@ -108,6 +120,8 @@ func TestDynamoDBWrapper(t *testing.T) {
|
|||
Region: "us-east-2",
|
||||
}
|
||||
|
||||
t.Logf("using dynamo driver options: %v", dynamoDBDriverParams)
|
||||
|
||||
dynamoClient, err := mdynamodb.GetDynamoClient(dynamoDBDriverParams)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
|
@ -117,6 +131,11 @@ func TestDynamoDBWrapper(t *testing.T) {
|
|||
So(dynamoDriver, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
imgTrustStore, err := imagetrust.NewCloudImageTrustStore(dynamoDBDriverParams.Region, dynamoDBDriverParams.Endpoint)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dynamoDriver.SetImageTrustStore(imgTrustStore)
|
||||
|
||||
resetDynamoDBTables := func() error {
|
||||
err := dynamoDriver.ResetRepoMetaTable()
|
||||
if err != nil {
|
||||
|
@ -1318,7 +1337,6 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func
|
|||
So(repoData.Signatures[string(manifestDigest1)]["cosign"][0].LayersInfo[0].Date,
|
||||
ShouldBeZeroValue)
|
||||
})
|
||||
|
||||
Convey("trusted signature", func() {
|
||||
_, _, manifest, _ := test.GetRandomImageComponents(10) //nolint:staticcheck
|
||||
manifestContent, _ := json.Marshal(manifest)
|
||||
|
@ -1344,7 +1362,10 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func
|
|||
}
|
||||
|
||||
tdir := t.TempDir()
|
||||
keyName := "notation-sign-test"
|
||||
uuid, err := guuid.NewV4()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
keyName := fmt.Sprintf("notation-sign-test-%s", uuid)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
@ -1394,44 +1415,18 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func
|
|||
})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = imagetrust.InitNotationDir(tdir)
|
||||
certificateContent, err := os.ReadFile(path.Join(
|
||||
tdir,
|
||||
"notation/localkeys",
|
||||
fmt.Sprintf("%s.crt", keyName),
|
||||
))
|
||||
So(err, ShouldBeNil)
|
||||
So(certificateContent, ShouldNotBeNil)
|
||||
|
||||
trustpolicyPath := path.Join(tdir, "_notation/trustpolicy.json")
|
||||
imgTrustStore, ok := metaDB.ImageTrustStore().(*imagetrust.ImageTrustStore)
|
||||
So(ok, ShouldBeTrue)
|
||||
|
||||
trustPolicy := `
|
||||
{
|
||||
"version": "1.0",
|
||||
"trustPolicies": [
|
||||
{
|
||||
"name": "notation-sign-test",
|
||||
"registryScopes": [ "*" ],
|
||||
"signatureVerification": {
|
||||
"level" : "strict"
|
||||
},
|
||||
"trustStores": ["ca:notation-sign-test"],
|
||||
"trustedIdentities": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
file, err := os.Create(trustpolicyPath)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.WriteString(trustPolicy)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
truststore := "_notation/truststore/x509/ca/notation-sign-test"
|
||||
truststoreSrc := "notation/truststore/x509/ca/notation-sign-test"
|
||||
err = os.MkdirAll(path.Join(tdir, truststore), 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.CopyFile(path.Join(tdir, truststoreSrc, "notation-sign-test.crt"),
|
||||
path.Join(tdir, truststore, "notation-sign-test.crt"))
|
||||
err = imagetrust.UploadCertificate(imgTrustStore.NotationStorage, certificateContent, "ca", keyName)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = metaDB.UpdateSignaturesValidity(repo, manifestDigest) //nolint:contextcheck
|
||||
|
@ -1439,6 +1434,7 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func
|
|||
|
||||
repoData, err := metaDB.GetRepoMeta(repo)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(repoData.Signatures[string(manifestDigest)]["notation"][0].LayersInfo[0].Signer,
|
||||
ShouldNotBeEmpty)
|
||||
So(repoData.Signatures[string(manifestDigest)]["notation"][0].LayersInfo[0].Date,
|
||||
|
@ -2721,31 +2717,6 @@ func TestCreateBoltDB(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
Convey("InitCosignAndNotationDirs fails", t, func() {
|
||||
rootDir := t.TempDir()
|
||||
|
||||
var storageConfig config.StorageConfig
|
||||
|
||||
storageConfig.RootDirectory = rootDir
|
||||
storageConfig.RemoteCache = false
|
||||
log := log.NewLogger("debug", "")
|
||||
|
||||
_, err := os.Create(path.Join(rootDir, "repo.db"))
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.Chmod(rootDir, 0o555)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
newMetaDB, err := meta.New(storageConfig, log)
|
||||
So(newMetaDB, ShouldBeNil)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(rootDir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func skipDynamo(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
@ -121,6 +121,10 @@ type MetaDB interface { //nolint:interfacebloat
|
|||
[]RepoMetadata, map[string]ManifestMetadata, map[string]IndexData, error)
|
||||
|
||||
PatchDB() error
|
||||
|
||||
ImageTrustStore() ImageTrustStore
|
||||
|
||||
SetImageTrustStore(imgTrustStore ImageTrustStore)
|
||||
}
|
||||
|
||||
type UserDB interface { //nolint:interfacebloat
|
||||
|
@ -160,6 +164,13 @@ type UserDB interface { //nolint:interfacebloat
|
|||
DeleteUserAPIKey(ctx context.Context, id string) error
|
||||
}
|
||||
|
||||
type ImageTrustStore interface {
|
||||
VerifySignature(
|
||||
signatureType string, rawSignature []byte, sigKey string, manifestDigest godigest.Digest, manifestContent []byte,
|
||||
repo string,
|
||||
) (string, time.Time, bool, error)
|
||||
}
|
||||
|
||||
type ManifestMetadata struct {
|
||||
ManifestBlob []byte
|
||||
ConfigBlob []byte
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
|
||||
|
@ -31,7 +32,7 @@ func TestVersioningBoltDB(t *testing.T) {
|
|||
log := log.NewLogger("debug", "")
|
||||
|
||||
boltdbWrapper, err := boltdb.New(boltDriver, log)
|
||||
defer os.Remove("repo.db")
|
||||
defer os.Remove(path.Join(boltDBParams.RootDir, "repo.db"))
|
||||
So(boltdbWrapper, ShouldNotBeNil)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
|
|
|
@ -106,6 +106,24 @@ type MetaDBMock struct {
|
|||
DeleteUserAPIKeyFn func(ctx context.Context, id string) error
|
||||
|
||||
PatchDBFn func() error
|
||||
|
||||
ImageTrustStoreFn func() mTypes.ImageTrustStore
|
||||
|
||||
SetImageTrustStoreFn func(mTypes.ImageTrustStore)
|
||||
}
|
||||
|
||||
func (sdm MetaDBMock) ImageTrustStore() mTypes.ImageTrustStore {
|
||||
if sdm.ImageTrustStoreFn != nil {
|
||||
return sdm.ImageTrustStoreFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sdm MetaDBMock) SetImageTrustStore(imgTrustStore mTypes.ImageTrustStore) {
|
||||
if sdm.SetImageTrustStoreFn != nil {
|
||||
sdm.SetImageTrustStoreFn(imgTrustStore)
|
||||
}
|
||||
}
|
||||
|
||||
func (sdm MetaDBMock) SetRepoDescription(repo, description string) error {
|
||||
|
|
Loading…
Reference in a new issue