2022年3月中国全国COVID-19疫情呈多点位大规模爆发趋势,截止本文编写时,相较昨日新增COVID-19病例14288例,现有确诊COVID-19病例277800例,同事不幸“中招”成为密切接触者,我也就作为次密切接触者,根据要求进行隔离观察,此时接入公司网络进行远程办公就成为了迫在眉睫需要解决的事情。

安装ocserv
因为vSphere连接不上,建不了虚拟机,无奈只好找了台之前做渗透测试的kali,先临时搭建起来一下,kali是一款为信息安全人员设计的Debian Linux衍生版本,版本信息如下:
Distributor ID: Kali
Description: Kali GNU/Linux Rolling
Release: 2022.1
Codename: kali-rolling
时间紧迫就不编译安装了,毕竟也不需要什么特殊的调整,性能上也没什么差异
sudo apt install ocserv
yum install epel-release
配置ocserv
出于信息安全考虑,本章节信息会进行必要的脱敏处理
其实无论是编译安装还是使用软件源安装都是比较简单的,难点在于配置,官方配置文档给出了28kb的参考配置文件,我直接一句好家伙,大体看了看发现ocserv提供了非常全面的自定义选项,从网络工程师那边儿要了个172.16.128.0/24网段和公网IP的端口,就开始配置,首先根据官方配置文档进行调整,删除大量注释和官方不推荐开启的、不需要的配置,开启一些优化功能,尽可能保持最简,配置文件和配置介绍如下:
#VPN登录验证方式
#如果有多个验证方式,则必须全部通过验证
#账号密码验证
auth = "plain[passwd=/etc/ocserv/ocpasswd]"
#用户证书验证
auth = "certificate"
#监听端口
tcp-port = 20000
udp-port = 20000
# 运行ocserv的用户,更合规的区分权限,例如为ocserv建立单独的系统账户,图省事也可以使用nobody用户和daemon组
run-as-user = ocserv
run-as-group = ocserv
#IPC (worker-main) 的套接字文件位置
socket-file = /run/ocserv.socket
# 用于协商对称加密密钥的非对称加密公钥和私钥,两者也称为“密钥对”(其实就是网站的https证书)
# 虚拟专用连接(VPN)与https类似,最终的数据传输将采用效率更高的对称加密算法对通讯数据进行加密,此时服务端与客户端就需要在不被信任的网络环境下交换对称加密算法密码,此时使用非对称加密算法来进行协商,保证对称加密算法密码的安全交换
# 经过测试,支持RSA和ECC两种非对称加密算法,没有测试国密SM算法。如何生成证书不再赘述。
server-cert = /etc/ocserv/ssl/certs/公钥.pem
server-key = /etc/ocserv/ssl/private/私钥.key
# Diffie-Hellman parameters,增强非对称加密算法在协商对称加密算法密码时的安全性,直接注释掉就可以,但是我配了。
dh-params = /etc/ocserv/ssl/Diffie-Hellman/dh.pem
#用户根证书
#使用证书验证方式时,需要为服务端配置根证书以验证客户证书。
ca-cert = /etc/ocserv/ssl/certs/用户根证书.pem
# 限制客户端数量,取消设置或设置为零以表示无限制。
max-clients = 128
# 限制客户的多点登录(即使用不同设备登录同一账号)。取消设置或设置为零以表示无限制。
max-same-clients = 2
# 统计信息重置时间
# 每日:86400,每周:604800
server-stats-reset-time = 604800
# 存活检测(秒)
keepalive = 300
# Dead peer detection in seconds.
# Note that when the client is behind a NAT this value
# needs to be short enough to prevent the NAT disassociating
# his UDP session from the port number. Otherwise the client
# could have his UDP connection stalled, for several minutes.
# 一种存活检测,没看懂啥意思,我的分析如下:
# ocserv有两种模式
#第一种是将ocserv作为网关(或者说路由器),建立虚拟网卡将客户端分配在自己虚拟网卡的子网中,类似VMware虚拟网络中的NAT模式,此时vpn客户端可以访问内网应用,因为服务端在公司局域网中,但是公司局域网中的用户不可以访问vpn客户端,因为vpn客户端在vpn服务端的子网下而不是公司网关的子网下,公司网关如不配置下一跳是无法实现访问的。
#第二种是在公司局域网的网关中去申请子网,例如192.168.128.0/24、172.16.128.0/24等,此时客户端在公司局域网网关子网下,通过路由追踪命令可以查看到先请求到vpn网段的虚拟网关,然后直接跳到下一个地址,而不是先请求到vpn服务端
#说了这么多,终于来到重点,当第二种情况下时,且vpn服务端配置路由信息为空或0.0.0.0/0,此时vpn客户端全部流量都走vpn,这时vpn客户端打开p2p下载软件,此时会使用路由器的UPnP,打通一条udp的内网映射,这个配置应该跟这个有关。。。
dpd = 60
#上述配置的手机端配置,如果设置太低可能会增加耗电
mobile-dpd = 300
# DTLS超时,如果超时则由UDP模式切换至TCP模式
switch-to-tcp-timeout = 25
# MTU 发现(必须启用 DPD)
try-mtu-discovery = true
# 将用于读取客户端证书中的用户 ID 的对象标识符
# 支持的OIDs:
# CN = 2.5.4.3, UID = 0.9.2342.19200300.100.1.1
# 因为开启了双因素认证,所以要配置此选项,这个选项代表从证书的哪个部分获取用户名,一般来说是CN,即“使用者”
cert-user-oid = 2.5.4.3
# 将用于读取客户端证书中的用户组的对象标识符
# 仅支持:
# OU (organizational unit) = 2.5.4.11
# 我单位部门比较多,所以生成证书的时候区分一下
cert-group-oid = 2.5.4.11
# 上述“ca-cert”颁发的证书的撤销列表。
# 请参阅手册以最初生成空 CRL。当 ocserv 检测到文件中的更改时,将定期重新加载 CRL。
# 如果没有需求可以注释掉,其实如果是双因素认证直接从ocpasswd删掉用户就足以阻断登录了
crl = /etc/ocserv/ssl/crl/crl.pem
# 启用压缩协商(LZS、LZ4)
compression = true
# 小于配置值的包将不会压缩
# 例如VoIP的包通常小于256
no-compress-limit = 256
# 客户端验证阶段超时(秒)
auth-timeout = 240
# 无流量断开超时(秒),超过这个时间无流量则断开. Unset to disable.
idle-timeout = 1200
# 移动端无流量断开超时(秒),超过这个时间无流量则断开. Unset to disable.
mobile-idle-timeout = 1800
# 在 ocserv 中禁止客户端时使用的是积分系统。积分超过该配置的 IP 地址将被禁止 min-reauth-time 秒
# 默认情况下,错误密码尝试为 10 分,KKDCP POST 为 1 分,连接为 1 分。
# 请注意,由于涉及到不同的过程,点的计数将不是实时精确的。
#
# 当从 HTTP 服务器本地接收代理连接时(即使用 listen-clear-file 时),不能可靠地使用分数禁止。
#
# Set to zero to disable.
max-ban-score = 80
# 重置为客户端积分的时间(以秒为单位)。
ban-reset-time = 300
# 如果您想更改默认积分值
#ban-points-wrong-password = 10
#ban-points-connection = 1
#ban-points-kkdcp = 1
# Cookie 超时 (in seconds)
# 一旦客户端通过身份验证,就会获得 cookie,客户端可以使用该 cookie 重新连接。
# 如果未在此超时值内使用,该 cookie 将失效。
# 此 cookie 在用户连接期间保持有效,并且在用户断开连接后,在该配置设置的时间内保持有效状态。
# 该设置用于允许在不同网络之间漫游。
cookie-timeout = 300
# 是否允许漫游,如果为 true则 cookie 仅限于单个 IP 地址,并且不能从不同的 IP 重复使用。
deny-roaming = false
# ReKey time (in seconds)
# ocserv 将要求客户端在经过此秒数后定期刷新密钥。 设置为零以禁用(请注意,如果禁用 rekey,某些客户端会连接失败)。
rekey-time = 172800
# ReKey 方式
# Valid options: ssl, new-tunnel
# ssl: 将在通道上执行有效的重新握手,从而在重新生成密钥期间实现无缝连接。
# new-tunnel: 将指示客户端断开并重新建立VPN。 仅当连接的客户端对 ssl 选项有问题时才使用此选项.
rekey-method = ssl
# 是否启用对 occtl 工具的支持 (i.e., either through D-BUS, or via a unix socket).
use-occtl = true
# PID 文件. 可以在命令行中覆盖.
pid-file = /run/ocserv.pid
# The name to use for the tun device虚拟网卡名称
device = vpns
# 生成的IP是否可以预测,即IP在可能的情况下对同一用户保持不变
predictable-ips = true
# 默认域名,不影响服务
default-domain = example.com
# VPN租约的地址池
ipv4-network = 192.168.0.0
ipv4-netmask = 255.255.255.0
# 指定网络的另一种方法:
#ipv4-network = 192.168.1.0/24
# IPv6租约地址池.
#ipv6-network = fda9:4efe:7e3b:03ea::/48
# 是否通过 VPN 传输所有 DNS 查询。
#tunnel-all-dns = true
# 配置DNS服务器地址
# dns = fc00::4be0
dns = 223.5.5.5
dns = 180.76.76.76
# 在从池中租用任何IP之前先ping以验证其没有被占用
ping-leases = true
# 路由,列出地址的流量将走vpn通道,如果不设置或配置为default则全部流量均走vpn
route = 10.0.0.0/8
route = 172.16.0.0/12
route = 192.168.0.0/16
#route = fd00::/8
#route = default
# 使其支持旧版CISCO客户端和小于等于7.08版本的openconnect客户端,如开启则dtls-legacy = true
cisco-client-compat = true
# 此选项允许运维人员禁用旧版 DTLS 协商 ,默认为开启的
dtls-legacy = true
根据配置项:auth = “plain[passwd=/etc/ocserv/ocpasswd]”配置的密码文件路径,我们使用以下命令建立和调整账户”数据库”(如文件不存在会自动建立)
建立账户: sudo ocpasswd -c /etc/ocserv/ocpasswd [用户名] 添加用户到组: sudo ocpasswd -c /etc/ocserv/ocpasswd -g [组名] [用户名] 锁定用户: sudo ocpasswd -c /etc/ocserv/ocpasswd -l [用户名] 解锁用户: sudo ocpasswd -c /etc/ocserv/ocpasswd -u [用户名] 删除用户: sudo ocpasswd -c /etc/ocserv/ocpasswd -d [用户名]
启动服务
使用命令启动ocserv
sudo systemctl restart ocserv
此时假设已将外网端口映射至本机20000(因上文配置监听端口为20000),将系统防火墙(如有)的20000端口开放,
将PKCS#12格式的用户证书密钥导入至VPN客户的系统中后,客户即可通过Cisco AnyConnect客户端建立VPN连接
遇到的问题
连接VPN后无法访问内网应用
此时发现连接VPN后访问内网服务是不通的,无论是vpn服务器所在网段的服务还是夸网段的服务均无法访问

