Cung cấp tất cả lưu lượng truy cập thông qua OpenVPN cho một không gian tên mạng cụ thể


16

Tôi đang cố gắng thiết lập VPN (sử dụng OpenVPN) sao cho tất cả lưu lượng truy cập và chỉ lưu lượng truy cập đến / từ các quy trình cụ thể đi qua VPN; các quá trình khác nên tiếp tục sử dụng thiết bị vật lý trực tiếp. Theo hiểu biết của tôi thì cách để làm điều này trong Linux là với các không gian tên mạng.

Nếu tôi sử dụng OpenVPN bình thường (nghĩa là kênh tất cả lưu lượng truy cập từ máy khách thông qua VPN), nó hoạt động tốt. Cụ thể, tôi bắt đầu OpenVPN như thế này:

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

(Một phiên bản được định nghĩa lại của Destination.ovpn nằm ở cuối câu hỏi này.)

Tôi bị kẹt ở bước tiếp theo, viết các tập lệnh giới hạn thiết bị đường hầm vào không gian tên. Tôi đã thử:

  1. Đặt thiết bị đường hầm trực tiếp trong không gian tên với

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

    Các lệnh này thực thi thành công, nhưng lưu lượng được tạo bên trong không gian tên (ví dụ với ip netns exec tns0 traceroute -n 8.8.8.8) rơi vào lỗ đen.

  2. Giả định rằng " bạn chỉ có thể [chỉ] giao các giao diện Ethernet ảo (veth) cho một không gian tên mạng " (mà nếu đúng, sẽ nhận giải thưởng năm nay cho hầu hết các hạn chế API không cần thiết một cách lố bịch), tạo ra một cặp veth và cầu nối, và đặt một đầu của cặp veth vào không gian tên. Điều này thậm chí còn không đến mức giảm lưu lượng trên sàn: nó sẽ không cho phép tôi đặt đường hầm vào cầu! [EDIT: Điều này có vẻ là do chỉ các thiết bị nhấn có thể được đưa vào cầu. Không giống như việc không thể đặt các thiết bị tùy ý vào một không gian tên mạng, điều đó thực sự có ý nghĩa, với những cây cầu là một khái niệm lớp Ethernet; thật không may, nhà cung cấp VPN của tôi không hỗ trợ OpenVPN ở chế độ nhấn, vì vậy tôi cần một cách giải quyết.]

    # 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
    

Các kịch bản ở cuối câu hỏi này là dành cho phương pháp veth. Các kịch bản cho cách tiếp cận trực tiếp có thể được tìm thấy trong lịch sử chỉnh sửa. Các biến trong các tập lệnh dường như được sử dụng mà không đặt chúng trước được đặt trong môi trường bởi openvpnchương trình - vâng, nó cẩu thả và sử dụng tên viết thường.

Vui lòng cung cấp lời khuyên cụ thể về cách làm cho điều này để làm việc. Tôi đau đớn nhận ra rằng tôi đang lập trình bởi giáo phái vận chuyển hàng hóa ở đây - có ai đã viết tài liệu toàn diện cho công cụ này chưa? Tôi không thể tìm thấy bất kỳ - vì vậy đánh giá mã chung của các tập lệnh cũng được đánh giá cao.

Trong trường hợp nó quan trọng:

# 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

Hạt nhân được xây dựng bởi nhà cung cấp hosting ảo của tôi ( Linode ) và, mặc dù được biên soạn với CONFIG_MODULES=y, không có module thực tế - chỉ CONFIG_*bộ biến để mtheo /proc/config.gzđược CONFIG_XEN_TMEM, và tôi không thực sự rằng mô-đun (kernel được lưu trữ bên ngoài hệ thống tập tin của tôi; /lib/modulestrống rỗng và /proc/moduleschỉ ra rằng nó không được tải một cách kỳ diệu nào đó). Đoạn trích /proc/config.gzđược cung cấp theo yêu cầu, nhưng tôi không muốn dán toàn bộ ở đây.

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

đích.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>

Hãy bắt đầu với điều hiển nhiên: các thiết bị veth có được hỗ trợ không? các mô-đun hạt nhân (veth) được tải?
phản biện

@countmode grep veth /proc/modulesliệt kê không có gì, nhưng tôi không biết nếu đó là kết luận. Các phiên bản Linode không có kernel được cài đặt bên trong phân vùng HĐH, vì vậy tôi không chắc mình có thể tải một mô-đun bị thiếu hay không.
zwol

lsmodsản xuất bất kỳ đầu ra ở tất cả? Có một thư mục /lib/modules?
phản hồi

lsmod: command not found. Có một /lib/modules, nhưng nó không có bất kỳ mô-đun nào trong đó, chỉ là một loạt các thư mục per-kernel chứa modules.depcác tệp trống . Tôi sẽ tìm kiếm sự giúp đỡ cụ thể của Linode và tìm hiểu xem đó có phải là sự thật không.
zwol

