项目背景
TM3.0项目,VPN方案采用刚开源的openconnect。在客户端认证方式测试过pam、certificate以及2FA(password file 结合OTP)。不过都不满足TM3.0的认证需求。最终采用Radius的方式进行客户端认证。
ocserv搭建
ocserv安装
首先我们来搭建openconnect服务端,openconnect服务端简称ocserv(open connect server)。其在ubuntu上的依赖问题比较麻烦,因此我们这里选择centos7.2。
ocserv已经在epel源提供,我们直接安装对应软件包。
yum install -y epel-release
yum install - y ocserv
安装完毕后使用命令
ocserv -v
检查安装。
设置开机自动启动
chkconfig ocserv on
生成证书:
anyconnect使用ssl证书来进行加密,ocserv也使用此方法。除了ca证书之外,还有服务端证书和客户端登陆用的证书,这比较麻烦,我们先使用密码验证来完成客户端登陆。而ca证书和服务端证书使用自签方式(由于ca证书自签,客户端登陆时将提示不受信任的服务器,待会我们会讲到。如果需要合法的ca证书,请自行寻找信誉良好的ca证书颁发机构进行签发)。
这里你需要先仔细阅读官方文档,简单的来说,如下几步
1. 创建文件夹
[root@ocserv ~]# mkdir anyconnect
[root@ocserv ~]# cd anyconnect
- 生成 CA 证书
[root@ocserv anyconnect]# certtool --generate-privkey --outfile ca-key.pem
Generating a 2048 bit RSA private key...
[root@ocserv anyconnect]# cat >ca.tmpl cn ="VPN CA"
> organization = "opscaff"
> serial = 1
> expiration_days = 3650
> ca
> signing_key
> cert_signing_key
> crl_signing_key
> EOF
[root@ocserv anyconnect]# certtool --generate-self-signed --load-privkey ca-key.pem \
> --template ca.tmpl --outfile ca-cert.pem
把生成的 ca-cert.pem 放到 /etc/ocserv/ 中
- 生成本地服务器证书
[root@ocserv anyconnect]# certtool --generate-privkey --outfile server-key.pem
Generating a 2048 bit RSA private key...
[root@ocserv anyconnect]# cat >server.tmpl cn = "xxx.xxx.xxx.xxx" //cn为ocserv服务器
> organization = "opscaff"
> serial = 2
> expiration_days = 3650
> encryption_key
> signing_key
> tls_www_server
> EOF
[root@ocserv anyconnect]# 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
把生成的 server-cert.pem 和 server-key.pem 放到 /etc/ocserv/ 中
- 生成客户端证书
创建 gen-client-cert.sh
[root@ocserv anyconnect]# vi gen-client-cert.sh
#!/bin/bash
USER=$1
CA_DIR=$2
SERIAL=`date +%s`
certtool --generate-privkey --outfile $USER-key.pem
cat user.tmpl
cn = "$USER"
unit = "admins"
serial = "$SERIAL"
expiration_days = 365
signing_key
tls_www_client
_EOF_
certtool --generate-certificate --load-privkey $USER-key.pem --load-ca-certificate $CA_DIR/ca-cert.pem --load-ca-privkey $CA_DIR/ca-k
ey.pem --template user.tmpl --outfile $USER-cert.pemopenssl pkcs12 -export -inkey $USER-key.pem -in $USER-cert.pem -name "$USER VPN Client Cert" -certfile $CA_DIR/ca-cert.pem -out $USER
.p12
[root@ocserv anyconnect]# chmod +x gen-client-cert.sh
- 创建用户文件夹并调用 gen-client-cert.sh 生成证书
[root@ocserv anyconnect]# mkdir user
[root@ocserv anyconnect]# cd user
[root@ocserv user]# ../gen-client-cert.sh opscaff001 ..
按提示设置证书使用密码,或直接回车不设密码
配置ocserv
vim /etc/ocserv/ocserv.conf
以下是需要修改的部分
这里的auth即指使用何种方式进行用户登陆验证,其中plain为ocserv自带的密码验证,需要通过ocserv的相关命令来添加用户(其实是按一定格式保存到文件里,是一种纯文本的用户记录方式)。radius方式即我们实际需要使用的方式,但是我们先使用plain方式,通过分步测试来确保每一步都可以检查是否正确。
auth = "plain[passwd=/etc/ocserv/ocpasswd]"
证书颁发机构,将被用来验证客户端证书(公钥)如果证书身份验证设置
ca-cert = /etc/ocserv/ca-cert.pem
服务端证书路径,这两个路径推荐如下所示,这样便于集中管理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.100.0
ipv4-netmask = 255.255.255.0
打开 PMTUD(自动优化VPN的网络性能)
try-mtu-discovery = true
注释掉所有的 route,让服务器成为 gateway
#route = 192.168.1.0/255.255.255.0
保存设置文件,接下来执行bash命令添加ocserv用户。
ocpasswd -c /etc/ocserv/ocpasswd opscaff001
系统配置
内核配置
其中 -c 参数后面是密码文件的路径,是我们之前在ocserv.conf中设置的路径,需要保持一致。后面的opscaff001是用户名。回车后输入两次密码以完成添加。添加完成后将在指定的目录出现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以查看实时输出
firewalld安装及配置
安装firewalld
yum install -y firewalld
配置firewalld
创建一个ocserv服务
vim /etc/firewalld/services/ocserv.xml
内容如下:
ocservCisco AnyConnect
启动firewalld
systemctl start firewalld
firewall-cmd --permanent --add-service=ocserv
firewall-cmd --permanent --add-masquerade
firewall-cmd --reload
配置内网转发
firewall-cmd --permanent --zone=public --add-rich-rule='rule family=ipv4 source address=192.168.100.0/24 masquerade'
firewall-cmd --reload
查看所有打开端口
# firewall-cmd --list-port
# firewall-cmd --zone=public --list-ports
# firewall-cmd --list-all
调试ocserv
ocserv -f -d 1
其中 -f 参数表示前台运行,-d 参数表示debug等级。通过这种方式,如果我们的设置有错误,我们将立即看到错误输出,这样我们就可以知道需要修改哪些设置。之所以放到screen里,是因为接下来还需要做其他事情,而为了不中断ocserv,因此在screen中运行。
一般情况下我们的ocserv应该已经成功运行并有输出了,接下来我们来测试以下是否可以用。在windows上使用编译好的客户端进行测试。
因为在内网测试,连接时间不到2s。
windows客户端配置证书(先欠着)
freeradius搭建
安装freeradius服务端
在另外一台centos服务器上搭建radius服务,这里采用开源的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的默认设置,即文本形式记录用户的用户名和密码。测试freeradius的工作是否正常,再测试ocserv调用freeradius是否正常,最后再设置freeradius使用mysql来读取用户记录。
freeradius的保存用户的文本在 /etc/raddb/user 其他设置也在这个目录,我们等会会改动这个目录下的设置。user文件是一个link,我们直接编辑其实际路径(vim似乎不能编辑link)
vim /etc/raddb/mods-config/files/authorize
在第一行插入如下内容后保存
opscaff001 Cleartext-Password := "123456"
这里,我们新建了一个用户,用户名为 opscaff001 ,密码为 123456 。之后我们新建一个screen并以调试模式运行radiusd
radiusd -X
好,现在我们可以看到我们的freeradius服务端已经处于运行状态了
我们退出screen,然后使用freeradius的调试工具进行测试,首先安装它
yum install freeradius-utils
执行以下命令
radtest opscaff001 123456 localhost 0 testing123
ps:如果修改了主机名,一定要当心出错。
如果看到 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服务端(这在实际生产环境中是不安全的,生产环境中请仔细配置每一个客户端设置)。
安装freeradius客户端
我们现在在ocserv服务器上安装freeradius客户端
下载freeradius-client源码包
这里我们使用的包为:freeradius-client-1.1.7.tar.gz
tar -zxvf freeradius-client-1.1.7.tar.gz
cd freeradius-client-1.1.7
安装依赖包:
yum install –y gcc gcc-c++
./configure
make -j2 && make install
我们现在在ocserv服务器上安装freeradius客户端
yum install freeradius-client
为了测试freeradius服务端在这台服务器上也可以访问,我们可以在这台服务器上也安装测试工具并进行测试(假设freeradius服务器地址为1.1.1.1)
yum install freeradius-utils
radtest opscaff001 123456 1.1.1.1 0 testing123
确认无误后我们进行接下来的设置。首先我们打开freeradius客户端设置文件
vim /usr/local/etc/radiusclient/radiusclient.conf
第37行改为
authserver 1.1.1.1
第42行改为
acctserver 1.1.1.1
保存这份文件
打开freeradius客户端的服务器设置
vim /usr/local/etc/radiusclient/servers
添加以下内容至最后一行
1.1.1.1 testing123
保存这份文件。自此,我们已经设置好了freeradius客户端需要做的设置。我们接下来让ocserv使用freeradius作为验证模块
我们再次打开ocserv配置文件
vim /etc/ocserv/ocserv.conf
我们修改第39行的验证方式设置,改为
auth = "radius[config=/usr/local/etc/radiusclient/radiusclient.conf,groupconfig=true]"
这样,我们就使得ocserv使用freeradius来进行用户验证。
但是使用文本保存密码不是我们最终需要的,我们到此仅仅是确认了ocserv调用freeradius正常,接下去我们完成freeradius服务端的mysql设置。
配置mysql
由于我们需要使用mysql来存储用户信息,我们还需要安装freeradius的mysql扩展
yum install 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。
数据库备份文件在freeradius服务器的 /etc/raddb/mods-config/sql/main/mysql/schema.sql 我们拷贝这个文件到数据库服务器的 /tmp/schema.sql
之后我们在mysql服务器上运行以下命令创建数据库并添加一条记录(用户名 opscaff001 密码 123456 )
mysql -u root -p
输入密码
create database radius;
use radius;
source /tmp/schema.sql;
insert into `radcheck` (`id`,`username`,`attribute`,`op`,`value`) values(1,'opscaff001','Cleartext-Password',':=','123456');
quit;
好,我们再此使用客户端进行尝试。如果你全部按照以上步骤进行的话,此时应该可以验证成功了。