问题来源

最近公司架构调整,将原来在阿里云的开发环境和测试环境迁移到了,公司内网环境,但是,最近在周末的时候,老是有对应的临时需求在周末发布,结果自己在家,没办法访问公司的内网测试环境,很是恼火,根本没办法测试,但无法在在家中解决,自己也不想去公司,需要在内网测试并且上进行分析和定位对应的 bug。第一个想到的就是远程登陆,但公司没有公网 IP,怎么办?

明确需求

经过一波调查明确如解决下需求:
1.一个是可以无视外网 ip 的变化 然后进行网络通信,使自己可以在家访问公司测试环境

经过最终在的调研,最终采用 open*** 实现使用外网访问公司的内网测试环境

知其然知其所以然

open*** 原理

open*是一个基于 OpenSSL 库的应用层实现。其主要技术核心是虚拟网卡和 SSL 协议实现。可以实现多种复杂的业务场景需求,包括单个客户端和服务器,多客户端和服务器 (客户端不通讯),多客户端和服务器 (客户端互通),这里的客户端和服务端都可以扩展到局域网的层面上,其使用工业标准的 SSL/TLS 协议实现第 2 层和第 3 层的安全数据链路*。其优点如下:
1、基于 SSL 协议,安全,并使用单一 TCP 或 UDP 端口即可实现;
2、使用双向验证,服务器只需保存自己的证书和密钥;
3、服务器只接受那些由主 CA 证书签名的客户端,并有撤回机制,而不需要重建整个 PKI;
4、可以实现基于 Common Name 的权限控制。

open*** 网络架构

,英文全称是 Virtual Private Network,字面解释可理解为:虚拟私有网络;可以运行为 Point-to-Point 模式,也可以运行为 Server 模式,在 Server 模式下,服务器可作为网关设备使用,用以给处于外部的不安全网络环境中的 Client 提供安全访问内网服务的通道

以 Server 模式运行的
其走包流程如下图所示:

Open*** 服务搭建与管理

环境准备

硬件
CentOS Linux release 7.7.1908 (Core) 服务器一台
客户端笔记本一台
工具包

https://github.com/Open***/easy-rsa.git

Open*** 搭建

安装依赖包

yum install lz4-devel lzo-devel pam-devel openssl-devel systemd-devel sqlite-devel

从 github 上下载 ope源代码包并解压*

wget https://github.com/Open***/open***/archive/v2.4.7.tar.gz
tar zvxf v2.4.7.tar.gz

编译 open并安装*

cd open***-2.4.7
autoreconf -i -v -f
./configure --prefix=/usr/local/open*** --enable-lzo --enable-lz4 --enable-crypto --enable-server --enable-plugins --enable-port-share --enable-iproute2 --enable-pf --enable-plugin-auth-pam --enable-pam-dlopen --enable-systemd
make && make install

配置系统服务
修改/usr/local/open*/lib/systemd/system/open*-server@.service

 [Service]
...
ExecStart=/usr/local/open***/sbin/open*** --config server.conf

将 open-server@.service 设置成系统服务*

cp /usr/local/open***/lib/systemd/system/open***-server@.service /usr/lib/systemd/system/open***.service 
systemctl enable open***

生成证书

下载 easy-rsa3 并解压

wget https://github.com/Open***/easy-rsa/archive/v3.0.7.tar.gz
tar -xvf v3.0.7.tar.gz

根据 easy-rsa-3.0.7/vars.example 文件生成全局配置文件 vars

set_var EASYRSA_REQ_COUNTRY     "CN" 
set_var EASYRSA_REQ_PROVINCE    "HUBEI"\
set_var EASYRSA_REQ_CITY        "WUHAN"\
set_var EASYRSA_REQ_ORG "ZJ"\
set_var EASYRSA_REQ_EMAIL       "zj@test.com"\
set_var EASYRSA_REQ_OU          "ZJ"\
set_var EASYRSA_KEY_SIZE        2048\
set_var EASYRSA_ALGO            rsa\

生成服务端证书
初始化,生成一系列文件与目录

./easyrsa init-pki   

生成根证书,记住 ca 密码

./easyrsa build-ca    

生成服务端证书

./easyrsa build-server-full server nopass ,nopass参数生成一个无密码的证书

生成 Diffie-Hellman

./easyrsa gen-dh     

生成客户端证书

./easyrsa build-client-full client1 nopass

备注:注:可生成 client1, client2, client3 或对应姓名的客户端证书
为了提高安全性,生成 ta.key

open*** --genkey --secret ta.key

整理证书

cp pki/ca.crt /etc/open***/server/
cp pki/private/server.key /etc/open***/server/
cp pki/issued/server.crt /etc/open***/server/
cp pki/dh.pem /etc/open***/server/
cp ta.key /etc/open***/server/

添加 SQLite 认证

下载 pam_sqlite3 并安装

git clone https://gitee.com/lang13002/pam_sqlite3.git
cd pam_sqlite3
make && make install

添加 pam 认证文件

# vim /etc/pam.d/open***
auth        required    pam_sqlite3.so db=/etc/open***/open***.db table=t_user user=username passwd=password expire=expire crypt=1
account     required    pam_sqlite3.so db=/etc/open***/open***.db table=t_user user=username passwd=password expire=expire crypt=1

创建 sqlite3 数据库文件

sqlite3 /etc/open***/open***.db

sqlite> create table t_user (
     username text not null, 
     password text not null, 
     active int, 
     expire text
);
sqlite> .quit

创建服务端配置文件 (参照 sample/sample-config-files/server.conf 文件)

