Enable TLS communication between frps and frpc
In this article I introduced how to use frp to establish an SSH connection to a local server. In fact, services running on a local server also can be exposed to the internet via frp's TCP forwarding. For instance, I ran a chat service on my old laptop without public IP address and made it accessible over the internet. However, a potential security issue is that the traffic between the local client and server is plaintext if you just use the tcp or http mode. This article records how to enable TLS feature.
Since the basic setup of frp was introduced previously, it does not receive description there. Check out this article if you haven't deployed frp on your hosts.
First of all, frp supports both HTTP and HTTPS transmission modes, and offers different services based on the domain name. However, I didn't get HTTPS to work properly (now I realized that maybe it is due to certificate issues). Thus I choose another way from the official document: encrypting with TLS. This is also the official recommended security configuration.
The content of this article is based on the official document. However, the document has some ambiguity and repeating parts, that spent me, a noob, a lot of time to figure out. I will explain them clearly and provide some examples.
Generate frps cert on client
Prepare openssl config file.
# show configuration directory
$ openssl version -d
OPENSSLDIR: "/usr/lib/ssl"
# copy to your directory
cp /usr/lib/ssl/openssl.cnf ./my-openssl.cnfBuild certs for server on client. Change IPADDRESS to server's IP address.
# generate ca certs
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.ca.com" -days 5000 -out ca.crt
# generate certs for server
openssl genrsa -out server.key 2048
openssl req -new -sha256 -key server.key \
-subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=server.com" \
-reqexts SAN \
-config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:localhost,IP:IPADDRESS,DNS:example.server.com")) \
-out server.csr
openssl x509 -req -days 3650 -sha256 \
-in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(printf "subjectAltName=DNS:localhost,IP:IPADDRESS,DNS:example.server.com") \
-out server.crtThe following figure shows an example.
Then, copy server.key and server.crt to the server using scp or something else.
Generate frpc cert on server
Similarly, generate ca certs according to the configuration file, then build certs for client on server. The IP address is not required in client cert, thus following codes can be run immediately without edit.
# show configuration directory
$ openssl version -d
OPENSSLDIR: "/usr/lib/ssl"
# copy to your directory
cp /usr/lib/ssl/openssl.cnf ./my-openssl.cnf
# generate ca serts
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=example.ca.com" -days 5000 -out ca.crt
# generate certs for server
openssl genrsa -out client.key 2048
openssl req -new -sha256 -key client.key \
-subj "/C=XX/ST=DEFAULT/L=DEFAULT/O=DEFAULT/CN=client.com" \
-reqexts SAN \
-config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:client.com,DNS:example.client.com")) \
-out client.csr
openssl x509 -req -days 3650 -sha256 \
-in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(printf "subjectAltName=DNS:client.com,DNS:example.client.com") \
-out client.crtThe following figure shows an example.
Then, copy client.key and client.crt to the client using scp or something else.
Edit configuration files
Configure following parameters under the [common] section to frpc.ini to enable TLS.
# enable tls
tls_enable = true
# specify the directory of client.* copied from server
tls_cert_file = path/client.crt
tls_key_file = path/client.key
# specify the directory of ca.crt
tls_trusted_ca_file = path/ca.crtConfigure following parameters under the [common] section to frps.ini to enable TLS.
# enforce tls to only accept TLS connections
tls_only = true
# enable tls
tls_enable = true
# specify the directory of server.* copied from client
tls_cert_file = path/server.crt
tls_key_file = path/server.key
# specify the directory of ca.crt
tls_trusted_ca_file = path/ca.crtFinally, run ./frpc -c frpc.ini on client and ./frps -c frps.ini on server, to see if it works well. If everything is working properly, run them as system services using systemctl. The method for adding a daemon has been introduced in previous article.
In this way, communication between the client and server is encrypted with TLS.
Expose a local port to the server
After configuring the TLS encryption, you can use tcp mode to expose the local service to the server as follows, and all communication between frpc and frps are secured.
[rocketchat_tcp]
type = tcp
local_ip = 127.0.0.1
local_port = 3000
remote_port = 3000Then, you can use nginx as reverse proxy to access this service through port 3000, like access a service deployed on the server.
Capture packets to see the difference
Before:
After: