IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    一个系统,两套网络

    依云发表于 2016-04-29 17:23:00
    love 0

    本文来自依云's Blog,转载请注明。

    创建并配置一个新的网络命名空间

    公司 VPN 用的网段是 192.168.1.0/24,而我家里的网络,就像大多数人的一样,也是 192.168.1.0/24。网段冲突了。当然啦,隔离得相当好的 lxc 可以解决这个问题。可是它隔离得太好了,我可不想再多维护一套环境。那么,就只隔离网络好了!

    正好之前看到 iproute2 套件可以管理网络命名空间。那么,就用它啦。

    首先,创建一个名为 vpn 的网络命名空间:

    ip netns add vpn
    

    现在如果进去看的话,会发现里边只有一个孤零零的 lo 网络接口,所以里边是一片与世隔绝的黑暗。我没有其它的网络接口给它用。桥接当然是可以尝试的,只是需要更改已有的网络配置。所以还是用另外的方案吧。弄根网线来,把里边和外边连接起来好了。

    创建一对 veth 接口。这就是我们的「网线」~

    ip link add vpn0 type veth peer name vpn1
    

    一端留外边,另一端移到 vpn 里边去:

    ip link set vpn1 netns vpn
    

    接好网线的两端:

    ip link set vpn0 up
    ip netns exec vpn ip link set vpn1 up
    

    好了,现在两个网络之间可以通信了~当然,只是链路层。为了使用 TCP/IP,我们得分配 IP 地址。在外边的这个就不需要 IP 地址了,因为我要把它接到一个网桥上,网桥自己有 IP 的。

    brctl addif br0 vpn0
    

    这里 br0 是我已有的网桥(给 lxc 用的那个)。如果你没有的话,就自己按以下方法弄一个。我用的是 bridge-utils 里的 brctl 命令。iproute2 也可以做的。

    brctl addbr br0
    ifconfig br0 192.168.57.1
    iptables -t nat -A POSTROUTING -s 192.168.57.1/24 -j MASQUERADE
    

    最后的 iptables 命令是做 NAT 啦。当然内核的 IPv4 转发功能还要开启的。

    vpn0 这端弄好了,再给 vpn1 分配 IP,并设置相关路由表项:

    ip netns exec vpn ip address add dev vpn1 192.168.57.101/24
    ip netns exec vpn ip route add default via 192.168.57.1
    

    现在就可以在里边连 VPN 啦~

    ip netns exec vpn openvpn ...
    

    当然也可以去里边开个 zsh 用

    ip netns exec vpn zsh
    

    不过要注意如果跑 GUI,或者连接 D-Bus 的话,需要手动把相关环境变量移进去。虽然是独立的网络命名空间,但是因为共享了文件系统,所以虽然看不到在监听的 UNIX 套接字,但是连起来还是没有问题的。X 和 D-Bus 都可以良好工作的。

    export DISPLAY=:0 LANG=zh_CN.UTF-8 LANGUAGE=zh_CN:zh_TW
    export GTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx XMODIFIERS=@im=fcitx
    export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
    # 如果在 tmux 里的话
    export TMUX=1
    

    再加上 mount 命名空间吧

    我这里在里边得用 IP 地址,因为我的 DNS 是 127.0.0.1,在里边当然是用不了的。怎么办呢?我不想改 DNS 服务器地址,因为里外的文件系统是共享的。那就再加上 mount 命名空间吧,这样就可以 bind mount 一个修改过的文件到 /etc/resolv.conf 上了。

    其实呢,ip netns 是通过把网络命名空间(/proc/<pid>/ns/net)bind mount 到文件来实现命名空间的持久化的(不然使用这个命名空间的进程都退出,该命名空间就销毁了)。其文件位于 /run/netns 下。对于 mount 命名空间我们可以手工这么做:

    mkdir -p /var/run/ns
    mount --bind /var/run/ns /var/run/ns
    # 命名空间只能 bind mount 到 private 挂载的文件系统上
    mount --make-private /var/run/ns
    # 随意找个普通文件就行。一般是用空文件;我这样可以省一个文件~
    cp /etc/resolv.conf /var/run/ns
    

    然后用 unshare 建立新的 mount 命名空间,并进入之前的 vpn 网络命名空间(当然用 nsenter 进入也是可以的):

    unshare --mount=/var/run/ns/resolv.conf ip netns exec vpn zsh
    

    创建了之后就可以用 nsenter 进去玩儿了:

    nsenter --mount=/var/run/ns/resolv.conf --net=/var/run/netns/vpn zsh
    

    可以在里边各种 bind mount,不会影响外边的哦:

    # 在新的命名空间里边
    mount --bind /var/run/ns/resolv.conf /etc/resolv.conf
    vim /etc/resolv.conf
    

    组合更多的命名空间

    当然也可以组合更多种的命名空间的。我还试过 pid 命名空间,不过 pid 命名空间比较特殊:它在 fork 后才生效,当 init 进程(pid=1 的进程)退出之后所有位于此 pid 命名空间的进程都会被杀死,并且再也进不去了。所以不是很好玩的啦。

    创建与进入的命令如下:

    unshare --mount-proc -f --pid=/var/run/ns/pid --mount=/var/run/ns/resolv.conf nsenter --net=/var/run/netns/vpn su - lilydjwg
    # 进入时用 -t 选项,或者重新 bind mount 文件(不然新进程会在原 pid 命名空间)
    nsenter -t 9416 --mount=/var/run/mountns/resolv.conf --net=/var/run/netns/vpn --pid su - lilydjwg
    


沪ICP备19023445号-2号
友情链接