Skip to content

Commit abc6947

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 6a7aacd commit abc6947

File tree

3 files changed

+31
-23
lines changed

3 files changed

+31
-23
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ and this project adheres to
6969
- [#4207](https://github.com/firecracker-microvm/firecracker/issues/4207): Fixed
7070
GSI numbering on aarch64 to correctly allow up to 96 devices being attached
7171
simultaneously.
72+
- [#5290](https://github.com/firecracker-microvm/firecracker/pull/5290): Fixed
73+
MMDS to reject PUT requests containing `X-Forwarded-For` header regardless of
74+
its casing (e.g. `x-forwarded-for`).
7275

7376
## [1.12.0]
7477

src/vmm/src/mmds/mod.rs

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ use crate::logger::{IncMetric, METRICS};
2222
use crate::mmds::data_store::{Mmds, MmdsDatastoreError as MmdsError, MmdsVersion, OutputFormat};
2323
use crate::mmds::token::PATH_TO_TOKEN;
2424
use crate::mmds::token_headers::{
25-
REJECTED_HEADER, X_AWS_EC2_METADATA_TOKEN_HEADER, X_AWS_EC2_METADATA_TOKEN_SSL_SECONDS_HEADER,
26-
X_METADATA_TOKEN_HEADER, X_METADATA_TOKEN_TTL_SECONDS_HEADER, get_header_value_pair,
25+
X_AWS_EC2_METADATA_TOKEN_HEADER, X_AWS_EC2_METADATA_TOKEN_SSL_SECONDS_HEADER,
26+
X_FORWARDED_FOR_HEADER, X_METADATA_TOKEN_HEADER, X_METADATA_TOKEN_TTL_SECONDS_HEADER,
27+
get_header_value_pair,
2728
};
2829

2930
#[rustfmt::skip]
@@ -242,11 +243,10 @@ fn respond_to_put_request(mmds: &mut Mmds, request: Request) -> Response {
242243
let custom_headers = request.headers.custom_entries();
243244

244245
// Reject `PUT` requests that contain `X-Forwarded-For` header.
245-
if custom_headers.contains_key(REJECTED_HEADER) {
246-
let error_msg = RequestError::HeaderError(HttpHeaderError::UnsupportedName(
247-
REJECTED_HEADER.to_string(),
248-
))
249-
.to_string();
246+
if let Some((header, _)) = get_header_value_pair(custom_headers, &[X_FORWARDED_FOR_HEADER]) {
247+
let error_msg =
248+
RequestError::HeaderError(HttpHeaderError::UnsupportedName(header.to_string()))
249+
.to_string();
250250
return build_response(
251251
request.http_version(),
252252
StatusCode::BadRequest,
@@ -754,19 +754,25 @@ mod tests {
754754
assert_eq!(actual_response.content_type(), MediaType::PlainText);
755755

756756
// Test unsupported `X-Forwarded-For` header
757-
let request = Request::try_from(
758-
b"PUT http://169.254.169.254/latest/api/token HTTP/1.0\r\n\
759-
X-Forwarded-For: 203.0.113.195\r\n\r\n",
760-
None,
761-
)
762-
.unwrap();
763-
let mut expected_response = Response::new(Version::Http10, StatusCode::BadRequest);
764-
expected_response.set_content_type(MediaType::PlainText);
765-
expected_response.set_body(Body::new(
766-
"Invalid header. Reason: Unsupported header name. Key: X-Forwarded-For".to_string(),
767-
));
768-
let actual_response = convert_to_response(mmds.clone(), request);
769-
assert_eq!(actual_response, expected_response);
757+
for header in ["X-Forwarded-For", "x-forwarded-for", "X-fOrWaRdEd-FoR"] {
758+
#[rustfmt::skip]
759+
let request = Request::try_from(
760+
format!(
761+
"PUT http://169.254.169.254/latest/api/token HTTP/1.0\r\n\
762+
{header}: 203.0.113.195\r\n\r\n"
763+
)
764+
.as_bytes(),
765+
None,
766+
)
767+
.unwrap();
768+
let mut expected_response = Response::new(Version::Http10, StatusCode::BadRequest);
769+
expected_response.set_content_type(MediaType::PlainText);
770+
expected_response.set_body(Body::new(format!(
771+
"Invalid header. Reason: Unsupported header name. Key: {header}"
772+
)));
773+
let actual_response = convert_to_response(mmds.clone(), request);
774+
assert_eq!(actual_response, expected_response);
775+
}
770776

771777
// Test invalid path
772778
let request = Request::try_from(

src/vmm/src/mmds/token_headers.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33

44
use std::collections::HashMap;
55

6-
/// Header rejected by MMDS.
7-
pub const REJECTED_HEADER: &str = "X-Forwarded-For";
8-
6+
// `X-Forwarded-For`
7+
pub(crate) const X_FORWARDED_FOR_HEADER: &str = "x-forwarded-for";
98
// `X-metadata-token`
109
pub(crate) const X_METADATA_TOKEN_HEADER: &str = "x-metadata-token";
1110
// `X-aws-ec2-metadata-token`

0 commit comments

Comments
 (0)