mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 04:48:26 +08:00
feat: upload cosign public key and notation certificates to cloud (#1744)
- using secrets manager for storing public keys and certificates
- adding a default truststore for notation verification and upload all certificates to this default truststore
- removig `truststoreName` query param from notation api for uploading certificates
(cherry picked from commit eafcc1a213)
Signed-off-by: Andreea-Lupu <andreealupu1470@yahoo.com>
This commit is contained in:
@@ -15,7 +15,6 @@ import (
|
||||
zerr "zotregistry.io/zot/errors"
|
||||
"zotregistry.io/zot/pkg/api/constants"
|
||||
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"
|
||||
@@ -24,9 +23,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) {
|
||||
@@ -73,12 +73,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))
|
||||
@@ -722,6 +731,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))
|
||||
@@ -779,8 +794,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{},
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
zerr "zotregistry.io/zot/errors"
|
||||
"zotregistry.io/zot/pkg/api/constants"
|
||||
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"
|
||||
@@ -37,10 +36,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,
|
||||
@@ -50,6 +52,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,
|
||||
}
|
||||
|
||||
@@ -87,6 +90,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 {
|
||||
@@ -625,6 +636,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
|
||||
|
||||
@@ -659,7 +676,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.NewAWSImageTrustStore(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
|
||||
|
||||
@@ -697,6 +703,9 @@ func TestWrapperErrors(t *testing.T) {
|
||||
|
||||
err = dynamoWrapper.UpdateSignaturesValidity("repo", "dig")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = dynamoWrapper.UpdateSignaturesValidity("repo", digest.FromString("dig"))
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("UpdateSignaturesValidity GetRepoMeta error", func() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+41
-70
@@ -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.NewAWSImageTrustStore(dynamoDBDriverParams.Region, dynamoDBDriverParams.Endpoint)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
dynamoDriver.SetImageTrustStore(imgTrustStore)
|
||||
|
||||
resetDynamoDBTables := func() error {
|
||||
err := dynamoDriver.ResetRepoMetaTable()
|
||||
if err != nil {
|
||||
@@ -1300,7 +1319,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)
|
||||
@@ -1326,7 +1344,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()
|
||||
@@ -1376,44 +1397,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")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = metaDB.UpdateSignaturesValidity(repo, manifestDigest) //nolint:contextcheck
|
||||
@@ -1421,6 +1416,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,
|
||||
@@ -2693,31 +2689,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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user