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

Add upstream TLS trust from CM bundles #1171

Merged
merged 8 commits into from
Dec 19, 2023

Conversation

ReToCode
Copy link
Member

@ReToCode ReToCode commented Dec 6, 2023

Changes

  • Adding support to trust multiple CAs (when a label is present) for upstream TLS connections
  • Checks if CAs are parsable before adding them to envoy
  • Additional to reading the ca.crt in activators cert secret, net-kourier now also adds additional trust bundles to the trust pool
  • Dropped the fallback implementation of reading service port names. With the above config read fix, this is no longer necessary
  • Tested the full set with CA rotation here

Partially knative/serving#14609

Release Note

net-kourier now allows to add additional CA trust bundles in ConfigMaps to verify upstream TLS connections

Docs

Will be added once the full encryption feature is completed.

@ReToCode ReToCode requested review from dprotaso and skonto December 6, 2023 09:03
@knative-prow knative-prow bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 6, 2023
Copy link

knative-prow bot commented Dec 6, 2023

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ReToCode

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@knative-prow knative-prow bot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Dec 6, 2023
@ReToCode
Copy link
Member Author

ReToCode commented Dec 6, 2023

@skonto @dprotaso what do you think of that approach for knative/serving#14609.

With this approach, this is not tied to trust-manager. ConfigMaps seem to be a very common interface for CA bundles, see https://github.com/ReToCode/knative-encryption/tree/main/8-trust-sources.

We can also extend this to knative/serving#14609 (comment) once this has landed. But I'd vote for reading it using the K8s clients instead of mounting it to the filesystem, as this solves the reloading concern.

Copy link

codecov bot commented Dec 6, 2023

Codecov Report

Attention: 18 lines in your changes are missing coverage. Please review.

Comparison is base (23848ce) 81.04% compared to head (2a72a55) 80.94%.
Report is 1 commits behind head on main.

❗ Current head 2a72a55 differs from pull request most recent head 9529993. Consider uploading reports for the commit 9529993 to get more accurate results

Files Patch % Lines
pkg/generator/ingress_translator.go 75.00% 12 Missing and 6 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1171      +/-   ##
==========================================
- Coverage   81.04%   80.94%   -0.10%     
==========================================
  Files          18       18              
  Lines        1456     1496      +40     
==========================================
+ Hits         1180     1211      +31     
- Misses        219      223       +4     
- Partials       57       62       +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@skonto
Copy link
Contributor

skonto commented Dec 7, 2023

ConfigMaps seem to be a very common interface for CA bundles,

In general I see secrets being used as well. For trust manager it seems cm are first option. I don't have strong preference.

@ReToCode
Copy link
Member Author

ReToCode commented Dec 8, 2023

In general I see secrets being used as well. For trust manager it seems cm are first option. I don't have strong preference.

Hm where do you mean? I don't think we have a current solution (other than ca.crt) that would put the bundles in a Secret. But we can always extend if necessary.

@skonto
Copy link
Contributor

skonto commented Dec 8, 2023

Hm where do you mean? I

I was referring to the docs: https://cert-manager.io/docs/trust/trust-manager

Bundle resources currently support several source types:

configMap - a ConfigMap resource in the trust-manager namespace
secret - a Secret resource in the trust-manager namespace
inLine - a manually specified string containing at least one certificate
useDefaultCAs - usually, a bundle of publicly trusted certificates

@ReToCode
Copy link
Member Author

ReToCode commented Dec 8, 2023

I was referring to the docs: https://cert-manager.io/docs/trust/trust-manager

That is just for sources when I'm reading it right. The result to mount in workloads is always a Configmap?

@skonto
Copy link
Contributor

skonto commented Dec 8, 2023

It seems it supports: sources/targets.

ConfigMap is the default target type, but as of v0.7.0 trust-manager also supports Secret resources as targets.

@ReToCode
Copy link
Member Author

@skonto this is ready now, please take another look.

/unhold

@knative-prow knative-prow bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 14, 2023
@@ -466,3 +517,24 @@ func domainsForRule(rule v1alpha1.IngressRule) []string {
}
return domains
}

