특정 네트워크 네임 스페이스에 대해서만 OpenVPN을 통한 모든 트래픽 공급


16

특정 프로세스 에 대한 모든 트래픽과 트래픽 VPN을 통과하도록 VPN (OpenVPN 사용)을 설정하려고합니다 . 다른 프로세스는 물리적 장치를 계속 직접 사용해야합니다. 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 pair와 bridge를 만들고 이름 공간에 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

커널은 내 가상 호스팅 제공 업체 (에 의해 만들어진 Linode )와, 컴파일하지만 CONFIG_MODULES=y, 실제 모듈이 없습니다 - 만 CONFIG_*에 변수 설정 m에 따라 /proc/config.gz되었다 CONFIG_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

destination.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>

veth 장치가 지원됩니까? 커널 모듈 (veth)이로드되어 있습니까?
카운터 모드

@countermode grep veth /proc/modules는 아무것도 나열하지 않지만 그것이 결정적인지 모르겠습니다. Linode 인스턴스에는 OS 파티션 내에 커널이 설치되어 있지 않으므로 누락 된 모듈을로드 할 수 있는지 잘 모르겠습니다.
zwol

않습니다 lsmod전혀 출력을 생성? 디렉토리가 /lib/modules있습니까?
카운터 모드

lsmod: command not found. 이 /lib/modules있지만 모듈 이 없으며 빈 modules.dep파일이 들어있는 커널 당 디렉토리 만 있습니다. Linode 관련 도움말을 살펴보고 그것이 어떻게되어 있는지 알아 보겠습니다.
zwol

흠 ... 매우 이상합니다. 나는 Linode에 익숙하지 않지만 veth 장치가 지원되지 않는 것처럼 보입니다.
카운터 모드

답변:


9

네임 스페이스 내에서 OpenVPN 링크를 시작한 다음 네임 스페이스 내에서 해당 OpenVPN 링크를 사용하려는 모든 명령을 실행할 수 있습니다. 내 작업이 아닌 수행 방법에 대한 자세한 내용은 다음과 같습니다.

http://www.naju.se/articles/openvpn-netns.html

나는 그것을 시도하고 작동합니다; 아이디어는 글로벌 네임 스페이스가 아닌 특정 네임 스페이스 내에서 OpenVPN 연결의 가동 및 라우팅 단계를 수행하기위한 사용자 지정 스크립트를 제공하는 것입니다. 앞으로 오프라인 상태가 될 경우를 대비하여 위 링크에서 인용합니다.

먼저 OpenVPN을위한 --up 스크립트를 생성하십시오. 이 스크립트는 기본 네임 스페이스 대신 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을 시작하고 ifconfig 및 route를 실행하는 대신 --up 스크립트를 사용하도록 지시하십시오.

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

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

ip netns exec vpn command

유일한 문제점은 호출하려면 루트 여야 ip netns exec ...하며 애플리케이션이 루트로 실행되지 않도록하는 것입니다. 해결책은 간단합니다.

sudo ip netns exec vpn sudo -u $ (whoami) 명령

1
안녕하세요, 사이트에 오신 것을 환영합니다! 사용자는 답변에 붙여 넣은 링크의 내용을 최소한 요약 (가능한 경우)하는 것이 좋습니다. 링크가 오래 될 경우 응답 품질을 유지하는 데 도움이됩니다 (예 : 사이트에 더 이상 액세스 할 수 없음). 링크 된 기사의 가장 중요한 부분 / 지침을 포함하여 답변을 개선하십시오.
Erathiel

이것은 훌륭하지만 쉘이 모든 변수를 확장하지 못하도록 heredoc 구분 기호를 작은 따옴표로 묶어야합니다.
ewatt

7

그것은 당신이 밝혀 수있는 네트워크 공간에 터널 인터페이스를 넣어. 내 전체 문제는 인터페이스를 표시하는 실수로 인한 것입니다.

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

문제는 "범위 링크"인데, 라우팅에만 영향을 미치는 것으로 오해했습니다. 커널이 터널로 보낸 모든 패킷의 소스 주소를 다음과 같이 설정합니다 0.0.0.0. 아마도 OpenVPN 서버는 RFC1122에 따라 유효하지 않은 것으로 버릴 것입니다. 그렇지 않은 경우에도 대상은 응답 할 수 없습니다.

openvpn의 내장 네트워크 구성 스크립트가이 실수를하지 않았기 때문에 네트워크 네임 스페이스가 없으면 모든 것이 올바르게 작동했습니다. 그리고 "scope link"가 없으면 내 원본 스크립트도 작동합니다.

straceopenvpn 프로세스에서 실행 하여 터널 디스크립터에서 읽은 모든 것을 16 진수 덤프로 설정 한 다음 수동으로 패킷 헤더를 디코딩하도록 설정했습니다.


