EC2

Amazon EC2 — the full 769-operation control plane. VPCs, subnets, security groups, instances, EBS, AMIs, transit gateways, VPN, IPAM, Verified Access, and the entire networking long tail at 100% Smithy conformance.

fakecloud implements 769 of 769 AWS EC2 operations at 100% Smithy conformance — the complete control plane for the largest service surface in AWS. Request/response shapes, flattened ec2Query XML lists, field names, enum validation, and integer/length bounds are checked against AWS's own Smithy model on every commit.

Supported features

  • Core networking — VPCs (+ secondary CIDRs, tenancy), DHCP option sets, subnets (+ CIDR reservations), security groups (rules, references, VPC associations), route tables, internet / egress-only / NAT gateways, and elastic IPs with transfer/move flows.
  • ComputeRunInstances and the full instance lifecycle (start/stop/reboot/terminate/monitor), instance attributes, credit specifications, metadata + maintenance options, instance types, and topology. Key pairs and placement groups. Instances are backed by real Docker/Podman containersRunInstances boots a container per instance, runs your user-data at boot, and maps start/stop/reboot/terminate onto the container lifecycle (falling back to a metadata-only control plane when no container runtime is present).
  • Storage — EBS volumes (+ modifications, recycle bin), snapshots (+ copy, tier, lock, fast restores, block-public-access), AMIs (register/copy/deprecate/deregistration-protection), and EBS encryption defaults.
  • Seeded public AMI catalogue — every account starts with a small catalogue of public Amazon-owned (Amazon Linux 2, Amazon Linux 2023 x86_64/arm64, Windows Server) and Canonical-owned (Ubuntu 22.04/24.04) AMIs, just like a real account. DescribeImages honours the Owner param (amazon / aws-marketplace / self / account id) and name filters with * wildcards, so the standard Terraform data "aws_ami" pattern (most_recent = true, owners = ["amazon"], a name glob) resolves to a real image instead of returning empty — and anything that chains off it (an aws_instance, an ELBv2 target-group attachment) can be planned.
  • Interfaces — elastic network interfaces with attachments, permissions, IPv4/IPv6 address assignment, and prefix lists.
  • Edge / advanced networking — network ACLs, VPC peering, VPC endpoints + PrivateLink (services, connection notifications), flow logs, launch templates (+ versions), spot requests / fleets / EC2 fleets, capacity reservations (+ fleets), reserved instances, dedicated hosts.
  • Transit Gateway — the complete 74-op surface: gateways, attachments, route tables (+ associations / propagations / prefix-list refs), peering, Connect + Connect peers, policy tables, route-table announcements, multicast domains (+ group members/sources), metering policies, and Client-VPN attachments.
  • Site-to-Site & Client VPN — customer gateways, virtual private gateways, VPN connections (+ routes / tunnels / device configs), VPN concentrators, and full Client VPN (endpoints, routes, authorization rules, target networks, connections, certificate/config export-import).
  • IPAM — IPAMs, scopes, pools, pool CIDRs + allocations, resource CIDRs, address history, resource discovery (+ associations, discovered getters), BYOASN, BYOIP-to-IPAM, external resource verification tokens, policies (+ allocation rules / org targets), and prefix-list resolvers (+ targets / rules / versions).
  • Verified Access — instances, trust providers (+ attach/detach), groups, endpoints, policies (group + endpoint), logging configuration, and client-config export.
  • Network Insights — reachability paths + analyses and access scopes + scope analyses (content, findings).
  • Outpost / hybrid — carrier gateways, CoIP pools + CIDRs, local-gateway route tables, routes, VPC + virtual-interface-group associations, virtual interfaces, and groups.
  • Access & diagnostics — EC2 Instance Connect endpoints, fast launch, serial-console access, console output / screenshot, and password data. GetConsoleOutput returns the instance container's real console log (its combined stdout/stderr, including anything user-data printed at boot), base64-encoded as on AWS.
  • Cross-cutting — tag specifications on create, CreateTags / DeleteTags / DescribeTags, Filter.N filtering, and MaxResults / NextToken pagination across every Describe*.

