Total Pageviews

Sunday, 4 March 2018

VRouter


一个基于 Virtualbox 和 openwrt 构建的项目, 旨在实现 macOS / Windows 平台上的透明代理.
VRouter 在后台运行一个 openwrt 的虚拟机, 通过更改系统的默认路由, 将所有系统数据包转发到虚拟机上. 依托 openwrt 的 iptables 进行数据包的处理.

如何使用

前提条件:
  1. 为了能在服务端进行 DNS 查询, 请确保 ss-server 开启了 UDP 转发.
  2. 为了能确保虚拟机可以联网, 确保上游路由器开启了 DHCP.
  3. 为了避免频繁在弹窗里输入密码,请设置免密码切换网关

自定义黑白名单

  • 每个域名/IP/IP段一行, 注释行以#开头
  • 域名
    • 域名可以是裸域名如 example.com, 将会匹配该域下的所有子域名如 a.example.comb.example.com 等等
    • 也可以填写子域名如 a.example.com, 如此不会匹配到 b.example.com
  • IP
    • 可以是独立的IP, 如 123.123.123.123
    • 也可以是IP段, 如 123.123.123.0/8 将会匹配 123.123.123.0~123.123.123.255 之间的IP地址. 123.123.0.0/16将匹配 123.123.0.0 ~ 123.123.255.255之间的IP地址

项目的意义

为什么需要透明代理?

使用 Shadowsocks 桌面客户端可以很方便地进行非透明代理, 但是仍然有以下的不足
  • 每个需要代理的软件都要逐一设置
  • 命令行软件虽然可以用 export http_proxy 的方式使用代理, 但有时并不管用. 需要进一步折腾 Proxifier 或者 Privoxy
  • 有些软件并不支持设置代理, 如 macOS 系统自带的 Mail APP
  • 鲜有支持 UDP 的软件

为什么不在路由器上设置透明代理?

强烈推荐在路由器设置透明代理. 但是, 在路由器透明代理虽然解决了客户端的不足, 仍然有其局限性.
  • 如果路由器性能不足, 使用 kcptun 等软件时, 负载会非常高. 而且速度比在桌面端运行 kcptun 慢很多
  • 路由器只能在固定地点使用

为什么不用 TUN/TAP 虚拟网络接口?

TUN/TAP 是一个很优雅的方案, 不需要依赖 VirtualBox. Windows 上有很好的 TAP 透明代理方案 SSTap, Linux 上也有诸如 kone 这样的项目. 遗憾的是 kone 的上游项目 water 目前对 macOS 的支持有限. 因此:
  • 在目前来看, 在 macOS 上用轻量的 OpenWRT 实现透明代理, 仍然是一个实用的方案.
  • 相对新的项目, OpenWRT 和 VirtualBox 的组合更稳定.
  • 在 OpenWRT 上可以根据情况随时更换代理软件, 适应性更强.

用虚拟机会不会有点杀鸡用牛刀?

大材小用确实有点委屈了虚拟机. 幸运的是 openwrt 非常轻量, 官网提供的镜像不足 5 MB, 转化为 virtualbox 虚拟机磁盘文件, 并在虚拟机上安装必要的软件后, 磁盘空间占用不足 30 MB. 虚拟机在 macOS 上的内存占用在 150MB 以内, 在 Windows 上的内存占用仅仅 20 MB 左右. CPU 占用率则跟网络流量正相关, 没有流量的情况下, Windows 版本 CPU 占用率接近 0% ( 2010 年的 Thinkpad ), macOS则是在 5% 以下 ( 2014 年的中端 13 寸 Macbook ).

优缺点

优点:
  • 可以实现 TCP / UDP 的透明代理
  • 性能强
  • 便携性强
  • 切换方便
  • 免费, 开源
缺点:
  • 依赖 VirtualBox
  • 无法服务局域网内的其他设备
  • UI 用 Electron 进行封装, 体积比虚拟机大太多
  • [ Windows ] Windows 改了默认路由后, Edge 浏览器和 UWP 应用无法连接网络. 其他应用则不受影响

