1
1
#! /bin/sh
2
2
set -e
3
3
4
- if [ " ${CODER_AGENT_URL#* host.docker.internal} " = " $CODER_AGENT_URL " ]; then
5
- # This is likely an external access URL, so we do not need to set up
6
- # port forwarding or DNS resolution for host.docker.internal.
4
+ # Docker-in-Docker setup for Coder dev containers using host.docker.internal
5
+ # URLs. When Docker runs inside a container, the "docker0" bridge interface
6
+ # can interfere with host.docker.internal DNS resolution, breaking
7
+ # connectivity to the Coder server.
7
8
8
- # Start the docker service if it is not running.
9
+ if [ " ${CODER_AGENT_URL#* host.docker.internal} " = " $CODER_AGENT_URL " ]; then
10
+ # External access URL detected, no networking workarounds needed.
9
11
sudo service docker start
10
-
11
12
exit 0
12
13
fi
13
14
14
- # The access URL is host.docker.internal, so we must set up forwarding
15
- # to the host Docker gateway IP address, which is typically 172.17.0.1,
16
- # this will allow the devcontainers to access the Coder server even if
17
- # the access URL has been shadowed by a "docker0" interface. This
18
- # usually happens if docker is started inside a devcontainer .
15
+ # host.docker.internal URL detected. Docker's default bridge network creates
16
+ # a "docker0" interface that can shadow the host.docker.internal hostname
17
+ # resolution. This typically happens when Docker starts inside a devcontainer,
18
+ # as the inner Docker daemon creates its own bridge network that conflicts
19
+ # with the outer one .
19
20
20
- # Enable IP forwarding to allow traffic to flow between the host and
21
- # the devcontainers.
21
+ # Enable IP forwarding to allow packets to route between the host network and
22
+ # the devcontainer networks. Without this, traffic cannot flow properly
23
+ # between the different Docker bridge networks.
22
24
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
23
25
sudo iptables -t nat -A POSTROUTING -j MASQUERADE
24
26
25
- # Get the IP address of the host Docker gateway, which is
26
- # typically 172.17.0.1 and set up port forwarding between this
27
- # workspace's Docker gateway and the host Docker gateway .
27
+ # Set up port forwarding to the host Docker gateway (typically 172.17.0.1).
28
+ # We resolve host.docker.internal to get the actual IP and create NAT rules
29
+ # to forward traffic from this workspace to the host .
28
30
host_ip=$( getent hosts host.docker.internal | awk ' {print $1}' )
31
+
32
+ echo " Host IP for host.docker.internal: $host_ip "
33
+
34
+ # Extract the port from CODER_AGENT_URL. The URL format is typically
35
+ # http://host.docker.internal:port/.
29
36
port=" ${CODER_AGENT_URL##*: } "
30
37
port=" ${port%%/* } "
31
38
case " $port " in
32
39
[0-9]* )
33
- sudo iptables -t nat -A PREROUTING -p tcp --dport $port -j DNAT --to-destination $host_ip :$port
40
+ # Specific port found, forward it to the host gateway.
41
+ sudo iptables -t nat -A PREROUTING -p tcp --dport " $port " -j DNAT --to-destination " $host_ip :$port "
34
42
echo " Forwarded port $port to $host_ip "
35
43
;;
36
44
* )
37
- sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination $host_ip :80
38
- sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination $host_ip :443
45
+ # No specific port or non-numeric port, forward standard web ports.
46
+ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination " $host_ip :80"
47
+ sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j DNAT --to-destination " $host_ip :443"
39
48
echo " Forwarded default ports 80/443 to $host_ip "
40
49
;;
41
50
esac
42
51
43
- # Start the docker service if it is not running, this will create
44
- # the "docker0" interface if it does not exist.
52
+ # Start Docker service, which creates the "docker0" interface if it doesn't
53
+ # exist. We need the interface to extract the second IP address for DNS
54
+ # resolution.
45
55
sudo service docker start
46
56
47
- # Since we cannot define "--add-host" for devcontainers, we define
48
- # a dnsmasq configuration that allows devcontainers to resolve the
49
- # host.docker.internal URL to this workspace, which is typically
50
- # 172.18.0.1. Note that we take the second IP address from
51
- # "hostname -I" because the first one is usually in the range
52
- # 172.17.0.0/16, which is the host Docker bridge.
57
+ # Configure DNS resolution to avoid requiring devcontainer project modifications.
58
+ # While devcontainers can use the "--add-host" flag, it requires explicit
59
+ # definition in devcontainer.json. Using a DNS server instead means every
60
+ # devcontainer project doesn't need to accommodate this.
61
+
62
+ # Wait for the workspace to acquire its Docker bridge IP address. The
63
+ # "hostname -I" command returns multiple IPs: the first is typically the host
64
+ # Docker bridge (172.17.0.0/16 range) and the second is the workspace Docker
65
+ # bridge (172.18.0.0/16). We need the second IP because that's where
66
+ # devcontainers will be able to reach us.
53
67
dns_ip=
54
68
while [ -z " $dns_ip " ]; do
55
69
dns_ip=$( hostname -I | awk ' {print $2}' )
@@ -59,25 +73,29 @@ while [ -z "$dns_ip" ]; do
59
73
fi
60
74
done
61
75
62
- # Create a simple dnsmasq configuration to allow devcontainers to
63
- # resolve host.docker.internal.
76
+ echo " Using DNS IP: $dns_ip "
77
+
78
+ # Install dnsmasq to provide custom DNS resolution. This lightweight DNS
79
+ # server allows us to override specific hostname lookups without affecting
80
+ # other DNS queries.
64
81
sudo apt-get update -y
65
82
sudo apt-get install -y dnsmasq
66
83
84
+ # Configure dnsmasq to resolve host.docker.internal to this workspace's IP.
85
+ # This ensures devcontainers can find the Coder server even when the "docker0"
86
+ # interface would normally shadow the hostname resolution.
67
87
echo " no-hosts" | sudo tee /etc/dnsmasq.conf
68
88
echo " address=/host.docker.internal/$dns_ip " | sudo tee -a /etc/dnsmasq.conf
69
89
echo " resolv-file=/etc/resolv.conf" | sudo tee -a /etc/dnsmasq.conf
70
90
echo " no-dhcp-interface=" | sudo tee -a /etc/dnsmasq.conf
71
91
echo " bind-interfaces" | sudo tee -a /etc/dnsmasq.conf
72
92
echo " listen-address=127.0.0.1,$dns_ip " | sudo tee -a /etc/dnsmasq.conf
73
93
74
- # Restart dnsmasq to apply the new configuration.
75
94
sudo service dnsmasq restart
76
95
77
- # Configure Docker to use the dnsmasq server for DNS resolution.
78
- # This allows devcontainers to resolve host.docker.internal to the
79
- # IP address of this workspace.
96
+ # Configure Docker daemon to use our custom DNS server. This is the critical
97
+ # piece that ensures all containers (including devcontainers) use our dnsmasq
98
+ # server for hostname resolution, allowing them to properly resolve
99
+ # host.docker.internal.
80
100
echo " {\" dns\" : [\" $dns_ip \" ]}" | sudo tee /etc/docker/daemon.json
81
-
82
- # Restart the Docker service to apply the new configuration.
83
101
sudo service docker restart
0 commit comments