简便的 OpenVPN 生活

为了和小伙伴们实现更多的内网互联功能,于是最后又找到了曾经烧脑很久的应用 OpenVPN。当然有 wireguard 的经验后其实使用 OpenVPN 已经非常轻松了


1、OpenVPN 简介

Wikipedia-OpenVPN [ 链接 ]

OpenVPN Community [ 链接 ]

OpenVPN 是 OpenVPN Inc. 联合创始人发起的开源项目的名称。

OpenVPN 完全是社区支持的 OSS 项目,它使用 GPL 许可证。

OpenVPN 是一个用于创建 VPN 加密通道的软件包,最早由 James Yonan 编写。OpenVPN 与 OpenSSL 库紧密相关,并由此获得许多加密功能。它大量使用了 OpenSSL 加密库中的 SSLv3/TLSv1 协议函数库。

OpenVPN 有许多来自 OpenVPN Inc. 和更广泛的 OpenVPN 社区的开发人员和贡献者。此外,还有许多项目扩展或与 OpenVPN 相关。

OpenVPN 允许创建的 VPN 使用公开密钥、电子证书、或者用户名/密码来进行身份验证。它支持 SSL/TLS 安全、Ethernet bridging、经由代理的 TCP 或 UDP 隧道和 NAT。它还支持动态 IP 地址以及 DHCP,可伸缩性足以支持数百或数千用户的使用场景。

OpenVPN 目前能在 Solaris、Linux、OpenBSD、FreeBSD、NetBSD、Mac OS X 与 Microsoft Windows 以及 Android 和 iOS 上运行,并包含了许多安全性的功能。

OpenVPN 并不是一个基于 Web 的 VPN 软件,也不与 IPsec 及其他 VPN 软件包兼容。


2、OpenVPN 安装与管理

安装 openvpn 十分简单

yum install -y openvpn easy-rsa NetworkManager-openvpn

安装好后,系统中会在 /etc/openvpn/server 下存放你的服务端配置, /etc/openvpn/client 下存放你的客户端配置

无论是服务端配置还是客户端配置,都是以 .conf 结尾的文件,这样才可以被 firewalld 监测到

启动和管理的服务器命令如下

systemctl status [email protected]

3、OpenVPN 证书管理

通常 OpenVPN 的登陆管理体系是证书体系(因为密码的难以精准区分)所以本文仅说明证书登陆体系需要的 OpenVPN 证书管理方式


3.1、初始化 easyrsa

首先你需要一个 easyrsa 程序和文件夹以容纳证书,为了方便我们干脆在 openvpn 的配置文件夹中额外创建一个文件夹

mkdir /etc/openvpn/certs

由于以后生成证书都在这个文件夹中,所以要禁止其他人访问此文件夹

chmod 750 /etc/openvpn/certs/

然后我们创建 easy-rsa,并复制 easy-rsa 和所需的文件到该文件夹中

mkdir -p /etc/openvpn/certs/example/easy-rsa
cp -a /usr/share/doc/easy-rsa-*/vars.example /etc/openvpn/certs/example/easy-rsa/vars
cp -ar /usr/share/easy-rsa/3/* /etc/openvpn/certs/example/easy-rsa/

接着一定不要忘了将证书文件夹的权限设置好组权限,以允许 openvpn 可以访问。

chown -R :openvpn /etc/openvpn/certs/

最后我们在 vars 中补全所有证书使用的信息即可
(除了证书之间区分需要的 CN 不写在这里,其他的都是 CentOS 自动生成的内容复制过来的)

echo 'set_var EASYRSA "$PWD"
set_var EASYRSA_PKI "$EASYRSA/pki"
set_var EASYRSA_DN "cn_only"
set_var EASYRSA_REQ_COUNTRY "US"
set_var EASYRSA_REQ_PROVINCE "California"
set_var EASYRSA_REQ_CITY "San Francisco"
set_var EASYRSA_REQ_ORG "Copyleft Certificate Co"
set_var EASYRSA_REQ_EMAIL "[email protected]"
set_var EASYRSA_REQ_OU "My Organizational Unit"
set_var EASYRSA_KEY_SIZE 2048
set_var EASYRSA_ALGO rsa
set_var EASYRSA_CA_EXPIRE 3650
set_var EASYRSA_CERT_EXPIRE 3650
set_var EASYRSA_NS_SUPPORT "no"
set_var EASYRSA_NS_COMMENT "Easy-RSA Generated Certificate"
set_var EASYRSA_EXT_DIR "$EASYRSA/x509-types"
set_var EASYRSA_SSL_CONF "$EASYRSA/openssl-1.0.cnf"
set_var EASYRSA_DIGEST "sha256"' >> /etc/openvpn/certs/example/easy-rsa/vars

3.2、使用 easyrsa 生成证书

初始化证书生成文件夹

./easyrsa init-pki

生成一个 CA 证书 ( build-ca ) ,生成方式为后台静默生成 ( –batch ) ,证书不使用密码 ( nopass )

./easyrsa --batch build-ca nopass

生成一个服务端证书 ( build-server-full ) ,自动生成,自动签名,证书 CN 为 ServerCN,保存文件名为 ServerCN

./easyrsa build-server-full ServerCN

生成一个客户端证书 ( build-client-full ) ,自动生成,自动签名,证书 CN 为 ClientCN,保存文件名为 ClientCN

./easyrsa build-client-full ClientCN

[ easyrsa 2 ] 生成一个证书 ( gen-req ) ,不指定证书身份,证书 CN 为 YourCN ( –req-cn=YourCN ) ,保存文件名为 CertFileName

./easyrsa --batch --req-cn=YourCN gen-req CertFileName nopass

[ easyrsa 2 ] 签名一个证书 ( sign-req ) ,证书文件名为 CertFileName,证书身份为 server

./easyrsa sign-req server CertFileName

验证一个证书是否被 CA 签名并在有效期内

openssl verify -CAfile pki/ca.crt pki/issued/CertFileName.crt

以上操作完毕后


4、OpenVPN 配置文件说明

OpenVPN 的使用参数推荐查阅 man openvpn 来看,也可以通过官方的 man 网页查阅

4.1、

/usr/share/doc/openvpn-*/sample/sample-config-files/server.conf