随即进行路由追踪,发现除了到vpn服务器的流量外,其他流量全部出不去,
遂想起出于安全考虑,Linux系统默认是禁止数据包转发,因为这通常是路由器所要实现的功能,当然您可以使用其他方式进行该功能的实现,但内核级别的转发是效率最高的一种。
要让Linux系统允许路由转发功能,需要配置一个Linux的内核参数net.ipv4.ip_forward,这个参数指定了Linux系统当前对路由转发功能的支持情况,其值为0时表示禁止进行转发,如果为1,则说明允许进行转发。
编辑内核配置文件/etc/sysctl.conf,增加(或由0调整为1)以下参数,
net.ipv4.ip_forward = 1
完成后执行以下命令以生效:
sysctl -p
同时使用iptables功能调整和配置内核netfilter转发和NAT功能
sudo iptables -t nat -A POSTROUTING -s [VPN网段地址,例如172.16.128.0]/[掩码,例如8、16、24] -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -s [VPN网段地址]/[掩码] -j ACCEPT
内网应用可以访问但公司网关看不到VPN子网,且VPN客户与公司内网服务、用户双向ping不通
遇到这个问题我一度以为是iptables的问题,后来发现ocserv运行有两种模式,
第一种是将ocserv作为网关(或者说路由器),建立虚拟网卡将VPN客户端分配在自己虚拟网卡的子网中,类似VMware虚拟网络中的NAT模式,此时vpn客户端可以访问内网应用,因为VPN服务端在公司局域网中,但是公司局域网中的用户不可以访问vpn客户端,因为vpn客户端在vpn服务端的子网下而不是公司网关的子网下,公司网关如不配置下一跳是无法实现访问的,就如同路由器下再接一个路由器,二级路由器可以访问一级路由器和其子网的用户、服务,而一级路由器无法访问到二级路由器中的用户和服务。
第二种是在公司局域网的网关中去申请子网,例如192.168.128.0/24、172.16.128.0/24等,此时客户端在公司局域网网关子网下,通过路由追踪命令可以查看到先请求到vpn网段的虚拟网关,然后直接跳到下一个地址,例如公司的总网关,然后进入目的地址的网关,再进入要访问的服务器,而不是先请求到vpn服务端。
很显然第二种方式是更合理的一种情况,第一种只能说是第二种的一种“降级”实现方式,但是为何ocserv采用了“降级”的方式运行呢?
通过翻阅ocserv官方文档Pseudo-Bridge setup with Proxy ARP发现了问题的原因
需要启用Linux内核的ARP代理功能
编辑内核配置文件/etc/sysctl.conf,增加(或由0调整为1)以下参数,
net.ipv4.conf.all.proxy_arp = 1
完成后执行以下命令以生效:
sysctl -p
此时的ping和路由信息正常,如下图:

发表回复