搭建OpenConnect+Radius+Mysql+WHMCS完整VPN解决方案
服务端测试环境:Centos7 | ocserv0.10.12 | FreeRADIUS3.0.4 | Mysql5.6 | WHMCS6.2.0 | Apache2 | PHP5.4
客户端测试环境:Android6.0.1 | Cisco AnyConnect Secure Mobility Client 4.0.05026
首先介绍一下openconnect,所谓openconnect即cisco anyconnect的兼容服务端。众所周知,cisco在网络设备领域处于霸主地位,其开发的anyconnect作为一种vpn协议,不是想封就能封的。封锁anyconnect将对大量跨国公司的子公司与母公司的通讯造成灾难性后果。因此,虽然anyconnect的握手特征很明显,但是依然可以正常使用。
而anyconnect作为cisco专有技术,其服务端只能运行在cisco专有设备上,即如果没有购买cisco相关设备,将无法使用anyconnect服务端。而openconnect的出现解决了这一个问题,openconnect是一个开源项目,其目标是在相对廉价的linux设备上运行与anyconnect协议兼容的服务端,以此来使用该协议而不需要购买cisco专有设备。openconnect目前已经足够完善,各种功能性支持也达到和anyconnect几乎无异。更多信息可以访问openconnect项目官网。
而radius则是一个登陆管理系统,通过非常简单的api来对接其他需要账号管理的程序。radius产品中最有名的是开源的freeradius,其超高负载量可以同时支持几W并发请求(数据库会不会跪还有待考证)。目前大多数ISP对用户拨号的验证处理使用的正是freeradius,因此其可靠性毋庸置疑。
Mysql作为全球使用量最大的关系性数据库,在这里将为radius服务。radius的账号记录有很多种方案,其中一种是mysql方式。这种情况下radius将访问mysql来获取账号信息。
WHMCS则是在国际上使用非常广泛的自动化账单系统,可以自动管理注册、登陆、支付、发货等。同时其高可扩展性使得其可以安装大量插件来管理更多内容或提供更多产品。WHMCS基于PHP,因此需要web服务端和php的支持,这里使用apache作为web服务端。
搭建openconnect:
首先我们来搭建openconnect服务端,openconnect服务端简称ocserv(open connect server)。其在ubuntu上的依赖问题比较麻烦,因此我们这里选择centos7(过低版本不行)。
ocserv已经在epel源提供,我们直接安装对应软件包。
yum install epel-release
yum install ocserv
安装完毕后使用命令
ocserv -v
检查安装。
anyconnect使用ssl证书来进行加密,ocserv也使用此方法。除了ca证书之外,还有服务端证书和客户端登陆用的证书,这比较麻烦,我们先使用密码验证来完成客户端登陆。而ca证书和服务端证书使用自签方式(由于ca证书自签,客户端登陆时将提示不受信任的服务器,待会我们会讲到。如果需要合法的ca证书,请自行寻找信誉良好的ca证书颁发机构进行签发)。首先我们来生成自签ca证书。
生成过程中产生的文件以后没有用,因此进入tmp目录完成操作。
cd /tmp
生成ca证书
certtool –generate-privkey –outfile ca-key.pem
cat >ca.tmpl <<EOF
cn = “VPN CA”
organization = “Big Corp”
serial = 1
expiration_days = 3650
ca
signing_key
cert_signing_key
crl_signing_key
EOF
certtool –generate-self-signed –load-privkey ca-key.pem \
–template ca.tmpl –outfile ca-cert.pem
接下来生成服务端证书
certtool –generate-privkey –outfile server-key.pem
cat >server.tmpl <<EOF
cn = “www.example.com”
organization = “MyCompany”
serial = 2
expiration_days = 3650
encryption_key
signing_key
tls_www_server
EOF
certtool –generate-certificate –load-privkey server-key.pem \
–load-ca-certificate ca-cert.pem –load-ca-privkey ca-key.pem \
–template server.tmpl –outfile server-cert.pem
完成以上步骤之后拷贝两份服务端证书文件到ocserv配置文件夹,ca证书反正是自签的,没有什么用,不需要一同拷贝(拷贝路径如下,待会会用到这个路径)。
cp server-cert.pem server-key.pem /etc/ocserv/
接下来我们配置ocserv,使用以下命令编辑其配置文件
vim /etc/ocserv/ocserv.conf
以下是需要修改的部分
#这里的auth即指使用何种方式进行用户登陆验证,其中plain为ocserv自带的密码验证,需要通过ocserv的相关命令来添加用户(其实是按一定格式保存到文件里,是一种纯文本的用户记录方式)。radius方式即我们实际需要使用的方式,但是我们先使用plain方式,通过分步测试来确保每一步都可以检查是否正确。
auth = “plain[passwd=/etc/ocserv/ocpasswd]”
#服务端证书路径,这两个路径推荐如下所示,这样便于集中管理ocserv有关文件。如果你之前生成证书后拷贝到了其他位置,请使用证书的实际位置作为设置。
server-cert = /etc/ocserv/server-cert.pem
server-key = /etc/ocserv/server-key.pem
#该选项指定本服务端最大可以同时接入的连接数量,设置为0表示无限制(依然会受到linux最大用户可打开文件数限制)。推荐值1024。
max-clients = 1024
#相同的账户可同时连接上限。这个选项可以限制同一个用户可同时使用的设备数量。设置为0表示无限制。
max-same-clients = 5
#以下是虚拟网卡设置。包括虚拟网卡名和ip池以及子网掩码,我们需要记住他们,接下来将会在其他设置中用到。
device = vpns
ipv4-network = 192.168.1.0
ipv4-netmask = 255.255.255.0
保存设置文件,接下来执行bash命令添加ocserv用户。
ocpasswd -c /etc/ocserv/ocpasswd czp
其中 -c 参数后面是密码文件的路径,是我们之前在ocserv.conf中设置的路径,需要保持一致。后面的czp是用户名。回车后输入两次密码以完成添加。添加完成后将在指定的目录出现ocpasswd文件,这就是保存密码用的文件。
关于涉及到nat的数据包转发,在linux上使用内核转发效率是最高的,因此ocserv也采用此方案。为此我们需要确认当前操作系统已开启内核转发功能。
vim /etc/sysctl.conf
打开该文件后检查是否存在行
net.ipv4.ip_forward = 1
若没有,添加到文件末尾。保存。然后重载配置文件。
sysctl -p
为确认ipv4内核转发已开启,我们可以查看以下虚文件
cat /proc/sys/net/ipv4/ip_forward
若值为1,表示内核转发已开启。接下来不使用service来执行ocserv,而直接运行ocserv以查看实时输出
screen
ocserv -f -d 1
其中 -f 参数表示前台运行,-d 参数表示debug等级。通过这种方式,如果我们的设置有错误,我们将立即看到错误输出,这样我们就可以知道需要修改哪些设置。之所以放到screen里,是因为接下来还需要做其他事情,而为了不中断ocserv,因此在screen中运行。
一般情况下我们的ocserv应该已经成功运行并有输出了,接下来我们来测试以下是否可以用anyconnect客户端进行连接(android上也有openconnect客户端,但是为了保证我们的服务端确实是和anyconnect兼容的,我们使用anyconnect客户端作为测试程序)。
当我们使用android版anyconnect客户端进行连接时,如果提示服务器不收信任,请至设置关闭“阻止不信任的服务器”选项。连接时如果再次提示此问题,请点选“connect anyway”。若滑动条变成ON状态并有“已连接”字样,表示客户端已成功通过验证并连接上服务端。此时anyconnect客户端将自动创建一个系统vpn连接,若系统询问相关权限许可,请点选允许。
此时我们将发现客户端设备此时无法访问互联网,不要惊讶,这是正常的。我们现在还没有做数据包转发,因此客户端发出的数据包无法进入互联网,而仅在nat内(对于子公司连接母公司内网来说这已经足够了)。下面我们将设置iptables来将数据包转发到互联网,从而实现客户端访问互联网,毕竟我国使用anyconnect的用户大多数是为了禾斗学上网。现在让我们放下手机重新回到服务器上进行相关转发设置。首先退出当前screen,
安装iptables
yum install iptables-services
开启iptables
service iptables start
设置iptables开机启动
chkconfig iptables on
以下设置将视当前iptables规则为空的情况下,若你的不是空的,使用以下命令清空两张表
iptables -F
iptables -F -t nat
创建以下iptables规则
iptables -A FORWARD -s 192.168.1.0/24 -j ACCEPT
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j MASQUERADE
iptables -A FORWARD -p tcp -m tcp –tcp-flags SYN,RST SYN -j TCPMSS –clamp-mss-to-pmtu
iptables -A FORWARD -o vpns0 -j ACCEPT
iptables -A FORWARD -i vpns0 -j ACCEPT
其中192.168.1.0/24是我们之前在配置文件中设置的ip地址池,eth1是你的服务器的外网网卡。vpns0是按照之前配置文件中设置的虚拟网卡设备的实际运作中的名称,如果你设置的是vpns,那么这个设备实际运作名称就是vpns0。如果你的iptables设置默认允许所有数据包通过,那么可以无视所有-A参数的命令,仅使用第二条命令就可以让设备正确访问互联网。
此时我们再次拿起手机,尝试访问google.com,很好,我们已经可以成功打开谷歌。(如果你的客户端能访问其他网站但无法访问谷歌,请尝试至ocserv配置文件修改DNS,设定为谷歌DNS以防DNS污染,大约在配置文件第403行)
确认ocserv工作无误后使用以下命令保存iptables规则。
service iptables save
我们回到之前运行ocserv的screen
screen -r
使用ctrl + c退出程序,此时我们已经关闭手动开启的ocserv。我们现在用service方式运行它,并设定开机启动
service ocserv start
chkconfig ocserv on
重新使用客户端连接服务器,此时ocserv已经部署完毕。更多详细设置请自行查看ocserv配置文件,每一条配置都有注释。
如果你的目的仅仅是搭建个人使用的openconnect服务器,那么到此已经达成目标。如果你还需要完善的多用户管理,那么请继续往下看。
搭建freeradius:
freeradius用来提供用户验证功能,我们现在来搭建freeradius然后调整ocserv设置以使其使用radius作为用户身份验证模块。freeradius分为两部分,客户端和服务端。验证请求由客户端发出,服务端收到后根据设置中的密码保存形式(文件、mysql等)完成验证,数据回传给客户端。freeradius并不是某一种附属程序或插件,而是单独运行的程序。ocserv通过调用freeradius客户端对连接者进行身份验证。因此我们分别需要安装并设置freeradius客户端和服务端。
freeradius服务端与ocserv不需要在同一台服务器上。freeradius客户端必须在ocserv服务器上(一个服务端可以对应多个客户端)。同样的,mysql数据库也不需要和freeradius在同一台服务器上。下面的示例中,采取的网络架构是三台服务器 ocserv服务器(安装有ocserv,freeradius client)、freeradius服务器(安装有freeradius server)、mysql服务器(安装有mysql)。
首先我们在freeradius服务器安装freeradius服务端
yum install freeradius
安装完毕后使用
radiusd -v
若看到版本号信息说明安装成功。
由于我们之后需要使用mysql来存储用户信息,我们还需要安装freeradius的mysql扩展
yum install freeradius-mysql
我们首先使用freeradius的默认设置,即文本形式记录用户的用户名和密码。测试freeradius的工作是否正常,再测试ocserv调用freeradius是否正常,最后再设置freeradius使用mysql来读取用户记录。
freeradius的保存用户的文本在 /etc/raddb/user 其他设置也在这个目录,我们等会会改动这个目录下的设置。user文件是一个link,我们直接编辑其实际路径(vim似乎不能编辑link)
vim /etc/raddb/mods-config/files/authorize
在第一行插入如下内容后保存
czp Cleartext-Password := “passwd_in_text”
这里,我们新建了一个用户,用户名为 czp ,密码为 passwd_in_text 。之后我们新建一个screen并以调试模式运行radiusd
screen
radiusd -X
好,现在我们可以看到我们的freeradius服务端已经处于运行状态了
我们退出screen,然后使用freeradius的调试工具进行测试,首先安装它
yum install freeradius-utils
执行以下命令
radtest czp passwd_in_text localhost 0 testing123
如果看到 Received Access-Accept 字样说明验证成功。如果看到 Received Access-Reject 说明验证失败,你需要检查使用的命令。当我们确认freeradius工作正常之后我们用freeradius客户端来访问freeradius服务端。不过在那之前我们需要先设置freeradius服务端设置。
首先打开设置文件(服务端上的客户端配置)
vim /etc/raddb/clients.conf
我们在这份文件可以对每个客户端进行设置(以ip区分),以及所有客户端的一些全局设置。内容很多,我们不一一详谈了。我们直接找到第30行的 client localhost,我们刚才测试工具之所以能正确访问freeradius服务端,是因为默认设置下,localhost已被加入这份设置,即localhost可以访问freeradius服务端。而我们之前在测试命令中使用的 testing123 在这份文件的第100行,这个设置用来指定客户端发送请求时带的密钥,若密钥错误将导致服务端返回验证失败的信息。这个设置很重要,我们这里不去修改他,但是真正的生产环境需要修改这个设置。
为了方便起见,我们不另外再开一个客户端设置了,我们直接修改现有的客户端设置即 client localhost 的设置中的客户端ip设置。
找到第42行,修改为
#ipaddr = 127.0.0.1
找到第46行,修改为
ipv4addr = * # any. 127.0.0.1 == localhost
保存这个文件。现在我们允许任何ipv4地址访问freeradius服务端(这在实际生产环境中是不安全的,生产环境中请仔细配置每一个客户端设置)。
我们现在在ocserv服务器上安装freeradius客户端
yum install freeradius-client
为了测试freeradius服务端在这台服务器上也可以访问,我们可以在这台服务器上也安装测试工具并进行测试(假设freeradius服务器地址为1.1.1.1)
yum install freeradius-utils
radtest czp passwd_in_text 1.1.1.1 0 testing123
确认无误后我们进行接下来的设置。首先我们打开freeradius客户端设置文件
vim /etc/radiusclient/radiusclient.conf
第37行改为
authserver 1.1.1.1
第42行改为
acctserver 1.1.1.1
保存这份文件
打开freeradius客户端的服务器设置
vim /etc/radiusclient/servers
添加以下内容至最后一行
1.1.1.1 testing123
保存这份文件。自此,我们已经设置好了freeradius客户端需要做的设置。我们接下来让ocserv使用freeradius作为验证模块
我们再次打开ocserv配置文件
vim /etc/ocserv/ocserv.conf
我们修改第39行的验证方式设置,改为
auth = “radius[config=/etc/radiusclient/radiusclient.conf,groupconfig=true]”
这样,我们就使得ocserv使用freeradius来进行用户验证。我们保存这个文件,重启ocserv
service ocserv restart
现在我们使用客户端连接ocserv,此时应该已经可以成功的用使用文本保存的radius用户账户进行登陆。
但是使用文本保存密码不是我们最终需要的,我们到此仅仅是确认了ocserv调用freeradius正常,接下去我们完成freeradius服务端的mysql设置。
我们首先需要启用freeradius的sql模块,方法很简单,做一个link即可
cd /etc/raddb/mods-enabled
ln -s /etc/raddb/mods-available/sql ./sql
这样,freeradius就可以加载sql模块了。
接下去我们设定sql模块设置,编辑以下文件
vim /etc/raddb/mods-available/sql
第31行
driver = “rlm_sql_mysql”
第73行
dialect = “mysql”
第77到80行(假设mysql服务器地址为2.2.2.2,用户名为radius,密码为123456789)
server = “2.2.2.2”
port = 3306
login = “radius”
password = “123456789”
第83行(假设库名为radius,其实这个一般不改)
radius_db = “radius”
第114行
read_groups = yes
第211行
read_clients = yes
保存这份文件,接下去我们修改freeradius服务端设置让它使用sql模块来进行用户验证。
vim /etc/raddb/sites-available/default
这份文件的主要结构是设置各种过程的操作步骤,例如验证,回话,验证失败等过程中的详细步骤。
第365行
# files
第372行
sql
第564行
# files
第602行
sql
第650行
sql
第676行
sql
第779行
sql
保存这份文件。接下去我们重启radiusd。我们进入之前开启的screen中,使用ctrl + c结束radiud的运行,然后再次运行它。
此时我们使用客户端进行测试,此时我们将在radiusd所在的screen看到错误信息,没错,这是因为我们的数据库不存在或者是空的(如果数据库服务器无法访问,将直接无法成功运行radiusd)。现在我们要创建这个数据库。数据库备份文件在freeradius服务器的 /etc/raddb/mods-config/sql/main/mysql/schema.sql 我们拷贝这个文件到数据库服务器的 /tmp/schema.sql
之后我们在mysql服务器上运行以下命令创建数据库并添加一条记录(用户名 czp 密码 passwd_in_mysql )
mysql -u root -p
输入密码
create database radius;
use radius;
source /tmp/schema.sql;
insert into `radcheck` (`id`,`username`,`attribute`,`op`,`value`) values(1,’czp’,’Cleartext-Password’,’:=’,’passwd_in_mysql’);
quit;
好,我们再此使用客户端进行尝试。如果你全部按照以上步骤进行的话,此时应该可以验证成功了。
我们进入freeradius服务器上的之前的screen关闭radiusd,让他作为服务运行并设定开机启动
service radiusd start
chkconfig radiusd on
如果你的目的仅仅是使用mysql来管理ocserv账户的话,到此就可以结束了。如果你还需要使用whmcs来进行用户管理、账单管理、自动发货的话,那么请继续往下看。
配置whmcs自动发货:
关于怎么搭建和设置whmcs不在本教程讨论范围,请至whmcs官网购买whmcs并根据其文档进行部署。
有whmcs之后我们需要一个freeradius插件,其购买地址如下 https://www.whmcs.com/members/cart.php?a=view
不清楚怎么部署的话可以查看这里 http://docs.whmcs.com/FreeRadius_Addon_Module
现在我们已经安装完毕了freeradius插件,接下来进行自动发货测试。很好,我们的产品显示已经自动发货完毕。但是当我们使用客户端连接时,却发现无法验证通过。如果此时我们查看freeradius服务端的日志,将看到错误信息。之所以会这样是因为这个插件插入数据库的数据freeradius不识别,我不知道为什么会产生这种情况,但事实是它发生了。
为此我们需要手动修改freeradius插件的代码,我们打开这份文件
直接搜索如下内容 User-Password
我们将看到两个搜索结果,一个在CreateAccount函数中,一个在ChangePassword函数中。这两个函数分别被whmcs在创建产品和修改产品密码时调用。我们查看以下数据库内容,会发现在radcheck表中被新插入的数据的attribute字段的值是User-Password,而freeradius实际上不识别这个内容。因此我们替换这两处搜索结果为 Cleartext-Password 。保存php文件。
删除之前创建的whmcs产品,重新进行一次购买测试。此时数据库中的新插入的数据的radcheck字段将变成我们之前修改的Cleartext-Password。我们再次使用客户端进行连接测试,此时freeradius服务端不再报错,客户端验证成功。
关于ocserv使用这个插件的缺点,其实很大。首先ocserv在调用freeradius进行验证时,仅调用一下,通过验证之后不再使用freeradius,因此radius表中关于在线时长,流量等记录完全没有用处。所以插件中的clientarea函数需要自己修改掉,改成例如显示验证次数,最后一次登陆之类的东西,否则使用者会感到很奇怪。其次这个插件非常奇葩,他直接使用用户的whmcs用户名作为radius用户名(邮箱),当一个客户有两个这个产品时,会变成邮箱后面再加数字(1、2、3、4这样排下去),非常操蛋。
可以用这个freeradius插件,免费的:
https://github.com/secondimpression/whmcs-freeradius
安装说明:
http://www.tophedu.com/2016/12/16/freeradius-plugin-for-whmcs/
至此,OpenConnect+Radius+Mysql+WHMCS完整VPN解决方案已部署完毕。整套架构将可以自动完成用户订购、付款、续费、产品开通、暂停、终止、通知邮件发送、在线自助服务等繁多功能。躺在床上月入百万,小伙伴们欢呼雀跃!