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

linkerd_identity: split linkerd_identity::Id into DNS and URI variants #2538

Merged
merged 6 commits into from
Dec 8, 2023

Conversation

zaharidichev
Copy link
Member

@zaharidichev zaharidichev commented Nov 28, 2023

Our linkerd_identity::Id type was capable of representing
only DNS-like SANs. In order to be able to perform SAN
verification on SPIFFE identities, in this PR we turn this
type into an enum that can represent either a DNS or URI ids.

Additionally the logic in `meshtls::verifier`` has been updated
to consider URI SANs as well.

Signed-off-by: Zahari Dichev [email protected]

@zaharidichev zaharidichev requested a review from a team as a code owner November 28, 2023 16:51
linkerd/identity/src/lib.rs Outdated Show resolved Hide resolved
|| suffixes.iter().any(|s| s.contains(&id.to_str()))
}
}) => match id {
id::Id::Uri(_) => identities.contains(&*id.to_str()),
Copy link
Member Author

Choose a reason for hiding this comment

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

does that make sense for now ?

linkerd/app/outbound/src/opaq/concrete.rs Outdated Show resolved Hide resolved
Copy link

codecov bot commented Nov 28, 2023

Codecov Report

Merging #2538 (2defa19) into main (31b2aea) will increase coverage by 0.05%.
The diff coverage is 84.84%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2538      +/-   ##
==========================================
+ Coverage   67.61%   67.66%   +0.05%     
==========================================
  Files         330      330              
  Lines       14781    14803      +22     
==========================================
+ Hits         9994    10017      +23     
+ Misses       4787     4786       -1     
Files Coverage Δ
linkerd/app/inbound/src/policy.rs 90.00% <100.00%> (-1.49%) ⬇️
linkerd/app/outbound/src/http/require_id_header.rs 50.00% <100.00%> (+1.72%) ⬆️
linkerd/app/outbound/src/tcp/tagged_transport.rs 86.95% <ø> (ø)
linkerd/identity/src/lib.rs 100.00% <100.00%> (+9.09%) ⬆️
linkerd/meshtls/tests/util.rs 96.81% <100.00%> (+0.63%) ⬆️
linkerd/meshtls/verifier/src/lib.rs 92.85% <100.00%> (+5.90%) ⬆️
linkerd/proxy/api-resolve/src/metadata.rs 100.00% <100.00%> (ø)
linkerd/proxy/server-policy/src/authz.rs 71.11% <100.00%> (+1.34%) ⬆️
linkerd/tls/src/client.rs 67.21% <100.00%> (+3.72%) ⬆️
linkerd/app/outbound/src/http/concrete.rs 66.87% <33.33%> (ø)
... and 3 more

... and 2 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 31b2aea...2defa19. Read the comment docs.

@hawkw hawkw self-assigned this Nov 28, 2023
linkerd/app/outbound/src/opaq/concrete.rs Outdated Show resolved Hide resolved
linkerd/identity/src/lib.rs Outdated Show resolved Hide resolved
linkerd/identity/src/lib.rs Outdated Show resolved Hide resolved
linkerd/meshtls/verifier/src/lib.rs Outdated Show resolved Hide resolved
linkerd/meshtls/verifier/src/lib.rs Outdated Show resolved Hide resolved
pub struct Id(pub linkerd_dns_name::Name);
pub enum Id {
Dns(linkerd_dns_name::Name),
Uri(http::Uri),
Copy link
Member

@olix0r olix0r Nov 30, 2023

Choose a reason for hiding this comment

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

I'm not sure about the suitability of http::Uri to represent RFC 3986 URIs. The crate documentation makes no claims about satisfying the spec, and is designed to represent the HTTP-specific URI quirks.

I believe our primary options are:

We already have a dependency on the url crate:

:; cargo tree -i url --depth 1
url v2.3.0
├── linkerd-http-route v0.1.0 (/workspaces/linkerd2-proxy/linkerd/http-route)
└── trust-dns-proto v0.22.0

so this is probably a more appealing target.

Also, it provides an Url::as_str, so we can avoid allocating strings on each comparison.

Comment on lines 19 to 21
// Wildcards can perhaps be handled in a future path...
return None;
}
Copy link
Member

@olix0r olix0r Nov 30, 2023

Choose a reason for hiding this comment

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

I'm curious where this arises from. Is there a bug in the current name parsing? Is this not handled properly by the ID parser?

Copy link
Member

Choose a reason for hiding this comment

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

You mentioned that this was already present but I do not see any other wildcard handling in the diff.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Seems this has been there for a while. Do you remember the precise reason?

Copy link
Member

@olix0r olix0r Dec 8, 2023

Choose a reason for hiding this comment

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

This comes from cd04d7a#diff-6eab3f24cc45f109bf10a46bd6be573f28ebd28d826dd38ffb5a7cb1cca46bc7L135-L143 -- there used to be an explicit variant.

It made sense to document this case when it was an explicit enum case we had to handle, but I don't think it makes any sense to explicitly handle a "*" DNS name: A "*" dns name must not be parsed by Id::parse_dns_name(dns).

Copy link
Member

@olix0r olix0r left a comment

Choose a reason for hiding this comment

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

  • Is it true that we don't actually enforce any requirements around spiffe:// IDs? This seems fine I just want to confirm that's the case.
  • Given that, it seems beneficial to have a test that documents the behavior with, for instance, a http:// uri.

self.0.without_trailing_dot().fmt(f)
match self {
Self::Dns(dns) => dns.without_trailing_dot().fmt(f),
Self::Uri(uri) => uri.fmt(f),
Copy link
Member

Choose a reason for hiding this comment

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

Should we test round-tripping an Id through a parser? like:

something like

let id: Id = "uri://host:1234/path".parse().unwrap();
assert_eq!(id, id.to_string().parse().unwrap());

pub fn parse_uri(s: &str) -> Result<Self, InvalidId> {
http::Uri::try_from(s)
.map(Self::Uri)
.map_err(|e| InvalidId(e.into()))
Copy link
Member

Choose a reason for hiding this comment

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

codecov says we never hit InvalidId. is that true? should we test passing a DNS name to parse_uri?

Copy link
Member Author

Choose a reason for hiding this comment

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

I have added a test for that

@zaharidichev
Copy link
Member Author

Is it true that we don't actually enforce any requirements around spiffe:// IDs? This seems fine I just want to confirm that's the case.

Given that, it seems beneficial to have a test that documents the behavior with, for instance, a http:// uri.

It is true. I have added a test for that.

Signed-off-by: Zahari Dichev <[email protected]>
Signed-off-by: Zahari Dichev <[email protected]>
Signed-off-by: Zahari Dichev <[email protected]>
Signed-off-by: Zahari Dichev <[email protected]>
@zaharidichev zaharidichev requested a review from olix0r December 7, 2023 09:37
Comment on lines +82 to +83
# `trust-dns-proto`, depends on `idna` v0.2.3 while `url` depends on v0.5.0
{ name = "idna" },
Copy link
Member

Choose a reason for hiding this comment

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

I looked into this and (1) it appears that trust-dns has rebranded as hickory-dns, so we're going to have to be aware of that when we upgrade trust-dns; (2) and the latest version is on idna v0.4.

Normally, we'd follow up this PR with an upstream PR to update hickory-dns to idna 0.5 so that we can be sure to resolve this when we take the next hickory udpate.

These exceptions are fine temporarily, but each skip here means we're double-building dependencies. We try to keep this overhead manageable over time.

@@ -30,6 +30,7 @@ linkerd-meshtls-rustls = { path = "../../meshtls/rustls", optional = true }
linkerd-proxy-client-policy = { path = "../../proxy/client-policy" }
linkerd-tonic-watch = { path = "../../tonic-watch" }
linkerd2-proxy-api = { version = "0.12", features = ["inbound"] }
linkerd-identity = { path = "../../identity" }
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be necessary: app-inbound and app-outbound pick up the vast majority of their dependencies via reexport from app-core:

pub mod identity {
pub use linkerd_identity::*;
pub use linkerd_meshtls::*;
pub use linkerd_proxy_identity_client as client;
}

Comment on lines 364 to 368
tls::ConditionalClientTls::Some(tls::ClientTls::new(
client_tls.server_id,
client_tls.server_name,
alpn,
))
Copy link
Member

Choose a reason for hiding this comment

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

tioli

           .map(move |mut client_tls| {
                client_tls.alpn = if use_transport_header {
                    use linkerd_app_core::transport_header::PROTOCOL;
                    Some(tls::client::AlpnProtocols(vec![PROTOCOL.into()]))
                } else {
                    None
                };

                tls::ConditionalClientTls::Some(client_tls)

Copy link
Member

@olix0r olix0r Dec 8, 2023

Choose a reason for hiding this comment

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

alternately -- we could add a ClientTls::with_alpn(self, alpn: Option<AlpnProtocols>) -> ClientTls if you wanted to avoid the mut, so this would end up

           .map(move |client_tls| {
                tls::ConditionalClientTls::Some(client_tls.with_alpn(if use_transport_header {
                    use linkerd_app_core::transport_header::PROTOCOL;
                    Some(tls::client::AlpnProtocols(vec![PROTOCOL.into()]))
                } else {
                    None
                }))

@@ -59,8 +59,7 @@ impl<S> RequireIdentity<S> {
#[inline]
fn extract_id<B>(req: &mut http::Request<B>) -> Option<identity::Id> {
let v = req.headers_mut().remove(HEADER_NAME)?;
let n = v.to_str().ok()?.parse::<dns::Name>().ok()?;
Some(n.into())
v.to_str().ok()?.parse::<identity::Id>().ok()
Copy link
Member

Choose a reason for hiding this comment

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

tioli -- the explicit annotation is no longer needed because we're not using an intermediate type.

Suggested change
v.to_str().ok()?.parse::<identity::Id>().ok()
v.to_str().ok()?.parse().ok()

let alpn = if use_transport_header {
use linkerd_app_core::transport_header::PROTOCOL;
Some(tls::client::AlpnProtocols(vec![PROTOCOL.into()]))
} else {
None
};
tls::ConditionalClientTls::Some(tls::ClientTls::new(server_id, alpn))
tls::ConditionalClientTls::Some(tls::ClientTls::new(
Copy link
Member

Choose a reason for hiding this comment

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

same suggestion as above could apply here

Comment on lines 168 to 172
tls::ConditionalClientTls::Some(tls::ClientTls::new(
client_tls.server_id,
client_tls.server_name,
alpn,
))
Copy link
Member

Choose a reason for hiding this comment

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

and here

@@ -79,10 +79,9 @@ pub struct ConnectMeta<M> {

impl ClientTls {
// XXX(ver) We'll have to change this when ServerIds are not necessarily DNS names.
pub fn new(server_id: ServerId, alpn: Option<AlpnProtocols>) -> Self {
let ServerId(linkerd_identity::Id(name)) = server_id.clone();
pub fn new(server_id: ServerId, server_name: ServerName, alpn: Option<AlpnProtocols>) -> Self {
Copy link
Member

Choose a reason for hiding this comment

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

I suspect you could leave this unchanged -- avoiding having to specifiy None in many cases -- and explicitly set the alpn field only when an override is necessary.

Copy link
Member

@olix0r olix0r left a comment

Choose a reason for hiding this comment

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

There's nothing blocking merging this, though there are a few small cleanups/followups we should include.

@zaharidichev
Copy link
Member Author

@olix0r I will address the followups, and we can merge after

Signed-off-by: Zahari Dichev <[email protected]>
@zaharidichev zaharidichev merged commit c5b1f63 into linkerd:main Dec 8, 2023
34 checks passed
olix0r added a commit to linkerd/linkerd2 that referenced this pull request Dec 13, 2023
This change culminates recent work to restructure the balancer to use a
PoolQueue so that balancer changes may occur independently of request
processing. This replaces independent discovery buffering so that the
balancer task is responsible for polling discovery streams without
independent buffering. Requests are buffered and processed as soon as
the pool has available backends. Fail-fast circuit breaking is enforced
on the balancer's queue so that requests can't get stuck in a queue
indefinitely.

In general, the new balancer is instrumented directly with metrics, and
the relevant metric name prefix and labelset is provided by the stack.
In addition to detailed queue metrics including request (in-queue)
latency histograms, but also failfast states, discovery updates counts,
and balancer endpoint pool sizes.

---

* outbound: Move queues into the concrete stack (linkerd/linkerd2-proxy#2539)
* metrics: Remove unused features (linkerd/linkerd2-proxy#2542)
* Add the PoolQueue middleware (linkerd/linkerd2-proxy#2540)
* ci: Fixup codecov config (linkerd/linkerd2-proxy#2545)
* ci: Cancel prior runs (linkerd/linkerd2-proxy#2546)
* ci: Skip ARM builds during non-release CI (linkerd/linkerd2-proxy#2547)
* deps: Update tokio, tonic, and prost (linkerd/linkerd2-proxy#2544)
* build(deps): bump tj-actions/changed-files from 40.2.0 to 40.2.1 (linkerd/linkerd2-proxy#2549)
* metrics: Use prometheus-client for proxy_build_info (linkerd/linkerd2-proxy#2551)
* balance: Add a p2c Pool implementation (linkerd/linkerd2-proxy#2541)
* metrics: Export process metrics using prometheus-client (linkerd/linkerd2-proxy#2552)
* linkerd_identity: split `linkerd_identity::Id` into DNS and URI variants (linkerd/linkerd2-proxy#2538)
* outbound: Move HTTP balancer into its own module (linkerd/linkerd2-proxy#2554)
* app: Setup prom registry for use in balancers (linkerd/linkerd2-proxy#2555)
* vscode: Move workspace settings to devcontainer (linkerd/linkerd2-proxy#2557)
* build(deps): bump tj-actions/changed-files from 40.2.1 to 40.2.2 (linkerd/linkerd2-proxy#2556)
* balance: Instrument metrics in pool balancer (linkerd/linkerd2-proxy#2558)
* Enable PoolQueue balancer (linkerd/linkerd2-proxy#2559)

Signed-off-by: Oliver Gould <[email protected]>
olix0r added a commit to linkerd/linkerd2 that referenced this pull request Dec 14, 2023
This change culminates recent work to restructure the balancer to use a
PoolQueue so that balancer changes may occur independently of request
processing. This replaces independent discovery buffering so that the
balancer task is responsible for polling discovery streams without
independent buffering. Requests are buffered and processed as soon as
the pool has available backends. Fail-fast circuit breaking is enforced
on the balancer's queue so that requests can't get stuck in a queue
indefinitely.

In general, the new balancer is instrumented directly with metrics, and
the relevant metric name prefix and labelset is provided by the stack.
In addition to detailed queue metrics including request (in-queue)
latency histograms, but also failfast states, discovery updates counts,
and balancer endpoint pool sizes.

---

* outbound: Move queues into the concrete stack (linkerd/linkerd2-proxy#2539)
* metrics: Remove unused features (linkerd/linkerd2-proxy#2542)
* Add the PoolQueue middleware (linkerd/linkerd2-proxy#2540)
* ci: Fixup codecov config (linkerd/linkerd2-proxy#2545)
* ci: Cancel prior runs (linkerd/linkerd2-proxy#2546)
* ci: Skip ARM builds during non-release CI (linkerd/linkerd2-proxy#2547)
* deps: Update tokio, tonic, and prost (linkerd/linkerd2-proxy#2544)
* build(deps): bump tj-actions/changed-files from 40.2.0 to 40.2.1 (linkerd/linkerd2-proxy#2549)
* metrics: Use prometheus-client for proxy_build_info (linkerd/linkerd2-proxy#2551)
* balance: Add a p2c Pool implementation (linkerd/linkerd2-proxy#2541)
* metrics: Export process metrics using prometheus-client (linkerd/linkerd2-proxy#2552)
* linkerd_identity: split `linkerd_identity::Id` into DNS and URI variants (linkerd/linkerd2-proxy#2538)
* outbound: Move HTTP balancer into its own module (linkerd/linkerd2-proxy#2554)
* app: Setup prom registry for use in balancers (linkerd/linkerd2-proxy#2555)
* vscode: Move workspace settings to devcontainer (linkerd/linkerd2-proxy#2557)
* build(deps): bump tj-actions/changed-files from 40.2.1 to 40.2.2 (linkerd/linkerd2-proxy#2556)
* balance: Instrument metrics in pool balancer (linkerd/linkerd2-proxy#2558)
* Enable PoolQueue balancer (linkerd/linkerd2-proxy#2559)

Signed-off-by: Oliver Gould <[email protected]>
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.

3 participants