舍友滚去实习了,恰好又要选课。学校的教务系统似乎无法从外网访问,于是便考虑给他们搞个代理。
一般情况下,代理服务器需要运行在内网,在公网边界暴露访问端口以便外网用户得以访问。但是很显然我没有校网边界路由器的权限,不可能去改防火墙。
好在我有一台VPS,在这种双方用户无法直连的情况下,就只能反过来利用第三台机器做中转了。思路很直接,校网的代理主动连接到中转服务器,再由中转服务器提供代理服务接收请求转发到校网的代理服务器中。
这种做法被叫做“反弹代理”。
在网上搜了一圈,发现有一个软件 ssocks 能支持反弹代理。遂下载编译。
在VPS上开放了端口8888用于反弹代理连接,并在1080端口提供Sock5代理服务:
Shell
./rcsocks -l 1080 -p 8888 -vv
./rcsocks-l1080-p8888-vv
在校网使用树莓派架设一个反弹代理的客户端,启动时自动连接到VPS的8888端口:
Shell
./rssocks -vv -s bakachu.cn:8888
./rssocks-vv-sbakachu.cn:8888
这样,反弹代理就搭建完毕了,之后只要用代理客户端连接VPS的1080端口就能获得服务。
但是Sock5代理的功能太强大,如果只提供HTTP代理服务就要做转换了。这里,我直接使用tengine配合其上的lua模块编写一个小的转换脚本来将HTTP请求转到本地的sock5代理上。
tengine配置:
server { listen 8080; server_name proxy.bakachu.cn; charset utf-8; access_log /home/ngx-logs/proxy.access.log; error_log /home/ngx-logs/proxy.error.log; location / { lua_socket_read_timeout 300s; default_type text/html; content_by_lua_file /data/proxy.lua; } }
server{ listen8080; server_name proxy.bakachu.cn; charset utf-8; access_log/home/ngx-logs/proxy.access.log; error_log/home/ngx-logs/proxy.error.log; location/{ lua_socket_read_timeout300s; default_type text/html; content_by_lua_file/data/proxy.lua; } }
lua脚本:
Lua
local socks5 = require("resty.socks5") local base64 = require("base64") local headers = ngx.req.get_headers() local proxy_auth = headers["Proxy-Authorization"] if proxy_auth == nil or proxy_auth == "" then ngx.header["Proxy-Authenticate"] = 'Basic realm="Passwd required."' ngx.exit(407) else local _, _, base64enc = string.find(proxy_auth, "Basic (.+)") local authinfo = base64.decode(base64enc) local _, _, username, passwd = string.find(authinfo, "(.+):(.+)") if username ~= "proxy_username" or passwd ~= "proxy_passwd" then ngx.header["Proxy-Authorization"] = "" ngx.header["Proxy-Authenticate"] = 'Basic realm="Bad username or password."' ngx.exit(407) else socks5.handle_request("127.0.0.1", 1080) end end
localsocks5=require("resty.socks5") localbase64=require("base64") localheaders=ngx.req.get_headers() localproxy_auth=headers["Proxy-Authorization"] ifproxy_auth==nilorproxy_auth==""then ngx.header["Proxy-Authenticate"]='Basic realm="Passwd required."' ngx.exit(407) else local_,_,base64enc=string.find(proxy_auth,"Basic (.+)") localauthinfo=base64.decode(base64enc) local_,_,username,passwd=string.find(authinfo,"(.+):(.+)") ifusername~="proxy_username"orpasswd~="proxy_passwd"then ngx.header["Proxy-Authorization"]="" ngx.header["Proxy-Authenticate"]='Basic realm="Bad username or password."' ngx.exit(407) else socks5.handle_request("127.0.0.1",1080) end end
这样,就可以通过http://proxy.bakachu.cn:8080和用户名密码来访问sock5代理了。