chown :openvpn /etc/openvpn/server/example.conf

chmod 644 /etc/openvpn/server/example.conf

 


5、OpenVPN 配置备忘录

由于 OpenVPN 配置容易踩坑,所以留下一份备忘录(面向问题编程)


5.1、手动指定客户端的 IP 地址

在这里首先有两种控制方案,第一种利用了 DHCP 获取 IP 时的自动续租

5.1.1、通过 DHCP 续租记录文件控制客户端 IP 地址

编辑服务端配置文件,添加 DHCP 续租的参数配置(文件里面储存了用户 CN 和 IP 的对应关系)并在此行命令最后添加 ” 0″ 设为不更新此文件

vim /etc/openvpn/server/servername-udp.conf
ifconfig-pool-persist /etc/openvpn/server/servername/ipp/udp.txt 0

然后我们编辑这个续租记录文件(这里的 servername_username 是我创建时的用户 CN 字段,请注意替换)

servername_username,10.0.0.10

改好后重启服务端程序

systemctl restart [email protected]

然后我们就可以观察到以下现象
1、续租记录文件中存在的 UA,可以正常访问
2、续租记录文件中不存在的 UA,无法获得 IP 地址,但是会得到一个其他的 IP 地址
3、续租记录文件在运行时修改,然后客户端重连,只会获得手动配置的 IP 地址。

5.1.2、通过客户端独占配置文件控制客户端 IP 地址

编辑服务端配置文件,添加客户端配置文件夹 client-config-dir 并创建好指定目录(记着用这个方式一定要允许更新 DHCP 续租记录)

vim /etc/openvpn/server/servername-udp.conf
client-config-dir /etc/openvpn/server/servername/clients/udp/

检查目录权限,允许用户组访问(默认组为 openvpn,如果配置文件指定过则参照配置文件)

chown -R :openvpn /etc/openvpn/server/servername/clients/udp/

创建一个客户端配置文件,注意此文件名为用户的 CN 字段

touch servername_username

修改客户端配置文件,添加客户端指定的 IP 地址(本例为 10.179.110.20/24)

vim servername_username
ifconfig-push 10.179.110.20 255.255.255.0

改好后重启服务端程序

systemctl restart [email protected]

然后我们就可以观察到以下现象
1、客户端持 UA 直接访问,正常访问,只得到指定的 IP 地址
2、客户端持其他 UA 直接访问,正常访问,但是会得到一个其他的 IP 地址
3、续租记录文件在运行时修改,然后客户端重连,只会获得手动配置的 IP 地址。


5.2、客户端想访问服务端连接的其他网络

编辑服务端配置文件,添加客户端配置文件夹 client-config-dir 并创建好指定目录,并且防火墙已放行。

vim /etc/openvpn/server/servername-udp.conf

在服务端配置文件中加入一条路由推送(本例推送 10.10.0.0/16)

push "route 10.10.0.0 255.255.0.0"

保存并重启服务端即可下发,客户端等同在配置文件中添加一条 route 10.10.0.0 255.255.0.0 所以能正常工作。


5.3、服务端想访问客户端连接的其他网络

首先前提是两个 CN 名不相同的 clientname01 和 clientname02 两者都有不同的子网,本例中两者分别为 10.20.0.0/16 和 10.21.0.0/16 两个地址段,防火墙已放行。

编辑服务端配置文件,添加客户端配置文件夹 client-config-dir 并创建好指定目录

vim /etc/openvpn/server/servername-udp.conf

在服务端配置文件中加入两条路由推送(本例推送 10.20.0.0/16 和 10.21.0.0/16)

push "route 10.20.0.0 255.255.0.0"
push "route 10.21.0.0 255.255.0.0"

在服务端配置文件中加入两条允许路由(本例推送 10.20.0.0/16 和 10.21.0.0/16)

route 10.20.0.0 255.255.0.0
route 10.21.0.0 255.255.0.0

然后我们进入配置文件中 client-config-dir 的目标文件夹,然后手动创建两个配置文件,添加 iroute 配置

echo "iroute 10.20.0.0 255.255.0.0" > clientname01
echo "iroute 10.21.0.0 255.255.0.0" > clientname02

创建完毕后,重启服务端,然后就可以检查是否配置正确。

