“远程办公” 整体解决方案-内网穿透

最近不用work overtime,可以抽出时间来折腾些有趣的事情;老是接触路由交换的抽象,枯燥的,东西也是挺没意思的。最近远程办公很刚需,远程会议是一方面,如果能实现家里或者公司地点‘电脑’级共享,那就再方便不过了。

最终要达到的效果:在准备一些简单的软硬件环境下,打通异地下的局域网环境,网络唤醒,随时随地访问内网资源。

添加图片注释,不超过 140 字(可选)

基本原理大概是:内网被访问的client发起到云主机(公网ip)的连接,云主机记录该会话的状态信息,其他client访问该云主机对应端口,云主机依据记录的会话状态信息封装后转发该数据到被访问的client,然后由被访问client解封装后转发数据到内网其他设备及服务,包括转发到自身,即反向代理原理。好了,let’s go

文章分为以下几个部分:

1:需准备的软硬件

》frp

》云主机

》树莓派Pi

2:部署

》frps

》frpc

》Magic Packet

3:安全的访问

》frp stcp

》fail2Ban

》2FA

1:需准备的软硬件

软件:FRP,一个开源的项目

硬件:一个有公网ip的云主机,例如Ucloud腾讯云;一个7×24开机的微型电脑(非必须),例如树莓派 Raspberry Pi

有一个反向代理的工具是必须的,类似的方案很多,推荐一个国人开源的项目FRP,’frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet’ ,这是项目主页对这个工具的介绍,github上目前已经有37K star,人气很高啊,而且是合规合法的项目。

FRP项目架构

有一个公网ip的云主机是必须的,它来负责所有互访流量的转发,随着那啥新基建,云主机的价格进一步下跌,最近公有云厂商618还有活动。

UCloud 1核 2G 2M 40G SSD 88元一年,

腾讯云 1核 2G 1M 50G SSD 95元一年。

去年双11抢购的腾讯三年期基本款的云主机,不到300大洋

腾讯云主机

有一个低功耗,小巧便携微型电脑 树莓派更方便,但不是必须的,只能说有了它,会有更多玩法。今年618买了一个旧款的Pi,全套配件不到300大洋。

Pi 3 B+,发热功耗更低,5w差不多
纯单板200内
添加图片注释,不超过 140 字(可选)

2:部署

既然软硬件都具备了,就可以开始部署了

以我买的腾讯云为例,主机买好了后,会分配你一个公网ip,ssh登录默认用户名密码会短信发你,也可到控制台查看或更改密码,安装系统时我选择的是centos 7.5,部署云主机选择可用区时请选择距离你工作生活地域比较近的,这样网络延时更低。

从github 下载frp合适的版本到云主机:以我的centos 64位为例

Mac:frp_0.33.0_darwin_amd64.tar.gz

Pi:frp_0.33.0_linux_arm.tar.gz

添加图片注释,不超过 140 字(可选)
[root@VM_0_2_centos software_install]# wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_amd64.tar.gz
[root@VM_0_2_centos software_install]# tar -zxvf frp_0.33.0_linux_386.tar.gz

云主机作为server端,解压下载后的tar包,编辑frps.ini配置文件:

详细功能配置请参考项目

https://github.com/fatedier/frp/blob/master/README.md

修改 frps.ini 文件,这里使用了最简化的配置,设置了 frp 服务器端接收客户端流量的端口:

# frps.ini
[common]
bind_port = 7000

启动 frps:(可以自定义一个Linux服务并设置开机启动,请参考systemd文件夹下文件)

1:如果没有Pi, Windows 10 ,我的是64位,作为client,下载合适版本并解压:

下载frp_0.33.0_windows_amd64.zip,解压:

以转发Windows 10 RDP(Remote Desktop Protocol)服务为例,修改 frpc.ini 文件,假设 frps 所在服务器的公网 IP 为 x.x.x.x:

# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000

[rdp]
type = tcp
# Windows 10所在ip,即本机
local_ip = 127.0.0.1
local_port = 3389
remote_port = 3389

