diff --git a/integration/copy_test.go b/integration/copy_test.go index d68cb0a2..29843b8c 100644 --- a/integration/copy_test.go +++ b/integration/copy_test.go @@ -4,8 +4,11 @@ import ( "bytes" "fmt" "io/ioutil" + "net/http" + "net/http/httptest" "os" "path/filepath" + "strings" "github.com/containers/image/manifest" "github.com/go-check/check" @@ -15,7 +18,7 @@ func init() { check.Suite(&CopySuite{}) } -const v2DockerRegistryURL = "localhost:5555" +const v2DockerRegistryURL = "localhost:5555" // Update also policy.json type CopySuite struct { cluster *openshiftCluster @@ -291,3 +294,82 @@ func (s *CopySuite) TestCopyCompression(c *check.C) { } } } + +func findRegularFiles(c *check.C, root string) []string { + result := []string{} + err := filepath.Walk(root, filepath.WalkFunc(func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.Mode().IsRegular() { + result = append(result, path) + } + return nil + })) + c.Assert(err, check.IsNil) + return result +} + +// --sign-by and policy use for docker: with sigstore +func (s *CopySuite) TestCopyDockerSigstore(c *check.C) { + const ourRegistry = "docker://" + v2DockerRegistryURL + "/" + + tmpDir, err := ioutil.TempDir("", "signatures-sigstore") + c.Assert(err, check.IsNil) + //defer os.RemoveAll(tmpDir) + copyDest := filepath.Join(tmpDir, "dest") + err = os.Mkdir(copyDest, 0755) + c.Assert(err, check.IsNil) + dirDest := "dir:" + copyDest + plainSigstore := filepath.Join(tmpDir, "sigstore") + splitSigstoreStaging := filepath.Join(tmpDir, "sigstore-staging") + + splitSigstoreReadServerHandler := http.NotFoundHandler() + splitSigstoreReadServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + splitSigstoreReadServerHandler.ServeHTTP(w, r) + })) + defer splitSigstoreReadServer.Close() + + policy := fileFromFixture(c, "fixtures/policy.json", map[string]string{"@keydir@": s.gpgHome}) + defer os.Remove(policy) + registriesDir := filepath.Join(tmpDir, "registries.d") + err = os.Mkdir(registriesDir, 0755) + c.Assert(err, check.IsNil) + registriesFile := fileFromFixture(c, "fixtures/registries.yaml", + map[string]string{"@sigstore@": plainSigstore, "@split-staging@": splitSigstoreStaging, "@split-read@": splitSigstoreReadServer.URL}) + err = os.Symlink(registriesFile, filepath.Join(registriesDir, "registries.yaml")) + c.Assert(err, check.IsNil) + + // Get an image to work with. Also verifies that we can use Docker repositories with no sigstore configured. + assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", "docker://busybox", ourRegistry+"original/busybox") + // Pulling an unsigned image fails. + assertSkopeoFails(c, ".*Source image rejected: A signature was required, but no signature exists.*", + "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"original/busybox", dirDest) + + // Signing with sigstore defined succeeds, + assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", "--sign-by", "personal@example.com", ourRegistry+"original/busybox", ourRegistry+"signed/busybox") + // a signature file has been created, + foundFiles := findRegularFiles(c, plainSigstore) + c.Assert(foundFiles, check.HasLen, 1) + // and pulling a signed image succeeds. + assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"signed/busybox", dirDest) + + // Deleting the image succeeds, + assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir, "delete", ourRegistry+"signed/busybox") + // and the signature file has been deleted (but we leave the directories around). + // a signature file has been created, + foundFiles = findRegularFiles(c, plainSigstore) + c.Assert(foundFiles, check.HasLen, 0) + + // Signing with a read/write sigstore split succeeds, + assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", "--sign-by", "personal@example.com", ourRegistry+"original/busybox", ourRegistry+"public/busybox") + // and a signature file has been created. + foundFiles = findRegularFiles(c, splitSigstoreStaging) + c.Assert(foundFiles, check.HasLen, 1) + // Pulling the image fails because the read sigstore URL has not been populated: + assertSkopeoFails(c, ".*Source image rejected: A signature was required, but no signature exists.*", + "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"public/busybox", dirDest) + // Pulling the image succeeds after the read sigstore URL is available: + splitSigstoreReadServerHandler = http.FileServer(http.Dir(splitSigstoreStaging)) + assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"public/busybox", dirDest) +} diff --git a/integration/fixtures/policy.json b/integration/fixtures/policy.json index c1b34fb6..8c1ddf43 100644 --- a/integration/fixtures/policy.json +++ b/integration/fixtures/policy.json @@ -6,6 +6,13 @@ ], "transports": { "docker": { + "localhost:5555": [ + { + "type": "signedBy", + "keyType": "GPGKeys", + "keyPath": "@keydir@/personal-pubkey.gpg" + } + ], "docker.io/openshift": [ { "type": "insecureAcceptAnything" diff --git a/integration/fixtures/registries.yaml b/integration/fixtures/registries.yaml new file mode 100644 index 00000000..9a42be60 --- /dev/null +++ b/integration/fixtures/registries.yaml @@ -0,0 +1,6 @@ +docker: + localhost:5555: + sigstore: file://@sigstore@ + localhost:5555/public: + sigstore-staging: file://@split-staging@ + sigstore: @split-read@ diff --git a/integration/registry.go b/integration/registry.go index 80c6983f..0136e336 100644 --- a/integration/registry.go +++ b/integration/registry.go @@ -67,6 +67,8 @@ loglevel: debug storage: filesystem: rootdirectory: %s + delete: + enabled: true http: addr: %s %s`