Project 8: An MMDS Feature Extension

The microVM Metadata Service (MMDS) is how a guest learns about itself without trusting the host with a side channel. It is Firecracker's in-VMM answer to the EC2 Instance Metadata Service: the operator PUTs a JSON document over the API socket, configures which guest network interface serves it and on which link-local address, and the guest fetches it over ordinary HTTP from inside the microVM. The twist that makes it interesting engineering is that there is no host-side HTTP server — the request is served by dumbo, a tiny TCP/IP stack that lives inside the VMM, intercepting packets on the configured interface and answering them. MMDS is a small, self-contained, well-bounded subsystem that touches the networking path, the API, a hand-rolled HTTP/TCP stack, and a security-relevant token mechanism — which makes it an unusually good target for a scoped, mergeable feature.

This project asks you to extend MMDS: add a new metadata capability, improve the V2 (token/session) ergonomics, or add diagnostics that make MMDS behavior observable — with tests. The deliverable is a scoped feature plus integration tests, and (because MMDS is a small, evolving, contained subsystem) a realistic path to a real merged PR. It is the gentlest on-ramp in the portfolio to landing a feature — not a tool, not a study — in Firecracker proper.

Note: Read the MMDS metadata service deep dive in full and do the networking masterclass, especially Lab 3: MMDS. This brief assumes you understand the MMDS V1 vs V2 (token/session, IMDSv2-like) distinction, the PUT /mmds/config vs PUT/PATCH/GET /mmds split, that the guest reaches MMDS over HTTP served by the in-VMM dumbo TCP/IP stack (there is no host metadata server), and the link-local address (typically 169.254.169.254) the guest queries. If those are fuzzy, do the lab first — this brief will not re-derive MMDS.


Problem & motivation

MMDS is deliberately minimal, and minimal subsystems leave well-shaped gaps that a careful contributor can fill without threatening the threat model or the device-model philosophy:

  • It's a contained, evolving subsystem. MMDS has changed across releases — V1 was deprecated in favor of the token-based V2, the config surface has been refined, the dumbo stack has been hardened. A subsystem that is small and still moving is exactly where a scoped feature has room to land, unlike the minimal device model where the bar for new surface is deliberately punishing.
  • V2 ergonomics have rough edges. The token/session model (get a token, then present it on every metadata request, with a configurable TTL) is more secure than V1 but more awkward to use and to debug. Clearer error responses, better token-lifecycle diagnostics, or a documented helper flow are real, low-risk improvements.
  • Diagnostics are thin. When MMDS doesn't answer — wrong interface, wrong address, missing token, malformed document, a dumbo packet not being intercepted — the failure is opaque from both the guest and the host side. Observability into the MMDS path (what request arrived, why it was refused, which interface it was served on) is a genuine gap and a naturally mergeable contribution.
  • It's security-relevant but bounded. MMDS sits on the guest-facing network path and the V2 token is a security mechanism, so changes get real scrutiny — but the blast radius is contained to one subsystem, making it tractable to reason about and test fully.

The motivation is the cleanest "ship a feature" story in the portfolio: a self-contained subsystem, an evolving surface with room to add value, and a security relevance that's serious enough to be worth doing well but bounded enough to do completely.


What you'll build

Pick one direction and build it to maintainer quality with tests.

DirectionWhat it isDifficultyMergeability
A. A new metadata capabilityA scoped extension to what MMDS can serve or how — e.g. a new response format/content negotiation, a richer query capability, a documented data-shape constraint, an IMDS-compat behavior FC lacksMedium-HardNeeds an issue/RFC first; scope tightly
B. V2 ergonomics / hardeningClearer V2 error responses, a refined token TTL/lifecycle behavior, tighter request validation, a documented session flow with better failure messagesMediumGood — security-positive, contained
C. Diagnostics / observabilityMMDS-path metrics and logging: requests received, refusals and why (bad token, wrong iface, not found), token issuance/expiry counts, served-interface attributionMediumBest — pure addition, no contract change

