Millet Porridge

English version of https://corvo.myseu.cn

0%

Ngrok Intranet Tunneling

This document uses a project that actually went live as an example to give a brief introduction to Ngrok. I hope readers can draw inferences — after all, you need to practice on concrete projects yourself to deepen the experience.

Below I will cover ‘an overview of NAT traversal’, ‘ngrok principles and use cases’, ‘the team’s needs’, ‘compiling and using ngrok’, and ‘using ngrok together with Nginx’. Readers who already understand the principles can jump straight to ‘compiling and using ngrok’ and what follows.

Overview of NAT Traversal

When we start talking about NAT traversal, I want to first introduce what an intranet is, and why we normally use intranets. Let’s look at a diagram first.

When you visit http://www.baidu.com, our destination is that website, but during the network request we first pass through a NAT server — of course there may be more than one such server, depending on the ISP.

ngrok Principles and Use Cases

The Team’s Needs

As a campus team, we have always been committed to providing students with high-quality services.

Many services that require the campus network are aggregated on our intranet servers, exposed as RESTful interfaces. Once these externally exposed servers hit certain special circumstances, they are likely to be affected and unable to serve normally. This kind of force majeure isn’t aimed at our system specifically, but it does cause considerable inconvenience in some respects.

Fortunately such situations are rare, and servers inside the campus network can access the network outside — this gives us an idea for solving the problem: with NAT traversal we can map certain services to the external network as a temporary fix.

Preparation

Make sure you have your own domain and a public IP. The rest of this article uses the domain myseu.cn as an example.

Adding Domain A Records

Ngrok needs a service established on the public network, so intranet servers need to resolve the domain to find the server IP.

You need to add two domain A records (both pointing to your public server IP):

A record Purpose
ngrok.myseu.cn client finds the server
*.ngrok.myseu.cn provides subdomain services

Downloading and Compiling ngrok

A lot of code follows. The purpose of much of it may not be obvious; if you’re interested, see the references at the end — the English version explains every step in detail.

Download:

1
2
git clone https://github.com/inconshreveable/ngrok.git ngrok
cd ngrok

Compile:

1
2
3
4
5
6
7
8
9
10
11
openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=ngrok.myseu.cn" -days 5000 -out rootCA.pem
openssl genrsa -out device.key 2048
openssl req -new -key device.key -subj "/CN=ngrok.myseu.cn" -out device.csr
openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 5000

cp rootCA.pem assets/client/tls/ngrokroot.crt
cp device.crt assets/server/tls/snakeoil.crt
cp device.key assets/server/tls/snakeoil.key

make release-server release-client

Check the result:

1
2
3
☁  ngrok [master] ⚡ cd bin 
☁ bin [master] ⚡ ls
go-bindata ngrok ngrokd

Simple Deployment

Deployment covers the server side and the client side.

First deploy the server:

    1. Copy the files to the server’s /tmp directory
1
2
☁  ngrok [master] ⚡scp bin/ngrokd heraldnew:/tmp
☁ ngrok [master] ⚡scp assets/server/tls/snakeoil.* heraldnew:/tmp
    1. Log into the server and start listening
1
2
3
4
5
6
➜  /tmp ./ngrokd -tlsKey=snakeoil.key  -tlsCrt=snakeoil.crt -domain="ngrok.myseu.cn" -tunnelAddr=":5555" -httpAddr=":8988" -httpsAddr=":8989"
[21:18:03 CST 2017-08-02] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [registry] [tun] No affinity cache specified
[21:18:03 CST 2017-08-02] [INFO] (ngrok/log.Info:112) Listening for public http connections on [::]:8988
[21:18:03 CST 2017-08-02] [INFO] (ngrok/log.Info:112) Listening for public https connections on [::]:8989
[21:18:03 CST 2017-08-02] [INFO] (ngrok/log.Info:112) Listening for control and proxy connections on [::]:5555
[21:18:03 CST 2017-08-02] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [metrics] Reporting every 30 seconds

Deploy the client:

    1. Create a config file (see the references for config files): ngrok.cfg
1
2
server_addr: "ngrok.myseu.cn:5555"
trust_host_root_certs: false
    1. Establish the connection
1
2
3
4
5
6
7
8
☁  bin [master] ⚡ ./ngrok -subdomain he -config=ngrok.cfg 80
ngrok (Ctrl+C to quit)

Tunnel Status online
Version 1.7/1.7
Forwarding https://he.ngrok.myseu.cn:8988 -> 127.0.0.1:80
Forwarding http://he.ngrok.myseu.cn:8988 -> 127.0.0.1:80
Web Interface 127.0.0.1:4040

The example maps the local port 80. That concludes intranet forwarding; next we need to configure the public server.

Using ngrok with Nginx

If our goal were only port forwarding, the work would be done. But we need to provide an external API, reverse-proxied by the public server’s Nginx — that is, we are still one Nginx away from really serving traffic.

Suppose we use a direct reverse proxy like this:

1
2
3
location /api {
proxy_pass http://127.0.0.1:8988/api;
}

Be sure to test before going live. Testing reveals this problem when requesting the API: Tunnel xxx not found.

The Nginx configuration must not be this simple. When proxying, it is actually Nginx making requests to the backend, and the request headers get filled in automatically — the backend can no longer recognize them. We need to modify them manually to restore their original state:

1
2
3
4
5
6
7
8
9
location /api {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host tyx.ngrok.myseu.cn:8988; # the port here must match the port specified when starting the ngrok server
proxy_set_header X-Nginx-Proxy true;
proxy_set_header Connection "";

proxy_pass http://tyx.ngrok.myseu.cn:8988/api;
}

References:

Run Ngrok on Your Own Server Using Self-Signed SSL Certificate Building your own ngrok service Notes on using ngrok

LICENSE:

Copyright © 2017 corvo. Commercial use without permission is prohibited. Please credit the source when reposting.