Skip to content

Commit

Permalink
feat: implement dpkg-sig Package signing (#515)
Browse files Browse the repository at this point in the history
* implement dpkg-sig Package signing

* Fix dpkgsig template syntax

* Fix dpkgsig template syntax

* Correctly handle template errors when reading dpkg-sig templates

* Fix dkpgsig signature templateing

* refactor: io.Copy

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>

* refactor: deb signature

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>

* feat: acceptance test

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>

* docs: document new option

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>

* test: acceptance

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>

* test: acceptance

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>

* chore: typo

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>

Co-authored-by: Sas Swart <sas.swart@xneelo.com>
  • Loading branch information
caarlos0 and SasSwart authored Jun 12, 2022
1 parent 7309737 commit d1c1066
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 56 deletions.
30 changes: 30 additions & 0 deletions acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,36 @@ func TestDebSpecific(t *testing.T) {
}
}

func TestDebSign(t *testing.T) {
t.Parallel()
for _, arch := range formatArchs["deb"] {
for _, sigtype := range []string{"dpkg-sig", "debsign"} {
func(t *testing.T, testSigtype, testArch string) {
t.Run(fmt.Sprintf("%s/%s", testArch, testSigtype), func(t *testing.T) {
target := "signed"
if testSigtype == "dpkg-sig" {
target = "dpkg-signed"
}
t.Parallel()
if testArch == "ppc64le" && os.Getenv("NO_TEST_PPC64LE") == "true" {
t.Skip("ppc64le arch not supported in pipeline")
}
accept(t, acceptParms{
Name: fmt.Sprintf("%s_sign_%s", testSigtype, testArch),
Conf: fmt.Sprintf("deb.%s.sign.yaml", testSigtype),
Format: "deb",
Docker: dockerParams{
File: "deb.dockerfile",
Target: target,
Arch: testArch,
},
})
})
}(t, sigtype, arch)
}
}
}

