A packet needs a route and an allowed flow. A public route alone does not open an application port, and an ingress rule alone does not create an internet path. Keep these decisions separate when reading a topology.
Our edge accepts HTTPS on443, the private API accepts8080 from the edge group, and the private database accepts5432 only from the API group. The plan includes IPv4 and IPv6 intentionally. A network diagram that quietly omits one address family can hide a real exposure or connectivity gap.
Security groups are stateful allow-rule collections in AWS: return traffic for an allowed flow is tracked. They do not have a deny-rule ordering like a stateless ACL. The exercise checks only new TCP ingress and unions allow matches; it does not reimplement provider connection tracking.
A source-group rule is different from a network-prefix rule. The former expresses a relationship between attached groups; the latter admits addresses in a range. The fixture group labels are abstract. Do not infer from one passing address probe that every provider route, interface or policy is configured.
from dataclasses import dataclass
import ipaddress
import re
class PlanError(ValueError):
pass
def bounded_text(value, label):
if not isinstance(value,str) or not value.strip() or len(value)>200:
raise PlanError(label)
return value
def integer(value, low, high):
if type(value) is not int or not low<=value<=high:
raise PlanError("integer outside plan bounds")
return value
@dataclass(frozen=True)
class IngressRule:
destination: str
source_kind: str
source: str
port: int
def ingress_allowed(rules, source_group, source_ip, destination, port):
"""TCP ingress subset, union of allow rules. Routing is a separate condition."""
if not isinstance(rules,(list,tuple)) or len(rules)>20:raise PlanError("bounded rules required")
bounded_text(source_group,"source group");bounded_text(destination,"destination");integer(port,1,65535)
try:address=ipaddress.ip_address(source_ip)
except (ValueError,TypeError):raise PlanError("source address") from None
allowed=False
for rule in rules:
if not isinstance(rule,IngressRule):raise PlanError("typed ingress rule required")
bounded_text(rule.destination,"destination");bounded_text(rule.source,"source");integer(rule.port,1,65535)
if rule.source_kind=="group":matches=source_group==rule.source
elif rule.source_kind=="cidr":
try:network=ipaddress.ip_network(rule.source,strict=True)
except (ValueError,TypeError):raise PlanError("canonical network required") from None
matches=address.version==network.version and address in network
else:raise PlanError("allow sources are group or cidr")
if matches and destination==rule.destination and port==rule.port:
allowed=True
return allowed
rules=[IngressRule("api","group","edge",8080)]
print("edge",ingress_allowed(rules,"edge","10.0.1.2","api",8080))
print("internet",ingress_allowed(rules,"internet","203.0.113.1","api",8080))Test both allowed and prohibited flows: internet→edge443, edge→API8080 and API→database5432 are intended; internet→database and edge→database are not. Test a wrong port as well. CIDR membership must respect address-family differences.
Validate every rule before returning a decision. A malformed later rule should not disappear because an earlier rule already matched. The build exercise rejects noncanonical prefixes and unknown source kinds instead of treating unsupported policy as an allow.
import ipaddress
network=ipaddress.ip_network("10.0.0.0/24")
for value in ("10.0.0.2","10.0.1.2"):
print(value, ipaddress.ip_address(value) in network)
print("a route and an allow rule are separate")Implement the ingress subset with positive, negative, wrong-port and IPv6 checks. Keep route availability as a separate architectural assumption. Next define who can act on the resources behind those flows.
Compute survives. Does the whole path?
Choose a placement failure. Two zones hold edge and API replicas, while the database has one zone-a placement. This model checks dependency presence; it does not measure real capacity, routing, failover or cloud availability.
zone-aplacements remain
44380805432zone-bplacements remain
4438080API depends on the database in zone-a.
Placement only. The healthy case is not a deployment or production-reliability claim.
Inspect the eight actual ingress decisions from the local checker
| Source | Destination | Port | Decision |
|---|---|---|---|
| internet | edge | 443 | allow |
| internet · IPv6 | edge | 443 | allow |
| edge | api | 8080 | allow |
| api | database | 5432 | allow |
| internet | api | 8080 | deny |
| internet | database | 5432 | deny |
| edge | database | 5432 | deny |
| edge | api | 22 | deny |