自由职业者的树莓派:内网穿透篇

摘要:本篇主要讲解如何使用开源软件 frp 进行内网穿透,将树莓派或其它电脑设备变成自己的云服务器,提供可灵活扩展的网站和工具服务,不再受制于高配置公有云主机的昂贵费用。内容涉及安装frp server 和 client 程序,进行相应的穿透配置,以及随时随地登录和访问部署的网站服务等。

阅读本篇之前,建议您先了解 基础准备篇 的内容。

对于云盘来说,是 P2P 的技术,所以远程访问时是互相知道对方的,可以直接同步数据。但如果在梅莓派上部署了网站服务,要让不知道访问来源的任何电脑访问,就不得行了。我们必须要有一个公网的 IP 地址,才能对外提供网站服务。

另外,对于自由职业者来说,如果要将重要的版本控制代码、项目资料托管在云主机上,总会感觉不那么安全;又或者,如果要开发、测试或演示自己的软件程序,部署大型程序非常占用主机资源,全部买云主机又不划算。这时,我们就可以用到今天说的内网穿透技术了。

我们可以通过手中的树莓派设备,用最小化配置和最简洁的方式,满足自由职业者随时随地能开发和演示的需要。

下面,我们就开始来亲手打造吧。

  1. 准备工作
  2. 云主机上安装 frp server
  3. 树莓派上安装 frp client
  4. 远程登录并访问树莓派服务

准备工作

先罗列一下本篇需要准备的资源:

  • 一台核心的树莓派4B设备(推荐:4GB+内存版本,32GB+ TF 卡,1TB 固态硬盘);
  • 一台带公网 IP 的低配置云主机(推荐:1核CPU、1GB内存即可、2MB带宽即可);
  • 一个域名(国内用请先备案)。

为了方便后续介绍,先做以下假定:
对云主机

  • 云主机公网 IP:11.22.33.44
  • 操作系统:Ubuntu,64-bit
  • 默认工作目录:~ (/home/ubuntu)

对树莓派设备

  • 树莓派内网 IP:192.168.31.199
  • 操作系统:Ubuntu on Raspberry Pi,64-bit
  • 默认工作目录:~ (/home/ubuntu)

然后,我们假定自己购买的域名为:mydomain.com,并已解析到公网 IP 11.22.33.44 上。另外假定:
访问云主机上服务的域名和端口:

  • frps dashboard:mydomain.com:7500

访问梅莓派上服务的二级域名:

  • frpc 管理控制台:frpc.mydomain.com
  • 容器管理控制台:portainer.mydomain.com
  • 私有网盘控制台:syncthing.mydomain.com
  • git 代码托管:code.mydomain.com
  • (未来可扩展更多可访问的服务)

云主机上安装 frp server

通过 ssh 登录到云主机。

ssh ubuntu@11.22.33.44

如果没有安装 wget 等相关工具,可提前用 snap 或 apt 安装好。

然后按如下命令安装 frps (frp server),您也可以再看看有没有 新版本

FRP_VERSION=0.42.0
# 下载并解压程序包
sudo wget https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_amd64.tar.gz
sudo tar -zxvf frp_${FRP_VERSION}_linux_amd64.tar.gz

# 复制程序和配置文件到系统目录
sudo mv frp_${FRP_VERSION}_linux_amd64 frp
sudo chmod +x frp/frps
sudo cp frp/frps /usr/bin/
sudo mkdir -p /etc/frp/
sudo cp frp/frps.ini /etc/frp/

# 复制 service 配置文件,以便开机以服务的方式自启动
sudo cp frp/systemd/frps@.service /lib/systemd/system/frps@.service

接下来,参考完整配置格式 frps_full.ini,并根据实际情况,编辑自己的配置文件 frps.ini。

sudo vi /etc/frp/frps.ini
# [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
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_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
vhost_http_port = 80
vhost_https_port = 443

# response header timeout(seconds) for vhost http server, default is 60s
# vhost_http_timeout = 60

# TcpMuxHttpConnectPort specifies the port that the server listens for TCP
# HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
# requests on one single port. If it's not - it will listen on this value for
# HTTP CONNECT requests. By default, this value is 0.
# tcpmux_httpconnect_port = 1337

# 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
dashboard_addr = 0.0.0.0
dashboard_port = 7500

# dashboard user and passwd for basic auth protect, if not set, both default value is admin
dashboard_user = admin
dashboard_pwd = mypassword

# enable_prometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port} in /metrics api.
enable_prometheus = true

# dashboard assets directory(only for debug mode)
# assets_dir = ./static
# console or real logFile path like ./frps.log
log_file = /var/log/frp/frps.log

# trace, debug, info, warn, error
log_level = info

log_max_days = 3

# disable log colors when log_file is console, default is false
disable_log_color = false

# DetailedErrorsToClient defines whether to send the specific error (with debug info) to frpc. By default, this value is true.
detailed_errors_to_client = true

