Tinkering with the Jiwei Router on New Year's Eve: SSH Reverse Proxy
Today is New Year's Eve, so let me take a moment to wish everyone a happy New Year's Eve and a happy New Year! May you all be promoted to "learning god" in the coming year. ^_^
I've spent the last couple of days tinkering with the router at home. Normally there are only my parents at home, so to save money we just relay off the neighbor's network to get online. We used to use a Xiaomi Mini router, but Xiaomi Mini has a lot of feature restrictions in relay mode, and I didn't want to flash third-party firmware (since that would lose the app control functionality, which isn't very convenient), so I just switched to a Jiwei Router 3. Jiwei still keeps most of its features in relay mode (I think that's how it should be — I really don't understand the logic behind Xiaomi Mini losing so many features once you switch to relay mode).
As someone who likes to tinker, once a new router is in hand there's always a lot to configure. Jiwei's router is based on OpenWrt, so it has a lot of room for hacking. First you need to complete the relay setup and get online, which is simple enough that I won't go into it. Next is getting SSH access, which Jiwei calls "applying for Developer Mode," or basically root access (it feels like Jiwei wants to be the "Apple of routers," but in this day and age, I doubt Apple's original growth model could really work anymore). This step isn't hard either, though once you apply for it you lose your warranty on the Jiwei router (again, I don't understand the logic there).
This post mainly covers how to install Python on OpenWrt (Jiwei router), and how to set up an SSH reverse proxy (to achieve NAT traversal into the home network). more
Installing Python onto the SD card
Once you have SSH access, there's a lot you can do. The first thing is to install Python, which opens up a lot of possibilities. This took a bit of effort. First you need to modify OpenWrt's package sources by editing the following three files:
/etc/opkg.conf /etc/opkg.d/opkg-secure.conf /etc/opkg.d/opkg-fast.conf
Comment out the first line
src/gz barrier_breaker https://upgrade.hiwifi.com/upgrade_file/ralink-HC5861/0.9017.1.11380s/packages
and then add the following at the bottom:
src/gz barrier_breaker http://downloads.openwrt.org.cn/PandoraBox/ralink/mt7620_old/packages/
After deleting the cache files, you can update the sources and install packages.
rm /var/opkg-lists/barrier_breaker
opkg update
Note that the Jiwei Router 3's ROM doesn't have much free space left, so it's best to install Python onto the SD card. To do this, first check the SD card's mount point with df -h (mine was /tmp/storage/mmcblk0p2), and then modify the same three files again:
/etc/opkg.conf /etc/opkg.d/opkg-secure.conf /etc/opkg.d/opkg-fast.conf
Under the line
dest root /
add the following:
dest usb /tmp/storage/mmcblk0p2
In addition, you need to modify the system environment variables by editing /etc/profile, like this (note the part in red):
export PATH=/tmp/storage/mmcblk0p2/usr/bin:/tmp/storage/mmcblk0p2/usr/sbin:/bin:/sbin:/usr/bin:/usr/sbin:$HIWIFI_CRYPTDA
export LD_LIBRARY_PATH=/tmp/storage/mmcblk0p2/lib:/tmp/storage/mmcblk0p2/usr/lib:$HIWIFI_CRYPTDATA/lib:$HIWIFI_CRYPTDATA
At this point, you can use
opkg install python -d usb
to install Python onto the SD card. Then install setuptools to get easy_install, followed by pip and the requests library. At this point, my Python environment setup is complete.
Setting up the SSH reverse proxy
Next, I wanted to find a way to punch through the home NAT, so that after I go back to school I can still manage the router at home. There are many ways to do this, such as ngrok (I haven't tried it, but from the tutorials it looks like the setup is a bit troublesome), n2n (this is P2P-based, which I really like, and it works on OpenWrt too — I got it working on my school's router, but it failed to run on the Jiwei router, so I had to give up on it), Peanut Hull's intranet edition (Jiwei has a plugin for Peanut Hull's intranet version, which seemed decent, but the free version has too many restrictions), and SSH reverse proxy. Since I wasn't satisfied with the first three, SSH reverse proxy was the only option left.
The principle behind an SSH reverse proxy is essentially SSH used in reverse — the direction is exactly opposite to a normal SSH login:
Normal SSH:
My computer ----> home router ----> my VPS
Reverse proxy:
My computer ----> your VPS ----> home router
The implementation is simple — it's just one line of code, and it works on almost any platform (as long as it supports SSH). Of course, there's quite a bit of preparation involved.
To use an SSH reverse proxy, you need your own VPS with a public IP. Mine is on Alibaba Cloud, at the student price of 9.9 RMB/month — quite a bargain. First, set things up on the VPS by editing /etc/ssh/sshd_config and adding:
GatewayPorts yes
Then restart SSH with /etc/init.d/ssh restart. After that, on the Jiwei router's OpenWrt, you just need to run the following command to establish a reverse tunnel to the VPS:
ssh -Nfg -R 11111:192.168.199.1:1022 root@1.1.1.1
This is basically the same as a normal SSH login, where 1.1.1.1 is the VPS's public IP, 192.168.199.1 is the Jiwei router's management IP, and 1022 is the router's SSH port (since I only need to SSH into the router). Once the login succeeds, accessing 1.1.1.1:11111 from anywhere is equivalent to accessing the router's 192.168.199.1:1022. For example, you could try ssh root@1.1.1.1 -p 11111 to see whether you can log into the router. (You can also check whether the port proxy was successfully established on the VPS with netstat -nlp|grep sshd.) As for what the parameters in the command above mean, you can check ssh -h.
At this point, the problem is basically solved, but there are a few remaining issues. First, SSH requires you to interactively type in the password each time. If you want passwordless login, you need to generate a key locally and upload it to the VPS (i.e., the private key–public key mechanism). Specifically, first generate a public key with the following code (this is the built-in method on OpenWrt; on a regular Linux system you'd normally use ssh-keygen):
dropbearkey -t rsa -f /etc/dropbear/id_rsa
dropbearkey -y -f /etc/dropbear/id_rsa | grep ssh-rsa > /tmp/id_rsa.pub
Then add the contents of /tmp/id_rsa.pub to ~/.ssh/authorized_keys on the VPS server (create the file if it doesn't exist yet), and then log in via
ssh -i /etc/dropbear/id_rsa -Nfg -R 11111:192.168.199.1:1022 root@1.1.1.1
and you'll find that you no longer need to enter a password.
Second, SSH itself isn't very stable — if there's no response for a long time, it will disconnect automatically. One way to solve this is to use autossh instead of the built-in ssh client (opkg install autossh -d usb), which is an SSH client with built-in monitoring. The autossh command is similar:
autossh -i /etc/dropbear/id_rsa -M 11112 -Nfg -R 11111:192.168.199.1:1022 1.1.1.1
The principle here is to add an extra port 11112 to monitor the connectivity of port 11111, and automatically reconnect if the connection drops.
Third, how to make this start automatically with the router. The router could lose power and restart at any time, so this needs to be added to the startup items — which is simple enough, just add it to /etc/rc.local. But there are two small issues: (1) My router connects to the internet via relay, so it's quite possible that when this script runs, the network connection hasn't been established yet. If there's no network connection, there's no point trying to SSH out, so we need to check whether the network is connected before running the script (of course, you could just sleep for a sufficiently long time before running it, but that's not very reliable). My method for checking network connectivity is simple: I set up a test.html on my own website whose content is just the string "pass." By visiting this page, if I get back "pass," I know the network is connected. (2) If the tunnel was successfully established once, and then the router restarts, the corresponding tunnel entry is still cached on the VPS. So the next time you run the code above, it will by default use the cached tunnel and will still report success — but if you then try ssh root@1.1.1.1 -p 11111 it will fail (which is when the VPS cache actually gets cleared). So after establishing the SSH tunnel, you need to test it once to check whether it actually works, and re-establish it if not. Since SSH itself runs in a blocking manner, we can take advantage of that.
The code is as follows:
#!/bin/sh
local_ip=`ifconfig br-lan|grep 'inet addr'|awk '{print $2}'|awk -F: '{print $2}'`
vps_ip=1.1.1.1
while true
do
wget http://kexue.fm/test.html
a=`cat test.html`
rm test.html
if [ $a = 'pass' ]
then
autossh -i /etc/dropbear/id_rsa -M 11112 -Nfg -R 11111:$local_ip:1022 $vps_ip
ssh -i /etc/dropbear/id_rsa -N $vps_ip -p 11111
fi
sleep 10
done
This integrates LAN IP detection, network connectivity checking, availability checking, and automatic reconnection all together. Save this code as run.sh, put it in the root directory, run chmod +x run.sh to give it execute permission, and then add the following before exit 0 in /etc/rc.local:
source /etc/profile
/run.sh
The first line is very important, because autossh and the other tools I installed live on the SD card, and they need the system environment variables to be loaded properly in order to work. Since it's not guaranteed that the environment variables will already be loaded when this script runs, it's best to import them manually, to make sure everything runs correctly.
And with that, I finally got everything working. For convenience, I also bound one of my subdomains to the VPS, giving me a pretty solid NAT-traversal setup — one that basically beats Peanut Hull's intranet edition hands down.
Reference Links
https://yxz.me/archives/881.html
http://blog.csdn.net/jk110333/article/details/11920163
https://blog.phpgao.com/ssh-reverse-tunnel.html
http://www.cage.tk/2015/01/22/openwrt-use-ssh-connect-access/
Translated automatically with claude-sonnet-5; all equations are reproduced verbatim from the source. Copyright remains with the original author.
