mirror of
https://github.com/containers/podman.git
synced 2025-11-29 17:48:05 +08:00
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:
@@ -12,13 +12,15 @@ import (
|
||||
"go.podman.io/image/v5/pkg/cli"
|
||||
"go.podman.io/image/v5/pkg/cli/sigstore"
|
||||
"go.podman.io/image/v5/signature/signer"
|
||||
"go.podman.io/image/v5/signature/simplesequoia"
|
||||
)
|
||||
|
||||
// SigningCLIOnlyOptions contains signing-related CLI options.
|
||||
// Some other options are defined in entities.ImagePushOptions.
|
||||
type SigningCLIOnlyOptions struct {
|
||||
signPassphraseFile string
|
||||
signBySigstoreParamFile string
|
||||
signPassphraseFile string
|
||||
signBySequoiaFingerprint string
|
||||
signBySigstoreParamFile string
|
||||
}
|
||||
|
||||
func DefineSigningFlags(cmd *cobra.Command, cliOpts *SigningCLIOnlyOptions, pushOpts *entities.ImagePushOptions) {
|
||||
@@ -28,6 +30,10 @@ func DefineSigningFlags(cmd *cobra.Command, cliOpts *SigningCLIOnlyOptions, push
|
||||
flags.StringVar(&pushOpts.SignBy, signByFlagName, "", "Add a signature at the destination using the specified key")
|
||||
_ = cmd.RegisterFlagCompletionFunc(signByFlagName, completion.AutocompleteNone)
|
||||
|
||||
signBySequoiaFingerprintFlagName := "sign-by-sq-fingerprint"
|
||||
flags.StringVar(&cliOpts.signBySequoiaFingerprint, signBySequoiaFingerprintFlagName, "", "Sign the image using a Sequoia-PGP key with the specified `FINGERPRINT`")
|
||||
_ = cmd.RegisterFlagCompletionFunc(signBySequoiaFingerprintFlagName, completion.AutocompleteNone)
|
||||
|
||||
signBySigstoreFlagName := "sign-by-sigstore"
|
||||
flags.StringVar(&cliOpts.signBySigstoreParamFile, signBySigstoreFlagName, "", "Sign the image using a sigstore parameter file at `PATH`")
|
||||
_ = cmd.RegisterFlagCompletionFunc(signBySigstoreFlagName, completion.AutocompleteDefault)
|
||||
@@ -42,6 +48,7 @@ func DefineSigningFlags(cmd *cobra.Command, cliOpts *SigningCLIOnlyOptions, push
|
||||
|
||||
if registry.IsRemote() {
|
||||
_ = flags.MarkHidden(signByFlagName)
|
||||
_ = flags.MarkHidden(signBySequoiaFingerprintFlagName)
|
||||
_ = flags.MarkHidden(signBySigstoreFlagName)
|
||||
_ = flags.MarkHidden(signBySigstorePrivateKeyFlagName)
|
||||
_ = flags.MarkHidden(signPassphraseFileFlagName)
|
||||
@@ -57,8 +64,20 @@ func PrepareSigning(pushOpts *entities.ImagePushOptions, cliOpts *SigningCLIOnly
|
||||
// c/common/libimage.Image does allow creating both simple signing and sigstore signatures simultaneously,
|
||||
// with independent passphrases, but that would make the CLI probably too confusing.
|
||||
// For now, use the passphrase with either, but only one of them.
|
||||
if cliOpts.signPassphraseFile != "" && pushOpts.SignBy != "" && pushOpts.SignBySigstorePrivateKeyFile != "" {
|
||||
return nil, fmt.Errorf("only one of --sign-by and sign-by-sigstore-private-key can be used with --sign-passphrase-file")
|
||||
if cliOpts.signPassphraseFile != "" {
|
||||
count := 0
|
||||
if pushOpts.SignBy != "" {
|
||||
count++
|
||||
}
|
||||
if cliOpts.signBySequoiaFingerprint != "" {
|
||||
count++
|
||||
}
|
||||
if pushOpts.SignBySigstorePrivateKeyFile != "" {
|
||||
count++
|
||||
}
|
||||
if count > 1 {
|
||||
return nil, fmt.Errorf("only one of --sign-by, --sign-by-sq-fingerprint and --sign-by-sigstore-private-key can be used with --sign-passphrase-file")
|
||||
}
|
||||
}
|
||||
|
||||
var passphrase string
|
||||
@@ -72,9 +91,16 @@ func PrepareSigning(pushOpts *entities.ImagePushOptions, cliOpts *SigningCLIOnly
|
||||
p := ssh.ReadPassphrase()
|
||||
passphrase = string(p)
|
||||
} // pushOpts.SignBy triggers a GPG-agent passphrase prompt, possibly using a more secure channel, so we usually shouldn’t prompt ourselves if no passphrase was explicitly provided.
|
||||
// With signBySequoiaFingerprint, we don’t prompt for a passphrase (for now??): We don’t know whether the key requires a passphrase.
|
||||
pushOpts.SignPassphrase = passphrase
|
||||
pushOpts.SignSigstorePrivateKeyPassphrase = []byte(passphrase)
|
||||
cleanup := signingCleanup{}
|
||||
succeeded := false
|
||||
defer func() {
|
||||
if !succeeded {
|
||||
cleanup.cleanup()
|
||||
}
|
||||
}()
|
||||
if cliOpts.signBySigstoreParamFile != "" {
|
||||
signer, err := sigstore.NewSignerFromParameterFile(cliOpts.signBySigstoreParamFile, &sigstore.Options{
|
||||
PrivateKeyPassphrasePrompt: cli.ReadPassphraseFile,
|
||||
@@ -87,6 +113,21 @@ func PrepareSigning(pushOpts *entities.ImagePushOptions, cliOpts *SigningCLIOnly
|
||||
pushOpts.Signers = append(pushOpts.Signers, signer)
|
||||
cleanup.signers = append(cleanup.signers, signer)
|
||||
}
|
||||
if cliOpts.signBySequoiaFingerprint != "" {
|
||||
opts := []simplesequoia.Option{
|
||||
simplesequoia.WithKeyFingerprint(cliOpts.signBySequoiaFingerprint),
|
||||
}
|
||||
if passphrase != "" {
|
||||
opts = append(opts, simplesequoia.WithPassphrase(passphrase))
|
||||
}
|
||||
signer, err := simplesequoia.NewSigner(opts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error using --sign-by-sq-fingerprint: %w", err)
|
||||
}
|
||||
pushOpts.Signers = append(pushOpts.Signers, signer)
|
||||
cleanup.signers = append(cleanup.signers, signer)
|
||||
}
|
||||
succeeded = true
|
||||
return cleanup.cleanup, nil
|
||||
}
|
||||
|
||||
|
||||
8
docs/source/markdown/options/sign-by-sq-fingerprint.md
Normal file
8
docs/source/markdown/options/sign-by-sq-fingerprint.md
Normal file
@@ -0,0 +1,8 @@
|
||||
####> This option file is used in:
|
||||
####> podman artifact push, manifest push, push
|
||||
####> If file is edited, make sure the changes
|
||||
####> are applicable to all of those.
|
||||
#### **--sign-by-sq-fingerprint**=*fingerprint*
|
||||
|
||||
Add a “simple signing” signature using a Sequoia-PGP key with the specified fingerprint.
|
||||
(This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
|
||||
@@ -4,4 +4,4 @@
|
||||
####> are applicable to all of those.
|
||||
#### **--sign-passphrase-file**=*path*
|
||||
|
||||
If signing the image (using either **--sign-by** or **--sign-by-sigstore-private-key**), read the passphrase to use from the specified path.
|
||||
If signing the image (using **--sign-by**, **sign-by-sq-fingerprint** or **--sign-by-sigstore-private-key**), read the passphrase to use from the specified path.
|
||||
|
||||
@@ -38,11 +38,12 @@ Add a “simple signing” signature at the destination using the specified key.
|
||||
|
||||
@@option sign-by-sigstore
|
||||
|
||||
|
||||
#### **--sign-by-sigstore-private-key**=*path*
|
||||
|
||||
Add a sigstore signature at the destination using a private key at the specified path. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
|
||||
|
||||
@@option sign-by-sq-fingerprint
|
||||
|
||||
@@option sign-passphrase-file
|
||||
|
||||
@@option tls-verify
|
||||
|
||||
@@ -70,6 +70,8 @@ Sign the pushed images with a “simple signing” signature using the specified
|
||||
|
||||
Sign the pushed images with a sigstore signature using a private key at the specified path. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
|
||||
|
||||
@@option sign-by-sq-fingerprint
|
||||
|
||||
@@option sign-passphrase-file
|
||||
|
||||
@@option tls-verify
|
||||
|
||||
@@ -98,6 +98,8 @@ Add a “simple signing” signature at the destination using the specified key.
|
||||
|
||||
Add a sigstore signature at the destination using a private key at the specified path. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
|
||||
|
||||
@@option sign-by-sq-fingerprint
|
||||
|
||||
@@option sign-passphrase-file
|
||||
|
||||
@@option tls-verify
|
||||
|
||||
@@ -1309,8 +1309,8 @@ func (p *PodmanTestIntegration) removeNetwork(name string) {
|
||||
|
||||
// generatePolicyFile generates a signature verification policy file.
|
||||
// it returns the policy file path.
|
||||
func generatePolicyFile(tempDir string, port int) string {
|
||||
keyPath := filepath.Join(tempDir, "key.gpg")
|
||||
func generatePolicyFile(tempDir string, port int, sequoiaKeyPath string) string {
|
||||
gpgKeyPath := filepath.Join(tempDir, "key.gpg")
|
||||
policyPath := filepath.Join(tempDir, "policy.json")
|
||||
conf := fmt.Sprintf(`
|
||||
{
|
||||
@@ -1339,11 +1339,18 @@ func generatePolicyFile(tempDir string, port int) string {
|
||||
"type": "sigstoreSigned",
|
||||
"keyPath": "testdata/sigstore-key.pub"
|
||||
}
|
||||
],
|
||||
"localhost:%[1]d/simple-sq-signed": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "GPGKeys",
|
||||
"keyPath": "%[3]s"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
`, port, keyPath)
|
||||
`, port, gpgKeyPath, sequoiaKeyPath)
|
||||
writeConf([]byte(conf), policyPath)
|
||||
return policyPath
|
||||
}
|
||||
|
||||
@@ -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 doesn’t
|
||||
// expose that as an option. So, for now, modify /etc/directly, and skip testing sigstore if
|
||||
// we don’t 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 isn’t writable: %s\n", string(output))
|
||||
GinkgoWriter.Printf("Skipping sigstore tests because /etc/containers/registries.d isn’t 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -189,7 +189,7 @@ default-docker:
|
||||
|
||||
if !IsRemote() {
|
||||
// Generate a signature verification policy file
|
||||
policyPath := generatePolicyFile(podmanTest.TempDir, port)
|
||||
policyPath := generatePolicyFile(podmanTest.TempDir, port, "testdata/sequoia-key.pub")
|
||||
defer os.Remove(policyPath)
|
||||
|
||||
session = podmanTest.Podman([]string{"pull", "-q", "--tls-verify=false", "--signature-policy", policyPath, pushedImage})
|
||||
|
||||
0
test/e2e/testdata/data/keystore/keystore.cookie
vendored
Normal file
0
test/e2e/testdata/data/keystore/keystore.cookie
vendored
Normal file
BIN
test/e2e/testdata/data/keystore/softkeys/1F5825285B785E1DB13BF36D2D11A19ABA41C6AE.pgp
vendored
Normal file
BIN
test/e2e/testdata/data/keystore/softkeys/1F5825285B785E1DB13BF36D2D11A19ABA41C6AE.pgp
vendored
Normal file
Binary file not shown.
BIN
test/e2e/testdata/data/keystore/softkeys/50DDE898DF4E48755C8C2B7AF6F908B6FA48A229.pgp
vendored
Normal file
BIN
test/e2e/testdata/data/keystore/softkeys/50DDE898DF4E48755C8C2B7AF6F908B6FA48A229.pgp
vendored
Normal file
Binary file not shown.
BIN
test/e2e/testdata/data/pgp.cert.d/1f/5825285b785e1db13bf36d2d11a19aba41c6ae
vendored
Normal file
BIN
test/e2e/testdata/data/pgp.cert.d/1f/5825285b785e1db13bf36d2d11a19aba41c6ae
vendored
Normal file
Binary file not shown.
BIN
test/e2e/testdata/data/pgp.cert.d/4d/8bcd544b7573eefaad18c278473e5f255d10b8
vendored
Normal file
BIN
test/e2e/testdata/data/pgp.cert.d/4d/8bcd544b7573eefaad18c278473e5f255d10b8
vendored
Normal file
Binary file not shown.
BIN
test/e2e/testdata/data/pgp.cert.d/50/dde898df4e48755c8c2b7af6f908b6fa48a229
vendored
Normal file
BIN
test/e2e/testdata/data/pgp.cert.d/50/dde898df4e48755c8c2b7af6f908b6fa48a229
vendored
Normal file
Binary file not shown.
BIN
test/e2e/testdata/data/pgp.cert.d/68/de230c4a009f5ee5fbb27984642d0130b86046
vendored
Normal file
BIN
test/e2e/testdata/data/pgp.cert.d/68/de230c4a009f5ee5fbb27984642d0130b86046
vendored
Normal file
Binary file not shown.
BIN
test/e2e/testdata/data/pgp.cert.d/trust-root
vendored
Normal file
BIN
test/e2e/testdata/data/pgp.cert.d/trust-root
vendored
Normal file
Binary file not shown.
0
test/e2e/testdata/data/pgp.cert.d/writelock
vendored
Normal file
0
test/e2e/testdata/data/pgp.cert.d/writelock
vendored
Normal file
38
test/e2e/testdata/sequoia-key.pub
vendored
Normal file
38
test/e2e/testdata/sequoia-key.pub
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEaGwFVhYJKwYBBAHaRw8BAQdAZzfnqEAgvE3RoCtPWEOc3Xp8oMURR0qjq+Ru
|
||||
PHJrc6TCwAsEHxYKAH0FgmhsBVYDCwkHCRD2+Qi2+kiiKUcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmcEjRQtILaFnIhczxeUkcfW0KMHEZ30
|
||||
wTdJ1v1iHB7NKQMVCggCmwECHgkWIQRQ3eiY305IdVyMK3r2+Qi2+kiiKQAA86gA
|
||||
/1ZkXWPHUxh3nQu/EL72ZeP9k/SLWkEuNKs6dJrmRud9AQCHbWwSUwKyt12EFVt/
|
||||
QvMFSQ95brUxsWLHgFMPpNfWAc0aU2tvcGVvIFNlcXVvaWEgdGVzdGluZyBrZXnC
|
||||
wA4EExYKAIAFgmhsBVYDCwkHCRD2+Qi2+kiiKUcUAAAAAAAeACBzYWx0QG5vdGF0
|
||||
aW9ucy5zZXF1b2lhLXBncC5vcmctF7xuY06GUyedOGjd2iNKwab85gV64zEAGKgi
|
||||
ExHRxgMVCggCmQECmwECHgkWIQRQ3eiY305IdVyMK3r2+Qi2+kiiKQAA3SEBAMe1
|
||||
y6rWaPjDpkeiDthLV1Umr6NsXVBv/IJTcP9RM4quAQCwmlsdQMddCsc+K3Y5KH88
|
||||
saIG0/MRZaPJdsd8vRGUCs4zBGhsBVYWCSsGAQQB2kcPAQEHQLN8yt/21QDMzcB4
|
||||
2bzFRg1LpkFZWECjkb2ty7Iju/aOwsC/BBgWCgExBYJobAVWCRD2+Qi2+kiiKUcU
|
||||
AAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmce9QEurrtI24ys
|
||||
vXssO/40rI5rlsNokEEFr7CVwVgWvAKbAr6gBBkWCgBvBYJobAVWCRB63Ra9Qdgp
|
||||
tkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmcBWCJsdUfj
|
||||
oYpld4qcYBqjxsyScwpID2vkNlYMLmS+IhYhBKyZqvZ6WI3zgaapXHrdFr1B2Cm2
|
||||
AAAEZwEA/UhpNN1XElYx6Xq+JMKlXywoIgButkQy1+H2EcRBeHsBAM7lq8BXvRKz
|
||||
bDjRlgxiIAYl77p7ihVQ5NYcuZcAlH0CFiEEUN3omN9OSHVcjCt69vkItvpIoikA
|
||||
AJcwAP9D4spfb28k16w2cemrWAtAE1WUgV8V+OEpE7+gpV+17gEA+0Kzf7jBHgd3
|
||||
pBAWwttuRd8OHlZZzKs3f26z28I6mgLOMwRobAVWFgkrBgEEAdpHDwEBB0DPyS14
|
||||
jQk1mSWNmuYR4P9M5zOfU2mkhwaqx1l3OWTZD8LAvwQYFgoBMQWCaGwFVgkQ9vkI
|
||||
tvpIoilHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn+wfK
|
||||
FmPmtrsi0sY5zIq9KFmbrQyhXz/VZIw6K8D1zdECmyC+oAQZFgoAbwWCaGwFVgkQ
|
||||
bwujLUxU69BHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn
|
||||
xF3KXB4+dN9suOhCD2XkYlAWUJ4GVBVV2wAmdQAueyEWIQTv1sMw2eUTIMQmb7Zv
|
||||
C6MtTFTr0AAA/LYA/iBkRh6dGbp76VzuuHVNUNgTqvXgz9FjizZGJKnVZctXAPwL
|
||||
TlHxcH6XX96AuiCy9QAMUpm8ZvMu8TAgjgOrlFPKCBYhBFDd6JjfTkh1XIwrevb5
|
||||
CLb6SKIpAAA0rQD9HWbBeSoshjH6/k5ntZjOfIAha4/TLlBrMq2w+t4LWD0A/2q5
|
||||
DEbYh6PwMidDxXteyHWf4Qnr0vH8vip9d+WHbDYEzjgEaGwFVhIKKwYBBAGXVQEF
|
||||
AQEHQLxXHw9STOAhb2PLEjrl3uQDwpaXIdigg67vId0jSstVAwEIB8LAAAQYFgoA
|
||||
cgWCaGwFVgkQ9vkItvpIoilHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9p
|
||||
YS1wZ3Aub3Jn8bvuQCv3uEYJtK6h5y5e4AY9lJtVXx3brexR5bmFCwcCmwwWIQRQ
|
||||
3eiY305IdVyMK3r2+Qi2+kiiKQAAEzkA/Az97rdlp3hf97S6a5AxU8pTry4gKI63
|
||||
lwKtBAT+uF/pAP9lAziQRlNEa1sX6qCXrQqeA/aQ0nj9gRJ1Wvi1PMxWBA==
|
||||
=7jmE
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
@@ -3,3 +3,5 @@ docker:
|
||||
use-sigstore-attachments: true
|
||||
localhost:5003/sigstore-signed-params:
|
||||
use-sigstore-attachments: true
|
||||
localhost:5003/simple-sq-signed:
|
||||
lookaside: file://@lookasideDir@
|
||||
|
||||
52
vendor/go.podman.io/image/v5/signature/simplesequoia/mechanism.go
generated
vendored
Normal file
52
vendor/go.podman.io/image/v5/signature/simplesequoia/mechanism.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
//go:build containers_image_sequoia
|
||||
|
||||
package simplesequoia
|
||||
|
||||
// This implements a signature.signingMechanismWithPassphrase that only supports signing.
|
||||
//
|
||||
// FIXME: Consider restructuring the simple signing signature creation code path
|
||||
// not to require this indirection and all those unimplemented methods.
|
||||
|
||||
import (
|
||||
"go.podman.io/image/v5/signature/internal/sequoia"
|
||||
)
|
||||
|
||||
// A GPG/OpenPGP signing mechanism, implemented using Sequoia.
|
||||
type sequoiaSigningOnlyMechanism struct {
|
||||
inner *sequoia.SigningMechanism
|
||||
}
|
||||
|
||||
func (m *sequoiaSigningOnlyMechanism) Close() error {
|
||||
panic("Should never be called")
|
||||
}
|
||||
|
||||
// SupportsSigning returns nil if the mechanism supports signing, or a SigningNotSupportedError.
|
||||
func (m *sequoiaSigningOnlyMechanism) SupportsSigning() error {
|
||||
panic("Should never be called")
|
||||
}
|
||||
|
||||
// Sign creates a (non-detached) signature of input using keyIdentity and passphrase.
|
||||
// Fails with a SigningNotSupportedError if the mechanism does not support signing.
|
||||
func (m *sequoiaSigningOnlyMechanism) SignWithPassphrase(input []byte, keyIdentity string, passphrase string) ([]byte, error) {
|
||||
return m.inner.SignWithPassphrase(input, keyIdentity, passphrase)
|
||||
}
|
||||
|
||||
// Sign creates a (non-detached) signature of input using keyIdentity.
|
||||
// Fails with a SigningNotSupportedError if the mechanism does not support signing.
|
||||
func (m *sequoiaSigningOnlyMechanism) Sign(input []byte, keyIdentity string) ([]byte, error) {
|
||||
panic("Should never be called")
|
||||
}
|
||||
|
||||
// Verify parses unverifiedSignature and returns the content and the signer's identity
|
||||
func (m *sequoiaSigningOnlyMechanism) Verify(unverifiedSignature []byte) (contents []byte, keyIdentity string, err error) {
|
||||
panic("Should never be called")
|
||||
}
|
||||
|
||||
// UntrustedSignatureContents returns UNTRUSTED contents of the signature WITHOUT ANY VERIFICATION,
|
||||
// along with a short identifier of the key used for signing.
|
||||
// WARNING: The short key identifier (which corresponds to "Key ID" for OpenPGP keys)
|
||||
// is NOT the same as a "key identity" used in other calls to this interface, and
|
||||
// the values may have no recognizable relationship if the public key is not available.
|
||||
func (m *sequoiaSigningOnlyMechanism) UntrustedSignatureContents(untrustedSignature []byte) (untrustedContents []byte, shortKeyIdentifier string, err error) {
|
||||
panic("Should never be called")
|
||||
}
|
||||
37
vendor/go.podman.io/image/v5/signature/simplesequoia/options.go
generated
vendored
Normal file
37
vendor/go.podman.io/image/v5/signature/simplesequoia/options.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package simplesequoia
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Option func(*simpleSequoiaSigner) error
|
||||
|
||||
// WithSequoiaHome returns an Option for NewSigner, specifying a Sequoia home directory to use.
|
||||
func WithSequoiaHome(sequoiaHome string) Option {
|
||||
return func(s *simpleSequoiaSigner) error {
|
||||
s.sequoiaHome = sequoiaHome
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithKeyFingerprint returns an Option for NewSigner, specifying a key to sign with, using the provided Sequoia-PGP key fingerprint.
|
||||
func WithKeyFingerprint(keyFingerprint string) Option {
|
||||
return func(s *simpleSequoiaSigner) error {
|
||||
s.keyFingerprint = keyFingerprint
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithPassphrase returns an Option for NewSigner, specifying a passphrase for the private key.
|
||||
func WithPassphrase(passphrase string) Option {
|
||||
return func(s *simpleSequoiaSigner) error {
|
||||
// The gpgme implementation can’t use passphrase with \n; reject it here for consistent behavior.
|
||||
// FIXME: We don’t need it in this API at all, but the "\n" check exists in the current call stack. That should go away.
|
||||
if strings.Contains(passphrase, "\n") {
|
||||
return errors.New("invalid passphrase: must not contain a line break")
|
||||
}
|
||||
s.passphrase = passphrase
|
||||
return nil
|
||||
}
|
||||
}
|
||||
88
vendor/go.podman.io/image/v5/signature/simplesequoia/signer.go
generated
vendored
Normal file
88
vendor/go.podman.io/image/v5/signature/simplesequoia/signer.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
//go:build containers_image_sequoia
|
||||
|
||||
package simplesequoia
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"go.podman.io/image/v5/docker/reference"
|
||||
internalSig "go.podman.io/image/v5/internal/signature"
|
||||
internalSigner "go.podman.io/image/v5/internal/signer"
|
||||
"go.podman.io/image/v5/signature"
|
||||
"go.podman.io/image/v5/signature/internal/sequoia"
|
||||
"go.podman.io/image/v5/signature/signer"
|
||||
)
|
||||
|
||||
// simpleSequoiaSigner is a signer.SignerImplementation implementation for simple signing signatures using Sequoia.
|
||||
type simpleSequoiaSigner struct {
|
||||
mech *sequoia.SigningMechanism
|
||||
sequoiaHome string // "" if using the system’s default
|
||||
keyFingerprint string
|
||||
passphrase string // "" if not provided.
|
||||
}
|
||||
|
||||
// NewSigner returns a signature.Signer which creates “simple signing” signatures using the user’s default
|
||||
// Sequoia PGP configuration.
|
||||
//
|
||||
// The set of options must identify a key to sign with, probably using a WithKeyFingerprint.
|
||||
//
|
||||
// The caller must call Close() on the returned Signer.
|
||||
func NewSigner(opts ...Option) (*signer.Signer, error) {
|
||||
s := simpleSequoiaSigner{}
|
||||
for _, o := range opts {
|
||||
if err := o(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if s.keyFingerprint == "" {
|
||||
return nil, errors.New("no key identity provided for simple signing")
|
||||
}
|
||||
|
||||
if err := sequoia.Init(); err != nil {
|
||||
return nil, err // Coverage: This is impractical to test in-process, with the static go_sequoia_dlhandle.
|
||||
}
|
||||
mech, err := sequoia.NewMechanismFromDirectory(s.sequoiaHome)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("initializing Sequoia: %w", err)
|
||||
}
|
||||
s.mech = mech
|
||||
succeeded := false
|
||||
defer func() {
|
||||
if !succeeded {
|
||||
s.mech.Close() // Coverage: This is currently unreachable.
|
||||
}
|
||||
}()
|
||||
|
||||
// Ideally, we should look up (and unlock?) the key at this point already. FIXME: is that possible? Anyway, low-priority.
|
||||
|
||||
succeeded = true
|
||||
return internalSigner.NewSigner(&s), nil
|
||||
}
|
||||
|
||||
// ProgressMessage returns a human-readable sentence that makes sense to write before starting to create a single signature.
|
||||
func (s *simpleSequoiaSigner) ProgressMessage() string {
|
||||
return "Signing image using Sequoia-PGP simple signing"
|
||||
}
|
||||
|
||||
// SignImageManifest creates a new signature for manifest m as dockerReference.
|
||||
func (s *simpleSequoiaSigner) SignImageManifest(ctx context.Context, m []byte, dockerReference reference.Named) (internalSig.Signature, error) {
|
||||
if reference.IsNameOnly(dockerReference) {
|
||||
return nil, fmt.Errorf("reference %s can’t be signed, it has neither a tag nor a digest", dockerReference.String())
|
||||
}
|
||||
wrapped := sequoiaSigningOnlyMechanism{
|
||||
inner: s.mech,
|
||||
}
|
||||
simpleSig, err := signature.SignDockerManifestWithOptions(m, dockerReference.String(), &wrapped, s.keyFingerprint, &signature.SignOptions{
|
||||
Passphrase: s.passphrase,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return internalSig.SimpleSigningFromBlob(simpleSig), nil
|
||||
}
|
||||
|
||||
func (s *simpleSequoiaSigner) Close() error {
|
||||
return s.mech.Close()
|
||||
}
|
||||
28
vendor/go.podman.io/image/v5/signature/simplesequoia/signer_stub.go
generated
vendored
Normal file
28
vendor/go.podman.io/image/v5/signature/simplesequoia/signer_stub.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
//go:build !containers_image_sequoia
|
||||
|
||||
package simplesequoia
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"go.podman.io/image/v5/signature/signer"
|
||||
)
|
||||
|
||||
// simpleSequoiaSigner is a signer.SignerImplementation implementation for simple signing signatures using Sequoia.
|
||||
type simpleSequoiaSigner struct {
|
||||
// This is not really used, we just keep the struct fields so that the With… Option functions can be compiled.
|
||||
|
||||
sequoiaHome string // "" if using the system's default
|
||||
keyFingerprint string
|
||||
passphrase string // "" if not provided.
|
||||
}
|
||||
|
||||
// NewSigner returns a signature.Signer which creates "simple signing" signatures using the user's default
|
||||
// Sequoia PGP configuration.
|
||||
//
|
||||
// The set of options must identify a key to sign with, probably using a WithKeyFingerprint.
|
||||
//
|
||||
// The caller must call Close() on the returned Signer.
|
||||
func NewSigner(opts ...Option) (*signer.Signer, error) {
|
||||
return nil, errors.New("Sequoia-PGP support is not enabled in this build")
|
||||
}
|
||||
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@@ -875,6 +875,7 @@ go.podman.io/image/v5/signature/sigstore
|
||||
go.podman.io/image/v5/signature/sigstore/fulcio
|
||||
go.podman.io/image/v5/signature/sigstore/internal
|
||||
go.podman.io/image/v5/signature/sigstore/rekor
|
||||
go.podman.io/image/v5/signature/simplesequoia
|
||||
go.podman.io/image/v5/signature/simplesigning
|
||||
go.podman.io/image/v5/storage
|
||||
go.podman.io/image/v5/tarball
|
||||
|
||||
Reference in New Issue
Block a user