-
Notifications
You must be signed in to change notification settings - Fork 271
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
Extract TLS id verification out of TLS backends #2507
Extract TLS id verification out of TLS backends #2507
Conversation
let server_name = match hello.server_name() { | ||
Some(name) => { | ||
let name = webpki::DnsNameRef::try_from_ascii_str(name) | ||
.expect("server name must be a valid server name"); | ||
webpki::SubjectNameRef::DnsName(name) | ||
} | ||
None => { | ||
debug!("no SNI -> no certificate"); | ||
return None; | ||
} | ||
}; | ||
|
||
// Verify that our certificate is valid for the given SNI name. | ||
let c = self.0.cert.first()?; | ||
if let Err(error) = webpki::EndEntityCert::try_from(c.as_ref()) | ||
.and_then(|c| c.verify_is_valid_for_subject_name(server_name)) | ||
{ | ||
debug!(%error, "Local certificate is not valid for SNI"); | ||
return None; | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is removed, because if we assume that our certificate can holds its Id in either URI or DNS SAN, we cannot pose the restriction that the Server Name that is contained in the ClientHello matches the value of the SAN. This further begs the question of whether we should use a resolver at all. An alternative approach is to just set the certificates that will be used when we construct the tls config.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is an analogous change necessary on the boring side?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not really. In boring we set the certificate that is going to be used, rather than relying on a resolver to pick it. There is the option to do that here as well. Not sure why the resolver has been picked as an approach. Maybe to add an extra check to ensure that the cert will have the SNI from the ClientHello.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I expect it's so that the TLS library may vary the certificates it presents based on the SNI value.
}; | ||
use tracing::trace; | ||
|
||
pub(crate) struct AnySANVerifier(Arc<RootCertStore>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not too sure about the naming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a few low-level suggestions I'll make in a followup, but my initial high-level reaction is (1) this is great and (2) verify_id
needs tests :)
We need to ensure that both of our verify_id implementations have the same behavior for a set of Certificate & Id pairs. Note that the linkerd-tls-test-util crate provides some readymade certificates that can be used for testing.
The key point is: by taking this verification logic out of the library--even though we're just copying an implementation in the short term--it becomes our responsibility to ensure correctness going forward.
To that end, perhaps there are rustls tests we can incorporate?
@olix0r That makes sense. I will add tests for these in my tomorrow. |
If we need test certs that aren't those already checked in, we may also want to consider the |
server_name, | ||
config, | ||
} | ||
} | ||
} | ||
|
||
fn extract_cert(c: &rustls::ClientConnection) -> io::Result<&rustls::Certificate> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm not sure if io::Error
is really the right return type for this, since the error is not actually IO-related...should we maybe instead change the Service::Error
associated type for Connect
? Or, does that break some other thing?
Signed-off-by: Zahari Dichev <zaharidichev@gmail.com>
4de0156
to
03ceeb5
Compare
Signed-off-by: Zahari Dichev <zaharidichev@gmail.com>
Signed-off-by: Zahari Dichev <zaharidichev@gmail.com>
6dca7be
to
e508425
Compare
Signed-off-by: Zahari Dichev <zaharidichev@gmail.com>
Note that we're going to have to allow duplicate versions of base64 in deny.toml |
Signed-off-by: Zahari Dichev <zaharidichev@gmail.com>
Signed-off-by: Zahari Dichev <zaharidichev@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent! Thank you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some last nits, but overall, this looks good to me!
match c.peer_certificates().and_then(|certs| certs.first()) { | ||
Some(leaf_cert) => io::Result::Ok(leaf_cert), | ||
None => Err(io::Error::new(io::ErrorKind::Other, "missing tls end cert")), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tiny nit: isn't this match just Option::ok_or_else
?
debug!("Certified"); | ||
Ok(()) | ||
|
||
// verify the id as the cert verifier does not do that (on purpose) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
// verify the id as the cert verifier does not do that (on purpose) | |
// verify the identity, as the cert verifier does not do that (on purpose) |
it might also be good if this comment explained why we don't do that --- right now this just says it's "on purpose", but it doesn't say why...
I'm going to merge this through but feel free to address nits in followups! |
* Include server address in server error logs (linkerd/linkerd2-proxy#2500) * dev: v42 (linkerd/linkerd2-proxy#2501) * Separate tls::ServerName and identity::Id types (linkerd/linkerd2-proxy#2506) * Use reference-counted strings in dns::Name (linkerd/linkerd2-proxy#2509) * build(deps): bump tj-actions/changed-files from 39.2.0 to 40.1.1 (linkerd/linkerd2-proxy#2508) * build(deps): bump actions/checkout from 4.1.0 to 4.1.1 (linkerd/linkerd2-proxy#2485) * ci: Fix check-each workflow (linkerd/linkerd2-proxy#2511) * ci: Turn off debuginfo in ci test builds (linkerd/linkerd2-proxy#2512) * ci: Fix fuzzer listing (linkerd/linkerd2-proxy#2513) * Use heap indirection to manage type signatures (linkerd/linkerd2-proxy#2510) * dev: optimize image build (linkerd/linkerd2-proxy#2452) * dev: Disable nightly install for now (linkerd/linkerd2-proxy#2515) * meshtls: Extract TLS id verification out of TLS backends (linkerd/linkerd2-proxy#2507) * Update DEFAULT_OUTBOUND_TCP_QUEUE_CAPACITY to 10K (linkerd/linkerd2-proxy#2521) * admin: Add optional /debug/pprof/profile endpoint (linkerd/linkerd2-proxy#2516) Signed-off-by: Oliver Gould <ver@buoyant.io>
* Include server address in server error logs (linkerd/linkerd2-proxy#2500) * dev: v42 (linkerd/linkerd2-proxy#2501) * Separate tls::ServerName and identity::Id types (linkerd/linkerd2-proxy#2506) * Use reference-counted strings in dns::Name (linkerd/linkerd2-proxy#2509) * build(deps): bump tj-actions/changed-files from 39.2.0 to 40.1.1 (linkerd/linkerd2-proxy#2508) * build(deps): bump actions/checkout from 4.1.0 to 4.1.1 (linkerd/linkerd2-proxy#2485) * ci: Fix check-each workflow (linkerd/linkerd2-proxy#2511) * ci: Turn off debuginfo in ci test builds (linkerd/linkerd2-proxy#2512) * ci: Fix fuzzer listing (linkerd/linkerd2-proxy#2513) * Use heap indirection to manage type signatures (linkerd/linkerd2-proxy#2510) * dev: optimize image build (linkerd/linkerd2-proxy#2452) * dev: Disable nightly install for now (linkerd/linkerd2-proxy#2515) * meshtls: Extract TLS id verification out of TLS backends (linkerd/linkerd2-proxy#2507) * Update DEFAULT_OUTBOUND_TCP_QUEUE_CAPACITY to 10K (linkerd/linkerd2-proxy#2521) * admin: Add optional /debug/pprof/profile endpoint (linkerd/linkerd2-proxy#2516) * proxy: Use debian12 distroless base image Signed-off-by: Oliver Gould <ver@buoyant.io>
To further prepare for the introduction of Spiffe IDs in the proxy,
this change disables TLS SAN verification in both rustls and boring
TLS implementations. The need to do that stems from the fact that
these implementations only look at DNS SANs and cannot handle
certificates that use URI SANs.
To enable adding this functionality, TLS Id verification is not extracted
as a separate step, after the TLS handshake completes. To do that in
boring, we need to
verify_hostname
. For rustls, we need to pass acustom
ServerCertVerifier
that skips name validation. In our case, theverifier is nearly identical to
ustls::client::WebPkiVerifier
, except forthe step where DNS SANs are validated based on the
ServerName
.This change is non functional, but it sets the stage to have more control
over TLS Id verification.
Signed-off-by: Zahari Dichev zaharidichev@gmail.com