#!/usr/bin/env bash # # JbTecWiz Support Centre -- generated fix script # # Fault : Docker breaks the host network, or containers cannot resolve names # Fix : Move Docker off the conflicting range # Source: https://jbtecwiz.com/support/lnx-ctr-network # # Run as : Root shell # Expect : 30 minutes # Risk : medium # Reversible : yes # # WHEN THIS IS THE RIGHT FIX # A subnet collision. Confirm it first -- the symptom is specific. # # HOW TO UNDO IT # Remove /etc/docker/daemon.json and restart Docker to return to the # defaults. # # Walks the fix one step at a time and asks before each. Steps with no # command are yours to do -- it prints those and waits. DRYRUN=1 prints # without executing; UNATTENDED=1 does not ask. # # -------------------------------------------------------------------- # NO WARRANTY - USE AT YOUR OWN RISK # # This script is provided by JbTecWiz as-is and with no warranty of any # kind, express or implied. You run it entirely at your own risk. # # JbTecWiz accepts no liability for any loss or damage arising from its # use, including but not limited to data loss, downtime, or configuration # changes that turn out to be wrong for your system. # # You are responsible for reading this script before running it, for # satisfying yourself that it suits the machine in front of you, and for # having a working backup first. Some steps cannot be undone. # -------------------------------------------------------------------- set -uo pipefail DRYRUN="${DRYRUN:-0}" UNATTENDED="${UNATTENDED:-0}" failed=0 if [ "$(id -u)" -ne 0 ]; then echo " This fix is documented as needing root. Re-run with sudo." >&2 exit 3 fi rule() { printf "\n%s\n" "$(printf '-%.0s' $(seq 1 70))"; if [ $# -gt 0 ]; then echo "$1"; fi; } prose() { echo "$1" | fold -s -w 74 | sed "s/^/ /"; } # Returns 0 when the caller should run the command, 1 when it should not. # A manual step always returns 1 -- there is nothing for the caller to run. step() { # step [command lines...] local n="$1" dotext="$2" why="$3" mode="$4"; shift 4 rule " Step $n of 4" prose "$dotext" if [ -n "$why" ]; then echo; prose "$why"; fi if [ "$mode" = "manual" ]; then echo; echo " -> Do this yourself, then press Enter to carry on." if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r _; fi return 1 fi echo; printf " %s\n" "$@"; echo if [ "$DRYRUN" = "1" ]; then echo " (dry run -- not executed)"; return 1; fi if [ "$UNATTENDED" = "0" ]; then read -r -p " Run this step? [Y]es / [S]kip / [Q]uit " a case "$a" in [Qq]*) echo " Stopped at your request."; exit 0 ;; [Ss]*) echo " Skipped."; return 1 ;; esac fi return 0 } rule echo " Docker breaks the host network, or containers cannot resolve names" echo " Move Docker off the conflicting range" echo echo " Risk: medium Reversible 30 minutes" echo prose 'No warranty. Use at your own risk - JbTecWiz accepts no liability. Read it before you run it, and have a backup.' rule echo if [ "$UNATTENDED" = "0" ] && [ "$DRYRUN" = "0" ]; then read -r -p " Ready? [y/N] " go case "$go" in [Yy]*) ;; *) echo " Nothing was changed."; exit 0;; esac fi if step 1 'Compare Docker'\''s networks against the real routing table.' 'A route to 172.17.0.0/16 via docker0 will beat a route to the same range via the real gateway. Everything in that range becomes unreachable, and nothing in the error mentions Docker.' cmd 'docker network ls' 'docker network inspect bridge --format '\''{{range .IPAM.Config}}{{.Subnet}}{{end}}'\''' 'ip route show'; then docker network ls docker network inspect bridge --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' ip route show if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 1 failed. The rest of the fix may depend on it." >&2 fi fi if step 2 'Set both the default bridge and the pool used for user-defined networks.' 'Setting bip alone only moves the default bridge. Compose creates its own networks from the address pool, so without the second setting the conflict returns the first time someone runs docker compose up.' cmd 'sudo tee /etc/docker/daemon.json >/dev/null <<'\''EOF'\''' '{' ' "bip": "10.200.0.1/24",' ' "default-address-pools": [' ' {"base": "10.201.0.0/16", "size": 24}' ' ]' '}' 'EOF'; then sudo tee /etc/docker/daemon.json >/dev/null <<'EOF' { "bip": "10.200.0.1/24", "default-address-pools": [ {"base": "10.201.0.0/16", "size": 24} ] } EOF if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 2 failed. The rest of the fix may depend on it." >&2 fi fi if step 3 'Restart Docker and remove the old networks so they are recreated in the new range.' '' cmd 'sudo systemctl restart docker' 'docker network prune -f'; then sudo systemctl restart docker docker network prune -f if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 3 failed. The rest of the fix may depend on it." >&2 fi fi if step 4 'Confirm the routes are now out of the way.' '' cmd 'ip route show | grep -E '\''docker|br-'\'''; then ip route show | grep -E 'docker|br-' if [ $? -ne 0 ]; then failed=$((failed+1)) echo " Step 4 failed. The rest of the fix may depend on it." >&2 fi fi rule " Confirm it worked" prose 'The previously unreachable hosts respond and containers still have connectivity.' if [ "$DRYRUN" = "0" ]; then ip route show | grep docker docker run --rm alpine ping -c2 1.1.1.1 fi rule if [ "$failed" -gt 0 ]; then echo " Finished with $failed failed step(s)." echo " Read the full write-up at https://jbtecwiz.com/support/lnx-ctr-network" else echo " Finished." fi echo prose 'To undo: Remove /etc/docker/daemon.json and restart Docker to return to the defaults.' rule