Skip to content

Commit c14a97e

Browse files
committed
fix(mmds): Reject X-Forwarded-For in case-insensitive way
As EC2 IMDS does, MMDS denies PUT requests if X-Forwarded-For header is included, but it was validated only in a case-sensitive way. However, it is defined that HTTP headers are case-insensitive. We should deny it regardless of its case. Signed-off-by: Takahiro Itazuri <[email protected]>
1 parent 72277a8 commit c14a97e

File tree

2 files changed

+27
-19
lines changed

2 files changed

+27
-19
lines changed

src/vmm/src/mmds/mod.rs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -235,11 +235,13 @@ fn respond_to_put_request(mmds: &mut Mmds, request: Request) -> Response {
235235
let custom_headers = request.headers.custom_entries();
236236

237237
// Reject `PUT` requests that contain `X-Forwarded-For` header.
238-
if custom_headers.contains_key(REJECTED_HEADER) {
239-
let error_msg = RequestError::HeaderError(HttpHeaderError::UnsupportedName(
240-
REJECTED_HEADER.to_string(),
241-
))
242-
.to_string();
238+
if let Some((header, __)) = custom_headers
239+
.iter()
240+
.find(|(k, _)| k.to_lowercase() == REJECTED_HEADER)
241+
{
242+
let error_msg =
243+
RequestError::HeaderError(HttpHeaderError::UnsupportedName(header.to_string()))
244+
.to_string();
243245
return build_response(
244246
request.http_version(),
245247
StatusCode::BadRequest,
@@ -733,19 +735,24 @@ mod tests {
733735
assert_eq!(actual_response.content_type(), MediaType::PlainText);
734736

735737
// Test unsupported `X-Forwarded-For` header
736-
let request = Request::try_from(
737-
b"PUT http://169.254.169.254/latest/api/token HTTP/1.0\r\n\
738-
X-Forwarded-For: 203.0.113.195\r\n\r\n",
739-
None,
740-
)
741-
.unwrap();
742-
let mut expected_response = Response::new(Version::Http10, StatusCode::BadRequest);
743-
expected_response.set_content_type(MediaType::PlainText);
744-
expected_response.set_body(Body::new(
745-
"Invalid header. Reason: Unsupported header name. Key: X-Forwarded-For".to_string(),
746-
));
747-
let actual_response = convert_to_response(mmds.clone(), request);
748-
assert_eq!(actual_response, expected_response);
738+
for header in ["X-Forwarded-For", "x-forwarded-for", "X-FoRwArDeD-FoR"] {
739+
let request = Request::try_from(
740+
format!(
741+
"PUT http://169.254.169.254/latest/api/token HTTP/1.0\r\n\
742+
{header}: 203.0.113.195\r\n\r\n"
743+
)
744+
.as_bytes(),
745+
None,
746+
)
747+
.unwrap();
748+
let mut expected_response = Response::new(Version::Http10, StatusCode::BadRequest);
749+
expected_response.set_content_type(MediaType::PlainText);
750+
expected_response.set_body(Body::new(format!(
751+
"Invalid header. Reason: Unsupported header name. Key: {header}"
752+
)));
753+
let actual_response = convert_to_response(mmds.clone(), request);
754+
assert_eq!(actual_response, expected_response);
755+
}
749756

750757
// Test invalid path
751758
let request = Request::try_from(

src/vmm/src/mmds/token_headers.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use std::result::Result;
77
use micro_http::{HttpHeaderError, RequestError};
88

99
/// Header rejected by MMDS.
10-
pub const REJECTED_HEADER: &str = "X-Forwarded-For";
10+
/// Defined in lowercase since HTTP headers are case-insensitive.
11+
pub const REJECTED_HEADER: &str = "x-forwarded-for";
1112

1213
/// `X-metadata-token` header might be used by HTTP clients to specify a token in order to
1314
/// authenticate to the session. This is used for GET requests issued by the guest to MMDS only.

0 commit comments

Comments
 (0)