Add --sign-by-sq-fingerprint to push operations

This adds a new feature that allows signing using Sequoia-backed
keys.  The existing options to sign using GPG-backed keys (and sigstore)
remain unchanged, and continue to use the same backends as usual.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This commit is contained in:
Miloslav Trmač
2025-07-11 17:54:50 +02:00
parent 2f005b67f4
commit 9e2850d0a8
25 changed files with 365 additions and 16 deletions

View File

@@ -3,9 +3,9 @@
package integration
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
@@ -13,9 +13,13 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
"go.podman.io/image/v5/signature/simplesequoia"
"go.podman.io/storage/pkg/archive"
)
// testSequoiaKeyFingerprint is a fingerprint of a test Sequoia key in testdata.
const testSequoiaKeyFingerprint = "50DDE898DF4E48755C8C2B7AF6F908B6FA48A229"
var _ = Describe("Podman push", func() {
BeforeEach(func() {
@@ -235,22 +239,26 @@ var _ = Describe("Podman push", func() {
Expect(push2).Should(ExitCleanly())
if !IsRemote() { // Remote does not support signing
By("pushing and pulling with --sign-by-sigstore-private-key")
// Ideally, this should set SystemContext.RegistriesDirPath, but Podman currently doesnt
// expose that as an option. So, for now, modify /etc/directly, and skip testing sigstore if
// we dont have permission to do so.
lookasideDir, err := filepath.Abs(filepath.Join(podmanTest.TempDir, "test-lookaside"))
Expect(err).ToNot(HaveOccurred())
systemRegistriesDAddition := "/etc/containers/registries.d/podman-test-only-temporary-addition.yaml"
cmd := exec.Command("cp", "testdata/sigstore-registries.d-fragment.yaml", systemRegistriesDAddition)
output, err := cmd.CombinedOutput()
registriesDFragment, err := os.ReadFile("testdata/sigstore-registries.d-fragment.yaml")
Expect(err).ToNot(HaveOccurred())
registriesDFragment = bytes.ReplaceAll(registriesDFragment, []byte("@lookasideDir@"), []byte(lookasideDir))
err = os.WriteFile(systemRegistriesDAddition, registriesDFragment, 0644)
if err != nil {
GinkgoWriter.Printf("Skipping sigstore tests because /etc/containers/registries.d isnt writable: %s\n", string(output))
GinkgoWriter.Printf("Skipping sigstore tests because /etc/containers/registries.d isnt writable: %s\n", err)
} else {
By("pushing and pulling with --sign-by-sigstore-private-key")
defer func() {
err := os.Remove(systemRegistriesDAddition)
Expect(err).ToNot(HaveOccurred())
}()
// Generate a signature verification policy file
policyPath := generatePolicyFile(podmanTest.TempDir, 5003)
policyPath := generatePolicyFile(podmanTest.TempDir, 5003, "testdata/sequoia-key.pub")
defer os.Remove(policyPath)
// Verify that the policy rejects unsigned images
@@ -289,6 +297,40 @@ var _ = Describe("Podman push", func() {
pull = podmanTest.Podman([]string{"pull", "-q", "--tls-verify=false", "--signature-policy", policyPath, "localhost:5003/sigstore-signed-params"})
pull.WaitWithDefaultTimeout()
Expect(pull).Should(ExitCleanly())
signer, err := simplesequoia.NewSigner(
simplesequoia.WithSequoiaHome("testdata"),
simplesequoia.WithKeyFingerprint(testSequoiaKeyFingerprint),
)
if err != nil {
GinkgoWriter.Printf("Skipping Sequoia tests because simplesequoia.NewSigner failed: %s\n", err)
} else {
signer.Close()
By("pushing and pulling with --sign-by-sq-fingerprint")
absSequoiaHome, err := filepath.Abs("testdata")
Expect(err).ToNot(HaveOccurred())
defer os.Unsetenv("SEQUOIA_HOME")
os.Setenv("SEQUOIA_HOME", absSequoiaHome)
// Verify that the policy rejects unsigned images
push = podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5003/simple-sq-signed"})
push.WaitWithDefaultTimeout()
Expect(push).Should(ExitCleanly())
pull = podmanTest.Podman([]string{"pull", "-q", "--tls-verify=false", "--signature-policy", policyPath, "localhost:5003/simple-sq-signed"})
pull.WaitWithDefaultTimeout()
Expect(pull).To(ExitWithError(125, "A signature was required, but no signature exists"))
// Sign an image, and verify it is accepted.
push = podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", "--sign-by-sq-fingerprint", testSequoiaKeyFingerprint, ALPINE, "localhost:5003/simple-sq-signed"})
push.WaitWithDefaultTimeout()
Expect(push).Should(ExitCleanly())
pull = podmanTest.Podman([]string{"pull", "-q", "--tls-verify=false", "--signature-policy", policyPath, "localhost:5003/simple-sq-signed"})
pull.WaitWithDefaultTimeout()
Expect(pull).Should(ExitCleanly())
}
}
}
})