注意,local_port(客户端侦听)和remote_port(服务器端暴露)是用来出入 frp 系统的两端,server_port则是服务器用来与客户端通讯的。

cmd窗口,启动 frpc:

./frpc -c ./frpc.ini

在任意一台可以上网的设备上访问Windows RDP服务:

添加图片注释,不超过 140 字(可选)
添加图片注释,不超过 140 字(可选)

2:Pi,我的为ARM CPU 32位,作为client端并与Windows同一个局域网,下载合适版本并解压:

pi@raspberrypi:~$ sudo wget https://github.com/fatedier/frp/releases/download/v0.33.0/frp_0.33.0_linux_arm.tar.gz
pi@raspberrypi:~$ sudo tar -zxvf frp_0.33.0_linux_arm.tar.gz
以转发Windows 10 RDP(Remote Desktop Protocol)服务为例,修改 frpc.ini 文件,假设 frps 所在服务器的公网 IP 为 x.x.x.x:
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000

[rdp]
type = tcp
# Windows 10所在ip,
local_ip = 192.168.1.10
local_port = 3389
remote_port = 3389

注意,local_port(客户端侦听)和remote_port(服务器端暴露)是用来出入 frp 系统的两端,server_port则是服务器用来与客户端通讯的。

启动 frpc:(可以自定义一个Linux服务并设置开机启动,请参考systemd文件夹下文件)

./frpc -c ./frpc.ini

在任意一台可以上网的设备上访问Windows RDP服务:

添加图片注释,不超过 140 字(可选)
添加图片注释,不超过 140 字(可选)

以上,部署完毕后,只要有网络,在任何一台设备上,都可以访问家里或者公司电脑RDP服务了,类似teamview那样远程控制软件达到的效果,而且比那种商业软件来的靠谱,方便。当然前提是被控制电脑RDP服务已经开启。

开启Windows 10 RDP服务:Windows10搜索框-远程协助

添加图片注释,不超过 140 字(可选)

3:网络唤醒(Wake On LAN)

你的主力电脑不可能24小时开机吧,那么当关机后,必须能通过网络的方式远程去开机,才能去保证随时随地的远程访问,21世纪的电脑主板都是支持网络唤醒的。

网络唤醒是主板支持的一项公有标准技术,当电脑关机或者休眠时,主板上网卡在超低功耗下监听特定的报文,当收到一种特殊的数据包时会引导主机开机,从而实现网络唤醒,本文讨论AMD 公司提出的 Magic Packet(幻数据包,魔术包)唤醒方式。

幻数据包最简单的构成是6字节的255(FF FF FF FF FF FF),紧接着为目标计算机的48位MAC地址,重复16次,数据包共计102字节。有时数据包内还会紧接着4-6字节的密码信息。这个帧片段可以包含在任何协议中,最常见的是包含在 UDP 中,port 7 或9

FF FF FF FF FF FF | MAC 地址 × 16 | 4-6字节的密码(可空)

在Windows和Pi在同一个局域网段时:

wireshark抓包数据包格式:

添加图片注释,不超过 140 字(可选)
某个破软件,更直观点

既然知道了数据包格式,就可以着手构造了,写一个python唤醒脚本:

在Pi下运行该脚本:./Magic_packet.py 测试OK。如果需要广域网唤醒,请修改相应信息并转发udp.port == 7的流量到内网

#!/usr/bin/python3.7

import socket
import struct
import re

mac='D0-50-99-83-E3-AA'  # 目标主机网卡Mac地址,请修改!
mac_address = ''.join(re.split(r'[-.:]', mac))
BROADCAST = "192.168.1.255"  # 目标主机网卡ip网络地址,请修改!
data = ''.join(['FFFFFFFFFFFF', mac_address * 16])  # 构造原始数据格式

send_data = b''
# 把原始数据转换为16进制字节数组,
for i in range(0, len(data), 2):
        send_data = b''.join([send_data, struct.pack('B', int(data[i: i + 2], 16))])

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.sendto(send_data, (BROADCAST, 7))
sock.close()

另外:Windows的网络唤醒功能 可能需要开启下,BIOS或者系统相关的设置,请自行search一下

