mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 04:17:55 +08:00
fix: call notation-go libs instead of using notation binary (#1104)
fix: add loading notation path Signed-off-by: Andreea-Lupu <andreealupu1470@yahoo.com> Co-authored-by: Roxana Nemulescu <roxana.nemulescu@gmail.com>
This commit is contained in:
+499
-14
@@ -4,20 +4,34 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/notaryproject/notation-core-go/signature/jws"
|
||||
"github.com/notaryproject/notation-core-go/testhelper"
|
||||
"github.com/notaryproject/notation-go"
|
||||
notconfig "github.com/notaryproject/notation-go/config"
|
||||
"github.com/notaryproject/notation-go/dir"
|
||||
notreg "github.com/notaryproject/notation-go/registry"
|
||||
"github.com/notaryproject/notation-go/signer"
|
||||
"github.com/notaryproject/notation-go/verifier"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
@@ -27,6 +41,9 @@ import (
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/options"
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
|
||||
"gopkg.in/resty.v1"
|
||||
"oras.land/oras-go/v2/registry"
|
||||
"oras.land/oras-go/v2/registry/remote"
|
||||
"oras.land/oras-go/v2/registry/remote/auth"
|
||||
|
||||
"zotregistry.io/zot/pkg/storage"
|
||||
)
|
||||
@@ -37,6 +54,8 @@ const (
|
||||
SleepTime = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
var NotationPathLock = new(sync.Mutex) //nolint: gochecknoglobals
|
||||
|
||||
// which: manifest, config, layer
|
||||
func GetTestBlobDigest(image, which string) godigest.Digest {
|
||||
prePath := "../test/data"
|
||||
@@ -61,8 +80,11 @@ func GetTestBlobDigest(image, which string) godigest.Digest {
|
||||
}
|
||||
|
||||
var (
|
||||
ErrPostBlob = errors.New("can't post blob")
|
||||
ErrPutBlob = errors.New("can't put blob")
|
||||
ErrPostBlob = errors.New("can't post blob")
|
||||
ErrPutBlob = errors.New("can't put blob")
|
||||
ErrAlreadyExists = errors.New("already exists")
|
||||
ErrKeyNotFound = errors.New("key not found")
|
||||
ErrSignatureVerification = errors.New("signature verification failed")
|
||||
)
|
||||
|
||||
type Image struct {
|
||||
@@ -667,11 +689,15 @@ func UploadImage(img Image, baseURL, repo string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = resty.R().
|
||||
resp, err = resty.R().
|
||||
SetHeader("Content-type", "application/vnd.oci.image.manifest.v1+json").
|
||||
SetBody(manifestBlob).
|
||||
Put(baseURL + "/v2/" + repo + "/manifests/" + img.Tag)
|
||||
|
||||
if ErrStatusCode(resp.StatusCode()) != http.StatusCreated {
|
||||
return ErrPutBlob
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -745,6 +771,469 @@ func ReadLogFileAndSearchString(logPath string, stringToMatch string, timeout ti
|
||||
}
|
||||
}
|
||||
|
||||
func CopyFile(sourceFilePath, destFilePath string) error {
|
||||
destFile, err := os.Create(destFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destFile.Close()
|
||||
|
||||
sourceFile, err := os.Open(sourceFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
if _, err = io.Copy(destFile, sourceFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadNotationPath(tdir string) {
|
||||
dir.UserConfigDir = filepath.Join(tdir, "notation")
|
||||
|
||||
// set user libexec
|
||||
dir.UserLibexecDir = dir.UserConfigDir
|
||||
}
|
||||
|
||||
func GenerateNotationCerts(tdir string, certName string) error {
|
||||
// generate RSA private key
|
||||
bits := 2048
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, bits)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyBytes, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})
|
||||
|
||||
rsaCertTuple := testhelper.GetRSASelfSignedCertTupleWithPK(key, "cert")
|
||||
|
||||
certBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: rsaCertTuple.Cert.Raw})
|
||||
|
||||
// write private key
|
||||
relativeKeyPath, relativeCertPath := dir.LocalKeyPath(certName)
|
||||
|
||||
configFS := dir.ConfigFS()
|
||||
|
||||
keyPath, err := configFS.SysPath(relativeKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certPath, err := configFS.SysPath(relativeCertPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := WriteFileWithPermission(keyPath, keyPEM, 0o600, false); err != nil { //nolint:gomnd
|
||||
return fmt.Errorf("failed to write key file: %w", err)
|
||||
}
|
||||
|
||||
// write self-signed certificate
|
||||
if err := WriteFileWithPermission(certPath, certBytes, 0o644, false); err != nil { //nolint:gomnd
|
||||
return fmt.Errorf("failed to write certificate file: %w", err)
|
||||
}
|
||||
|
||||
signingKeys, err := notconfig.LoadSigningKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keySuite := notconfig.KeySuite{
|
||||
Name: certName,
|
||||
X509KeyPair: ¬config.X509KeyPair{
|
||||
KeyPath: keyPath,
|
||||
CertificatePath: certPath,
|
||||
},
|
||||
}
|
||||
|
||||
// addKeyToSigningKeys
|
||||
if Contains(signingKeys.Keys, keySuite.Name) {
|
||||
return ErrAlreadyExists
|
||||
}
|
||||
|
||||
signingKeys.Keys = append(signingKeys.Keys, keySuite)
|
||||
|
||||
// Add to the trust store
|
||||
trustStorePath := path.Join(tdir, fmt.Sprintf("notation/truststore/x509/ca/%s", certName))
|
||||
|
||||
if _, err := os.Stat(filepath.Join(trustStorePath, filepath.Base(certPath))); err == nil {
|
||||
return ErrAlreadyExists
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(trustStorePath, 0o755); err != nil { //nolint:gomnd
|
||||
return fmt.Errorf("GenerateNotationCerts os.MkdirAll failed: %w", err)
|
||||
}
|
||||
|
||||
trustCertPath := path.Join(trustStorePath, fmt.Sprintf("%s%s", certName, dir.LocalCertificateExtension))
|
||||
|
||||
err = CopyFile(certPath, trustCertPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Save to the SigningKeys.json
|
||||
if err := signingKeys.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SignWithNotation(keyName string, reference string, tdir string) error {
|
||||
ctx := context.TODO()
|
||||
|
||||
// getSigner
|
||||
var newSigner notation.Signer
|
||||
|
||||
mediaType := jws.MediaTypeEnvelope
|
||||
|
||||
// ResolveKey
|
||||
signingKeys, err := LoadNotationSigningkeys(tdir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
idx := Index(signingKeys.Keys, keyName)
|
||||
if idx < 0 {
|
||||
return ErrKeyNotFound
|
||||
}
|
||||
|
||||
key := signingKeys.Keys[idx]
|
||||
|
||||
if key.X509KeyPair != nil {
|
||||
newSigner, err = signer.NewFromFiles(key.X509KeyPair.KeyPath, key.X509KeyPair.CertificatePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// prepareSigningContent
|
||||
// getRepositoryClient
|
||||
authClient := &auth.Client{
|
||||
Credential: func(ctx context.Context, reg string) (auth.Credential, error) {
|
||||
return auth.EmptyCredential, nil
|
||||
},
|
||||
Cache: auth.NewCache(),
|
||||
ClientID: "notation",
|
||||
}
|
||||
|
||||
authClient.SetUserAgent("notation/zot_tests")
|
||||
|
||||
plainHTTP := true
|
||||
|
||||
// Resolve referance
|
||||
ref, err := registry.ParseReference(reference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteRepo := &remote.Repository{
|
||||
Client: authClient,
|
||||
Reference: ref,
|
||||
PlainHTTP: plainHTTP,
|
||||
}
|
||||
|
||||
sigRepo := notreg.NewRepository(remoteRepo)
|
||||
|
||||
sigOpts := notation.SignOptions{
|
||||
ArtifactReference: ref.String(),
|
||||
SignatureMediaType: mediaType,
|
||||
PluginConfig: map[string]string{},
|
||||
}
|
||||
|
||||
_, err = notation.Sign(ctx, newSigner, sigRepo, sigOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func VerifyWithNotation(reference string, tdir string) error {
|
||||
// check if trustpolicy.json exists
|
||||
trustpolicyPath := path.Join(tdir, "notation/trustpolicy.json")
|
||||
|
||||
if _, err := os.Stat(trustpolicyPath); errors.Is(err, os.ErrNotExist) {
|
||||
trustPolicy := `
|
||||
{
|
||||
"version": "1.0",
|
||||
"trustPolicies": [
|
||||
{
|
||||
"name": "good",
|
||||
"registryScopes": [ "*" ],
|
||||
"signatureVerification": {
|
||||
"level" : "audit"
|
||||
},
|
||||
"trustStores": ["ca:good"],
|
||||
"trustedIdentities": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
file, err := os.Create(trustpolicyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.WriteString(trustPolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// start verifying signatures
|
||||
ctx := context.TODO()
|
||||
|
||||
// getRepositoryClient
|
||||
authClient := &auth.Client{
|
||||
Credential: func(ctx context.Context, reg string) (auth.Credential, error) {
|
||||
return auth.EmptyCredential, nil
|
||||
},
|
||||
Cache: auth.NewCache(),
|
||||
ClientID: "notation",
|
||||
}
|
||||
|
||||
authClient.SetUserAgent("notation/zot_tests")
|
||||
|
||||
plainHTTP := true
|
||||
|
||||
// Resolve referance
|
||||
ref, err := registry.ParseReference(reference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteRepo := &remote.Repository{
|
||||
Client: authClient,
|
||||
Reference: ref,
|
||||
PlainHTTP: plainHTTP,
|
||||
}
|
||||
|
||||
repo := notreg.NewRepository(remoteRepo)
|
||||
|
||||
manifestDesc, err := repo.Resolve(ctx, ref.Reference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ref.ValidateReferenceAsDigest(); err != nil {
|
||||
ref.Reference = manifestDesc.Digest.String()
|
||||
}
|
||||
|
||||
// getVerifier
|
||||
newVerifier, err := verifier.NewFromConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remoteRepo = &remote.Repository{
|
||||
Client: authClient,
|
||||
Reference: ref,
|
||||
PlainHTTP: plainHTTP,
|
||||
}
|
||||
|
||||
repo = notreg.NewRepository(remoteRepo)
|
||||
|
||||
configs := map[string]string{}
|
||||
|
||||
verifyOpts := notation.RemoteVerifyOptions{
|
||||
ArtifactReference: ref.String(),
|
||||
PluginConfig: configs,
|
||||
MaxSignatureAttempts: math.MaxInt64,
|
||||
}
|
||||
|
||||
_, outcomes, err := notation.Verify(ctx, newVerifier, repo, verifyOpts)
|
||||
if err != nil || len(outcomes) == 0 {
|
||||
return ErrSignatureVerification
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ListNotarySignatures(reference string, tdir string) ([]godigest.Digest, error) {
|
||||
signatures := []godigest.Digest{}
|
||||
|
||||
ctx := context.TODO()
|
||||
|
||||
// getSignatureRepository
|
||||
ref, err := registry.ParseReference(reference)
|
||||
if err != nil {
|
||||
return signatures, err
|
||||
}
|
||||
|
||||
plainHTTP := true
|
||||
|
||||
// getRepositoryClient
|
||||
authClient := &auth.Client{
|
||||
Credential: func(ctx context.Context, registry string) (auth.Credential, error) {
|
||||
return auth.EmptyCredential, nil
|
||||
},
|
||||
Cache: auth.NewCache(),
|
||||
ClientID: "notation",
|
||||
}
|
||||
|
||||
authClient.SetUserAgent("notation/zot_tests")
|
||||
|
||||
remoteRepo := &remote.Repository{
|
||||
Client: authClient,
|
||||
Reference: ref,
|
||||
PlainHTTP: plainHTTP,
|
||||
}
|
||||
|
||||
sigRepo := notreg.NewRepository(remoteRepo)
|
||||
|
||||
artifectDesc, err := sigRepo.Resolve(ctx, reference)
|
||||
if err != nil {
|
||||
return signatures, err
|
||||
}
|
||||
|
||||
err = sigRepo.ListSignatures(ctx, artifectDesc, func(signatureManifests []ispec.Descriptor) error {
|
||||
for _, sigManifestDesc := range signatureManifests {
|
||||
signatures = append(signatures, sigManifestDesc.Digest)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return signatures, err
|
||||
}
|
||||
|
||||
func LoadNotationSigningkeys(tdir string) (*notconfig.SigningKeys, error) {
|
||||
var err error
|
||||
|
||||
var signingKeysInfo *notconfig.SigningKeys
|
||||
|
||||
filePath := path.Join(tdir, "notation/signingkeys.json")
|
||||
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
// create file
|
||||
newSigningKeys := notconfig.NewSigningKeys()
|
||||
|
||||
newFile, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return newSigningKeys, err
|
||||
}
|
||||
|
||||
defer newFile.Close()
|
||||
|
||||
encoder := json.NewEncoder(newFile)
|
||||
encoder.SetIndent("", " ")
|
||||
|
||||
err = encoder.Encode(newSigningKeys)
|
||||
|
||||
return newSigningKeys, err
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
err = json.NewDecoder(file).Decode(&signingKeysInfo)
|
||||
|
||||
return signingKeysInfo, err
|
||||
}
|
||||
|
||||
func LoadNotationConfig(tdir string) (*notconfig.Config, error) {
|
||||
var configInfo *notconfig.Config
|
||||
|
||||
filePath := path.Join(tdir, "notation/signingkeys.json")
|
||||
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return configInfo, err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
err = json.NewDecoder(file).Decode(&configInfo)
|
||||
if err != nil {
|
||||
return configInfo, err
|
||||
}
|
||||
|
||||
// set default value
|
||||
configInfo.SignatureFormat = strings.ToLower(configInfo.SignatureFormat)
|
||||
if configInfo.SignatureFormat == "" {
|
||||
configInfo.SignatureFormat = "jws"
|
||||
}
|
||||
|
||||
return configInfo, nil
|
||||
}
|
||||
|
||||
func WriteFileWithPermission(path string, data []byte, perm fs.FileMode, overwrite bool) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
flag := os.O_WRONLY | os.O_CREATE
|
||||
|
||||
if overwrite {
|
||||
flag |= os.O_TRUNC
|
||||
} else {
|
||||
flag |= os.O_EXCL
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(path, flag, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = file.Write(data)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return file.Close()
|
||||
}
|
||||
|
||||
func IsDigestReference(ref string) bool {
|
||||
parts := strings.SplitN(ref, "/", 2) //nolint:gomnd
|
||||
if len(parts) == 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
index := strings.Index(parts[1], "@")
|
||||
|
||||
return index != -1
|
||||
}
|
||||
|
||||
type isser interface {
|
||||
Is(string) bool
|
||||
}
|
||||
|
||||
// Index returns the index of the first occurrence of name in s,
|
||||
// or -1 if not present.
|
||||
func Index[E isser](s []E, name string) int {
|
||||
for i, v := range s {
|
||||
if v.Is(name) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// Contains reports whether name is present in s.
|
||||
func Contains[E isser](s []E, name string) bool {
|
||||
return Index(s, name) >= 0
|
||||
}
|
||||
|
||||
func UploadImageWithBasicAuth(img Image, baseURL, repo, user, password string) error {
|
||||
for _, blob := range img.Layers {
|
||||
resp, err := resty.R().
|
||||
@@ -883,17 +1372,13 @@ func SignImageUsingNotary(repoTag, port string) error {
|
||||
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
_, err = exec.LookPath("notation")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
NotationPathLock.Lock()
|
||||
defer NotationPathLock.Unlock()
|
||||
|
||||
os.Setenv("XDG_CONFIG_HOME", tdir)
|
||||
LoadNotationPath(tdir)
|
||||
|
||||
// generate a keypair
|
||||
cmd := exec.Command("notation", "cert", "generate-test", "--trust", "notation-sign-test")
|
||||
|
||||
err = cmd.Run()
|
||||
err = GenerateNotationCerts(tdir, "notation-sign-test")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -901,7 +1386,7 @@ func SignImageUsingNotary(repoTag, port string) error {
|
||||
// sign the image
|
||||
image := fmt.Sprintf("localhost:%s/%s", port, repoTag)
|
||||
|
||||
cmd = exec.Command("notation", "sign", "--key", "notation-sign-test", "--plain-http", image)
|
||||
err = SignWithNotation("notation-sign-test", image, tdir)
|
||||
|
||||
return cmd.Run()
|
||||
return err
|
||||
}
|
||||
|
||||
+480
-1
@@ -12,6 +12,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
notconfig "github.com/notaryproject/notation-go/config"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
@@ -453,7 +454,7 @@ func TestUploadImage(t *testing.T) {
|
||||
}
|
||||
|
||||
err := test.UploadImage(img, baseURL, "test")
|
||||
So(err, ShouldBeNil)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("Upload image with authentification", t, func() {
|
||||
@@ -792,3 +793,481 @@ func TestInjectUploadImageWithBasicAuth(t *testing.T) {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestCopyFile(t *testing.T) {
|
||||
Convey("destFilePath does not exist", t, func() {
|
||||
err := test.CopyFile("/path/to/srcFile", "~/path/to/some/unexisting/destDir/file")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("sourceFile does not exist", t, func() {
|
||||
err := test.CopyFile("/path/to/some/unexisting/file", path.Join(t.TempDir(), "destFile.txt"))
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsDigestReference(t *testing.T) {
|
||||
Convey("not digest reference", t, func() {
|
||||
res := test.IsDigestReference("notDigestReference/input")
|
||||
So(res, ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("wrong input format", t, func() {
|
||||
res := test.IsDigestReference("wrongInput")
|
||||
So(res, ShouldBeFalse)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLoadNotationSigningkeys(t *testing.T) {
|
||||
Convey("notation directory doesn't exist", t, func() {
|
||||
_, err := test.LoadNotationSigningkeys(t.TempDir())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("wrong content of signingkeys.json", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
dir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(dir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
filePath := path.Join(dir, "signingkeys.json")
|
||||
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = test.LoadNotationSigningkeys(tempDir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("not enough permissions to access signingkeys.json", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
dir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(dir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
filePath := path.Join(dir, "signingkeys.json")
|
||||
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o300) //nolint: gosec
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = test.LoadNotationSigningkeys(tempDir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("signingkeys.json not exists so it is created successfully", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
dir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(dir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = test.LoadNotationSigningkeys(tempDir)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("signingkeys.json not exists - error trying to create it", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
dir := path.Join(tempDir, "notation")
|
||||
// create notation directory without write permissions
|
||||
err := os.Mkdir(dir, 0o555)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = test.LoadNotationSigningkeys(tempDir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLoadNotationConfig(t *testing.T) {
|
||||
Convey("directory doesn't exist", t, func() {
|
||||
_, err := test.LoadNotationConfig(t.TempDir())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("wrong content of signingkeys.json", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
dir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(dir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
filePath := path.Join(dir, "signingkeys.json")
|
||||
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
_, err = test.LoadNotationConfig(tempDir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("check default value of signature format", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
dir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(dir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
filePath := path.Join(dir, "signingkeys.json")
|
||||
err = os.WriteFile(filePath, []byte("{\"SignatureFormat\": \"\"}"), 0o666) //nolint: gosec
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
configInfo, err := test.LoadNotationConfig(tempDir)
|
||||
So(err, ShouldBeNil)
|
||||
So(configInfo.SignatureFormat, ShouldEqual, "jws")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSignWithNotation(t *testing.T) {
|
||||
Convey("notation directory doesn't exist", t, func() {
|
||||
err := test.SignWithNotation("key", "reference", t.TempDir())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("key not found", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
dir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(dir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
filePath := path.Join(dir, "signingkeys.json")
|
||||
err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.SignWithNotation("key", "reference", tempDir)
|
||||
So(err, ShouldEqual, test.ErrKeyNotFound)
|
||||
})
|
||||
|
||||
Convey("not enough permissions to access notation/localkeys dir", t, func() {
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
tdir := t.TempDir()
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tdir)
|
||||
|
||||
err = test.GenerateNotationCerts(tdir, "key")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(tdir, "notation", "localkeys"), 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.SignWithNotation("key", "reference", tdir)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(path.Join(tdir, "notation", "localkeys"), 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("error parsing reference", t, func() {
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
tdir := t.TempDir()
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tdir)
|
||||
|
||||
err = test.GenerateNotationCerts(tdir, "key")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.SignWithNotation("key", "invalidReference", tdir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("error signing", t, func() {
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
tdir := t.TempDir()
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tdir)
|
||||
|
||||
err = test.GenerateNotationCerts(tdir, "key")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.SignWithNotation("key", "localhost:8080/invalidreference:1.0", tdir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVerifyWithNotation(t *testing.T) {
|
||||
Convey("notation directory doesn't exist", t, func() {
|
||||
err := test.VerifyWithNotation("reference", t.TempDir())
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("error parsing reference", t, func() {
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
tdir := t.TempDir()
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tdir)
|
||||
|
||||
err = test.GenerateNotationCerts(tdir, "key")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.VerifyWithNotation("invalidReference", tdir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("error trying to get manifest", t, func() {
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
tdir := t.TempDir()
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tdir)
|
||||
|
||||
err = test.GenerateNotationCerts(tdir, "key")
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.VerifyWithNotation("localhost:8080/invalidreference:1.0", tdir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("invalid content of trustpolicy.json", t, func() {
|
||||
// start a new server
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
dir := t.TempDir()
|
||||
|
||||
conf := config.New()
|
||||
conf.HTTP.Port = port
|
||||
conf.Storage.RootDirectory = dir
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
cm := test.NewControllerManager(ctlr)
|
||||
// this blocks
|
||||
cm.StartAndWait(port)
|
||||
defer cm.StopServer()
|
||||
|
||||
repoName := "signed-repo"
|
||||
tag := "1.0"
|
||||
cfg, layers, manifest, err := test.GetImageComponents(2)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.UploadImage(
|
||||
test.Image{
|
||||
Config: cfg,
|
||||
Layers: layers,
|
||||
Manifest: manifest,
|
||||
Tag: tag,
|
||||
}, baseURL, repoName)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
content, err := json.Marshal(manifest)
|
||||
So(err, ShouldBeNil)
|
||||
digest := godigest.FromBytes(content)
|
||||
So(digest, ShouldNotBeNil)
|
||||
|
||||
tempDir := t.TempDir()
|
||||
notationDir := path.Join(tempDir, "notation")
|
||||
err = os.Mkdir(notationDir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
filePath := path.Join(notationDir, "trustpolicy.json")
|
||||
err = os.WriteFile(filePath, []byte("some dummy file content"), 0o666) //nolint: gosec
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tempDir)
|
||||
|
||||
err = test.VerifyWithNotation(fmt.Sprintf("localhost:%s/%s:%s", port, repoName, tag), tempDir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListNotarySignatures(t *testing.T) {
|
||||
Convey("error parsing reference", t, func() {
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
tdir := t.TempDir()
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
_, err = test.ListNotarySignatures("invalidReference", tdir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("error trying to get manifest", t, func() {
|
||||
cwd, err := os.Getwd()
|
||||
So(err, ShouldBeNil)
|
||||
defer func() { _ = os.Chdir(cwd) }()
|
||||
tdir := t.TempDir()
|
||||
_ = os.Chdir(tdir)
|
||||
|
||||
_, err = test.ListNotarySignatures("localhost:8080/invalidreference:1.0", tdir)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGenerateNotationCerts(t *testing.T) {
|
||||
Convey("write key file with permission", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
notationDir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(notationDir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
filePath := path.Join(notationDir, "localkeys")
|
||||
err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tempDir)
|
||||
|
||||
err = test.GenerateNotationCerts(t.TempDir(), "cert")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("write cert file with permission", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
notationDir := path.Join(tempDir, "notation", "localkeys")
|
||||
err := os.MkdirAll(notationDir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
filePath := path.Join(notationDir, "cert.crt")
|
||||
err = os.WriteFile(filePath, []byte("{}"), 0o666) //nolint: gosec
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = os.Chmod(filePath, 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tempDir)
|
||||
|
||||
err = test.GenerateNotationCerts(t.TempDir(), "cert")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Chmod(filePath, 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("signingkeys.json file - not enough permission", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
notationDir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(notationDir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
filePath := path.Join(notationDir, "signingkeys.json")
|
||||
_, err = os.Create(filePath) //nolint: gosec
|
||||
So(err, ShouldBeNil)
|
||||
err = os.Chmod(filePath, 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tempDir)
|
||||
|
||||
err = test.GenerateNotationCerts(t.TempDir(), "cert")
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.Remove(filePath)
|
||||
So(err, ShouldBeNil)
|
||||
err = os.RemoveAll(path.Join(notationDir, "localkeys"))
|
||||
So(err, ShouldBeNil)
|
||||
signingKeysBuf, err := json.Marshal(notconfig.SigningKeys{})
|
||||
So(err, ShouldBeNil)
|
||||
err = os.WriteFile(filePath, signingKeysBuf, 0o555)
|
||||
So(err, ShouldBeNil)
|
||||
err = test.GenerateNotationCerts(t.TempDir(), "cert")
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
Convey("keysuite already exists in signingkeys.json", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
notationDir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(notationDir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
certName := "cert-test"
|
||||
filePath := path.Join(notationDir, "signingkeys.json")
|
||||
keyPath := path.Join(notationDir, "localkeys", certName+".key")
|
||||
certPath := path.Join(notationDir, "localkeys", certName+".crt")
|
||||
signingKeys := notconfig.SigningKeys{}
|
||||
keySuite := notconfig.KeySuite{
|
||||
Name: certName,
|
||||
X509KeyPair: ¬config.X509KeyPair{
|
||||
KeyPath: keyPath,
|
||||
CertificatePath: certPath,
|
||||
},
|
||||
}
|
||||
signingKeys.Keys = []notconfig.KeySuite{keySuite}
|
||||
signingKeysBuf, err := json.Marshal(signingKeys)
|
||||
So(err, ShouldBeNil)
|
||||
err = os.WriteFile(filePath, signingKeysBuf, 0o600)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tempDir)
|
||||
|
||||
err = test.GenerateNotationCerts(t.TempDir(), certName)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
Convey("truststore files", t, func() {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
notationDir := path.Join(tempDir, "notation")
|
||||
err := os.Mkdir(notationDir, 0o777)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
certName := "cert-test"
|
||||
trustStorePath := path.Join(notationDir, fmt.Sprintf("truststore/x509/ca/%s", certName))
|
||||
err = os.MkdirAll(trustStorePath, 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
err = os.Chmod(path.Join(notationDir, "truststore/x509"), 0o000)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
test.NotationPathLock.Lock()
|
||||
defer test.NotationPathLock.Unlock()
|
||||
|
||||
test.LoadNotationPath(tempDir)
|
||||
|
||||
err = test.GenerateNotationCerts(tempDir, certName)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.RemoveAll(path.Join(notationDir, "localkeys"))
|
||||
So(err, ShouldBeNil)
|
||||
err = os.Chmod(path.Join(notationDir, "truststore/x509"), 0o755)
|
||||
So(err, ShouldBeNil)
|
||||
_, err = os.Create(path.Join(trustStorePath, "cert-test.crt"))
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.GenerateNotationCerts(tempDir, certName)
|
||||
So(err, ShouldNotBeNil)
|
||||
|
||||
err = os.RemoveAll(path.Join(notationDir, "localkeys"))
|
||||
So(err, ShouldBeNil)
|
||||
err = os.Remove(path.Join(trustStorePath, "cert-test.crt"))
|
||||
So(err, ShouldBeNil)
|
||||
err = os.Chmod(path.Join(notationDir, "truststore/x509/ca", certName), 0o555)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
err = test.GenerateNotationCerts(tempDir, certName)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user