拓扑结构

topology
from https://github.com/icymind/VRouter
https://github.com/overcache/VRouter
--------

虚拟openwrt-随身携带的梯子


“有哪些东西你用了之后就回不去了?”
kcptun 是我的答案之一.
广州电信 50M 光纤 , 纯粹用 shadowsocks 的话 , 看看资本主义的网页速度勉勉强强 , 视频什么就别想了. 但是搭配上 kcptun , 上网高峰期放 1080P 都没问题. 唯一的问题是 kcptun 性能消耗有点大, 在网件 WNDR4300 上部署, 一看视频路由器的 CPU 占用率就会飙到 100% , 而且速度比桌面端的 kcptun 差几倍.
解决方法挺多, 土豪出门右转上狗东买个旗舰路由, Geek 在路由下再挂个树莓派跑 kcptun , 实用者在桌面端用 cow 做智能代理.
智能代理其实也不错, 但是透明代理更佳, 谁用谁知道. 在透明代理下, 终端是完全感觉不到墙得存在的, 墙是什么? 能吃吗? 不连家里 openwrt 的 Wi-Fi 时, 我就用智能代理, 但是有些终端软件它不走系统代理, 有些软件只走 http 代理, 在这些情况下, 只能一个个去明确代理. 以前用 shadowsocksX 时, 系统自带的邮件应用就没法连接 Google, npm/docker/brew也需要指定代理.
不(mei)想(qian) 买 surge 之类的软件, 于是鼓捣了这么一个东西: virualbox 里的 openwrt, 用这个虚拟路由器接管宿主的所有流量, 在虚拟路由里做透明代理. 这样就相当绑了一个 openwrt 路由在电脑上, 无论接入什么网络, 都可以访问资本主义网站. 而且自由度比 surge 之类的要高, 毕竟直接扔了一个 linux 给你啊喂. 想用什么工具链在 openwrt 里搞就行了, 还不用担心这个路由器的性能不够. 当然比起土豪的实体路由还是逊色一点, 毕竟虚拟路由只能自己使用(或者在笔记本上再接一个实体路由, 供其他设备连接?).
让 virtualbox 里的 openwrt 接管宿主的流量, 需要给虚拟机配两张网卡:
  1. 一张网卡设置为桥接. 虚拟机通过这张网卡从上级路由获取 IP , 摇身变为和宿主同等的网络设备.
  2. 另一张设为 Host-only, 宿主的网络数据从这张网卡流向虚拟路由.
设好网络后, 用 route 命令把宿主的默认网关改为虚拟路由器, 在把 DNS 也设置为虚拟路由器的地址. That’s all.
本来想试试 docker , 但是 osx 的 docker 引擎是虚拟机, container 是跑在这个虚拟机里, 貌似比较麻烦或者说不可能实现. 所以用了 virtualbox , 仅提供个思路, 抛砖引玉, 有大神出个 docker 的思路就最吼了!

安装

根据 wiki 的说明 , 先把 openwrt 安装到虚拟机里 :
  • 下载 openwrt-15.05.1-x86
  • 解压: gunzip openwrt-15.05.1-x86-generic-combined-ext4.img.gz
  • 将解压出来的镜像转化为 virtualbox 的原始格式: VBoxManage convertfromraw --format VDI openwrt-15.05.1-x86-generic-combined-ext4.img openwrt.vdi
  • 用 virtualbox 建一个虚拟机 openwrt :
    Name: openwrt
    Type: Linux
    Memory size: 512 MB
    Use an existing virtual hard disk file: openwrt.vdi
  • 打开 virtualbox 的全局设置,在网络选项卡里新建一个 Host-Only Networks :
    IPv4 Address: 192.168.100.2
    IPv4 Network Mask: 255.255.255.0
    DHCP Server: Disable
  • 打开虚拟机 openwrt 的设置,在网络选项卡里对 「适配器1」进行设置 :
    Enable Network Adapter
    Attached to: Host-only Adapter
    Name: vboxnet0「这里选择在之前步骤新建的 Host- Only Networks 」
    另外再添加一张「适配器2」:
    Enable Network Adapter
    Attached to: Bridged Adapter
    Name: en0: Wi-Fi(AirPort)「这里选择宿主上网的网卡」