vim /etc/open***/server/server.conf
port 1194
proto tcp-server
;proto udp
dev tun
topology subnet

ca /etc/open***/server/ca.crt
cert /etc/open***/server/server.crt
key /etc/open***/server/server.key
dh /etc/open***/server/dh.pem

cipher AES-256-CBC
auth SHA512
tls-version-min 1.2
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA

tls-auth /etc/open***/server/ta.key 0
#tls-crypt /etc/open***/server/ta.key

user nobody
group nobody

server 10.8.0.0 255.255.255.0
;ifconfig-pool-persist ipp.txt
;push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 114.114.114.114"
push "route 192.168.133.0 255.255.255.0"
push "route-gateway 10.200.227.114"

;client-to-client

verify-client-cert none
username-as-common-name
plugin /usr/local/open***/lib/open***/plugins/open***-plugin-auth-pam.so open***

keepalive 10 120
comp-lzo
compress "lz4"
persist-key
persist-tun
status /var/log/open***-status.log
log    /var/log/open***.log
verb 3 

开启路由转发功能与防火墙

# 路由转发
# vim /etc/sysctl.conf
net.ipv4.ip_forward = 1

# 临时启用
# echo 1 > /proc/sys/net/ipv4/ip_forward

# 防火墙
# firewall-cmd --zone=public --add-service=open***

启动 open服务*

systemctl start open***

配置客户端

下载客户端程序

https://gitee.com/lang13002/open***-portable/repository/archive/v1.0?ref=v1.0&sha=1a7401cb8767a8f15726f04613d84458abccd272&format=zip&captcha_type=yunpian&token=335b826daacc4b17a81d2ce8e8f36c8a&authenticate=ced5c39711c343f0bfdb41531204a3ea

安装驱动
运行 open*-portable/tap-windows.exe
设置客户端证书
将上面生成的 ca.crt, client1.crt, client1.key 放到 open
*-portable 的 data/config 下,并修改客户端配置

client
dev tun
proto tcp-client
remote ***server.com 1194

allow-recursive-routing

resolv-retry infinite
nobind
persist-key
persist-tun

remote-cert-tls server
auth-user-pass
auth-nocache
ca ca.crt
cert client1.crt
key client1.key

remote-cert-tls server
auth-user-pass
auth-nocache

cipher AES-256-CBC
auth SHA512
tls-version-min 1.2
tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA

tls-auth ta.key 1

comp-lzo
compress lz4
verb 3
mute 20

备注

注:当有多个客户端时,有多个文件(ca.crt, client1.crt, client1.key, client.o***)需要分发给客户,势必会很麻烦;可以将证书嵌入到客户端配置文件中; 
;ca ca.crt         // 将这行注释掉
;cert client.crt   // 将这行注释掉
;key client.key    // 将这行注释掉
;tls-auth ta.key 1 // 将这行注释掉
<ca>
-----BEGIN CERTIFICATE-----
MIIDGDCCAgCgAwIBAgIJAI9Ld4PlKEiOMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNV
....
OCeTQvQ4WhyIvVgURV3ITcAKYFKUQ1sPbpjuZg==
-----END CERTIFICATE---
</ca>
<cert>
-----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIRAIZoEQ5PvHDs9xpTLMP3RqMwDQYJKoZIhvcNAQELBQAw
......
nCpzC3l8sVezxk2r
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDw1iq3HBe1otCU
......
ullaNc6mu3N/wTPZoQhDOKAO
-----END PRIVATE KEY-----
</key>
<tls-crypt>
#
# 2048 bit Open*** static key
#
-----BEGIN Open*** Static key V1-----
376ff00121bc6cd39fe1382c44be1433
......
-----END Open*** Static key V1-----
</tls-crypt>

连接代理

Open*** 用户管理与日志

安装依赖

pip install peewee tornado

下载 ope-web*

git clone https://gitee.com/lang13002/open***_web.git

创建相应的数据库表

# sqlite3 /etc/open***/ope**n.db
sqlite> .read open***_web/model/open***.sql

添加日志脚本
服务端配置添加运行脚本

script-security 2
client-connect /etc/open***/server/connect.py
client-disconnect /etc/open***/server/disconnect.py

connect.py

!/usr/bin/python

import os
import time
import sqlite3

username = os.environ['common_name']
trusted_ip = os.environ['trusted_ip']
trusted_port = os.environ['trusted_port']
local = os.environ['ifconfig_local']
remote = os.environ['ifconfig_pool_remote_ip']
timeunix= os.environ['time_unix']

logintime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))

conn = sqlite3.connect("/etc/open***/open***.db")
cursor = conn.cursor()
query = "insert into t_logs(username, timeunix, trusted_ip, trusted_port, local, remote, logintime) values('%s','%s', '%s', '%s', '%s', '%s', '%s')" %  (username, timeunix, trusted_ip, trusted_port, local, remote, logintime)
cursor.execute(query)
conn.commit()
conn.close()

disconnect.py

/usr/bin/python

import os
import time
import sqlite3

username = os.environ['common_name']
trusted_ip = os.environ['trusted_ip']
received = os.environ['bytes_received']
sent = os.environ['bytes_sent']

logouttime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))

conn = sqlite3.connect("/etc/open***/open***.db")
cursor = conn.cursor()
query = "update t_logs set logouttime='%s', received='%s', sent= '%s' where username = '%s' and trusted_ip = '%s'" %  (logouttime, received, sent, username, trusted_ip)
cursor.execute(query)
conn.commit()
conn.close()

启动服务

python myapp.py


↙↙↙阅读原文可查看相关链接,并与作者交流