Recently, I thought about a Raspberry Pi gathering dust, so today I experimented with frp for internal network penetration, allowing my Raspberry Pi at home to be accessed via the Internet.
Prerequisites:
- One machine with a public IP address (either a broadband connection with a public IP or a VPS)
- A domain name (optional)
Environment Introduction
First, we need to configure and start the frp server on a machine with a public IP. I used my own VPS, and all operations related to the frp server were executed on my VPS. The system environment is as follows:
1 | root@visionsmile:~# lsb_release -a |
Download frp
Next, download frp. The frp project provides binary programs for many platforms, which can be downloaded as needed here.
I need to download the amd64 version:
1 | $ wget https://github.com/fatedier/frp/releases/download/v0.20.0/frp_0.20.0_linux_amd64.tar.gz |
After downloading, extract it:
1 | $ tar -xvzf frp_0.20.0_linux_amd64.tar.gz |
frp Configuration and Startup
At this point, the directory structure looks like this:
1 | root:~/downloads/frp_0.20.0_linux_amd64# ls |
frps is the server program for frp, and frps.ini is the configuration for the server. All configuration options for frps.ini can be found here: frps_full.ini.
The default configuration for the current version of frps.ini is as follows:
1 | $ cat frps.ini |
Here, bind_port
is the port that we will use to connect frpc (the frp client) to the server, so please ensure that this port is allowed through the firewall (iptables).
Note: If it is an Alibaba Cloud server, you need to set the port to be allowed in the web security group; using ufw or iptables directly on the server will not work.
I won’t write anything too complicated here; the default settings are sufficient. For more features, you can read the project’s frp/README.
Then you can start frp:
1 | $ ./frps -c frps.ini |
After execution, it appears as follows:
Configure frps to Start on Boot
There are two ways to configure startup on Linux: one is to add the startup command to /etc/ec.local
, and the other is to use systemd
to create a service. Here, I will introduce both methods, but I recommend using systemd.
First, copy frp to the /etc
directory on the server:
1 | $ sudo mkdir /etc/frp |
Using systemd
Next, create a service for startup. I am using systemd to start the frps service:
1 | $ sudo cd /etc/systemd/system |
Fill in the following content:
1 | [Unit] |
Save and exit, then modify permissions:
1 | $ sudo chmod 755 frps.service |
Now you can start it:
1 | $ sudo systemctl start frps |
Using /etc/rc.local
Since the server actively receives connections and does not need to depend on the startup sequence of other services, you can also directly write it to /etc/rc.local
. The client is a bit different; I will explain that later.
Add the startup command (after moving the frp directory to /etc/frp
):
1 | $ sudo nano /etc/rc.local |
Use Domain Name to Resolve Server Address (Optional)
The reason for using a domain name is that if our server encounters issues, the client will also be of no use. However, I want to be able to use a different server without changing the client if this server goes down.
If we directly fill in the server’s IP address in frpc, we won’t be able to achieve that, so I thought of using a domain name that resolves to the server address I want to connect to.
So, we use a domain name (or subdomain) that resolves to the server address we want to connect to: for instance, I used dnspod to resolve frp.imzlp.com
to the server XXX.XXX.XXX.XXX
. After making the changes, don’t forget to ping
to check if it’s reachable.
Using the same method as downloading the frp server, download the frp client. Note that the following commands are executed on the client machine (Raspberry Pi).
The default configuration for the frp client is as follows:
1 | $ cat frpc.ini |
The only thing that needs to be modified temporarily is server_addr
and server_port
. Since I used the default port above, I only need to change server_addr
here.
Change it to my custom domain name (if you don’t have a domain name, you can directly specify the server’s IP address), as follows:
1 | $ cat frpc.ini |
In the [ssh]
label, the local_port
on the client forwards to the server’s remote_port
. Ensure that the remote_port
is allowed through the firewall on the server; otherwise, the connection will fail.
Then you can start frpc
on the client:
1 | $ ./frpc -c frpc.ini |
Try to Connect to Internal SSH
With the above operations, both frps and frpc have been started. We can try to connect to the Raspberry Pi’s SSH via the Internet. Since I used domain name resolution, I can use the domain name directly:
1 | $ ssh -oPort=6000 pi@frp.imzlp.com |
Note: Be sure to specify the port for the SSH connection on the server (i.e. the remote_port
parameter in frpc.ini). If all goes well, the connection should succeed, as shown below:
Configure frpc to Start on Boot on the Client
Note: This is for Linux; scripts for Windows will be introduced later.
1 | # Execute in the frp_0.20.0_linux_arm directory (configuration files have been changed) |
For client bootup, I am using systemd
services:
1 | $ sudo cd /etc/systemd/system |
Fill in the following content:
1 | [Unit] |
Pay special attention to these two lines in the script above:
1 | Type=idle |
The purpose of these two statements is to wait for system initialization to complete. If frp starts at boot, it may fail due to the inability to connect to the network (if using a domain name, it might fail due to DNS resolution issues).
Note: If you do not want to wait, you can remove ExecStartPre
, but you are required to set login_fail_exit
to false
in the frpc configuration.
Start frpc and set it to start on boot:
1 | $ systemctl start frpc |
Use kcp Protocol
If you have used kcp to accelerate ss, you will know that kcp acceleration brings higher transmission efficiency and reduces latency.
The current version of frp (>v0.12.0) supports kcp at the underlying communication protocol level and can be manually enabled in the configuration file:
Server:
Enable kcp protocol support in the frps.ini on the server, specifying a UDP port to receive client requests:
1 | # frps.ini |
Client:
Specify the protocol type to be used in frpc.ini, which currently only supports tcp and kcp. Other proxy configurations do not need to change:
1 | # frpc.ini |
Enable Encryption and Traffic Compression
Some local area networks may perform traffic feature recognition and interception, so frp also offers traffic encryption and compression (add to frpc.ini):
1 | use_encryption = true |
Use Mstsc for Remote Connection
In my previous article, Transforming Raspberry Pi into a Portable Linux Compilation Environment, I mentioned that you can install xrdp
on the Raspberry Pi so it can be connected via Windows mstsc.
1 | # Install xrdp to allow mstsc connections |
When connecting via LAN, it looks like this:
Since mstsc defaults to port 3389, when we want to access via the Internet, we need to map the Raspberry Pi (client) port 3389 to the server port.
The specific operation is as follows:
Edit the client frpc.ini and add a label:
1 | [xvnc] |
This means mapping the local 3389 port to the server’s 6001 port. Save the changes and restart the frpc service:
1 | $ systemctl restart frpc |
Note: As mentioned above, ensure that the server’s 6001 port is not blocked by the firewall.
Once completed, you can use Mstsc to connect to the external network (again, I used the domain name):
After connecting, it is just like connecting through the local network:
After logging in:
The same method can also be used for remote connections on Windows clients, which is much stronger than anydesk or teamviewer.
It’s incredibly smooth, just like operating a local computer.
You can also use rdpwrap so that remote connections do not log out the local account when accessing other accounts (equivalent to Windows Server functionality).
Reload Configuration
frp provides a way to read changes from modified configuration files and apply new proxy updates (so you don’t have to stop and restart).
Add the following parameters to frpc.ini
:
1 | # frpc.ini |
The command to reload is:
1 | frpc reload -c ./frpc.ini |
Note: The current version of frp (v0.20.0) will only reload the contents in start
, such as server_addr
, etc., so if server_addr
has changed in frpc.ini
, using reload
will not reconnect to the new server.
My frp Configuration Files
1 | #frps.ini |
1 | #frpc.ini |
I wrote a Python script to detect DNS updates for my domain. If there’s an update, it restarts the client’s frpc service:
1 | # Note: This script cannot be used on Windows. |
Similarly, I added it to the systemctl
for startup:
1 | # /etc/systemd/system/syncfrpcdns.service |
It is also set to start on boot:
1 | $ sudo systemctl enable syncfrpcdns.service |
The above script can only run on Linux; I also wrote a similar one that can run on Windows:
1 | # SyncFrpServerHost.py |
Note: This script can act as the frpc startup program without needing to set frpc to start separately. It’s best to set login_fail_exit
to false
in frpc.ini
.
You can create a simple VBS to hide the window when starting, or you can use pythonw
:
1 | DIM objShell |
Place its shortcut in C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp
.
This way, when I switch the server for frp.imzlp.me
, the client will detect DNS changes and update the connection to the new server.
Conclusion
The most important thing during the operation is to check if the port is allowed through the firewall. If you cannot connect, it’s very likely that the port is blocked rather than a problem with the frp configuration.
For more ways to use frp, check here: frp/README.
Update
2018.11.10 16:19
- Added the use of systemd to start frps on the server.
- Revised some wording.
- Added information about Alibaba Cloud security groups.