Pass tunnel info into sec-websocket-protocol header

This commit is contained in:
Σrebe - Romain GERARD 2023-12-26 21:16:34 +01:00
parent f752ce67fb
commit 259da14d4d
No known key found for this signature in database
GPG key ID: 7A42B4B97E0332F4
3 changed files with 29 additions and 24 deletions

View file

@ -1,4 +1,4 @@
use super::{to_host_port, JwtTunnelConfig, JWT_KEY}; use super::{to_host_port, JwtTunnelConfig, JWT_HEADER_PREFIX, JWT_KEY};
use crate::{LocalToRemote, WsClientConfig}; use crate::{LocalToRemote, WsClientConfig};
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
@ -8,7 +8,7 @@ use fastwebsockets::WebSocket;
use futures_util::pin_mut; use futures_util::pin_mut;
use http_body_util::Empty; use http_body_util::Empty;
use hyper::body::Incoming; use hyper::body::Incoming;
use hyper::header::{AUTHORIZATION, COOKIE, SEC_WEBSOCKET_VERSION, UPGRADE}; use hyper::header::{AUTHORIZATION, COOKIE, SEC_WEBSOCKET_PROTOCOL, SEC_WEBSOCKET_VERSION, UPGRADE};
use hyper::header::{CONNECTION, HOST, SEC_WEBSOCKET_KEY}; use hyper::header::{CONNECTION, HOST, SEC_WEBSOCKET_KEY};
use hyper::upgrade::Upgraded; use hyper::upgrade::Upgraded;
use hyper::{Request, Response}; use hyper::{Request, Response};
@ -42,16 +42,16 @@ pub async fn connect(
let mut req = Request::builder() let mut req = Request::builder()
.method("GET") .method("GET")
.uri(format!( .uri(format!("/{}/events", &client_cfg.http_upgrade_path_prefix,))
"/{}/events?bearer={}",
&client_cfg.http_upgrade_path_prefix,
tunnel_to_jwt_token(request_id, tunnel_cfg)
))
.header(HOST, &client_cfg.http_header_host) .header(HOST, &client_cfg.http_header_host)
.header(UPGRADE, "websocket") .header(UPGRADE, "websocket")
.header(CONNECTION, "upgrade") .header(CONNECTION, "upgrade")
.header(SEC_WEBSOCKET_KEY, fastwebsockets::handshake::generate_key()) .header(SEC_WEBSOCKET_KEY, fastwebsockets::handshake::generate_key())
.header(SEC_WEBSOCKET_VERSION, "13") .header(SEC_WEBSOCKET_VERSION, "13")
.header(
SEC_WEBSOCKET_PROTOCOL,
format!("v1, {}{}", JWT_HEADER_PREFIX, tunnel_to_jwt_token(request_id, tunnel_cfg)),
)
.version(hyper::Version::HTTP_11); .version(hyper::Version::HTTP_11);
for (k, v) in &client_cfg.http_headers { for (k, v) in &client_cfg.http_headers {

View file

@ -49,6 +49,7 @@ impl JwtTunnelConfig {
} }
} }
static JWT_HEADER_PREFIX: &str = "authorization.bearer.";
static JWT_SECRET: &[u8; 15] = b"champignonfrais"; static JWT_SECRET: &[u8; 15] = b"champignonfrais";
static JWT_KEY: Lazy<(Header, EncodingKey)> = static JWT_KEY: Lazy<(Header, EncodingKey)> =
Lazy::new(|| (Header::new(Algorithm::HS256), EncodingKey::from_secret(JWT_SECRET))); Lazy::new(|| (Header::new(Algorithm::HS256), EncodingKey::from_secret(JWT_SECRET)));

View file

@ -10,10 +10,10 @@ use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use super::{JwtTunnelConfig, JWT_DECODE}; use super::{JwtTunnelConfig, JWT_DECODE, JWT_HEADER_PREFIX};
use crate::{socks5, tcp, tls, udp, LocalProtocol, WsServerConfig}; use crate::{socks5, tcp, tls, udp, LocalProtocol, WsServerConfig};
use hyper::body::Incoming; use hyper::body::Incoming;
use hyper::header::COOKIE; use hyper::header::{COOKIE, SEC_WEBSOCKET_PROTOCOL};
use hyper::http::HeaderValue; use hyper::http::HeaderValue;
use hyper::server::conn::http1; use hyper::server::conn::http1;
use hyper::service::service_fn; use hyper::service::service_fn;
@ -221,22 +221,23 @@ fn validate_url(
#[inline] #[inline]
fn extract_tunnel_info(req: &Request<Incoming>) -> Result<TokenData<JwtTunnelConfig>, Response<String>> { fn extract_tunnel_info(req: &Request<Incoming>) -> Result<TokenData<JwtTunnelConfig>, Response<String>> {
let jwt: TokenData<JwtTunnelConfig> = match req.uri().query().unwrap_or_default().split_once('=') { let jwt = req
Some(("bearer", jwt)) => { .headers()
let (validation, decode_key) = JWT_DECODE.deref(); .get(SEC_WEBSOCKET_PROTOCOL)
match jsonwebtoken::decode(jwt, decode_key, validation) { .and_then(|header| header.to_str().ok())
Ok(jwt) => jwt, .and_then(|header| header.split_once(JWT_HEADER_PREFIX))
err => { .map(|(_prefix, jwt)| jwt)
error!("error while decoding jwt for tunnel info {:?}", err); .unwrap_or_default();
return Err(http::Response::builder()
.status(StatusCode::BAD_REQUEST) let (validation, decode_key) = JWT_DECODE.deref();
.body("Invalid upgrade request".to_string()) let jwt = match jsonwebtoken::decode(jwt, decode_key, validation) {
.unwrap()); Ok(jwt) => jwt,
}
}
}
err => { err => {
error!("Missing jwt tunnel config from request {:?}", err); warn!(
"error while decoding jwt for tunnel info {:?} header {:?}",
err,
req.headers().get(SEC_WEBSOCKET_PROTOCOL)
);
return Err(http::Response::builder() return Err(http::Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body("Invalid upgrade request".to_string()) .body("Invalid upgrade request".to_string())
@ -358,6 +359,9 @@ async fn server_upgrade(server_config: Arc<WsServerConfig>, mut req: Request<Inc
}; };
response.headers_mut().insert(COOKIE, header_val); response.headers_mut().insert(COOKIE, header_val);
} }
response
.headers_mut()
.insert(SEC_WEBSOCKET_PROTOCOL, HeaderValue::from_static("v1"));
Response::from_parts(response.into_parts().0, "".to_string()) Response::from_parts(response.into_parts().0, "".to_string())
} }