一、概述

本节内容开始之前,已经部署好了openVPN以及LDAP认证。

企业环境中,LDAP用户名密码可以说是一号走天下,一旦出现用户名密码泄露(粗心程序员传到github),那损失是巨大的,因此加上双因子认证,也是加上了一层保险。这里我们的双因子认证是通过GoogleAuthenticator实现的,因为他的原理比较简单,在业内也很通用。

二、安装

要实现LDAP+GoogleAuthenticator认证,openvpn需要安装两个插件

https://github.com/threerings/openvpn-auth-ldap

https://github.com/evgeny-gridasov/openvpn-otp

第一个实现LDAP登陆,第二个实现OTP。由于两个插件先后顺序需要兼容,必须使用最新版本。

#@安装LDAP插件
git clone https://github.com/threerings/openvpn-auth-ldap
cd openvpn-auth-ldap
#@安装依赖
yum install re2c libtool openldap openldap-devel openvpn openvpn-devel gcc-objc openssl openssl-devel -y
#@下载安装包解压,开始编译
./regen.sh 
./configure --with-openldap=/usr/ --with-openvpn=/usr/ CFLAGS="-fPIC" OBJCFLAGS="-std=gnu11"
make 
make install
#@安装OTP插件
git clone https://github.com/evgeny-gridasov/openvpn-otp
cd openvpn-otp
yum install autoconf automake libtool* -y
./autogen.sh
./configure 
make
make install

安装完成后可以找到这两个插件。(.so模块可以任意拷贝到其他目录使用的)

/usr/local/lib/openvpn-auth-ldap.so
/usr/local/lib/openvpn/openvpn-otp.so

三、服务器端配置

有了插件以后我们就可以修改服务器端配置了,主要用到了三个配置文件:

  1. openVPN 的主配置文件 server.conf
  2. openvpn-auth-ldap 插件的配置文件 ldap.conf
  3. openvpn-otp 插件的配置文件 otp-secrets

3.1 openVPN 接入插件模块

先看下openvpn的主配置文件 server.conf,其指定了两个插件模块以及配置文件的路径

注意:使用以下配置时,需删除注释内容。

port 13588 
proto tcp
dev tun

ca /etc/openvpn/certs/ca.crt
cert /etc/openvpn/certs/server.crt
key /etc/openvpn/certs/server.key  # This file should be kept secret
dh /etc/openvpn/certs/dh.pem

server 10.8.0.0 255.255.255.0
push "route 10.0.16.0 255.255.255.0"
keepalive 10 120
cipher AES-256-CBC
compress lz4-v2
push "compress lz4-v2"
max-clients 2048
user openvpn
group openvpn
status /var/log/openvpn/openvpn-status.log
log-append /var/log/openvpn/openvpn.log
verb 3
mute 20
client-cert-not-required

#@启用ldap模块
plugin "/usr/local/lib/openvpn-auth-ldap.so" "/etc/openvpn/auth/ldap.conf"

#@启用otp模块
plugin /usr/local/lib/openvpn/openvpn-otp.so "password_is_cr=1 otp_secrets=/etc/openvpn/auth/otp-secrets"

#@reneg-sec服务器端会定期检查认证情况,默认3600秒一小时,使用OTP的话尽量时间长一些,否则客户端需要重新输入用户名密码和OTP一次性密码。0表示不重新认证(服务端和客户端同时配置才可生效,若两端不一致,则以时长短的一个为准)
reneg-sec 0 

3.2 接入 openLDAP 服务

注意:这里需要使用 groupOfUniqueNames 类型的组才支持组过滤

配置 /etc/openvpn/auth/ldap.conf,接入ldap服务(已提前安装LDAP服务)

<LDAP>
        URL             ldap://127.0.0.1:389
        BindDN 	        cn=admin,dc=ldap,dc=com
		Password		admin
        Timeout         15
        TLSEnable       no
        FollowReferrals yes 
</LDAP>

<Authorization>
        BaseDN         "ou=users,dc=ldap,dc=com"
        SearchFilter    "uid=%u"
        
        #启用组过滤功能,限制VPN组中的账号才能登录
        RequireGroup    true 
        <Group>
                BaseDN          "ou=groups,dc=ldap,dc=com"
                SearchFilter    "cn=vpn"
                MemberAttribute uniqueMember 
        </Group>
	    PasswordIsCR  true #@此选项必须加上以支持otp验证码功能
