Author Archives: mine260309

自己搭建WireGuard给Android用

WireGuard这个VPN最近有点热门,搜了一下,大都在讲点对点VPN,Android上的配置基本上都用AzireVPN已经配置好的config,如何在自己的VPS搭建,并给自己的Android用,稍微摸索了一下,记录一下。

服务器端的配置

在VPS上的配置很简单,按照网上的主流的教程配置就好,比如说Linode上的文章写得很清楚了,不过其中漏掉了一点点东西(ipv4 forwarding),这里放一个完整的步骤。

安装

  • 依赖linux-headers
    sudo apt install linux-headers-$(uname -r)
    
  • 安装WireGuard
    sudo add-apt-repository ppa:wireguard/wireguard
    sudo apt-get install wireguard
    

配置

  • 创建key
    umask 077
    wg genkey | tee privatekey | wg pubkey > publickey
    
  • 编辑config文件/etc/wireguard/wg0.conf,具体见注释
    [Interface]
    Address = 192.168.2.1/24   # VPN server自己的地址,可以改成任意的内网地址
    Address = fd86:ea04:1115::1/64   # VPN server自己的ipv6的地址(可选)
    SaveConfig = true   # 设为true之后,每次重启服务(stop service时)都会自动保存config
    
    # 以下是重点: 当服务启动时,通过iptables配置wg0来的流量forward到eth0
    # 如果你的device不是eth0而是别的名字,把下面的eth0改成别的。
    # 当服务停止的时候,删除相关的iptables规则
    PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
    
    ListenPort = 10443   # 随便选一个空闲的端口
    PrivateKey = <private-key>   #在上一步里生成的privatekey的内容
    

这样我们就创建好了一个wg0的WireGurad网络的配置。

启用服务

  • 手动开启、关闭wg0的网络:
    wg-quick up wg0
    wg-quick down wg0
    
  • 通过systemd来启用这个service,这样系统重新启动时这个服务也会自动启动
    systemctl enable wg-quick@wg0
    

额外的配置

为了让VPN能正常工作,需要enable ipv4的ip_forward功能:

sysctl -w net.ipv4.ip_forward=1

这样,服务端一开始的配置就弄好了。

手机端的配置

接下来在Android上配置WireGuard。

安装

直接在PlayStore或者F-Droid上下载WireGuard。

配置