Whichever you choose, the artifact is: the feature as a minimum-diff change behind the existing MMDS structure, unit tests for the logic (the dumbo HTTP/token handling), a pytest integration test that drives MMDS from inside a real guest, a CHANGELOG.md entry, and a design note stating what you did and did not change about the request/response contract.

Tip: Direction C (diagnostics) is the recommended starting point. It's a pure addition — no change to the guest-facing request/response contract, no new attack surface, immediate value — which makes it the most likely to land and the best Phase-1 slice. You can always extend toward A or B as a stretch once the diagnostics exist (and they'll help you debug A/B).


Prerequisites


Phased plan

Phase 0 — Build, map MMDS, and drive it from a guest (1–2 days)

Make MMDS concrete: configure it, query it from inside a microVM, then read how the request flows from guest packet → dumbo → the MMDS document.

tools/devtool build --release
# MMDS and the in-VMM TCP/IP stack — read by role, never by line number:
rg -n "Mmds|MmdsVersion|V1|V2|token|session|mmds" src/vmm/src/mmds/
rg -n "dumbo|Tcp|parse_request|http|EthernetFrame|Ipv4|handle" src/vmm/src/dumbo/
# The API surface that configures and populates MMDS:
rg -n "/mmds|mmds-config|MmdsConfig|put_mmds|patch_mmds" src/firecracker/src/api_server/ src/vmm/src/vmm_config/
# How an MMDS request is dispatched on the net device path:
rg -n "mmds|ns|Mmds|is_mmds|169.254" src/vmm/src/devices/virtio/net/

Then drive it end to end. Configure V2, get a token from inside the guest, fetch metadata:

API=/tmp/fc.sock
# Pre-boot: set the document, then point MMDS at an interface as V2 (shapes — verify swagger):
curl -X PUT --unix-socket "$API" --data '{"latest":{"meta-data":{"instance-id":"i-abc"}}}' http://localhost/mmds
curl -X PUT --unix-socket "$API" --data '{"version":"V2","network_interfaces":["net1"],"ipv4_address":"169.254.169.254"}' http://localhost/mmds/config
# ... boot ... then INSIDE the guest (V2 = token first, like IMDSv2):
#   TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" -H "X-metadata-token-ttl-seconds: 21600")
#   curl -s -H "X-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id

Anti-staleness: the MMDS module layout (src/vmm/src/mmds/, src/vmm/src/dumbo/), the exact V2 token header names and TTL semantics, the /mmds/config body shape, and the V1 deprecation state all move between releases. Confirm on your branch with rg/find, read docs/mmds/ (path — verify) and the swagger.yaml for the exact bodies, and check git log --oneline -- src/vmm/src/mmds/ src/vmm/src/dumbo/ and gh issue list --repo firecracker-microvm/firecracker --search "mmds OR metadata" for recent changes and live work you might be duplicating.

Produce capstone-work/mmds-path.md: the trace from a guest HTTP request → the net device recognizing an MMDS-destined packet → dumbo parsing the TCP/HTTP → the token check (V2) → the metadata lookup → the response, with file references found by rg.

Phase 1 — Reproduce the gap (the framing)

Before building, demonstrate the gap your feature fills. This makes the value concrete and gives you the "test that fails without the change."

  • Direction C (diagnostics): show that today, a failed MMDS request (bad token, wrong interface, document not found) is opaque — no host-side signal of why. Capture the current (un-observable) behavior as the baseline.
  • Direction B (V2 ergonomics): show a confusing or unhelpful V2 failure (e.g. a missing or expired token producing an opaque response) that your change clarifies.
  • Direction A (new capability): show the capability is genuinely absent and would be used — and write the issue/RFC comment proposing it before you build, because a new guest-facing capability needs maintainer agreement.

Milestone 1: a written, reproduced statement of the gap, with the current behavior captured from a real guest.