以上,FRP本身只提供基于端口转发,至于上层是什么应用并不关心,你可以根据需要转发任何你想要部署的服务,例如文件夹共享服务,web服务,SSH等,但是像一些双信道协议的服务暂时还不支持,例如FTP协议,SIP等,如果确实需要可以通过在端口转发基础上部署VPN服务,然后基于IP转发。

3:安全的访问

基于前面的工作,功能的实现是没什么问题了,但是对于暴露到互联网上的那些服务,安全是个严重的问题,每一秒都会有人在扫描你的服务并暴力破解你的密码,因为便于你便捷的访问,没有做访问源的限制,别人也可以访问,一旦你设置了弱口令被暴力破解了,那就裸奔了。

先感受下:全是别人尝试登录de失败记录

添加图片注释,不超过 140 字(可选)
添加图片注释,不超过 140 字(可选)

针对这个问题,有两种方式:限制访问源,牺牲便捷;2FA,即二次认证,本地密码+动态口令。

限制访问源:

  • FRP 安全的访问STCP,自身提供的功能
  • fail2Ban Linux自带的入侵检测服务

STCP

是FRP的一个小特性,在访问者和被访问者设备上都需要配置frpc client客户端,限定特定设备才能去访问你的内网的服务,以在Windows环境下部署为例,没有Pi:

启动 frpc,转发内网的 RDP 服务,配置如下,不需要指定远程端口:

# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000

[secret_rdp]
type = stcp
# 只有 sk 一致的用户才能访问到此服务
sk = abcdefg
local_ip = 127.0.0.1
local_port = 3389

在要访问这个服务的机器上启动另外一个 frpc,配置如下:

# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000

[secret_rdp_visitor]
type = stcp
# stcp 的访问者
role = visitor
# 要访问的 stcp 代理的名字
server_name = secret_rdp
sk = abcdefg
# 绑定本地端口用于访问 rdp 服务
bind_addr = 127.0.0.1
bind_port = 6000

type = stcp # stcp 的访问者 role = visitor # 要访问的 stcp 代理的名字 server_name = secret_rdp sk = abcdefg # 绑定本地端口用于访问 rdp 服务 bind_addr = 127.0.0.1 bind_port = 6000

添加图片注释,不超过 140 字(可选)

这种方式有点类似SSH 动态端口转发,关键点是visitor服务名字要对应且不能重复

什么是fail2Ban?

Fail2Ban 是一款入侵防御软件,可以保护服务器免受暴力攻击。 它是用 Python 编程语言编写的。 Fail2Ban 基于auth 日志文件工作,默认情况下它会扫描所有 auth 日志文件,如 /var/log/auth.log、/var/log/apache/access.log 等,并禁止带有恶意标志的IP,比如密码失败太多,寻找漏洞等等标志。

通常,Fail2Ban 用于更新防火墙规则,用于在指定的时间内拒绝 IP 地址。 它也会发送邮件通知。 Fail2Ban 为各种服务提供了许多过滤器,如 ssh、apache、nginx、squid、named、mysql、nagios 等。

Fail2Ban 能够降低错误认证尝试的速度,但是它不能消除弱认证带来的风险。 这只是服务器防止暴力攻击的安全手段之一

但是由于FRP的部署特性,目前不能获取到真实源地址,所以这个方案不太合适,而且限制公网ip源地址的方案本身就不是太靠谱,我没有部署,有兴趣的同学可以去尝试下。

2FA:

这个是我要重点推荐的方案,靠谱,成熟,部署简单,Google开源的一个动态口令认证方案,支持主流服务,SSH,OpenVPN等,支持国外主流网站二次验证。

科普视频请参考这个:

https://www.bilibili.com/video/BV1Rp411f78Q?vd_source=1543e097db211c58d6893668324a3a7e

以SSH服务为例:

centos7 详细部署请参考这个:

https://www.techrepublic.com/article/how-to-set-up-two-factor-authentication-on-centos-7/
https://www.techrepublic.com/article/how-to-set-up-two-factor-authentication-on-centos-7/

Pi详细部署请参考这个:

https://www.raspberrypi.com/news/setting-up-two-factor-authentication-on-your-raspberry-pi/

我自己的centos 7环境部署如下:

安装google-authenticator 开源库,在你想启用二次验证的对应的用户下安装:

[centos@centos7 ~]$ sudo yum install google-authenticator
生成动态口令相关配置及文件:
[centos@centos7 ~]$ google-authenticator

Do you want authentication tokens to be time-based (y/n) y
Warning: pasting the following URL into your browser exposes the OTP secret to Google:
  https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/centos@centos7.linuxvmimages.com%3Fsecret%3D7KEETF3SZI6U5MUY6UW2RYFVDY%26issuer%3Dcentos7.linuxvmimages.com
二维码在这。。。
请拿出手机上的APP扫码这个。。。
Your new secret key is: 7KEETF3SZI6U5MUY6UW2RYFVDY
Your verification code is 115666
Your emergency scratch codes are:当手机丢失,请用这些一次性的验证码登录然后重新配置,请保存到其他地方
  28795299
  65495311
  88449266
  32078888
  73519255

Do you want me to update your "/home/centos/.google_authenticator" file? (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y 选择Y,不允许一码多用

By default, a new token is generated every 30 seconds by the mobile app.
In order to compensate for possible time-skew between the client and the server,
we allow an extra token before and after the current time. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) n 没必要,时间同步是靠谱的

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting? (y/n) y 对错误尝试的验证做限制
[centos@centos7 ~]$ 

配置SSH服务:

添加到最后面,先输入本地密码,然后再输入验证码

添加到最前面,先输入验证码,再输入本地密码

sudo nano /etc/pam.d/sshd 以下为配置文件内容

#%PAM-1.0
auth       required     pam_sepermit.so
auth       substack     password-auth
auth       include      postlogin
# Used with polkit to reauthorize users in remote sessions
-auth      optional     pam_reauthorize.so prepare
account    required     pam_nologin.so
account    include      password-auth
password   include      password-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    required     pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open env_params
session    required     pam_namespace.so
session    optional     pam_keyinit.so force revoke
session    include      password-auth
session    include      postlogin
# Used with polkit to reauthorize users in remote sessions
-session   optional     pam_reauthorize.so prepare
# 以下为添加的内容,启用2FA相关配置
#auth sufficient pam_google_authenticator.so 本地密码或验证码,满足一方即可,但是不是太好使
auth required pam_google_authenticator.so

关联SSH登录 需要二次验证:

nano /etc/ssh/sshd_config
改变为如下行:
ChallengeResponseAuthentication yes

重启SSH服务:

sudo systemctl restart sshd

二次验证登录:

APP store搜索authen关键字,单机软件,都可以用

添加图片注释,不超过 140 字(可选)
添加图片注释,不超过 140 字(可选)

小特性:只对在本地网络外的登录启用两步验证,本地网络不需要两步验证,更方便了

建立一个文件(比如/etc/security/access-local.conf),然后仿照下面的例子配置你想要跳过两步验证的网络地址:

# only allow from local IP range
+ : ALL : 192.168.0.0/16
+ : ALL : 10.0.0.0/8
# Additional network: VPN tunnel ip range (in case you have one)
+ : ALL : 172.16.0.0/12
#+ : ALL : LOCAL
- : ALL : ALL

然后编辑你的/etc/pam.d/sshd,添加这一行:

#%PAM-1.0
#auth     required  pam_securetty.so     #disable remote root
##############以下为添加的一行
auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-local.conf
###########以上为添加的一行
auth      required  pam_google_authenticator.so
auth      include   system-remote-login
account   include   system-remote-login
password  include   system-remote-login
session   include   system-remote-login

以上的以上,到此就比较放心使用了。

one more thing:

密码+token

token+密码

单独token

所有组合都可以

thanks!

本文献给‘996’的你们,好好搬砖,少摸鱼

注:996工作制,是指一种“晚上9点睡觉,早上9点起床,每天工作6小时的工作习惯

‘sleep at 9 get up at 9 work 6 hours a day’

Related Posts

Leave a Reply

Your email address will not be published.