Skip to content

Commit 86b73aa

Browse files
committed
admin: Assume meshed connections are HTTP/2 (#982)
We've recently updated the admin server to assume that protocol detection timeouts indicate that the client was just HTTP/1; but this will almost never be true for meshed connections, which are transported over HTTP/2. This change updates the fallback logic to account for this. Detection timeouts on meshed connections are now assumed to be HTTP/2. When we get TLS connections that we can't terminate, we now return an error message that includes the SNI value to more clearly indicate that the client expected an alternate server identity.
1 parent b58ced6 commit 86b73aa

File tree

1 file changed

+38
-6
lines changed

1 file changed

+38
-6
lines changed

linkerd/app/src/admin.rs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,12 @@ pub struct Admin {
3232
}
3333

3434
#[derive(Debug, Error)]
35-
#[error("non-HTTP connection from {} (tls={:?})", self.0.client_addr, self.0.tls)]
36-
struct NonHttpClient(TcpAccept);
35+
#[error("non-HTTP connection from {}", self.0)]
36+
struct NonHttpClient(Remote<ClientAddr>);
37+
38+
#[derive(Debug, Error)]
39+
#[error("Unexpected TLS connection to {} from {}", self.0, self.1)]
40+
struct UnexpectedSni(tls::ServerId, Remote<ClientAddr>);
3741

3842
// === impl Config ===
3943

@@ -77,11 +81,39 @@ impl Config {
7781
)| {
7882
match version {
7983
Ok(Some(version)) => Ok(HttpAccept::from((version, tcp))),
80-
Err(_) => {
81-
debug!("HTTP detection timed out; handling as HTTP/1");
82-
Ok(HttpAccept::from((http::Version::Http1, tcp)))
84+
// If detection timed out, we can make an educated guess
85+
// at the proper behavior:
86+
// - If the connection was meshed, it was most likely
87+
// transported over HTTP/2.
88+
// - If the connection was unmehsed, it was mostly
89+
// likely HTTP/1.
90+
// - If we received some unexpected SNI, the client is
91+
// mostly likely confused/stale.
92+
Err(_timeout) => {
93+
let version = match tcp.tls.clone() {
94+
tls::ConditionalServerTls::None(_) => http::Version::Http1,
95+
tls::ConditionalServerTls::Some(tls::ServerTls::Established {
96+
..
97+
}) => http::Version::H2,
98+
tls::ConditionalServerTls::Some(tls::ServerTls::Passthru {
99+
sni,
100+
}) => {
101+
debug_assert!(false, "If we know the stream is non-mesh TLS, we should be able to prove its not HTTP.");
102+
return Err(Error::from(UnexpectedSni(sni, tcp.client_addr)));
103+
}
104+
};
105+
debug!(%version, "HTTP detection timed out; assuming HTTP");
106+
Ok(HttpAccept::from((version, tcp)))
83107
}
84-
Ok(None) => Err(NonHttpClient(tcp)),
108+
// If the connection failed HTTP detection, check if we
109+
// detected TLS for another target. This might indicate
110+
// that the client is confused/stale.
111+
Ok(None) => match tcp.tls {
112+
tls::ConditionalServerTls::Some(tls::ServerTls::Passthru { sni }) => {
113+
Err(UnexpectedSni(sni, tcp.client_addr).into())
114+
}
115+
_ => Err(NonHttpClient(tcp.client_addr).into()),
116+
},
85117
}
86118
},
87119
)

0 commit comments

Comments
 (0)