Phase 2 — Build the feature (minimum diff, the MMDS way)

Implement behind the existing MMDS/dumbo structure. Keep the guest-facing contract stable unless your chosen direction is a contract change (and if it is, you've already RFC'd it in Phase 1).

  • Diagnostics (C): add metrics/log points at the MMDS dispatch and refusal sites — request received, refusal reason, token issued/expired, served interface — wired into FC's existing metrics/logging. No request/response change.
  • V2 ergonomics (B): improve the response/validation at the dumbo HTTP layer — clearer status/body on a bad/expired token, tighter request validation — without breaking the IMDSv2-compatible happy path.
  • New capability (A): add the capability behind a clear boundary, defaulting to current behavior so nothing existing changes unless explicitly used.
cargo test -p vmm mmds        # the MMDS/dumbo unit tests — run them, then add yours
cargo clippy --all --all-targets --all-features -- -D warnings

Milestone 2: the feature works end to end from inside a real guest, and the existing MMDS behavior (V1 where still supported, the V2 happy path) is unchanged.

Phase 3 — Tests (the maintainer requirement)

New functionality requires an integration test — this is a hard rule, and MMDS is exactly the kind of guest-facing behavior that needs one. Drive MMDS from inside a real microVM and assert your new behavior.

# The existing MMDS integration tests are your template:
rg -n "mmds|def test_.*mmds|169.254|token" tests/integration_tests/functional/ | head
  • Unit: cargo test for the dumbo HTTP/token logic and any metadata-handling code you touched.
  • Integration (required): a pytest test that configures MMDS, boots, and from the guest exercises your feature — the diagnostic appearing in metrics, the clearer V2 error, or the new capability — and asserts both the new behavior and that the happy path still works.
  • Negative control: the test should fail without your production change.

Phase 4 — Validation and the write-up

  • tools/devtool checkstyle + checkbuild --all output.
  • A CHANGELOG.md entry under ## [Unreleased].
  • A design note: what you changed, what you deliberately left alone (especially the guest-facing request/response contract and the V2 token semantics), and why.

Key code areas

AreaFind it with
MMDS core (versions, token, document)rg -n "Mmds|MmdsVersion|V2|token|session|ttl" src/vmm/src/mmds/
The dumbo in-VMM TCP/IP + HTTP stackrg -n "dumbo|Tcp|parse_request|http|Ipv4|EthernetFrame" src/vmm/src/dumbo/
MMDS dispatch on the net pathrg -n "mmds|is_mmds|ns|169.254|Mmds" src/vmm/src/devices/virtio/net/
The /mmds + /mmds/config APIrg -n "/mmds|MmdsConfig|mmds-config" src/firecracker/src/api_server/ src/firecracker/swagger/firecracker.yaml
MMDS config plumbingrg -n "Mmds|mmds" src/vmm/src/vmm_config/
Metrics/logging hooks (Direction C)rg -n "METRICS|metric|IncMetric|SharedIncMetric|log" src/vmm/src/logger/ src/vmm/src/mmds/
Existing MMDS testsrg -n "mmds|def test_" tests/integration_tests/functional/
Docsls docs/mmds/ (path — verify)