# AuthenticationMethod specifies what authentication method to use authenticate frpc with frps.
# If "token" is specified - token will be read into login message.
# If "oidc" is specified - OIDC (Open ID Connect) token will be issued using OIDC settings. By default, this value is "token".
authentication_method = token

# AuthenticateHeartBeats specifies whether to include authentication token in heartbeats sent to frps. By default, this value is false.
authenticate_heartbeats = false

# AuthenticateNewWorkConns specifies whether to include authentication token in new work connections sent to frps. By default, this value is false.
authenticate_new_work_conns = false

# auth token
token = mytoken

# OidcClientId specifies the client ID to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
# By default, this value is "".
oidc_client_id =

# OidcClientSecret specifies the client secret to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
# By default, this value is "".
oidc_client_secret = 

# OidcAudience specifies the audience of the token in OIDC authentication if AuthenticationMethod == "oidc". By default, this value is "".
oidc_audience = 

# OidcTokenEndpointUrl specifies the URL which implements OIDC Token Endpoint.
# It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "".
oidc_token_endpoint_url = 

# 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
#allow_ports = 2000-3000,3001,3003,4000-50000

# pool_count in each proxy will change to max_pool_count if they exceed the maximum value
max_pool_count = 5

# max ports can be used for each client, default value is 0 means no limit
max_ports_per_client = 0

# TlsOnly specifies whether to only accept TLS-encrypted connections. By default, the value is false.
tls_only = false

# tls_cert_file = server.crt
# tls_key_file = server.key
# tls_trusted_ca_file = ca.crt

# 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
subdomain_host = mydomain.com

# 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

# specify udp packet size, unit is byte. If not set, the default value is 1500.
# This parameter should be same between client and server.
# It affects the udp and sudp proxy.
udp_packet_size = 1500

在官方的完整默认配置基础上,主要做了这几点改动:

dashboard_pwd = mypassword
log_file = /var/log/frp/frps.log
token = mytoken
#allow_ports = 2000-3000,3001,3003,4000-50000
subdomain_host = mydomain.com

说明:

  • dashboard_pwd:frps 管理控制台登录密码,建议您修改一下为自己的密码;
  • log_file:改放到系统常规放置的地方,注意 chmod 文件权限,否则可能出现无权限问题,比如:
# 创建日志文件
sudo mkdir -p /var/log/frp
sudo touch /var/log/frp/frps.log
sudo chmod -R 666 /var/log/frp/frps.log
  • token: frp client 与 server 之间的约定令牌,两端必须相同。建议自己通过 uuidgen 生成一个唯一的令牌,比如:
uuidgen | sed 's/-//g'
ff9acab846cd4de88d5bc8840fa181e2
  • allow_ports:为了方便开发和测试,暂不配置,即允许所有端口(待需要时再添加)。
  • subdomain:填写自己购买的域名,方便 vhost 配置的子域名访问。

接下来,因为配置文件名是frps.ini,所以使用 @frps.service 方式启动或管理服务。

# 注册和启动服务
sudo systemctl enable frps@frps.service
sudo systemctl start frps@frps.service

如果要检查是否启动成功、查看日志等,可以使用:

# 查询服务状态和日志
sudo systemctl status frps@frps.service
sudo journalctl -e -u frps@frps.service

其它可能还会偶尔用到的命令:

# 修改配置后,重启服务
sudo systemctl daemon-reload
sudo systemctl restart frps@frps.service
# 停止或关闭服务
sudo systemctl stop frps@frps.service
sudo systemctl disable frps@frps.service

补充1:
如果配置了 vhost 参数,希望直接使用 80 和 443 这种 1024 以下的端口,可能出现无法启动 frps 的情况,查看服务日志,可以看到是因为无权限。

frps[126933]: Create vhost http listener error, listen tcp 0.0.0.0:80: bind: permission denied

这时需要 +s 提升当前启动服务的用户权限:

sudo chmod u+s /usr/bin/frps
ls -l /usr/bin/frps
-rwsr-xr-x 1 root root 13205504 Oct 24 22:48 /usr/bin/frps

树莓派上安装 frp client

下载并解压程序包:

FRP_VERSION=0.42.0
sudo wget https://github.com/fatedier/frp/releases/download/v${FRP_VERSION}/frp_${FRP_VERSION}_linux_arm64.tar.gz
sudo tar -zxvf frp_${FRP_VERSION}_linux_arm64.tar.gz

# 复制程序和配置文件到系统目录:
sudo mv frp_${FRP_VERSION}_linux_arm64 frp
sudo chmod +x frp/frpc
sudo cp frp/frpc /usr/bin/
sudo mkdir -p /etc/frp/
sudo cp frp/frpc.ini /etc/frp/

复制 service 配置文件,以便开机以服务的方式自启动:

