如果在家中或在任何內網中佈建了一些服務,需要從外部連入,Reverse SSH tunnel (Remote Port Forwarding),是一個很好的選擇。但是如果要讓在 Internet 上的人可以存取這個內部的服務,就必須要有外部網站再轉 tunnel 的服務,這也就是 ngrok 所提供的服務之一。ngrok 雖然有提供免費的服務,但是限制頗多。如果並不常用,訂閱起來也蠻浪費的。
如下圖,本文要說明的是如何實作自製的 ngrok 服務,其中會使用下面幾種技術:
- Reverse SSH tunnel (Remote Port Forwarding)
- NGINX reverse proxy
- (optional) Let’s Encrypt
要做到本文的成果,您必須要有:
- domain name
- Internet 上的 Linux server (自建或 Cloud 服務),以下使用 GCP
在 Mac 中設定開機自動連接 SSH tunnel
參考此篇:https://blog.kylemanna.com/osx/ssh-reverse-tunnel-on-mac-os-x/
建立持續連接的 SSH reverse tunnel
在內部機器使用以下指令,連接建立 SSH tunnel,將內部實際提供服的 port 3000,連接到外部 GCP VM 的 port 13000,並使其持續連接,不會因為閒置沒有流量而斷掉:
ssh -o TCPKeepAlive=yes -o ServerAliveCountMax=20 -o ServerAliveInterval=15 -NR 13000:localhost:3000 [email protected]
參數說明: -o: 覆蓋 ssh_config 的選項 -f: 背景執行 -N: 不執行遠端指令,適用於 port forwarding (reverse tunnel) -R: 執行 remote port forwarding (reverse tunnel) 13000: 遠端 server 的 port localhost: 本機 3000: 本機的 port TCPKeepAlive: 持續送出 KeepAlive 封包,以保持連線 ServerAliveInterval: 每送出一次 KeepAlive,會等幾秒回應 ServerAliveCountMax: 最多等幾次沒有回應的 KeepAlive,以上面指令為例,如果 20x15=300 秒,沒有收到回應,才會斷開。 /
安裝 NGINX
sudo apt update
sudo apt upgrade
sudo apt install nginx
設定 NGINX: 設定 Virtual Host、使用 Reverse Proxy
先完成支援 HTTP 的設定
hlchang@ubuntu-1:~$ cd /etc/nginx/sites-available/ hlchang@ubuntu-1:~$ sudo vi tunnel server { server_name tunnel.example.com; # Virtual Host 的名稱 listen 443 ssl; location /gf/ { # 將 /gf 導引到建立好的 SSH tunnel proxy_pass http://localhost:13000/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_redirect off; } location /lgtn/ { proxy_pass http://localhost:18086/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; proxy_redirect off; } }
連結至 sites-enabled 目錄、重新啟動 NGINX
hlchang@ngrok:~$ cd ../sites-enabled
hlchang@ngrok:~$ sudo ln -s ../sites-available/tunnel .
hlchang@ngrok:~$ sudo service nginx restart
安裝支援 NGINX 的 Let’s Encrypt Certbot
hlchang@ngrok:~$ sudo apt install certbot python3-certbot-nginx
使用 Certbot 申請與驗證憑證,並自動更新 NGINX 設定檔
注意:使用時,必須確保 HTTP 在 Port 80 有提供服務,否則無法完成驗證。
hlchang@ngrok:~$ sudo certbot --nginx -d tunnel.example.com Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator nginx, Installer nginx Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): [email protected] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must agree in order to register with the ACME server at https://acme-v02.api.letsencrypt.org/directory - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (A)gree/(C)ancel: A - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Would you be willing to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: N Obtaining a new certificate Performing the following challenges: http-01 challenge for tunnel.example.com Waiting for verification... Cleaning up challenges Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/tunnel Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2 No matching insecure server blocks listening on port 80 found. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations! You have successfully enabled https://tunnel.example.com You should test your configuration at: https://www.ssllabs.com/ssltest/analyze.html?d=tunnel.example.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/tunnel.example.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/tunnel.example.com/privkey.pem Your cert will expire on 2020-11-29. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le
確認最後的 NGIX Virtual Host 設定檔
hlchang@ubuntu-1:~$ cd /etc/nginx/sites-available/
hlchang@ubuntu-1:/etc/nginx/sites-available$ more tunnel
server {
server_name tunnel.example.com;
listen 443 ssl;
location /gf/ {
proxy_pass http://localhost:13000/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
}
location /lgtn/ {
proxy_pass http://localhost:18086/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
}
#listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/tunnel.example.com/fullchain.pem; # ma
naged by Certbot
ssl_certificate_key /etc/letsencrypt/live/tunnel.example.com/privkey.pem; #
managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}