Skip to content

Commit a4ecd0d

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 0522721 commit a4ecd0d

File tree

2 files changed

+28
-19
lines changed

2 files changed

+28
-19
lines changed

src/vmm/src/mmds/mod.rs

Lines changed: 26 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,
@@ -734,19 +736,25 @@ mod tests {
734736
assert_eq!(actual_response.content_type(), MediaType::PlainText);
735737

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

751759
// Test invalid path
752760
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)