Merge pull request #30718 from klihub/fixes/package/multiple-charts-single-passwd-from-stdin

Allow signing multiple charts with a single passphrase from stdin.
pull/30766/head
Robert Sirchia 5 months ago committed by GitHub
commit 8733103743
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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