preface

Recently, I was maintaining a tool for Intranet penetration. I found that there are many Intranet penetration tools in the market, including Ngrok, Localtunnel and FRP, all of which have similar implementation principles. I initially thought that Intranet penetration could be implemented using Nginx, but after analyzing the principles of some Intranet penetration tools I found that MY understanding was too simple. Next I will talk about the principles of Intranet penetration tools and implement an Intranet penetration tool using Node.

What is Intranet penetration

Intranet penetration, also known as NAT penetration, simply means that the extranet can access local services.

Extranet IP is expensive. Although enterprises have a small number of independent extranet IP, they cannot provide an extranet IP for each host due to cost constraints, or not all services need to be exposed to the extranet due to security constraints. Therefore, an enterprise may use the NAT technology to map a large number of internal IP addresses to a small number of external IP addresses according to certain rules.

Through NAT technology, we can access the external network resources, but the disadvantage is that the external network cannot initiate a request to the internal host, so we need to use the internal network penetration service.

What is NAT technology

NAT network address translation

NAT technology mentioned above, what is NAT technology? Network Address Translation (NAT) translates a large number of Intranet IP addresses into a small number of external IP addresses based on certain rules, enabling Intranet users to access external resources. Generally, a NAT gateway is set up on a router to maintain a mapping table between Intranet AND extranet IP addresses. There are many implementation schemes of the NAT technology. The following is a common one.

NAPT Network address port translation

Network Address Port Translation (NAPT) is a type of NAT technology. An IP Address can correspond to multiple ports. Therefore, information is communicated through the mapping between multiple internal IP addresses and multiple ports of a single external IP Address.

The implementation principle of NAPT is as follows. For example, if two machines on the Intranet are 192.168.1.1 and 192.168.1.2, the external IP address of the router is 162.105.178.62. When 192.168.1.1 accesses the Internet, the NAT gateway on the router automatically allocates port 6666 and saves the mapping between the port and the Intranet IP address (6666 -> 192.168.1.1). The route will access the external resource in 162.105.178.62:6666 mode. When the external resource responds, the data will also be sent to the address 162.105.178.62:6666. At this time, the NAT gateway mapping table will be checked according to the port. Find the mapping between port 6666 and 192.168.1.1 and return resources to 192.168.1.1, as shown below:

For an example of the more popular, go out shopping, businesses must be at least know where the plot and house number to delivery for me, the goods sent to residential property, residential property according to the house number sent to my home, then my village is the network IP, house number is the port, the property is NAT gateway, my house is a network machine.

The limitations of NAT

However, NAT is also limited. The Internet cannot initiate requests to Intranet machines for security reasons. In the preceding process, the NAT gateway establishes a mapping relationship only when the Intranet initiates a request first. Without the mapping relationship, the extranet channel cannot be connected.

For example, the number of the house is changed, and the property will issue the number before going out. Outsiders can’t find my house without the number.

The difference with VPN

VPN (Virtual Private Network) is usually used to do two things, one is to allow any two machines in the world to enter a Virtual LAN, the other is used to climb over the wall.

The implementation principle is to create a virtual NETWORK adapter through the operating system. The virtual network adapter negotiates with the VPN Server to obtain a virtual LAN IP address. The data is intercepted by the network adapter, encapsulated by encryption and then forwarded to the VPN Server. If it is the communication between users of the virtual LAN, the request will be forwarded according to the IP of the virtual LAN. If it is to cross the wall, if you visit Google, the VPN Server will change the source IP of the data to the IP address of the VPN Server, and then send the data to Google to realize the data communication. This is similar to NAT, as shown below:

VPN can also implement Intranet penetration, but it is different from the following Intranet penetration implementation:

  • In order to access Intranet services, users need to install VPN clients to virtual network cards. This is not as convenient as providing an external IP address for Intranet machines
  • The application scenarios are not consistent. VPNS can provide services for all ports on Intranet machines. However, the Intranet penetration to be realized next is mainly to provide services for a single port on a single machine, so VPNS are generally used in virtual office environments instead of providing single services

Why not use Nginx

What can Nginx do in the first place? Load balancing and static/static separation will not be discussed, but instead, Nginx can be used as a proxy:

Forward agent

Forward proxy forwards requests to the corresponding server, but Nginx deployed proxy server and target server are not the same network environment, both have their own external IP. This is inconsistent with our need to provide an external IP address for the target server.

The reverse proxy

Reverse proxy, which forwards external requests to internal servers. Reverse proxy can implement Intranet penetration, but the internal server and the proxy server deployed by Nginx are in the same network environment, as shown in the following figure:

Other scenarios

As mentioned above, Intranet penetration can also be achieved through reverse proxy, but more often than not, the proxy server and internal server are deployed in a different environment, in which case Nginx cannot meet the requirements. So what we’re going to discuss and implement is this case where Nginx doesn’t work.

Realize the principle of

To achieve Intranet penetration, it is necessary to have a proxy server that can access external resources. If this server is also in the Intranet environment, it can be achieved through Nginx reverse proxy. But the reality is that the servers live on independent networks, and the bridge between the proxy server and the internal server is not open. However, the communication between the proxy server and the internal server can be established by establishing Socket communication tunnels, and then the proxy server accesses external resources and transmits them to the internal server.

