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:
parent
edc8edc1ca
commit
994cbb47c3
@ -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 != "" {
|
||||
|
@ -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)
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user