配置虚拟机网络

  • 启动并进入 openwrt , 对网络进行设置 vim /etc/config/network :
    config interface 'loopback'
    option ifname 'lo'
    option proto 'static'
    option ipaddr '127.0.0.1'
    option netmask '255.0.0.0'
    config interface 'lan'
    option ifname 'eth0'
    option type 'bridge'
    option proto 'static'
    option ipaddr '192.168.100.1' 「将 lan 地址改为 192.168.100.1 」
    option netmask '255.255.255.0'
    option ip6assign '60'
    config interface 'wan'
    option ifname 'eth1'
    option proto 'dhcp'
    config interface 'wan6'
    option ifname 'eth1'
    option proto 'dhcpv6'
    config globals 'globals'
    option ula_prefix 'fde9:2442:ec95::/48'
  • 在 openwrt 上执行: /etc/init.d/network restart 重启 openwrt 的网络, 在 openwrt 上执行 ifconfig 查看网络,应该可以看到 eth0 的 IP 是我们设定的 192.168.100.1 ,宿主这时可以通过 ssh 登录到 虚拟 openwrt . 而 eth1 网卡已经从上级路由器获取到了对应的 IP 地址, 在 openwrt 上执行 ping baidu.com , 不通的话再检查下哪里没做对.
  • 将宿主上执行 :sudo route change default 192.168.100.1 将默认路由设置为 openwrt , 同时在宿主上执行 : sudo networksetup -setdnsservers Wi-Fi 192.168.100.1 将 DNS 设置为 openwrt . 此时所有宿主的流量都被 openwrt 接管, DNS 也在 openwrt 上进行查询. 试着用宿主浏览网页, 网络不通的话再检查下:
    • openwrt 是否已经启动, 并且在 openwrt 里已经可以上网
    • 在宿主上执行 ifconfig vboxnet0 检查 vboxnet0 网卡是否已经准备好, 宿主的数据都是经过这个网卡流向虚拟路由器.
    • 在宿主上执行 netstat -nr | grep default 查看默认路由是否指向了虚拟 openwrt
  • 为了方便之后的操作, 在虚拟路由上执行 passwd 更改密码, 然后将宿主的公钥加入 openwrt 的 /etc/dropbear/authorized_keys 中以便免密码登录. 一些常用命令:
    • 用 VBoxManage startvm --type headless openwrt , 可以在后台运行 openwrt
    • 需要关闭 openwrt 时, 运行命令: VBoxManage controlvm openwrt poweroff
    • 在宿主上的 $HOME/.ssh/config 文件内配置 openwrt , 通过 ssh openwrt 登录比起 sshroot@id-address 更方便 :
      Host openwrt
      HostName 192.168.100.1
      User root

透明代理

在 openwrt 上设置透明代理的方法有很多 , 个人认为最优雅的组合是 :
  • 在 gfvvlist 的基础上 , 自己维护一个需要走代理的域名列表
  • 用 dnsmasq 架设 DNS 服务 , 列表内的域名转发到 shadowsocks 的 ss-tunnel , 让 vps 进行 DNS 查询 , 避免污染 . 域名外的域名直接交给上级路由查询 , 可以获得 cdn 加速 .
  • dnsmasq 得到列表中域名的 IP 地址后 , 加入一个 ipset 集合中 . 于是我们就得到了一个需要走代理的 IP 地址集合 .
  • 用 iptables 的 ipset 功能 , 目标地址在指定集合内的数据包 , 转发到 shadowsocks 的 ss-redir , 科学上网.
  • 当然 , shadowsocks 的数据全部交给 kcptun 进行加速 .
利用以上这个组合 , 就可以根据域名区分数据走向 . 以下仅仅是记录过程 , 了解更多原理请点 : 那些从墙上学会的知识

