Chapter 3 — Network Architecture
3.1 VPC Design
Two VPC networks, one per environment, both in NYC2:
| VPC | Environment | Members | Peering |
|---|---|---|---|
prod-vpc-nyc2 |
Prod | sbsdash-server-prod, monitor-servers, sbs-wiki (post NYC2 rebuild) |
None |
dev-vpc-nyc2 |
Dev | Dev-SBS-server |
None |
Design constraints, stated for accuracy against DigitalOcean's VPC model:
- A DigitalOcean VPC is a single flat private network per region — there is no user-defined sub-subnetting within a VPC, no route tables, and no security groups in the AWS sense. Segmentation inside the VPC is therefore enforced by Cloud Firewalls (tag-scoped) plus host firewalls (§3.5, §3.6), not by subnet boundaries.
- VPCs are region-scoped. This is the technical basis for the
sbs-wikiNYC2 rebuild requirement (§2.3): a NYC1 droplet cannot be a member ofprod-vpc-nyc2. - No VPC peering between Dev and Prod — the environments have no private network path. Promotion of artifacts occurs through the IaC/CI workflow (Chapter 10) via the GitHub repository, never via direct network transfer.
3.2 Address Plan
| Network | CIDR | Notes |
|---|---|---|
prod-vpc-nyc2 |
10.10.0.0/20 |
Assigned at VPC creation; DigitalOcean allocates member addresses from this range automatically |
dev-vpc-nyc2 |
10.20.0.0/20 |
Deliberately non-overlapping with Prod to keep operator tooling, monitoring configs, and logs unambiguous |
Reserved documentation convention: Prod private IPs are recorded in the Build Register per droplet at VPC enrollment; the authoritative address inventory lives in the IaC state (Chapter 10), not in this document, to avoid drift.
3.3 Internal Routing and Service Communication
All service-to-service traffic between Prod droplets transits the VPC private interface (eth1), never public IPs. Enforced expectations:
- Application → data services — the application stack on
sbsdash-server-prodreaches PostgreSQL, MongoDB, and Redis exclusively on VPC-private addresses. Data services bind to the private interface only; they have no listener on the public interface (verified at gate V-series network checks, LH-SBS-INST-001). - Prometheus scrape paths —
monitor-serversscrapes exporters onsbsdash-server-prodandsbs-wikiover the VPC. Exporter ports (9100 node_exporter, application exporters) are admitted only from themonitor-serversprivate IP via tag-scoped firewall rule — they are unreachable from the public interface and from other VPC members. - Wiki isolation — BookStack on
sbs-wikioriginates no connections to the application or data tier. Its only VPC-internal traffic is inbound scrape frommonitor-servers. This reflects its role: operational surface, no tenant data (§1.2). - DNS resolution — droplets use systemd-resolved against DigitalOcean resolvers; internal service addressing uses IaC-managed
/etc/hostsentries (small fixed fleet) rather than a private DNS zone. Revisit if the fleet grows past ~10 hosts.
Inter-Compartment note: Compartment isolation at the network layer (one of the eight isolation layers) is realized within sbsdash-server-prod at the Docker Compose network level — each Compartment's containers join a dedicated compose network; no two Compartment networks share a bridge. Cross-Compartment traffic is denied by Docker network non-membership, consistent with §2.2.1 of the platform architecture. Container-level detail is specified in Chapter 6.
3.4 Egress Control
DigitalOcean provides no managed NAT/egress gateway; egress control is enforced at the droplet firewall layer:
- Default-deny outbound is not practical at the cloud firewall (Cloud Firewalls support outbound rules but a strict allowlist breaks OS packaging and ACME). Policy instead: restricted-egress allowlist at the host firewall (nftables) on each Prod droplet, permitting:
- TCP 443 to Anthropic API endpoints (platform AI traffic)
- TCP 443 to GitHub (deploy pulls; pinned to GitHub's published IP ranges, refreshed by IaC)
- TCP 443 to OS package mirrors and container registries enumerated in Chapter 11 (supply-chain allowlist)
- TCP/UDP 123 (NTP) to configured pool
- TCP 443 to Let's Encrypt ACME endpoints (TLS issuance, Chapter 5)
- DNS (53) to DigitalOcean resolvers only
- All other outbound denied and logged — egress-deny log lines ship to the observability pipeline (Chapter 12) and are an anomaly-detection input under MON-02. Unexpected egress attempts from a Compartment container are a primary indicator for AI-02 (tool allowlisting) and LLM01 (prompt-injection attempting exfiltration) violations.
- Compartment containers inherit a narrower egress set than the host: only the Anthropic API and explicitly allowlisted per-Compartment integration endpoints. Enforced via per-compose-network nftables chains; the allowlist is IaC-managed and change-controlled.
3.5 Cloud Firewall Rules (DigitalOcean Cloud Firewalls, tag-scoped)
Three firewalls, attached by droplet tag:
FW prod-app → tag prod (sbsdash-server-prod)
| Dir | Proto/Port | Source / Destination | Purpose |
|---|---|---|---|
| In | TCP 443 | 0.0.0.0/0, ::/0 | Tenant surfaces (sbsdash.com, admin., clients.) via reverse proxy |
| In | TCP 80 | 0.0.0.0/0, ::/0 | ACME HTTP-01 + 301 redirect to 443 only |
| In | TCP 22 | Admin allowlist IPs (Build Register) | SSH, key-only |
| In | TCP 9100, 9x xx (exporters) | monitor-servers private IP |
Prometheus scrape |
| Out | Per §3.4 | — | Restricted egress |
FW prod-monitor → tag grafana (monitor-servers)
| Dir | Proto/Port | Source | Purpose |
|---|---|---|---|
| In | TCP 443 | 0.0.0.0/0 | monitor.sbsdash.com (Grafana behind IdP auth, Chapter 9) |
| In | TCP 80 | 0.0.0.0/0 | ACME + redirect |
| In | TCP 22 | Admin allowlist | SSH |
| — | — | — | Prometheus (9090) and Alertmanager (9093) bind VPC-private only — no public rule exists |
FW prod-wiki → tag wiki (sbs-wiki)
| Dir | Proto/Port | Source | Purpose |
|---|---|---|---|
| In | TCP 443 | 0.0.0.0/0 | wiki.sbsdash.com (BookStack behind SSO) |
| In | TCP 80 | 0.0.0.0/0 | ACME + redirect |
| In | TCP 22 | Admin allowlist | SSH |
| In | TCP 9100 | monitor-servers private IP |
node_exporter scrape |
FW dev-app → tag develop mirrors prod-app with SSH open to the same admin allowlist and no public 443 requirement (Dev has no production DNS); Dev web surfaces admit only admin allowlist IPs.
Rule hygiene: firewalls are IaC-defined (Chapter 10); console edits to firewall rules are treated as drift and reverted. The admin SSH allowlist is a named variable in IaC, reviewed at each IAM-06 access review.
3.6 Host Firewall (nftables)
Cloud Firewalls filter at the platform edge; nftables on each droplet provides defense-in-depth and the egress allowlist (§3.4), which Cloud Firewalls alone cannot express per-container:
- Base policy: inbound default-deny (mirror of the cloud firewall admit set), outbound default-deny with the §3.4 allowlist, established/related admitted.
- Per-Compartment chains: one nftables chain per Compartment compose network implementing that Compartment's egress set.
- Rulesets are rendered from IaC templates and versioned; live ruleset hash is a monitored value (MON-01 input) so tampering surfaces as drift.
3.7 Verification
Network controls in this chapter map to gates in LH-SBS-INST-001: public-listener scan (no data-service or Prometheus ports on public interfaces), Dev↔Prod isolation probe (no private path), egress-allowlist test (denied domain attempt logged), and per-Compartment cross-network denial test. INF-01 is the controlling SEC-001 requirement throughout.