Skip to content

Commit

Permalink
feat(policy, go-witness): implemented decisionLogUrl and also impleme…
Browse files Browse the repository at this point in the history
…nted the policydecision cloudevent posting to decision log provider
  • Loading branch information
kriscoleman committed Aug 4, 2023
1 parent 4bd19be commit 400ea6c
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 17 deletions.
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ require (
require (
filippo.io/edwards25519 v1.0.0 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/cloudevents/sdk-go/v2 v2.14.0 // indirect
github.com/cloudflare/circl v1.3.2 // indirect
github.com/coreos/go-oidc/v3 v3.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
Expand All @@ -32,8 +33,11 @@ require (
github.com/google/go-containerregistry v0.13.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/pjbgf/sha1cd v0.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
Expand All @@ -44,6 +48,9 @@ require (
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/zclconf/go-cty v1.12.1 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/oauth2 v0.5.0 // indirect
golang.org/x/tools v0.6.0 // indirect
Expand Down
17 changes: 17 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7N
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cloudevents/sdk-go/v2 v2.14.0 h1:Nrob4FwVgi5L4tV9lhjzZcjYqFVyJzsA56CwPaPfv6s=
github.com/cloudevents/sdk-go/v2 v2.14.0/go.mod h1:xDmKfzNjM8gBvjaF8ijFjM1VYOVUEeUfapHMUX1T5To=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.3.2 h1:VWp8dY3yH69fdM7lM6A1+NhhVoDu9vqK0jOgmkQHFWk=
github.com/cloudflare/circl v1.3.2/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw=
Expand Down Expand Up @@ -95,6 +97,7 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.13.0 h1:y1C7Z3e149OJbOPDBxLYR8ITPz8dTKqQwjErKVHJC8k=
github.com/google/go-containerregistry v0.13.0/go.mod h1:J9FQ+eSS4a1aC2GNZxvNpbWhgp0487v+cgiilB4FqDo=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.15.0 h1:1JYBfzqrWPcCclBwxFCPAou9n+q86mfnu7NAeHfte7A=
Expand All @@ -111,6 +114,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 h1:dYTbLf4m0a5u0KLmPfB6mgxbcV7588bOCx79hxa5Sr4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
Expand All @@ -130,6 +135,11 @@ github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPn
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/open-policy-agent/opa v0.49.1 h1:hXnKOmzVk8TyCTHEoHTZvYjAqwOBS9n+3mf4zmdDtP0=
github.com/open-policy-agent/opa v0.49.1/go.mod h1:Wvc+vpbXuYxhKk0uvhTxmDmZ3rjSXtqYJBtGAPNoJ6E=
Expand Down Expand Up @@ -175,6 +185,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand Down Expand Up @@ -215,6 +226,12 @@ github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtC
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.step.sm/crypto v0.25.0 h1:a+7sKyozZH9B30s0dHluygxreUxI1NtCBEmuNXx7a4k=
go.step.sm/crypto v0.25.0/go.mod h1:kr1rzO6SzeQnLm6Zu6lNtksHZLiFe9k8LolSJNhoc94=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
Expand Down
12 changes: 7 additions & 5 deletions policy/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ type PublicKey struct {
}

type PolicyDecision struct {
id uuid.UUID
SubjectName string
DigestID string
Timestamp time.Time
Decision Decision
id uuid.UUID
Digests []string
Timestamp time.Time
Decision Decision
PolicyGitoid string
EvidenceHashes []string
}

// Decision defines the type for the "decision" enum field.
Expand Down Expand Up @@ -214,6 +215,7 @@ func (p Policy) Verify(ctx context.Context, opts ...VerifyOption) (map[string][]
}
}

// TODO: this is where we need to return more data on when steps failed vs when they passed
passedByStep := make(map[string][]source.VerifiedCollection)
for depth := 0; depth < vo.searchDepth; depth++ {
for stepName, step := range p.Steps {
Expand Down
116 changes: 104 additions & 12 deletions verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@
package witness

import (
"bytes"
"context"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"time"

cloudevents "github.com/cloudevents/sdk-go/v2"
"github.com/edwarnicke/gitoid"
"github.com/testifysec/go-witness/cryptoutil"
"github.com/testifysec/go-witness/dsse"
"github.com/testifysec/go-witness/log"
"github.com/testifysec/go-witness/policy"
"github.com/testifysec/go-witness/source"
"github.com/testifysec/go-witness/timestamp"
Expand All @@ -44,6 +49,7 @@ type verifyOptions struct {
policyVerifiers []cryptoutil.Verifier
collectionSource source.Sourcer
subjectDigests []string
decisionLogURL string
}

type VerifyOption func(*verifyOptions)
Expand All @@ -64,6 +70,12 @@ func VerifyWithCollectionSource(source source.Sourcer) VerifyOption {
}
}

func VerifyWithDecisionLogProvider(decisionLogURL string) VerifyOption {
return func(vo *verifyOptions) {
vo.decisionLogURL = decisionLogURL
}
}

// Verify verifies a set of attestations against a provided policy. The set of attestations that satisfy the policy will be returned
// if verifiation is successful.
func Verify(ctx context.Context, policyEnvelope dsse.Envelope, policyVerifiers []cryptoutil.Verifier, opts ...VerifyOption) (map[string][]source.VerifiedCollection, error) {
Expand All @@ -85,19 +97,19 @@ func Verify(ctx context.Context, policyEnvelope dsse.Envelope, policyVerifiers [
return nil, fmt.Errorf("failed to unmarshal policy from envelope: %w", err)
}

pubKeysById, err := pol.PublicKeyVerifiers()
if err != nil {
return nil, fmt.Errorf("failed to get pulic keys from policy: %w", err)
pubKeysById, verifyErr := pol.PublicKeyVerifiers()
if verifyErr != nil {
return nil, fmt.Errorf("failed to get pulic keys from policy: %w", verifyErr)
}

pubkeys := make([]cryptoutil.Verifier, 0)
for _, pubkey := range pubKeysById {
pubkeys = append(pubkeys, pubkey)
}

trustBundlesById, err := pol.TrustBundles()
if err != nil {
return nil, fmt.Errorf("failed to load policy trust bundles: %w", err)
trustBundlesById, verifyErr := pol.TrustBundles()
if verifyErr != nil {
return nil, fmt.Errorf("failed to load policy trust bundles: %w", verifyErr)
}

roots := make([]*x509.Certificate, 0)
Expand All @@ -107,9 +119,9 @@ func Verify(ctx context.Context, policyEnvelope dsse.Envelope, policyVerifiers [
intermediates = append(intermediates, intermediates...)
}

timestampAuthoritiesById, err := pol.TimestampAuthorityTrustBundles()
if err != nil {
return nil, fmt.Errorf("failed to load policy timestamp authorities: %w", err)
timestampAuthoritiesById, verifyErr := pol.TimestampAuthorityTrustBundles()
if verifyErr != nil {
return nil, fmt.Errorf("failed to load policy timestamp authorities: %w", verifyErr)
}

timestampVerifiers := make([]dsse.TimestampVerifier, 0)
Expand All @@ -126,10 +138,90 @@ func Verify(ctx context.Context, policyEnvelope dsse.Envelope, policyVerifiers [
dsse.VerifyWithIntermediates(intermediates...),
dsse.VerifyWithTimestampVerifiers(timestampVerifiers...),
)
accepted, err := pol.Verify(ctx, policy.WithSubjectDigests(vo.subjectDigests), policy.WithVerifiedSource(verifiedSource))
if err != nil {
return nil, fmt.Errorf("failed to verify policy: %w", err)
accepted, verifyErr := pol.Verify(ctx, policy.WithSubjectDigests(vo.subjectDigests), policy.WithVerifiedSource(verifiedSource))

if verifyErr != nil {
decisionErr := createPolicyDecision(vo, policy.DecisionDenied, policyEnvelope, accepted)
if decisionErr != nil {
return nil, fmt.Errorf("failed to verify policy and post decision: /n %v /n %w", verifyErr, decisionErr)
}
return nil, fmt.Errorf("failed to verify policy: %w", verifyErr)
}

decisionErr := createPolicyDecision(vo, policy.DecisionAllowed, policyEnvelope, accepted)
if decisionErr != nil {
return nil, fmt.Errorf("failed to post decision: /n %v", decisionErr)
}

return accepted, nil
}

// this creates a policy decision and sends it as a cloud event to your decision log url, if provided
func createPolicyDecision(vo verifyOptions, decision policy.Decision, policyEnvelope dsse.Envelope, evidence map[string][]source.VerifiedCollection) error {
if vo.decisionLogURL == "" {
return nil
}

policyEnvelopeBytes, err := json.Marshal(policyEnvelope)
if err != nil {
log.Errorf("failed to marshal policyEnvelope: %v", err)
return err
}

gid, err := gitoid.New(bytes.NewReader(policyEnvelopeBytes), gitoid.WithContentLength(int64(len(policyEnvelopeBytes))), gitoid.WithSha256())
if err != nil {
log.Errorf("failed to generate gitoid: %v", err)
return err
}

pd := policy.PolicyDecision{
Digests: vo.subjectDigests,
Timestamp: time.Now(), // TODO: Time Stamp Authority?
Decision: decision,
PolicyGitoid: string(gid.Bytes()),
EvidenceHashes: make([]string, len(evidence)),
}

num := 0
for _, stepEvidence := range evidence {
for _, e := range stepEvidence {
pd.EvidenceHashes[num] = string([]byte(e.Reference))
}
}

event := cloudevents.NewEvent()
c, err := cloudevents.NewClientHTTP()
if err != nil {
return fmt.Errorf("failed to create client, %v", err)
}

event.SetSource("/policy-decision")
event.SetType("com.testifysec.policydecision")
event.SetData(cloudevents.ApplicationJSON, fmt.Sprintf(`%#v`, pd))

log.Infof("Policy decision created: %v", pd.Decision)

err = postPolicyDecision(vo, event, c)
if err != nil {
return err
}

return nil
}

// this attempts to post the policy decision provided to the DecisionLogURL provided
func postPolicyDecision(vo verifyOptions, event cloudevents.Event, c cloudevents.Client) error {
if vo.decisionLogURL == "" {
return nil
}
ctx := cloudevents.ContextWithTarget(context.Background(), vo.decisionLogURL)
log.Infof("Sending policy decision as payload of cloudevent to %v. Cloudevent payload: %s", vo.decisionLogURL, event)
if result := c.Send(ctx, event); cloudevents.IsUndelivered(result) || cloudevents.IsNACK(result) {
return fmt.Errorf("Failed to post policy decision to DecisionLogURL, %v", result)
} else {
log.Infof("sent: %v", event)
log.Infof("result: %v", result)
log.Info("Posted policy decision to DecisonLogURL successfully")
return nil
}
}

0 comments on commit 400ea6c

Please sign in to comment.