如下图
WireGuard-Android-Config

    • Name: 自己起个名字
    • 点击GENERATE,它会自动生成Private/Public Key
      点击Public key,它会把public key复制到剪贴版,之后要用到。
    • Addresses: 填跟服务器端的配置里同样网段的IP,比如说192.168.2.2/24
    • DNS: 我填了8.8.8.8,这个应该是可选的
    • 添加一个Peer,在Peer里:
      • Public key: 填服务器端生成的public key
      • Allowed IPs: 填0.0.0.0/0,允许所有IP(这个很重要,否则即使连上了VPN,也无法访问别的网站)
      • Endpoint: 填服务器的IP:端口(比如上面服务端的配置10443

这样手机端的配置也好了。

服务端添加client

再次回到服务器,添加这个手机的public key

# android-public-key填手机的WireGuard里生成的public key
sudo wg set wg0 peer <android-public-key> allowed-ips 192.168.2.2/24

这样,所有的配置都弄好了,在手机上打开WireGuard,启动刚才配置好的服务,enjoy the freedom

后话

总体来说,WireGuard的配置相比别的要简单多了,而且速度确实很快。
不过,有几个问题:

  1. 目前的Android的WireGuard的功能很有限,只有简单的Exclude apps来排除不需要用的app。如果加上了GFWList,应该会实用很多。
  2. 虽然没有看过code,但是从原理上来说,这个VPN应该还是很容易从协议层面被GFW识别,如果没有混淆,GFW只要愿意,很容易定点封IP。

综上,WireGuard适合喜欢尝鲜的朋友,作为日常的翻墙工具,还需要国人在目前的基础上增加工能。GFWList应该好办,能不能混淆,需要去看看白皮书了。。。

Share

做了一个查询上海居转户状态的小工具

去年下半年申请了居转户,据说最多要等1年半,所以偶尔会要去上海居转户的网站上查询一下状态。
既然是重复性的劳动,那就写个脚本来处理吧。

Short version

用Chrome抓包,分析一下网页的表单,然后用python的各种库发送请求、解析验证码,最后得到结果。

工具放在了github上,执行shjzh_username=$your_id_number shjzh_password=$your_password ./query.py -q,把your_id_number, your_password替换成自己的身份证号、密码,或者设置为环境变量即可。

结果大概长这样:

['例子', '123123199001011234', '受理通过']

Long version

首先通过Chrome把登录网站、查询的过程抓成har包,方便之后分析。
screenshot-chrome

整个过程包含:

  1. 第一次HTTP GET访问网站,得到cookie(即requests里的session)
  2. 发送一次HTTP GET请求验证码
    screenshot-chrome-captcha
  3. 发送一次HTTP POST把用户名、密码、验证码发送到网站
    screenshot-chrome-login
  4. 成功登陆后,发送一次HTTP POST点击”我接受”的按钮
    screenshot-chrome-accept
  5. 进入后续的页面,再发送一次HTTP GET点击”我的申报信息”的按钮
    screenshot-chrome-myinfo

这个过程本身很简单,这里只记录几个要注意的点:

  1. 用户名是经过处理的,具体是md5.jshex_md5()这个函数,把字符串作为hex值然后算md5就可以了;
  2. 密码是明文,通过HTTP发送的,政府的网站无力吐槽。。。
  3. 通过pytesseract解析验证码不一定每次都成功,如果失败,可以再试一次,之后可以在这个脚本的基础上加一个retry
  4. beautifulsoup解析网页,发现这里面table的格式实现是——太不规则了。目前只能hard code来得到最后的结果。所以如果网页稍微有点变化,这个脚本可能就需要更新。
  5. 这个网站开放时间不是全天,而是8:00至22:00。 而22:00至次日8:00是打不开的。。。

然后把这个脚本设个cron task定期跑一下,结果通过邮件发送给自己,就不再需要自己去网站上check啦!

最后,附上github的code: https://github.com/mine260309/shjzh_query

Share

记一个Nexus 6p解锁loop问题

用了两年多的Nexus 6p出现问题了。
表现为:启动到主屏的时候,输入正确的密码后,系统闪退,然后再次要求输入密码。
在Android Central里,有人报过类似的问题

adb log显示:

01-11 19:26:41.950 4549 5940 W System.err: java.security.UnrecoverableKeyException: Failed to obtain information about key
01-11 19:26:41.950 4549 5940 W System.err: at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(AndroidKeyStoreProvider.java:282)
01-11 19:26:41.950 4549 5940 W System.err: at android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:98)
01-11 19:26:41.950 4549 5940 W System.err: at java.security.KeyStore.getKey(KeyStore.java:1062)
01-11 19:26:41.950 4549 5940 W System.err: at com.android.server.locksettings.SyntheticPasswordCrypto.decryptBlob(SyntheticPasswordCrypto.java:120)
01-11 19:26:41.950 4549 5940 W System.err: at com.android.server.locksettings.SyntheticPasswordManager.decryptSPBlob(SyntheticPasswordManager.java:1040)
01-11 19:26:41.950 4549 5940 W System.err: at com.android.server.locksettings.SyntheticPasswordManager.unwrapSyntheticPasswordBlob(SyntheticPasswordManager.java:906)
01-11 19:26:41.950 4549 5940 W System.err: at com.android.server.locksettings.SyntheticPasswordManager.unwrapPasswordBasedSyntheticPassword(SyntheticPasswordManager.java:843)
01-11 19:26:41.950 4549 5940 W System.err: at com.android.server.locksettings.LockSettingsService.spBasedDoVerifyCredential(LockSettingsService.java:2105)
01-11 19:26:41.950 4549 5940 W System.err: at com.android.server.locksettings.LockSettingsService.doVerifyCredential(LockSettingsService.java:1553)
01-11 19:26:41.950 4549 5940 W System.err: at com.android.server.locksettings.LockSettingsService.checkCredential(LockSettingsService.java:1526)
01-11 19:26:41.950 4549 5940 W System.err: at com.android.internal.widget.ILockSettings$Stub.onTransact(ILockSettings.java:164)
01-11 19:26:41.950 4549 5940 W System.err: at android.os.Binder.execTransact(Binder.java:697)
01-11 19:26:41.950 4549 5940 W System.err: Caused by: android.security.KeyStoreException: Key not yet valid
01-11 19:26:41.950 4549 5940 W System.err: at android.security.KeyStore.getKeyStoreException(KeyStore.java:697)
01-11 19:26:41.950 4549 5940 W System.err: at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(AndroidKeyStoreProvider.java:283)
01-11 19:26:41.950 4549 5940 W System.err: ... 11 more
01-11 19:26:41.951 4549 5940 E JavaBinder: *** Uncaught remote exception! (Exceptions are not yet supported across processes.)
01-11 19:26:41.951 4549 5940 E JavaBinder: java.lang.RuntimeException: Failed to decrypt blob
01-11 19:26:41.951 4549 5940 E JavaBinder: at com.android.server.locksettings.SyntheticPasswordCrypto.decryptBlob(SyntheticPasswordCrypto.java:129)
01-11 19:26:41.951 4549 5940 E JavaBinder: at com.android.server.locksettings.SyntheticPasswordManager.decryptSPBlob(SyntheticPasswordManager.java:1040)
01-11 19:26:41.951 4549 5940 E JavaBinder: at com.android.server.locksettings.SyntheticPasswordManager.unwrapSyntheticPasswordBlob(SyntheticPasswordManager.java:906)
01-11 19:26:41.951 4549 5940 E JavaBinder: at com.android.server.locksettings.SyntheticPasswordManager.unwrapPasswordBasedSyntheticPassword(SyntheticPasswordManager.java:843)
01-11 19:26:41.951 4549 5940 E JavaBinder: at com.android.server.locksettings.LockSettingsService.spBasedDoVerifyCredential(LockSettingsService.java:2105)
01-11 19:26:41.951 4549 5940 E JavaBinder: at com.android.server.locksettings.LockSettingsService.doVerifyCredential(LockSettingsService.java:1553)
01-11 19:26:41.951 4549 5940 E JavaBinder: at com.android.server.locksettings.LockSettingsService.checkCredential(LockSettingsService.java:1526)
01-11 19:26:41.951 4549 5940 E JavaBinder: at com.android.internal.widget.ILockSettings$Stub.onTransact(ILockSettings.java:164)
01-11 19:26:41.951 4549 5940 E JavaBinder: at android.os.Binder.execTransact(Binder.java:697)
01-11 19:26:41.951 4549 5940 E JavaBinder: Caused by: java.security.UnrecoverableKeyException: Failed to obtain information about key
01-11 19:26:41.951 4549 5940 E JavaBinder: at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(AndroidKeyStoreProvider.java:282)
01-11 19:26:41.951 4549 5940 E JavaBinder: at android.security.keystore.AndroidKeyStoreSpi.engineGetKey(AndroidKeyStoreSpi.java:98)
01-11 19:26:41.951 4549 5940 E JavaBinder: at java.security.KeyStore.getKey(KeyStore.java:1062)
01-11 19:26:41.951 4549 5940 E JavaBinder: at com.android.server.locksettings.SyntheticPasswordCrypto.decryptBlob(SyntheticPasswordCrypto.java:120)
01-11 19:26:41.951 4549 5940 E JavaBinder: ... 8 more
01-11 19:26:41.951 4549 5940 E JavaBinder: Caused by: android.security.KeyStoreException: Key not yet valid
01-11 19:26:41.951 4549 5940 E JavaBinder: at android.security.KeyStore.getKeyStoreException(KeyStore.java:697)
01-11 19:26:41.951 4549 5940 E JavaBinder: at android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(AndroidKeyStoreProvider.java:283)
01-11 19:26:41.951 4549 5940 E JavaBinder: ... 11 more

