diff --git a/.travis.yml b/.travis.yml index e11af1b46..26be59df1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ addons: packages: - python-virtualenv - graphviz + - gnupg2 env: global: diff --git a/context/context.go b/context/context.go index eae6f1b53..eb7c88854 100644 --- a/context/context.go +++ b/context/context.go @@ -387,23 +387,42 @@ func (context *AptlyContext) pgpProvider() string { provider = context.config().GpgProvider } - if !(provider == "gpg" || provider == "internal") { // nolint: goconst + switch provider { + case "gpg": // nolint: goconst + case "gpg1": // nolint: goconst + case "gpg2": // nolint: goconst + case "internal": // nolint: goconst + default: Fatal(fmt.Errorf("unknown gpg provider: %v", provider)) } return provider } +func (context *AptlyContext) getGPGFinder(provider string) pgp.GPGFinder { + switch context.pgpProvider() { + case "gpg1": + return pgp.GPG1Finder() + case "gpg2": + return pgp.GPG2Finder() + case "gpg": + return pgp.GPGDefaultFinder() + } + + panic("uknown GPG provider type") +} + // GetSigner returns Signer with respect to provider func (context *AptlyContext) GetSigner() pgp.Signer { context.Lock() defer context.Unlock() - if context.pgpProvider() == "gpg" { // nolint: goconst - return pgp.NewGpgSigner() + provider := context.pgpProvider() + if provider == "internal" { // nolint: goconst + return &pgp.GoSigner{} } - return &pgp.GoSigner{} + return pgp.NewGpgSigner(context.getGPGFinder(provider)) } // GetVerifier returns Verifier with respect to provider @@ -411,11 +430,12 @@ func (context *AptlyContext) GetVerifier() pgp.Verifier { context.Lock() defer context.Unlock() - if context.pgpProvider() == "gpg" { // nolint: goconst - return pgp.NewGpgVerifier() + provider := context.pgpProvider() + if provider == "internal" { // nolint: goconst + return &pgp.GoVerifier{} } - return &pgp.GoVerifier{} + return pgp.NewGpgVerifier(context.getGPGFinder(provider)) } // UpdateFlags sets internal copy of flags in the context diff --git a/pgp/gnupg.go b/pgp/gnupg.go index ba5f24b3e..a4d6c21bb 100644 --- a/pgp/gnupg.go +++ b/pgp/gnupg.go @@ -3,6 +3,7 @@ package pgp import ( "bufio" "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -18,12 +19,10 @@ var ( _ Verifier = &GpgVerifier{} ) -// Skip GPG version check for GPG 1.x -var skipGPGVersionCheck bool - // GpgSigner is implementation of Signer interface using gpg as external program type GpgSigner struct { gpg string + version GPGVersion keyRef string keyring, secretKeyring string passphrase, passphraseFile string @@ -55,7 +54,7 @@ func (g *GpgSigner) gpgArgs() []string { if g.keyring != "" { args = append(args, "--no-auto-check-trustdb", "--no-default-keyring", "--keyring", g.keyring) } - if g.secretKeyring != "" { + if g.secretKeyring != "" && g.version == 1 { args = append(args, "--secret-keyring", g.secretKeyring) } @@ -64,7 +63,9 @@ func (g *GpgSigner) gpgArgs() []string { } if g.passphrase != "" || g.passphraseFile != "" { - args = append(args, "--no-use-agent") + if g.version == 1 { + args = append(args, "--no-use-agent") + } } if g.passphrase != "" { @@ -77,53 +78,21 @@ func (g *GpgSigner) gpgArgs() []string { if g.batch { args = append(args, "--no-tty", "--batch") - } - - return args -} - -func cliVersionCheck(cmd string, marker string) bool { - output, err := exec.Command(cmd, "--version").CombinedOutput() - if err != nil { - return false - } - return skipGPGVersionCheck || strings.Contains(string(output), marker) -} - -func findSuitableCLI(cmds []string, versionMarker string) string { - for _, cmd := range cmds { - if cliVersionCheck(cmd, versionMarker) { - return cmd + if g.version == 2 { + args = append(args, "--pinentry-mode", "loopback") } } - return "" -} - -// We only support gpg1 at this time. Make sure we find a suitable binary. -func findGPG1() (string, error) { - cmd := findSuitableCLI([]string{"gpg", "gpg1"}, "gpg (GnuPG) 1.") - if cmd != "" { - return cmd, nil - } - return "", fmt.Errorf("Couldn't find a suitable gpg executable. Make sure gnupg1 is available as either gpg or gpg1 in $PATH") -} -// We only support gpgv1 at this time. Make sure we find a suitable binary. -func findGPGV1() (string, error) { - cmd := findSuitableCLI([]string{"gpgv", "gpgv1"}, "gpgv (GnuPG) 1.") - if cmd != "" { - return cmd, nil - } - return "", fmt.Errorf("Couldn't find a suitable gpgv executable. Make sure gpgv1 is available as either gpgv or gpgv1 in $PATH") + return args } // NewGpgSigner creates a new gpg signer -func NewGpgSigner() *GpgSigner { - gpg, err := findGPG1() +func NewGpgSigner(finder GPGFinder) *GpgSigner { + gpg, version, err := finder.FindGPG() if err != nil { panic(err) } - return &GpgSigner{gpg: gpg} + return &GpgSigner{gpg: gpg, version: version} } // Init verifies availability of gpg & presence of keys @@ -171,22 +140,27 @@ func (g *GpgSigner) ClearSign(source string, destination string) error { type GpgVerifier struct { gpg string gpgv string + version GPGVersion keyRings []string } // NewGpgVerifier creates a new gpg verifier -func NewGpgVerifier() *GpgVerifier { - gpg, err := findGPG1() +func NewGpgVerifier(finder GPGFinder) *GpgVerifier { + gpg, versionGPG, err := finder.FindGPG() if err != nil { panic(err) } - gpgv, err := findGPGV1() + gpgv, versionGPGV, err := finder.FindGPGV() if err != nil { panic(err) } - return &GpgVerifier{gpg: gpg, gpgv: gpgv} + if versionGPG != versionGPGV { + panic(errors.New("gpg and gpgv versions don't match")) + } + + return &GpgVerifier{gpg: gpg, gpgv: gpgv, version: versionGPG} } // InitKeyring verifies that gpg is installed and some keys are trusted @@ -417,11 +391,3 @@ func (g *GpgVerifier) ExtractClearsigned(clearsigned io.Reader) (text *os.File, return } - -func init() { - skipCheck := os.Getenv("APTLY_SKIP_GPG_VERSION_CHECK") - switch strings.ToLower(skipCheck) { - case "1", "y", "yes", "true": - skipGPGVersionCheck = true - } -} diff --git a/pgp/gnupg_finder.go b/pgp/gnupg_finder.go new file mode 100644 index 000000000..18a0c0169 --- /dev/null +++ b/pgp/gnupg_finder.go @@ -0,0 +1,134 @@ +package pgp + +import ( + "errors" + "os/exec" + "strings" +) + +// Skip GPG version check for GPG 1.x +var skipGPGVersionCheck bool + +// GPGVersion stores discovered GPG version +// +// 1 for 1.x, and 2 for 2.x +type GPGVersion int + +// GPGFinder implement search for gpg executables and returns version of discovered executables +type GPGFinder interface { + FindGPG() (gpg string, version GPGVersion, err error) + FindGPGV() (gpgv string, version GPGVersion, err error) +} + +type pathGPGFinder struct { + gpgNames []string + gpgvNames []string + execPath string + errorMessage string + + expectedVersionSubstring string + version GPGVersion +} + +type iteratingGPGFinder struct { + finders []GPGFinder + errorMessage string +} + +// GPGDefaultFinder looks for GPG1 first, but falls back to GPG2 if GPG1 is not available +func GPGDefaultFinder() GPGFinder { + return &iteratingGPGFinder{ + finders: []GPGFinder{GPG1Finder(), GPG2Finder()}, + errorMessage: "Couldn't find a suitable gpg executable. Make sure gnupg is installed", + } +} + +// GPG1Finder looks for GnuPG1.x only +func GPG1Finder() GPGFinder { + return &pathGPGFinder{ + gpgNames: []string{"gpg", "gpg1"}, + gpgvNames: []string{"gpgv", "gpgv1"}, + expectedVersionSubstring: "(GnuPG) 1.", + errorMessage: "Couldn't find a suitable gpg executable. Make sure gnupg1 is available as either gpg(v) or gpg(v)1 in $PATH", + version: 1, + } +} + +// GPG2Finder looks for GnuPG2.x only +func GPG2Finder() GPGFinder { + return &pathGPGFinder{ + gpgNames: []string{"gpg", "gpg2"}, + gpgvNames: []string{"gpgv", "gpgv2"}, + expectedVersionSubstring: "(GnuPG) 2.", + errorMessage: "Couldn't find a suitable gpg executable. Make sure gnupg2 is available as either gpg(v) or gpg(v)2 in $PATH", + version: 2, + } +} + +func (pgf *pathGPGFinder) FindGPG() (gpg string, version GPGVersion, err error) { + for _, cmd := range pgf.gpgNames { + if cliVersionCheck(cmd, pgf.expectedVersionSubstring) { + gpg = cmd + break + } + } + + version = pgf.version + + if gpg == "" { + err = errors.New(pgf.errorMessage) + } + + return +} + +func (pgf *pathGPGFinder) FindGPGV() (gpgv string, version GPGVersion, err error) { + for _, cmd := range pgf.gpgvNames { + if cliVersionCheck(cmd, pgf.expectedVersionSubstring) { + gpgv = cmd + break + } + } + + version = pgf.version + + if gpgv == "" { + err = errors.New(pgf.errorMessage) + } + + return +} + +func (it *iteratingGPGFinder) FindGPG() (gpg string, version GPGVersion, err error) { + for _, finder := range it.finders { + gpg, version, err = finder.FindGPG() + if err == nil { + return + } + } + + err = errors.New(it.errorMessage) + + return +} + +func (it *iteratingGPGFinder) FindGPGV() (gpg string, version GPGVersion, err error) { + for _, finder := range it.finders { + gpg, version, err = finder.FindGPGV() + if err == nil { + return + } + } + + err = errors.New(it.errorMessage) + + return +} + +func cliVersionCheck(cmd string, marker string) bool { + output, err := exec.Command(cmd, "--version").CombinedOutput() + if err != nil { + return false + } + return strings.Contains(string(output), marker) +} diff --git a/pgp/gnupg_test.go b/pgp/gnupg_test.go index 30e93ca30..6b9679b9f 100644 --- a/pgp/gnupg_test.go +++ b/pgp/gnupg_test.go @@ -2,6 +2,7 @@ package pgp import ( "os" + "os/exec" "path/filepath" "runtime" @@ -26,7 +27,7 @@ func (s *GnupgSuite) TestGPG1(c *C) { os.Setenv("PATH", filepath.Join(s.bins, "gpg1")) defer func() { os.Setenv("PATH", origPath) }() - signer := NewGpgSigner() + signer := NewGpgSigner(GPG1Finder()) c.Assert(signer.gpg, Equals, "gpg") } @@ -36,7 +37,7 @@ func (s *GnupgSuite) TestGPG1Not2(c *C) { os.Setenv("PATH", filepath.Join(s.bins, "gpg2-and-1")) defer func() { os.Setenv("PATH", origPath) }() - signer := NewGpgSigner() + signer := NewGpgSigner(GPG1Finder()) c.Assert(signer.gpg, Equals, "gpg1") } @@ -46,7 +47,7 @@ func (s *GnupgSuite) TestGPGNothing(c *C) { os.Setenv("PATH", filepath.Join(s.bins, "gpg2-only")) defer func() { os.Setenv("PATH", origPath) }() - c.Assert(func() { NewGpgSigner() }, PanicMatches, `Couldn't find a suitable gpg executable.+`) + c.Assert(func() { NewGpgSigner(GPG1Finder()) }, PanicMatches, `Couldn't find a suitable gpg executable.+`) } // If gpgv == gpgv1 = pick gpgv @@ -55,7 +56,7 @@ func (s *GnupgSuite) TestGPGV1(c *C) { os.Setenv("PATH", filepath.Join(s.bins, "gpgv1")+":"+filepath.Join(s.bins, "gpg1")) defer func() { os.Setenv("PATH", origPath) }() - verifier := NewGpgVerifier() + verifier := NewGpgVerifier(GPG1Finder()) c.Assert(verifier.gpgv, Equals, "gpgv") } @@ -65,7 +66,7 @@ func (s *GnupgSuite) TestGPGV1Not2(c *C) { os.Setenv("PATH", filepath.Join(s.bins, "gpgv2-and-1")+":"+filepath.Join(s.bins, "gpg1")) defer func() { os.Setenv("PATH", origPath) }() - verifier := NewGpgVerifier() + verifier := NewGpgVerifier(GPG1Finder()) c.Assert(verifier.gpgv, Equals, "gpgv1") } @@ -75,7 +76,7 @@ func (s *GnupgSuite) TestGPGVNothing(c *C) { os.Setenv("PATH", filepath.Join(s.bins, "gpgv2-only")+":"+filepath.Join(s.bins, "gpg1")) defer func() { os.Setenv("PATH", origPath) }() - c.Assert(func() { NewGpgVerifier() }, PanicMatches, `Couldn't find a suitable gpgv executable.+`) + c.Assert(func() { NewGpgVerifier(GPG1Finder()) }, PanicMatches, `Couldn't find a suitable gpg executable.+`) } type Gnupg1VerifierSuite struct { @@ -85,7 +86,13 @@ type Gnupg1VerifierSuite struct { var _ = Suite(&Gnupg1VerifierSuite{}) func (s *Gnupg1VerifierSuite) SetUpTest(c *C) { - s.verifier = NewGpgVerifier() + finder := GPG1Finder() + _, _, err := finder.FindGPG() + if err != nil { + c.Skip(err.Error()) + } + + s.verifier = NewGpgVerifier(finder) s.verifier.AddKeyring("./trusted.gpg") c.Assert(s.verifier.InitKeyring(), IsNil) @@ -98,7 +105,18 @@ type Gnupg1SignerSuite struct { var _ = Suite(&Gnupg1SignerSuite{}) func (s *Gnupg1SignerSuite) SetUpTest(c *C) { - s.signer = NewGpgSigner() + finder := GPG1Finder() + _, _, err := finder.FindGPG() + if err != nil { + c.Skip(err.Error()) + } + + s.keyringNoPassphrase = [2]string{"keyrings/aptly.pub", "keyrings/aptly.sec"} + s.keyringPassphrase = [2]string{"keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec"} + s.passphraseKey = "F30E8CB9CDDE2AF8" + s.noPassphraseKey = "21DBB89C16DB3E6D" + + s.signer = NewGpgSigner(finder) s.signer.SetBatch(true) s.verifier = &GoVerifier{} @@ -109,3 +127,63 @@ func (s *Gnupg1SignerSuite) SetUpTest(c *C) { s.SignerSuite.SetUpTest(c) } + +type Gnupg2VerifierSuite struct { + VerifierSuite +} + +var _ = Suite(&Gnupg2VerifierSuite{}) + +func (s *Gnupg2VerifierSuite) SetUpTest(c *C) { + finder := GPG2Finder() + _, _, err := finder.FindGPG() + if err != nil { + c.Skip(err.Error()) + } + + s.verifier = NewGpgVerifier(finder) + s.verifier.AddKeyring("./trusted.gpg") + + c.Assert(s.verifier.InitKeyring(), IsNil) +} + +type Gnupg2SignerSuite struct { + SignerSuite +} + +var _ = Suite(&Gnupg2SignerSuite{}) + +func (s *Gnupg2SignerSuite) SetUpTest(c *C) { + finder := GPG2Finder() + gpg, _, err := finder.FindGPG() + if err != nil { + c.Skip(err.Error()) + } + + output, err := exec.Command(gpg, "--import", "keyrings/aptly2.sec.armor").CombinedOutput() + c.Log(string(output)) + c.Check(err, IsNil) + + output, err = exec.Command(gpg, "--import", "--passphrase", "verysecret", "--no-tty", "--batch", "--pinentry-mode", "loopback", "keyrings/aptly2_passphrase.sec.armor").CombinedOutput() + c.Log(string(output)) + c.Check(err, IsNil) + + s.keyringNoPassphrase = [2]string{"keyrings/aptly2.gpg", ""} + s.keyringPassphrase = [2]string{"keyrings/aptly2_passphrase.gpg", ""} + s.noPassphraseKey = "751DF85C2B220D45" + s.passphraseKey = "6656CD181E92D2D5" + + s.signer = NewGpgSigner(finder) + s.signer.SetBatch(true) + + s.verifier = &GoVerifier{} + s.verifier.AddKeyring("./keyrings/aptly2_trusted.pub") + + c.Assert(s.verifier.InitKeyring(), IsNil) + + s.SignerSuite.SetUpTest(c) +} + +func (s *Gnupg2SignerSuite) TearDownTest(c *C) { + s.SignerSuite.TearDownTest(c) +} diff --git a/pgp/internal_test.go b/pgp/internal_test.go index 79fd60d57..1780e73dc 100644 --- a/pgp/internal_test.go +++ b/pgp/internal_test.go @@ -24,6 +24,11 @@ type GoSignerSuite struct { var _ = Suite(&GoSignerSuite{}) func (s *GoSignerSuite) SetUpTest(c *C) { + s.keyringNoPassphrase = [2]string{"keyrings/aptly.pub", "keyrings/aptly.sec"} + s.keyringPassphrase = [2]string{"keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec"} + s.passphraseKey = "F30E8CB9CDDE2AF8" + s.noPassphraseKey = "21DBB89C16DB3E6D" + s.signer = &GoSigner{} s.signer.SetBatch(true) diff --git a/pgp/keyrings/aptly2.gpg b/pgp/keyrings/aptly2.gpg new file mode 100644 index 000000000..1bc0cfebc Binary files /dev/null and b/pgp/keyrings/aptly2.gpg differ diff --git a/pgp/keyrings/aptly2.sec.armor b/pgp/keyrings/aptly2.sec.armor new file mode 100644 index 000000000..b3a96f09c --- /dev/null +++ b/pgp/keyrings/aptly2.sec.armor @@ -0,0 +1,57 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +=vqFY +-----END PGP PRIVATE KEY BLOCK----- diff --git a/pgp/keyrings/aptly2_passphrase.gpg b/pgp/keyrings/aptly2_passphrase.gpg new file mode 100644 index 000000000..46a5209d4 Binary files /dev/null and b/pgp/keyrings/aptly2_passphrase.gpg differ diff --git a/pgp/keyrings/aptly2_passphrase.sec.armor b/pgp/keyrings/aptly2_passphrase.sec.armor new file mode 100644 index 000000000..09bc6df5f --- /dev/null +++ b/pgp/keyrings/aptly2_passphrase.sec.armor @@ -0,0 +1,59 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +bt98YTvZ8TecvlNxvBHIPuD3NFXd11Tm68KeWA== +=YDL1 +-----END PGP PRIVATE KEY BLOCK----- diff --git a/pgp/keyrings/aptly2_trusted.pub b/pgp/keyrings/aptly2_trusted.pub new file mode 100644 index 000000000..9da33ce3d Binary files /dev/null and b/pgp/keyrings/aptly2_trusted.pub differ diff --git a/pgp/sign_test.go b/pgp/sign_test.go index 583329e77..8cd064e0d 100644 --- a/pgp/sign_test.go +++ b/pgp/sign_test.go @@ -20,6 +20,12 @@ type SignerSuite struct { cleartext []byte passwordFile string + + keyringNoPassphrase [2]string + keyringPassphrase [2]string + + noPassphraseKey Key + passphraseKey Key } func (s *SignerSuite) SetUpTest(c *C) { @@ -70,20 +76,23 @@ func (s *SignerSuite) testSignDetached(c *C) { } func (s *SignerSuite) TestSignDetachedNoPassphrase(c *C) { - s.signer.SetKeyRing("keyrings/aptly.pub", "keyrings/aptly.sec") + s.signer.SetKey(string(s.noPassphraseKey)) + s.signer.SetKeyRing(s.keyringNoPassphrase[0], s.keyringNoPassphrase[1]) s.testSignDetached(c) } func (s *SignerSuite) TestSignDetachedPassphrase(c *C) { - s.signer.SetKeyRing("keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec") + s.signer.SetKey(string(s.passphraseKey)) + s.signer.SetKeyRing(s.keyringPassphrase[0], s.keyringPassphrase[1]) s.signer.SetPassphrase("verysecret", "") s.testSignDetached(c) } func (s *SignerSuite) TestSignDetachedPassphraseFile(c *C) { - s.signer.SetKeyRing("keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec") + s.signer.SetKey(string(s.passphraseKey)) + s.signer.SetKeyRing(s.keyringPassphrase[0], s.keyringPassphrase[1]) s.signer.SetPassphrase("", s.passwordFile) s.testSignDetached(c) @@ -114,21 +123,24 @@ func (s *SignerSuite) testClearSign(c *C, expectedKey Key) { } func (s *SignerSuite) TestClearSignNoPassphrase(c *C) { - s.signer.SetKeyRing("keyrings/aptly.pub", "keyrings/aptly.sec") + s.signer.SetKey(string(s.noPassphraseKey)) + s.signer.SetKeyRing(s.keyringNoPassphrase[0], s.keyringNoPassphrase[1]) - s.testClearSign(c, "21DBB89C16DB3E6D") + s.testClearSign(c, s.noPassphraseKey) } func (s *SignerSuite) TestClearSignPassphrase(c *C) { - s.signer.SetKeyRing("keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec") + s.signer.SetKey(string(s.passphraseKey)) + s.signer.SetKeyRing(s.keyringPassphrase[0], s.keyringPassphrase[1]) s.signer.SetPassphrase("verysecret", "") - s.testClearSign(c, "F30E8CB9CDDE2AF8") + s.testClearSign(c, s.passphraseKey) } func (s *SignerSuite) TestClearSignPassphraseFile(c *C) { - s.signer.SetKeyRing("keyrings/aptly_passphrase.pub", "keyrings/aptly_passphrase.sec") + s.signer.SetKey(string(s.passphraseKey)) + s.signer.SetKeyRing(s.keyringPassphrase[0], s.keyringPassphrase[1]) s.signer.SetPassphrase("", s.passwordFile) - s.testClearSign(c, "F30E8CB9CDDE2AF8") + s.testClearSign(c, s.passphraseKey) } diff --git a/system/gpg-gen-key b/system/gpg-gen-key index 6af211b92..6970209ad 100644 --- a/system/gpg-gen-key +++ b/system/gpg-gen-key @@ -10,4 +10,4 @@ Expire-Date: 0 %secring aptly.sec # Do a commit here, so that we can later print "done" :-) %commit -%echo done \ No newline at end of file +%echo done diff --git a/system/lib.py b/system/lib.py index d2cd4f12e..fd8adf667 100644 --- a/system/lib.py +++ b/system/lib.py @@ -141,7 +141,16 @@ def prepare_fixture(self): self.fixtureWebServer)) if self.fixtureGpg: - self.run_cmd(["gpg", "--no-default-keyring", "--trust-model", "always", "--batch", "--keyring", "aptlytest.gpg", "--import"] + + # try to find gpg1 as that's what aptly prefers by default to build trusted keys in DB + # in lowest supported format + gpg = "gpg1" + try: + subprocess.check_output(["gpg1", "--version"]) + except: + gpg = "gpg" + + # TODO: fixme + self.run_cmd([gpg, "--no-default-keyring", "--trust-model", "always", "--batch", "--keyring", "aptlytest.gpg", "--import"] + [os.path.join(os.path.dirname(inspect.getsourcefile(BaseTest)), "files", key) for key in self.fixtureGpgKeys]) if hasattr(self, "fixtureCmds"):