</Authorization>

3.2 接入 OTP 服务

配置 /etc/openvpn/auth/otp-secrets,接入otp服务。

此处实现了两个脚本,可以根据 openLDAP 服务中的用户生成对应的 secrets配置:

确保已提前安装 google-authenticator

yum install -y google-authenticator

3.2.1 添加用户

用法:bash create_google_auth.sh <用户名>

# cat create_google_auth.sh 
#!/bin/bash
USER=$1
GOOGLE_FILE_PATH="/opt/google_auth/" #密钥存放路径
OTP_SECRETS="/etc/openvpn/auth/otp-secrets"

[ -f $GOOGLE_FILE_PATH ] && mkdir -p $GOOGLE_FIEL_PATH  
 
while [ -z $USER ] ; do
  read -p  "未提供用户名,请重新输入。按[q]退出: " USER
done
[ $USER == 'q' ] && exit 1
grep $USER $OTP_SECRETS &> /dev/null
if [ $? -eq 0 ]; then 
  #while  [ -z $CON ] ; do
    read -p "用户已存在,是否重新生成? [y]继续 [任意键]退出 : " CON
  #done
  [ ${CON}end == 'y'end ] && sed -ri "/$USER/d" ${OTP_SECRETS} || exit 1
fi 

google-authenticator  --time-based --force --disallow-reuse --rate-limit=3 --rate-time=30 --window-size=3 --issuer=kuaidi100 --label=${USER} --secret=${GOOGLE_FILE_PATH}${USER}
[ $? -eq 0 ] && echo "用户认证文件已保存至: ${GOOGLE_FILE_PATH}${USER}" # && echo "二维码见此URL: $(grep http ${GOOGLE_FILE_PATH}${USER})"

#SECRET_KEY=$(cat ${GOOGLE_FILE_PATH}${USER} | grep "secret key" | awk '{print $NF}')
SECRET_KEY=$(sed -n '1p' ${GOOGLE_FILE_PATH}${USER})
echo "$USER otp totp:sha1:base32:${SECRET_KEY}::xxx *" >> ${OTP_SECRETS}

3.2.3 删除用户

用法:bash delete_google_auth.sh <用户名>

# cat delete_google_auth.sh 
#!/bin/bash
USER=$1
GOOGLE_FILE_PATH="/opt/google_auth/" #密钥存放路径
OTP_SECRETS="/etc/openvpn/auth/otp-secrets"

while [ -z $USER ] ; do
  read -p  "未提供用户名,请重新输入。按[q]退出: " USER
done
[ $USER == 'q' ] && exit 1
[ -f ${GOOGLE_FILE_PATH}${USER} ] && rm -f ${GOOGLE_FILE_PATH}${USER} 
sed -ri "/$USER/d" ${OTP_SECRETS}
echo "$USER 用户已删除!"

[root@VM-16-2-centos openvpn]# less /root/create_google_auth.sh 
[root@VM-16-2-centos openvpn]# cat /root/delete_google_auth.sh 
#!/bin/bash
USER=$1
GOOGLE_FILE_PATH="/opt/google_auth/"
OTP_SECRETS="/etc/openvpn/auth/otp-secrets"

while [ -z $USER ] ; do
  read -p  "未提供用户名,请重新输入。按[q]退出: " USER
done
[ $USER == 'q' ] && exit 1
[ -f ${GOOGLE_FILE_PATH}${USER} ] && rm -f ${GOOGLE_FILE_PATH}${USER} 
sed -ri "/$USER/d" ${OTP_SECRETS}
echo "$USER 用户已删除!"

生成的otp-secrets文件内容(参考):

test otp totp:sha1:base32:X5IZ6G3YB6ETX6CDOZHAM66AYU::xxx *

四、OpenVPN客户端配置

client
remote 101.43.252.204 13588
proto tcp
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
ns-cert-type server
ca ca.crt
#cert test.crt
#key test.key
remote-cert-tls server
auth-user-pass	#启用账号认证功能
auth-nocache
static-challenge "Enter Google Authenticator Token" 1 #弹出验证框,1表示明文显示,0表示密文显示
redirect-gateway def1
comp-lzo
verb 3
reneg-sec 0 #禁用定时认证

五、参考链接:

https://github.com/threerings/openvpn-auth-ldap https://github.com/evgeny-gridasov/openvpn-otp

https://www.ipcpu.com/2019/04/openvpn-googleauthenticator/