sudo cp frp/systemd/frpc@.service /lib/systemd/system/frpc@.service

接下来,参考完整配置格式 frpc_full.ini,并根据实际情况,编辑自己的配置文件 frpc.ini。

# [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"
server_addr = 11.22.33.44
server_port = 7000

# console or real logFile path like ./frpc.log
log_file = /var/log/frp/frpc.log

# trace, debug, info, warn, error
log_level = info

log_max_days = 3

# disable log colors when log_file is console, default is false
disable_log_color = false

# for authentication
token = mytoken

# set admin address for control frpc's action by http api such as reload
admin_addr = 0.0.0.0
admin_port = 7400
admin_user = admin
admin_pwd =mypassword
# Admin assets directory. By default, these assets are bundled with frpc.
# assets_dir = ./static

# connections will be established in advance, default value is zero
#pool_count = 5

# if tcp stream multiplexing is used, default is true, it must be same with frps
tcp_mux = true

# your proxy name will be changed to {user}.{proxy}
user = raspi

# 'ssh' is the unique proxy name
# if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh'
[ssh]
# tcp | udp | http | https | stcp | xtcp, default is tcp
type = tcp
local_ip = 127.0.0.1
local_port = 22
# limit bandwidth for this proxy, unit is KB and MB
bandwidth_limit = 2MB
# true or false, if true, messages between frps and frpc will be encrypted, default is false
use_encryption = false
# if true, message will be compressed
use_compression = false
# remote port listen by frps
remote_port = 6022

在官方的完整默认配置的基础部分之上,主要做了这几点改动:

server_addr = 11.22.33.44
log_file = /var/log/frp/frpc.log
token = mytoken
admin_pwd =mypassword
user = raspi
[ssh] bandwidth_limit = 2MB
[ssh] remote_port = 6022

说明:

  • server_addr:frps 所在云主机的公网 IP 地址;
  • log_file:改放到系统常规放置的地方,注意 chmod 文件权限,否则可能出现无权限问题,比如:
# 创建日志文件
sudo mkdir -p /var/log/frp
sudo touch /var/log/frp/frpc.log
sudo chmod -R 666 /var/log/frp/frpc.log
  • token: frp client 与 server 之间的约定令牌,两端必须相同;
  • admin_pwd:frp client 的管理控制台密码;
  • user: 这个相当于给下面的代理名称加个前缀,以避免同其它客户端的配置出现名称重复
  • [ssh] bandwidth_limit:限定带宽,可以根据您实际购买的公网带宽修改,或者注释掉。
  • [ssh] remote_port:公网 IP 上暴露的端口,用于远程登录时的访问。

有了以上的配置,我们其实就可以通过 ssh 远程登录到树莓派,进行相应的系统管理了。
不过,按我们的原定计划,继续配置各应用服务的代理,以便提供更多服务访问。

# Resolve your domain names to [server_addr] so you can use http://xxx.mydomain.com to browse xxx.
[frpc-admin-http]
type = http
local_port = 7400
subdomain = frpc

[portainer-http]
type = http
local_port = 9000
subdomain = portainer

[syncthing-http]
type = http
local_port = 8384
subdomain = syncthing

[gitea-http]
type = http
local_port = 3000
subdomain = code

# more ...

frp 支持的更多功能,请参见官方的帮助文档:https://github.com/fatedier/frp

接下来,因为配置文件名是frpc.ini,所以使用 @frpc.service 方式启动或管理服务。

# 注册和启动服务
sudo systemctl enable frpc@frpc.service
sudo systemctl start frpc@frpc.service

如果要检查是否启动成功、查看日志等,可以使用:

# 查询服务状态和日志
sudo systemctl status frpc@frpc.service
sudo journalctl -e -u frpc@frpc.service

其它可能还会偶尔用到的命令:

# 修改配置后,重启服务
sudo systemctl daemon-reload
sudo systemctl restart frpc@frpc.service
# 停止或关闭服务
sudo systemctl stop frpc@frpc.service
sudo systemctl disable frpc@frpc.service

注意
当有 frp 新版本,要进行升级时,请在本机或局域网内主机操作。因为,如果远程断开 frpc 服务后,就无法再连接上,只有回到本地网络中。当然,如果您有两个树莓派的话,也可以互相当跳板机进行升级。


远程登录并访问树莓派服务

现在,可以分别访问自己的服务网站了。

frps dashboard
frpc 管理控制台
portainer 控制台

还可以通过 ssh 远程访问树莓派,进行系统管理了:

sudo ssh ubuntu@11.22.33.44:6022
Welcome to Ubuntu ...
ubuntu@raspi:~$

恭喜您,已完成整个内网穿透的搭建。如果您还想扩展更多的开发或演示网站,添加相应的代理配置就可以了。

我是几昆虫,一个追求终身成长的努力者。感谢您完整阅读这篇文章,期待与您的思想相遇。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342