Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compatibility with GnuPG 1.x and 2.x, auto-detect GnuPG version #779

Merged
merged 2 commits into from
Oct 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ system/env/

# created by make build for release artifacts
build/

pgp/keyrings/aptly2*.gpg
pgp/keyrings/aptly2*.gpg~
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ addons:
packages:
- python-virtualenv
- graphviz
- gnupg2
- gpgv2

env:
global:
Expand Down
2 changes: 1 addition & 1 deletion cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ package environment to new version.`,
cmd.Flag.Bool("dep-verbose-resolve", false, "when processing dependencies, print detailed logs")
cmd.Flag.String("architectures", "", "list of architectures to consider during (comma-separated), default to all available")
cmd.Flag.String("config", "", "location of configuration file (default locations are /etc/aptly.conf, ~/.aptly.conf)")
cmd.Flag.String("gpg-provider", "", "PGP implementation (\"gpg\" for external gpg or \"internal\" for Go internal implementation)")
cmd.Flag.String("gpg-provider", "", "PGP implementation (\"gpg\", \"gpg1\", \"gpg2\" for external gpg or \"internal\" for Go internal implementation)")

if aptly.EnableDebug {
cmd.Flag.String("cpuprofile", "", "write cpu profile to file")
Expand Down
34 changes: 27 additions & 7 deletions context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,35 +387,55 @@ 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
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
Expand Down
18 changes: 15 additions & 3 deletions man/aptly.1
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "APTLY" "1" "November 2017" "" ""
.TH "APTLY" "1" "September 2018" "" ""
.
.SH "NAME"
\fBaptly\fR \- Debian repository management tool
Expand Down Expand Up @@ -150,7 +150,7 @@ don\(cqt verify remote mirrors with gpg(1), also can be disabled on per\-mirror
.
.TP
\fBgpgProvider\fR
implementation of PGP signing/validation \- \fBgpg\fR for external \fBgpg\fR utility or \fBinternal\fR to use Go internal implementation
implementation of PGP signing/validation \- \fBgpg\fR for external \fBgpg\fR utility or \fBinternal\fR to use Go internal implementation; \fBgpg1\fR might be used to force use of GnuPG 1\.x, \fBgpg2\fR enables GnuPG 2\.x only; default is to use GnuPG 1\.x if available and GnuPG 2\.x otherwise
.
.TP
\fBdownloadSourcePackages\fR
Expand Down Expand Up @@ -434,7 +434,7 @@ when processing dependencies, print detailed logs
.
.TP
\-\fBgpg\-provider\fR=
PGP implementation ("gpg" for external gpg or "internal" for Go internal implementation)
PGP implementation ("gpg", "gpg1", "gpg2" for external gpg or "internal" for Go internal implementation)
.
.SH "CREATE NEW MIRROR"
\fBaptly\fR \fBmirror\fR \fBcreate\fR \fIname\fR \fIarchive url\fR \fIdistribution\fR [\fIcomponent1\fR \|\.\|\.\|\.]
Expand Down Expand Up @@ -2038,5 +2038,17 @@ Matt Martyn (https://github\.com/MMartyn)
.IP "\[ci]" 4
Ludovico Cavedon (https://github\.com/cavedon)
.
.IP "\[ci]" 4
Petr Jediny (https://github\.com/pjediny)
.
.IP "\[ci]" 4
Maximilian Stein (https://github\.com/steinymity)
.
.IP "\[ci]" 4
Strajan Sebastian (https://github\.com/strajansebastian)
.
.IP "\[ci]" 4
Artem Smirnov (https://github\.com/urpylka)
.
.IP "" 0

4 changes: 3 additions & 1 deletion man/aptly.1.ronn.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ Options:

* `gpgProvider`:
implementation of PGP signing/validation - `gpg` for external `gpg` utility or
`internal` to use Go internal implementation
`internal` to use Go internal implementation; `gpg1` might be used to force use
of GnuPG 1.x, `gpg2` enables GnuPG 2.x only; default is to use GnuPG 1.x if
available and GnuPG 2.x otherwise

* `downloadSourcePackages`:
if enabled, all mirrors created would have flag set to download source packages;
Expand Down
76 changes: 21 additions & 55 deletions pgp/gnupg.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pgp
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
Expand All @@ -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
Expand Down Expand Up @@ -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 == GPG1x {
args = append(args, "--secret-keyring", g.secretKeyring)
}

Expand All @@ -64,7 +63,9 @@ func (g *GpgSigner) gpgArgs() []string {
}

if g.passphrase != "" || g.passphraseFile != "" {
args = append(args, "--no-use-agent")
if g.version == GPG1x {
args = append(args, "--no-use-agent")
}
}

if g.passphrase != "" {
Expand All @@ -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 == GPG21xPlus {
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
Loading