cert extension funcs are now policy methods
Signed-off-by: Meredith Lancaster <malancas@github.com>
This commit is contained in:
parent
e5b2b09a6e
commit
e16b69bd08
3 changed files with 101 additions and 41 deletions
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/sigstore/sigstore-go/pkg/fulcio/certificate"
|
||||
"github.com/sigstore/sigstore-go/pkg/verify"
|
||||
|
|
@ -20,11 +21,13 @@ const (
|
|||
)
|
||||
|
||||
type ExpectedExtensions struct {
|
||||
RunnerEnvironment string
|
||||
SANRegex string
|
||||
SAN string
|
||||
BuildSourceRepo string
|
||||
SignerWorkflow string
|
||||
RunnerEnvironment string
|
||||
SANRegex string
|
||||
SAN string
|
||||
BuildSourceRepoURI string
|
||||
SignerWorkflow string
|
||||
SourceRepositoryOwnerURI string
|
||||
SourceRepositoryURI string
|
||||
}
|
||||
|
||||
type SigstoreInstance string
|
||||
|
|
@ -40,10 +43,13 @@ type Policy struct {
|
|||
ExpectedPredicateType string
|
||||
ExpectedSigstoreInstance string
|
||||
Artifact artifact.DigestedArtifact
|
||||
OIDCIssuer string
|
||||
}
|
||||
|
||||
func newPolicy(opts *Options, a artifact.DigestedArtifact) (Policy, error) {
|
||||
p := Policy{}
|
||||
p := Policy{
|
||||
Artifact: a,
|
||||
}
|
||||
|
||||
if opts.SignerRepo != "" {
|
||||
signedRepoRegex := expandToGitHubURL(opts.Tenant, opts.SignerRepo)
|
||||
|
|
@ -68,10 +74,25 @@ func newPolicy(opts *Options, a artifact.DigestedArtifact) (Policy, error) {
|
|||
|
||||
if opts.Repo != "" {
|
||||
if opts.Tenant != "" {
|
||||
p.ExpectedExtensions.BuildSourceRepo = fmt.Sprintf("https://%s.ghe.com/%s", opts.Tenant, opts.Repo)
|
||||
p.ExpectedExtensions.BuildSourceRepoURI = fmt.Sprintf("https://%s.ghe.com/%s", opts.Tenant, opts.Repo)
|
||||
}
|
||||
p.ExpectedExtensions.BuildSourceRepo = fmt.Sprintf("https://github.com/%s", opts.Repo)
|
||||
p.ExpectedExtensions.BuildSourceRepoURI = fmt.Sprintf("https://github.com/%s", opts.Repo)
|
||||
}
|
||||
|
||||
if opts.Tenant != "" {
|
||||
p.ExpectedExtensions.SourceRepositoryOwnerURI = fmt.Sprintf("https://%s.ghe.com/%s", opts.Tenant, opts.Owner)
|
||||
} else {
|
||||
p.ExpectedExtensions.SourceRepositoryOwnerURI = fmt.Sprintf("https://github.com/%s", opts.Owner)
|
||||
}
|
||||
|
||||
// if issuer is anything other than the default, use the user-provided value;
|
||||
// otherwise, select the appropriate default based on the tenant
|
||||
if opts.Tenant != "" {
|
||||
p.OIDCIssuer = fmt.Sprintf(verification.GitHubTenantOIDCIssuer, opts.Tenant)
|
||||
} else {
|
||||
p.OIDCIssuer = opts.OIDCIssuer
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
|
|
@ -115,6 +136,16 @@ func (p *Policy) buildCertificateIdentityOption() (verify.PolicyOption, error) {
|
|||
return verify.WithCertificateIdentity(certId), nil
|
||||
}
|
||||
|
||||
func (p *Policy) VerifyPredicateType(a []*api.Attestation) ([]*api.Attestation, error) {
|
||||
filteredAttestations := verification.FilterAttestations(p.ExpectedPredicateType, a)
|
||||
|
||||
if len(filteredAttestations) == 0 {
|
||||
return nil, fmt.Errorf("✗ No attestations found with predicate type: %s\n", p.ExpectedPredicateType)
|
||||
}
|
||||
|
||||
return filteredAttestations, nil
|
||||
}
|
||||
|
||||
func (p *Policy) SigstorePolicy() (verify.PolicyBuilder, error) {
|
||||
artifactDigestPolicyOption, err := verification.BuildDigestPolicyOption(p.Artifact)
|
||||
if err != nil {
|
||||
|
|
@ -130,10 +161,6 @@ func (p *Policy) SigstorePolicy() (verify.PolicyBuilder, error) {
|
|||
return policy, nil
|
||||
}
|
||||
|
||||
func addSchemeToRegex(s string) string {
|
||||
return fmt.Sprintf("^https://%s", s)
|
||||
}
|
||||
|
||||
func validateSignerWorkflow(opts *Options) (string, error) {
|
||||
// we expect a provided workflow argument be in the format [HOST/]/<OWNER>/<REPO>/path/to/workflow.yml
|
||||
// if the provided workflow does not contain a host, set the host
|
||||
|
|
@ -150,5 +177,55 @@ func validateSignerWorkflow(opts *Options) (string, error) {
|
|||
return "", errors.New("unknown host")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("^https://%s/%s/%s", opts.Hostname, opts.SignerWorkflow), nil
|
||||
return fmt.Sprintf("^https://%s/%s", opts.Hostname, opts.SignerWorkflow), nil
|
||||
}
|
||||
|
||||
func (p *Policy) VerifyCertExtensions(results []*verification.AttestationProcessingResult) error {
|
||||
if len(results) == 0 {
|
||||
return errors.New("no attestations proccessing results")
|
||||
}
|
||||
|
||||
var atLeastOneVerified bool
|
||||
for _, attestation := range results {
|
||||
if err := p.verifyCertExtensions(attestation); err != nil {
|
||||
return err
|
||||
}
|
||||
atLeastOneVerified = true
|
||||
}
|
||||
|
||||
if atLeastOneVerified {
|
||||
return nil
|
||||
} else {
|
||||
return verification.ErrNoAttestationsVerified
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Policy) verifyCertExtensions(attestation *verification.AttestationProcessingResult) error {
|
||||
if p.ExpectedExtensions.SourceRepositoryOwnerURI != "" {
|
||||
sourceRepositoryOwnerURI := attestation.VerificationResult.Signature.Certificate.Extensions.SourceRepositoryOwnerURI
|
||||
if !strings.EqualFold(p.ExpectedExtensions.SourceRepositoryOwnerURI, sourceRepositoryOwnerURI) {
|
||||
return fmt.Errorf("expected SourceRepositoryOwnerURI to be %s, got %s", p.ExpectedExtensions.SourceRepositoryOwnerURI, sourceRepositoryOwnerURI)
|
||||
}
|
||||
}
|
||||
|
||||
// if repo is set, check the SourceRepositoryURI field
|
||||
if p.ExpectedExtensions.SourceRepositoryURI != "" {
|
||||
sourceRepositoryURI := attestation.VerificationResult.Signature.Certificate.Extensions.SourceRepositoryURI
|
||||
if !strings.EqualFold(p.ExpectedExtensions.SourceRepositoryURI, sourceRepositoryURI) {
|
||||
return fmt.Errorf("expected SourceRepositoryURI to be %s, got %s", p.ExpectedExtensions.SourceRepositoryURI, sourceRepositoryURI)
|
||||
}
|
||||
}
|
||||
|
||||
if p.OIDCIssuer != "" {
|
||||
certIssuer := attestation.VerificationResult.Signature.Certificate.Extensions.Issuer
|
||||
if !strings.EqualFold(p.OIDCIssuer, certIssuer) {
|
||||
if strings.Index(certIssuer, p.OIDCIssuer+"/") == 0 {
|
||||
return fmt.Errorf("expected Issuer to be %s, got %s -- if you have a custom OIDC issuer policy for your enterprise, use the --cert-oidc-issuer flag with your expected issuer", p.OIDCIssuer, certIssuer)
|
||||
} else {
|
||||
return fmt.Errorf("expected Issuer to be %s, got %s", p.OIDCIssuer, certIssuer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ func TestBuildPolicy(t *testing.T) {
|
|||
SANRegex: "^https://github.com/sigstore/",
|
||||
}
|
||||
|
||||
_, err = buildVerifyPolicy(opts, *artifact)
|
||||
_, err = newPolicy(opts, *artifact)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +87,6 @@ func TestValidateSignerWorkflow(t *testing.T) {
|
|||
workflowRegex, err := validateSignerWorkflow(opts)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expectedWorkflowRegex, workflowRegex)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -255,21 +255,9 @@ func runVerify(opts *Options) error {
|
|||
attestations = filteredAttestations
|
||||
}
|
||||
|
||||
policy, err := buildVerifyPolicy(opts, *artifact)
|
||||
sigstoreResults, err := verifyAll(opts, *artifact, attestations)
|
||||
if err != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to build verification policy"))
|
||||
return err
|
||||
}
|
||||
|
||||
sigstoreRes := opts.SigstoreVerifier.Verify(attestations, policy)
|
||||
if sigstoreRes.Error != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed"))
|
||||
return sigstoreRes.Error
|
||||
}
|
||||
|
||||
// Verify extensions
|
||||
if err := verification.VerifyCertExtensions(sigstoreRes.VerifyResults, opts.Tenant, opts.Owner, opts.Repo, opts.OIDCIssuer); err != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed"))
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red(err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +266,7 @@ func runVerify(opts *Options) error {
|
|||
// If an exporter is provided with the --json flag, write the results to the terminal in JSON format
|
||||
if opts.exporter != nil {
|
||||
// print the results to the terminal as an array of JSON objects
|
||||
if err = opts.exporter.Write(opts.Logger.IO, sigstoreRes.VerifyResults); err != nil {
|
||||
if err = opts.exporter.Write(opts.Logger.IO, sigstoreResults.VerifyResults); err != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to write JSON output"))
|
||||
return err
|
||||
}
|
||||
|
|
@ -288,7 +276,7 @@ func runVerify(opts *Options) error {
|
|||
opts.Logger.Printf("%s was attested by:\n", artifact.DigestWithAlg())
|
||||
|
||||
// Otherwise print the results to the terminal in a table
|
||||
tableContent, err := buildTableVerifyContent(opts.Tenant, sigstoreRes.VerifyResults)
|
||||
tableContent, err := buildTableVerifyContent(opts.Tenant, sigstoreResults.VerifyResults)
|
||||
if err != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("failed to parse results"))
|
||||
return err
|
||||
|
|
@ -365,30 +353,26 @@ func buildTableVerifyContent(tenant string, results []*verification.AttestationP
|
|||
return content, nil
|
||||
}
|
||||
|
||||
func verifyAll(opts *Options, artifact artifact.DigestedArtifact, attestations []*api.Attestation) error {
|
||||
func verifyAll(opts *Options, artifact artifact.DigestedArtifact, attestations []*api.Attestation) (*verification.SigstoreResults, error) {
|
||||
policy, err := newPolicy(opts, artifact)
|
||||
if err != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to build verification policy"))
|
||||
return err
|
||||
return nil, fmt.Errorf("✗ Failed to build verification policy")
|
||||
}
|
||||
|
||||
sp, err := policy.SigstorePolicy()
|
||||
if err != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Failed to build verification policy"))
|
||||
return err
|
||||
return nil, fmt.Errorf("✗ Failed to build Sigstore verification policy")
|
||||
}
|
||||
|
||||
sigstoreRes := opts.SigstoreVerifier.Verify(attestations, sp)
|
||||
if sigstoreRes.Error != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed"))
|
||||
return sigstoreRes.Error
|
||||
return nil, fmt.Errorf("✗ Sigstore verification failed")
|
||||
}
|
||||
|
||||
// Verify extensions
|
||||
if err := verification.VerifyCertExtensions(sigstoreRes.VerifyResults, opts.Tenant, opts.Owner, opts.Repo, opts.OIDCIssuer); err != nil {
|
||||
opts.Logger.Println(opts.Logger.ColorScheme.Red("✗ Verification failed"))
|
||||
return err
|
||||
return nil, fmt.Errorf("✗ Policy verification failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
return sigstoreRes, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue