Search code examples
linuxdockervpncheckpoint

Server not reachable within a VPN (SNX) out of a Docker container


I am working with the latest Manjaro with the kernel: x86_64 Linux 5.10.15-1-MANJARO.

I am connected to my company network via VPN. For this I use SNX with the build version 800010003.

When I start a Docker container (Docker version 20.10.3, build 48d30b5b32) which should connect to a machine from the company network, I get the following message.

[maurice@laptop ~]$ docker run --rm alpine ping company-server
ping: bad address 'company-server'

Also using the IP from the 'company-server' doesn't work.

A ping outside the container works, no matter using the name or IP.

The resolv.conf looks correct to me.

[maurice@laptop ~]$ docker run --rm alpine cat /etc/resolv.conf
# Generated by NetworkManager
search lan
nameserver 10.1.0.250
nameserver 10.1.0.253
nameserver 192.168.86.1

What I have found out so far.

If I downgrade packages glibc and lib32-glibc to version 2.32-5, the ping out of the container works again. Because of dependencies I have also to downgrade gcc, gcc-libs and lib32-gcc-libs to version 10.2.0-4.

I tried the whole thing with a fresh Pop OS 20.10 installation, same problem. I also did a test with another VPN (OpenVPN) which worked fine. However, this was only a test scenario and cannot be used as an alternative.

I have been looking for a solution for several days but have not found anything. It would be really nice if someone could help me with this.


Solution

  • TL;DR: on kernel >5.8 the tunsnx interface is no longer created with global scope and need to be recreated. small script to the rescure https://gist.github.com/Fahl-Design/ec1e066ec2ef8160d101dff96a9b56e8

    Longer version:

    Here are my findings and the solution to (temp) fix it:

    Steps to reproduce:

    • connect your snx tunnel

    • see ping fails to server behind tunnel

      docker run --rm -ti --net=company_net busybox /bin/sh -c "ping 192.168.210.210"
      
    • run this command to check ip and scope of the "tunsnx" interface

      ip -o address show "tunsnx" | awk -F ' +' '{print $4 " " $6 " " $8}'
      

    if you get something like

    192.168.210.XXX 192.168.210.30/32 247
    

    or (Thx Timz)

    192.168.210.XXX 192.168.210.30/32 nowhere
    

    the scope is not set to "global" and no connection can be established

    to fix this, like "ronan lanore" suggested, you need to change the scope to global

    this can be done with a little helper script like this one:

    #!/usr/bin/env bash
    #
    # Usage: [dry_run=1] [debug=1] [interface=tunsnx] docker-fix-snx
    #
    # Credits to: https://github.com/docker/for-linwux/issues/288#issuecomment-825580160
    #
    # Env Variables:
    #   interface - Defaults to tunsnx
    #   dry_run - Set to 1 to have a dry run, just printing out the iptables command
    #   debug   - Set to 1 to see bash substitutions
    
    set -eu
    
    _log_stderr() {
      echo "$*" >&2
    }
    
    if [ "${debug:=0}" = 1 ]; then
      set -x
      dry_run=${dry_run:=1}
    fi
    
    : ${dry_run:=0}
    : ${interface:=tunsnx}
    
    data=($(ip -o address show "$interface" | awk -F ' +' '{print $4 " " $6 " " $8}'))
    
    LOCAL_ADDRESS_INDEX=0
    PEER_ADDRESS_INDEX=1
    SCOPE_INDEX=2
    
    if [ "$dry_run" = 1 ]; then
      echo "[-] DRY-RUN MODE"
    fi
    
    if [ "${data[$SCOPE_INDEX]}" == "global" ]; then
      echo "[+] Interface ${interface} is already set to global scope. Skip!"
      exit 0
    else
      echo "[+] Interface ${interface} is set to scope ${data[$SCOPE_INDEX]}."
    
      tmpfile=$(mktemp --suffix=snxwrapper-routes)
      echo "[+] Saving current IP routing table..."
      if [ "$dry_run" = 0 ]; then
        sudo ip route save >$tmpfile
      fi
    
      echo "[+] Deleting current interface ${interface}..."
      if [ "$dry_run" = 0 ]; then
        sudo ip address del ${data[$LOCAL_ADDRESS_INDEX]} peer ${data[$PEER_ADDRESS_INDEX]} dev ${interface}
      fi
    
      echo "[+] Recreating interface ${interface} with global scope..."
      if [ "$dry_run" = 0 ]; then
        sudo ip address add ${data[$LOCAL_ADDRESS_INDEX]} dev ${interface} peer ${data[$PEER_ADDRESS_INDEX]} scope global
      fi
    
      echo "[+] Restoring routing table..."
      if [ "$dry_run" = 0 ]; then
        sudo ip route restore <$tmpfile 2>/dev/null
      fi
    
      echo "[+] Cleaning temporary files..."
      rm $tmpfile
    
      echo "[+] Interface ${interface} is set to global scope. Done!"
      if [ "$dry_run" = 0 ]; then
        echo "[+] Result:"
        ip -o address show "tunsnx" | awk -F ' +' '{print $4 " " $6 " " $8}'
      fi
      exit 0
    fi
    
    [ "$debug" = 1 ] && set +x