type acceptParms struct {
Name string
Conf string
Expand Down
93 changes: 50 additions & 43 deletions deb/deb.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,61 +128,68 @@ func (d *Deb) Package(info *nfpm.Info, deb io.Writer) (err error) { // nolint: f
return fmt.Errorf("cannot add data.tar.gz to deb: %w", err)
}

// TODO: refactor this
method := "debsign"
if info.Deb.Signature.Method != "" {
method = info.Deb.Signature.Method
}

if info.Deb.Signature.KeyFile != "" {
var data io.Reader
var sigType string
var sig []byte

if method == "debsign" {
data = readDebsignData(debianBinary, controlTarGz, dataTarball)
sig, sigType, err := doSign(info, debianBinary, controlTarGz, dataTarball)
if err != nil {
return err
}

sigType = "origin"
if info.Deb.Signature.Type != "" {
sigType = info.Deb.Signature.Type
if err := addArFile(w, "_gpg"+sigType, sig); err != nil {
return &nfpm.ErrSigningFailure{
Err: fmt.Errorf("add signature to ar file: %w", err),
}
}
}

if sigType != "origin" && sigType != "maint" && sigType != "archive" {
return &nfpm.ErrSigningFailure{
Err: ErrInvalidSignatureType,
}
}
return nil
}

sig, err = sign.PGPArmoredDetachSignWithKeyID(data, info.Deb.Signature.KeyFile, info.Deb.Signature.KeyPassphrase, info.Deb.Signature.KeyID)
if err != nil {
return &nfpm.ErrSigningFailure{Err: err}
}
func doSign(info *nfpm.Info, debianBinary, controlTarGz, dataTarball []byte) ([]byte, string, error) {
switch info.Deb.Signature.Method {
case "dpkg-sig":
return dpkgSign(info, debianBinary, controlTarGz, dataTarball)
default:
return debSign(info, debianBinary, controlTarGz, dataTarball)
}
}

} else if method == "dpkg-sig" {
data, err := readDpkgSigData(info, debianBinary, controlTarGz, dataTarball)
if err != nil {
return &nfpm.ErrSigningFailure{Err: err}
}
func dpkgSign(info *nfpm.Info, debianBinary, controlTarGz, dataTarball []byte) ([]byte, string, error) {
sigType := "builder"
if info.Deb.Signature.Type != "" {
sigType = info.Deb.Signature.Type
}

sigType = "builder"
if info.Deb.Signature.Type != "" {
sigType = info.Deb.Signature.Type
}
data, err := readDpkgSigData(info, debianBinary, controlTarGz, dataTarball)
if err != nil {
return nil, sigType, &nfpm.ErrSigningFailure{Err: err}
}

sig, err = sign.PGPClearSignWithKeyID(data, info.Deb.Signature.KeyFile, info.Deb.Signature.KeyPassphrase, info.Deb.Signature.KeyID)
if err != nil {
return &nfpm.ErrSigningFailure{Err: err}
}
}
sig, err := sign.PGPClearSignWithKeyID(data, info.Deb.Signature.KeyFile, info.Deb.Signature.KeyPassphrase, info.Deb.Signature.KeyID)
if err != nil {
return nil, sigType, &nfpm.ErrSigningFailure{Err: err}
}
return sig, sigType, nil
}

if err := addArFile(w, "_gpg"+sigType, sig); err != nil {
return &nfpm.ErrSigningFailure{
Err: fmt.Errorf("add signature to ar file: %w", err),
}
func debSign(info *nfpm.Info, debianBinary, controlTarGz, dataTarball []byte) ([]byte, string, error) {
data := readDebsignData(debianBinary, controlTarGz, dataTarball)

sigType := "origin"
if info.Deb.Signature.Type != "" {
sigType = info.Deb.Signature.Type
}

if sigType != "origin" && sigType != "maint" && sigType != "archive" {
return nil, sigType, &nfpm.ErrSigningFailure{
Err: ErrInvalidSignatureType,
}
}

return nil
sig, err := sign.PGPArmoredDetachSignWithKeyID(data, info.Deb.Signature.KeyFile, info.Deb.Signature.KeyPassphrase, info.Deb.Signature.KeyID)
if err != nil {
return nil, sigType, &nfpm.ErrSigningFailure{Err: err}
}
return sig, sigType, nil
}

func readDebsignData(debianBinary, controlTarGz, dataTarball []byte) io.Reader {
Expand Down
14 changes: 2 additions & 12 deletions internal/sign/pgp.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,23 +101,13 @@ func PGPClearSignWithKeyID(message io.Reader, keyFile, passphrase string, hexKey
return nil, fmt.Errorf("clear sign: %w", err)
}

messageBytes, err := io.ReadAll(message)
if err != nil {
if _, err := io.Copy(writeCloser, message); err != nil {
return nil, fmt.Errorf("clear sign: %w", err)
}

writtenLength, err := writeCloser.Write(messageBytes)
if err != nil {
if err := writeCloser.Close(); err != nil {
return nil, fmt.Errorf("clear sign: %w", err)
}
if writtenLength != len(messageBytes) {
return nil, fmt.Errorf("partial signature written")
}
writeCloser.Close()

if err != nil {
return nil, fmt.Errorf("armored detach sign: %w", err)
}

return signature.Bytes(), nil
}
Expand Down
2 changes: 1 addition & 1 deletion testdata/acceptance/core.signed.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ arch: "${BUILD_ARCH}"
platform: "linux"
version: "v1.0.0"
maintainer: "John Doe <john@example.com>"
description: This package is sigend
description: This package is signed
vendor: "FooBarCorp"
homepage: "http://example.com"
contents:
Expand Down
15 changes: 15 additions & 0 deletions testdata/acceptance/deb.debsign.sign.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: "foo"
arch: "${BUILD_ARCH}"
platform: "linux"
version: "v1.0.0"
maintainer: "John Doe <john@example.com>"
description: This package is signed
vendor: "FooBarCorp"
homepage: "http://example.com"
contents:
- src: ./testdata/fake
dst: /usr/local/bin/fake
deb:
signature:
method: debsign
key_file: ./internal/sign/testdata/privkey_unprotected.asc
7 changes: 7 additions & 0 deletions testdata/acceptance/deb.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ RUN debsig-verify /tmp/foo.deb | grep "debsig: Verified package from 'Test packa
RUN echo "" > /etc/dpkg/dpkg.cfg
RUN dpkg -i /tmp/foo.deb

# ---- signed dpkg-sig test ----
FROM test_base AS dpkg-signed
RUN apt update -y
RUN apt install -y dpkg-sig
# TODO: we should properly check the signature here, not sure how to do so.
RUN dpkg-sig --verify /tmp/foo.deb | grep "UNKNOWNSIG _gpgbuilder 15BD80B3"
RUN dpkg -i /tmp/foo.deb

# ---- overrides test ----
FROM min AS overrides
Expand Down
15 changes: 15 additions & 0 deletions testdata/acceptance/deb.dpkg-sig.sign.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: "foo"
arch: "${BUILD_ARCH}"
platform: "linux"
version: "v1.0.0"
maintainer: "John Doe <john@example.com>"
description: This package is signed
vendor: "FooBarCorp"
homepage: "http://example.com"
contents:
- src: ./testdata/fake
dst: /usr/local/bin/fake
deb:
signature:
method: dpkg-sig
key_file: ./internal/sign/testdata/privkey_unprotected.asc
3 changes: 3 additions & 0 deletions www/docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,9 @@ deb:

# The package is signed if a key_file is set
signature:
# Signature method, either "dpkg-sig" or "debsign".
# Defaults to "debsign"
method: dpkg-sig
# PGP secret key (can also be ASCII-armored). The passphrase is taken
# from the environment variable $NFPM_DEB_PASSPHRASE with a fallback
# to $NFPM_PASSPHRASE.
Expand Down

0 comments on commit d1c1066

Please sign in to comment.