I'm trying to create an air-gapped dockerized environment where we can test restoring production backups of databases and application instances. The idea is spin up a PostgreSQL server, restore the database and then spin an instance of the application we have which is configured to connect the restored database. Then we could ensure the application is working as expected.
The problem is that there are some background tasks (and some user actions) that can trigger notifications to the actual end-user (it's a production db after all). This is I was thinking there should be a way to do network isolated tests with docker where the containers can see the services within the docker-compose.yaml, but have no access to the internet.
What would be a cross-platform way to do this? Is there a docker driver that can achieve it or would the user need to do manual interventions?
The way to do this is actually easy, but requires some manual steps. Here are the steps for Linux:
You create a network:
$ docker network create docker_airgap_network
bb7645298697d420dab8eaf1fd4738fccceb8230c783aa93ebe76abec6c40f41
Fetch the created bridge interface name:
$ export IFACE=br-$(docker network inspect docker_airgap_network | jq -r '.[0].Id | .[:12]')
The above will output something like: br-bb7645298697
Verify this interface is actually there:
$ ip -br l | grep -i "$IFACE"
br-bb7645298697 DOWN 01:43:e3:a2:5f:9d <BROADCAST,MULTICAST,DOWN,LOWER_UP>
Add an iptables
rule to drop traffic outside the docker subnet (this would allow communication between all containers; you might want to adjust this just to the docker_airgap_network
subnet if you need to be more precise)
$ iptables -I DOCKER-USER -i $IFACE ! -d 172.0.0.0/8 -j DROP
$ iptables -nvL DOCKER-USER
Chain DOCKER-USER (1 references)
pkts bytes target prot opt in out source destination
48 4032 DROP 0 -- br-bb7645298697 * 0.0.0.0/0 !172.0.0.0/8
406 34104 0 -- * * 0.0.0.0/0 0.0.0.0/0
430 36120 RETURN 0 -- * * 0.0.0.0/0 0.0.0.0/0
Bring up your docker-compose.yml
file to life with docker-compose up
:
version: '3'
services:
service1:
image: alpine
command: /bin/sh
tty: true
networks:
- docker_airgap_network
service2:
image: alpine
command: /bin/sh
tty: true
networks:
- docker_airgap_network
networks:
docker_airgap_network:
external: true
Test
$ docker-compose exec -it service1 ping -c 1 -W 1 google.com
PING google.com (142.250.184.142): 56 data bytes
--- google.com ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
$ docker-compose exec -it service1 ping -c 1 -W 1 service2
PING service2 (172.26.0.2): 56 data bytes
64 bytes from 172.26.0.2: seq=0 ttl=64 time=0.077 ms
--- service2 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.077/0.077/0.077 ms
$ docker-compose exec -it service2 ping -c 1 -W 1 google.com
PING google.com (142.250.184.142): 56 data bytes
--- google.com ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss
$ docker-compose exec -it service2 ping -c 1 -W 1 service1
PING service1 (172.26.0.3): 56 data bytes
64 bytes from 172.26.0.3: seq=0 ttl=64 time=0.054 ms
--- service1 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.054/0.054/0.054 ms