2023-08-19 00:52:03 -05:00
|
|
|
//go:build imagetrust
|
|
|
|
// +build imagetrust
|
|
|
|
|
|
|
|
package imagetrust
|
2023-05-24 11:46:16 -05:00
|
|
|
|
|
|
|
import (
|
2023-08-02 13:58:34 -05:00
|
|
|
"context"
|
2023-12-04 17:13:50 -05:00
|
|
|
"fmt"
|
2023-05-24 11:46:16 -05:00
|
|
|
"time"
|
|
|
|
|
2023-09-08 02:03:58 -05:00
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
2023-10-18 05:25:29 -05:00
|
|
|
"github.com/aws/aws-sdk-go-v2/aws/transport/http"
|
2023-09-08 02:03:58 -05:00
|
|
|
"github.com/aws/aws-sdk-go-v2/config"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
|
2023-10-18 05:25:29 -05:00
|
|
|
"github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
|
2023-09-08 02:03:58 -05:00
|
|
|
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"
|
2023-10-18 05:25:29 -05:00
|
|
|
smithy "github.com/aws/smithy-go"
|
2023-05-24 11:46:16 -05:00
|
|
|
godigest "github.com/opencontainers/go-digest"
|
|
|
|
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
|
|
|
|
2024-01-31 23:34:07 -05:00
|
|
|
zerr "zotregistry.dev/zot/errors"
|
|
|
|
zcommon "zotregistry.dev/zot/pkg/common"
|
|
|
|
"zotregistry.dev/zot/pkg/log"
|
|
|
|
mTypes "zotregistry.dev/zot/pkg/meta/types"
|
|
|
|
"zotregistry.dev/zot/pkg/scheduler"
|
2023-05-24 11:46:16 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2023-08-19 00:52:03 -05:00
|
|
|
defaultDirPerms = 0o700
|
|
|
|
defaultFilePerms = 0o644
|
2023-05-24 11:46:16 -05:00
|
|
|
)
|
|
|
|
|
2023-09-08 02:03:58 -05:00
|
|
|
type ImageTrustStore struct {
|
|
|
|
CosignStorage publicKeyStorage
|
|
|
|
NotationStorage certificateStorage
|
|
|
|
}
|
|
|
|
|
2023-09-11 02:13:22 -05:00
|
|
|
type SecretsManagerClient interface {
|
|
|
|
CreateSecret(ctx context.Context, params *secretsmanager.CreateSecretInput,
|
|
|
|
optFns ...func(*secretsmanager.Options)) (*secretsmanager.CreateSecretOutput, error)
|
|
|
|
DeleteSecret(ctx context.Context, params *secretsmanager.DeleteSecretInput,
|
|
|
|
optFns ...func(*secretsmanager.Options)) (*secretsmanager.DeleteSecretOutput, error)
|
|
|
|
ListSecrets(ctx context.Context, params *secretsmanager.ListSecretsInput,
|
|
|
|
optFns ...func(*secretsmanager.Options)) (*secretsmanager.ListSecretsOutput, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type SecretsManagerCache interface {
|
|
|
|
GetSecretString(secretID string) (string, error)
|
|
|
|
}
|
|
|
|
|
2023-09-08 02:03:58 -05:00
|
|
|
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 NewAWSImageTrustStore(region, endpoint string) (*ImageTrustStore, error) {
|
|
|
|
secretsManagerClient, err := GetSecretsManagerClient(region, endpoint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
secretsManagerCache := GetSecretsManagerRetrieval(region, endpoint)
|
|
|
|
|
|
|
|
publicKeyStorage := NewPublicKeyAWSStorage(secretsManagerClient, secretsManagerCache)
|
|
|
|
|
|
|
|
certStorage, err := NewCertificateAWSStorage(secretsManagerClient, secretsManagerCache)
|
2023-05-24 11:46:16 -05:00
|
|
|
if err != nil {
|
2023-09-08 02:03:58 -05:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ImageTrustStore{
|
|
|
|
CosignStorage: publicKeyStorage,
|
|
|
|
NotationStorage: certStorage,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetSecretsManagerClient(region, endpoint string) (*secretsmanager.Client, error) {
|
2024-06-13 00:51:32 -05:00
|
|
|
customResolver := aws.EndpointResolverWithOptionsFunc( //nolint: staticcheck
|
|
|
|
func(service, region string, options ...interface{}) (aws.Endpoint, error) { //nolint: staticcheck
|
|
|
|
return aws.Endpoint{ //nolint: staticcheck
|
2023-09-08 02:03:58 -05:00
|
|
|
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),
|
2024-06-13 00:51:32 -05:00
|
|
|
config.WithEndpointResolverWithOptions(customResolver)) //nolint: staticcheck
|
2023-09-08 02:03:58 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return secretsmanager.NewFromConfig(cfg), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetSecretsManagerRetrieval(region, endpoint string) *secretcache.Cache {
|
|
|
|
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,
|
2023-05-24 11:46:16 -05:00
|
|
|
}
|
|
|
|
|
2023-09-08 02:03:58 -05:00
|
|
|
// Instantiate the cache
|
|
|
|
cache, _ := secretcache.New(
|
|
|
|
func(c *secretcache.Cache) { c.CacheConfig = config },
|
|
|
|
func(c *secretcache.Cache) { c.Client = client },
|
|
|
|
)
|
2023-05-24 11:46:16 -05:00
|
|
|
|
2023-09-08 02:03:58 -05:00
|
|
|
return cache
|
2023-05-24 11:46:16 -05:00
|
|
|
}
|
|
|
|
|
2023-10-18 05:25:29 -05:00
|
|
|
func IsResourceExistsException(err error) bool {
|
|
|
|
if opErr, ok := err.(*smithy.OperationError); ok { //nolint: errorlint
|
|
|
|
if resErr, ok := opErr.Err.(*http.ResponseError); ok { //nolint: errorlint
|
|
|
|
if _, ok := resErr.Err.(*types.ResourceExistsException); ok { //nolint: errorlint
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-09-08 02:03:58 -05:00
|
|
|
func (imgTrustStore *ImageTrustStore) VerifySignature(
|
2023-10-30 15:06:04 -05:00
|
|
|
signatureType string, rawSignature []byte, sigKey string, manifestDigest godigest.Digest, imageMeta mTypes.ImageMeta,
|
2023-05-24 11:46:16 -05:00
|
|
|
repo string,
|
2024-02-14 12:08:08 -05:00
|
|
|
) (mTypes.Author, mTypes.ExpiryDate, mTypes.Validity, error) {
|
2023-05-24 11:46:16 -05:00
|
|
|
desc := ispec.Descriptor{
|
2023-10-30 15:06:04 -05:00
|
|
|
MediaType: imageMeta.MediaType,
|
|
|
|
Digest: imageMeta.Digest,
|
|
|
|
Size: imageMeta.Size,
|
2023-05-24 11:46:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if manifestDigest.String() == "" {
|
2023-12-08 03:05:02 -05:00
|
|
|
return "", time.Time{}, false, zerr.ErrBadSignatureManifestDigest
|
2023-05-24 11:46:16 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
switch signatureType {
|
2023-08-19 00:52:03 -05:00
|
|
|
case zcommon.CosignSignature:
|
2023-09-08 02:03:58 -05:00
|
|
|
author, isValid, err := VerifyCosignSignature(imgTrustStore.CosignStorage, repo, manifestDigest, sigKey, rawSignature)
|
2023-05-24 11:46:16 -05:00
|
|
|
|
|
|
|
return author, time.Time{}, isValid, err
|
2023-08-19 00:52:03 -05:00
|
|
|
case zcommon.NotationSignature:
|
2023-09-08 02:03:58 -05:00
|
|
|
return VerifyNotationSignature(imgTrustStore.NotationStorage, desc, manifestDigest.String(), rawSignature, sigKey)
|
2023-05-24 11:46:16 -05:00
|
|
|
default:
|
|
|
|
return "", time.Time{}, false, zerr.ErrInvalidSignatureType
|
|
|
|
}
|
|
|
|
}
|
2023-08-02 13:58:34 -05:00
|
|
|
|
|
|
|
func NewTaskGenerator(metaDB mTypes.MetaDB, log log.Logger) scheduler.TaskGenerator {
|
|
|
|
return &sigValidityTaskGenerator{
|
2023-10-30 15:06:04 -05:00
|
|
|
repos: []mTypes.RepoMeta{},
|
2023-08-02 13:58:34 -05:00
|
|
|
metaDB: metaDB,
|
|
|
|
repoIndex: -1,
|
|
|
|
log: log,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type sigValidityTaskGenerator struct {
|
2023-10-30 15:06:04 -05:00
|
|
|
repos []mTypes.RepoMeta
|
2023-08-02 13:58:34 -05:00
|
|
|
metaDB mTypes.MetaDB
|
|
|
|
repoIndex int
|
|
|
|
done bool
|
|
|
|
log log.Logger
|
|
|
|
}
|
|
|
|
|
2024-02-01 12:15:53 -05:00
|
|
|
func (gen *sigValidityTaskGenerator) Name() string {
|
|
|
|
return "SignatureValidationGenerator"
|
|
|
|
}
|
|
|
|
|
2023-08-02 13:58:34 -05:00
|
|
|
func (gen *sigValidityTaskGenerator) Next() (scheduler.Task, error) {
|
|
|
|
if len(gen.repos) == 0 {
|
|
|
|
ctx := context.Background()
|
|
|
|
|
2023-10-30 15:06:04 -05:00
|
|
|
repos, err := gen.metaDB.GetMultipleRepoMeta(ctx, func(repoMeta mTypes.RepoMeta) bool {
|
2023-08-02 13:58:34 -05:00
|
|
|
return true
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
gen.repos = repos
|
|
|
|
}
|
|
|
|
|
|
|
|
gen.repoIndex++
|
|
|
|
|
|
|
|
if gen.repoIndex >= len(gen.repos) {
|
|
|
|
gen.done = true
|
|
|
|
|
2023-08-07 14:55:19 -05:00
|
|
|
gen.log.Info().Msg("finished generating tasks for updating signatures validity")
|
|
|
|
|
2023-08-02 13:58:34 -05:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return NewValidityTask(gen.metaDB, gen.repos[gen.repoIndex], gen.log), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (gen *sigValidityTaskGenerator) IsDone() bool {
|
|
|
|
return gen.done
|
|
|
|
}
|
|
|
|
|
2023-08-07 14:55:19 -05:00
|
|
|
func (gen *sigValidityTaskGenerator) IsReady() bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2023-08-02 13:58:34 -05:00
|
|
|
func (gen *sigValidityTaskGenerator) Reset() {
|
|
|
|
gen.done = false
|
|
|
|
gen.repoIndex = -1
|
2023-10-30 15:06:04 -05:00
|
|
|
gen.repos = []mTypes.RepoMeta{}
|
2023-08-07 14:55:19 -05:00
|
|
|
|
|
|
|
gen.log.Info().Msg("finished resetting task generator for updating signatures validity")
|
2023-08-02 13:58:34 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
type validityTask struct {
|
|
|
|
metaDB mTypes.MetaDB
|
2023-10-30 15:06:04 -05:00
|
|
|
repo mTypes.RepoMeta
|
2023-08-02 13:58:34 -05:00
|
|
|
log log.Logger
|
|
|
|
}
|
|
|
|
|
2023-10-30 15:06:04 -05:00
|
|
|
func NewValidityTask(metaDB mTypes.MetaDB, repo mTypes.RepoMeta, log log.Logger) *validityTask {
|
2023-08-02 13:58:34 -05:00
|
|
|
return &validityTask{metaDB, repo, log}
|
|
|
|
}
|
|
|
|
|
2023-09-05 11:48:56 -05:00
|
|
|
func (validityT *validityTask) DoWork(ctx context.Context) error {
|
2023-09-13 07:48:31 -05:00
|
|
|
validityT.log.Info().Msg("update signatures validity")
|
2023-08-02 13:58:34 -05:00
|
|
|
|
|
|
|
for signedManifest, sigs := range validityT.repo.Signatures {
|
2023-11-24 03:40:10 -05:00
|
|
|
if zcommon.IsContextDone(ctx) {
|
|
|
|
return ctx.Err()
|
|
|
|
}
|
|
|
|
|
2023-08-19 00:52:03 -05:00
|
|
|
if len(sigs[zcommon.CosignSignature]) != 0 || len(sigs[zcommon.NotationSignature]) != 0 {
|
2023-11-24 03:40:10 -05:00
|
|
|
err := validityT.metaDB.UpdateSignaturesValidity(ctx, validityT.repo.Name, godigest.Digest(signedManifest))
|
2023-08-02 13:58:34 -05:00
|
|
|
if err != nil {
|
2023-12-08 03:05:02 -05:00
|
|
|
validityT.log.Info().Msg("failed to verify signatures")
|
2023-08-02 13:58:34 -05:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-13 07:48:31 -05:00
|
|
|
validityT.log.Info().Msg("update signatures validity completed")
|
2023-08-02 13:58:34 -05:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2023-12-04 17:13:50 -05:00
|
|
|
|
|
|
|
func (validityT *validityTask) String() string {
|
|
|
|
return fmt.Sprintf("{sigValidityTaskGenerator: %s, repo: %s}",
|
|
|
|
"signatures validity task", // description of generator's task purpose
|
|
|
|
validityT.repo.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (validityT *validityTask) Name() string {
|
|
|
|
return "SignatureValidityTask"
|
|
|
|
}
|