hmm ... rất kỳ quặc Tôi không quen thuộc với Linode nhưng với tôi có vẻ như các thiết bị veth không được hỗ trợ.
phản ứng

Câu trả lời:


9

Bạn có thể bắt đầu liên kết OpenVPN bên trong một không gian tên và sau đó chạy mọi lệnh bạn muốn sử dụng liên kết OpenVPN đó bên trong không gian tên. Chi tiết về cách thực hiện (không phải công việc của tôi) tại đây:

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

Tôi đã thử nó và nó hoạt động; ý tưởng là cung cấp một tập lệnh tùy chỉnh để thực hiện các giai đoạn lên và lên của kết nối OpenVPN bên trong một không gian tên cụ thể thay vì toàn cầu. Tôi trích dẫn từ liên kết trên chỉ trong trường hợp nó sẽ ngoại tuyến trong tương lai:

Đầu tiên tạo tập lệnh --up cho OpenVPN. Kịch bản lệnh này sẽ tạo giao diện đường hầm VPN bên trong một không gian tên mạng được gọi là vpn, thay vì không gian tên mặc định.

$ 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

Sau đó khởi động OpenVPN và bảo nó sử dụng tập lệnh --up của chúng tôi thay vì thực thi ifconfig và tuyến đường.

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

Bây giờ bạn có thể bắt đầu các chương trình được tạo đường hầm như thế này:

ip netns exec vpn command

Điều hấp dẫn duy nhất là bạn cần phải root để gọi ip netns exec ...và có thể bạn không muốn ứng dụng của mình chạy bằng root. Giải pháp rất đơn giản:

sudo ip netns exec lệnh vpn sudo -u $ (whoami)

1
Xin Chào và Chào Mừng đến với trang! Chúng tôi khuyến khích người dùng ít nhất tóm tắt (nếu có thể) nội dung của các liên kết họ dán trong câu trả lời. Điều này giúp duy trì chất lượng câu trả lời trong trường hợp liên kết trở nên cũ (ví dụ: trang web không còn truy cập được). Vui lòng cải thiện câu trả lời của bạn bằng cách bao gồm các phần / hướng dẫn quan trọng nhất từ ​​bài viết được liên kết.
Erathiel

Điều này thật tuyệt nhưng bạn cần đặt các trích dẫn đơn xung quanh dấu phân cách di truyền mở để ngăn shell mở rộng tất cả các biến.
ewatt

7

Hóa ra bạn có thể đặt giao diện đường hầm vào không gian tên mạng. Toàn bộ vấn đề của tôi là do lỗi trong việc đưa ra giao diện:

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

Vấn đề là "phạm vi liên kết", mà tôi hiểu nhầm là chỉ ảnh hưởng đến định tuyến. Nó khiến kernel đặt địa chỉ nguồn của tất cả các gói được gửi vào đường hầm tới 0.0.0.0; có lẽ máy chủ OpenVPN sau đó sẽ loại bỏ chúng là không hợp lệ theo RFC1122; ngay cả khi nó không, đích đến rõ ràng là không thể trả lời.

Mọi thứ hoạt động chính xác trong trường hợp không có không gian tên mạng vì tập lệnh cấu hình mạng tích hợp của openvpn không mắc lỗi này. Và không có "phạm vi liên kết", kịch bản gốc của tôi cũng hoạt động.

(Làm thế nào mà tôi khám phá này, bạn yêu cầu? Bằng cách chạy stracevề quá trình openvpn, thiết lập để hexdump tất cả mọi thứ nó đọc từ mô tả đường hầm, và sau đó tự giải mã các tiêu đề gói tin.)


Bất kỳ cơ hội bạn có thể viết lên một hướng dẫn về điều này? Tôi đang cố gắng thiết lập một cái gì đó tương tự nhưng thật khó để nói phần nào trong câu hỏi của bạn là tốt để bắt đầu và đâu là con đường dẫn đến thất bại.
run 31/12/14

@tremby Tôi không có thời gian để làm điều đó trong tương lai gần, nhưng bạn có thể thấy github.com/zackw/tbbscraper/blob/master/scripts/openvpn-netns.c hữu ích.
zwol

Các bạn, tôi không chắc chương trình C 1100 dòng sẽ có ích. Làm thế nào về cấu hình cuối cùng, tập lệnh và câu thần chú đã hoàn thành công việc cho bạn? ... Hay đó là chương trình C thực hiện cuối cùng của bạn về điều này?
run 31/12/14

@tremby Vâng, chương trình C đó là triển khai cuối cùng của tôi. .
zwol

@tremby Trong cách khác, hãy xem "Tập lệnh được thực thi từ bên trong openvpn", bắt đầu từ github.com/zackw/tbbscraper/blob/master/scripts/ , để xem cách thiết lập và phá hủy không gian tên mạng; và yêu cầu thực tế của ứng dụng khách ovpn là tại github.com/zackw/tbbscraper/blob/master/scripts/ . Phần còn lại của mã có thể được coi là một triển khai shell nhỏ để làm cho các hoạt động đó ít mệt mỏi hơn để viết.
zwol

