Skip to content

feat: add inline cert provider#601

Merged
susanshi merged 9 commits intonotaryproject:mainfrom
cse-labs:inline-cert-provider
Feb 9, 2023
Merged

feat: add inline cert provider#601
susanshi merged 9 commits intonotaryproject:mainfrom
cse-labs:inline-cert-provider

Conversation

@noelbundick-msft
Copy link
Copy Markdown
Contributor

Description

What this PR does / why we need it:

Users need a way to refer to public certs (where they don't have the private key) during signature verification

Ratify currently has an "azurekeyvault" certificate provider. This PR adds an "inline" cert provider where a PEM-format certificate (chain) can be directly specified. This is a big improvement over the current experience, where users have to (ab)use the ratifyTestCert Helm param and/or reverse engineer the chart to volume mount a ConfigMap/Secret.

Which issue(s) this PR fixes (optional, using fixes #<issue number>(, fixes #<issue_number>, ...) format, will close the issue(s) when the PR gets merged):

Fixes #576

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Please also list any relevant details for your test configuration

  • added unit tests
  • manually verified by adding a CertificateStore containing my CA public certificate, configuring verifier-notary to , which was able to verify a notary v2 signature that was created by a leaf certificate. ex:
apiVersion: config.ratify.deislabs.io/v1alpha1
kind: CertificateStore
metadata:
  annotations:
  name: certstore-sssc
  namespace: default
spec:
  parameters:
    value: |
      -----BEGIN CERTIFICATE-----
      MIIFxzCCA6+gAwIBAgIUHzdB1Cgt3LJZWoBtXW+pZX4ZhSAwDQYJKoZIhvcNAQELBQAwazELMAkG
      A1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdSZWRtb25kMRMwEQYDVQQKDApNeSBDb21w
      YW55MQ8wDQYDVQQLDAZNeSBPcmcxFzAVBgNVBAMMDmNhLmV4YW1wbGUuY29tMB4XDTIzMDEyNjE4
      NDkwOFoXDTMzMDEyMzE4NDkwOFowazELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQH
      DAdSZWRtb25kMRMwEQYDVQQKDApNeSBDb21wYW55MQ8wDQYDVQQLDAZNeSBPcmcxFzAVBgNVBAMM
      DmNhLmV4YW1wbGUuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuXCxpdemBfCm
      bVcQ86aQfR3LtNUGlQXbhg/6b7oYSJOTjrH/dxHCZB+7kD2wQt2Bqobd1bDOwRs+6EydggUIlmln
      p5KsYMrE+vMHvaTZckThchckdln6pyfPTshicFaPpLgs2whAqiScpzBnHZBqr23c/xdYyBy33tfi
      MRkR2/BFSuzblGvL8laP5oaXC2/h79ZV3MFPWfbTwY7FqeU9uyoSlcuFTG0B8QJGSJ2SVhs4nLQl
      tflQH7br30Tj06RItff5sX4qhgZHGdnmfgZPpLEoQ0wg5sut/9cD1TaNf5z4hfakrv69HysrrK8w
      vKVUx2xIO0RIAghtFTQz7x9RR+C8oSCtfMvEyGvm/JMhBre49swCccmBMgNTQRV8zDNalUQfwMk3
      HfghtTc3rlIehnOUujGjXkkgvIakOf9OLTkEodPftvhqF1JZIlnT3iqQX2fJAAbC8tzvgp+H4nmU
      yE3pygQQk9seek7GaZOyVqmQil6PVg7wxhF/cbSZ/cvvHXUPBieLrzKLxvTlQ6y7751eU6Ski0N/
      tey+b+JLme+0FpAffgOf+fpyTsWlktV+sOCO5VEynFrq67e01YYvfFl8RhiX2xnXy17eTSIP0M51
      d3/lubqfH7lt+YHE+U312BFV76wg0ka9MsK2M67u32h5E32r1Hf8xt3I4nCMBe8CAwEAAaNjMGEw
      HQYDVR0OBBYEFIbirF2xUcUsfkzsnFAWEJPVe+VMMB8GA1UdIwQYMBaAFIbirF2xUcUsfkzsnFAW
      EJPVe+VMMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IC
      AQCFVvbt77g1z94RtFeAREYQGAJGjO+d5kuCMcDt+69v9p/IrHZ0B8etGJmb4PFRWsfv2kla9sje
      k+vs1RSuW3N0n35S00qG5QVxp1bTh0/l8gp/bx5p0+3yQcM14/fB6iMx0ZciKiIDnkOhNzkrHKCt
      Wfpo2d3vpZW+FF/qmOhIa9+8WmG/+b64hoXissNQrOoFV8AlQZFxObToNC1QK7ncJ+XPCp7Ay9Fj
      gxJkLPTg/rqrQeVbekDO4oIklMogL0U/Ms0Y92Jzjku58oCysM4za43FcTuRTb20RdP7lq7Pb6n3
      6o31mySN+1KitjwCSBITe+4wqtD5//HVowF3GuoR1+Wf5VzKtP+/KurjBOxFdxFvukw36FcLNyD8
      RfvcPx2Qo5QuoY59qxUujeSBB51pb015bzDcsRFHw8dNQjAXUzDepkP8O42y/Iw2HfrV5PDmflgT
      MIl8kL6DuizWQf74JW9UnV3jwYUsAAiADVNAFrE+L5CDFKbHaPgfCQ9/C8YKDgvkZzzSK6fJixmt
      d5J0iKR36VQvSkq16+d/VG5P+diMQ9sXMMPJY3fU65PjYf3NO9w0x316M6dKXJnuERVP9HDkycps
      /0ExavCbSu+pSX+NKFdYWrfm4JxVjuEvrQ1x5VDLM2BdaX6UjJ4vMbGWGnTTrD72m1HRDHT5Q3iU
      4w==
      -----END CERTIFICATE-----
  provider: inline
apiVersion: config.ratify.deislabs.io/v1alpha1
kind: Verifier
metadata:
  name: verifier-notary
spec:
  artifactTypes: application/vnd.cncf.notary.signature
  name: notaryv2
  parameters:
    trustPolicyDoc:
      trustPolicies:
      - name: default
        registryScopes:
        - '*'
        signatureVerification:
          level: strict
        trustStores:
        - ca:certs
        trustedIdentities:
        - '*'
      version: "1.0"
    verificationCertStores:
      certs:
      - certstore-sssc

Checklist:

  • Does the affected code have corresponding tests?
  • Are the changes documented, not just with inline documentation, but also with conceptual documentation such as an overview of a new feature, or task-based documentation like a tutorial? Consider if this change should be announced on your project blog.
    • No docs yet. Should be documented alongside CertificateStore CRD feature
  • Does this introduce breaking changes that would require an announcement or bumping the major version?
  • Do all new files have appropriate license header?

@noelbundick-msft
Copy link
Copy Markdown
Contributor Author

@susanshi Went ahead and took a stab at implementing this. It's functional and tested as-is. It seems relatively straightforward - I've not used this outside of my own dev box yet

Wanted to get some design feedback/thoughts before moving it out of draft

This currently specifies "one PEM per CertificateStore" - which could be a cert chain if you are using root/intermediate CA's. I wasn't sure whether to keep it that way or make it multivalued since you can add multiple entries under verificationCertStores.

Ex: certs might have certstore-akv, certstore-inline1 and certstore-inline2. There's not the same performance / API rate limit overhead of reaching out to Key Vault N times. Store multiple certs in one store felt more complicated and may require a refactor of the Key Vault provider, so this initial version is simple to get some more thoughts.

Copy link
Copy Markdown
Collaborator

@susanshi susanshi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Noel, thanks so much for the turnaround. A new inline provider is exactly what we ve discussed, it looks great! i have left some refactoring comments, let me know what you think! thanks!

@akashsinghal akashsinghal changed the title Add inline cert provider feat: add inline cert provider Feb 2, 2023
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 6, 2023

Codecov Report

Base: 42.07% // Head: 42.03% // Decreases project coverage by -0.05% ⚠️

Coverage data is based on head (e01cbd6) compared to base (5c36bf8).
Patch coverage: 53.65% of modified lines in pull request are covered.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #601      +/-   ##
==========================================
- Coverage   42.07%   42.03%   -0.05%     
==========================================
  Files          53       55       +2     
  Lines        3194     3214      +20     
==========================================
+ Hits         1344     1351       +7     
- Misses       1703     1715      +12     
- Partials      147      148       +1     
Impacted Files Coverage Δ
pkg/controllers/certificatestore_controller.go 12.50% <0.00%> (-18.85%) ⬇️
pkg/certificateprovider/azurekeyvault/provider.go 21.89% <9.09%> (-1.18%) ⬇️
pkg/certificateprovider/certificate_provider.go 83.33% <83.33%> (ø)
pkg/certificateprovider/inline/provider.go 100.00% <100.00%> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@noelbundick-msft noelbundick-msft marked this pull request as ready for review February 6, 2023 19:46
type CertificateProvider interface {
// Returns an array of certificates based on certificate properties defined in attrib map
GetCertificatesContent(ctx context.Context, attrib map[string]string) ([]types.Certificate, error)
GetCertificates(ctx context.Context, attrib map[string]string) ([]*x509.Certificate, error)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the future items is to enable Certificate providers in the cosign verifier path. It looks like cosign load a ECDSA key, will this interface work with cosign?
We want to keep this interface generic so it works for all verifiers that requires a validation cert. We haven't started exploring the integration with Cosign yet, appreciate your insight here :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will look into this and get back w/ some info :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I've found as a complete cosign newbie:

  • We're not actually using this interface right now- was just trying to get things lined up consistently to prepare for implementing the factory in the future - and to try to use common interfaces vs the Key Vault-specific types.Certificate one here
  • This won't work as-is with our cosign code that exists today - it expects a plain public key rather than a certificate. I'd be wary of doing "not certificate things" in the "certificate provider". Might need a new "public key provider", or change the name to something else in the future to cover both use cases?
  • Our linked usage of cosign functions points to functions labeled "TODO: move this to an internal package" and only seem to be used to parse a key. I wonder if there are more suitable functions in the sigstore/signature package directly?

If cosign users have and typically uses ECDSA certificates, it seems easy enough to use. This test case loads in a cert w/ an ECDSA key, and then uses it to load up a sigstore verifier:

func TestUseX509WithSigstore(t *testing.T) {
	certString := "-----BEGIN CERTIFICATE-----\nMIIB+TCCAZ+gAwIBAgIUNK/+de2dV3kCMVOGj+X6mdyO6A8wCgYIKoZIzj0EAwIw\nUjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAldBMRAwDgYDVQQHDAdSZWRtb25kMREw\nDwYDVQQKDAhUZXN0Q2VydDERMA8GA1UEAwwIdGVzdGNlcnQwHhcNMjMwMjA4MTkz\nNjAwWhcNMjQwMjA4MTkzNjAwWjBSMQswCQYDVQQGEwJVUzELMAkGA1UECAwCV0Ex\nEDAOBgNVBAcMB1JlZG1vbmQxETAPBgNVBAoMCFRlc3RDZXJ0MREwDwYDVQQDDAh0\nZXN0Y2VydDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDLn+GbbWXyXcVuQ/PFP\nmC1f8FiXK7IBCATvOZXxQLJnU8B55mo1BTZn8yBzT1TJA4XfQktNrZYHIWwJkeFh\njgSjUzBRMB0GA1UdDgQWBBQ4wBIh6ub86EFcRvPss6dEPPd7IDAfBgNVHSMEGDAW\ngBQ4wBIh6ub86EFcRvPss6dEPPd7IDAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49\nBAMCA0gAMEUCIQCaEZDL399k2fnwGQCluc32Xk8Dugsucv+niWZCIBtA4AIgDYBZ\n2HVNJ/29vqxAHG4mzNXH4uBZj9DvJpmYbzUvlOk=\n-----END CERTIFICATE-----\n"
	c1 := []byte(certString)

	// this works for certificates, but wouldn't work for plain public keys
	certs, err := DecodeCertificates(c1)
	if err != nil {
		t.Fatalf(err.Error())
	}

	// I havent verified anything with this, but this creates an ECDSAVerifier w/o errors
	verifier, err := signature.LoadVerifier(certs[0].PublicKey, crypto.SHA256)
	if err != nil {
		t.Fatalf(err.Error())
	}

	fmt.Printf("verifier: %+v", verifier)
}

That said, if cosign typically uses public keys directly - this approach (and therefore involving x509) probably wouldn't make much sense

I've seen some cosign refactoring issues/PR's, particularly around making it work with authenticated registries. Would it make sense to land this feature and then include "how do we handle certs/keys in a consistent way?" in the scope of the design for that improvement?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fair to say that our current cosign implementation with respect to handling public keys and interacting with registries is not optimal. I'd be in favor of a general overhaul of how cosign plugin and integration with ORAS store is done. This one is going to require a fair bit of research and work as far as I can tell...

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for looking into this. I think you make a good point, certificate providers should return certificates. I think we can go ahead with the interface update.
"TODO: move this to an internal package" this message is worrying, we probably will need to revisit the cosign verifier soon.

Copy link
Copy Markdown
Collaborator

@susanshi susanshi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for adding the e2e test! LGTM

@susanshi
Copy link
Copy Markdown
Collaborator

susanshi commented Feb 9, 2023

Hi @noelbundick-msft , once this is rebased, this PR is good to merge.

@susanshi susanshi merged commit 789b86e into notaryproject:main Feb 9, 2023
@noelbundick-msft noelbundick-msft deleted the inline-cert-provider branch February 9, 2023 19:05
bspaans pushed a commit to bspaans/ratify that referenced this pull request Oct 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Configuring Ratify to trust a Certificate Authority + problems with Azure Key Vault

4 participants