Skip to content

Commit 549502e

Browse files
committed
feat(mmds): Support token generation via PUT in MMDS v1
To enable smoother migration from v1 to v2, make MMDS v1 support PUT request to /latest/api/token for token generation. We do NOT make MMDS v1 deny GET requests even with invalid tokens not to break existing workloads. It does not validate a given toekn at this point, but it will validate to increment a metric that will be added to count such requests having invalid tokens. Signed-off-by: Takahiro Itazuri <[email protected]>
1 parent 80b5b61 commit 549502e

File tree

11 files changed

+128
-163
lines changed

11 files changed

+128
-163
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ and this project adheres to
3131
- [#5290](https://github.com/firecracker-microvm/firecracker/pull/5290): Changed
3232
MMDS to validate the value of "X-metadata-token-ttl-seconds" header only if it
3333
is a PUT request to /latest/api/token, as in EC2 IMDS.
34+
- [#5290](https://github.com/firecracker-microvm/firecracker/pull/5290): Changed
35+
MMDS version 1 to support the session oriented method as in version 2,
36+
allowing easier migration to version 2. Note that MMDS version 1 accepts a GET
37+
request even with no token or an invalid token so that existing workloads
38+
continue to work.
3439

3540
### Deprecated
3641

docs/mmds/mmds-user-guide.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,13 @@ must be issued. The requested resource can be referenced by its corresponding
231231
the MMDS request. The HTTP response content will contain the referenced metadata
232232
resource.
233233

234-
The only HTTP method supported by MMDS version 1 is `GET`. Requests containing
235-
any other HTTP method will receive **405 Method Not Allowed** error.
234+
As in version 2, version 1 also supports a session oriented method in order to
235+
make the migration easier. See [the next section](#version-2) for the session
236+
oriented method. Note that version 1 returns a successful response to a `GET`
237+
request even with an invalid token or no token not to break existing workloads.
238+
239+
Requests containing any other HTTP methods than `GET` and `PUT` will receive
240+
**405 Method Not Allowed** error.
236241

237242
```bash
238243
MMDS_IPV4_ADDR=169.254.170.2

src/vmm/src/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,7 @@ pub(crate) mod tests {
987987
net_builder.build(net_config).unwrap();
988988
let net = net_builder.iter().next().unwrap();
989989
let mut mmds = Mmds::default();
990-
mmds.set_version(mmds_version).unwrap();
990+
mmds.set_version(mmds_version);
991991
net.lock().unwrap().configure_mmds_network_stack(
992992
MmdsNetworkStack::default_ipv4_addr(),
993993
Arc::new(Mutex::new(mmds)),

src/vmm/src/device_manager/persist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ impl<'a> Persist<'a> for MMIODeviceManager {
569569
// If there's at least one network device having an mmds_ns, it means
570570
// that we are restoring from a version that did not persist the `MmdsVersionState`.
571571
// Init with the default.
572-
constructor_args.vm_resources.mmds_or_default();
572+
constructor_args.vm_resources.mmds_or_default()?;
573573
}
574574

575575
for net_state in &state.net_devices {

src/vmm/src/mmds/data_store.rs

Lines changed: 20 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ use crate::mmds::token::{MmdsTokenError as TokenError, TokenAuthority};
1212
/// The Mmds is the Microvm Metadata Service represented as an untyped json.
1313
#[derive(Debug)]
1414
pub struct Mmds {
15+
version: MmdsVersion,
1516
data_store: Value,
16-
// None when MMDS V1 is configured, Some for MMDS V2.
17-
token_authority: Option<TokenAuthority>,
17+
token_authority: TokenAuthority,
1818
is_initialized: bool,
1919
data_store_limit: usize,
2020
}
@@ -65,19 +65,20 @@ pub enum MmdsDatastoreError {
6565
// Used for ease of use in tests.
6666
impl Default for Mmds {
6767
fn default() -> Self {
68-
Self::default_with_limit(51200)
68+
Self::try_new(51200).unwrap()
6969
}
7070
}
7171

7272
impl Mmds {
7373
/// MMDS default instance with limit `data_store_limit`
74-
pub fn default_with_limit(data_store_limit: usize) -> Self {
75-
Mmds {
74+
pub fn try_new(data_store_limit: usize) -> Result<Self, MmdsDatastoreError> {
75+
Ok(Mmds {
76+
version: MmdsVersion::V1,
7677
data_store: Value::default(),
77-
token_authority: None,
78+
token_authority: TokenAuthority::try_new()?,
7879
is_initialized: false,
7980
data_store_limit,
80-
}
81+
})
8182
}
8283

8384
/// This method is needed to check if data store is initialized.
@@ -92,52 +93,29 @@ impl Mmds {
9293
}
9394

9495
/// Set the MMDS version.
95-
pub fn set_version(&mut self, version: MmdsVersion) -> Result<(), MmdsDatastoreError> {
96-
match version {
97-
MmdsVersion::V1 => {
98-
self.token_authority = None;
99-
Ok(())
100-
}
101-
MmdsVersion::V2 => {
102-
if self.token_authority.is_none() {
103-
self.token_authority = Some(TokenAuthority::try_new()?);
104-
}
105-
Ok(())
106-
}
107-
}
96+
pub fn set_version(&mut self, version: MmdsVersion) {
97+
self.version = version;
10898
}
10999

110-
/// Return the MMDS version by checking the token authority field.
100+
/// Get the MMDS version.
111101
pub fn version(&self) -> MmdsVersion {
112-
if self.token_authority.is_none() {
113-
MmdsVersion::V1
114-
} else {
115-
MmdsVersion::V2
116-
}
102+
self.version
117103
}
118104

119105
/// Sets the Additional Authenticated Data to be used for encryption and
120-
/// decryption of the session token when MMDS version 2 is enabled.
106+
/// decryption of the session token.
121107
pub fn set_aad(&mut self, instance_id: &str) {
122-
if let Some(ta) = self.token_authority.as_mut() {
123-
ta.set_aad(instance_id);
124-
}
108+
self.token_authority.set_aad(instance_id);
125109
}
126110

127111
/// Checks if the provided token has not expired.
128-
pub fn is_valid_token(&self, token: &str) -> Result<bool, TokenError> {
129-
self.token_authority
130-
.as_ref()
131-
.ok_or(TokenError::InvalidState)
132-
.map(|ta| ta.is_valid(token))
112+
pub fn is_valid_token(&self, token: &str) -> bool {
113+
self.token_authority.is_valid(token)
133114
}
134115

135116
/// Generate a new Mmds token using the token authority.
136117
pub fn generate_token(&mut self, ttl_seconds: u32) -> Result<String, TokenError> {
137-
self.token_authority
138-
.as_mut()
139-
.ok_or(TokenError::InvalidState)
140-
.and_then(|ta| ta.generate_token_secret(ttl_seconds))
118+
self.token_authority.generate_token_secret(ttl_seconds)
141119
}
142120

143121
/// set MMDS data store limit to `data_store_limit`
@@ -304,11 +282,11 @@ mod tests {
304282
assert_eq!(mmds.version(), MmdsVersion::V1);
305283

306284
// Test setting MMDS version to v2.
307-
mmds.set_version(MmdsVersion::V2).unwrap();
285+
mmds.set_version(MmdsVersion::V2);
308286
assert_eq!(mmds.version(), MmdsVersion::V2);
309287

310-
// Test setting MMDS version back to default.
311-
mmds.set_version(MmdsVersion::V1).unwrap();
288+
// Test setting MMDS version back to v1.
289+
mmds.set_version(MmdsVersion::V1);
312290
assert_eq!(mmds.version(), MmdsVersion::V1);
313291
}
314292

@@ -593,37 +571,4 @@ mod tests {
593571

594572
assert_eq!(mmds.get_data_str().len(), 2);
595573
}
596-
597-
#[test]
598-
fn test_is_valid() {
599-
let mut mmds = Mmds::default();
600-
// Set MMDS version to V2.
601-
mmds.set_version(MmdsVersion::V2).unwrap();
602-
assert_eq!(mmds.version(), MmdsVersion::V2);
603-
604-
assert!(!mmds.is_valid_token("aaa").unwrap());
605-
606-
mmds.token_authority = None;
607-
assert_eq!(
608-
mmds.is_valid_token("aaa").unwrap_err().to_string(),
609-
TokenError::InvalidState.to_string()
610-
)
611-
}
612-
613-
#[test]
614-
fn test_generate_token() {
615-
let mut mmds = Mmds::default();
616-
// Set MMDS version to V2.
617-
mmds.set_version(MmdsVersion::V2).unwrap();
618-
assert_eq!(mmds.version(), MmdsVersion::V2);
619-
620-
let token = mmds.generate_token(1).unwrap();
621-
assert!(mmds.is_valid_token(&token).unwrap());
622-
623-
mmds.token_authority = None;
624-
assert_eq!(
625-
mmds.generate_token(1).err().unwrap().to_string(),
626-
TokenError::InvalidState.to_string()
627-
);
628-
}
629574
}

0 commit comments

Comments
 (0)