Docker环境中使用frp内网穿透服务
因为本人习惯用docker跑服务,所以对于frp的内网穿透服务我使用的也是docker。
frp在github上的项目地址为:https://github.com/fatedier/frp,里面有相关文档,可以参考配置。由于我使用的docker,自己做了个简单的记录。
frp的docker项目的服务器端和客户端是独立的,地址分别为:https://hub.docker.com/r/snowdreamtech/frps和https://hub.docker.com/r/snowdreamtech/frpc,文档说明地址为:https://gofrp.org/docs/。
先安装服务器端的frp,这台服务器在公网中,作为内网穿透的服务器。
拉取镜像:
docker pull snowdreamtech/frps
建立容器:
docker run \
--restart=always \
--network host -d \
-v /docker/frp/frps.ini:/etc/frp/frps.ini \
--name frps \
snowdreamtech/frps
由于挂载了文件到宿主主机,可以直接在宿主主机上编辑/docker/frp/frps.ini进行配置:
[common]
bind_port = 7000
# 启用面板
dashboard_port = 7500
# 面板登录名和密码
dashboard_user = admin
dashboard_pwd = 12345678
# 使用http代理并使用8888端口进行穿透
vhost_http_port = 8888
# 使用https代理并使用9999端口进行穿透
vhost_https_port = 9999
服务器上的frp安装配置完成,然后在你内网的客户机上安装frp。
拉取镜像:
docker pull snowdreamtech/frpc
建立容器:
docker run \
--restart=always \
--network host -d \
-v /docker/frp/:/etc/frp/ \
--name frpc \
snowdreamtech/frpc
在配置客户端前,我先说下这台内网机器上我跑的docker服务:
1、使用nginx和php的docker跑了2个网站,通过虚拟主机方式配置了2个网站,在虚拟网站配置文件中,server_name的域名指向分别为one.yourdomain.com和two.yourdomain.com,一个使用的80端口一个使用443端口,并在域名商处将域名全部解析到frp的服务器的ip上(注意不是客户端,因为客户端在内网没有公网ip,需要通过服务器的公网ip来穿透),在同个端口下配置了多个虚拟主机也能通过自定域名来区分服务。
使用nginx虚拟主机配置为(以https为例子):
server {
listen 443 ssl http2;
server_name two.yourdomain.com;
index index.html index.htm index.php;
root /usr/share/nginx/html/two.yourdomain.com;
ssl_certificate /etc/nginx/SSL/two.yourdomain.com.cer;
ssl_certificate_key /etc/nginx/SSL/two.yourdomain.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
ssl_session_cache builtin:1000 shared:SSL:10m;
location ~ [^/]\.php(/|$) { # 处理php文件交给php容器
root /var/www/html/two.yourdomain.com;
fastcgi_pass php:9000; # php的容器名和端口
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/two.yourdomain.com$fastcgi_script_name;
include fastcgi_params;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
}
location ~ .*\.(js|css)?$ {
expires 12h;
}
location ~ /.well-known {
allow all;
}
location ~ /\. {
deny all;
}
access_log /var/log/nginx/two.yourdomain.com.log;
}
2、安装了filebrowser容器,宿主主机与容器的端口映射为8004:80,nginx配置中使用了反代但未启用ssl,已将要使用的域名file.yourdomain.com解析到公网服务器主机的ip上。
基本配置如下:
server {
listen 80;
server_name file.yourdomain.com;
# Allow large attachments
client_max_body_size 128M;
location / {
proxy_pass http://file; # filebrowser的容器名(默认端口80)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
access_log /var/log/nginx/file.yourdomain.com.log;
}
3、安装了bitwarden的容器,已将宿主主机的8008映射到了容器的80端口,同时配置了nginx进行反代,并已设置了ssl证书,已将要使用的域名pwd.yourdomain.com解析到公网服务器主机的ip上;
nginx反代bitwarden的配置文件为:
server
{
listen 443 ssl http2;
server_name pwd.yourdomain.com;
ssl_certificate /etc/nginx/SSL/pwd.yourdomain.com.cer;
ssl_certificate_key /etc/nginx/SSL/pwd.yourdomain.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
ssl_session_cache builtin:1000 shared:SSL:10m;
# Allow large attachments
client_max_body_size 128M;
location / {
proxy_pass http://bitwarden; # bitwarden的容器名(容器端口80)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /admin {
# See: https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/
auth_basic "Private";
auth_basic_user_file /etc/nginx/htpasswd;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://bitwarden; # bitwarden的容器名(容器端口80)
}
access_log /var/log/nginx/pwd.yourdomain.com.log;
}
按以上的实际情况,我在frp的客户端上进行了如下配置:
[common]
# server_addr为frp的服务器ip或已解析到该ip的域名
server_addr = x.x.x.x
# server_port与服务端配置bind_port一致
server_port = 7000
[ssh]
# 开启ssh内网穿透
type = tcp
local_ip = 127.0.0.1
# 内网主机ssh端口
local_port = 22
# 远程连接ssh的端口
remote_port = 2222
[http web1]
# 第一个网站,nginx配置为http访问
type = http
local_port = 80
custom_domains = one.yourdomain.com
[https web2]
# 第二个网站,nginx配置为https访问
type = https
local_port = 443
custom_domains = two.yourdomain.com
[https filebrowser]
# filebrowser容器,直接使用https访问
type = https
custom_domains = file.yourdomain.com
plugin = https2http
# 宿主机ip地址和宿主机映射到容器的端口8004
plugin_local_addr = 127.0.0.1:8004
plugin_crt_path = /etc/frp/yourdomain.com.cer
plugin_key_path = /etc/frp/yourdomain.com.key
# 宿主机的ip地址
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
[https bitwarden]
# bitwarden容器,已使用nginx反代并配置为https访问
type = https
local_port = 443
custom_domains = pwd.yourdomain.com
注意:上面filebrowser和bitwarden的https配置是可以互换的,区别在于,一个直接访问容器,一个通过nginx容器进行了反代。在上面的例子中,filebrowser安装后,没有配置nginx,直接在frp配置文件中设置ssl证书,使用宿主机映射的端口8004映射到容器,完成加密传输;bitwarden安装后,配置了nginx,在nginx的配置文件中设置了ssl证书,并进行了反代,使用nginx容器的443端口完成加密传输。
安装配置完成后,即可通过http://one.yourdomain.com:8888、https://two.yourdomain.com:9999、https://file.yourdomain.com:9999和https://pwd.yourdomain.com:9999分别穿透内网,并访问不同的服务了。
对于使用ssh等其它服务,可以参考官方文档进行配置,大同小异,只是配置不同而已。gitee上的文档也可参考:https://gitee.com/yijicai/frp#frp
注意,如果要使用frp进行内网穿透,需要注意以下几点:
vhost_http_port和vhost_https_port只能设置一个,但我要运行多个http服务,可以使用subdomain_host进行区别,具体如下:
1、在Docker的Nginx容器中做好端口映射,明确宿主主机和nginx容器之间的端口关系,如:
-p 9080:80
-p 9081:81
-p 9082:82
-p 9083:83
-p 9443:443
2、在frps.ini中添加:
subdomain_host = psay.cn
3、在frpc.ini中修改,主要就是删除custom_domains,并添加subdomain:
[web d.psay.cn]
type = http
# 第一个http服务的宿主主机端口9080,nginx容器的配置文件监听映射端口80
local_port = 9080
# 使用二级域名d.psay.cn区分
subdomain = d
[web a.psay.cn]
type = http
# 第二个http服务的宿主主机端口9081,nginx容器的配置文件监听映射端口81
local_port = 9081
# 使用二级域名a.psay.cn区分
subdomain = a
proxy_protocol_version = v2
[web a2.psay.cn]
type = http
# 第三个http服务的宿主主机端口9082,nginx容器的配置文件监听映射端口82
local_port = 9082
# 使用二级域名a2.psay.cn区分
subdomain = a2
proxy_protocol_version = v2
贴一个我使用没问题的配置文件,一个aria远程下载的ui界面,一个aria2服务,一个php的filerun用来查看下载内容(包括nginx和frp):
第一个nginx配置,反代并运行php文件(跑FileRun):
server {
listen 80;
server_name d.psay.cn;
index index.html index.php;
root /usr/share/nginx/html/d.psay.cn;
location ~ [^/]\.php(/|$) { # 处理php文件交给php容器
root /var/www/html/d.psay.cn;
fastcgi_pass php:9000; # php的容器名和端口
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /var/www/html/d.psay.cn$fastcgi_script_name;
include fastcgi_params;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
expires 30d;
}
location ~ .*\.(js|css)?$ {
expires 12h;
}
location ~ /.well-known {
allow all;
}
location ~ /\. {
deny all;
}
access_log /var/log/nginx/d.psay.cn.log;
}
第二个nginx配置文件,反代ariang可视界面:
server {
listen 81 proxy_protocol;
server_name a.psay.cn;
# 配合使用proxy_protocol获取真实ip
real_ip_header proxy_protocol;
real_ip_recursive on;
set_real_ip_from 172.18.0.1;
# Allow large attachments
client_max_body_size 128M;
location / {
proxy_pass http://ariang:6880; # aira2容器名和端口
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
access_log /var/log/nginx/a.psay.cn.log;
}
第三个nginx配置文件,反代aria2下载服务:
server {
listen 82 proxy_protocol;
server_name a2.psay.cn;
# 配合使用proxy_protocol获取真实ip
real_ip_header proxy_protocol;
real_ip_recursive on;
set_real_ip_from 172.18.0.1;
# Allow large attachments
client_max_body_size 128M;
location / {
proxy_pass http://aria2:6800; # aira2容器名和端口
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
access_log /var/log/nginx/a2.psay.cn.log;
}
frp server 配置文件:
[common]
# 客户端连接服务端的端口和允许的IP
bind_addr = 0.0.0.0
bind_port = 9000
# 启用面板指定端口
dashboard_port = 9001
# 设置udp端口,以帮助udp进行nat穿透
bind_udp_port = 9002
# 用于kcp协议的udp端口,它可以与'bind_port'相同。
# 如果不设置,在frps中kcp默认禁用。
kcp_bind_port = 9000
# 面板登录名和密码
dashboard_user = admin
dashboard_pwd = admin
# 定义主域名,此处配置了,客户端中不能配置custom_domains,相应的增加subdomain指定子域
subdomain_host = psay.cn
# 指定http和https代理端口进行穿透
vhost_http_port = 9080
vhost_https_port = 9443
# 认证token
token = ed2c4592529
# 日启并设置日志存储路径
log_file = /etc/frp/frps.log
# 日志级别:trace, debug, info, warn, error
log_level = info
# 日志存储天数
log_max_days = 3
frp client 配置文件:
[common]
# server_addr为frp的服务器ip或已解析到该ip的域名
server_addr = x.x.x.x
# server_port与服务端配置bind_port一致
server_port = 9000
# 认证token
token = ed2c4592529
[ssh]
# 开启ssh内网穿透
type = tcp
local_ip = 127.0.0.1
# 内网主机ssh端口
local_port = 22
# 远程连接ssh的端口
remote_port = 2020
[web d.psay.cn]
type = http
local_port = 9080
# 二级域名 d.psay.cn
subdomain = d
[web a.psay.cn]
type = http
local_port = 9081
# 二级域名 a.psay.cn
subdomain = a
proxy_protocol_version = v2
# ui界面设置账户密码
http_user = webmaster
http_pwd = webmaster
[aira2 a2.psay.cn]
type = http
local_port = 9082
# 二级域名 a2.psay.cn
subdomain = a2
proxy_protocol_version = v2