Skip to content

Commit

Permalink
add --ca-roots and --ca-intermediates flags to 'cosign verify' (#3464)
Browse files Browse the repository at this point in the history
* add --certificate-bundle flag to 'cosign verify'

Related to issue #3462.  Current commit adds the flag
to verify the CLI options.  The new flag doesn't have
any effect yet (will add in follow-up PRs).

Signed-off-by: Dmitry S <dsavints@gmail.com>

* Add --ca-roots flag for 'cosign verify'

Add --ca-roots command-line flag for 'cosign verify'
to enable verifying cosign signatures using PEM bundles
of CA roots. Whether to also add --ca-intermediates flag
is TBD.  Unit tests will be added in the next commit(s).

Fixes #3462.

Signed-off-by: Dmitry S <dsavints@gmail.com>

* add functional tests for --ca-roots flag

Signed-off-by: Dmitry S <dsavints@gmail.com>

* setup-crane action for e2e_test_pkcs11.sh

Signed-off-by: Dmitry S <dsavints@gmail.com>

* rebase on trunk

Signed-off-by: Dmitry Savintsev <dsavints@yahooinc.com>

* transform gencert subpackage to helper function

Signed-off-by: Dmitry S <dsavints@gmail.com>

* use the trunk version of workflows/e2e-tests.yml

Signed-off-by: Dmitry S <dsavints@gmail.com>

* correct certificate generation for e2e tests

Signed-off-by: Dmitry S <dsavints@gmail.com>

* refactor test cert/keys generation and corresponding test

Signed-off-by: Dmitry S <dsavints@gmail.com>

* add license header

Signed-off-by: Dmitry S <dsavints@gmail.com>

* remove test shell scripts

Signed-off-by: Dmitry S <dsavints@gmail.com>

* remove unused certFile param to verifyCertBundle

Signed-off-by: Dmitry S <dsavints@gmail.com>

* remove duplicate test functions

Signed-off-by: Dmitry S <dsavints@gmail.com>

---------

Signed-off-by: Dmitry S <dsavints@gmail.com>
Signed-off-by: Dmitry Savintsev <dsavints@yahooinc.com>
  • Loading branch information
dmitris authored Jul 1, 2024
1 parent 7c20052 commit 40fc15f
Show file tree
Hide file tree
Showing 12 changed files with 847 additions and 14 deletions.
17 changes: 16 additions & 1 deletion cmd/cosign/cli/options/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type CertVerifyOptions struct {
CertGithubWorkflowName string
CertGithubWorkflowRepository string
CertGithubWorkflowRef string
CAIntermediates string
CARoots string
CertChain string
SCT string
IgnoreSCT bool
Expand Down Expand Up @@ -75,12 +77,25 @@ func (o *CertVerifyOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.CertGithubWorkflowRef, "certificate-github-workflow-ref", "",
"contains the ref claim from the GitHub OIDC Identity token that contains the git ref that the workflow run was based upon.")
// -- Cert extensions end --
cmd.Flags().StringVar(&o.CAIntermediates, "ca-intermediates", "",
"path to a file of intermediate CA certificates in PEM format which will be needed "+
"when building the certificate chains for the signing certificate. "+
"The flag is optional and must be used together with --ca-roots, conflicts with "+
"--certificate-chain.")
_ = cmd.Flags().SetAnnotation("ca-intermediates", cobra.BashCompFilenameExt, []string{"cert"})
cmd.Flags().StringVar(&o.CARoots, "ca-roots", "",
"path to a bundle file of CA certificates in PEM format which will be needed "+
"when building the certificate chains for the signing certificate. Conflicts with --certificate-chain.")
_ = cmd.Flags().SetAnnotation("ca-roots", cobra.BashCompFilenameExt, []string{"cert"})

cmd.Flags().StringVar(&o.CertChain, "certificate-chain", "",
"path to a list of CA certificates in PEM format which will be needed "+
"when building the certificate chain for the signing certificate. "+
"Must start with the parent intermediate CA certificate of the "+
"signing certificate and end with the root certificate")
"signing certificate and end with the root certificate. Conflicts with --ca-roots and --ca-intermediates.")
_ = cmd.Flags().SetAnnotation("certificate-chain", cobra.BashCompFilenameExt, []string{"cert"})
cmd.MarkFlagsMutuallyExclusive("ca-roots", "certificate-chain")
cmd.MarkFlagsMutuallyExclusive("ca-intermediates", "certificate-chain")

cmd.Flags().StringVar(&o.SCT, "sct", "",
"path to a detached Signed Certificate Timestamp, formatted as a RFC6962 AddChainResponse struct. "+
Expand Down
6 changes: 6 additions & 0 deletions cmd/cosign/cli/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ against the transparency log.`,
# verify image with local certificate and certificate chain
cosign verify --cert cosign.crt --cert-chain chain.crt <IMAGE>
# verify image with local certificate and certificate bundles of CA roots
# and (optionally) CA intermediates
cosign verify --cert cosign.crt --ca-roots ca-roots.pem --ca-intermediates ca-intermediates.pem <IMAGE>
# verify image using keyless verification with the given certificate
# chain and identity parameters, without Fulcio roots (for BYO PKI):
cosign verify --cert-chain chain.crt --certificate-oidc-issuer https://issuer.example.com --certificate-identity foo@example.com <IMAGE>
Expand Down Expand Up @@ -115,6 +119,8 @@ against the transparency log.`,
CertGithubWorkflowName: o.CertVerify.CertGithubWorkflowName,
CertGithubWorkflowRepository: o.CertVerify.CertGithubWorkflowRepository,
CertGithubWorkflowRef: o.CertVerify.CertGithubWorkflowRef,
CAIntermediates: o.CertVerify.CAIntermediates,
CARoots: o.CertVerify.CARoots,
CertChain: o.CertVerify.CertChain,
IgnoreSCT: o.CertVerify.IgnoreSCT,
SCTRef: o.CertVerify.SCT,
Expand Down
53 changes: 46 additions & 7 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ type VerifyCommand struct {
CertGithubWorkflowName string
CertGithubWorkflowRepository string
CertGithubWorkflowRef string
CAIntermediates string
CARoots string
CertChain string
CertOidcProvider string
IgnoreSCT bool
Expand Down Expand Up @@ -173,7 +175,8 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
}
}
if keylessVerification(c.KeyRef, c.Sk) {
if c.CertChain != "" {
switch {
case c.CertChain != "":
chain, err := loadCertChainFromFileOrURL(c.CertChain)
if err != nil {
return err
Expand All @@ -186,9 +189,32 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
co.IntermediateCerts.AddCert(cert)
}
}
} else {
// This performs an online fetch of the Fulcio roots. This is needed
// for verifying keyless certificates (both online and offline).
case c.CARoots != "":
caRoots, err := loadCertChainFromFileOrURL(c.CARoots)
if err != nil {
return err
}
co.RootCerts = x509.NewCertPool()
if len(caRoots) > 0 {
for _, cert := range caRoots {
co.RootCerts.AddCert(cert)
}
}
if c.CAIntermediates != "" {
caIntermediates, err := loadCertChainFromFileOrURL(c.CAIntermediates)
if err != nil {
return err
}
if len(caIntermediates) > 0 {
co.IntermediateCerts = x509.NewCertPool()
for _, cert := range caIntermediates {
co.IntermediateCerts.AddCert(cert)
}
}
}
default:
// This performs an online fetch of the Fulcio roots from a TUF repository.
// This is needed for verifying keyless certificates (both online and offline).
co.RootCerts, err = fulcio.GetRoots()
if err != nil {
return fmt.Errorf("getting Fulcio roots: %w", err)
Expand Down Expand Up @@ -237,8 +263,9 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
if err != nil {
return err
}
if c.CertChain == "" {
// If no certChain is passed, the Fulcio root certificate will be used
switch {
case c.CertChain == "" && co.RootCerts == nil:
// If no certChain and no CARoots are passed, the Fulcio root certificate will be used
co.RootCerts, err = fulcio.GetRoots()
if err != nil {
return fmt.Errorf("getting Fulcio roots: %w", err)
Expand All @@ -251,7 +278,7 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
if err != nil {
return err
}
} else {
case c.CertChain != "":
// Verify certificate with chain
chain, err := loadCertChainFromFileOrURL(c.CertChain)
if err != nil {
Expand All @@ -261,14 +288,26 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
if err != nil {
return err
}
case co.RootCerts != nil:
// Verify certificate with root (and if given, intermediate) certificate
pubKey, err = cosign.ValidateAndUnpackCert(cert, co)
if err != nil {
return err
}
default:
return errors.New("no certificate chain provided to verify certificate")
}

if c.SCTRef != "" {
sct, err := os.ReadFile(filepath.Clean(c.SCTRef))
if err != nil {
return fmt.Errorf("reading sct from file: %w", err)
}
co.SCT = sct
}
default:
// Do nothing. Neither keyRef, c.Sk, nor certRef were set - can happen for example when using Fulcio and TSA.
// For an example see the TestAttachWithRFC3161Timestamp test in test/e2e_test.go.
}
co.SigVerifier = pubKey

Expand Down
4 changes: 3 additions & 1 deletion doc/cosign_dockerfile_verify.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion doc/cosign_manifest_verify.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion doc/cosign_verify-attestation.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion doc/cosign_verify-blob-attestation.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion doc/cosign_verify-blob.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 40fc15f

Please sign in to comment.