然后我们就可以观察到以下现象
1、route -n 命令查看到存在此路由,但是下一跳 (gateway) 不正确,端口正确
2、直接 Ping 的时候可以直接 ping 通,不需要 NAT
3、如果没有写那两个服务端的 route 配置,你会发现你没有路由条目指向,你的流量最终都匹配了缺省路由出去了。
4、如果没有写那两个客户端的 iroute 配置,即使你手动指定了静态路由,最终对端都不会收到任何你发送的数据报文。
5、如果没有写那两个服务端的 push 配置,你的其他客户端不会知道此路由可以由你转发。

额外的,第一步添加两条允许路由,你也可以用 ip route add 或者 /etc/sysconfig/network-scripts/route-tun0 的方式添加
添加时下一跳地址只要写对应 network 的网段任意 IP(非本机),最终 openvpn 会清洗掉下一跳地址并根据 iroute 发给对应用户
(当多个用户存在相同 iroute 时未测试,)


5.4、Windows 客户端的额外配置

????????????

配置文件中添加一条语句就可以仅使用服务端下发的 DNS 进行解析了

block-outside-dns

然后额外的,服务端 push 网关的时候,其实有一些参数可以控制

push “redirect-gateway def1 bypass-dhcp”

上面的 redirect-gateway 是选项名,而通常使用的 def1 和 bypass 这两个参数标志,更多参数标志请看官方 WIKI 文档
其中第一个保证不覆盖默认网关,将生成一个 0.0.0.0/1 ,由于路由匹配采用最长子网掩码原则,它比默认路由 0.0.0.0/0 匹配的优先级高。
其中第二个 bypass-dhcp 是为了避免将客户端连接网络所需的 DHCP 服务器的流量也截到隧道中,所以要将对应流量跳过隧道,同样的还有 bypass-dns 跳过本机 DNS


5.5、OpenVPN 的 DNS 问题

首先你要有心理准备,这是个非常烧脑的问题,甚至持续了四年之久官方也没什么动力去让它变得更好些

5.5.1、Linux 环境的 DNS 问题

先放一下 Arch 的解决方式 [ 链接 ] 和相关联的 systemd-resolv 问题 issues [ 链接 ]

在这里,首先说明一下,Arch 的解决方案不一定完整适用于其他平台,所以仅供思路参考

核心内容主要为,Linux 的配置文件启动时,可以自动执行一个脚本,以及停止时,也自动执行一个脚本

利用脚本的运行,获取服务端 push 来的 dns,然后写入到 resolv.conf 中,停止时恢复原有 resolv.conf

至今各大讨论区讨论的内容,主干逻辑也是这样推荐的,唯一区别在于,谁来写(可能是 systemd),和写入权限(比如换 mount 避免修改源文件)

至今最普遍的方案是安装 resolvconf 并利用这个程序来写入系统的 /etc/resolv.conf


5.5.2、Windows 环境的 DNS 问题

Windows 平台是最方便的一个平台,毕竟它本身就支持从 OpenVPN 的 DHCP 获取 DNS 。

如果你获取不到,你甚至可以在 TAP 网卡上手动配置一个 DN S 也没问题。


5.5.3、MacOS 环境的 DNS 问题

MacOS 平台和 Windows 平台很接近,唯一区别是不能像 Windows 一样可以用 block-outside-dns 完全阻止掉其他的 DNS 解析。

MacOS 依然可以自动从 OpenVPN 的 DHCP 中获取 DNS,以及可以直接编辑网卡的 DNS 配置。


5.5.4、Android 环境的 DNS 问题

Android 平台配置正确的情况下,Android 可以从 OpenVPN 的 DHCP 获取 DNS 。

但是提醒一下安卓用户,因为安卓各发行版碎片化的问题,有些机器就可能获取不到,甚至你都不能使用 adb 附属的 setprop  命令

通常这些故障机器你需要安装一个能联动手机 adb 的软件,进而运行命令 setprop net.dns1 your-vpn-dns-ip 来手动配置 DNS

如果你不这样做的话,你的所有解析依然会试图访问运营商给你的 DNS。然而由于运营商下发到本地的 DNS 其实是他的内网 IP,所以你从 VPN 将流量发出去后无法访问 DNS。


5.5.5、IOS 环境的 DNS

IOS 没有测试,本文不讨论。


5.6、

 


5.7、

 


5.8、

 


6、OpenVPN 自动化脚本

虽然为了学习不建议使用脚本,但是 OpenVPN 的证书生成的确是很麻烦的问题,所以还是留一个脚本源代码以供参考(极长预警)