根据crash的log看,应该是系统在从keystore里读key的时候,认为时间无效:

01-11 19:26:41.950 4549 5940 W System.err: Caused by: android.security.KeyStoreException: Key not yet valid

相关的code,大概是在https://android.googlesource.com/platform/system/security/+/master/keystore/keystore_keymaster_enforcement.h#41

bool activation_date_valid(uint64_t activation_date) const override {
  time_t now = time(NULL);
  if (now == static_cast<time_t>(-1)) {
    // Failed to obtain current time -- fail safe: activation_date hasn't yet occurred.
    return false;
  } else if (now < 0) {
    // Current time is prior to start of the epoch -- activation_date hasn't yet occurred.
    return false;
  }
  // time(NULL) returns seconds since epoch and "loses" milliseconds information. We thus add
  // 999 ms to now_date to avoid a situation where an activation_date of up to 999ms in the
  // past may still be considered to still be in the future. This can be removed once
  // time(NULL) is replaced by a millisecond-precise source of time.
  uint64_t now_date = static_cast<uint64_t>(now) * 1000 + 999;
  return now_date >= activation_date;
}

因为有root,可以尝试设置系统时间,结果还是没用,即使设置到2250年,它还是报同样的错。所以我也不知道到底问题是不是在这里了。

然后注意到hwclock不太对:

