CQUPT Reverse Proxy

By on

To make our school’s internal resources accessible from the internet, I built a reverse proxy to expose everything behind our firewall to the internet.

Disclaimer: This is an experimental project and everything is protected via Cloudflare Access with Origin Pull to ensure that only me and the people I authorized can access the endpoint.

Screenshot

How to use it?

  • Domain Available: Replace cqupt.edu.cn with endpoint.domain1 in the domain part of any URL.
  • IP Only: Prepend IP address in front of .endpoint.domain.

e.g.

  • If you want to visit jwzx.cqupt.edu.cn, just type jwzx.endpoint.domain
  • Visit 127.20.0.1.endpoint.domain instead of 127.20.0.1
Advanced Notices
  1. Some destination server requires an TLS connection, and *.secure.endpoint.domain is aimed to do that. Otherwise, *.endpoint.domain will initiate a plain HTTP request to the destination.
  2. Considering there will be many direct IP forwards, and there is no need to acquire a certificate for them. Thus, any domain access like jwzx.endpoint.domain is provided with a valid wildcard certificate, while IP accesses are not.
  3. Destinations with unusual port(other than 80 and 443) are not supported and their link will not be overridden.

Under the hood

I used a Raspberry Pi 4 as the gateway benefited by its gigabit ethernet controller.

NGINX Configuration ➡︎

Override redirects

Of domains ending with cqupt.edu.cn

proxy_redirect 		~^http://cqupt.edu.cn/(.*) https://endpoint.domain/$1;
proxy_redirect 		~^http://(.*).cqupt.edu.cn/(.*) https://$1.endpoint.domain/$2;
proxy_redirect		~^https://(.*).cqupt.edu.cn/(.*) https://$1.secure.endpoint.domain/$2;

Of IPs

proxy_redirect		~^http://([0-9.]+)/(.*) http://$1.endpoint.domain/$2;
proxy_redirect		~^https://([0-9.]+)/(.*) http://$1.secure.endpoint.domain/$2;

nginx_substitutions_filter is an enhanced sub_filter module that allows multiple regexes to replace concurrently.
Check out Substitutions | NGINX to find out more.

subs_filter		'http://cqupt.edu.cn' https://endpoint.domain gir;
subs_filter		'http://(.*).cqupt.edu.cn' https://$1.endpoint.domain gir;
subs_filter		'https://(.*).cqupt.edu.cn' https://$1.secure.endpoint.domain gir;
subs_filter		'http://([0-9.]+)' http://$1.endpoint.domain gir;
subs_filter		'https://([0-9.]+)' http://$1.secure.endpoint.domain gir;

There is no need to match exact IPs since I configured a firewall to prevent malicious requests.

Use the following statement to resolve our internal domains from our internal DNS servers.

resolver [ns1.ip] [ns2.ip] valid=3600s;

Then configure iptables to filter invalid requests.

# Generated by xtables-save v1.8.3 on Thu Dec 19 03:26:35 2019
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -d [network/mask] -p tcp -m owner --uid-owner 33 -j ACCEPT
...
-A OUTPUT -d [ns1.ip] -p udp -m owner --uid-owner 33 -j ACCEPT
-A OUTPUT -d [ns2.ip] -p udp -m owner --uid-owner 33 -j ACCEPT
-A OUTPUT -m owner --uid-owner 33 -j REJECT --reject-with icmp-port-unreachable
COMMIT
# Completed on Thu Dec 19 03:26:35 2019
  1. endpoint.domain denotes the domain name of the endpoint just in this article. Remember to replace it in the real world. ↩︎