Unverified Commit 9f2d3755 authored by Kroese's avatar Kroese Committed by GitHub
Browse files

feat: Improved networking implementation (#872)

parent 76318f9a
Loading
Loading
Loading
Loading
+207 −87
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ set -Eeuo pipefail
: "${DHCP:="N"}"
: "${NETWORK:="Y"}"
: "${HOST_PORTS:=""}"
: "${USER_PORTS:=""}"
: "${ADAPTER:="virtio-net-pci"}"

: "${VM_NET_IP:=""}"
@@ -15,7 +16,7 @@ set -Eeuo pipefail
: "${VM_NET_TAP:="qemu"}"
: "${VM_NET_MAC:="$MAC"}"
: "${VM_NET_HOST:="$APP"}"
: "${VM_NET_GATEWAY:=""}"
: "${VM_NET_BRIDGE:="docker"}"
: "${VM_NET_MASK:="255.255.255.0"}"

: "${PASST:="passt"}"
@@ -120,12 +121,14 @@ configureDNS() {
  local mask="$5"
  local gateway="$6"

  [[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0
  [[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..."

  local log="/var/log/dnsmasq.log"
  rm -f "$log"

  if [[ "${NETWORK,,}" != "user"* ]]; then
  case "${NETWORK,,}" in
    "nat" | "tap" | "tun" | "tuntap" | "y" )

      # Create lease file for faster resolve
      echo "0 $mac $ip $host 01:$mac" > /var/lib/misc/dnsmasq.leases
@@ -143,13 +146,19 @@ configureDNS() {
      DNSMASQ_OPTS+=" --dhcp-option=option:router,$gateway"
      DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,$gateway"

  fi
  esac

  # Set interfaces
  DNSMASQ_OPTS+=" --interface=$if"
  DNSMASQ_OPTS+=" --bind-interfaces"

  # Add DNS entry for container
  DNSMASQ_OPTS+=" --address=/host.lan/$gateway"

  # Set local dns resolver to dnsmasq when needed
  [ -f /etc/resolv.dnsmasq ] && DNSMASQ_OPTS+=" --resolv-file=/etc/resolv.dnsmasq"

  # Enable logging to file
  DNSMASQ_OPTS+=" --log-facility=$log"

  DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
@@ -171,6 +180,38 @@ configureDNS() {
  return 0
}

getUserPorts() {

  local args=""
  local list=$1
  local ssh="22"

  [[ "${BOOT_MODE:-}" == "windows"* ]] && ssh="3389"
  [ -z "$list" ] && list="$ssh" || list+=",$ssh"

  list="${list//,/ }"
  list="${list## }"
  list="${list%% }"

  for port in $list; do
    proto="tcp"
    num="$port"

    if [[ "$port" == */udp ]]; then
      proto="udp"
      num="${port%/udp}"
    elif [[ "$port" == */tcp ]]; then
      proto="tcp"
      num="${port%/tcp}"
    fi

    args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
  done

  echo "${args%?}"
  return 0
}

getHostPorts() {

  local list="$1"
@@ -190,15 +231,83 @@ getHostPorts() {
    [ -z "$list" ] && list="$WEB_PORT" || list+=",$WEB_PORT"
  fi

  if [[ "${NETWORK,,}" == "user"* ]]; then
    # Temporary workaround for Passt bug
    [ -z "$list" ] && list="53" || list+=",53"
  if [[ "${NETWORK,,}" == "passt" ]]; then

    local DNS_PORT="53"
    local SAMBA_PORT="445"

    if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
      [ -z "$list" ] && list="$DNS_PORT" || list+=",$DNS_PORT"
    fi

    if [[ "${BOOT_MODE:-}" == "windows"* ]]; then
      if [[ "${SAMBA:-}" != [Nn]* ]]; then
        [ -z "$list" ] && list="$SAMBA_PORT" || list+=",$SAMBA_PORT"
      fi
    fi

  fi

  echo "$list"
  return 0
}

compat() {

  local gateway="$1"
  local interface="$2"
  local samba="20.20.20.1"

  [[ "$samba" == "$gateway" ]] && return 0
  [[ "${BOOT_MODE:-}" != "windows"* ]] && return 0

  if [[ "$interface" != "${interface:0:8}" ]]; then
    error "Bridge name too long!" && return 1
  fi

  # Backwards compatibility with old installations
  if ip address add dev "$interface" "$samba/24" label "$interface:compat"; then
    SAMBA_INTERFACE="$samba"
  else
    warn "failed to configure IP alias!"
  fi

  return 0
}

configureSlirp() {

  [[ "$DEBUG" == [Yy1]* ]] && echo "Configuring slirp networking..."

  local ip="$IP"
  [ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
  local base="${ip%.*}."
  [ "${ip/$base/}" -lt "4" ] && ip="${ip%.*}.4"
  local gateway="${ip%.*}.1"

  # Backwards compatibility
  ! compat "$gateway" "$VM_NET_DEV" && exit 24

  local ipv6=""
  [ -n "$IP6" ] && ipv6="ipv6=on,"

  NET_OPTS="-netdev user,id=hostnet0,ipv4=on,host=$gateway,net=${gateway%.*}.0/24,dhcpstart=$ip,${ipv6}hostname=$VM_NET_HOST"

  local forward
  forward=$(getUserPorts "${USER_PORTS:-}")
  [ -n "$forward" ] && NET_OPTS+=",$forward"

  if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
    # Force local DNS to dnsmasq as slirp provides no way to set it
    cp /etc/resolv.conf /etc/resolv.dnsmasq
    echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
    configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
  fi

  VM_NET_IP="$ip"
  return 0
}

configurePasst() {

  [[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..."
@@ -210,19 +319,19 @@ configurePasst() {
  [ -s "$pid" ] && pKill "$(<"$pid")"

  local ip="$IP"
  local gateway="$VM_NET_GATEWAY"
  [ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"

  if [ -z "$gateway" ]; then
  local gateway=""
  if [[ "$ip" != *".1" ]]; then
    gateway="${ip%.*}.1"
  else
    gateway="${ip%.*}.2"
  fi
  fi

  # passt configuration:
  # Backwards compatibility
  ! compat "$gateway" "$VM_NET_DEV" && exit 24

  # passt configuration:
  [ -z "$IP6" ] && PASST_OPTS+=" -4"

  PASST_OPTS+=" -a $ip"
@@ -239,18 +348,14 @@ configurePasst() {

  PASST_OPTS+=" -t $exclude"
  PASST_OPTS+=" -u $exclude"

  # For backwards compatiblity
  if [[ "$ip" != "20.20.20."* ]]; then
    PASST_OPTS+=" --map-host-loopback $gateway"
    PASST_OPTS+=" --map-host-loopback 20.20.20.1"
  fi

  PASST_OPTS+=" -H $VM_NET_HOST"
  PASST_OPTS+=" -M $VM_NET_MAC"

  if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
    PASST_OPTS+=" --dns-forward $gateway"
    PASST_OPTS+=" --dns-host 127.0.0.1"
  fi

  PASST_OPTS+=" -P /var/run/passt.pid"
  PASST_OPTS+=" -l $log"
  PASST_OPTS+=" -q"
@@ -274,8 +379,6 @@ configurePasst() {
  configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1

  VM_NET_IP="$ip"
  VM_NET_GATEWAY="$gateway"

  return 0
}

@@ -303,47 +406,36 @@ configureNAT() {
  if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
    { sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
    if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
      [[ "$PODMAN" == [Yy1]* ]] && return 1
      warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
      return 1
    fi
  fi

  local ip="172.30.0.2"
  local samba="20.20.20.1"
  local gateway="$VM_NET_GATEWAY"
  [ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"

  if [ -z "$gateway" ]; then
  local gateway=""
  if [[ "$ip" != *".1" ]]; then
    gateway="${ip%.*}.1"
  else
    gateway="${ip%.*}.2"
  fi
  fi

  # For backwards compatibility
  if [[ "$samba" == "$gateway" ]]; then
    samba=""
  else
    if ! ip address add dev "$VM_NET_DEV" "$samba/24" label "$VM_NET_DEV:compat"; then
      samba=""
      warn "failed to configure IP alias!"
    fi
  fi

  # Create a bridge with a static IP for the VM guest
  { ip link add dev dockerbridge type bridge ; rc=$?; } || :
  { ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || :

  if (( rc != 0 )); then
    warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
  fi

  if ! ip address add "$gateway/24" broadcast "${ip%.*}.255" dev dockerbridge; then
  if ! ip address add "$gateway/24" broadcast "${ip%.*}.255" dev "$VM_NET_BRIDGE"; then
    warn "failed to add IP address pool!" && return 1
  fi

  while ! ip link set dockerbridge up; do
  # Backwards compatibility
  ! compat "$gateway" "$VM_NET_BRIDGE" && exit 24

  while ! ip link set "$VM_NET_BRIDGE" up; do
    info "Waiting for IP address to become available..."
    sleep 2
  done
@@ -370,7 +462,7 @@ configureNAT() {
    sleep 2
  done

  if ! ip link set dev "$VM_NET_TAP" master dockerbridge; then
  if ! ip link set dev "$VM_NET_TAP" master "$VM_NET_BRIDGE"; then
    warn "failed to set master bridge!" && return 1
  fi

@@ -419,12 +511,9 @@ configureNAT() {

  NET_OPTS+=",script=no,downscript=no"

  configureDNS "dockerbridge" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
  configureDNS "$VM_NET_BRIDGE" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1

  VM_NET_IP="$ip"
  VM_NET_GATEWAY="$gateway"
  SAMBA_INTERFACE="$samba"

  return 0
}

@@ -432,17 +521,21 @@ closeBridge() {

  local pid="/var/run/dnsmasq.pid"
  [ -s "$pid" ] && pKill "$(<"$pid")"
  rm -f "$pid"

  pid="/var/run/passt.pid"
  [ -s "$pid" ] && pKill "$(<"$pid")"
  rm -f "$pid"

  [[ "${NETWORK,,}" == "user"* ]] && return 0
  case "${NETWORK,,}" in
    "user"* | "passt" | "slirp" ) return 0 ;;
  esac

  ip link set "$VM_NET_TAP" down promisc off &> null || true
  ip link delete "$VM_NET_TAP" &> null || true

  ip link set dockerbridge down &> null || true
  ip link delete dockerbridge &> null || true
  ip link set "$VM_NET_BRIDGE" down &> null || true
  ip link delete "$VM_NET_BRIDGE" &> null || true

  return 0
}
@@ -478,6 +571,7 @@ closeNetwork() {
cleanUp() {

  # Clean up old files
  rm -f /etc/resolv.dnsmasq
  rm -f /var/run/passt.pid
  rm -f /var/run/dnsmasq.pid

@@ -622,7 +716,12 @@ getInfo() {
    error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
  fi

  [ -f "/run/.containerenv" ] && PODMAN="Y" || PODMAN="N"
  if [[ "$PODMAN" == [Yy1]* && "$DHCP" != [Yy1]* ]]; then
    if [ -z "$NETWORK" ] || [[ "${NETWORK^^}" == "Y" ]]; then
      # By default Podman has no permissions for NAT networking
      NETWORK="user"
    fi
  fi

  if [[ "$DEBUG" == [Yy1]* ]]; then
    line="Host: $HOST  IP: $IP  Gateway: $GATEWAY  Interface: $VM_NET_DEV  MAC: $VM_NET_MAC  MTU: $mtu"
@@ -661,35 +760,56 @@ if [[ "$DHCP" == [Yy1]* ]]; then

else

  if [[ "${NETWORK,,}" != "user"* ]]; then
  case "${NETWORK,,}" in
    "user"* | "passt" | "slirp" ) ;;
    "nat" | "tap" | "tun" | "tuntap" | "y" )

    # Configure for tap interface
      # Configure tap interface
      if ! configureNAT; then

        closeBridge
        NETWORK="user"
        msg="falling back to user-mode networking!"
      if [[ "$PODMAN" != [Yy1]* ]]; then
        msg="failed to setup NAT networking, $msg"
      else
        msg="podman detected, $msg"
      fi
      warn "$msg"

    fi
      fi ;;

  fi
  esac

  if [[ "${NETWORK,,}" == "user"* ]]; then
    if [[ "${BOOT_MODE:-}" != "windows_legacy" ]]; then
      NETWORK="passt"
    else
      NETWORK="slirp"
    fi
  fi

  case "${NETWORK,,}" in
    "nat" | "tap" | "tun" | "tuntap" | "y" ) ;;
    "passt" )

      # Configure for user-mode networking (passt)
      if ! configurePasst; then
        error "Failed to configure user-mode networking!"
        exit 24
    fi
      fi ;;

    "slirp" )

      # Configure for user-mode networking (slirp)
      if ! configureSlirp; then
        error "Failed to configure user-mode networking!"
        exit 24
      fi

      if [ -z "$USER_PORTS" ]; then
        info "Notice: slirp networking is active, so when you want to expose ports, you will need to map them using this variable: \"USER_PORTS=80,443\"."
      fi ;;

    *)
      error "Unrecognized NETWORK value: \"$NETWORK\"" && exit 24 ;;
  esac

fi

NET_OPTS+=" -device $ADAPTER,id=net0,netdev=hostnet0,romfile=,mac=$VM_NET_MAC"
+13 −6
Original line number Diff line number Diff line
@@ -4,12 +4,9 @@ set -Eeuo pipefail
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
[[ "${TRACE:-}" == [Yy1]* ]] && set -o functrace && trap 'echo "# $BASH_COMMAND" >&2' DEBUG

[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
[ ! -f "/run/entry.sh" ] && error "Script must be run inside the container!" && exit 11
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12

echo "❯ Starting $APP for Docker v$(</run/version)..."
echo "❯ For support visit $SUPPORT"

# Docker environment variables

: "${BOOT:=""}"            # Path of ISO file
@@ -27,13 +24,23 @@ echo "❯ For support visit $SUPPORT"

# Helper variables

PODMAN="N"
ENGINE="Docker"
PROCESS="${APP,,}"
PROCESS="${PROCESS// /-}"

if [ -f "/run/.containerenv" ]; then
  PODMAN="Y"
  ENGINE="Podman"
fi

echo "❯ Starting $APP for $ENGINE v$(</run/version)..."
echo "❯ For support visit $SUPPORT"

INFO="/run/shm/msg.html"
PAGE="/run/shm/index.html"
TEMPLATE="/var/www/index.html"
FOOTER1="$APP for Docker v$(</run/version)"
FOOTER1="$APP for $ENGINE v$(</run/version)"
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"

CPU=$(cpu)
@@ -169,7 +176,7 @@ if (( VNC_PORT < 5900 )); then
fi

cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
html "Starting $APP for $ENGINE..."

if [[ "${WEB:-}" != [Nn]* ]]; then