server/src/docker/setup_docker_api_only_iptables.sh
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

MACHINE_NAME="$1"
API_ONLY_NETWORK_NAME="$2"
API_IP="$3"

# $network_id is something like br-123456789012. It's the ID of the VM host network interface associated
# with $API_ONLY_NETWORK_NAME. Traffic to and from API-only Docker containers goes through this interface.
network_id=br-$(docker network inspect "$API_ONLY_NETWORK_NAME" --format '{{.ID}} {{.Name}}' \
  | grep "$MACHINE_NAME" \
  | awk '{print substr($1, 1, 12)}')

echo "Network interface for '$API_ONLY_NETWORK_NAME' is: $network_id"

comment="added by setup_docker_api_only_iptables.sh for $MACHINE_NAME"

set +o pipefail

# delete any existing rules added by this script

echo clearing FORWARD

iptables --numeric --line-numbers --list FORWARD | \
    grep "$comment" | \
    sort --numeric-sort --reverse | \
    cut -d' ' -f1 | \
    xargs --verbose --no-run-if-empty --max-args=1 iptables -D FORWARD

echo clearing POSTROUTING

iptables --numeric --line-numbers -t nat --list POSTROUTING | \
    grep "$comment" | \
    sort --numeric-sort --reverse | \
    cut -d' ' -f1 | \
    xargs --verbose --no-run-if-empty --max-args=1 iptables -t nat -D POSTROUTING

set -o pipefail

# Drop all traffic from API-only Docker containers, except...
iptables -I FORWARD -i "$network_id" -j DROP -m comment --comment "$comment"
# Allow traffic from API-only Docker containers to $API_IP.
iptables -I FORWARD -i "$network_id" -d $API_IP -j ACCEPT -m comment --comment "$comment"

# Drop all traffic to API-only Docker containers, except...
iptables -I FORWARD -o "$network_id" -j DROP -m comment --comment "$comment"
# Allow traffic from $API_IP to API-only Docker containers.
iptables -I FORWARD -o "$network_id" -s $API_IP -j ACCEPT -m comment --comment "$comment"

# $subnet is something like 172.25.0.0/16. It's a range of IP addresses that a Docker container connected to
# $API_ONLY_NETWORK_NAME could have.
subnet=$(docker network inspect "$API_ONLY_NETWORK_NAME" --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}')

# If a Docker container with the IP address 172.25.0.2 makes a request to $API_IP:4001 on your developer machine,
# your developer machine will, by default, try to respond to 172.25.0.2. However, your developer machine doesn't
# know about this IP address! 172.25.0.2 is only a valid IP address on the VM host
# Therefore, we use MASQUERADE to replace the source IP address of the request with the VM host's public-facing IP address.
# That way, your developer machine will send responses to the VM host, which will forward them to the Docker container.
iptables -t nat -I POSTROUTING -s $subnet -j MASQUERADE -m comment --comment "$comment"

echo "Done!"