准备相关软件

  • 下载 shadowsocks-libev for openwrt 并在宿主上执行 scp shadowsocks-libev*.ipk openrt:~ 传送到 openwrt 上 . 在 openwrt 上执行 opkg update && opkg install shadowsocks-libev_2.5.6-1_x86.ipk 进行安装 .
  • 下载 kcptun for linux 并把客户端可执行文件移动到 openwrt 上的系统路径 , 在 openwrt 上执行 : mv client_linux_x386 /usr/bin/kcptun
  • 因为只有 dnsmasq-full 才支持 ipset,卸载 dnsmasq 并安装 dnsmasq-full : opkg remove dnsmasq && opkg install dnsmasq-full
  • 安装 ipset : opkg install ipset

配置 shadowsocks

  • /etc/ss-local.json
    {
    "server": "127.0.0.1",
    "server_port": 1070,
    "local_address": "0.0.0.0",
    "local_port": 1080,
    "password": "paaasssword",
    "timeout": 300,
    "method": "chacha20",
    "fast_open": true
    }
  • /etc/ss-tunnel.json
    {
    "server": "server-ip",
    "server_port": 8989,
    "local_address": "0.0.0.0",
    "local_port": 5353,
    "password": "paaasssword",
    "timeout": 300,
    "method": "chacha20",
    "fast_open": true
    }
  • /etc/init.d/ss
    #!/bin/sh /etc/rc.common
    START=105
    SERVICE_USE_PID=1
    SERVICE_WRITE_PID=1
    SERVICE_DAEMONIZE=1
    start() {
    service_start /usr/bin/ss-tunnel -L 8.8.8.8:53 -c /etc/ss-tunnel.json -u
    service_start /usr/bin/ss-redir -c /etc/ss-local.json -u
    }
    stop() {
    service_stop /usr/bin/ss-tunnel
    service_stop /usr/bin/ss-redir
    }
  • 开机启动 : /etc/init.d/ss enable , 手动开启/关闭 : /etc/init.d/ss start|stop

配置kcptun

  • /etc/kcptun-client.json
    {
    "remoteaddr": "server-ip:server-port",
    "localaddr": ":1070",
    "mode": "fast3",
    "crypt": "server-crypt",
    "nocomp": true | false (must be same as server side)
    }
  • /etc/init.d/kcptun
    #!/bin/sh /etc/rc.common
    # Copyright (C) 2006-2011 OpenWrt.org
    START=70
    SERVICE_USE_PID=1
    SERVICE_WRITE_PID=1
    SERVICE_DAEMONIZE=1
    start() {
    service_start /usr/bin/kcptun -c /etc/kcptun-client.json --log /var/log/kcptun.log
    }
    stop() {
    service_stop /usr/bin/kcptun
    }
  • 随开机启动 : /etc/init.d/kcptun enable , 手动启动/停止 : /etc/init.d/kcptun start|stop

配置 iptables

  • /etc/firewall.user
    #redir packages from openwrt itself
    iptables -t nat -A OUTPUT -p tcp -m set --match-set SS dst -j REDIRECT --to-port 1080
    iptables -t nat -A OUTPUT -p udp -m set --match-set SS dst -j REDIRECT --to-port 1080
    # redir packages from client
    iptables -t nat -A PREROUTING -p tcp -m set --match-set SS dst -j REDIRECT --to-port 1080
    iptables -t nat -A PREROUTING -p udp -m set --match-set SS dst -j REDIRECT --to-port 1080
    # new ipset: SS
    ipset -N SS hash:ip
  • 使 iptables 规则生效 : /etc/init.d/firewall restart

配置 dnsmasq

  • /etc/dnsmasq.conf
    conf-dir=/etc/dnsmasq.d/
    min-cache-ttl=3600
    cache-size=9999
  • /etc/dnsmasq.d/over-gfw.conf , 可以用这个 domains-to-dnsmasq 脚本 自动生成 :
    server=/google.com/127.0.0.1#5353
    ipset=/google.com/SS
    server=/twitter/127.0.0.1#5353
    ipset=/twitter/SS
    ...
    ...
  • 重启 dnsmasq , 使配置文件生效 : /etc/init.d/dnsmasq restart
