Skip to content

Commit

Permalink
do not replace host header
Browse files Browse the repository at this point in the history
  • Loading branch information
polvorin committed Aug 30, 2024
1 parent 200db71 commit 4a04b36
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 123 deletions.
68 changes: 21 additions & 47 deletions implementations/rust/ockam/ockam_api/src/http_auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,49 +28,39 @@ struct HttpAuthInterceptorState {
struct HttpAuthInterceptor {
state: Arc<Mutex<HttpAuthInterceptorState>>,
token_refresher: TokenLeaseRefresher,
upstream: String,
}

impl HttpAuthInterceptor {
fn new(token_refresher: TokenLeaseRefresher, upstream: String) -> Self {
fn new(token_refresher: TokenLeaseRefresher) -> Self {
let state = HttpAuthInterceptorState {
state: RequestState::ParsingHeader(None),
};
Self {
state: Arc::new(Mutex::new(state)),
token_refresher,
upstream,
}
}
}

pub struct HttpAuthInterceptorFactory {
token_refresher: TokenLeaseRefresher,
upstream: String,
}

impl HttpAuthInterceptorFactory {
pub fn new(token_refresher: TokenLeaseRefresher, upstream: String) -> Self {
Self {
token_refresher,
upstream,
}
pub fn new(token_refresher: TokenLeaseRefresher) -> Self {
Self { token_refresher }
}
}

impl PortalInterceptorFactory for HttpAuthInterceptorFactory {
fn create(&self) -> Arc<dyn PortalInterceptor> {
Arc::new(HttpAuthInterceptor::new(
self.token_refresher.clone(),
self.upstream.clone(),
))
Arc::new(HttpAuthInterceptor::new(self.token_refresher.clone()))
}
}

fn attach_auth_token_and_serialize_into(
req: &httparse::Request,
token: &str,
upstream: &str,
buffer: &mut Vec<u8>,
) {
debug!("Serializing http req header");
Expand All @@ -84,9 +74,8 @@ fn attach_auth_token_and_serialize_into(
.unwrap();

write!(buffer, "Authorization: Token {}\r\n", token).unwrap();
write!(buffer, "Host: {}\r\n", upstream).unwrap();
for h in &*req.headers {
if !(h.name.eq_ignore_ascii_case("HOST") || h.name.eq_ignore_ascii_case("Authorization")) {
if !h.name.eq_ignore_ascii_case("Authorization") {
write!(buffer, "{}: ", h.name).unwrap();
buffer.extend_from_slice(h.value);
buffer.extend_from_slice(b"\r\n");
Expand Down Expand Up @@ -124,15 +113,10 @@ fn body_state(method: &str, headers: &[Header]) -> ockam_core::Result<RequestSta

impl RequestState {
/* Parse the incoming data, attaching an Authorization header token to it.
* data is received in chunks, and there is no warranty on what we get on each:
* incomplete requests, multiple requests, etc.
*/
fn process_http_buffer(
&mut self,
buf: &[u8],
token: &str,
upstream: &str,
) -> ockam_core::Result<Vec<u8>> {
* data is received in chunks, and there is no warranty on what we get on each:
* incomplete requests, multiple requests, etc.
*/
fn process_http_buffer(&mut self, buf: &[u8], token: &str) -> ockam_core::Result<Vec<u8>> {
let mut acc = Vec::with_capacity(buf.len());
let mut cursor = buf;
loop {
Expand Down Expand Up @@ -162,7 +146,7 @@ impl RequestState {
}
Ok(httparse::Status::Complete(body_offset)) => {
cursor = &cursor[body_offset - prev_size..];
attach_auth_token_and_serialize_into(&req, token, upstream, &mut acc);
attach_auth_token_and_serialize_into(&req, token, &mut acc);
*self = body_state(req.method.unwrap(), req.headers)?;
}
Err(e) => {
Expand Down Expand Up @@ -269,11 +253,9 @@ impl PortalInterceptor for HttpAuthInterceptor {
if token.is_none() {
error!("No authorization token available");
}
let out = guard.state.process_http_buffer(
buffer,
&token.unwrap_or_default(),
&self.upstream,
)?;
let out = guard
.state
.process_http_buffer(buffer, &token.unwrap_or_default())?;
Ok(Some(out))
}
}
Expand All @@ -293,11 +275,9 @@ Transfer-Encoding: gzip, chunked\r\n\r\n\

const TOKEN: &str = "SAMPLE-TOKEN";

const UPSTREAM: &str = "upstream.com:6565";

const EXPECTED: &str = "POST / HTTP/1.1\r\n\
Authorization: Token SAMPLE-TOKEN\r\n\
Host: upstream.com:6565\r\n\
Host: www.example.com\r\n\
User-Agent: Mozilla/5.0\r\n\
Accept-Encoding: gzip, deflate, br\r\n\
Transfer-Encoding: gzip, chunked\r\n\r\n\
Expand All @@ -313,9 +293,7 @@ Transfer-Encoding: gzip, chunked\r\n\r\n\
let mut result = Vec::new();
let mut request_state = RequestState::ParsingHeader(None);
for chunk in data.chunks(size) {
let data_out = request_state
.process_http_buffer(chunk, TOKEN, UPSTREAM)
.unwrap();
let data_out = request_state.process_http_buffer(chunk, TOKEN).unwrap();
result.extend_from_slice(&data_out);
}
assert_eq!(
Expand All @@ -336,11 +314,11 @@ field1=value1&field2=value2";
let expected_r = format!(
"POST /test HTTP/1.1\r\n\
Authorization: Token {}\r\n\
Host: {}\r\n\
Host: foo.example\r\n\
Content-Type: application/x-www-form-urlencoded\r\n\
Content-Length: 27\r\n\r\n\
field1=value1&field2=value2",
TOKEN, UPSTREAM
TOKEN
);

let data = [req.as_bytes(), req.as_bytes()].concat();
Expand All @@ -350,9 +328,7 @@ field1=value1&field2=value2",
let mut result = Vec::new();
let mut request_state = RequestState::ParsingHeader(None);
for chunk in data.chunks(size) {
let data_out = request_state
.process_http_buffer(chunk, TOKEN, UPSTREAM)
.unwrap();
let data_out = request_state.process_http_buffer(chunk, TOKEN).unwrap();
result.extend_from_slice(&data_out);
}
assert_eq!(
Expand All @@ -371,18 +347,16 @@ field1=value1&field2=value2",
data.extend_from_slice(req.as_bytes());

let mut expected = format!(
"GET /home/user/example.txt HTTP/1.1\r\nAuthorization: Token {}\r\nHost: {}\r\n\r\n",
TOKEN, UPSTREAM
"GET /home/user/example.txt HTTP/1.1\r\nAuthorization: Token {}\r\n\r\n",
TOKEN
);
expected = expected.clone() + &expected;

for size in [1, 5, 32, 1024] {
let mut result = Vec::new();
let mut request_state = RequestState::ParsingHeader(None);
for chunk in data.chunks(size) {
let data_out = request_state
.process_http_buffer(chunk, TOKEN, UPSTREAM)
.unwrap();
let data_out = request_state.process_http_buffer(chunk, TOKEN).unwrap();
result.extend_from_slice(&data_out);
}
assert_eq!(String::from_utf8(result).unwrap(), expected);
Expand Down
12 changes: 6 additions & 6 deletions implementations/rust/ockam/ockam_api/src/nodes/models/portal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ pub struct CreateInlet {
/// TCP won't be used to transfer data between the Inlet and the Outlet.
#[n(10)] pub disable_tcp_fallback: bool,

/// If present, it instruct the creation of HTTP inlet, that is, TCP inlet +
/// If enabled it instruct the creation of HTTP inlet, that is, TCP inlet +
/// a http interceptor that modify incoming requests, adding to them an Authorization
/// token.
#[n(11)] pub http_upstream: Option<HostnamePort>,
#[n(11)] pub is_http_auth_inlet: bool,
}

impl CreateInlet {
Expand All @@ -68,7 +68,7 @@ impl CreateInlet {
wait_connection: bool,
enable_udp_puncture: bool,
disable_tcp_fallback: bool,
http_upstream: Option<HostnamePort>,
is_http_auth_inlet: bool,
) -> Self {
Self {
listen_addr: listen,
Expand All @@ -81,7 +81,7 @@ impl CreateInlet {
secure_channel_identifier: None,
enable_udp_puncture,
disable_tcp_fallback,
http_upstream,
is_http_auth_inlet,
}
}

Expand All @@ -94,7 +94,7 @@ impl CreateInlet {
wait_connection: bool,
enable_udp_puncture: bool,
disable_tcp_fallback: bool,
http_upstream: Option<HostnamePort>,
is_http_auth_inlet: bool,
) -> Self {
Self {
listen_addr: listen,
Expand All @@ -107,7 +107,7 @@ impl CreateInlet {
secure_channel_identifier: None,
enable_udp_puncture,
disable_tcp_fallback,
http_upstream,
is_http_auth_inlet,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl Inlets for BackgroundNodeClient {
secure_channel_identifier: &Option<Identifier>,
enable_udp_puncture: bool,
disable_tcp_fallback: bool,
http_upstream: Option<HostnamePort>,
is_http_auth_inlet: bool,
) -> miette::Result<Reply<InletStatus>> {
let request = {
let via_project = outlet_addr.matches(0, &[ProjectProto::CODE.into()]);
Expand All @@ -39,7 +39,7 @@ impl Inlets for BackgroundNodeClient {
wait_connection,
enable_udp_puncture,
disable_tcp_fallback,
http_upstream,
is_http_auth_inlet,
)
} else {
CreateInlet::to_node(
Expand All @@ -50,7 +50,7 @@ impl Inlets for BackgroundNodeClient {
wait_connection,
enable_udp_puncture,
disable_tcp_fallback,
http_upstream,
is_http_auth_inlet,
)
};
if let Some(e) = policy_expression.as_ref() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub trait Inlets {
secure_channel_identifier: &Option<Identifier>,
enable_udp_puncture: bool,
disable_tcp_fallback: bool,
http_upstream: Option<HostnamePort>,
is_http_auth_inlet: bool,
) -> miette::Result<Reply<InletStatus>>;

async fn show_inlet(&self, ctx: &Context, alias: &str) -> miette::Result<Reply<InletStatus>>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use ockam::{route, Address, Result};
use ockam_core::api::{Error, Response};
use ockam_core::AllowAll;
use ockam_node::Context;
use ockam_transport_core::HostnamePort;
use ockam_transport_tcp::PortalInletInterceptor;

use crate::http_auth::HttpAuthInterceptorFactory;
Expand Down Expand Up @@ -35,13 +34,12 @@ impl NodeManagerWorker {
secure_channel_identifier,
enable_udp_puncture,
disable_tcp_fallback,
http_upstream,
is_http_auth_inlet,
} = create_inlet;

// PABLO
let prefix_route = if let Some(upstream) = http_upstream {
let prefix_route = if is_http_auth_inlet {
let interceptor_address = self
.create_http_auth_interceptor(ctx, upstream, &alias)
.create_http_auth_interceptor(ctx, &alias)
.await
.map_err(|e| {
Response::bad_request_no_request(&format!(
Expand Down Expand Up @@ -80,14 +78,10 @@ impl NodeManagerWorker {
async fn create_http_auth_interceptor(
&self,
ctx: &Context,
upstream: HostnamePort,
inlet_alias: &String,
) -> Result<Address, Error> {
let token_refresher = TokenLeaseRefresher::new(ctx, self.node_manager.clone()).await?;
let http_interceptor_factory = Arc::new(HttpAuthInterceptorFactory::new(
token_refresher,
format!("{}:{}", upstream.hostname(), upstream.port()).to_string(),
));
let http_interceptor_factory = Arc::new(HttpAuthInterceptorFactory::new(token_refresher));

let interceptor_address: Address = (inlet_alias.to_owned() + "_http_intercetor").into();
//TODO: incoming and outgoing access control?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ impl AppState {
&None,
false,
false,
None,
false,
)
.await
.map_err(|err| {
Expand Down
Loading

0 comments on commit 4a04b36

Please sign in to comment.