Protocol

EC2 uses the ec2Query protocol: form-encoded requests and flattened-XML responses (no <member> wrapper, lower-camel element names, lowercase <requestId>, no <Result> envelope). Because EC2 declares no per-operation error shapes, fakecloud is lenient on not-found and validates only wire-observable negatives — missing required scalars, invalid enum values, out-of-range integers / MaxResults, and bad lengths.

Introspection

  • GET /_fakecloud/ec2/instances — list every fakecloud-managed EC2 instance across all account partitions with its control-plane metadata (instanceId, imageId, instanceType, state, privateIp, publicIp, subnetId, vpcId, keyName, securityGroupIds, availabilityZone, launchTime) plus containerId — the backing Docker container id or Kubernetes Pod name, or null when the instance runs metadata-only (no container runtime). Lets tests assert on instance state (and which container backs it) without re-parsing DescribeInstances XML. Exposed through every fakecloud introspection SDK — fc.ec2().getInstances() (Go/Java/TS/PHP), fc.ec2.get_instances() (Python), fc.ec2().get_instances() (Rust).
  • GET /_fakecloud/ec2/instance-networks — inspect each instance's real backing network: the Docker/Podman network (fakecloud-subnet-<id>) or k8s NetworkPolicy (fakecloud-ec2-<id>) that backs it, its container IP, the isolationBackend (docker/podman/kubernetes/none), the securityGroupEnforcement mechanism (nftables/networkpolicy/disabled), and whether enforcement is currently enforcementActive. A debugging aid for "why can't X reach Y". Exposed through every introspection SDK — fc.ec2().getInstanceNetworks() (Go/Java/TS/PHP), fc.ec2.get_instance_networks() (Python), fc.ec2().get_instance_networks() (Rust).

Instance metadata service (IMDS)