4

Lỗi khi cố gắng tạo các thiết bị veth là do thay đổi cách ipdiễn giải các đối số dòng lệnh.

Yêu cầu chính xác ipđể tạo một cặp thiết bị veth là

ip link add name veth0 type veth peer name veth1

( nameinstad của dev)

Bây giờ, làm thế nào để có được lưu lượng truy cập từ không gian tên đến đường hầm VPN? Vì bạn chỉ có các thiết bị điều chỉnh theo ý của bạn, "máy chủ" phải định tuyến. Tức là tạo cặp veth và đặt một cái vào không gian tên. Kết nối khác thông qua định tuyến đến đường hầm. Do đó, cho phép chuyển tiếp, và sau đó thêm các tuyến cần thiết.

Ví dụ, giả sử đó eth0là giao diện chính của bạn, tun0là giao diện đường hầm VPN của bạn và veth0/ veth1cặp giao diện veth1nằm trong không gian tên. Trong không gian tên bạn chỉ thêm một tuyến mặc định cho veth1.

Trên máy chủ bạn cần sử dụng định tuyến chính sách, xem ở đây chẳng hạn. Bạn cần gì để làm:

Thêm / nối một mục như

1   vpn

để /etc/iproute2/rt_tables. Bằng cách này, bạn có thể gọi bảng (chưa được tạo) theo tên.

Sau đó sử dụng các tuyên bố sau:

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

Tôi không thể thử ở đây với một thiết lập như của bạn, nhưng điều này sẽ làm chính xác những gì bạn muốn. Bạn có thể gia tăng rằng bằng các quy tắc lọc gói sao cho cả mạng vpn và "khách" không bị xáo trộn.

NB Di chuyển tun0vào không gian tên ở vị trí đầu tiên có vẻ như là điều đúng đắn. Nhưng giống như bạn, tôi đã không làm việc đó. Định tuyến chính sách trông giống như điều phải làm tiếp theo. Giải pháp của Mahendra có thể áp dụng nếu bạn biết các mạng phía sau VPN tất cả các ứng dụng khác sẽ không bao giờ truy cập vào các mạng đó. Nhưng điều kiện ban đầu của bạn ("tất cả lưu lượng truy cập và chỉ lưu lượng truy cập, đến / từ các quy trình cụ thể đi qua VPN") nghe như thể điều sau không thể được đảm bảo.


Cảm ơn, điều này giúp tôi tiến xa hơn một chút, nhưng giờ tôi bị kẹt ở phần "và sau đó bạn sử dụng một cây cầu để kết nối thiết bị veth với đường hầm" - vui lòng xem câu hỏi đã được sửa đổi.
zwol

Theo câu trả lời tôi vừa đăng, toàn bộ điều này dẫn đến một lỗi ngớ ngẩn trong kịch bản gốc của tôi - "phạm vi liên kết" không có nghĩa là tôi nghĩ nó có nghĩa gì. Nhưng tôi sẽ mang lại cho bạn tiền thưởng, bởi vì bạn đã nỗ lực rất nhiều để giúp tôi thử nhiều khả năng khác nhau và có lẽ tôi đã từ bỏ hoàn toàn nếu bạn không làm vậy.
zwol

Này Zack, cảm ơn rất nhiều. Không gian tên và định tuyến chính sách là một điều thú vị để nghiên cứu. Tôi sẽ không thực sự nỗ lực nhiều vào việc này nếu nó không thú vị.
phản ứng

0

Nếu các mạng mà bạn truy cập thông qua VPN được biết đến, bạn có thể chỉnh sửa bảng định tuyến của mình để đạt được những gì bạn muốn.

  1. Lưu ý lộ trình mặc định hiện tại của bạn.

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

  2. Thực thi VPN và điều này sẽ giới thiệu một mục định tuyến.

  3. Xóa tuyến mặc định hiện tại (được VPN thêm vào) trong đó tuyến mặc định trước đó là mục mặc định đầu tiên trong bảng.

    # 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. Thêm các tuyến tùy chỉnh vào các mạng trong VPN để định tuyến qua tun0.

    # ip route add <net1>/16 dev tun0

    # ip route add <net2>/24 dev tun0

  5. Thêm cả hai mục nhập máy chủ tên (trong độ phân giải) cũng như cho VPN và kết nối trực tiếp.

Bây giờ tất cả các kết nối net1 và net2 sẽ đi qua VPN và thiết lập lại sẽ đi trực tiếp (thông qua wlo1 trong ví dụ này).


Đáng tiếc, các mạng được truy cập qua VPN không được biết trước, vì vậy điều này sẽ không hoạt động đối với tôi.
zwol
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.