fix: allow signing multiple charts with passphrase from stdin.

Cache the signing key passphrase. When signing multiple charts
with the passphrase from stdin, this allows signing all charts
instead of all but the first failing with an error about stdin
already having been closed.

Signed-off-by: Krisztian Litkey <krisztian.litkey@intel.com>
pull/30718/head
Krisztian Litkey 6 months ago
parent 0199b748aa
commit 3102c28804
No known key found for this signature in database
GPG Key ID: 637F2939D50AF85D

@ -39,6 +39,7 @@ type Package struct {
Key string Key string
Keyring string Keyring string
PassphraseFile string PassphraseFile string
cachedPassphrase []byte
Version string Version string
AppVersion string AppVersion string
Destination string Destination string
@ -55,6 +56,10 @@ type Package struct {
InsecureSkipTLSverify bool InsecureSkipTLSverify bool
} }
const (
passPhraseFileStdin = "-"
)
// NewPackage creates a new Package object with the given configuration. // NewPackage creates a new Package object with the given configuration.
func NewPackage() *Package { func NewPackage() *Package {
return &Package{} return &Package{}
@ -128,7 +133,7 @@ func (p *Package) Clearsign(filename string) error {
passphraseFetcher := promptUser passphraseFetcher := promptUser
if p.PassphraseFile != "" { if p.PassphraseFile != "" {
passphraseFetcher, err = passphraseFileFetcher(p.PassphraseFile, os.Stdin) passphraseFetcher, err = p.passphraseFileFetcher(p.PassphraseFile, os.Stdin)
if err != nil { if err != nil {
return err return err
} }
@ -156,7 +161,17 @@ func promptUser(name string) ([]byte, error) {
return pw, err return pw, err
} }
func passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.PassphraseFetcher, error) { func (p *Package) passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.PassphraseFetcher, error) {
// When reading from stdin we cache the passphrase here. If we are
// packaging multiple charts, we reuse the cached passphrase. This
// allows giving the passphrase once on stdin without failing with
// complaints about stdin already being closed.
//
// An alternative to this would be to omit file.Close() for stdin
// below and require the user to provide the same passphrase once
// per chart on stdin, but that does not seem very user-friendly.
if p.cachedPassphrase == nil {
file, err := openPassphraseFile(passphraseFile, stdin) file, err := openPassphraseFile(passphraseFile, stdin)
if err != nil { if err != nil {
return nil, err return nil, err
@ -168,13 +183,20 @@ func passphraseFileFetcher(passphraseFile string, stdin *os.File) (provenance.Pa
if err != nil { if err != nil {
return nil, err return nil, err
} }
p.cachedPassphrase = passphrase
return func(_ string) ([]byte, error) { return func(_ string) ([]byte, error) {
return passphrase, nil return passphrase, nil
}, nil }, nil
}
return func(_ string) ([]byte, error) {
return p.cachedPassphrase, nil
}, nil
} }
func openPassphraseFile(passphraseFile string, stdin *os.File) (*os.File, error) { func openPassphraseFile(passphraseFile string, stdin *os.File) (*os.File, error) {
if passphraseFile == "-" { if passphraseFile == passPhraseFileStdin {
stat, err := stdin.Stat() stat, err := stdin.Stat()
if err != nil { if err != nil {
return nil, err return nil, err

@ -29,8 +29,9 @@ import (
func TestPassphraseFileFetcher(t *testing.T) { func TestPassphraseFileFetcher(t *testing.T) {
secret := "secret" secret := "secret"
directory := ensure.TempFile(t, "passphrase-file", []byte(secret)) directory := ensure.TempFile(t, "passphrase-file", []byte(secret))
testPkg := NewPackage()
fetcher, err := passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil) fetcher, err := testPkg.passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil)
if err != nil { if err != nil {
t.Fatal("Unable to create passphraseFileFetcher", err) t.Fatal("Unable to create passphraseFileFetcher", err)
} }
@ -48,8 +49,9 @@ func TestPassphraseFileFetcher(t *testing.T) {
func TestPassphraseFileFetcher_WithLineBreak(t *testing.T) { func TestPassphraseFileFetcher_WithLineBreak(t *testing.T) {
secret := "secret" secret := "secret"
directory := ensure.TempFile(t, "passphrase-file", []byte(secret+"\n\n.")) directory := ensure.TempFile(t, "passphrase-file", []byte(secret+"\n\n."))
testPkg := NewPackage()
fetcher, err := passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil) fetcher, err := testPkg.passphraseFileFetcher(path.Join(directory, "passphrase-file"), nil)
if err != nil { if err != nil {
t.Fatal("Unable to create passphraseFileFetcher", err) t.Fatal("Unable to create passphraseFileFetcher", err)
} }
@ -66,17 +68,48 @@ func TestPassphraseFileFetcher_WithLineBreak(t *testing.T) {
func TestPassphraseFileFetcher_WithInvalidStdin(t *testing.T) { func TestPassphraseFileFetcher_WithInvalidStdin(t *testing.T) {
directory := t.TempDir() directory := t.TempDir()
testPkg := NewPackage()
stdin, err := os.CreateTemp(directory, "non-existing") stdin, err := os.CreateTemp(directory, "non-existing")
if err != nil { if err != nil {
t.Fatal("Unable to create test file", err) t.Fatal("Unable to create test file", err)
} }
if _, err := passphraseFileFetcher("-", stdin); err == nil { if _, err := testPkg.passphraseFileFetcher("-", stdin); err == nil {
t.Error("Expected passphraseFileFetcher returning an error") t.Error("Expected passphraseFileFetcher returning an error")
} }
} }
func TestPassphraseFileFetcher_WithStdinAndMultipleFetches(t *testing.T) {
testPkg := NewPackage()
stdin, w, err := os.Pipe()
if err != nil {
t.Fatal("Unable to create pipe", err)
}
passphrase := "secret-from-stdin"
go func() {
w.Write([]byte(passphrase + "\n"))
}()
for i := 0; i < 4; i++ {
fetcher, err := testPkg.passphraseFileFetcher("-", stdin)
if err != nil {
t.Errorf("Expected passphraseFileFetcher to not return an error, but got %v", err)
}
pass, err := fetcher("key")
if err != nil {
t.Errorf("Expected passphraseFileFetcher invocation to succeed, failed with %v", err)
}
if string(pass) != string(passphrase) {
t.Errorf("Expected multiple passphrase fetch to return %q, got %q", passphrase, pass)
}
}
}
func TestValidateVersion(t *testing.T) { func TestValidateVersion(t *testing.T) {
type args struct { type args struct {
ver string ver string

Loading…
Cancel
Save