func checkCertBundle(certs []byte) error {
Copy link
Contributor

@skonto skonto Dec 14, 2023

Choose a reason for hiding this comment

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

As a side note it seems that this error is not being caught by decode (probably not considered an error):

var rsaSecretCertPEM = `-----BEGIN CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ
hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa
rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv
zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW
r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
-----END CERTIFICATE-----
`

Copy link
Contributor

Choose a reason for hiding this comment

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

I played around with decode a bit.
The way it is written:

func Decode(data []byte) (p *Block, rest []byte) {
	// pemStart begins with a newline. However, at the very beginning of
	// the byte array, we'll accept the start string without it.
	rest = data
	for {
		if bytes.HasPrefix(rest, pemStart[1:]) {
			rest = rest[len(pemStart)-1:]
		} else if _, after, ok := bytes.Cut(rest, pemStart); ok {
			rest = after
		} else {
			return nil, data
		}

		var typeLine []byte
		typeLine, rest = getLine(rest)
		if !bytes.HasSuffix(typeLine, pemEndOfLine) {
			continue
		}
		typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]

		p = &Block{
			Headers: make(map[string]string),
			Type:    string(typeLine),
		}
...

It seems it is always going to consume the input.
I am wondering if there is a scenario where block== nil but we have len(rest) >0.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes I had one case with that, that's why I added

|| len(rest) > 0, this way we error out in checkCertBundle when there is still a rest that is not consumed.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I think it parses from comment to comment, so having multiple -----BEGIN CERTIFICATE----- is just ignored, as long as the cert within is valid.

if block != nil {
switch block.Type {
case "CERTIFICATE":
_, err := x509.ParseCertificate(block.Bytes)
Copy link
Contributor

@skonto skonto Dec 14, 2023

Choose a reason for hiding this comment

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

Are we planning to do any verification at some point like with this function: https://github.com/istio/istio/blob/master/security/pkg/pki/util/keycertbundle.go#L284 or as soon as we get the cert bundle we assume an external entity is responsible to create valid combinations of ca certs/tls certs etc?

Copy link
Member Author

@ReToCode ReToCode Dec 15, 2023

Choose a reason for hiding this comment

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

Yes, we give the CA to envoy, envoy will then use it to verify the upstream TLS server cert. I don't think we need to pre-validate. Otherwise we'd also need to reconcile on a number of upstream certs which we actually don't really want to read from each ingress.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah just wondering if Istio is doing a pre-validation tep.

false,
&envoycorev3.TransportSocket{
Name: wellknown.TransportSocketTls,
ConfigType: typedConfig(false, nil),
Copy link
Contributor

Choose a reason for hiding this comment

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

Passing nil here (also tested with a missing cm) means that we end up having no validation since this is passed to

InlineBytes: expectedCert,
.
Check comments here too:
// TLS certificate data containing certificate authority certificates to use in verifying
// a presented peer certificate (e.g. server certificate for clusters or client certificate
// for listeners). If not specified and a peer certificate is presented it will not be
// verified. By default, a client certificate is optional, unless one of the additional
// options (:ref:`require_client_certificate
// <envoy_v3_api_field_extensions.transport_sockets.tls.v3.DownstreamTlsContext.require_client_certificate>`,
// :ref:`verify_certificate_spki
// <envoy_v3_api_field_extensions.transport_sockets.tls.v3.CertificateValidationContext.verify_certificate_spki>`,
// :ref:`verify_certificate_hash
// <envoy_v3_api_field_extensions.transport_sockets.tls.v3.CertificateValidationContext.verify_certificate_hash>`, or
// :ref:`match_typed_subject_alt_names
// <envoy_v3_api_field_extensions.transport_sockets.tls.v3.CertificateValidationContext.match_typed_subject_alt_names>`) is also
// specified.
//
// It can optionally contain certificate revocation lists, in which case Envoy will verify
// that the presented peer certificate has not been revoked by one of the included CRLs. Note
// that if a CRL is provided for any certificate authority in a trust chain, a CRL must be
// provided for all certificate authorities in that chain. Failure to do so will result in
// verification failure for both revoked and unrevoked certificates from that chain.
// The behavior of requiring all certificates to contain CRLs can be altered by

Would that allow tls communication without validation or connection is failing?
Also do you think that this affects us envoyproxy/envoy#19326 downstream? 🤔

Copy link
Contributor

@skonto skonto Dec 15, 2023

Choose a reason for hiding this comment

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

What about this part:

" Note
// that if a CRL is provided for any certificate authority in a trust chain, a CRL must be
// provided for all certificate authorities in that chain. Failure to do so will result in
// verification failure for both revoked and unrevoked certificates from that chain."
Do you know the implications of this (I am not familiar with this case)?

@skonto
Copy link
Contributor

skonto commented Dec 18, 2023

/lgtm
@ReToCode feel free to unhold if you are ready.

@knative-prow knative-prow bot added the lgtm Indicates that a PR is ready to be merged. label Dec 18, 2023
@skonto
Copy link
Contributor

skonto commented Dec 18, 2023

/hold

@knative-prow knative-prow bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 18, 2023
@ReToCode
Copy link
Member Author

/unhold

@knative-prow knative-prow bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Dec 19, 2023
@knative-prow knative-prow bot merged commit 00304db into knative-extensions:main Dec 19, 2023
41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants