Tinkering with HiWiFi on New Year's Eve: SSH Reverse Proxy

By 苏剑林 | February 07, 2016

Happy Year of the Monkey!

Today is New Year's Eve. I would like to briefly wish everyone a Happy New Year's Eve and a Happy New Year! I hope everyone becomes a "study god" in the coming year. ^_^

I've spent the last couple of days tinkering with the router at home. Usually, only my parents are home, so to save money, we just bridge the neighbor's network to access the internet. Originally, we used a Xiaomi Router mini, but its functionality is extremely limited in relay mode, and I didn't want to flash third-party firmware (because I'd lose the app control features, which is inconvenient). So, I simply swapped it for a HiWiFi 3 (JeeWiFi). The HiWiFi retains most of its functions even in relay mode (which I think is how it should be; I don't understand the logic behind Xiaomi mini losing so many features after entering relay mode).

As a tinkerer, once a new router arrives, there's a lot to configure. HiWiFi is based on OpenWrt, so it has high playability. First, complete the relay and get online—this is simple and won't be detailed here. Next is obtaining SSH permissions, which HiWiFi calls "applying for Developer Mode," or root (it feels like HiWiFi wants to be the Apple of routers, but in this day and age, that kind of development model is hard to sustain). This step isn't difficult either, though applying will void the HiWiFi warranty (I don't understand the logic behind that either).

This article primarily introduces how to install Python on OpenWrt (HiWiFi) and how to establish an SSH reverse proxy (to achieve intranet penetration).

Installing Python to SD card

Once you have SSH permissions, you can do many things. The first thing to do is install Python, which opens up many possibilities. This took a bit of effort. First, you need to modify the OpenWrt software 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

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 software:

rm /var/opkg-lists/barrier_breaker
opkg update

Note that the HiWiFi 3 ROM has very little remaining space, so it's best to install Python on the SD card. To do this, first check the SD card mount point using df -h (mine is /tmp/storage/mmcblk0p2). Then, modify the same three files again:

/etc/opkg.conf /etc/opkg.d/opkg-secure.conf /etc/opkg.d/opkg-fast.conf

Below the line:

dest root /

Add:

dest usb /tmp/storage/mmcblk0p2

Additionally, you need to modify the system environment variables by editing /etc/profile, as follows (notice the highlighted parts):

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

Now, you can use the following command to install Python to the SD card:

opkg install python -d usb

Then install setuptools to get easy_install, followed by pip and the requests library. At this point, my Python environment configuration is complete.

Configuring SSH Reverse Proxy

Next, I wanted to find a way to penetrate the intranet so that I could manage the home router from school. There are many methods for this, such as ngrok (I haven't tried it, but looking at tutorials, the configuration seems slightly cumbersome), n2n (this is P2P-based, which I like, and it works on OpenWrt; I successfully configured it on my school router, but it failed on the HiWiFi, so I gave up), and PeanutHull Intranet version (HiWiFi has a PeanutHull plugin, which is okay, but the free version is too restrictive). With the first three being unsatisfactory, SSH reverse proxy was the remaining choice.

The principle of SSH reverse proxy is the inverse use of SSH, exactly the opposite order of a normal SSH login:

Normal SSH: My Computer ----> Intranet Router ----> My VPS
Reverse Proxy: My Computer ----> Your VPS ----> Intranet Router

Implementation is very simple—just one line of code—and it works on almost all platforms (as long as they support SSH). Of course, the preparation takes some work.

To use an SSH reverse proxy, you need your own VPS with a public IP. I use an Alibaba Cloud server, which is very affordable at 9.9 RMB/month for students. First, configure the VPS by modifying /etc/ssh/sshd_config to include:

GatewayPorts yes

Then restart SSH with /etc/init.d/ssh restart. Next, on the HiWiFi OpenWrt, establish the reverse tunnel to the VPS using the following code:

ssh -Nfg -R 11111:192.168.199.1:1022 root@1.1.1.1

This is just like a normal SSH login, where 1.1.1.1 is the VPS public IP, 192.168.199.1 is the HiWiFi management IP, and 1022 is the HiWiFi SSH port (since I only want to SSH into the router). Once the login is successful, accessing 1.1.1.1:11111 from anywhere is equivalent to accessing 192.168.199.1:1022 on the router. For example, you can try ssh root@1.1.1.1 -p 11111 to see if you can log into the router. (You can also run netstat -nlp|grep sshd on the VPS to see if the port proxy was established successfully.) The meaning of the parameters above can be found via ssh -h.

At this point, the problem is basically solved, but there are a few issues. First, SSH requires manual password entry interactively. For passwordless login, you need to generate a key locally and upload it to the VPS (the private-public key mechanism). The specific step is to generate the public key first (this is the built-in method on OpenWrt; a standard Linux system should 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

Next, add the content of /tmp/id_rsa.pub to the ~/.ssh/authorized_keys file on the VPS server (create it if it doesn't exist). Then, log in using:

ssh -i /etc/dropbear/id_rsa -Nfg -R 11111:192.168.199.1:1022 root@1.1.1.1

You will find that no password is required.

Second, SSH itself isn't very stable; it will automatically disconnect after a long period of inactivity. One way to solve this is to use autossh instead of the built-in ssh (opkg install autossh -d usb). autossh is an SSH client with monitoring capabilities. The code for autossh is similar:

autossh -i /etc/dropbear/id_rsa -M 11112 -Nfg -R 11111:192.168.199.1:1022 1.1.1.1

Its principle is adding an additional port 11112 to monitor the connectivity status of port 11111; if it disconnects, it automatically reconnects.

Third, how to start with the router. The router might restart at any time due to power loss, so the command needs to be added to the startup items. This is also simple: just add it to /etc/rc.local. However, there are two minor problems: (1) My router accesses the internet via relay, so it's likely that when the script runs, the network hasn't connected yet. Connecting to SSH without internet is pointless, so I need to check if the network is up before executing (I could also use sleep to wait long enough, but that's not very reliable). My method for checking network connectivity is simple: I created a test.html on my own website with the content pass; if accessing this page returns pass, the connection is up. (2) If a tunnel was successfully established and then the router restarts, the corresponding tunnel might still be cached on the VPS. Thus, the next time the code runs, it might default to the cached tunnel, showing success while actually failing when you try to ssh root@1.1.1.1 -p 11111 (this action would then clear the VPS cache). Therefore, after establishing the SSH tunnel, I need to test if it's usable; if not, I need to re-establish it. Since SSH itself runs blocking, we can utilize 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 script integrates LAN IP detection, network connectivity detection, usability checks, and automatic reconnection. Save this code as run.sh, place it in the root directory, and run chmod +x run.sh to grant execution rights. Then, in /etc/rc.local before exit 0, add:

source /etc/profile
/run.sh &

The first line is very important because my autossh and other tools are installed on the SD card and require the system environment variables to be loaded. When this script runs at boot, the environment variables might not be loaded yet, so it's best to import them manually to ensure everything runs correctly.

Finally, everything is configured. For easy access, I even bound one of my subdomains to the VPS. This creates a relatively perfect intranet penetration platform, which essentially beats the PeanutHull intranet version.

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/