Design considerations & trade-offs

  • The guest-facing contract is near-permanent. The MMDS HTTP surface and the V2 token protocol are things guests depend on; a break is a downstream emergency. Diagnostics (C) are safe because they don't touch it; ergonomics (B) must preserve the IMDSv2-compatible happy path; a new capability (A) must default to current behavior. State explicitly what you kept stable.
  • dumbo is a hand-rolled stack, and it's attack surface. Every byte of the in-VMM TCP/IP and HTTP parser is host code reachable from the guest network. Changes there get security scrutiny — keep them minimal, validate inputs, and don't add parsing surface you don't need. This is the threat model in miniature.
  • V2 is a security mechanism, not just ergonomics. The token/session model exists to prevent SSRF-style abuse (a confused guest process being tricked into fetching metadata). "Improving ergonomics" must not weaken that — a clearer error is good; a looser token check is a regression.
  • Diagnostics must not leak. MMDS can hold sensitive data (it's metadata). A diagnostic that logs request contents or token values is a leak. Log shapes and reasons, not secrets — and say so in the design note.
  • Scope is the whole game here. MMDS is mergeable because it's small. A sprawling rewrite of dumbo or a large new capability loses that advantage. The Phase-1 slice should be the smallest change that delivers real value.
  • IMDS compatibility is a design anchor. MMDS deliberately mirrors EC2 IMDS so guests built for one work with the other. Any new behavior should ask "does this stay IMDS-compatible?" — divergence needs justification.

How to test & validate

  • Correctness from the guest: drive MMDS from inside a real microVM (V2 token flow included) and assert your new behavior end to end. A guest that gets the right metadata through the right path is the proof the dumbo/MMDS plumbing is correct.
  • Regression safety: the existing MMDS happy path (V2 token issue → metadata fetch, and V1 where still supported) must be unchanged — assert it in the same test.
  • Unit: cargo test for the dumbo HTTP/token and metadata-handling logic.
  • Integration (required): a pytest test under tests/integration_tests/functional/ modeled on the existing MMDS tests; it must fail without your change.
  • Security check: if you touched dumbo parsing or V2 tokens, reason about (and test) the malformed-input and bad/expired-token cases — they're the security-relevant paths.

Stretch goals

  • Tie diagnostics to the boot/density harness: MMDS request latency under load is a real serverless concern; measure it.
  • A V2 token-lifecycle test matrix: TTL expiry, token reuse, missing token, wrong-interface requests — turning the security-relevant edges into a thorough test suite (valuable on its own).
  • A guest-side helper or documented flow for the V2 token dance, contributed to the docs — ergonomics that don't touch the protocol.
  • Combine C with B: ship diagnostics first (the safe slice), then use them to drive an evidence-backed V2 ergonomics improvement.

What a strong deliverable looks like

A strong deliverable is a scoped MMDS feature — diagnostics, a V2 ergonomics/hardening improvement, or a new capability — implemented as a minimum diff behind the existing MMDS/dumbo structure, with unit tests and a required pytest integration test driving it from inside a real guest, a CHANGELOG.md entry, and a design note stating exactly what stayed stable about the guest-facing contract and the V2 token semantics.

The upstreaming path is the friendliest in the portfolio for a feature:

  1. Diagnostics are the safest merge. A pure-addition observability change (C) touches no contract and adds no attack surface — the most likely brief in the portfolio to land as written. Find the live state: gh issue list --repo firecracker-microvm/firecracker --search "mmds OR metadata" and read docs/mmds/.
  2. Comment before you code for B and especially A. Anything touching the guest-facing surface or the V2 token semantics is compatibility- and security-sensitive — confirm the maintainers want it and agree on the shape before writing it.
  3. Bring the test. New functionality requires an integration test; a guest-driven test that exercises your feature and proves the happy path still works is exactly what gets an MMDS PR reviewed and merged.

Even if nothing lands upstream, a scoped MMDS feature with tests and a write-up is a portfolio-grade artifact: it demonstrates you can extend a guest-facing, security-relevant subsystem without breaking its contract — the core skill of a Firecracker feature contributor. A finished version at 90+ on the rubric is maintainer-grade networking/metadata work.


This is the last brief in the portfolio. You don't do all eight — you do one or two, well. Whatever you finished, present it the way the portfolio overview demands: lead with the problem and the constraint, show the before/after, show the diff is clean, state what you deliberately didn't do, and make it reproducible.


Next: return to the Capstone Project Portfolio overview to choose (or confirm) your one or two briefs, revisit the evaluation rubric to self-grade, and keep the appendix — the glossary, key types by crate, API endpoint map, and the cheat-sheets — open in a pane while you build.