How can I share my unrooted Android device's internet connection with my Linux machine when ordinary tethering is not an option?
I have a Linux computer, but no internet connection available for it. I also have an unrooted Android device with an internet connection and a USB cable to connect it to the computer. How can I share the Android's internet connection with the Linux machine if ordinary tethering is not an option?
Solution 1:
tl;dr
Create a default route to a dummy network interface and then use redsocks
along with ssh -D
over an adb
-forwarded port to treat an SSH server on your Android device as a Socks5 proxy.
Overview
At a high level, without worrying about being too technically precise, the final communication structure that you want is something like this:
Program ↔
iptables
↔redsocks
↔ssh
↔adb
↔sshd
↔ Internet
So, for instance, when a program on your Linux machine generates some outgoing network traffic, it sends that traffic off as normal, but iptables
rules intercept that outgoing traffic and deliver it to redsocks
. redsocks
converts that traffic to messages for a Socks5 proxy and sends them through an SSH tunnel. The SSH connection, in turn, is on a port forwarded over a USB connection by adb
. On the Android's end, an SSH server (like dropbear
through SimpleSSHD) acts as the Socks5 server and puts the traffic out on the internet. The process for incoming traffic is similar, but in reverse.
Choices
As part of this setup you will need to choose four port numbers, all of which should be unprivileged (1024
or higher) and not already in use on your Linux machine:
- A
redsocks
local port (for these instructions, I use the default,12345
) - A
redsocks
DNS port (for these instructions, I use the default,5300
) - A tunneled Socks5 port (for these instructions, I use one common choice,
8123
) - An SSH port (for these instructions, I use SimpleSSHD's default,
2222
)
You will also need to choose a DNS server that your Android will have access to (for these instructions, I will use the ever popular 8.8.8.8
) and an IP address to assign to your Linux machine, preferably a NAT address to avoid conflicts (I will use 192.168.123.123
).
If you choose different port numbers, a different DNS server, and/or a different IP address for the Linux machine, just substitute those values throughout the following instructions.
Android Setup
If it is not already enabled, enable USB debugging on your Android device.
From the Play Store or wherever else you get your apps, install an SSH server that supports Socks5 and that can be configured to listen on an unprivileged port (thereby not needing root access). Choose the app carefully, keeping in mind that a malicious app could see and manipulate your network traffic just as much as any tethering app. In these instructions I will use SimpleSSHD, which is free, has all of the required features, and is overall very nice to work with. But, just to be clear, I cannot vouch for its security.
Open the app and configure your SSH server, making sure to have it listen on the unprivileged SSH port you chose above and, if applicable, expect the username that you use on your Linux machine. At the time of writing, SimpleSSHD has appropriate default settings if you are using port 2222
.
Linux Machine Software Installation
If you do not already have adb
on your Linux computer, open a browser on your Android, and navigate to the download page for the Android SDK Platform Tools. Click on the link to download the platform tools for Linux and read and agree to the license when prompted.
Connect the Android device to your Linux machine by USB cable, transfer the downloaded archive to your Linux machine, and unarchive the platform tools into a convenient folder. Make sure that the Android is unlocked and run the command
/.../platform-tools/adb devices
where /.../platform-tools/adb
is the full path to your download of adb
. If you have not connected to your Android from your Linux machine with adb
before, the Android device will ask you to grant access and offer to remember the Linux machine. Check the box to remember the computer and grant access. Remembering the computer is necessary now because the scripts below expect instant access and will not wait for you to manually grant permission each time you connect.
On your Linux machine, attempt (knowing it will fail) to install your distribution's redsocks
package using you usual package manager. For example:
sudo apt install redsocks
The command will error out because the package manager has no internet access, but it should print out the URLs for the packages that it attempted to download. Visit each of these URLs in a web browser on your Android to download the packages, and transfer them to your Linux machine. Then, on your Linux machine, open and install them in the order that they were listed in the error messages.
Full Transparent Proxy Setup
Once the packages are installed, edit your system's redsocks
configuration, which is normally at /etc/redsocks.conf
and owned by root
. (The changes here assume that you are not already using redsocks
for something else. If you are, then I assume you know what you are doing and can adjust all of the following instructions appropriately.) In the base
section, the default settings should be sufficient. In a lone redsocks
section, make sure that you have the following settings (but with the port numbers you chose) and that all other settings are commented out:
local_ip = 127.0.0.1;
local_port = 12345;
ip = 127.0.0.1;
port = 8123;
type = socks5;
Similarly, in a lone redudp
section, make sure that you have the following settings (with the port numbers and DNS server you chose) and that all other settings are commented out:
local_ip = 127.0.0.1;
local_port = 12345;
ip = 127.0.0.1;
port = 8123;
dest_ip = 8.8.8.8;
dest_port = 53;
udp_timeout = 30;
udp_timeout_stream = 180;
Reboot your Linux machine to apply these changes.
Now create a Bash script containing the text below. Change the quoted path on line 7 to the full path to your downloaded copy of adb
, and remember to substitute in the port numbers, DNS server, and Linux machine IP that you chose. Also, this script assumes that you are using Network Manager, so if you are not, replace the nmcli
commands with appropriate equivalents for your machine.
#! /usr/bin/env bash
# Tell Bash to exit immediately if any error occurs.
set -e
# Set the full path to adb.
export ADB='/.../platform-tools/adb'
echo 'Connecting to Android device.'
# Ensure that any previous ADB server is stopped.
"$ADB" kill-server
# Start a new ADB server and connect to any devices attached by USB.
"$ADB" devices
# Forward the SSH port, port 2222.
"$ADB" forward tcp:2222 tcp:2222
echo 'Configuring network (administrator password required).'
# Ensure that the kernel will let us create a dummy network interface.
# (We use a dummy network interface to convince Linux that it has an internet
# connection; otherwise it will not even try to send out network traffic.)
sudo modprobe dummy
# Create a dummy network interface if it does not already exist.
ip link show redsocks || (sudo ip link add redsocks type dummy && echo 'Created device "redsocks".')
# Assign an IP address to the dummy network interface if it does not already
# have one.
[ -n "$(ip addr show label redsocks:0)" ] || sudo ip addr add 192.168.123.123/24 brd + dev redsocks label redsocks:0
# Bring up the connection.
nmcli connection up redsocks
# Set a DNS server for the connection.
nmcli connection modify redsocks ipv4.dns 8.8.8.8
# Make the connection the default route if there is not already a default.
[ -n "$(ip route show default)" ] || sudo ip route add default via 192.168.100.1 dev redsocks
# Save the current packet filter rules so that we can restore them later.
IPTABLES_SAVE=$(mktemp)
sudo iptables-save > "$IPTABLES_SAVE"
# Clean out any existing jumps to a REDSOCKS chain.
sudo iptables -t nat -D OUTPUT -p udp -j REDSOCKS >& /dev/null || true
sudo iptables -t nat -D OUTPUT -p tcp -j REDSOCKS >& /dev/null || true
# Clean out any existing REDSOCKS chain.
sudo iptables -t nat -F REDSOCKS >& /dev/null || true
sudo iptables -t nat -X REDSOCKS >& /dev/null || true
# Create a new chain which we will program to mean "route through redsocks".
sudo iptables -t nat -N REDSOCKS
# Tell the new chain to ignore traffic to local, NAT, and reserved addresses.
sudo iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 10.0.0.0/8 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 100.64.0.0/10 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 169.254.0.0/16 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 172.16.0.0/12 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 192.168.0.0/16 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 198.18.0.0/15 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 224.0.0.0/4 -j RETURN
sudo iptables -t nat -A REDSOCKS -d 240.0.0.0/4 -j RETURN
# Tell the new chain to send all other traffic to the redsocks local port.
sudo iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 12345
# Apply the REDSOCKS chain to all outgoing UDP traffic.
sudo iptables -t nat -A OUTPUT -p udp -j REDSOCKS
# Apply the REDSOCKS chain to all outgoing TCP traffic.
sudo iptables -t nat -A OUTPUT -p tcp -j REDSOCKS
# Tunnel the Socks5 port, port 8123, over SSH.
echo
echo 'Tethering will activate once you enter the SSH password.'
echo 'After that, press control-C to end tethering.'
echo 'Creating tunnel (Android SSH server password required).'
ssh localhost -p 2222 -D 8123 -N
# Restore the original packet filter rules.
echo
echo 'Reverting network changes.'
sudo iptables-restore "$IPTABLES_SAVE"
rm "$IPTABLES_SAVE"
# Delete the dummy network interface.
sudo ip link delete redsocks
echo 'Finished.'
Mark this script as executable. You are now ready to share the internet connection.
Usage
Whenever you want to share your Android's internet connection with your Linux machine, connect it to your Linux machine by USB cable, start its SSH server, run the Bash script on your Linux machine, enter your administrator password when prompted, and enter your password for the SSH server when prompted. (If you are using SimpleSSHD, note that it generates a new password for every connection, so you will have to read that password off of the Android device.) If everything goes well, you will then have internet access, and the script will sit waiting for you to end the connection. (If you find that you cannot resolve hosts with DNS, you may have to wait one or two minutes for your system to catch up with the new setup.)
While connected, be a good citizen. One reason that mobile providers try to discourage or limit tethering is that users get carried away with the amount of data they are moving, unfairly affecting others. Because providers' usual mechanisms for monitoring tethering may not detect this setup, it is up to you to be self-policing and adhere to your contract. A good practice is to keep an eye on the data usage by the app that acts as your SSH server so that you can ensure that it does not exceed the tethering limit set by your mobile provider.
When you are ready to disconnect, press control-c to interrupt the SSH connection. The script will then run its cleanup commands to undo all of its changes to your network configuration (it may need your administrator password again if you've been connected long enough) and report that it is finished.