From bc5ec37699e0fb6e2a18bc60cdc1ee3f6741b3f1 Mon Sep 17 00:00:00 2001 From: aalsabag <43807484+aalsabag@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:44:05 -0500 Subject: [PATCH] resolves #516 adds support for private rekor for gitsign attest (#517) * resolves #516 adds support for private rekor instances for gitsign attest Signed-off-by: Ahmed Alsabag * address comments and lint errors Signed-off-by: Ahmed Alsabag * moving the config to the base Attestor struct Signed-off-by: Ahmed Alsabag * adding missing error handling Signed-off-by: Ahmed Alsabag --------- Signed-off-by: Ahmed Alsabag --- internal/attest/attest.go | 20 ++++++++++++++++---- internal/attest/attest_test.go | 12 ++++++++++-- internal/commands/attest/attest.go | 2 +- internal/utils.go | 10 ++++++++++ internal/utils_test.go | 27 +++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 internal/utils_test.go diff --git a/internal/attest/attest.go b/internal/attest/attest.go index 7a32db04..90bbd548 100644 --- a/internal/attest/attest.go +++ b/internal/attest/attest.go @@ -26,15 +26,18 @@ import ( "sort" "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/config" + gitconfig "github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/storage" + "github.com/go-openapi/strfmt" "github.com/jonboulle/clockwork" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" "github.com/sigstore/cosign/v2/pkg/cosign/attestation" "github.com/sigstore/cosign/v2/pkg/types" + utils "github.com/sigstore/gitsign/internal" + gitsignconfig "github.com/sigstore/gitsign/internal/config" rekorclient "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/models" dssesig "github.com/sigstore/sigstore/pkg/signature/dsse" @@ -57,13 +60,15 @@ type Attestor struct { repo *git.Repository sv *sign.SignerVerifier rekorFn rekorUpload + config *gitsignconfig.Config } -func NewAttestor(repo *git.Repository, sv *sign.SignerVerifier, rekorFn rekorUpload) *Attestor { +func NewAttestor(repo *git.Repository, sv *sign.SignerVerifier, rekorFn rekorUpload, config *gitsignconfig.Config) *Attestor { return &Attestor{ repo: repo, sv: sv, rekorFn: rekorFn, + config: config, } } @@ -175,7 +180,7 @@ func (a *Attestor) WriteAttestation(ctx context.Context, refName string, sha plu // Step 3: Make the commit // Grab the user from the repository config so we know who to attribute the commit to. - cfg, err := a.repo.ConfigScoped(config.GlobalScope) + cfg, err := a.repo.ConfigScoped(gitconfig.GlobalScope) if err != nil { return plumbing.ZeroHash, err } @@ -242,8 +247,15 @@ func (a *Attestor) signPayload(ctx context.Context, sha plumbing.Hash, b []byte, return nil, err } + rekorHost, rekorBasePath := utils.StripURL(a.config.Rekor) + tc := &rekorclient.TransportConfig{ + Host: rekorHost, + BasePath: rekorBasePath, + } + rcfg := rekorclient.NewHTTPClientWithConfig(strfmt.Default, tc) + // Upload to rekor - entry, err := a.rekorFn(ctx, rekorclient.Default, envelope, a.sv.Cert) + entry, err := a.rekorFn(ctx, rcfg, envelope, a.sv.Cert) if err != nil { return nil, err } diff --git a/internal/attest/attest_test.go b/internal/attest/attest_test.go index 55c7daf6..96e3d0d1 100644 --- a/internal/attest/attest_test.go +++ b/internal/attest/attest_test.go @@ -37,6 +37,7 @@ import ( "github.com/jonboulle/clockwork" "github.com/secure-systems-lab/go-securesystemslib/dsse" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" + gitsignconfig "github.com/sigstore/gitsign/internal/config" "github.com/sigstore/rekor/pkg/generated/client" "github.com/sigstore/rekor/pkg/generated/models" "github.com/sigstore/sigstore/pkg/signature" @@ -71,7 +72,12 @@ func TestAttestCommitRef(t *testing.T) { name := "test.json" content := readFile(t, filepath.Join("testdata/", name)) - attestor := NewAttestor(repo, sv, fakeRekor) + cfg, err := gitsignconfig.Get() + if err != nil { + t.Fatal(err) + } + + attestor := NewAttestor(repo, sv, fakeRekor, cfg) fc := []fileContent{ { @@ -149,7 +155,9 @@ func TestAttestTreeRef(t *testing.T) { name := "test.json" content := readFile(t, filepath.Join("testdata", name)) - attestor := NewAttestor(repo, sv, fakeRekor) + cfg, _ := gitsignconfig.Get() + + attestor := NewAttestor(repo, sv, fakeRekor, cfg) fc := []fileContent{ { diff --git a/internal/commands/attest/attest.go b/internal/commands/attest/attest.go index a940ffda..76a7fcbe 100644 --- a/internal/commands/attest/attest.go +++ b/internal/commands/attest/attest.go @@ -84,7 +84,7 @@ func (o *options) Run(ctx context.Context) error { } defer sv.Close() - attestor := attest.NewAttestor(repo, sv, cosign.TLogUploadInTotoAttestation) + attestor := attest.NewAttestor(repo, sv, cosign.TLogUploadInTotoAttestation, o.Config) out, err := attestor.WriteFile(ctx, refName, sha, o.FlagPath, o.FlagAttestationType) if err != nil { diff --git a/internal/utils.go b/internal/utils.go index 57761a91..4c94656e 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -19,6 +19,7 @@ import ( "crypto/sha1" // #nosec G505 "crypto/x509" "encoding/hex" + "net/url" ) // certHexFingerprint calculated the hex SHA1 fingerprint of a certificate. @@ -35,3 +36,12 @@ func certFingerprint(cert *x509.Certificate) []byte { fpr := sha1.Sum(cert.Raw) // nolint:gosec return fpr[:] } + +// StripURL returns the baseHost with the basePath given a full endpoint +func StripURL(endpoint string) (string, string) { + u, err := url.Parse(endpoint) + if err != nil { + return "", "" + } + return u.Host, u.Path +} diff --git a/internal/utils_test.go b/internal/utils_test.go new file mode 100644 index 00000000..55fa5da4 --- /dev/null +++ b/internal/utils_test.go @@ -0,0 +1,27 @@ +// Copyright 2024 The Sigstore Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import ( + "testing" +) + +func TestStripUrl(t *testing.T) { + endpoint := "https://private.rekor.com/rekor" + host, basePath := StripURL(endpoint) + if host != "private.rekor.com" || basePath != "/rekor" { + t.Fatalf("Host and/or BasePath are not correct") + } +}