自从ngrok免费版本不进行维护后,我一直在寻求一款稳定可靠,开源的转发工具来将我内网的业务映射到公网上。
FRP(Github)正好可以满足我的要求。不仅可以实现内网业务的映射,也可以实现服务器端业务的映射,以使得在内网安全稳定地访问公网敏感业务(例如SSH)。同时还支持注册为systemd服务,稳定性更好。
0x00 明确需求
在使用FRP之前我们要搞清楚自己希望通过FRP实现什么。
以一个最简单的拓扑为例:
我有一台公网服务器,我希望内网的业务暴露在公网。业务类型可能是HTTP(s)类业务或者是TCP类业务。我也希望我能够隐藏公网的一些服务(比如SSH),通过FRP自带的STCP功能将流量加密地发回到我的客户端,并通过客户端的特定端口来进行访问。
以我的实际拓扑为例:
我有一台公网服务器用作堡垒机,作为FRP的服务器。我有另一台公网服务器承载业务,我希望通过堡垒机管理这台生产服务器的SSH。
我使用FRP的STCP功能加密SSH流量发到堡垒机上,并在公有云后端的防火墙对SSH端口进行封禁。
我有一台内网服务器,上面是一个ESXi主机,其中我建立了一台Edge Server作为边界节点,Edge上运行了FRP客户端,把公网服务器的SSH流量转发到自己的某个端口。同时客户端还把内网中的其他业务暴露到FRP堡垒机。
最后基于安全配置,我在堡垒机(FRP服务器)上也开启了FRP的客户端,将堡垒机的SSH也转发到自身,并在内网的Edge服务器访问。同时关闭堡垒机侧防火墙公网对SSH端口的访问。
0x01 配置FRP服务器(堡垒机)
0x010 服务器配置
下载回来frp组件,frps是服务器组件,frpc是客户端组件。
针对默认配置,我们做了一些修改。
# [common] is integral section [common] # A literal address or host name for IPv6 must be enclosed # in square brackets, as in "[::1]:80", "[ipv6-host]:http" or "[ipv6-host%zone]:80" bind_addr = 0.0.0.0 # 这里绑定管理端口 bind_port = 7000 # udp port to help make udp hole to penetrate nat # 绑定UDP端口 bind_udp_port = 7001 # udp port used for kcp protocol, it can be same with 'bind_port' # if not set, kcp is disabled in frps # 绑定KCP端口 kcp_bind_port = 7000 # specify which address proxy will listen for, default value is same with bind_addr # proxy_bind_addr = 127.0.0.1 # if you want to support virtual host, you must set the http port for listening (optional) # Note: http port and https port can be same with bind_port # 进行HTTP(s)流量转发时需要用到这个端口,流量会被打到这个端口上并用二级域名做区分。 vhost_http_port = 6666 vhost_https_port = 8888 # response header timeout(seconds) for vhost http server, default is 60s # vhost_http_timeout = 60 # set dashboard_addr and dashboard_port to view dashboard of frps # dashboard_addr's default value is same with bind_addr # dashboard is available only if dashboard_port is set # Web管理面板,非必须。0.0.0.0指这个面板可以被任意访问,127.0.0.1指这个面板只能被自己访问。 dashboard_addr = 127.0.0.1 dashboard_port = 6688 # dashboard user and passwd for basic auth protect, if not set, both default value is admin # 管理面板的用户名、密码 dashboard_user = username dashboard_pwd = password # dashboard assets directory(only for debug mode) # assets_dir = ./static # console or real logFile path like ./frps.log log_file = ./frps.log # trace, debug, info, warn, error log_level = warn log_max_days = 3 # disable log colors when log_file is console, default is false disable_log_color = false # auth token # 指定和客户端的通讯密码 token = password # heartbeat configure, it's not recommended to modify the default value # the default value of heartbeat_timeout is 90 # heartbeat_timeout = 90 # only allow frpc to bind ports you list, if you set nothing, there won't be any limit # 允许TCP、UDP流量打到FRP服务器特定的端口组 allow_ports = 6101-6200 # pool_count in each proxy will change to max_pool_count if they exceed the maximum value max_pool_count = 50 # max ports can be used for each client, default value is 0 means no limit max_ports_per_client = 0 # if subdomain_host is not empty, you can set subdomain when type is http or https in frpc's configure file # when subdomain is test, the host used by routing is test.frps.com # 用作HTTP流量转发的子域名 subdomain_host = frp.iam.lc # if tcp stream multiplexing is used, default is true tcp_mux = true # custom 404 page for HTTP requests # custom_404_page = /path/to/404.html
将这个配置文件保存到 /etc/frp/frps.ini
然后我们开始写systemd脚本:
# /etc/systemd/system/frps.service [Unit] Description=Frp Server Service After=network.target [Service] Type=simple User=root Restart=always RestartSec=5s ExecStart=/usr/bin/frps -c /etc/frp/frps.ini [Install] WantedBy=multi-user.target
配置好自启动,服务器端就配置完成了。
0x011 安全组配置与端口放通
关于端口放通,我们需要放通以下端口:
7000 TCP+UDP 主通信端口
7001 UDP UDP通信端口
6666 TCP HTTP虚拟主机端口
8888 TCP HTTPS虚拟主机端口
6101-6200 TCP+UDP 客户端业务注册端口
6688 TCP 管理面板端口
0x012 域名设置
基于七层HTTP的业务映射,FRP允许我们使用 A.frp.iam.lc 子域名来进行访问。A可以自行定义。而基于TCP的端口业务映射,我们也通常给予一个域名 frp.iam.lc 来进行访问。
我们需要添加以下两条A记录到你的DNS解析:
*.frp.iam.lc -> 堡垒机IP
frp.iam.lc -> 堡垒机IP
至此,服务器侧配置完成。
0x02 内网客户端映射到FRP服务器
在内网的linux服务器安装好frpc客户端,我们开始写配置文件:
/etc/frp/frpc.ini
[common] server_addr = 1.2.3.4 # 服务器IP server_port = 7000 token = password pool_count = 4 protocol = kcp
以上是每个客户端的公共配置部分。这将作为基础连接参数,被下面要添加的各种服务引用。
下面我们要把内网的NAS存储转发到公网,内网的NAS IP是192.168.1.230,我们打算转发到公网的6101端口,并使用TCP映射。
[local_nas] type = tcp remote_port = 6101 local_port = 443 local_ip = 192.168.1.230
这样我们就把NAS的HTTPS端口映射到了frp.iam.lc:6101上。
而我们转发的实际上是HTTPS流量,我们还需要在证书扩展背景名称中将frp.iam.lc加进来。(仅适用于自签名证书)
0x03 将生产服务器的SSH安全转发到本地
在生产服务器,我们部署frpc客户端后,进行配置。
首先添加公共模块,如上所示。
然后我们添加SSH的STCP转发:
[stcp_ssh] # 对应Server Name,在客户端用到 type = stcp sk = your-secret # 密钥 local_ip = 127.0.0.1 # 转发本地流量,也可以转发其他主机流量 local_port = 22 # 转发端口,这里以SSH为例 tls_enable = true # tls加密 use_compression = true # 启用压缩
值得一提的是,STCP流量并不要求我们指定端口。
回到内网的Edge服务器,我们添加以下配置:
[stcp_ssh_client] type = stcp role = visitor server_name = stcp_ssh sk = your-secret bind_addr = 0.0.0.0 # 如果绑定0.0.0.0则会被内网所有主机访问。如果只想被自己访问则绑定127.0.0.1 bind_port = 6101 # 这是绑定到Edge的端口,和服务器无关。 use_encryption = true use_compression = true
至此,我们可以通过frp在内网通过堡垒机加密流量,ssh到公网服务器了。
假设Edge Server的IP是192.168.1.250,我们可以通过以下命令连接到公网服务器SSH:
ssh -p6101 192.168.1.250
0x04 客户端的自启动配置
非常简单,类似服务器配置:
# /etc/systemd/system/frpc.service [Unit] Description=Frp Client Service After=network.target [Service] Type=simple User=root Restart=on-failure RestartSec=5s ExecStart=/usr/bin/frpc -c /etc/frp/frpc.ini ExecReload=/usr/bin/frpc reload -c /etc/frp/frpc.ini [Install] WantedBy=multi-user.target
systemctl daemon-reload systemctl enable --now frpc