mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-17 20:47:50 +02:00
test: add test for sign pipeline
The unit tests needs a test key to work with. I have tried to create a test keyring on the fly and while that worked I was not able to successfully sign with that. gpg would bail with an ioctl error which I didn't track down since using a static key works.
This commit is contained in:
parent
7b95e1e342
commit
d68b864f01
@ -7,7 +7,7 @@ that the artifacts have been generated by yourself and your users can verify
|
||||
that by comparing the generated signature with your public signing key.
|
||||
|
||||
Signing works in combination with checksum files and it is generally sufficient
|
||||
to sign the checksum files only.
|
||||
to sign the checksum files only.
|
||||
|
||||
The default is configured to create a detached signature for the checksum files
|
||||
with [GunPG](https://www.gnupg.org/) and your default key. To enable signing
|
||||
@ -24,7 +24,7 @@ To customize the signing pipeline you can use the following options:
|
||||
```yml
|
||||
# .goreleaser.yml
|
||||
sign:
|
||||
# name of the signature file.
|
||||
# name of the signature file.
|
||||
# '${in}' is the path to the artifact that should be signed.
|
||||
#
|
||||
# signature: "${artifact}.sig"
|
||||
|
@ -10,12 +10,14 @@ import (
|
||||
"github.com/goreleaser/goreleaser/pipeline"
|
||||
)
|
||||
|
||||
// Pipe for artifact signing.
|
||||
type Pipe struct{}
|
||||
|
||||
func (Pipe) String() string {
|
||||
return "signing artifacts"
|
||||
}
|
||||
|
||||
// Default sets the Pipes defaults.
|
||||
func (Pipe) Default(ctx *context.Context) error {
|
||||
cfg := &ctx.Config.Sign
|
||||
if cfg.Cmd == "" {
|
||||
@ -33,6 +35,7 @@ func (Pipe) Default(ctx *context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run executes the Pipe.
|
||||
func (Pipe) Run(ctx *context.Context) error {
|
||||
switch ctx.Config.Sign.Artifacts {
|
||||
case "checksum":
|
||||
@ -85,6 +88,10 @@ func signone(ctx *context.Context, artifact string) (string, error) {
|
||||
args = append(args, expand(a, env))
|
||||
}
|
||||
|
||||
// The GoASTScanner flags this as a security risk.
|
||||
// However, this works as intended. The nosec annotation
|
||||
// tells the scanner to ignore this.
|
||||
// #nosec
|
||||
cmd := exec.Command(cfg.Cmd, args...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if len(output) > 200 {
|
||||
|
@ -1,18 +1,96 @@
|
||||
package sign
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/goreleaser/goreleaser/config"
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
gpgPlainKeyID = "0279C27FC1602A0E"
|
||||
gpgEncryptedKeyID = "2AB4ABE1A4A47546"
|
||||
gpgPassword = "secret"
|
||||
gpgHome = "./testdata/gnupg"
|
||||
)
|
||||
const keyring = "testdata/gnupg"
|
||||
|
||||
func TestDescription(t *testing.T) {
|
||||
assert.NotEmpty(t, Pipe{}.String())
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
// create temp dir for file and signature
|
||||
tmpdir, err := ioutil.TempDir("", "goreleaser")
|
||||
if err != nil {
|
||||
t.Fatal("TempDir: ", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
artifact := "foo.txt"
|
||||
signature := artifact + ".sig"
|
||||
|
||||
// create fake artifact
|
||||
file := filepath.Join(tmpdir, artifact)
|
||||
if err = ioutil.WriteFile(file, []byte("foo"), 0644); err != nil {
|
||||
t.Fatal("WriteFile: ", err)
|
||||
}
|
||||
|
||||
// fix permission on keyring dir to suppress warning about insecure permissions
|
||||
if err = os.Chmod(keyring, 0700); err != nil {
|
||||
t.Fatal("Chmod: ", err)
|
||||
}
|
||||
|
||||
// sign artifact
|
||||
ctx := &context.Context{
|
||||
Config: config.Project{
|
||||
Dist: tmpdir,
|
||||
Sign: config.Sign{
|
||||
Artifacts: "all",
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx.AddArtifact(artifact)
|
||||
|
||||
err = Pipe{}.Default(ctx)
|
||||
if err != nil {
|
||||
t.Fatal("Default: ", err)
|
||||
}
|
||||
|
||||
// make sure we are using the test keyring
|
||||
ctx.Config.Sign.Args = append([]string{"--homedir", keyring}, ctx.Config.Sign.Args...)
|
||||
|
||||
err = Pipe{}.Run(ctx)
|
||||
if err != nil {
|
||||
t.Fatal("Run: ", err)
|
||||
}
|
||||
|
||||
// verify signature was made with key for usesr 'nopass'
|
||||
if err := verifySig(t, keyring, file, filepath.Join(tmpdir, signature), "nopass"); err != nil {
|
||||
t.Fatal("verify: ", err)
|
||||
}
|
||||
|
||||
// check signature is an artifact
|
||||
assert.Equal(t, ctx.Artifacts, []string{artifact, signature})
|
||||
}
|
||||
|
||||
func verifySig(t *testing.T, keyring, file, sig, user string) error {
|
||||
cmd := exec.Command("gpg", "--homedir", keyring, "--verify", sig, file)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Log(string(out))
|
||||
return err
|
||||
}
|
||||
|
||||
// check if the signature matches the user we expect to do this properly we
|
||||
// might need to have either separate keyrings or export the key from the
|
||||
// keyring before we do the verification. For now we punt and look in the
|
||||
// output.
|
||||
if !bytes.Contains(out, []byte(user)) {
|
||||
return fmt.Errorf("signature is not from %s", user)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
1
pipeline/sign/testdata/.gitignore
vendored
Normal file
1
pipeline/sign/testdata/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
S-*
|
80
pipeline/sign/testdata/README.md
vendored
Normal file
80
pipeline/sign/testdata/README.md
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
# Creating test keys for GnuPG
|
||||
|
||||
The unit tests needs a test key to work with. I have tried to create a test keyring
|
||||
on the fly and while that worked I was not able to successfully sign with that.
|
||||
gpg would bail with an ioctl error which I didn't track down since using a static
|
||||
key works.
|
||||
|
||||
This uses the `--homedir .` option to create the test keys so that we do not touch
|
||||
the local keyring file.
|
||||
|
||||
1. Create signing keys
|
||||
|
||||
cd $GOPATH/src/github.com/goreleaser/goreleaser/pipeline/sign/testdata/gnupg
|
||||
gpg --homedir . --quick-generate-key --batch --passphrase '' nopass default default 10y
|
||||
|
||||
1. Check that the key exists
|
||||
|
||||
## $ gpg --homedir . --list-keys
|
||||
|
||||
pub rsa2048 2017-12-13 [SC][expires: 2027-12-11]
|
||||
FB6BEDFCECE1761EDD68BF32EF2D274B0EDAAE12
|
||||
uid [ultimate] nopass
|
||||
sub rsa2048 2017-12-13 [E]
|
||||
|
||||
1) Check that signing works
|
||||
|
||||
# create a test file
|
||||
|
||||
echo "bar" > foo
|
||||
|
||||
# sign and verfiy
|
||||
|
||||
gpg --homedir . --detach-sign foo
|
||||
gpg --homedir . --verify foo.sig foo
|
||||
|
||||
gpg: Signature made Wed Dec 13 22:02:49 2017 CET
|
||||
gpg: using RSA key FB6BEDFCECE1761EDD68BF32EF2D274B0EDAAE12
|
||||
gpg: Good signature from "nopass" [ultimate]
|
||||
|
||||
# cleanup
|
||||
|
||||
rm foo foo.sig
|
||||
|
||||
1) Make sure you have keyrings for both gpg1 and gpg2
|
||||
|
||||
travis-ci.org runs on an old Ubuntu installation which
|
||||
has gpg 1.4 installed. We need to provide keyrings that
|
||||
have the same keys and users for both formats.
|
||||
|
||||
This demonstrates the conversion from gpg2 to gpg1
|
||||
format but should work the same the other way around.
|
||||
|
||||
# get gpg version
|
||||
|
||||
gpg --version
|
||||
gpg (GnuPG) 2.2.3
|
||||
...
|
||||
|
||||
# install gpg1
|
||||
|
||||
brew install gpg1
|
||||
|
||||
# brew install gpg2 # if you have gpg1 installed
|
||||
|
||||
# migrate the keys from gpg2 to gpg1
|
||||
|
||||
gpg --homedir . --export nopass | gpg1 --homedir . --import
|
||||
gpg --homedir . --export-secret-key nopass | gpg1 --homedir . --import
|
||||
|
||||
# check keys are the same
|
||||
|
||||
gpg --homedir . --list-keys --keyid-format LONG
|
||||
gpg1 --homedir . --list-keys --keyid-format LONG
|
||||
|
||||
gpg --homedir . --list-secret-keys --keyid-format LONG
|
||||
gpg1 --homedir . --list-secret-keys --keyid-format LONG
|
||||
|
||||
```
|
||||
|
||||
```
|
0
pipeline/sign/testdata/gnupg/.gpg-v21-migrated
vendored
Normal file
0
pipeline/sign/testdata/gnupg/.gpg-v21-migrated
vendored
Normal file
32
pipeline/sign/testdata/gnupg/openpgp-revocs.d/23E7505EC0A490C582CB9B27F1D733BF0B343347.rev
vendored
Normal file
32
pipeline/sign/testdata/gnupg/openpgp-revocs.d/23E7505EC0A490C582CB9B27F1D733BF0B343347.rev
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
This is a revocation certificate for the OpenPGP key:
|
||||
|
||||
pub rsa2048 2017-12-13 [SC] [expires: 2027-12-11]
|
||||
23E7505EC0A490C582CB9B27F1D733BF0B343347
|
||||
uid nopass
|
||||
|
||||
A revocation certificate is a kind of "kill switch" to publicly
|
||||
declare that a key shall not anymore be used. It is not possible
|
||||
to retract such a revocation certificate once it has been published.
|
||||
|
||||
Use it to revoke this key in case of a compromise or loss of
|
||||
the secret key. However, if the secret key is still accessible,
|
||||
it is better to generate a new revocation certificate and give
|
||||
a reason for the revocation. For details see the description of
|
||||
of the gpg command "--generate-revocation" in the GnuPG manual.
|
||||
|
||||
To avoid an accidental use of this file, a colon has been inserted
|
||||
before the 5 dashes below. Remove this colon with a text editor
|
||||
before importing and publishing this revocation certificate.
|
||||
|
||||
:-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Comment: This is a revocation certificate
|
||||
|
||||
iQE2BCABCAAgFiEEI+dQXsCkkMWCy5sn8dczvws0M0cFAloxnpUCHQAACgkQ8dcz
|
||||
vws0M0dbdwf+KevSTLl688bOMMKcexoUra4porA/JJvtrkQAdHWqspqf+lQ+1rK0
|
||||
Y4YNHkXixv6+R0aoECSrnx4ehk9nLH7hx5423DXEvmPkF70rWkF0eGeG8gVrUz2O
|
||||
YWobOMldWPk6QPZ6rV5c5PdQSCx8+WHVXu/ym7u70fbmJV4IHuFlKiFUXsGk2PYj
|
||||
k38ssFeqGo1bNZlIfuCggOurpfSXhKAsLRHjJVQe7ZowmloPiwHFfMwtuiBWpsZ/
|
||||
3niEy3A5mdBC+ebtEL1KyaqjV6IN58YT3z0aBnDjA/qeweeT2jKjEWqHAPsJaJke
|
||||
AyC0e8t71lnH4RZyKyfsBveKFfC4KN9SUA==
|
||||
=DnZE
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
BIN
pipeline/sign/testdata/gnupg/private-keys-v1.d/6B365DDB6E25317FED9A739DC350E033855A833A.key
vendored
Normal file
BIN
pipeline/sign/testdata/gnupg/private-keys-v1.d/6B365DDB6E25317FED9A739DC350E033855A833A.key
vendored
Normal file
Binary file not shown.
BIN
pipeline/sign/testdata/gnupg/private-keys-v1.d/939A5E777E4F7DFF2FA87806D0155052B5FC8F56.key
vendored
Normal file
BIN
pipeline/sign/testdata/gnupg/private-keys-v1.d/939A5E777E4F7DFF2FA87806D0155052B5FC8F56.key
vendored
Normal file
Binary file not shown.
BIN
pipeline/sign/testdata/gnupg/pubring.gpg
vendored
Normal file
BIN
pipeline/sign/testdata/gnupg/pubring.gpg
vendored
Normal file
Binary file not shown.
BIN
pipeline/sign/testdata/gnupg/pubring.kbx
vendored
Normal file
BIN
pipeline/sign/testdata/gnupg/pubring.kbx
vendored
Normal file
Binary file not shown.
BIN
pipeline/sign/testdata/gnupg/secring.gpg
vendored
Normal file
BIN
pipeline/sign/testdata/gnupg/secring.gpg
vendored
Normal file
Binary file not shown.
BIN
pipeline/sign/testdata/gnupg/trustdb.gpg
vendored
Normal file
BIN
pipeline/sign/testdata/gnupg/trustdb.gpg
vendored
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user