특정 네트워크 네임스페이스에 대해 OpenVPN을 통해서만 모든 트래픽을 제공합니다.

특정 네트워크 네임스페이스에 대해 OpenVPN을 통해서만 모든 트래픽을 제공합니다.

모든 트래픽에 대해 VPN(OpenVPN 사용)을 설정하려고 하는데,오직특정 프로세스에서 들어오고 나가는 트래픽은 VPN을 통해 전달됩니다. 다른 프로세스는 계속해서 물리적 장치를 직접 사용해야 합니다. 내가 아는 한, Linux에서 이를 수행하는 방법은 네트워크 네임스페이스를 사용하는 것입니다.

OpenVPN을 정상적으로 사용하는 경우(예: 퍼널모두VPN을 통한 클라이언트의 트래픽), 정상적으로 작동합니다. 구체적으로 OpenVPN을 시작한 방법은 다음과 같습니다.

# openvpn --config destination.ovpn --auth-user-pass credentials.txt

(destination.ovpn의 편집된 버전은 이 질문의 끝에 있습니다.)

터널 장치를 네임스페이스로 제한하는 스크립트를 작성하는 다음 단계에서 막혔습니다. 나는 시도했다:

  1. 터널 장치를 네임스페이스에 직접 배치

    # ip netns add tns0
    # ip link set dev tun0 netns tns0
    # ip netns exec tns0 ( ... commands to bring up tun0 as usual ... )
    

    이러한 명령은 성공적으로 실행되지만 네임스페이스(예: ip netns exec tns0 traceroute -n 8.8.8.8) 내에서 생성된 트래픽은 블랙홀에 빠집니다.

  2. 추정하다 "네트워크 네임스페이스에는 가상 이더넷(veth) 인터페이스만 할당할 수 있습니다."(사실이라면 가장 우스꽝스럽고 불필요한 API 제한에 대한 올해의 상을 봅니다.) Veth 쌍과 브리지를 만들고 Veth 쌍의 한쪽 끝을 네임스페이스에 넣습니다. 이는 트래픽을 줄이는 데까지 이르지 않습니다. 모두 바닥에: 다리 위에 터널을 놓을 수 없습니다. [편집: 이것은 단지 이유인 것 같습니다!수도꼭지장치를 브리지에 넣을 수 있습니다. 임의의 장치를 네트워크 네임스페이스에 넣을 수 없는 것과는 달리 브리지는 이더넷 계층 개념이기 때문에 실제로 의미가 있습니다. 불행히도 내 VPN 공급자는 탭 모드에서 OpenVPN을 지원하지 않으므로 하나의 솔루션이 필요합니다. ]

    # ip addr add dev tun0 local 0.0.0.0/0 scope link
    # ip link set tun0 up
    # ip link add name teo0 type veth peer name tei0
    # ip link set teo0 up
    # brctl addbr tbr0
    # brctl addif tbr0 teo0
    # brctl addif tbr0 tun0
    can't add tun0 to bridge tbr0: Invalid argument
    

이 질문 끝에 있는 스크립트는 veth 방법을 위한 것입니다. 직접 메소드에 대한 스크립트는 편집 내역에서 확인할 수 있습니다. 먼저 설정하지 않고 스크립트에서 사용되는 것으로 보이는 변수는 환경의 프로그램에 의해 설정됩니다 openvpn. 예, 엉성하고 소문자 이름을 사용합니다.

이 작업을 수행하는 방법에 대한 구체적인 제안을 제공해 주십시오. 나는 내가 여기서 화물 숭배 집단을 통해 프로그램되고 있다는 사실을 고통스럽게 인식하고 있습니다.누구나이러한 사항에 대한 포괄적인 문서를 작성한 적이 있습니까? 아무것도 찾을 수 없었습니다. 따라서 스크립트에 대한 일반적인 코드 검토도 부탁드립니다.

중요한 경우:

# uname -srvm
Linux 3.14.5-x86_64-linode42 #1 SMP Thu Jun 5 15:22:13 EDT 2014 x86_64
# openvpn --version | head -1
OpenVPN 2.3.2 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [eurephia] [MH] [IPv6] built on Mar 17 2014
# ip -V
ip utility, iproute2-ss140804
# brctl --version
bridge-utils, 1.5

