Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat:vault signer #276

Merged
merged 8 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test-github-token.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ jobs:
run: make build

- name: Test
run: ./bin/witness run -l debug -s test -a github --enable-archivist -o test.json --fulcio https://fulcio.sigstore.dev --fulcio-oidc-client-id https://oauth2.sigstore.dev/auth --fulcio-oidc-issuer sigstore --timestamp-servers https://freetsa.org/tsr -- echo "hello" > test.txt
run: ./bin/witness run -l debug -s test -a github --enable-archivist -o test.json --signer-fulcio-url https://fulcio.sigstore.dev --signer-fulcio-oidc-issuer https://oauth2.sigstore.dev/auth --signer-fulcio-oidc-client-id sigstore --timestamp-servers https://freetsa.org/tsr -- echo "hello" > test.txt
- name: log output
run: cat test.json
run: cat test.json
10 changes: 5 additions & 5 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"errors"
"fmt"
"os"
"strings"

"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand Down Expand Up @@ -61,16 +62,15 @@ func initConfig(rootCmd *cobra.Command, rootOptions *options.RootOptions) error
if f.Value.Type() == "stringSlice" {
configValue := v.GetStringSlice(configKey)
if len(configValue) > 0 {
for _, v := range configValue {
if err := f.Value.Set(v); err != nil {
log.Errorf("failed to set config value: %s", err)
}
configValueStr := strings.Join(configValue, ",")
if err := flags.Set(f.Name, configValueStr); err != nil {
log.Errorf("failed to set config value: %s", err)
}
}
} else {
configValue := v.GetString(configKey)
if configValue != "" {
if err := f.Value.Set(configValue); err != nil {
if err := flags.Set(f.Name, configValue); err != nil {
log.Errorf("failed to set config value: %s", err)
}
}
Expand Down
69 changes: 38 additions & 31 deletions cmd/keyloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,50 +17,57 @@ package cmd
import (
"context"
"fmt"
"strings"

"github.com/spf13/pflag"
"github.com/testifysec/go-witness/cryptoutil"
"github.com/testifysec/go-witness/signer/file"
"github.com/testifysec/go-witness/signer/fulcio"
"github.com/testifysec/go-witness/signer/spiffe"
"github.com/testifysec/go-witness/log"
"github.com/testifysec/go-witness/signer"
"github.com/testifysec/witness/options"
)

func loadSigners(ctx context.Context, ko options.KeyOptions) ([]cryptoutil.Signer, []error) {
signers := []cryptoutil.Signer{}
errors := []error{}
// signerProvidersFromFlags looks at all flags that were set by the user to determine which signer providers we should use
func signerProvidersFromFlags(flags *pflag.FlagSet) map[string]struct{} {
signerProviders := make(map[string]struct{})
flags.Visit(func(flag *pflag.Flag) {
if !strings.HasPrefix(flag.Name, "signer-") {
return
}

//Load key from fulcio
if ko.FulcioURL != "" {
fulcioSigner, err := fulcio.Signer(ctx, ko.FulcioURL, ko.OIDCClientID, ko.OIDCIssuer, ko.Token)
if err != nil {
err := fmt.Errorf("failed to create signer from Fulcio: %w", err)
errors = append(errors, err)
} else {
signers = append(signers, fulcioSigner)
parts := strings.Split(flag.Name, "-")
if len(parts) < 2 {
return
}
}

//Load key from file
if ko.KeyPath != "" {
fileSigner, err := file.Signer(ctx, ko.KeyPath, ko.CertPath, ko.IntermediatePaths)
signerProviders[parts[1]] = struct{}{}
})

return signerProviders
}

// loadSigners loads all signers that appear in the signerProviders set and creates their respective signers, using any options provided in so
func loadSigners(ctx context.Context, so options.SignerOptions, signerProviders map[string]struct{}) ([]cryptoutil.Signer, error) {
signers := make([]cryptoutil.Signer, 0)
for signerProvider := range signerProviders {
setters := so[signerProvider]
sp, err := signer.NewSignerProvider(signerProvider, setters...)
if err != nil {
err := fmt.Errorf("failed to create signer from file: %w", err)
errors = append(errors, err)
} else {
signers = append(signers, fileSigner)
log.Errorf("failed to create %v signer provider: %w", signerProvider, err)
continue
}
}

//Load key from spire agent
if ko.SpiffePath != "" {
spiffeSigner, err := spiffe.Signer(ctx, ko.SpiffePath)
s, err := sp.Signer(ctx)
if err != nil {
err := fmt.Errorf("failed to create signer from spiffe: %w", err)
errors = append(errors, err)
} else {
signers = append(signers, spiffeSigner)
log.Errorf("failed to create %v signer: %w", signerProvider, err)
continue
}

signers = append(signers, s)
}

if len(signers) == 0 {
return signers, fmt.Errorf("failed to load any signers")
}

return signers, errors
return signers, nil
}
82 changes: 48 additions & 34 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/testifysec/go-witness/cryptoutil"
"github.com/testifysec/go-witness/signer"
"github.com/testifysec/go-witness/signer/file"
"github.com/testifysec/witness/options"
)

Expand All @@ -47,48 +52,57 @@ func Test_loadOutfile(t *testing.T) {
}

func Test_loadSignersKeyPair(t *testing.T) {
privatePem, _ := rsakeypair(t)

keyOptions := options.KeyOptions{
KeyPath: privatePem.Name(),
}

_, errors := loadSigners(context.Background(), keyOptions)
if len(errors) != 0 {
t.Errorf("unexpected errors: %v", errors)
}

keyOptions.KeyPath = "not-a-file"
_, errors = loadSigners(context.Background(), keyOptions)
if len(errors) != 1 {
t.Errorf("expected 1 error, got %d", len(errors))
}
t.Run("success", func(t *testing.T) {
privatePem, _ := rsakeypair(t)
signerOptions := options.SignerOptions{}
signerOptions["file"] = []func(signer.SignerProvider) (signer.SignerProvider, error){
func(sp signer.SignerProvider) (signer.SignerProvider, error) {
fsp := sp.(file.FileSignerProvider)
fsp.KeyPath = privatePem.Name()
return fsp, nil
},
}

signers, err := loadSigners(context.Background(), signerOptions, map[string]struct{}{"file": {}})
require.NoError(t, err)
require.Len(t, signers, 1)
assert.IsType(t, &cryptoutil.RSASigner{}, signers[0])
})

t.Run("failure", func(t *testing.T) {
signerOptions := options.SignerOptions{}
signerOptions["file"] = []func(signer.SignerProvider) (signer.SignerProvider, error){
func(sp signer.SignerProvider) (signer.SignerProvider, error) {
fsp := sp.(file.FileSignerProvider)
fsp.KeyPath = "not-a-file"
return fsp, nil
},
}

signers, err := loadSigners(context.Background(), signerOptions, map[string]struct{}{"file": {}})
require.Error(t, err)
require.Len(t, signers, 0)
})
}

func Test_loadSignersCertificate(t *testing.T) {
_, intermediates, leafcert, leafkey := fullChain(t)

keyOptions := options.KeyOptions{
KeyPath: leafkey.Name(),
IntermediatePaths: []string{
intermediates[0].Name(),
signerOptions := options.SignerOptions{}
signerOptions["file"] = []func(signer.SignerProvider) (signer.SignerProvider, error){
func(sp signer.SignerProvider) (signer.SignerProvider, error) {
fsp := sp.(file.FileSignerProvider)
fsp.KeyPath = leafkey.Name()
fsp.IntermediatePaths = []string{intermediates[0].Name()}
fsp.CertPath = leafcert.Name()
return fsp, nil
},
CertPath: leafcert.Name(),
}

signers, errors := loadSigners(context.Background(), keyOptions)
if len(errors) != 0 {
t.Errorf("unexpected errors: %v", errors)
}

_, err := signers[0].Verifier()
if err != nil {
t.Errorf("unexpected error: %v", err)
}

if len(signers) != 1 {
t.Errorf("expected 1 signer, got %d", len(signers))
}
signers, err := loadSigners(context.Background(), signerOptions, map[string]struct{}{"file": {}})
require.NoError(t, err)
require.Len(t, signers, 1)
require.IsType(t, &cryptoutil.X509Signer{}, signers[0])
}

func rsakeypair(t *testing.T) (privatePem *os.File, publicPem *os.File) {
Expand Down
32 changes: 14 additions & 18 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,18 @@ import (
"github.com/testifysec/go-witness/attestation/commandrun"
"github.com/testifysec/go-witness/attestation/material"
"github.com/testifysec/go-witness/attestation/product"
"github.com/testifysec/go-witness/cryptoutil"
"github.com/testifysec/go-witness/dsse"
"github.com/testifysec/go-witness/log"
"github.com/testifysec/go-witness/registry"
"github.com/testifysec/go-witness/timestamp"
"github.com/testifysec/witness/options"
)

func RunCmd() *cobra.Command {
o := options.RunOptions{
AttestorOptSetters: make(map[string][]func(attestation.Attestor) (attestation.Attestor, error)),
SignerOptions: options.SignerOptions{},
}

cmd := &cobra.Command{
Expand All @@ -43,7 +46,12 @@ func RunCmd() *cobra.Command {
SilenceErrors: true,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runRun(cmd.Context(), o, args)
signers, err := loadSigners(cmd.Context(), o.SignerOptions, signerProvidersFromFlags(cmd.Flags()))
if err != nil {
return fmt.Errorf("failed to load signers")
}

return runRun(cmd.Context(), o, args, signers...)
},
Args: cobra.ArbitraryArgs,
}
Expand All @@ -52,22 +60,12 @@ func RunCmd() *cobra.Command {
return cmd
}

func runRun(ctx context.Context, ro options.RunOptions, args []string) error {
signers, errors := loadSigners(ctx, ro.KeyOptions)
if len(errors) > 0 {
for _, err := range errors {
log.Error(err)
}
return fmt.Errorf("failed to load signers")
}

func runRun(ctx context.Context, ro options.RunOptions, args []string, signers ...cryptoutil.Signer) error {
if len(signers) > 1 {
log.Error("only one signer is supported")
return fmt.Errorf("only one signer is supported")
}

if len(signers) == 0 {
log.Error("no signers found")
return fmt.Errorf("no signers found")
}

Expand All @@ -93,16 +91,14 @@ func runRun(ctx context.Context, ro options.RunOptions, args []string) error {

attestors = append(attestors, addtlAttestors...)
for _, attestor := range attestors {
setters, ok := ro.AttestorOptSetters[attestor.Type()]
setters, ok := ro.AttestorOptSetters[attestor.Name()]
if !ok {
continue
}

for _, setter := range setters {
attestor, err = setter(attestor)
if err != nil {
return fmt.Errorf("failed to set attestor option for %v: %w", attestor.Type(), err)
}
attestor, err = registry.SetOptions(attestor, setters...)
if err != nil {
return fmt.Errorf("failed to set attestor option for %v: %w", attestor.Type(), err)
}
}

Expand Down
Loading