Connect to HKUβs VPN from a Docker container β then use the tunnel as a local proxy so only the apps you choose go through HKUβs network.
The problem: HKUβs official Cisco AnyConnect client routes all your traffic through the VPN. That means slower speeds, privacy trade-offs, and broken workflows β when you need HKU resources (HKU Library academic resources, in-campus remote desktops) and sites like Claude (Code) and ChatGPT (Currently are not available in both Hong Kong and mainland China, and require additional proxy to bypass the access restrictions) at the same time.
This solution: Run the VPN inside a lightweight container and expose
it as SOCKS5 / HTTP proxies on localhost. You decide per-app whether
to use the VPN. Everything else stays on your normal connection.
TL;DR β Run
hkuvpn, type your 6-digit OTP, done. SOCKS5 on127.0.0.1:1080, HTTP on127.0.0.1:1088.
Typical pain without this project:
6152).With docker-vpn:
127.0.0.1:6152) as-is
for ChatGPT / Claude (Code).hkuvpn in Docker to expose local HKU proxies (127.0.0.1:1080
/ 127.0.0.1:1088).Before you start, make sure you have:
| # | Requirement | Why |
|---|---|---|
| 1 | An HKU account with VPN access | The service connects to HKUβs VPN endpoint |
| 2 | Microsoft Authenticator on your phone, set up for your HKU account | Youβll type a 6-digit one-time code each time you connect |
| 3 | Your HKU Portal PIN (the static password you use to log in to HKU Portal β not the OTP) | The launcher sends it automatically so you only have to type the OTP |
| 4 | Docker installed on your computer | The VPN runs inside a Docker container β see Step 1 |
| 5 | A terminal (Terminal.app, iTerm2, Windows Terminal, etc.) | Youβll run a few commands to set things up and connect |
New to Docker? Docker lets you run a small, isolated Linux environment (βcontainerβ) on your computer. You donβt need to know how it works β just install it and the launcher script handles the rest.
You only need to do these steps once. After that, connecting is a single command.
Pick your operating system:
We recommend Colima β a lightweight Docker runtime that runs in the background. Install it with Homebrew:
brew install colima docker
Start it once to make sure it works:
colima start
Don't have Homebrew? Install it first:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Alternative: Docker Desktop for Mac also works. If you use Docker Desktop, remove the Colima health-check block from the launcher function (Step 4) β it's only needed for Colima.
Install Docker Engine using your distro's package manager. For example, on Ubuntu / Debian:
sudo apt update
sudo apt install docker.io
sudo systemctl enable --now docker
Then add yourself to the docker group so you don't need sudo every
time:
sudo usermod -aG docker $USER
Log out and back in (or run newgrp docker) for the group change to
take effect.
Verify it works:
docker run --rm hello-world
See the official Docker docs for other distros.
localhost:1080 / localhost:1088 inside WSL2
automatically forward to the Windows host, so your Windows apps can
use them too.Note: Windows support is community-tested. If you run into issues, please open an issue.
Clone this repository and build the Docker image:
git clone https://github.com/rqhu1995/docker-vpn.git ~/docker-vpn
cd ~/docker-vpn
docker build -t local/vpn .
This downloads a small Alpine Linux base image and installs the VPN tools inside it. It only needs internet access this one time.
Whatβs happening:
docker buildreads theDockerfilein this repo and creates a local image calledlocal/vpn. It installsopenconnect(the VPN client),pproxy(the proxy server), and a few helper tools. The resulting image is ~80 MB.
Create a private directory for your VPN config:
mkdir -p ~/.vpn
a) Copy and edit the config template:
cp ~/docker-vpn/examples/hku.env.example ~/.vpn/hku.env
Open ~/.vpn/hku.env in any text editor and change youruid to your
actual HKU UID:
# Students: uid@connect.hku.hk
# Staff: uid@hku.hk
HKU_USER=youruid@connect.hku.hk
Save and close the file. Leave the other settings at their defaults for now.
b) Save your Portal PIN:
printf '%s' 'YOUR_PORTAL_PIN' > ~/.vpn/hku.pass
chmod 600 ~/.vpn/hku.pass
Replace YOUR_PORTAL_PIN with your actual HKU Portal password (the
static one, not the 6-digit OTP).
Why
printfinstead ofecho?echoadds a hidden newline character at the end of the file, which would make your password wrong.printf '%s'writes exactly what you give it.Verify itβs correct: Run
wc -c ~/.vpn/hku.passβ the number should equal the length of your PIN (e.g. an 8-character PIN β8).
The launcher is a shell function that handles Docker, certificates, and credentials for you. Add it to your shell profile:
For Zsh (default on macOS):
cat ~/docker-vpn/examples/hkuvpn.zsh >> ~/.zshrc
source ~/.zshrc
For Bash (default on most Linux distros):
cat ~/docker-vpn/examples/hkuvpn.zsh >> ~/.bashrc
source ~/.bashrc
Not sure which shell you use? Run
echo $SHELL. If the output ends inzsh, use.zshrc; if it ends inbash, use.bashrc.
Linux users: The launcher includes a Colima health-check (lines 15-18 in
hkuvpn.zsh) designed for macOS. On Linux, Docker runs natively so this block is harmless β it simply gets skipped whendocker infosucceeds. No changes needed.
After the one-time setup, connecting is just:
hkuvpn
Response:, open Microsoft Authenticator on your
phone, find the 6-digit code for HKU, type it, and press Enter.HKU provides two VPN endpoints. The launcher picks one automatically, or you can choose:
hkuvpn # use the default from ~/.vpn/hku.env
hkuvpn cn # mainland China endpoint (faster from CN)
hkuvpn hk # Hong Kong endpoint (better outside CN)
| Endpoint | Host | Best for |
|---|---|---|
cn |
121.37.195.197 |
Users in mainland China |
hk |
vpn2fa.hku.hk |
Users in Hong Kong / overseas |
The first time you connect to an endpoint, the launcher fetches and
caches its certificate fingerprint in ~/.vpn/pin-{cn,hk}.cache. This
is automatic.
Once connected, two proxies are available on your machine:
| Protocol | Address | Default Port |
|---|---|---|
| SOCKS5 | 127.0.0.1 |
1080 |
| HTTP | 127.0.0.1 |
1088 |
Any app you point at these proxies will route its traffic through HKUβs VPN. Everything else uses your normal internet.
Open a new terminal window (keep the VPN terminal running) and try:
curl -x socks5h://127.0.0.1:1080 -I https://www.hku.hk
curl -x http://127.0.0.1:1088 -I https://www.hku.hk
If you see HTTP/... 200, the VPN is working.
If ports 1080 or 1088 are already taken (e.g. by Surge, Clash, etc.),
change them in ~/.vpn/hku.env:
HKU_SOCKS_PORT=11080
HKU_HTTP_PORT=11088
Or override per-session:
HKU_SOCKS_PORT=11080 HKU_HTTP_PORT=11088 hkuvpn
Set your browserβs proxy to SOCKS5 127.0.0.1:1080. For per-site
rules (recommended), use an extension like
FoxyProxy or
SwitchyOmega to
only route *.hku.hk through the proxy.
Most command-line tools respect the ALL_PROXY environment variable:
export ALL_PROXY=socks5h://127.0.0.1:1080
Add this to your shell profile to make it persistent, or prefix individual commands:
ALL_PROXY=socks5h://127.0.0.1:1080 curl https://lib.hku.hk
If you already run a proxy client (Surge, Clash, V2RayN, etc.), the cleanest setup is to add docker-vpn as an upstream proxy and write rules to route only HKU traffic through it.
[Proxy]
HKU-SOCKS5 = socks5, 127.0.0.1, 1080
HKU-HTTP = http, 127.0.0.1, 1088
[Rule]
DOMAIN-SUFFIX,hku.hk,HKU-SOCKS5
DOMAIN-SUFFIX,hku.edu.hk,HKU-SOCKS5
SOCKS5 is preferred (UDP-capable); HTTP is a fallback for apps that don't speak SOCKS.
proxies:
- name: "HKU-SOCKS5"
type: socks5
server: 127.0.0.1
port: 1080
udp: true
- name: "HKU-HTTP"
type: http
server: 127.0.0.1
port: 1088
proxy-groups:
- name: "HKU"
type: select
proxies:
- HKU-SOCKS5
- HKU-HTTP
- DIRECT
rules:
- DOMAIN-SUFFIX,hku.hk,HKU
- DOMAIN-SUFFIX,hku.edu.hk,HKU
# ... your other rules below
In the UI, pick HKU-SOCKS5 from the new "HKU" proxy group.
Add to your Xray JSON config:
{
"outbounds": [
{
"tag": "hku-vpn",
"protocol": "socks",
"settings": {
"servers": [
{ "address": "127.0.0.1", "port": 1080 }
]
}
}
],
"routing": {
"rules": [
{
"type": "field",
"domain": ["domain:hku.hk", "domain:hku.edu.hk"],
"outboundTag": "hku-vpn"
}
]
}
}
Apply via "Settings β Routing β Custom routing rules" or a custom config profile.
The pattern is the same in any tool that supports upstream SOCKS5/HTTP
proxies + rule-based routing: declare 127.0.0.1:1080 as a proxy,
write a domain rule for hku.hk, point the rule at it. Quantumult X,
Loon, sing-box, Shadowrocket, etc. all support this.
Which domains to route: hku.hk and hku.edu.hk cover most
cases (portal, internal services, library). Add specific journal proxy
hostnames as needed. Only route what actually requires an HKU IP.
Your computer Docker container
βββββββββββββ ββββββββββββββββ
ββββββββββββββββββββββββββββ
hkuvpn (shell fn) β hku-connect.exp (PID 1) β
β β ββ openconnect β VPN β
ββ loads ~/.vpn/hku.env β ββ sends Portal PIN β
ββ loads ~/.vpn/hku.pass β then waits for OTP β
ββ fetches certificate pin β β
ββ docker run ββββββββββββββββββββββββββββββββββββββββββββββββΊ β supervisord (daemon) β
β ββ SOCKS5 on :1080 β
Surge (example local mixed-routing entry on :6152) β ββ HTTP on :1088 β
ββ HKU rules (hku.hk, hku.edu.hk, library systems) ββββββββββββΊβ localhost:1080 / :1088 β
ββ ChatGPT / Claude(Code) / other traffic βββββββββββββββββββββΊβ your normal proxy path β
β tun0 ββ VPN tunnel ββββΊ βββ HKU network
ββββββββββββββββββββββββββββ
127.0.0.1 on your machine.127.0.0.1:6152) for ChatGPT / Claude(Code), and
only forward HKU domains to docker-vpn (127.0.0.1:1080 / :1088).Your network canβt reach that VPN endpoint. Try the other one:
hkuvpn cn # if 'hk' failed
hkuvpn hk # if 'cn' failed
Or manually write the pin to ~/.vpn/pin-{cn,hk}.cache in the format
pin-sha256:BASE64STRING.
The OTP expired β they rotate every 30 seconds. Wait for a fresh code in Microsoft Authenticator and try again.
Find whatβs using it:
lsof -i :1080
Then either stop that process, or set custom ports (see Custom Ports).
colima stop
colima start
If that doesnβt help:
colima delete
colima start --cpu 2 --memory 2 --disk 20 --vm-type vz --mount-type virtiofs
Make sure youβre testing in a different terminal window β the VPN terminal must stay open. Then verify the container is running:
docker ps | grep vpn-hku
If HKU rotates their VPN certificate, delete the cache and reconnect:
rm ~/.vpn/pin-*.cache
hkuvpn
~/.vpn/hku.pass stores your Portal PIN in plaintext. Enable
full-disk encryption (FileVault on macOS, LUKS on Linux, BitLocker on
Windows) to protect it at rest.NET_ADMIN capability and /dev/net/tun for
the VPN tunnel. Proxies bind to 127.0.0.1 only β they are not
exposed to your LAN.Forked from ethack/docker-vpn.
This edition strips it down to HKU-specific use, adds expect-based
MFA handling, replaces the SSH-based SOCKS proxy with pproxy, and
removes the embedded sshd.
Inherits the upstream license. See LICENSE.