脚本说明
/etc/openvpn/server/servername.conf 服务端配置文件
/etc/openvpn/server/servername/* 服务端证书文件
/etc/openvpn/server/servername/clients/tcp/clientname 服务端 TCP 协议绑定的客户端配置
/etc/openvpn/server/servername/clients/udp/clientname 服务端 UDP 协议绑定的客户端配置
/etc/openvpn/server/servername/ipp/tcp/* 服务端 TCP 协议保持的客户端 DHCP 续租 IP 地址
/etc/openvpn/server/servername/ipp/tcp/* 服务端 UDP 协议保持的客户端 DHCP 续租 IP 地址

/etc/openvpn/server/clientname.conf 服务端配置文件
/etc/openvpn/client/clientname/* 客户端证书文件
/etc/openvpn/server/clientname.ovpn 服务端完整配置文件


6.1、OpenVPN 安装脚本

#!/bin/bash
rpm -q openvpn > /dev/null 2>&1 && echo "openvpn already install" || yum install -y openvpn
rpm -q NetworkManager-openvpn > /dev/null 2>&1 && echo "NMopenvpn already install" || yum install -y NetworkManager-openvpn
rpm -q easy-rsa > /dev/null 2>&1 && echo "easy-rsa already install" || yum install -y easy-rsa
if [ -f "/etc/openvpn/server/example.conf" ] ; then
echo "Already copy example.conf"
else
cp /usr/share/doc/openvpn-*/sample/sample-config-files/server.conf /etc/openvpn/server/example.conf
chown :openvpn /etc/openvpn/server/example.conf
chmod 644 /etc/openvpn/server/example.conf
echo "Copy example.conf Finish"
fi
if [ -d "/etc/openvpn/certs/example/" ] ; then
echo "Already copy easy-rsa file"
else
mkdir -p /etc/openvpn/certs/example/easy-rsa && chmod 750 /etc/openvpn/certs/
cp -a /usr/share/doc/easy-rsa-*/vars.example /etc/openvpn/certs/example/easy-rsa/vars
echo 'set_var EASYRSA "$PWD"
set_var EASYRSA_PKI "$EASYRSA/pki"
set_var EASYRSA_DN "cn_only"
set_var EASYRSA_REQ_COUNTRY "US"
set_var EASYRSA_REQ_PROVINCE "California"
set_var EASYRSA_REQ_CITY "San Francisco"
set_var EASYRSA_REQ_ORG "Copyleft Certificate Co"
set_var EASYRSA_REQ_EMAIL "[email protected]"
set_var EASYRSA_REQ_OU "My Organizational Unit"
set_var EASYRSA_KEY_SIZE 2048
set_var EASYRSA_ALGO rsa
set_var EASYRSA_CA_EXPIRE 3650
set_var EASYRSA_CERT_EXPIRE 3650
set_var EASYRSA_NS_SUPPORT "no"
set_var EASYRSA_NS_COMMENT "Easy-RSA Generated Certificate"
set_var EASYRSA_EXT_DIR "$EASYRSA/x509-types"
set_var EASYRSA_SSL_CONF "$EASYRSA/openssl-1.0.cnf"
set_var EASYRSA_DIGEST "sha256"' >> /etc/openvpn/certs/example/easy-rsa/vars
chmod 755 /etc/openvpn/certs/example/easy-rsa/vars
cp -r /usr/share/easy-rsa/3/* /etc/openvpn/certs/example/easy-rsa/
chown -R :openvpn /etc/openvpn/certs/
echo "Copy easy-rsa file Finish"
fi

6.2、OpenVPN 证书管理脚本

#!/usr/bin/bash env
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
#
# OPENVPN Certificate Automatic Creation Tool
#
# System Required:  CentOS 7+
#
# Author: StarryVoid <[email protected]>
# Intro:  https://blog.starryvoid.com/archives/356.html
#

red='\033[0;31m'
green='\033[0;32m'
yellow='\033[0;33m'
plain='\033[0m'

config_select(){
    echo
    while true
    do
    echo  "------------------------"
    echo  "Which choose you'd select:"
    echo  "------------------------"
    echo  "(1) Create CA certs"
    echo  "(2) Create Server Certs"
    echo  "(3) Create Client Certs"
    echo  "(4) Create Server Config"
    echo  "(5) Create Client Config"
    echo  "(6) View Server Config"
    echo  "(7) View Client Config"
    echo  "(8) Exit"
    echo  "------------------------"
    read -p "Please enter a number (Default 8):" selected
    [ -z "${selected}" ] && selected="8"
    case "${selected}" in
        1|2|3|4|5|6|7|8)
        echo
        echo "You choose = ${selected}"
        echo
        break
        ;;
        *)
        echo -e "[${red}Error${plain}] Please only enter a number [1-5]"
        ;;
    esac
    done
}

check_servername() {
    # Chech ServerName File
    if [ -d "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/" ] ; then
        echo -e "[${plain}Info] Successfully found a valid folder for \"${SERVERNAME}\"."
        cd /etc/openvpn/certs/${SERVERNAME}/easy-rsa/
        if [ -f "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/SERVERNAME" ] ; then
            SERVERNAMEBAK=$(cat "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/SERVERNAME")
            if [ "${SERVERNAMEBAK}" == "${SERVERNAME}" ] ; then
                echo -e "[${green}Check${plain}] Verify successful ServerName."
            else
                echo -e "[${red}Error${plain}] Verification failed ServerName."
                exit 1
            fi
        fi
    else
        echo -e "[${red}Error${plain}] Failed to find valid folder for \"${SERVERNAME}\"."
        continue
    fi
}

create_ca_cert() {
    if [ -d "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/" ] ; then
        echo -e "[${plain}Info] Successfully found a valid folder for \"${SERVERNAME}\"."
        cd /etc/openvpn/certs/${SERVERNAME}/easy-rsa/
    else
        cp -r /etc/openvpn/certs/example /etc/openvpn/certs/${SERVERNAME} && echo -e "[${green}OK${plain}] Directory created successfully \"${SERVERNAME}\"" &&  cd /etc/openvpn/certs/${SERVERNAME}/easy-rsa/
    fi
    if [ -f "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/ca.key" ] ; then 
        echo -e "[${plain}Info] CA certificate \"${SERVERNAME}\" already exists."
        echo -e "[${yellow}Warning${plain}] Do you need to recreate and overwrite? (yes/no)" && read caselected
        [ -z "${caselected}" ] && caselected="no"
        if [ "${caselected}" == "yes" ] ; then
            if [ -f "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/dh.pem" ] ; then
                echo -e "[${yellow}Warning${plain}] Do you need to keep the dh.pem? (yes/no)" && read cadhselected
                [ -z "${cadhselected}" ] && cadhselected="no"
                if [ "${cadhselected}" == "yes" ] ; then mv /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/dh.pem /etc/openvpn/certs/${SERVERNAME}/easy-rsa/dh.pem ; else echo -e "[${yellow}Warning${plain}] User chooses not to keep the dh.pem." ; fi
            fi
            rm -rf /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/ && echo -e "[${yellow}Warning${plain}] Old CA certificate \"${SERVERNAME}\" has been successfully deleted."
            if [ -f "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/dh.pem" ] ; then mv /etc/openvpn/certs/${SERVERNAME}/easy-rsa/dh.pem /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/dh.pem ; fi
            create_ca_cert_files
        else
            echo -e "[${yellow}Warning${plain}] User chooses not to change the CA certificate."
        fi
    else 
        create_ca_cert_files
    fi
}

create_ca_cert_files () {
    echo -e "[${green}OK${plain}] Start creating \"${SERVERNAME}\" CA certificate."
    echo "$SERVERNAME" > /etc/openvpn/certs/${SERVERNAME}/easy-rsa/SERVERNAME
    ./easyrsa init-pki && echo -e "[${green}OK${plain}] Initialization successful."
    ./easyrsa --batch build-ca nopass && echo -e "[${green}OK${plain}] Creating ${SERVERNAME} CA certificate."
    echo -e "[${green}OK${plain}] Successfully created \"${SERVERNAME}\" CA certificate."
}

create_server_cert() {
    check_servername
    if [ -f "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/${SERVERNAME}.key" ] ; then
        echo -e "[${plain}Info] Server certificate \"${SERVERNAME}\" already exists."
        echo -e "[${yellow}Warning${plain}] Do you need to recreate and overwrite? (yes/no)" && read scertselected
        [ -z "${scertselected}" ] && scertselected="no"
        if [ "${scertselected}" == "yes" ] ; then
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/issued/${SERVERNAME}.crt
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/${SERVERNAME}.key
            mv /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/ca.key /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/ca.key.bak
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/certs_by_serial/*
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/issued/*.crt
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/*.key
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/reqs/*
            mv /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/ca.key.bak /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/ca.key
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/index*
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/serial*
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/${SERVERNAME}.tlsauth
            touch /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/index.txt
            echo "01" > /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/serial
            echo -e "[${yellow}Warning${plain}] Old Server certificate \"${SERVERNAME}\" has been successfully deleted."
            create_server_cert_files
            sign_server_cert_files
        else
            echo -e "[${yellow}Warning${plain}] User chooses not to change the server certificate."
        fi
    else
        create_server_cert_files
        sign_server_cert_files
    fi
}

create_server_cert_files() {
    echo -e "[${green}OK${plain}] Start creating \"${SERVERNAME}\" server certificate."
    ./easyrsa --batch --req-cn=${SERVERNAME} gen-req ${SERVERNAME} nopass && echo -e "[${green}OK${plain}] Creating \"${SERVERNAME}\" server certificate."
    echo -e "[${green}OK${plain}] Successfully creating \"${SERVERNAME}\" server certificate."
}

sign_server_cert_files() {
    echo -e "[${green}OK${plain}] Start Sign \"${SERVERNAME}\" server certificate."
    echo "yes" | ./easyrsa sign-req server ${SERVERNAME} && echo -e "[${green}OK${plain}] Sign \"${SERVERNAME}\" server certificate."
    openssl verify -CAfile /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/ca.crt /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/issued/${SERVERNAME}.crt > /dev/null 2>&1 && SIGNSERVERCERTSTATUS=1 || SIGNSERVERCERTSTATUS=0
    if [ ${SIGNSERVERCERTSTATUS} = 1 ] ; then echo -e "[${green}OK${plain}] Successfully sign \"${SERVERNAME}\" server certificate." ; fi
    if [ ${SIGNSERVERCERTSTATUS} = 0 ] ; then echo -e "[${red}Error${plain}] Failed to sign \"${SERVERNAME}\" server certificate." && continue ; fi
}

create_server_dh_parameters () {
    check_servername
    if [ -f "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/dh.pem" ] ; then
        echo -e "[${plain}Info] DH parameters file \"${SERVERNAME}\" already exists."
        echo -e "[${yellow}Warning${plain}] Do you need to recreate and overwrite? (yes/no)" && read dhselected
        [ -z "${dhselected}" ] && dhselected="no"
        if [ "${dhselected}" == "yes" ] ; then
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/dh.pem && echo -e "[${yellow}Warning${plain}] Old DH parameters file has been successfully deleted."
            create_server_tls_authentication_files
            create_server_dh_parameters_files
        else
            echo -e "[${yellow}Warning${plain}] User chooses not to change the DH parameters file."
        fi
    else
        create_server_tls_authentication_files
        create_server_dh_parameters_files
    fi
}

create_server_dh_parameters_files() {
    echo -e "[${green}OK${plain}] Start creating a DH parameters file."
    ./easyrsa gen-dh && echo -e "[${green}OK${plain}] Creating \"${SERVERNAME}\" DH parameters file."
    if [ -f "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/dh.pem" ] ; then echo -e "[${green}OK${plain}] Successfully created \"${SERVERNAME}\" DH parameters file." ; fi
}

create_server_tls_authentication_files() {
    echo -e "[${green}OK${plain}] Start creating a TLS Authentication file for \"${SERVERNAME}\"."
    openvpn --genkey --secret /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/${SERVERNAME}.tlsauth && echo -e "[${green}OK${plain}] Creating \"${SERVERNAME}\" a TLS Authentication file for \"${SERVERNAME}\"."
    if [ -f "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/${SERVERNAME}.tlsauth" ] ; then echo -e "[${green}OK${plain}] Successfully created a TLS Authentication file for \"${SERVERNAME}\"." ; fi
}

copy_server_cert_files() {
    echo -e "[${green}OK${plain}] Stary copy server certificate file for \"${SERVERNAME}\"."
    mkdir -p /etc/openvpn/server/${SERVERNAME}/
    /usr/bin/cp /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/ca.crt /etc/openvpn/server/${SERVERNAME}/ca.crt
    /usr/bin/cp /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/issued/${SERVERNAME}.crt /etc/openvpn/server/${SERVERNAME}/server.crt
    /usr/bin/cp /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/${SERVERNAME}.key /etc/openvpn/server/${SERVERNAME}/server.key
    /usr/bin/cp /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/dh.pem /etc/openvpn/server/${SERVERNAME}/dh2048.pem
    /usr/bin/cp /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/${SERVERNAME}.tlsauth /etc/openvpn/server/${SERVERNAME}/server.tlsauth
    mkdir /etc/openvpn/server/${SERVERNAME}/ipp
    mkdir /etc/openvpn/server/${SERVERNAME}/clients
    mkdir /etc/openvpn/server/${SERVERNAME}/clients/tcp
    mkdir /etc/openvpn/server/${SERVERNAME}/clients/udp
    echo "" > /etc/openvpn/server/${SERVERNAME}/ipp/tcp.txt
    echo "" > /etc/openvpn/server/${SERVERNAME}/ipp/udp.txt
    chown -R :openvpn /etc/openvpn/server/${SERVERNAME}/
    chmod 755 /etc/openvpn/server/${SERVERNAME}/
    echo -e "[${green}OK${plain}] Successfully copy server certificate file for \"${SERVERNAME}\"."
}

create_client_cert() {
    check_servername
    if [ -f "/etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/${SERVERNAME}_${CLIENTNAME}.key" ] ; then
        echo -e "[${plain}Info] Client certificate \"${SERVERNAME}_${CLIENTNAME}\" already exists."
        echo -e "[${yellow}Warning${plain}] Do you need to recreate and overwrite? (yes/no)" && read ccertselected
        [ -z "${ccertselected}" ] && ccertselected="no"
        if [ "${ccertselected}" == "yes" ] ; then
            DELTEMPCLIENTFILENUM=$(sed -n "$[$(sed -n -e '/Serial Number/=' /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/issued/${SERVERNAME}_${CLIENTNAME}.crt)+1]p" /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/issued/${SERVERNAME}_${CLIENTNAME}.crt | sed s/[[:space:]]//g | sed s/://g | tr 'a-z' 'A-Z')
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/issued/${SERVERNAME}_${CLIENTNAME}.crt
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/${SERVERNAME}_${CLIENTNAME}.key
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/reqs/${SERVERNAME}_${CLIENTNAME}.req
            rm -f /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/certs_by_serial/DELCLIENTFILENUM.req
            sed -i "/${SERVERNAME}_${CLIENTNAME}/d" /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/index.txt
            echo -e "[${yellow}Warning${plain}] Old Client certificate \"${SERVERNAME}_${CLIENTNAME}\" has been successfully deleted."
            create_client_cert_files
            sign_client_cert_files
        else
            echo -e "[${yellow}Warning${plain}] User chooses not to change the client certificate."
        fi
    else
        create_client_cert_files
        sign_client_cert_files
    fi
}

create_client_cert_files() {
    echo -e "[${green}OK${plain}] Start creating \"${SERVERNAME}_${CLIENTNAME}\" client certificate."
    ./easyrsa --batch --req-cn=${SERVERNAME}_${CLIENTNAME} gen-req ${SERVERNAME}_${CLIENTNAME} nopass && echo -e "[${green}OK${plain}] Creating \"${SERVERNAME}_${CLIENTNAME}\" client certificate."
    echo -e "[${green}OK${plain}] Successfully creating \"${SERVERNAME}_${CLIENTNAME}\" client certificate."
}

sign_client_cert_files() {
    echo -e "[${green}OK${plain}] Start Sign \"${SERVERNAME}_${CLIENTNAME}\" client certificate."
    echo "yes" | ./easyrsa sign-req client ${SERVERNAME}_${CLIENTNAME} && echo -e "[${green}OK${plain}] Sign \"${SERVERNAME}\" client certificate."
    openssl verify -CAfile /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/ca.crt /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/issued/${SERVERNAME}_${CLIENTNAME}.crt > /dev/null 2>&1 && SIGNCLIENTCERTSTATUS=1 || SIGNCLIENTCERTSTATUS=0
    if [ ${SIGNCLIENTCERTSTATUS} = 1 ] ; then echo -e "[${green}OK${plain}] Successfully sign \"${SERVERNAME}_${CLIENTNAME}\" client certificate." ; fi
    if [ ${SIGNCLIENTCERTSTATUS} = 0 ] ; then echo -e "[${red}Error${plain}] Failed to sign \"${SERVERNAME}_${CLIENTNAME}\" client certificate." && continue ; fi
}

copy_client_cert_files() {
    echo -e "[${green}OK${plain}] Stary copy client certificate file for \"${SERVERNAME}_${CLIENTNAME}\"."
    mkdir -p /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}
    /usr/bin/cp /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/ca.crt /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/ca.crt
    /usr/bin/cp /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/issued/${SERVERNAME}_${CLIENTNAME}.crt /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/client.crt
    /usr/bin/cp /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/private/${SERVERNAME}_${CLIENTNAME}.key /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/client.key
    /usr/bin/cp /etc/openvpn/certs/${SERVERNAME}/easy-rsa/pki/${SERVERNAME}.tlsauth /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/client.tlsauth
    chown -R :openvpn /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/
    chmod 755 /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/
    echo -e "[${green}OK${plain}] Successfully copy client certificate file for \"${SERVERNAME}_${CLIENTNAME}\"."
}

build_server_config_files() {
    echo -e "[${green}OK${plain}] Start build server config for \"${SERVERNAME}-${SERVERPROTOCOL}\"."
    if [ -f /etc/openvpn/server/${SERVERNAME}-${SERVERPROTOCOL}.conf ] ; then rm -f /etc/openvpn/server/${SERVERNAME}.conf ; fi
    if [ ${SERVERPROTOCOL} == "tcp" ] ; then
        SERVERPROTO=tcp-server
        export_server_config_files
        sed -i "/explicit-exit-notify/d" /etc/openvpn/server/${SERVERNAME}-${SERVERPROTOCOL}.conf
    elif [ ${SERVERPROTOCOL} == "udp" ] ; then
        SERVERPROTO=udp
        export_server_config_files
    else
        echo -e "[${red}Error${plain}] The protocol name is wrong.\"." && continue 
    fi
    echo -e "[${green}OK${plain}] Successfully build server config for \"${SERVERNAME}-${SERVERPROTOCOL}\"."
}

export_server_config_files() {
cat > /etc/openvpn/server/${SERVERNAME}-${SERVERPROTOCOL}.conf <<-EOF
local ${SERVERIPADDRESS}
port ${SERVERPORT}
proto ${SERVERPROTO}
mode server
dev tun${DEVICENUM}
float
tls-server
ca /etc/openvpn/server/${SERVERNAME}/ca.crt
cert /etc/openvpn/server/${SERVERNAME}/server.crt
key /etc/openvpn/server/${SERVERNAME}/server.key
dh /etc/openvpn/server/${SERVERNAME}/dh2048.pem
tls-crypt /etc/openvpn/server/${SERVERNAME}/server.tlsauth
topology subnet
push "topology subnet"
server ${SERVERNETWORK} ${SERVERNETMASK}
ifconfig-pool-persist /etc/openvpn/server/${SERVERNAME}/ipp/${SERVERPROTOCOL}.txt 10
client-config-dir /etc/openvpn/server/${SERVERNAME}/clients/${SERVERPROTOCOL}/
;push "dhcp-option DNS 8.8.8.8"
;push "redirect-gateway def1 bypass-dhcp"
push "route 10.0.0.0 255.255.0.0"
;mssfix 1300
;client-to-client
keepalive 10 120
max-clients 100
compress lz4-v2
push "compress lz4-v2"
cipher AES-256-GCM
auth SHA256
user nobody
group nobody
persist-key
persist-tun
explicit-exit-notify 1
status /var/log/openvpn/${SERVERNAME}-${SERVERPROTOCOL}.status
verb 3
log-append /var/log/openvpn/${SERVERNAME}-${SERVERPROTOCOL}.log
mute 20
EOF
}

build_client_config_files(){
    echo -e "[${green}OK${plain}] Start build server config for \"${SERVERNAME}-${SERVERPROTOCOL}\"."
    if [ -f /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.conf ] ; then rm -f /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.conf ; fi
    if [ -f /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn ] ; then rm -f /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.conf ; fi
    export_client_config_files
    /usr/bin/cp /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.conf /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    sed -i "/\/${SERVERNAME}_${CLIENTNAME}\//d" /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    echo "<ca>" >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    cat /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/ca.crt >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    echo "</ca>" >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.conf >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    echo "<cert>" >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    cat /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/client.crt >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    echo "</cert>" >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    echo "<key>" >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    cat /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/client.key >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    echo "</key>" >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    echo "<tls-crypt>" >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    cat /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/client.tlsauth >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    echo "</tls-crypt>" >> /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.ovpn
    echo -e "[${green}OK${plain}] Successfully build client config for \"${SERVERNAME}_${CLIENTNAME}\"."
}

export_client_config_files() {
cat > /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.conf <<-EOF
client
pull
remote ${SERVERIPADDRESS} ${SERVERPORT} udp4
remote ${SERVERIPADDRESS} ${SERVERPORT} tcp4-client
dev tun${DEVICENUM}
float
tls-client
ca /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/ca.crt
cert /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/client.crt
key /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/client.key
tls-crypt /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/client.tlsauth
resolv-retry infinite
nobind
keepalive 10 120
topology subnet
compress lz4-v2
cipher AES-256-GCM
auth SHA256
user nobody
group nobody
persist-key
persist-tun
explicit-exit-notify 1
status /var/log/openvpn/${SERVERNAME}_${CLIENTNAME}.status
verb 3
log-append /var/log/openvpn/${SERVERNAME}_${CLIENTNAME}.log
mute 20
EOF
}

look_server_config() {
    cat /etc/openvpn/server/${SERVERNAME}*.conf
}

look_client_config() {
    cat /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}.conf
}

main () {
    clear
    while true
    do
        config_select
        if [ ${selected} = 1 ]; then
            read -p "OpenVPN ServerName: (oneserver)" SERVERNAME
            [ -z "${SERVERNAME}" ] && SERVERNAME="oneserver"
            create_ca_cert
        fi
        if [ ${selected} = 2 ]; then
            read -p "OpenVPN ServerName: (oneserver)" SERVERNAME
            [ -z "${SERVERNAME}" ] && SERVERNAME="oneserver"
            create_server_cert
            create_server_dh_parameters
            if [ -d "/etc/openvpn/server/${SERVERNAME}/" ] ; then rm -rf /etc/openvpn/server/${SERVERNAME}/ ; fi
            copy_server_cert_files
        fi
        if [ ${selected} = 3 ]; then
            read -p "OpenVPN ServerName: (oneserver)" SERVERNAME
            [ -z "${SERVERNAME}" ] && SERVERNAME="oneserver"
            read -p "OpenVPN ClientName: (oneclient)" CLIENTNAME
            [ -z "${CLIENTNAME}" ] && CLIENTNAME="oneclient"
            create_client_cert
            if [ -d "/etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/" ] ; then rm -rf /etc/openvpn/client/${SERVERNAME}_${CLIENTNAME}/ ; fi
            copy_client_cert_files
        fi
        if [ ${selected} = 4 ]; then
            read -p "OpenVPN ServerName: (oneserver)" SERVERNAME
            [ -z "${SERVERNAME}" ] && SERVERNAME="oneserver"
            read -p "OpenVPN ServerIPAddress: (127.0.0.1)" SERVERIPADDRESS
            [ -z "${SERVERIPADDRESS}" ] && SERVERIPADDRESS="127.0.0.1"
            read -p "OpenVPN ServerPort: (1194)" SERVERPORT
            [ -z "${SERVERPORT}" ] && SERVERPORT="1194"
            read -p "OpenVPN DeviceNumber: (0)" DEVICENUM
            [ -z "${DEVICENUM}" ] && DEVICENUM="0"
            read -p "OpenVPN ServerProtocol: (udp/tcp)" SERVERPROTOCOL
            [ -z "${SERVERPROTOCOL}" ] && SERVERPROTOCOL="udp"
            read -p "OpenVPN ServerNetwork: (10.0.0.0)" SERVERNETWORK
            [ -z "${SERVERNETWORK}" ] && SERVERNETWORK="10.0.0.0"
            read -p "OpenVPN ServerNetmask: (255.255.255.0)" SERVERNETMASK
            [ -z "${SERVERNETMASK}" ] && SERVERNETMASK="255.255.255.0"
            build_server_config_files
        fi
        if [ ${selected} = 5 ]; then
            read -p "OpenVPN ServerName: (oneserver)" SERVERNAME
            [ -z "${SERVERNAME}" ] && SERVERNAME="oneserver"
            read -p "OpenVPN ClientName: (oneclient)" CLIENTNAME
            [ -z "${CLIENTNAME}" ] && CLIENTNAME="oneclient"
            read -p "OpenVPN ServerIPAddress: (127.0.0.1)" SERVERIPADDRESS
            [ -z "${SERVERIPADDRESS}" ] && SERVERIPADDRESS="127.0.0.1"
            read -p "OpenVPN ServerPort: (1194)" SERVERPORT
            [ -z "${SERVERPORT}" ] && SERVERPORT="1194"
            read -p "OpenVPN DeviceNumber: (0)" DEVICENUM
            [ -z "${DEVICENUM}" ] && DEVICENUM="0"
            build_client_config_files
        fi
        if [ ${selected} = 6 ]; then
            read -p "OpenVPN ServerName: (oneserver)" SERVERNAME
            [ -z "${SERVERNAME}" ] && SERVERNAME="oneserver"
            look_server_config
        fi
        if [ ${selected} = 7 ]; then
            read -p "OpenVPN ServerName: (oneserver)" SERVERNAME
            [ -z "${SERVERNAME}" ] && SERVERNAME="oneserver"
            read -p "OpenVPN ClientName: (oneclient)" CLIENTNAME
            [ -z "${CLIENTNAME}" ] && CLIENTNAME="oneclient"
            look_client_config
        fi
        if [ ${selected} = 8 ]; then
            echo "The script will exit"
            echo
            exit 0
        fi
    done
}

main

 


7、历史修订

 


8、相关链接

Wikipedia-OpenVPN [ 链接 ]

OpenVPN Community [ 链接 ]

OpenVPN Arch Wiki (English) [ 链接 ]

OpenVPN Arch Wiki (Chinese) [ 链接 ]

Openvpn24ManPage [ 链接 ]

 

 

 

点赞