이것에 대한 가이드를 작성할 수 있습니까? 비슷한 것을 설정하려고하는데 질문의 어느 부분이 시작하기에 좋으며 어떤 부분이 실패했는지를 말하기는 어렵습니다.
tremby

@tremby 가까운 시일 내에 그렇게 할 시간이 없지만 github.com/zackw/tbbscraper/blob/master/scripts/openvpn-netns.c가 유용 할 것입니다.
zwol

Yeesh, 1100 라인 C 프로그램이 도움이 될지 확신 할 수 없습니다. 최종 구성, 스크립트 및 주문은 어떻습니까? ... 또는 C 프로그램이 최종 구현입니까?
tremby

@tremby 네, C 프로그램은 저의 최종 구현입니다. (사용 시나리오에서 setuid를 설정해야합니다.) 상단에 큰 주석이 그것을 사용하는 방법을 설명하지 않으면 알려주십시오.
zwol

@tremby 대안으로, github.com/zackw/tbbscraper/blob/master/scripts/… 에서 시작하여 "openvpn 내부에서 실행 된 스크립트" 를보고 네트워크 네임 스페이스가 어떻게 설정되고 해제되는지 확인하십시오. ovpn 클라이언트의 실제 호출은 github.com/zackw/tbbscraper/blob/master/scripts/…에 있습니다. 코드의 나머지 부분은 이러한 작업을 작성하는 데 덜 지루한 미니 셸 구현으로 생각할 수 있습니다.
zwol

4

veth 장치를 만들려고 할 때 발생하는 오류 ip는 명령 줄 인수를 해석하는 방식이 변경되어 발생합니다 .

ip한 쌍의 veth 장치를 생성하는 올바른 호출 은

ip link add name veth0 type veth peer name veth1

(의 name인스 타드 dev)

이제 네임 스페이스에서 VPN 터널로 트래픽을 내보내는 방법은 무엇입니까? 원하는대로 튜닝 장치 만 있으므로 "호스트"는 라우팅해야합니다. 즉, veth 쌍을 만들어 네임 스페이스에 넣습니다. 라우팅을 통해 다른 하나를 터널에 연결하십시오. 따라서 전달을 활성화 한 다음 필요한 경로를 추가하십시오.

예를 위해서가 그 가정을 위해 eth0메인 인터페이스입니다, tun0당신의 VPN 터널 인터페이스, 그리고 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이나 ​​"게스트"넷이 방해받지 않도록 보강 할 수 있습니다.

NB tun0네임 스페이스로 이동 하는 것이 옳은 일처럼 보입니다. 그러나 당신처럼 나는 그것을 작동시키지 못했습니다. 정책 라우팅은 다음으로해야 할 일처럼 보입니다. 당신은 VPN 뒤에 네트워크가 알고있는 경우 마헨드라의 솔루션을 적용 하고 다른 모든 응용 프로그램이 그 네트워크에 액세스하지 않습니다. 그러나 초기 상태 ( " 특정 프로세스 에 대한 모든 트래픽 및 트래픽 VPN을 통과")는 후자가 보장 될 수없는 것처럼 들립니다.


고마워, 이것은 조금 더 나아졌지 만 이제는 "교량을 사용하여 정맥 장치를 터널에 연결합니다"부분에 붙어 있습니다-수정 된 질문을 참조하십시오.
zwol

방금 게시 한 답변에 따라 전체 내용은 원래 스크립트에서 어리석은 실수로 귀착됩니다. "범위 링크"는 내가 생각한 것을 의미하지는 않습니다. 그러나 나는 당신에게 현상금을 줄 것입니다. 왜냐하면 당신은 다양한 가능성을 시험해 볼 수 있도록 많은 노력을 기울 였기 때문입니다. 만약 당신이하지 않았다면 나는 완전히 포기했을 것입니다.
zwol

잭, 고마워 네임 스페이스와 정책 라우팅은 흥미로운 연구였습니다. 나는 그 자체로 흥미 진진하지 않았다면 실제로 많은 노력을 기울이지 않았을 것입니다.
카운터 모드

0

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. VPN에있는 네트워크에 사용자 지정 경로를 추가하여 tun0을 통해 경로를 지정하십시오.

    # ip route add <net1>/16 dev tun0

    # ip route add <net2>/24 dev tun0

  5. VPN 및 직접 연결을 위해 이름 서버 항목 (resolv.conf에 모두)을 추가하십시오.

이제 모든 net1 및 net2 연결이 VPN을 통과하고 재설정이 직접 수행됩니다 (이 예에서는 wlo1을 통해).


안타깝게도 VPN을 통해 액세스 한 네트워크는 사전에 알려지지 않았으므로 작동하지 않습니다.
zwol
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.