angler:/sys/class/rtc/rtc0 # date "030100002018"
Thu Mar 1 00:00:00 CST 2018
angler:/sys/class/rtc/rtc0 # hwclock -l
Mon Jan 12 09:21:32 1970 0.000000 seconds
angler:/sys/class/rtc/rtc0 # hwclock -w
hwclock: ioctl 4024700a: Invalid argument

高通的这个RTC莫非根本不能写,只能读?

angler:/sys/class/rtc/rtc0 # ls -l
total 0
-r--r--r-- 1 root root 4096 2018-03-01 00:01 date
-r--r--r-- 1 root root 4096 2018-03-01 00:01 dev
lrwxrwxrwx 1 root root 0 2018-03-01 00:01 device -> ../../../qpnp-rtc-8
-r--r--r-- 1 root root 4096 2018-03-01 00:01 hctosys
-rw-r--r-- 1 root root 4096 2018-03-01 00:01 max_user_freq
-r--r--r-- 1 root root 4096 2018-03-01 00:01 name
drwxr-xr-x 2 root root 0 2018-03-01 00:01 power
-r--r--r-- 1 root root 4096 2018-03-01 00:01 since_epoch
lrwxrwxrwx 1 root root 0 2018-03-01 00:01 subsystem -> ../../../../../class/rtc
-r--r--r-- 1 root root 4096 2018-03-01 00:01 time
-rw-r--r-- 1 root root 4096 2018-03-01 00:01 uevent

最后我怀疑是time(NULL);返回了-1,但是没有log,也不知道是不是这样。

总之,这个问题,好像只能factory reset了。
如果再出问题,能只编个AOSP,加上log看一下了。

Share

记一个systemd的NFS boot问题

前段时间遇到个问题,一个基于systemd的系统在从NFS启动的时候,user space启动到一半,网络掉了,于是NFS hang住,系统也不可用了。

从log上看,很明显是systemd-networkd启动之后,IP被release掉,又没有拿到新的IP,于是网络就断了。

由于systemd-networkd是通过读取配置文件来初始化网络的,一开始猜测,是不是它先release掉IP,然后再读配置文件,导致这样的问题的。
然而从code上看,它倒是先读了config,然后再初始化网络的。