Intranet penetration tools are generally divided into Server side and Client side. Deployed on a proxy Server, the Server manages Client communication and distributes requests to corresponding clients. The Client is deployed on a local machine and is used as required. The Client is responsible for sending requests from the Server to the corresponding local service. The following figure briefly describes the Intranet penetration process in the mode of domain name mapping:

This implementation is similar to the implementation principle of NAT. Then we continue NAT process is also a popular example to explain the community service upgrades, service outsourcing to a site, outsiders want to visit your home you need to service site to fill out the application form in advance, when outsiders to the community under the query service site form will know the location of your home, the service site is network transmission tool, The application form is the mapping between the Server and the Client.

The following is the implementation process of domain name mapping:

Preparation stage

If the local port 8000 service needs to be accessed from the Intranet, the Client first establishes a communication connection between the local port 8000 service and the Server. The Client sends a request to the Server. On receiving the request, the Server creates a channel to communicate with the Client (usually a Socket connection) and assigns a subdomain such as a.example.com, The Server saves the subdomain name mapping (a.example.com -> Socket connections on the Client) and returns the subdomain name to the Client. After the preparation phase is complete, accessing a.example.com is equivalent to accessing the local port 8000 service.

The request phase

When accessing a.example.com, a request is sent to the Server, the Socket connection is found by querying the subdomain name mapping, and the request is sent to the local port 8000 through the Socket. After the request is processed, the local port 8000 returns the request.

Destruction of phase

When the Client is closed, the Socket connection on the Server is closed. In this case, you need to destroy the corresponding domain name mapping.

Implement a version with Node

Implement a simple version of the Intranet penetration tool using Node.

For the sake of demonstration, we do not do domain name mapping, and pass the subdomain name as URL parameter directly

First, the Server code is implemented to create a Server service. When a Client connection is established, a subdomain name is randomly generated, kept in the map table, and returned to the Client.

// server.js
const net = require('net');

// Randomly get 4 characters
const getRandomStr = (a)= > {
    const chars = 'abcdefghijklmnopqrstuvwxyz';
    let result = ' ';
    for (var i = 4; i > 0; --i) {
        result += chars[ Math.floor( Math.random() * chars.length ) ];
    }
    return result;
};

// Get the subdomain name
const getSubdoman = (a)= > {
    const subdoman = getRandomStr();
    if (map[subdoman]) {
        return getSubdoman();
    } else {
        returnsubdoman; }};/ / map
const map = {};

// Establish a connection between the Server and the Client
const server = net.createServer(socket= > {
    // Randomly obtain subdomain names
    const subdoman = getSubdoman();
    // Save the mapping between subdomain name and Socket connection
    map[subdoman] = socket;
    // Return the subdomain name to the Client
    socket.write(`Tunnel-Subdoman: ${subdoman}`);
});

server.listen(9999, () = > {console.log('start server');
});
Copy the code

Then to realize the Client code, the following assuming that the port number of the local service is 8888, establish a connection between the local service and the Server, when the request is received to determine whether the Server returns the subdomain name, if so, output the subdomain name, otherwise the request will be passed to the local service.

// client.js
const net = require('net');

// Establish a connection with the local service
const local = net.createConnection({
    port: 8888
});

// Establish a connection to the Server
const client = net.createConnection({
    port: 9999
});

// The Client received the request
client.on('data', data => {
    const content = data.toString();
    // Check whether the data is a subdomain
    if (content.indexOf('Tunnel-Subdoman')! = =- 1) {
        // Output subdomain names
        console.log(`${content}`);
    } else {
        // Forward the data to the local servicelocal.write(data); }});// The local service returns the processing result to the Client
local.on('data', data => {
    client.write(data);
});
Copy the code

In this way, although the Server communicates with multiple clients, the external request still cannot be delivered to the Server. Therefore, a Web service is needed to accurately deliver the request to the corresponding Client by accepting the external request and querying the mapping table.

// server.js.// Get the subdomain parameters of the URL
const getSubdomanFromUrl = data= > {
    const content = data.toString();
    const match = content.match(/tunnelSubdoman=(\w*)/);
    if (match) {
        return match[1]; }};// Receive external requests
const web = net.createServer(socket= > {
    let subdoman = ' ';
    socket.on('data', chunk => {
        if(! subdoman) {// Obtain subdomain parameters from the protocol content
            subdoman = getSubdomanFromUrl(chunk);
            // Return the content processed by the Client
            map[subdoman].pipe(socket);
        }
        // Forwards the request to the Client
        map[subdoman].write(chunk);
    });
});

web.listen(9997, () = > {console.log('start web');
});
Copy the code

Note: This code is for demonstration purposes only and cannot be used in a production environment

Start the Server, then start the Client (remember local port 6666 service) :

node server.js
// start server
// start web
Copy the code
node client.js
// Tunnel-Subdoman: esrs
Copy the code

The above Client output ESRS is the subdomain name, here mainly for the convenience of not through the domain name to do the mapping, so directly through the URL parameter to pass, visit http://127.0.0.1:9997? TunnelSubdoman = esRS is equivalent to accessing the local 6666 service.

Write in the last

The code above is for demonstration purposes only and cannot be used in a production environment because there is still a lot of work to be done. The above Client and Server sockets are easily disconnected, but there is no reconnection mechanism, and only TCP forwarding is not implemented UDP forwarding. If you have a better implementation scheme or any flaws in the article, welcome to discuss and learn from me.

Reference reading:

  • Intranet penetration tool principle and development of actual combat
  • Fully understand what Nginx can do
  • VPN working principle and construction method