커널은 내 웹 호스팅 공급자(에노드) 그리고 로 컴파일되었지만 CONFIG_MODULES=y실제 모듈은 없습니다. 기반으로 CONFIG_*설정된 유일한 변수는 실제로 가지고 있지 않은 입니다 .m/proc/config.gzCONFIG_XEN_TMEM가지다모듈(커널은 내 파일 시스템 외부에 저장되어 있으며 /lib/modules비어 있으며 /proc/modules어떻게든 마술처럼 로드되지 않았음을 나타냅니다). 요청 시 발췌문이 제공되지만 /proc/config.gz여기에 전체 내용을 붙여넣고 싶지는 않습니다.

netns-up.sh

#! /bin/sh

mask2cidr () {
    local nbits dec
    nbits=0
    for dec in $(echo $1 | sed 's/\./ /g') ; do
        case "$dec" in
            (255) nbits=$(($nbits + 8)) ;;
            (254) nbits=$(($nbits + 7)) ;;
            (252) nbits=$(($nbits + 6)) ;;
            (248) nbits=$(($nbits + 5)) ;;
            (240) nbits=$(($nbits + 4)) ;;
            (224) nbits=$(($nbits + 3)) ;;
            (192) nbits=$(($nbits + 2)) ;;
            (128) nbits=$(($nbits + 1)) ;;
            (0)   ;;
            (*) echo "Error: $dec is not a valid netmask component" >&2
                exit 1
                ;;
        esac
    done
    echo "$nbits"
}

mask2network () {
    local host mask h m result
    host="$1."
    mask="$2."
    result=""
    while [ -n "$host" ]; do
        h="${host%%.*}"
        m="${mask%%.*}"
        host="${host#*.}"
        mask="${mask#*.}"
        result="$result.$(($h & $m))"
    done
    echo "${result#.}"
}

maybe_config_dns () {
    local n option servers
    n=1
    servers=""
    while [ $n -lt 100 ]; do
       eval option="\$foreign_option_$n"
       [ -n "$option" ] || break
       case "$option" in
           (*DNS*)
               set -- $option
               servers="$servers
nameserver $3"
               ;;
           (*) ;;
       esac
       n=$(($n + 1))
    done
    if [ -n "$servers" ]; then
        cat > /etc/netns/$tun_netns/resolv.conf <<EOF
# name servers for $tun_netns
$servers
EOF
    fi
}

config_inside_netns () {
    local ifconfig_cidr ifconfig_network

    ifconfig_cidr=$(mask2cidr $ifconfig_netmask)
    ifconfig_network=$(mask2network $ifconfig_local $ifconfig_netmask)

    ip link set dev lo up

    ip addr add dev $tun_vethI \
        local $ifconfig_local/$ifconfig_cidr \
        broadcast $ifconfig_broadcast \
        scope link
    ip route add default via $route_vpn_gateway dev $tun_vethI
    ip link set dev $tun_vethI mtu $tun_mtu up
}

PATH=/sbin:/bin:/usr/sbin:/usr/bin
export PATH

set -ex

# For no good reason, we can't just put the tunnel device in the
# subsidiary namespace; we have to create a "virtual Ethernet"
# device pair, put one of its ends in the subsidiary namespace,
# and put the other end in a "bridge" with the tunnel device.