fakecloud serves the EC2 instance metadata service on /latest/* so an app that resolves credentials through IMDS (rather than static keys) runs unmodified. Point the SDK's IMDS client at fakecloud with AWS_EC2_METADATA_SERVICE_ENDPOINT=http://<host>:<port>/ and the default credential chain resolves locally with no code change.

Both IMDSv1 (plain GET) and IMDSv2 (PUT /latest/api/token then GET with X-aws-ec2-metadata-token) are supported; fakecloud hands out a token but does not enforce it. Served paths: meta-data/iam/security-credentials/ (role name) and meta-data/iam/security-credentials/{role} (credential JSON, from the same registered-credential cache as /_fakecloud/credentials, so it verifies under --verify-sigv4), meta-data/iam/info, meta-data/instance-id, meta-data/placement/region, meta-data/placement/availability-zone, and dynamic/instance-identity/document. The reported instance ID is set with --imds-instance-id. See the Run an app unmodified guide.

Network isolation

VPC/subnet/security-group/NACL metadata isn't just stored — fakecloud gives instances real network isolation backed by the container runtime:

  • Default VPC — every account+region ships a default VPC (172.31.0.0/16) with an internet gateway, a main route table, one default subnet per AZ, a default security group, and a default NACL, exactly like AWS. RunInstances with no SubnetId lands in the default subnet and attaches the default security group.

  • L3 isolation (Docker/Podman) — each subnet gets its own daemon network (fakecloud-subnet-<id>); instances in the same subnet share a bridge and can talk, while instances in different VPCs/subnets land on different bridges and cannot route to each other. Private subnets (no 0.0.0.0/0 → igw route) back onto --internal networks with no NAT to the host.

  • Security-group + NACL enforcement (Docker/Podman) — when enabled, security-group and NACL rules are translated into an nftables ruleset applied on the host, so SG rules actually block/allow traffic. This needs CAP_NET_ADMIN + nft, so it is opt-in via FAKECLOUD_EC2_SG_ENFORCEMENT=1 and degrades gracefully: without the capability (CI, Docker Desktop, rootless podman) the rules are tracked but not enforced, with a one-time startup warning — L3 isolation still holds. The published image ships nft (plus kmod/procps, which it needs to load and enable bridge netfilter so same-subnet traffic is actually filtered); you only need to grant the capability and set the env var. With docker run:

    docker run -e FAKECLOUD_EC2_SG_ENFORCEMENT=1 --cap-add=NET_ADMIN \
      -p 4566:4566 -v /var/run/docker.sock:/var/run/docker.sock \
      ghcr.io/faiscadev/fakecloud:latest

    or in docker-compose.yml, uncomment the cap_add: [NET_ADMIN] and FAKECLOUD_EC2_SG_ENFORCEMENT lines in the shipped compose. Confirm it took effect via GET /_fakecloud/ec2/instance-networkssecurityGroupEnforcement reads nftables and enforcementActive is true when active.

  • Kubernetes — on the k8s backend, isolation is expressed as NetworkPolicy objects (one per instance, derived from its security groups) and enforced by the cluster CNI. fakecloud detects the CNI (Calico/Cilium enforce; kindnet does not) and warns when NetworkPolicy won't be enforced, but always creates the policies.

  • Compose interop — to let your own containers reach an instance by its private IP, attach them to the instance's subnet network: docker network connect fakecloud-subnet-<id> <your-container> (find <id> via /_fakecloud/ec2/instance-networks). Cross-VPC traffic stays isolated by design.

Known limitations

  • Instances run as containers, not VMsRunInstances boots a real container per instance (Amazon Linux by default, overridable via FAKECLOUD_EC2_DEFAULT_IMAGE) and runs user-data at boot, but it is a container, not a full virtual machine: no kernel modules, no nested virtualization, and the instance type / EBS sizing is metadata only. The container runs as a local Docker/Podman container by default, or as a native Kubernetes Pod when FAKECLOUD_EC2_BACKEND=k8s (or the global FAKECLOUD_CONTAINER_BACKEND=k8s) is set — mirroring the Lambda/ECS/RDS/ElastiCache backends, so fakecloud running inside Kubernetes needs no Docker daemon. On the k8s backend a stopped instance's Pod is deleted and recreated on start (instances are not persistent disks). When no container runtime is available at all, the control plane degrades to a metadata-only instance so every API call still succeeds.
  • A handful of model operations are absent from the vendored AWS SDK (DescribeIpamPoolAllocations, ModifyIpamPoolAllocation, the capacity-reservation cancellation-quote pair, and the AttachImageWatermark/DetachImageWatermark AMI watermark pair). They are implemented and conformance-probed via raw ec2Query, and graduate to typed SDK calls on the next SDK refresh.
  • Security-group / NACL packet filtering needs host privileges. Real enforcement (above) requires CAP_NET_ADMIN + nft on Docker/Podman, or a NetworkPolicy-enforcing CNI on Kubernetes. Where neither is available the rules are stored and returned faithfully but tracked-only; L3 (per-subnet/VPC) isolation still applies. ICMP and IPv6 rules are modeled best-effort. On Docker/Podman, fakecloud enables bridge netfilter (net.bridge.bridge-nf-call-iptables) so same-subnet traffic is actually filtered.
  • Enforced security-group source matching uses real container IPs, not the AWS address space. A backing container has a Docker/Podman bridge IP, not an address in the VPC's CIDR. So an enforced ingress rule keyed on a specific AWS CIDR (e.g. 10.0.0.0/8) won't match a peer instance's real source IP — only 0.0.0.0/0 (anywhere) and referenced security groups (which resolve to member instances' real IPs, like the default group's allow-from-self) match as intended. Metadata (DescribeSecurityGroups) is unaffected; this only concerns the optional packet-filtering path.