1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-02-01 13:07:49 +02:00

fix: sign with cert only (#2757)

* fix: sign with cert only

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* test: coverage

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* chore: style

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* fix: empty sig

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* fix: log

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>

* chore: fmt

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>
This commit is contained in:
Carlos Alexandro Becker 2021-12-16 13:41:50 -03:00 committed by GitHub
parent edc8edc1ca
commit 994cbb47c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 29 deletions

View File

@ -6,6 +6,7 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/apex/log"
@ -125,6 +126,29 @@ func sign(ctx *context.Context, cfg config.Sign, artifacts []*artifact.Artifact)
return nil
}
func relativeToDist(dist, f string) (string, error) {
af, err := filepath.Abs(f)
if err != nil {
return "", err
}
df, err := filepath.Abs(dist)
if err != nil {
return "", err
}
if strings.HasPrefix(af, df) {
return f, nil
}
return filepath.Join(dist, f), nil
}
func tmplPath(ctx *context.Context, env map[string]string, s string) (string, error) {
result, err := tmpl.New(ctx).WithEnv(env).Apply(expand(s, env))
if err != nil || result == "" {
return "", err
}
return relativeToDist(ctx.Config.Dist, result)
}
func signone(ctx *context.Context, cfg config.Sign, art *artifact.Artifact) ([]*artifact.Artifact, error) {
env := ctx.Env.Copy()
env["artifactName"] = art.Name // shouldn't be used
@ -140,13 +164,13 @@ func signone(ctx *context.Context, cfg config.Sign, art *artifact.Artifact) ([]*
env[k] = v
}
name, err := tmpl.New(ctx).WithEnv(env).Apply(expand(cfg.Signature, env))
name, err := tmplPath(ctx, env, cfg.Signature)
if err != nil {
return nil, fmt.Errorf("sign failed: %s: %w", art.Name, err)
}
env["signature"] = name
cert, err := tmpl.New(ctx).WithEnv(env).Apply(expand(cfg.Certificate, env))
cert, err := tmplPath(ctx, env, cfg.Certificate)
if err != nil {
return nil, fmt.Errorf("sign failed: %s: %w", art.Name, err)
}
@ -180,6 +204,12 @@ func signone(ctx *context.Context, cfg config.Sign, art *artifact.Artifact) ([]*
}
fields := log.Fields{"cmd": cfg.Cmd, "artifact": art.Name}
if name != "" {
fields["signature"] = name
}
if cert != "" {
fields["certificate"] = cert
}
// The GoASTScanner flags this as a security risk.
// However, this works as intended. The nosec annotation
@ -199,24 +229,22 @@ func signone(ctx *context.Context, cfg config.Sign, art *artifact.Artifact) ([]*
return nil, fmt.Errorf("sign: %s failed: %w: %s", cfg.Cmd, err, b.String())
}
if cfg.Signature == "" {
return nil, nil
}
var result []*artifact.Artifact
// re-execute template results, using artifact desc as artifact so they eval to the actual needed file desc.
env["artifact"] = art.Name
name, _ = tmpl.New(ctx).WithEnv(env).Apply(expand(cfg.Signature, env)) // could never error as it passed the previous check
cert, _ = tmpl.New(ctx).WithEnv(env).Apply(expand(cfg.Certificate, env)) // could never error as it passed the previous check
result := []*artifact.Artifact{
{
if cfg.Signature != "" {
result = append(result, &artifact.Artifact{
Type: artifact.Signature,
Name: name,
Path: env["signature"],
Extra: map[string]interface{}{
artifact.ExtraID: cfg.ID,
},
},
})
}
if cert != "" {

View File

@ -2,9 +2,12 @@ package sign
import (
"os"
"path/filepath"
"strings"
"testing"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/gio"
"github.com/goreleaser/goreleaser/internal/testlib"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
@ -49,9 +52,9 @@ func TestDockerSignInvalidArtifacts(t *testing.T) {
func TestDockerSignArtifacts(t *testing.T) {
testlib.CheckPath(t, "cosign")
key := "testdata/cosign/cosign.key"
key := "cosign.key"
cmd := "sh"
args := []string{"-c", "echo ${artifact} > ${signature} && cosign sign -key=" + key + " -upload=false ${artifact} > ${signature}"}
args := []string{"-c", "echo ${artifact} > ${signature} && cosign sign --key=" + key + " --upload=false ${artifact} > ${signature}"}
password := "password"
img1 := "ghcr.io/caarlos0/goreleaser-docker-manifest-actions-example:1.2.1-amd64"
@ -69,34 +72,50 @@ func TestDockerSignArtifacts(t *testing.T) {
Artifacts: "all",
Stdin: &password,
Cmd: "cosign",
Args: []string{"sign", "--key=" + key, "-upload=false", "${artifact}"},
Args: []string{"sign", "--key=" + key, "--upload=false", "${artifact}"},
},
},
},
"only certificate": {
Expected: []string{
"ghcrio-caarlos0-goreleaser-docker-manifest-actions-example-121-amd64.pem",
"ghcrio-caarlos0-goreleaser-docker-manifest-actions-example-121-arm64v8.pem",
"ghcrio-caarlos0-goreleaser-docker-manifest-actions-example-121.pem",
},
Signs: []config.Sign{
{
Artifacts: "all",
Stdin: &password,
Cmd: "cosign",
Certificate: `{{ replace (replace (replace .Env.artifact "/" "-") ":" "-") "." "" }}.pem`,
Args: []string{"sign", "--output-certificate=${certificate}", "--key=" + key, "--upload=false", "${artifact}"},
},
},
},
"sign all": {
Expected: []string{
"testdata/cosign/all_img1.sig",
"testdata/cosign/all_img2.sig",
"testdata/cosign/all_man1.sig",
"all_img1.sig",
"all_img2.sig",
"all_man1.sig",
},
Signs: []config.Sign{
{
Artifacts: "all",
Stdin: &password,
Signature: `testdata/cosign/all_${artifactID}.sig`,
Signature: `all_${artifactID}.sig`,
Cmd: cmd,
Args: args,
},
},
},
"sign all filtering id": {
Expected: []string{"testdata/cosign/all_filter_by_id_img2.sig"},
Expected: []string{"all_filter_by_id_img2.sig"},
Signs: []config.Sign{
{
Artifacts: "all",
IDs: []string{"img2"},
Stdin: &password,
Signature: "testdata/cosign/all_filter_by_id_${artifactID}.sig",
Signature: "all_filter_by_id_${artifactID}.sig",
Cmd: cmd,
Args: args,
},
@ -104,26 +123,26 @@ func TestDockerSignArtifacts(t *testing.T) {
},
"sign images only": {
Expected: []string{
"testdata/cosign/images_img1.sig",
"testdata/cosign/images_img2.sig",
"images_img1.sig",
"images_img2.sig",
},
Signs: []config.Sign{
{
Artifacts: "images",
Stdin: &password,
Signature: "testdata/cosign/images_${artifactID}.sig",
Signature: "images_${artifactID}.sig",
Cmd: cmd,
Args: args,
},
},
},
"sign manifests only": {
Expected: []string{"testdata/cosign/manifests_man1.sig"},
Expected: []string{"manifests_man1.sig"},
Signs: []config.Sign{
{
Artifacts: "manifests",
Stdin: &password,
Signature: "testdata/cosign/manifests_${artifactID}.sig",
Signature: "manifests_${artifactID}.sig",
Cmd: cmd,
Args: args,
},
@ -134,12 +153,12 @@ func TestDockerSignArtifacts(t *testing.T) {
t.Run(name, func(t *testing.T) {
ctx := context.New(config.Project{})
ctx.Config.DockerSigns = cfg.Signs
t.Cleanup(func() {
for _, f := range cfg.Expected {
require.NoError(t, os.Remove(f))
}
})
wd, err := os.Getwd()
require.NoError(t, err)
tmp := testlib.Mktmp(t)
require.NoError(t, gio.Copy(filepath.Join(wd, "testdata/cosign/"), tmp))
ctx.Config.Dist = "dist"
require.NoError(t, os.Mkdir("dist", 0o755))
ctx.Artifacts.Add(&artifact.Artifact{
Name: img1,
@ -169,8 +188,14 @@ func TestDockerSignArtifacts(t *testing.T) {
require.NoError(t, DockerPipe{}.Default(ctx))
require.NoError(t, DockerPipe{}.Publish(ctx))
var sigs []string
for _, sig := range ctx.Artifacts.Filter(artifact.ByType(artifact.Signature)).List() {
for _, sig := range ctx.Artifacts.Filter(
artifact.Or(
artifact.ByType(artifact.Signature),
artifact.ByType(artifact.Certificate),
),
).List() {
sigs = append(sigs, sig.Name)
require.Truef(t, strings.HasPrefix(sig.Path, ctx.Config.Dist), "signature %q is not in dist dir %q", sig.Path, ctx.Config.Dist)
}
require.Equal(t, cfg.Expected, sigs)
})