先是在SO上提了个问题,没人回答。
然后经过一番Google,发现了CriticalConnection这个config,作用是:

When true, the connection will never be torn down even if the DHCP lese expires.

虽然它在说DHCP的connection不会被释放,但是经过测试,其实它也适用于static ip。

也就是说,只要在config文件里加上了这么一段:

[DHCP]
CriticalConnection=true

这个interface的网络在systemd-networkd启动的时候就不会被断开,这样NFS mount就不会hang住,系统也就能正常启动了。(顺便自己回答了SO上的问题。。。)

Share

在同一根网线上支持电信宽带和4K IPTV

由于弱电箱到客厅只有一根线,一个网口,之前写过一篇文章介绍如何在一根网线上承载两路网络

这个方式有两个缺点:

  1. 带宽只有100M,而现在电信的宽带已经用上200M了,浪费了一半的带宽;
  2. 用了4K IPTV的话,IPTV的机顶盒在一根网线上需要同时获得VLAN的IP和公网IP才能正常工作。

第一个缺点还能忍,第二个问题,如果还用之前的方式,IPTV就彻底不能用了。
所以要解决这个问题。

原因

首先看一下,4K IPTV是如何在一根网线上同时获得VLAN和公网的IP的。

  • 光猫作为路由器,IPTV直接接LAN2;
  • 光猫的设置里,VLAN和LAN2之前有数据绑定;

所以LAN2既能直接上网(有公网IP),也连接了电信的VLAN,而IPTV通过LAN2就可以同时获得这两个IP了。

解决

目标还是和之前一样,我要让光猫只当猫,由我自己的路由器来拨号,4K IPTV只能接到我自己的路由器上,让路由器来支持4K IPTV。所以网络变了一点点:
家庭网络设备

所以路由器相比之前,要多做一件事:把电信IPTV的VLAN桥接到我的内网来,并把VLAN里DHCP的消息也桥接进来。

而我的R6300v2的官方固件是不支持VLAN的,所以只能刷其它固件了,我选了梅林。

以下同详细的设置:

  1. 光猫的配置和以前差不多,桥接Internet(为了路由器拨号),桥接VLAN(为了IPTV)
    光猫VLAN设置
  2. 路由器端,拨号设置跟以前一样,多加一个dnsmasq的配置:
    # cat /jffs/scripts/dnsmasq.postconf
    #!/bin/sh
    CONFIG=$1
    source /usr/sbin/helper.sh
    
    # 通过 pc_append把dhcp相关的设置append到 /tmp/etc/dnsmasq.conf 文件里
    # 这样IPTV就能获取到VLAN的IP了
    pc_append "dhcp-option-force=125,00:00:00:00:1a:02:06:48:47:57:2d:43:54:03:04:5a:58:48:4e:0a:02:20:00:0b:02:00:55:0d:02:00:2e
    dhcp-option=15
    dhcp-option=28
    dhcp-option=60,00:00:01:06:68:75:61:71:69:6E:02:0A:48:47:55:34:32:31:4E:20:76:33:03:0A:48:47:55:34:32:31:4E:20:76:33:04:10:32:30:30:2E:55:59:59:2E:30:2E:41:2E:30:2E:53:48:05:04:00:01:00:50" /tmp/etc/dnsmasq.conf
    
    # 配置VLAN
    robocfg vlan 51 ports "0t 1t 2t 3t 4t 8t" vlan 85 ports "0t 1t 2t 3t 4t 8t"
    

以上配置好之后,打开IPTV,它能在路由器上同时获取到公网和VLAN IP,可以正常工作了。
而且,这样连接方式,路由器和光猫之间是通过千兆网络连接的,公网的带宽也不受影响,喜大普奔~

注:以上配置完全参考[教程] 上海电信完美解决4K IPTV一劳永逸增强板,其中光猫的配置的截图也来自于这个链接。
非常感谢这个贴子!

Share