五分钟搭建你的专属ocserv服务
背景
在天朝,由于互联网审查制度(通常被称为“防火长城”或“GFW”)的存在,一些国际网站和服务(如Google、Facebook、Twitter等)被屏蔽,因此,很多人需要使用VPN(虚拟专用网络)来访问这些被屏蔽的资源。VPN通过创建一个加密的连接,可以使用户的网络流量从审查制度中绕过,从而访问这些资源。作为一名程序员,经常需要翻越全世界的资料,所以,突破GFW是必备技能,但是一定要合法合规使用。
选择 ocserv 原因
以下是市面上常见的几种VPN协议的简单对比:
- PPTP(点对点隧道协议):这是最早的VPN协议之一,大部分操作系统都内置了对它的支持。但它的安全性较差,很容易被封锁,因此现在已经不推荐使用。
- L2TP/IPSec(第二层隧道协议/安全IP):L2TP/IPSec相较于PPTP来说更为安全,但其流量特征明显,容易被防火长城检测并封锁。
- OpenVPN:OpenVPN是目前最常用、最安全的VPN协议之一。它使用SSL/TLS进行密钥交换,可以提供非常高的安全性。但是,OpenVPN的流量特征也比较明显,因此有可能被防火长城封锁。
- SSTP(安全套接字隧道协议):SSTP也是一个基于SSL的协议,其安全性较高。然而,SSTP主要被Windows系统支持,对于其他系统的支持性较差。
- IKEv2(Internet Key Exchange version 2):IKEv2是一个非常安全且稳定的VPN协议,尤其适合移动设备。但是,像L2TP/IPSec一样,它的流量特征也较为明显,可能会被防火长城封锁。
- ocserv(OpenConnect SSL VPN):ocserv使用的是Cisco的AnyConnect SSL VPN协议,这是一种在全球范围内广泛使用的协议,包括许多在中国大陆运营的外贸企业。它的流量特征和普通的HTTPS流量非常相似,这使得防火长城很难区分和封锁使用ocserv的VPN流量。所以,相比其他协议,ocserv的VPN服务更不容易被封锁。
用过很多种VPN也自己搭建过好几种,最稳定的就属于 ocserv,速度也是最快的,不好的地方就是不能使用 PAC,因为它是 VPN,不是代理软件。
一键安装
本文更多是针对自己成果的存档,相关概念请自行谷歌学习。
我写了个一键部署脚本,在CentOS7以及CentOS8上测试通过,理论上也支持Ubuntu16及以上的系统。
sudo -i yum install wget -y && wget https://raw.githubusercontent.com/wangwanjie/ocserv-install/master/ocserv-install.sh && chmod +x ocserv-install.sh && ./ocserv-install.sh
首次执行脚本会提示安装或者退出,跟随指引一路走下去即可。
安装后再次运行脚本将出现如下菜单:
-> # ./ocserv-install.sh
检查是否安装了 ocserv ...
请选择要执行的功能:
1) 升级 ocserv 5) 配置域名 9) 关闭 ocserv
2) 卸载 ocserv 6) 查看ocserv登录日志 10) 查看 ocserv 状态
3) 添加 ocserv 用户 7) 查看系统日志 11) 退出
4) 移除 ocserv 用户 8) 启动或重启 ocserv
#?
安装后事宜
安装后请执行命令“配置域名”,提前在服务商做好DNS解析为证书登录做准备。注意,配置域名这里只对阿里做了处理,其他服务商请自行准备ssl证书并填入ocserv配置文件,执行脚本重启 ocserv 即可。
脚本主要部分展示
主要部分
#!/bin/bash
# 判断系统版本,根据不同系统选择不同的安装命令
if cat /etc/os-release | grep -q "centos"; then
PKG_MANAGER="yum"
elif cat /etc/os-release | grep -q "ubuntu\|debian"; then
PKG_MANAGER="apt-get"
else
echo "当前系统不受支持!"
exit 1
fi
# Detect Debian users running the script with "sh" instead of bash
if readlink /proc/$$/exe | grep -q "dash"; then
echo 'This installer needs to be run with "bash", not "sh".'
exit
fi
# Discard stdin. Needed when running from an one-liner which includes a newline
read -N 999999 -t 0.001
# Detect OpenVZ 6
if [[ $(uname -r | cut -d "." -f 1) -eq 2 ]]; then
echo "The system is running an old kernel, which is incompatible with this installer."
exit
fi
# Detect OS
# $os_version variables aren't always in use, but are kept here for convenience
if grep -qs "ubuntu" /etc/os-release; then
os="ubuntu"
os_version=$(grep 'VERSION_ID' /etc/os-release | cut -d '"' -f 2 | tr -d '.')
group_name="nogroup"
elif [[ -e /etc/debian_version ]]; then
os="debian"
os_version=$(grep -oE '[0-9]+' /etc/debian_version | head -1)
group_name="nogroup"
elif [[ -e /etc/almalinux-release || -e /etc/rocky-release || -e /etc/centos-release ]]; then
os="centos"
os_version=$(grep -shoE '[0-9]+' /etc/almalinux-release /etc/rocky-release /etc/centos-release | head -1)
group_name="nobody"
elif [[ -e /etc/fedora-release ]]; then
os="fedora"
os_version=$(grep -oE '[0-9]+' /etc/fedora-release | head -1)
group_name="nobody"
else
echo "This installer seems to be running on an unsupported distribution.
Supported distros are Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS and Fedora."
exit
fi
if [[ "$os" == "ubuntu" && "$os_version" -lt 1804 ]]; then
echo "Ubuntu 18.04 or higher is required to use this installer.
This version of Ubuntu is too old and unsupported."
exit
fi
if [[ "$os" == "debian" && "$os_version" -lt 9 ]]; then
echo "Debian 9 or higher is required to use this installer.
This version of Debian is too old and unsupported."
exit
fi
if [[ "$os" == "centos" && "$os_version" -lt 7 ]]; then
echo "CentOS 7 or higher is required to use this installer.
This version of CentOS is too old and unsupported."
exit
fi
# Detect environments where $PATH does not include the sbin directories
if ! grep -q sbin <<< "$PATH"; then
echo '$PATH does not include sbin. Try using "su -" instead of "su".'
exit
fi
if [[ "$EUID" -ne 0 ]]; then
echo "This installer needs to be run with superuser privileges."
exit
fi
if [[ ! -e /dev/net/tun ]] || ! ( exec 7<>/dev/net/tun ) 2>/dev/null; then
echo "The system does not have the TUN device available.
TUN needs to be enabled before running this installer."
exit
fi
OCSERV=/etc/ocserv
PORT=443
ipv4_network="192.169.103.0"
# 升级ocserv
function upgradeOcserv() {
echo "升级 ocserv ..."
# 根据系统使用合适的更新命令
if [[ $PKG_MANAGER == "yum" ]]; then
$PKG_MANAGER -y upgrade ocserv
else
$PKG_MANAGER -y upgrade
$PKG_MANAGER -y install ocserv
fi
echo "ocserv 升级完成!"
}
# 卸载ocserv
function uninstallOcserv() {
read -p "此操作将会卸载 ocserv 及其所有相关文件和配置,确认执行吗? [y/n]: " confirm
if [[ "$confirm" = [yY] ]]; then
echo "卸载 ocserv ..."
$PKG_MANAGER -y remove ocserv
rm -rf $OCSERV/
rm -rf /root/anyconnect
rm -rf /var/www/html/user
rm -rf /var/lib/ocserv
echo "ocserv 卸载完成!"
else
echo "已取消操作。"
fi
}
# 添加用户
function addUser() {
sudo /root/anyconnect/user_add.sh
}
# 移除用户
function removeUser() {
sudo /root/anyconnect/user_del.sh
}
# 启动或重启 ocserv
function startOrRestartOcserv() {
if pgrep "ocserv" > /dev/null ; then
echo "正在重启 ocserv ..."
systemctl restart ocserv
else
echo "正在启动 ocserv ..."
systemctl start ocserv
fi
if pgrep "httpd" > /dev/null ; then
echo "正在重启 httpd ..."
sudo systemctl restart httpd
else
echo "正在启动 httpd ..."
sudo systemctl start httpd
fi
}
# 关闭 ocserv
function stopOcserv() {
echo "正在关闭 ocserv ..."
systemctl stop ocserv
}
# 查看ocserv状态
function statusOcserv() {
systemctl status ocserv
}
# 配置 ipv4防火墙
function configIpv4Firewall() {
echo "配置 ipv4防火墙 ..."
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/60-custom.conf
read -p "是否开启bbr? [y/n]: " confirm
if [[ "$confirm" = [yY] ]]; then
if [[ "$os" == "centos" ]]; then
if [[ "$os_version" -eq "8" ]]; then
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.d/60-custom.conf
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.d/60-custom.conf
elif [[ "$os_version" -eq "7" ]]; then
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh https://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install kernel-ml -y
# sudo egrep ^menuentry /etc/grub2.cfg | cut -f 2 -d \'
# sudo grub2-set-default 0
# grub2-mkconfig -o /boot/grub2/grub.cfg
fi
elif [[ "$os" == "ubuntu" ]]; then
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.d/60-custom.conf
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.d/60-custom.conf
else
echo "Unsupported OS type."
exit 1
fi
else
echo "不安装bbr"
fi
sudo sysctl -p /etc/sysctl.d/60-custom.conf
# Check if firewall-cmd is installed
if [ "$(which firewall-cmd)" ]; then
echo "firewall-cmd is already installed."
else
if [[ "$os" == "centos" ]]; then
if [[ "$os_version" -eq "8" ]]; then
sudo dnf install firewalld -y
elif [[ "$os_version" -eq "7" ]]; then
sudo yum install firewalld -y
fi
elif [[ "$os" == "ubuntu" ]]; then
sudo apt-get update
sudo apt-get install firewalld -y
else
echo "Unsupported OS type."
exit 1
fi
fi
# Start firewall-cmd and enable at boot
sudo systemctl start firewalld
sudo systemctl enable firewalld
sudo firewall-cmd --permanent --add-port=$PORT/tcp
sudo firewall-cmd --permanent --add-port=$PORT/udp
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='${ipv4_network}/24' masquerade"
sudo systemctl reload firewalld
echo "配置 ipv4防火墙结束,已重启防火墙"
}
function prepare() {
if [[ "$os" == "centos" ]]; then
if [[ "$os_version" -eq "8" ]]; then
cd /etc/yum.repos.d/
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo
yum clean all
yum makecache
sudo yum install wget -y
sudo yum install dnf -y
sudo yum install expect -y
sudo dnf install httpd -y
sudo systemctl enable httpd
sudo dnf install epel-release -y
sudo yum install -y gnutls-utils
sudo dnf install ocserv -y
elif [[ "$os_version" -eq "7" ]]; then
sudo yum install wget -y
sudo yum install expect -y
sudo yum install httpd -y
sudo systemctl enable httpd
sudo yum install epel-release -y
sudo yum install -y gnutls-utils
sudo yum install ocserv -y
fi
elif [[ "$os" == "ubuntu" ]]; then
sudo apt update
sudo apt install ocserv
fi
get_public_ip=$(grep -m 1 -oE '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' <<< "$(wget -T 10 -t 1 -4qO- "http://ip1.dynupdate.no-ip.com/" || curl -m 10 -4Ls "http://ip1.dynupdate.no-ip.com/")")
read -p "Public IPv4 address / hostname [$get_public_ip]: " public_ip
# If the checkip service is unavailable and user didn't provide input, ask again
until [[ -n "$get_public_ip" || -n "$public_ip" ]]; do
echo "Invalid input."
read -p "Public IPv4 address / hostname: " public_ip
done
[[ -z "$public_ip" ]] && public_ip="$get_public_ip"
echo "公网 IP:$public_ip"
cd $OCSERV
mkdir -p pki user config-per-group config-per-user defaults tmpl pem
mkdir -p /root/anyconnect
remote_repo=https://raw.githubusercontent.com/wangwanjie/ocserv-install
remote_repo_branch=master
rm -rf ocserv.conf connect-script config-per-group/* tmpl/* pem/*
wget --no-check-certificate $remote_repo/$remote_repo_branch/ocserv.conf
wget --no-check-certificate $remote_repo/$remote_repo_branch/connect-script
wget --no-check-certificate $remote_repo/$remote_repo_branch/config-per-group/main -O config-per-group/main
wget --no-check-certificate $remote_repo/$remote_repo_branch/config-per-group/others -O config-per-group/others
chmod +x connect-script
cd /root/anyconnect
wget --no-check-certificate $remote_repo/$remote_repo_branch/gen-client-cert.sh
wget --no-check-certificate $remote_repo/$remote_repo_branch/user_add.sh
wget --no-check-certificate $remote_repo/$remote_repo_branch/user_del.sh
chmod +x gen-client-cert.sh
chmod +x user_add.sh
chmod +x user_del.sh
read -p "请输入证书签发组织名称 [vanjay.cn]: " sign_org
[[ -z "$sign_org" ]] && sign_org="vanjay.cn"
read -p "请输入证书有效期(天) [3650]: " cert_valid_days
[[ -z "$cert_valid_days" ]] && cert_valid_days="3650"
cd $OCSERV/tmpl
cat >ca.tmpl <<EOF
cn = "VanJay AnyConnect CA"
organization = "$sign_org"
serial = 1
expiration_days = $cert_valid_days
ca
signing_key
cert_signing_key
crl_signing_key
EOF
cat >server.tmpl <<EOF
cn = "VanJay AnyConnect CA"
organization = "$sign_org"
serial = 2
expiration_days = $cert_valid_days
encryption_key
signing_key
tls_www_server
EOF
cat << _EOF_ >crl.tmpl
crl_next_update = 365
crl_number = 1
_EOF_
}
function configDomain() {
read -p "请输入 VPN 域名!(默认为 tz.vanjay.cn): " domain_name
if [ -z "$domain_name" ]; then
domain_name="tz.vanjay.cn"
fi
read -p "请输入您的 Email!(默认为 396736694@qq.com): " mail_address
if [ -z "$mail_address" ]; then
mail_address="396736694@qq.com"
fi
while true; do
read -p "请输入ali_key: " ali_key
if [[ -n "$ali_key" ]]; then
break
else
echo "无效的 ali_key"
fi
done
while true; do
read -p "请输入ali_secret: " ali_secret
if [[ -n "$ali_secret" ]]; then
break
else
echo "无效的 ali_secret"
fi
done
export Ali_Key=$ali_key
export Ali_Secret=$ali_secret
yum install socat -y
curl https://get.acme.sh | sh
export PATH="$PATH:$HOME/.acme.sh"
alias acme.sh=~/.acme.sh/acme.sh
acme.sh --register-account -m $mail_address --server zerossl
acme.sh --issue --dns dns_ali -d $domain_name
mkdir -p $OCSERV/pki
cp -Rf ~/.acme.sh/${domain_name}_ecc/ $OCSERV/pki
cer_path=$OCSERV/pki/${domain_name}_ecc/${domain_name}.cer
key_path=$OCSERV/pki/${domain_name}_ecc/${domain_name}.key
# 更新 ocserv.conf 文件
sed -i "s#\(server-cert = \).*#\1$cer_path#" $OCSERV/ocserv.conf
sed -i "s#\(server-key = \).*#\1$key_path#" $OCSERV/ocserv.conf
sed -i "s#\(default-domain = \).*#\1$domain_name#" $OCSERV/ocserv.conf
startOrRestartOcserv
echo "已修改 ocserv.conf,已重启 ocserv 服务"
}
function generate_server_cert() {
cd $OCSERV/pem
# 生成 CA 证书
certtool --generate-privkey --outfile ca-key.pem
certtool --generate-self-signed --load-privkey ca-key.pem --template $OCSERV/tmpl/ca.tmpl --outfile ca-cert.pem
# 生成本地服务器证书
certtool --generate-privkey --outfile server-key.pem
certtool --generate-certificate --load-privkey server-key.pem \
--load-ca-certificate ca-cert.pem --load-ca-privkey ca-key.pem \
--template $OCSERV/tmpl/server.tmpl --outfile server-cert.pem
# 生成证书注销文件
touch $OCSERV/pem/revoked.pem
certtool --generate-crl --load-ca-privkey ca-key.pem \
--load-ca-certificate ca-cert.pem \
--template $OCSERV/tmpl/crl.tmpl --outfile crl.pem
}
function useSystemDNS() {
sed -i -e "/^#*\s*dns\s*=.*$/d" $OCSERV/ocserv.conf
# Locate the proper resolv.conf
# Needed for systems running systemd-resolved
if grep '^nameserver' "/etc/resolv.conf" | grep -qv '127.0.0.53' ; then
resolv_conf="/etc/resolv.conf"
else
resolv_conf="/run/systemd/resolve/resolv.conf"
fi
# Obtain the resolvers from resolv.conf and use them for OpenVPN
grep -v '^#\|^;' "$resolv_conf" | grep '^nameserver' | grep -v '127.0.0.53' | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' | while read line; do
echo "\ndns = $line" >> $OCSERV/ocserv.conf
done
}
function useOtherDNS() {
sed -i "s|dns = 1.1.1.1|dns = $1|g" $OCSERV/ocserv.conf
sed -i "s|dns = 8.8.8.8 # the second|dns = $2|g" $OCSERV/ocserv.conf
}
# 配置 ocserv.conf
function configOcserv() {
read -p "请输入要监听的端口号(推荐使用80或443或10443)[443]: " PORT
until [[ -z "$PORT" || "$PORT" =~ ^[0-9]+$ && "$PORT" -le 65535 ]]; do
echo "$PORT: invalid port."
read -p "Port [443]: " PORT
done
[[ -z "$PORT" ]] && PORT="443"
echo "请选择DNS:"
select FUNC in "Current system resolvers" "Google" "1.1.1.1" "Google & 1.1.1.1" "OpenDNS" "Quad9" "AdGuard"; do
case $FUNC in
"Current system resolvers" ) useSystemDNS; break;;
"Google" ) useOtherDNS 8.8.8.8 8.8.4.4; break;;
"1.1.1.1" ) useOtherDNS 1.1.1.1 1.0.0.1; break;;
"Google & 1.1.1.1" ) useOtherDNS 1.1.1.1 8.8.8.8; break;;
"OpenDNS" ) useOtherDNS 208.67.222.222 208.67.220.220; break;;
"Quad9" ) useOtherDNS 9.9.9.9 149.112.112.112; break;;
"AdGuard" ) useOtherDNS 94.140.14.14 94.140.15.15; break;;
esac
done
until [[ $valid_ip == true ]]
do
read -p "请输入 ipv4_network [192.168.103.0]: " ipv4_network
[[ -z "$ipv4_network" ]] && ipv4_network="192.168.103.0"
valid_ip=true
IFS='.' read -ra ip_array <<< "$ipv4_network"
if [[ ${#ip_array[@]} -ne 4 ]]; then
valid_ip=false
else
for (( i=0; i<${#ip_array[@]}; i++ ))
do
octet=${ip_array[i]}
if [[ "$i" -eq 0 ]]; then
if [[ "$octet" -eq 0 ]]; then
echo "$i ----- $octet"
valid_ip=false
break
else
# 判断第一个分段是否为1到3位数字,且不能以0开头
if [[ ! "$octet" =~ ^[1-9][0-9]{0,2}$ ]]; then
valid_ip=false
break
fi
fi
else
# 判断每个分段是否为1到3位数字,可以以0开头
if [[ ! "$octet" =~ ^([0-9]|[1-9][0-9]{1,2})$ ]]; then
valid_ip=false
break
elif [[ "$octet" -lt 0 || "$octet" -gt 255 ]]; then
valid_ip=false
break
fi
fi
done
fi
if [[ $valid_ip == false ]]; then
echo "输入的IP地址不合法,请重新输入!"
fi
done
sed -i "s|tcp-port = 443|tcp-port = $PORT|g" $OCSERV/ocserv.conf
sed -i "s|udp-port = 443|udp-port = $PORT|g" $OCSERV/ocserv.conf
sed -i "s|ipv4-network = 192.168.103.0|ipv4-network = $ipv4_network|g" $OCSERV/ocserv.conf
if [[ -n "$public_ip" ]]; then
sed -i "s/47.242.201.43/$public_ip/g" $OCSERV/ocserv.conf
fi
echo "ocserv配置修改成功!"
}
# 启用开机自启
function enableAutoStart() {
echo "是否开启开机自启?(yes或no)"
select yn in "yes" "no"; do
case $yn in
yes ) systemctl enable ocserv; break;;
no ) break;;
esac
done
}
function logOcserv() {
if [ -f /etc/ocserv/login.log ]; then
tail -f /etc/ocserv/login.log
else
echo "Error: /etc/ocserv/login.log not found!"
fi
}
function logSystem() {
if [ -f /var/log/messages ]; then
tail -f /var/log/messages
else
echo "Error: /var/log/messages not found!"
fi
}
# 安装ocserv
echo "检查是否安装了 ocserv ..."
if ! hash ocserv 2>/dev/null; then
echo "ocserv 未安装!"
echo "请选择安装 ocserv 或退出:"
select yn in "安装" "退出"; do
case $yn in
安装 ) break;;
退出 ) exit;;
esac
done
# 根据系统使用合适的安装命令
if [[ $PKG_MANAGER == "yum" ]]; then
$PKG_MANAGER -y upgrade
$PKG_MANAGER -y install epel-release
$PKG_MANAGER -y install ocserv
else
sudo -i
$PKG_MANAGER install wget -y
$PKG_MANAGER -y update
$PKG_MANAGER install epel-release wget -y
$PKG_MANAGER install ocserv httpd -y
fi
prepare
generate_server_cert
configOcserv
configIpv4Firewall
enableAutoStart
echo "ocserv 安装完成!"
else
# 主程序
echo "请选择要执行的功能:"
select FUNC in "升级 ocserv" "卸载 ocserv" "添加 ocserv 用户" "移除 ocserv 用户" "配置域名" "查看ocserv登录日志" "查看系统日志" "启动或重启 ocserv" "关闭 ocserv" "查看 ocserv 状态" "退出"; do
case $FUNC in
"升级 ocserv" ) upgradeOcserv; break;;
"卸载 ocserv" ) uninstallOcserv; break;;
"添加 ocserv 用户" ) addUser; break;;
"移除 ocserv 用户" ) removeUser; break;;
"配置域名" ) configDomain; break;;
"查看ocserv登录日志" ) logOcserv; break;;
"查看系统日志" ) logSystem; break;;
"启动或重启 ocserv" ) startOrRestartOcserv; break;;
"关闭 ocserv" ) stopOcserv; break;;
"查看 ocserv 状态" ) statusOcserv; break;;
"退出" ) exit;;
esac
done
fi
echo "ocserv 脚本运行结束!"
echo "再次运行此脚本可选择功能!"
生成证书
#!/bin/bash
# 检查是否使用 root 账户执行脚本
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root."
exit 1
fi
# 从命令行参数获取用户名和路径
USER=$1
OCSERV=$2
USER_DIR=$OCSERV/user/$USER
# 确保目录存在
mkdir -p $USER_DIR && cd $USER_DIR
# 生成私钥
SERIAL=`date +%s`
certtool --generate-privkey --outfile $USER-key.pem
# 生成证书的模板文件
cat << _EOF_ >$USER.tmpl
cn = "$USER"
unit = "users"
serial = "$SERIAL"
expiration_days = 9999
signing_key
tls_www_client
_EOF_
# 用私钥、证书模板以及根证书生成证书文件
cd $USER_DIR
certtool --generate-certificate --load-privkey $USER-key.pem --load-ca-certificate $OCSERV/pem/ca-cert.pem --load-ca-privkey $OCSERV/pem/ca-key.pem --template $USER.tmpl --outfile $USER-cert.pem
# 将证书文件导出为 p12 格式
openssl pkcs12 -export -inkey $USER-key.pem -in $USER-cert.pem -name "$USER VPN Client Cert" -certfile $OCSERV/pem/ca-cert.pem -out $USER.p12
添加用户
#!/bin/bash
# 这个脚本是用来同时添加 VPN 用户和他们的证书的
# 检查是否使用 root 账户执行脚本
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root."
exit 1
fi
function input_user() {
# 从两个数据源获取服务器的公网 IP
get_public_ip=$(grep -m 1 -oE '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' <<< "$(wget -T 10 -t 1 -4qO- "http://ip1.dynupdate.no-ip.com/" || curl -m 10 -4Ls "http://ip1.dynupdate.no-ip.com/")")
public_ip="$get_public_ip"
# 如果第一个数据源没有返回正确的公网 IP,就尝试第二个数据源
if [[ -z "$public_ip" ]]; then
public_ip=$(lynx --source www.monip.org | sed -nre 's/^.* (([0-9]{1,3}\.){3}[0-9]{1,3}).*$/\1/p')
fi
# 从 ocserv.conf 文件中获取 VPN 端口号
PORT=$(grep ^\s*tcp-port /etc/ocserv/ocserv.conf | awk '{print $NF}')
# 获取 VPN 用户名和组别
read -p "请输入您的 VPN 用户名: " user_name
if [[ ! -n "$user_name" ]]; then
echo "您没有输入用户名,请重新执行程序"
else
read -p "请输入您的 VPN 用户组别: " user_group
fi
# 获取 VPN 用户密码
if [[ ! -n "$user_group" ]]; then
echo "您没有输入用户组别,将使用配置文件中的默认组别"
user_group="others"
fi
read -p "请输入您的密码: " user_pass
if [[ ! -n "$user_pass" ]]; then
echo "您没有输入密码,请重新执行程序"
else
user_add
cert_add
fi
}
function user_add() {
# 根据不同的系统,选择不同的 expect 路径
if [ -x "$(command -v yum)" ]; then
EXPECT_CMD="/usr/bin/expect"
else
EXPECT_CMD="/usr/bin/env expect"
fi
sudo touch /etc/ocserv/ocpasswd
sudo chmod 600 /etc/ocserv/ocpasswd
$EXPECT_CMD <<-END
spawn sudo ocpasswd -c /etc/ocserv/ocpasswd -g $user_group $user_name
expect "Enter password:"
send "$user_pass\r"
expect "Re-enter password:"
send "$user_pass\r"
expect eof
exit
END
}
# 为用户添加证书
function cert_add() {
OCSERV=/etc/ocserv
user_root_dir=$OCSERV/user
mkdir -p $user_root_dir/$user_name
cd $user_root_dir/$user_name
# 根据不同的系统,选择不同的 expect 路径
if [ -x "$(command -v yum)" ]; then
EXPECT_CMD="/usr/bin/expect"
else
EXPECT_CMD="/usr/bin/env expect"
fi
$EXPECT_CMD <<-END
spawn sudo /root/anyconnect/gen-client-cert.sh $user_name $OCSERV
expect "Enter Export Password:"
send "$user_pass\r"
expect "Verifying - Enter Export Password:"
send "$user_pass\r"
expect eof
exit
END
cd $user_root_dir && mkdir -p /var/www/html/user
cp -R $user_name /var/www/html/user/$user_name
echo "$user_name 用户已成功创建,密码为 $user_pass"
echo "$user_name 的证书已成功创建,请点击以下链接进行下载。"
echo "http://$public_ip/user/$user_name/$user_name.p12"
echo "证书本地路径为:$user_root_dir/$user_name"
echo "导入证书的密码是 $user_pass"
echo "VPN 地址和端口是 $public_ip:$PORT"
}
# 安装 shell
function shell_install() {
input_user
}
shell_install
删除用户
#!/bin/bash
# ocserv 删除用户及注销用户的证书脚本文件
# 检查是否使用 root 账户执行脚本
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root."
exit 1
fi
function user_del() {
OCSERV=/etc/ocserv
# 获取要删除用户的用户名
read -p "请输入您想要删除的用户名!" user_name
if [[ ! -n "$user_name" ]]; then
echo "您没有输入用户名,请重新执行程序"
else
# 使用 ocpasswd 命令删除用户
/usr/bin/ocpasswd -d $user_name
echo "$user_name 用户已成功删除"
# 将用户证书添加到撤销列表,并生成 CRL 文件
cat $OCSERV/user/$user_name/$user_name-cert.pem >> $OCSERV/pem/revoked.pem
certtool --generate-crl --load-ca-privkey $OCSERV/pem/ca-key.pem --load-ca-certificate $OCSERV/pem/ca-cert.pem --load-certificate $OCSERV/pem/revoked.pem --template $OCSERV/tmpl/crl.tmpl --outfile $OCSERV/pem/crl.pem
echo "$user_name 用户的证书已被注销"
# 重启 ocserv 服务
systemctl restart ocserv.service
fi
}
# 调用函数
user_del
脚本
脚本已开源,如有定制需求可以fork自行修改。
相关常用命令
journalctl -u ocserv -f
systemctl reload firewalld
firewall-cmd --list-all
systemctl enable firewalld
systemctl disable firewalld
systemctl start firewalld
firewall-cmd --permanent --add-service=https
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-port=xxx/tcp
firewall-cmd --permanent --remove-port=xxx/tcp
firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='your_ipv4_mask/24' masquerade"
资料
客户端下载
可以使用 OpenConnect GUI 也可以使用 openconnect 命令行连接,使用方法自行搜索。
推荐使用 Cisco Anyconnect 或者 Cisco Secure Client 连接,下载链接