0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-03-25 02:32:57 -05:00

feat: upload cosign public key and notation certificates to cloud

Signed-off-by: Andreea-Lupu <andreealupu1470@yahoo.com>
This commit is contained in:
Andreea-Lupu 2023-07-04 15:15:54 +03:00
parent 0731fd3828
commit 5f2f8adbc3
15 changed files with 791 additions and 233 deletions

2
go.mod
View file

@ -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.16.2
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

9
go.sum
View file

@ -405,6 +405,7 @@ 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.287/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go v1.44.322 h1:7JfwifGRGQMHd99PvfXqxBaZsjuRaOF6e3X9zRx2uYo=
github.com/aws/aws-sdk-go v1.44.322/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250=
@ -466,6 +467,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.16.2 h1:3x1Qilin49XQ1rK6pDNAfG+DmCFPfB7Rrpl+FUDAR/0=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.2/go.mod h1:HEBBc70BYi5eUvxBqC3xXjU/04NO96X/XNUe5qhC7Bc=
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,6 +477,8 @@ 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=
@ -1808,6 +1813,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 +2084,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 +2095,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 +2113,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=

View file

@ -193,7 +193,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, rh.c.MetaDB, prefixedRouter, 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.

View file

@ -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, metaDB mTypes.MetaDB, router *mux.Router, 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}
sigStore, _ := metaDB.SignatureStorage().(*signatures.SigStore)
trust := ImageTrust{Conf: conf, SigStore: sigStore, 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
SigStore *signatures.SigStore
Log log.Logger
}
// Cosign handler godoc
@ -93,7 +90,7 @@ func (trust *ImageTrust) HandleCosignPublicKeyUpload(response http.ResponseWrite
return
}
err = signatures.UploadPublicKey(body)
err = signatures.UploadPublicKey(trust.SigStore.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 = signatures.UploadCertificate(body, truststoreType, truststoreName)
err = signatures.UploadCertificate(trust.SigStore.NotationStorage, body, truststoreType, truststoreName)
if err != nil {
if errors.Is(err, zerr.ErrInvalidTruststoreType) ||
errors.Is(err, zerr.ErrInvalidTruststoreName) ||
@ -178,6 +175,6 @@ func EnableImageTrustVerification(conf *config.Config, taskScheduler *scheduler.
generator := signatures.NewTaskGenerator(metaDB, log)
numberOfHours := 2
interval := time.Duration(numberOfHours) * time.Minute
interval := time.Duration(numberOfHours) * time.Hour
taskScheduler.SubmitGenerator(generator, interval, scheduler.MediumPriority)
}

View file

@ -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, metaDB mTypes.MetaDB, router *mux.Router, 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")
}

View file

@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"path/filepath"
"strings"
"time"
@ -23,9 +24,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
SigStore *signatures.SigStore
Log log.Logger
}
func New(boltDB *bbolt.DB, log log.Logger) (*BoltDB, error) {
@ -71,13 +73,25 @@ func New(boltDB *bbolt.DB, log log.Logger) (*BoltDB, error) {
return nil, err
}
dir := filepath.Dir(boltDB.Path())
sigStore, err := signatures.NewLocalSigStore(dir)
if err != nil {
return nil, err
}
return &BoltDB{
DB: boltDB,
Patches: version.GetBoltDBPatches(),
Log: log,
DB: boltDB,
Patches: version.GetBoltDBPatches(),
SigStore: sigStore,
Log: log,
}, nil
}
func (bdw *BoltDB) SignatureStorage() mTypes.SignatureStorage {
return bdw.SigStore
}
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))
@ -778,7 +792,7 @@ func (bdw *BoltDB) UpdateSignaturesValidity(repo string, manifestDigest godigest
layersInfo := []mTypes.LayerInfo{}
for _, layerInfo := range sigInfo.LayersInfo {
author, date, isTrusted, _ := signatures.VerifySignature(sigType, layerInfo.LayerContent, layerInfo.SignatureKey,
author, date, isTrusted, _ := bdw.SigStore.VerifySignature(sigType, layerInfo.LayerContent, layerInfo.SignatureKey,
manifestDigest, blob, repo)
if isTrusted {

View file

@ -36,10 +36,16 @@ type DynamoDB struct {
UserDataTablename string
VersionTablename string
Patches []func(client *dynamodb.Client, tableNames map[string]string) error
SigStore *signatures.SigStore
Log log.Logger
}
func New(client *dynamodb.Client, params DBDriverParameters, log log.Logger) (*DynamoDB, error) {
sigStore, err := signatures.NewCloudSigStore(params.Region, params.Endpoint)
if err != nil {
return nil, err
}
dynamoWrapper := DynamoDB{
Client: client,
RepoMetaTablename: params.RepoMetaTablename,
@ -49,10 +55,11 @@ func New(client *dynamodb.Client, params DBDriverParameters, log log.Logger) (*D
UserDataTablename: params.UserDataTablename,
APIKeyTablename: params.APIKeyTablename,
Patches: version.GetDynamoDBPatches(),
SigStore: sigStore,
Log: log,
}
err := dynamoWrapper.createVersionTable()
err = dynamoWrapper.createVersionTable()
if err != nil {
return nil, err
}
@ -86,6 +93,10 @@ func New(client *dynamodb.Client, params DBDriverParameters, log log.Logger) (*D
return &dynamoWrapper, nil
}
func (dwr *DynamoDB) SignatureStorage() mTypes.SignatureStorage {
return dwr.SigStore
}
func (dwr *DynamoDB) SetManifestData(manifestDigest godigest.Digest, manifestData mTypes.ManifestData) error {
mdAttributeValue, err := attributevalue.Marshal(manifestData)
if err != nil {
@ -658,7 +669,7 @@ func (dwr *DynamoDB) UpdateSignaturesValidity(repo string, manifestDigest godige
layersInfo := []mTypes.LayerInfo{}
for _, layerInfo := range sigInfo.LayersInfo {
author, date, isTrusted, _ := signatures.VerifySignature(sigType, layerInfo.LayerContent, layerInfo.SignatureKey,
author, date, isTrusted, _ := dwr.SigStore.VerifySignature(sigType, layerInfo.LayerContent, layerInfo.SignatureKey,
manifestDigest, blob, repo)
if isTrusted {

View file

@ -9,7 +9,6 @@ import (
"zotregistry.io/zot/pkg/log"
"zotregistry.io/zot/pkg/meta/boltdb"
mdynamodb "zotregistry.io/zot/pkg/meta/dynamodb"
"zotregistry.io/zot/pkg/meta/signatures"
mTypes "zotregistry.io/zot/pkg/meta/types"
)
@ -33,11 +32,6 @@ func New(storageConfig config.StorageConfig, log log.Logger) (mTypes.MetaDB, err
return nil, err
}
err = signatures.InitCosignAndNotationDirs(params.RootDir)
if err != nil {
return nil, err
}
return Create("boltdb", driver, params, log) //nolint:contextcheck
}

View file

@ -6,6 +6,7 @@ import (
"math/rand"
"os"
"path"
"path/filepath"
"testing"
"time"
@ -59,7 +60,11 @@ func TestBoltDB(t *testing.T) {
err = os.Chmod("repo.db", 0o600)
So(err, ShouldBeNil)
defer os.Remove("repo.db")
defer func() {
os.Remove("repo.db")
os.RemoveAll("_cosign")
os.RemoveAll("_notation")
}()
})
Convey("BoltDB Wrapper", t, func() {
@ -70,7 +75,13 @@ func TestBoltDB(t *testing.T) {
log := log.NewLogger("debug", "")
boltdbWrapper, err := boltdb.New(boltDriver, log)
defer os.Remove("repo.db")
defer func() {
os.Remove("repo.db")
os.RemoveAll("_cosign")
os.RemoveAll("_notation")
}()
So(boltdbWrapper, ShouldNotBeNil)
So(err, ShouldBeNil)
@ -1208,10 +1219,14 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func
})
So(err, ShouldBeNil)
err = signatures.InitNotationDir(tdir)
notationDirPath, err := metaDB.(*boltdb.BoltDB).SigStore.
NotationStorage.(*signatures.CertificateLocalStorage).GetNotationDirPath()
So(err, ShouldBeNil)
trustpolicyPath := path.Join(tdir, "_notation/trustpolicy.json")
notationDir, err := filepath.Abs(notationDirPath)
So(err, ShouldBeNil)
trustpolicyPath := path.Join(notationDir, "trustpolicy.json")
trustPolicy := `
{
@ -1239,13 +1254,13 @@ func RunMetaDBTests(t *testing.T, metaDB mTypes.MetaDB, preparationFuncs ...func
_, err = file.WriteString(trustPolicy)
So(err, ShouldBeNil)
truststore := "_notation/truststore/x509/ca/notation-sign-test"
truststore := "truststore/x509/ca/notation-sign-test"
truststoreSrc := "notation/truststore/x509/ca/notation-sign-test"
err = os.MkdirAll(path.Join(tdir, truststore), 0o755)
err = os.MkdirAll(path.Join(notationDir, truststore), 0o755)
So(err, ShouldBeNil)
err = test.CopyFile(path.Join(tdir, truststoreSrc, "notation-sign-test.crt"),
path.Join(tdir, truststore, "notation-sign-test.crt"))
path.Join(notationDir, truststore, "notation-sign-test.crt"))
So(err, ShouldBeNil)
err = metaDB.UpdateSignaturesValidity(repo, manifestDigest) //nolint:contextcheck

View file

@ -10,10 +10,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 @@ 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 {

View file

@ -1,8 +1,10 @@
package signatures
import (
"bytes"
"context"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
@ -14,6 +16,9 @@ import (
"sync"
"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"
@ -29,34 +34,73 @@ import (
const notationDirRelativePath = "_notation"
var (
notationDir = "" //nolint:gochecknoglobals
TrustpolicyLock = new(sync.Mutex) //nolint: gochecknoglobals
)
var TrustpolicyLock = new(sync.Mutex) //nolint: gochecknoglobals
func InitNotationDir(rootDir string) error {
type CertificateLocalStorage struct {
notationDir string
}
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`
@ -65,40 +109,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": [
"*"
]
}
]
}`
return notationStorage.InitTrustpolicy([]byte(trustPolicy))
}
func (local *CertificateLocalStorage) InitTrustpolicy(trustpolicy []byte) error {
TrustpolicyLock.Lock()
defer TrustpolicyLock.Unlock()
return os.WriteFile(path.Join(configDir, dir.PathTrustPolicy), []byte(trustPolicy), defaultDirPerms)
notationDir, err := local.GetNotationDirPath()
if err != nil {
return err
}
return os.WriteFile(path.Join(notationDir, dir.PathTrustPolicy), trustpolicy, defaultDirPerms)
}
func GetNotationDirPath() (string, error) {
if notationDir != "" {
return notationDir, nil
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)
return err
}
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
@ -116,33 +243,69 @@ func LoadTrustPolicyDocument(notationDir string) (*trustpolicy.Document, error)
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()
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(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
@ -158,7 +321,7 @@ func VerifyNotationSignature(
}
// Initialize verifier.
verifier, err := NewFromConfig()
verifier, err := NewFromConfig(notationStorage)
if err != nil {
return author, date, false, err
}
@ -216,7 +379,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
@ -228,35 +393,22 @@ 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
}
@ -276,7 +428,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 {
@ -294,7 +506,7 @@ func validateTruststoreName(truststoreName string) bool {
}
// 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)
@ -302,7 +514,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...)
@ -311,7 +523,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)
@ -319,9 +531,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
}

View file

@ -5,6 +5,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"
@ -21,18 +29,106 @@ const (
defaultFilePerms = 0o644
)
func InitCosignAndNotationDirs(rootDir string) error {
err := InitCosignDir(rootDir)
if err != nil {
return err
}
err = InitNotationDir(rootDir)
return err
type SigStore struct {
CosignStorage publicKeyStorage
NotationStorage certificateStorage
}
func VerifySignature(
func NewLocalSigStore(rootDir string) (*SigStore, error) {
publicKeyStorage, err := NewPublicKeyLocalStorage(rootDir)
if err != nil {
return nil, err
}
certStorage, err := NewCertificateLocalStorage(rootDir)
if err != nil {
return nil, err
}
return &SigStore{
CosignStorage: publicKeyStorage,
NotationStorage: certStorage,
}, nil
}
func NewCloudSigStore(region, endpoint string) (*SigStore, 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 &SigStore{
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 (sigStore *SigStore) VerifySignature(
signatureType string, rawSignature []byte, sigKey string, manifestDigest godigest.Digest, manifestContent []byte,
repo string,
) (string, time.Time, bool, error) {
@ -53,11 +149,11 @@ func VerifySignature(
switch signatureType {
case CosignSignature:
author, isValid, err := VerifyCosignSignature(repo, manifestDigest, sigKey, rawSignature)
author, isValid, err := VerifyCosignSignature(sigStore.CosignStorage, repo, manifestDigest, sigKey, rawSignature)
return author, time.Time{}, isValid, err
case NotationSignature:
return VerifyNotationSignature(desc, manifestDigest.String(), rawSignature, sigKey)
return VerifyNotationSignature(sigStore.NotationStorage, desc, manifestDigest.String(), rawSignature, sigKey)
default:
return "", time.Time{}, false, zerr.ErrInvalidSignatureType
}

View file

@ -33,16 +33,17 @@ func TestInitCosignAndNotationDirs(t *testing.T) {
err := os.Chmod(dir, 0o000)
So(err, ShouldBeNil)
err = signatures.InitCosignAndNotationDirs(dir)
_, err = signatures.NewPublicKeyLocalStorage(dir)
So(err, ShouldNotBeNil)
err = os.Chmod(dir, 0o500)
So(err, ShouldBeNil)
err = signatures.InitCosignAndNotationDirs(dir)
_, err = signatures.NewPublicKeyLocalStorage(dir)
So(err, ShouldNotBeNil)
cosignDir, err := signatures.GetCosignDirPath()
pubKeyStorage := &signatures.PublicKeyLocalStorage{}
cosignDir, err := pubKeyStorage.GetCosignDirPath()
So(cosignDir, ShouldBeEmpty)
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
@ -53,22 +54,23 @@ func TestInitCosignAndNotationDirs(t *testing.T) {
err := os.Chmod(dir, 0o000)
So(err, ShouldBeNil)
err = signatures.InitCosignAndNotationDirs(dir)
_, err = signatures.NewPublicKeyLocalStorage(dir)
So(err, ShouldNotBeNil)
err = signatures.InitNotationDir(dir)
_, err = signatures.NewCertificateLocalStorage(dir)
So(err, ShouldNotBeNil)
err = os.Chmod(dir, 0o500)
So(err, ShouldBeNil)
err = signatures.InitCosignAndNotationDirs(dir)
_, err = signatures.NewPublicKeyLocalStorage(dir)
So(err, ShouldNotBeNil)
err = signatures.InitNotationDir(dir)
_, err = signatures.NewCertificateLocalStorage(dir)
So(err, ShouldNotBeNil)
notationDir, err := signatures.GetNotationDirPath()
certStorgae := &signatures.CertificateLocalStorage{}
notationDir, err := certStorgae.GetNotationDirPath()
So(notationDir, ShouldBeEmpty)
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
@ -90,7 +92,8 @@ func TestInitCosignAndNotationDirs(t *testing.T) {
So(err, ShouldBeNil)
So(certificateContent, ShouldNotBeNil)
err = signatures.UploadCertificate(certificateContent, "ca", "notation-upload-test")
certStorgae := &signatures.CertificateLocalStorage{}
err = signatures.UploadCertificate(certStorgae, certificateContent, "ca", "notation-upload-test")
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
})
@ -114,7 +117,8 @@ func TestInitCosignAndNotationDirs(t *testing.T) {
So(err, ShouldBeNil)
So(publicKeyContent, ShouldNotBeNil)
err = signatures.UploadPublicKey(publicKeyContent)
pubKeyStorage := &signatures.PublicKeyLocalStorage{}
err = signatures.UploadPublicKey(pubKeyStorage, publicKeyContent)
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
})
@ -124,7 +128,8 @@ func TestVerifySignatures(t *testing.T) {
Convey("wrong manifest content", t, func() {
manifestContent := []byte("wrong json")
_, _, _, err := signatures.VerifySignature("", []byte(""), "", "", manifestContent, "repo")
sigStore := &signatures.SigStore{}
_, _, _, err := sigStore.VerifySignature("", []byte(""), "", "", manifestContent, "repo")
So(err, ShouldNotBeNil)
})
@ -135,7 +140,8 @@ func TestVerifySignatures(t *testing.T) {
manifestContent, err := json.Marshal(image.Manifest)
So(err, ShouldBeNil)
_, _, _, err = signatures.VerifySignature("", []byte(""), "", "", manifestContent, "repo")
sigStore := &signatures.SigStore{}
_, _, _, err = sigStore.VerifySignature("", []byte(""), "", "", manifestContent, "repo")
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrBadManifestDigest)
})
@ -149,7 +155,8 @@ func TestVerifySignatures(t *testing.T) {
manifestDigest := image.Digest()
_, _, _, err = signatures.VerifySignature("wrongType", []byte(""), "", manifestDigest, manifestContent, "repo")
sigStore := &signatures.SigStore{}
_, _, _, err = sigStore.VerifySignature("wrongType", []byte(""), "", manifestDigest, manifestContent, "repo")
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrInvalidSignatureType)
})
@ -166,7 +173,11 @@ func TestVerifySignatures(t *testing.T) {
manifestDigest := image.Digest()
Convey("cosignDir is not set", func() {
_, _, _, err = signatures.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
sigStore := &signatures.SigStore{
CosignStorage: &signatures.PublicKeyLocalStorage{},
}
_, _, _, err = sigStore.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
})
@ -174,31 +185,39 @@ func TestVerifySignatures(t *testing.T) {
Convey("cosignDir does not have read permissions", func() {
dir := t.TempDir()
err := signatures.InitCosignDir(dir)
pubKeyStorage, err := signatures.NewPublicKeyLocalStorage(dir)
So(err, ShouldBeNil)
cosignDir, err := signatures.GetCosignDirPath()
cosignDir, err := pubKeyStorage.GetCosignDirPath()
So(err, ShouldBeNil)
err = os.Chmod(cosignDir, 0o300)
So(err, ShouldBeNil)
_, _, _, err = signatures.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
sigStore := &signatures.SigStore{
CosignStorage: pubKeyStorage,
}
_, _, _, err = sigStore.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
So(err, ShouldNotBeNil)
})
Convey("no valid public key", func() {
dir := t.TempDir()
err := signatures.InitCosignDir(dir)
pubKeyStorage, err := signatures.NewPublicKeyLocalStorage(dir)
So(err, ShouldBeNil)
cosignDir, err := signatures.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 := signatures.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
sigStore := &signatures.SigStore{
CosignStorage: pubKeyStorage,
}
_, _, isTrusted, err := sigStore.VerifySignature("cosign", []byte(""), "", manifestDigest, manifestContent, repo)
So(err, ShouldBeNil)
So(isTrusted, ShouldBeFalse)
})
@ -221,10 +240,10 @@ func TestVerifySignatures(t *testing.T) {
err := test.UploadImage(image, baseURL, repo, tag)
So(err, ShouldBeNil)
err = signatures.InitCosignDir(rootDir)
pubKeyStorage, err := signatures.NewPublicKeyLocalStorage(rootDir)
So(err, ShouldBeNil)
cosignDir, err := signatures.GetCosignDirPath()
cosignDir, err := pubKeyStorage.GetCosignDirPath()
So(err, ShouldBeNil)
cwd, err := os.Getwd()
@ -280,8 +299,12 @@ func TestVerifySignatures(t *testing.T) {
}
}
sigStore := &signatures.SigStore{
CosignStorage: pubKeyStorage,
}
// signature is trusted
author, _, isTrusted, err := signatures.VerifySignature("cosign", rawSignature, sigKey, manifestDigest,
author, _, isTrusted, err := sigStore.VerifySignature("cosign", rawSignature, sigKey, manifestDigest,
manifestContent, repo)
So(err, ShouldBeNil)
So(isTrusted, ShouldBeTrue)
@ -301,7 +324,11 @@ func TestVerifySignatures(t *testing.T) {
manifestDigest := image.Digest()
Convey("notationDir is not set", func() {
_, _, _, err = signatures.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent, repo)
sigStore := &signatures.SigStore{
NotationStorage: &signatures.CertificateLocalStorage{},
}
_, _, _, err = sigStore.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent, repo)
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrSignConfigDirNotSet)
})
@ -309,10 +336,14 @@ func TestVerifySignatures(t *testing.T) {
Convey("no signature provided", func() {
dir := t.TempDir()
err := signatures.InitNotationDir(dir)
certStorage, err := signatures.NewCertificateLocalStorage(dir)
So(err, ShouldBeNil)
_, _, isTrusted, err := signatures.VerifySignature("notation", []byte(""), "", manifestDigest, manifestContent, repo)
sigStore := &signatures.SigStore{
NotationStorage: certStorage,
}
_, _, isTrusted, err := sigStore.VerifySignature("notation", []byte(""), "", manifestDigest, manifestContent, repo)
So(err, ShouldNotBeNil)
So(isTrusted, ShouldBeFalse)
})
@ -320,32 +351,40 @@ func TestVerifySignatures(t *testing.T) {
Convey("trustpolicy.json does not exist", func() {
dir := t.TempDir()
err := signatures.InitNotationDir(dir)
certStorage, err := signatures.NewCertificateLocalStorage(dir)
So(err, ShouldBeNil)
notationDir, _ := signatures.GetNotationDirPath()
notationDir, _ := certStorage.GetNotationDirPath()
err = os.Remove(path.Join(notationDir, "trustpolicy.json"))
So(err, ShouldBeNil)
_, _, _, err = signatures.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent, repo)
sigStore := &signatures.SigStore{
NotationStorage: certStorage,
}
_, _, _, err = sigStore.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent, repo)
So(err, ShouldNotBeNil)
})
Convey("trustpolicy.json has invalid content", func() {
dir := t.TempDir()
err := signatures.InitNotationDir(dir)
certStorage, err := signatures.NewCertificateLocalStorage(dir)
So(err, ShouldBeNil)
notationDir, err := signatures.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 = signatures.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent,
sigStore := &signatures.SigStore{
NotationStorage: certStorage,
}
_, _, _, err = sigStore.VerifySignature("notation", []byte("signature"), "", manifestDigest, manifestContent,
repo)
So(err, ShouldNotBeNil)
})
@ -368,10 +407,10 @@ func TestVerifySignatures(t *testing.T) {
err := test.UploadImage(image, baseURL, repo, tag)
So(err, ShouldBeNil)
err = signatures.InitNotationDir(rootDir)
certStorage, err := signatures.NewCertificateLocalStorage(rootDir)
So(err, ShouldBeNil)
notationDir, err := signatures.GetNotationDirPath()
notationDir, err := certStorage.GetNotationDirPath()
So(err, ShouldBeNil)
test.NotationPathLock.Lock()
@ -443,8 +482,12 @@ func TestVerifySignatures(t *testing.T) {
}
}
sigStore := &signatures.SigStore{
NotationStorage: certStorage,
}
// signature is trusted
author, _, isTrusted, err := signatures.VerifySignature("notation", rawSignature, sigKey, manifestDigest,
author, _, isTrusted, err := sigStore.VerifySignature("notation", rawSignature, sigKey, manifestDigest,
manifestContent, repo)
So(err, ShouldBeNil)
So(isTrusted, ShouldBeTrue)
@ -454,7 +497,7 @@ func TestVerifySignatures(t *testing.T) {
So(err, ShouldBeNil)
// signature is not trusted
author, _, isTrusted, err = signatures.VerifySignature("notation", rawSignature, sigKey, manifestDigest,
author, _, isTrusted, err = sigStore.VerifySignature("notation", rawSignature, sigKey, manifestDigest,
manifestContent, repo)
So(err, ShouldNotBeNil)
So(isTrusted, ShouldBeFalse)
@ -490,7 +533,11 @@ func TestCheckExpiryErr(t *testing.T) {
func TestUploadPublicKey(t *testing.T) {
Convey("public key - invalid content", t, func() {
err := signatures.UploadPublicKey([]byte("wrong content"))
dir := t.TempDir()
pubKeyStorage, err := signatures.NewPublicKeyLocalStorage(dir)
So(err, ShouldBeNil)
err = signatures.UploadPublicKey(pubKeyStorage, []byte("wrong content"))
So(err, ShouldNotBeNil)
})
@ -513,41 +560,53 @@ func TestUploadPublicKey(t *testing.T) {
So(err, ShouldBeNil)
So(publicKeyContent, ShouldNotBeNil)
err = signatures.InitCosignDir(rootDir)
pubKeyStorage, err := signatures.NewPublicKeyLocalStorage(rootDir)
So(err, ShouldBeNil)
err = signatures.UploadPublicKey(publicKeyContent)
err = signatures.UploadPublicKey(pubKeyStorage, publicKeyContent)
So(err, ShouldBeNil)
})
}
func TestUploadCertificate(t *testing.T) {
Convey("invalid truststore type", t, func() {
err := signatures.UploadCertificate([]byte("certificate content"), "wrongType", "store")
dir := t.TempDir()
certStorage, err := signatures.NewCertificateLocalStorage(dir)
So(err, ShouldBeNil)
err = signatures.UploadCertificate(certStorage, []byte("certificate content"), "wrongType", "store")
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrInvalidTruststoreType)
})
Convey("invalid truststore name", t, func() {
err := signatures.UploadCertificate([]byte("certificate content"), "ca", "*store?")
dir := t.TempDir()
certStorage, err := signatures.NewCertificateLocalStorage(dir)
So(err, ShouldBeNil)
err = signatures.UploadCertificate(certStorage, []byte("certificate content"), "ca", "*store?")
So(err, ShouldNotBeNil)
So(err, ShouldEqual, zerr.ErrInvalidTruststoreName)
})
Convey("invalid certificate content", t, func() {
err := signatures.UploadCertificate([]byte("invalid content"), "ca", "store")
dir := t.TempDir()
certStorage, err := signatures.NewCertificateLocalStorage(dir)
So(err, ShouldBeNil)
err = signatures.UploadCertificate(certStorage, []byte("invalid content"), "ca", "store")
So(err, ShouldNotBeNil)
content := `-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
`
err = signatures.UploadCertificate([]byte(content), "ca", "store")
err = signatures.UploadCertificate(certStorage, []byte(content), "ca", "store")
So(err, ShouldNotBeNil)
content = ``
err = signatures.UploadCertificate([]byte(content), "ca", "store")
err = signatures.UploadCertificate(certStorage, []byte(content), "ca", "store")
So(err, ShouldNotBeNil)
})
@ -567,16 +626,16 @@ func TestUploadCertificate(t *testing.T) {
So(err, ShouldBeNil)
So(certificateContent, ShouldNotBeNil)
err = signatures.InitNotationDir(rootDir)
certStorage, err := signatures.NewCertificateLocalStorage(rootDir)
So(err, ShouldBeNil)
notationDir, err := signatures.GetNotationDirPath()
notationDir, err := certStorage.GetNotationDirPath()
So(err, ShouldBeNil)
err = os.Chmod(notationDir, 0o100)
So(err, ShouldBeNil)
err = signatures.UploadCertificate(certificateContent, "ca", "notation-upload-test")
err = signatures.UploadCertificate(certStorage, certificateContent, "ca", "notation-upload-test")
So(err, ShouldNotBeNil)
err = os.Chmod(notationDir, 0o777)
@ -599,10 +658,10 @@ func TestUploadCertificate(t *testing.T) {
So(err, ShouldBeNil)
So(certificateContent, ShouldNotBeNil)
err = signatures.InitNotationDir(rootDir)
certStorage, err := signatures.NewCertificateLocalStorage(rootDir)
So(err, ShouldBeNil)
notationDir, err := signatures.GetNotationDirPath()
notationDir, err := certStorage.GetNotationDirPath()
So(err, ShouldBeNil)
err = os.MkdirAll(path.Join(notationDir, "truststore/x509/ca/notation-upload-test"), 0o777)
@ -611,7 +670,7 @@ func TestUploadCertificate(t *testing.T) {
err = os.Chmod(path.Join(notationDir, "truststore/x509/ca/notation-upload-test"), 0o100)
So(err, ShouldBeNil)
err = signatures.UploadCertificate(certificateContent, "ca", "notation-upload-test")
err = signatures.UploadCertificate(certStorage, certificateContent, "ca", "notation-upload-test")
So(err, ShouldNotBeNil)
})
@ -631,17 +690,17 @@ func TestUploadCertificate(t *testing.T) {
So(err, ShouldBeNil)
So(certificateContent, ShouldNotBeNil)
err = signatures.InitNotationDir(rootDir)
certStorage, err := signatures.NewCertificateLocalStorage(rootDir)
So(err, ShouldBeNil)
notationDir, err := signatures.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 = signatures.UploadCertificate(certificateContent, "ca", "notation-upload-test")
err = signatures.UploadCertificate(certStorage, certificateContent, "ca", "notation-upload-test")
So(err, ShouldNotBeNil)
})
@ -661,13 +720,13 @@ func TestUploadCertificate(t *testing.T) {
So(err, ShouldBeNil)
So(certificateContent, ShouldNotBeNil)
err = signatures.InitNotationDir(rootDir)
certStorage, err := signatures.NewCertificateLocalStorage(rootDir)
So(err, ShouldBeNil)
notationDir, err := signatures.GetNotationDirPath()
notationDir, err := certStorage.GetNotationDirPath()
So(err, ShouldBeNil)
trustpolicyDoc, err := signatures.LoadTrustPolicyDocument(notationDir)
trustpolicyDoc, err := certStorage.LoadTrustPolicyDocument()
So(err, ShouldBeNil)
trustpolicyDoc.TrustPolicies[0].TrustStores = append(trustpolicyDoc.TrustPolicies[0].TrustStores,
@ -679,7 +738,7 @@ func TestUploadCertificate(t *testing.T) {
err = os.WriteFile(path.Join(notationDir, "trustpolicy.json"), trustpolicyDocContent, 0o400)
So(err, ShouldBeNil)
err = signatures.UploadCertificate(certificateContent, "ca", "notation-upload-test")
err = signatures.UploadCertificate(certStorage, certificateContent, "ca", "notation-upload-test")
So(err, ShouldBeNil)
})
@ -699,10 +758,10 @@ func TestUploadCertificate(t *testing.T) {
So(err, ShouldBeNil)
So(certificateContent, ShouldNotBeNil)
err = signatures.InitNotationDir(rootDir)
certStorage, err := signatures.NewCertificateLocalStorage(rootDir)
So(err, ShouldBeNil)
err = signatures.UploadCertificate(certificateContent, "ca", "notation-upload-test")
err = signatures.UploadCertificate(certStorage, certificateContent, "ca", "notation-upload-test")
So(err, ShouldBeNil)
})
}

View file

@ -121,6 +121,8 @@ type MetaDB interface { //nolint:interfacebloat
[]RepoMetadata, map[string]ManifestMetadata, map[string]IndexData, error)
PatchDB() error
SignatureStorage() SignatureStorage
}
type UserDB interface { //nolint:interfacebloat
@ -156,6 +158,13 @@ type UserDB interface { //nolint:interfacebloat
DeleteUserAPIKey(ctx context.Context, id string) error
}
type SignatureStorage 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

View file

@ -102,6 +102,16 @@ type MetaDBMock struct {
DeleteUserAPIKeyFn func(ctx context.Context, id string) error
PatchDBFn func() error
SignatureStorageFn func() mTypes.SignatureStorage
}
func (sdm MetaDBMock) SignatureStorage() mTypes.SignatureStorage {
if sdm.SignatureStorageFn != nil {
return sdm.SignatureStorageFn()
}
return nil
}
func (sdm MetaDBMock) SetRepoDescription(repo, description string) error {