Skip to main content

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-wiki NYC2 rebuild requirement (§2.3): a NYC1 droplet cannot be a member of prod-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-prod reaches 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 pathsmonitor-servers scrapes exporters on sbsdash-server-prod and sbs-wiki over the VPC. Exporter ports (9100 node_exporter, application exporters) are admitted only from the monitor-servers private IP via tag-scoped firewall rule — they are unreachable from the public interface and from other VPC members.
  • Wiki isolation — BookStack on sbs-wiki originates no connections to the application or data tier. Its only VPC-internal traffic is inbound scrape from monitor-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/hosts entries (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.