tun_tundv=$dev
tun_netns=tns${dev#tun}
tun_bridg=tbr${dev#tun}
tun_vethI=tei${dev#tun}
tun_vethO=teo${dev#tun}

case "$tun_netns" in
     (tns[0-9] | tns[0-9][0-9] | tns[0-9][0-9][0-9]) ;;
     (*) exit 1;;
esac

if [ $# -eq 1 ] && [ $1 = "INSIDE_NETNS" ]; then
    [ $(ip netns identify $$) = $tun_netns ] || exit 1
    config_inside_netns
else

    trap "rm -rf /etc/netns/$tun_netns ||:
          ip netns del $tun_netns      ||:
          ip link del $tun_vethO       ||:
          ip link set $tun_tundv down  ||:
          brctl delbr $tun_bridg       ||:
         " 0

    mkdir /etc/netns/$tun_netns
    maybe_config_dns

    ip addr add dev $tun_tundv local 0.0.0.0/0 scope link
    ip link set $tun_tundv mtu $tun_mtu up

    ip link add name $tun_vethO type veth peer name $tun_vethI
    ip link set $tun_vethO mtu $tun_mtu up

    brctl addbr $tun_bridg
    brctl setfd $tun_bridg 0
    #brctl sethello $tun_bridg 0
    brctl stp $tun_bridg off

    brctl addif $tun_bridg $tun_vethO
    brctl addif $tun_bridg $tun_tundv
    ip link set $tun_bridg up

    ip netns add $tun_netns
    ip link set dev $tun_vethI netns $tun_netns
    ip netns exec $tun_netns $0 INSIDE_NETNS

    trap "" 0
fi

netns-down.sh

#! /bin/sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin
export PATH

set -ex

tun_netns=tns${dev#tun}
tun_bridg=tbr${dev#tun}

case "$tun_netns" in
     (tns[0-9] | tns[0-9][0-9] | tns[0-9][0-9][0-9]) ;;
     (*) exit 1;;
esac

[ -d /etc/netns/$tun_netns ] || exit 1

pids=$(ip netns pids $tun_netns)
if [ -n "$pids" ]; then
    kill $pids
    sleep 5
    pids=$(ip netns pids $tun_netns)
    if [ -n "$pids" ]; then
        kill -9 $pids
    fi
fi

# this automatically cleans up the the routes and the veth device pair
ip netns delete "$tun_netns"
rm -rf /etc/netns/$tun_netns

# the bridge and the tunnel device must be torn down separately
ip link set $dev down
brctl delbr $tun_bridg

목적지.ovpn

client
auth-user-pass
ping 5
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
ns-cert-type server
verb 3
route-metric 1
proto tcp
ping-exit 90
remote [REDACTED]
<ca>
[REDACTED]
</ca>
<cert>
[REDACTED]
</cert>
<key>
[REDACTED]
</key>

답변1

네임스페이스 내에서 OpenVPN 링크를 시작한 다음 네임스페이스 내에서 해당 OpenVPN 링크를 사용하는 각 명령을 실행할 수 있습니다. 이 작업을 수행하는 방법에 대한 자세한 내용은 다음을 참조하세요.Sebastian Thorarensen의 네트워크 네임스페이스 내에서 OpenVPN 터널 실행.

나는 그것을 시도했고 작동합니다. 아이디어는 (전역 네임스페이스가 아닌) 특정 네임스페이스 내에서 OpenVPN 연결의 시작 및 라우팅 단계를 수행하는 사용자 정의 스크립트를 제공하는 것입니다. 위 소스를 바탕으로 한 답변이지만  resolv.conf.

먼저- 위로OpenVPN 스크립트. 이 스크립트는 다음과 같은 네트워크 네임스페이스 내에 VPN 터널 인터페이스를 생성합니다.VPN, 기본 네임스페이스 대신.

$ cat > netns-up << 'EOF'
#!/bin/sh
case $script_type in
        up)
                ip netns add vpn
                ip netns exec vpn ip link set dev lo up
                mkdir -p /etc/netns/vpn
                echo "nameserver 8.8.8.8" > /etc/netns/vpn/resolv.conf
                ip link set dev "$1" up netns vpn mtu "$2"
                ip netns exec vpn ip addr add dev "$1" \
                        "$4/${ifconfig_netmask:-30}" \
                        ${ifconfig_broadcast:+broadcast "$ifconfig_broadcast"}
                test -n "$ifconfig_ipv6_local" && \
                        ip netns exec vpn ip addr add dev "$1" \
                                "$ifconfig_ipv6_local"/112
                ;;
        route-up)
                ip netns exec vpn ip route add default via "$route_vpn_gateway"
                test -n "$ifconfig_ipv6_remote" && \
                        ip netns exec vpn ip route add default via \
                                "$ifconfig_ipv6_remote"
                ;;
        down)
                ip netns delete vpn
                ;;
esac
EOF

그런 다음 OpenVPN을 시작하고 우리의 VPN을 사용하도록 지시하십시오.- 위로ifconfig 및 라우팅을 실행하는 대신 스크립트를 실행합니다.

openvpn --ifconfig-noexec --route-noexec --up netns-up --route-up netns-up --down netns-up

이제 다음과 같이 터널링하려는 프로그램을 시작할 수 있습니다.

ip netns는 VPN을 실행합니다.주문하다

유일한 문제는 이를 호출하려면 루트여야 하며 ip netns exec ...응용 프로그램이 루트로 실행되는 것을 원하지 않을 수도 있다는 것입니다. 해결책은 간단합니다.

sudo ip netns exec vpn sudo -u $(whoami)주문하다

답변2

그래서 당신은할 수 있는터널 인터페이스를 네트워크 네임스페이스에 넣습니다. 내 모든 문제는 인터페이스를 열 때 발생하는 오류로 인해 발생합니다.

ip addr add dev $tun_tundv \
    local $ifconfig_local/$ifconfig_cidr \
    broadcast $ifconfig_broadcast \
    scope link

문제는 "범위 연결"인데, 라우팅에만 영향을 미치는 것으로 오해했습니다. 이는 커널이 터널로 전송된 모든 패킷의 소스 주소를 로 설정하게 합니다 0.0.0.0. 아마도 OpenVPN 서버는 RFC1122에 따라 해당 패킷을 유효하지 않은 것으로 폐기할 것이며, 그렇지 않은 경우에도 대상은 분명히 응답할 수 없습니다.

openvpn의 내장 네트워크 구성 스크립트는 이러한 실수를 하지 않기 때문에 네트워크 네임스페이스 없이도 모든 것이 잘 작동합니다. 내 원본 스크립트는 "범위 연결" 없이 작동합니다.

(이것을 어떻게 발견했는지 묻습니다. straceopenvpn 프로세스에서 실행하고 터널 설명자에서 읽은 모든 것을 hexdump로 설정한 다음 수동으로 패킷 헤더를 디코딩합니다.)

답변3

veth 장치를 생성하려고 할 때 발생하는 오류는 ip명령줄 인수가 해석되는 방식의 변경으로 인해 발생합니다.

ipveth 장치 쌍을 생성하는 올바른 호출은 다음과 같습니다.

ip link add name veth0 type veth peer name veth1

( name바꾸다 dev)

이제 네임스페이스에서 VPN 터널로 트래픽을 어떻게 라우팅합니까? 작업할 tun 장치만 있으므로 "호스트"가 라우팅을 수행해야 합니다. 즉, veth 쌍을 생성하여 네임스페이스에 넣습니다. 라우팅을 통해 다른 쪽을 터널에 연결합니다. 따라서 전달을 활성화한 다음 필요한 경로를 추가하십시오.

eth0예를 들어 이것이 기본 인터페이스이고, tun0VPN 터널 인터페이스이고 veth0/ 이거나 네임스페이스의 veth1인터페이스 쌍이라고 가정해 보겠습니다 . veth1네임스페이스 내에 veth1.

정책 라우팅을 사용해야 하는 호스트에서는 다음을 참조하세요.여기예를 들어. 당신이 해야 할 일:

추가/추가 항목(예:

1   vpn

도착하다 /etc/iproute2/rt_tables. 이 방법으로 (아직 생성된) 테이블을 이름으로 호출할 수 있습니다.

그런 다음 다음 명령문을 사용하십시오.

ip rule add iif veth0 priority 1000 table vpn
ip rule add iif tun0 priority 1001 table vpn
ip route add default via <ip-addr-of-tun0> table vpn
ip route add <ns-network> via <ip-addr-of-veth0> table vpn

여기서는 귀하와 같은 설정을 시도할 수 없지만 이것이 정확히 귀하가 원하는 작업을 수행할 것입니다. VPN이나 ​​"게스트" 네트워크가 방해받지 않도록 패킷 필터링 규칙을 사용하여 이를 향상시킬 수 있습니다.

참고: tun0먼저 네임스페이스로 들어가는 것이 올바른 일인 것 같습니다. 하지만 당신처럼 나도 그렇게 하지 않았어요. 정책 라우팅은 다음으로 옳은 것처럼 보입니다. VPN 뒤에 있는 네트워크를 이해하면 Mahendra의 솔루션이 작동합니다.그리고다른 모든 응용 프로그램은 이러한 네트워크에 액세스하지 않습니다. 하지만 초기 조건("모든 흐름 및오직특정 프로세스로 들어오고 나가는 트래픽은 VPN을 통과합니다.") 후자는 보장할 수 없는 것 같습니다.

답변4

VPN을 통해 액세스하는 네트워크가 알려진 경우 라우팅 테이블을 편집하여 원하는 결과를 얻을 수 있습니다.

  1. 현재 기본 경로를 기록해 두세요.

    # ip route | grep default default via 192.168.43.1 dev wlo1 proto static metric 1024

  2. 라우팅 항목을 가져오는 VPN을 수행합니다.

  3. 이전 기본 경로가 테이블의 첫 번째 기본 항목이 되는 현재 기본 경로(VPN에 의해 ​​추가됨)를 제거합니다.

    # ip route | grep default default dev tun0 scope link default via 192.168.43.1 dev wlo1 proto static metric 1024

    # ip route del default dev tun0 scope link

  4. tun0을 통해 라우팅하려면 VPN의 네트워크에 사용자 지정 경로를 추가하세요.

    # ip route add <net1>/16 dev tun0

    # ip route add <net2>/24 dev tun0

  5. 두 개의 네임서버 항목(resolv.conf에 있음)과 VPN 및 직접 연결을 추가합니다.

이제 모든 net1 및 net2 연결은 VPN을 통과하며 재설정은 직접 발생합니다(이 예에서는 wlo1을 통해).

관련 정보