全部配置后之后就可以上资本主义网站了 . 但是当宿主的外部网络发生变化 , 比如连接另一个 Wi-Fi 时 , 我们自定义的 iptables 规则会被系统重写而丢失 , kcptun 在没有网络的情况下也会崩溃退出 , 因此可以加一个脚本监控这些状态 , 随时恢复到可以正常使用的状态:
  • /root/keep-kt-ss-alive
    #!/bin/sh
    kcptun_pid=$(pgrep kcptun)
    ss_redir_pid=$(pgrep ss-redir)
    ss_tunnel_pid=$(pgrep ss-tunnel)
    iptable_rules=$(iptables -S -t nat | grep 1080)
    # KCPTUN
    if [ -z "$kcptun_pid" ];then
    /etc/init.d/kcptun restart
    fi
    # SHADOWSOCKS
    if [ -z "$ss_redir_pid" ] || [ -z "$ss_tunnel_pid" ];then
    /etc/init.d/ss restart
    fi
    # IPTABLES
    if [ -z "$iptable_rules" ];then
    /etc/init.d/firewall restart
    fi
    别忘了给脚本添加可执行权限 : chmod +x /root/keep-kt-ss-alive
  • 新建计划任务 , 每分钟执行 keep-kt-ss-alive 脚本 : crontab -e
    * * * * * /root/keep-kt-ss-alive
  • 还需要让系统的 cron 服务随开机启动 , 我们的计划任务才能执行: /etc/init.d/cron enable && /etc/init.d/cron start

自动化

随宿主开机启动 openwrt

  • cat ~/Library/LaunchAgents/com.icymind.openwrt.sh
    #!/bin/bash
    # iCyMind <icyminnd@gmail.com>
    function stopvm()
    {
    if [ -n "$(/usr/local/bin/VBoxManage list runningvms | grep openwrt)" ];then
    echo "$(/bin/date): shutdown openwrt"
    /usr/local/bin/VBoxManage controlvm openwrt poweroff
    fi
    exit 0
    }
    function startvm()
    {
    if [ -z "$(/usr/local/bin/VBoxManage list runningvms | grep openwrt)" ];then
    echo "$(/bin/date): startup openwrt"
    /usr/local/bin/VBoxHeadless -s openwrt &
    wait $!
    fi
    }
    trap stopvm HUP KILL TERM
    startvm;
  • cat ~/Library/LaunchAgents/com.icymind.openwrt.plist
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
    <key>Label</key>
    <string>com.icymind.openwrt</string>
    <key>ProgramArguments</key>
    <array>
    <string>/Users/simon/Dropbox/Script/com.icymind.vboxopenwrt.sh</string>
    </array>
    <key>KeepAlive</key>
    <true/>
    <key>RunAtLoad</key>
    <true/>
    <key>UserName</key>
    <string>simon</string>
    <key>WorkingDirectory</key>
    <string>/Users/simon/</string>
    </dict>
    </plist>
  • 加载 launchd 任务 : launchctl bootstrap gui/501 ~/Library/LaunchAgents/com.icymind.openwrt.plist . 其中 501 是用户的 uid , 可以通过执行 id -u 获知 .

宿主开机时/网络变化时 , 自动更改网关和 DNS

  • cat ~/Dropbox/Script/com.icymind.network.sh
    这个脚本会在 vboxnet0 网卡激活后 , 将宿主的默认网关/ DNS 指向虚拟机 . 同时重启 openwrt 的网络 , 让它重新从上游路由器获取 IP 地址.
    #!/bin/bash
    # make sure your had set nopasswd for route/networksetup command.
    INTERFACE="Wi-Fi"
    GATEWAY="192.168.100.1"
    USER="root"
    # wait for vboxnet0 available
    # vboxnet0 active after virtual machine startup. If change route table before vboxnet0 active, your network will be unreachable
    while true; do
    ifconfig vboxnet0
    if [ $? -eq 0 ]; then
    break
    fi
    sleep 5
    done
    # restart openwrt network to renew ip from upper dhcp
    ssh $USER@$GATEWAY "/etc/init.d/network restart && sleep 4 && /etc/init.d/kcptun restart && /etc/init.d/firewall restart > /dev/null 2>&1"
    # change default route.
    sudo route change default $GATEWAY
    sudo networksetup -setdnsservers $INTERFACE $GATEWAY
  • cat ~/Library/LaunchAgents/com.icymind.network.plist
    在 OSX 上 , 每次开机或者网络环境发生变化时:
    • /etc/resolv.conf
    • /Library/Preferences/SystemConfiguration/NetworkInterfaces.plist
    • /Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist
      这三个文件都会发生变化 , 写个 Agent 监控这几个文件 , 当发生改变时自动执行更改网关的脚本.
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
      <key>Label</key>
      <string>com.icymind.network</string>
      <key>ProgramArguments</key>
      <array>
      <string>/Users/simon/Dropbox/Script/com.icymind.network.sh</string>
      </array>
      <key>WatchPaths</key>
      <array>
      <string>/etc/resolv.conf</string>
      <string>/Library/Preferences/SystemConfiguration/NetworkInterfaces.plist</string>
      <string>/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist</string>
      </array>
      <key>RunAtLoad</key>
      <true/>
      </dict>
      </plist>
  • 同理用 launchctl bootstrap gui/501 ~/Library/LaunchAgents/com.icymind.network.plist加载并执行 .

log –verbose

  • 开始用的是 64 位的 openwrt 镜像 , 装了两遍虚拟路由都不能从上级路由器获取到 IP , 于是改用 32 位的镜像.
  • 更改网关和 DNS 需要 sudo route 和 sudo networksetup , 为了能让脚本顺利执行 , 需要在 /etc/sudoers 中添加:
    simon ALL=(ALL) NOPASSWD: /usr/sbin/networksetup, /sbin/route
    其中 simon 是我的用户名
  • 重启 openwrt 的网络需要登录到路由器执行 , 为了让脚本能顺利执行 , 需要开启密钥登录:
    • 如果宿主没有密钥对的话 , 用 ssh-keygen 生成 , 公钥在宿主的 ~/.ssh/id_rsa.pub
    • 将宿主公钥的内容复制到 openwrt 的 /etc/dropbear/authorized_keys 文件内即可.
from https://icymind.com/virtual-openwrt
----------------------------------

VRouter: 一个虚拟路由器, 旨在实现 OS X/macOS 上的透明代理

解决的需求


OS X 上没有 linux 的 iptables, 无法直接将流量转发给本地的端口. 所有应用想走代理, 必须要先进行设置, 问题在于:
  • 有些应用不支持设置代理
  • 命令行软件虽然可以用 export http_proxy 的方式使用代理, 但有时并不管用. 需要进一步折腾 Proxifier 或者 Privoxy
所以为了实现透明代理, 通常有以下选择:
  • 用 surge 之类的软件
  • 在路由器设置代理
用 surge 最方便, 但是不便宜. 在路由器设置:
  • 一来受限于路由器的性能, 运行 kcptun 之类的软件资源吃紧
  • 二来路由器通常固定, 离开家 /公司需要使用代理时不方便
VRouter 是另一种透明代理的思路, 把路由器变成虚拟机, 接管系统流量, 它有以下特点:
  • 性能不受局限
  • 安装在系统上, 没有便携性的问题
  • 可配置性强, 毕竟是个 linux 发行版
缺点是:
  • 需要先安装 virtualbox
  • 无法服务局域网的其他设备

一些截图


 from http://archive.is/qrGSl
https://www.v2ex.com/t/371583
---------------

相关帖子:
http://briteming.blogspot.com/2018/03/kone.html
http://briteming.blogspot.com/2018/03/water.html

No comments:

Post a Comment