openvpn部署

版权声明:哈哈哈,如有转载请标明出处哟~~~ https://blog.csdn.net/qq_33633013/article/details/81912771
(1) yum upgrade #升级
(2) yum install epel-release -y #安装epel库
(3) yum install easy-rsa openssh-server lzo openssl openssl-devel openvpn NetworkManager-openvpn openvpn-auth-ldap -y #安装必要软件
(4) cp /usr/share/doc/openvpn-*/sample/sample-config-files/server.conf /etc/openvpn #拷贝server.conf到/etc/openvpn目录下
(5) cat /etc/openvpn/server.conf #编辑服务端文件
local 192.168.3.39     #指定监听的本机IP(因为有些计算机具备多个IP地址),该命令是可选的,默认监听所有IP地址。
port 1194             #指定监听的本机端口号
proto udp             #指定采用的传输协议,可以选择tcp或udp
dev tun               #指定创建的通信隧道类型,可选tun或tap
ca ca.crt             #指定CA证书的文件路径
cert server.crt       #指定服务器端的证书文件路径
key server.key    #指定服务器端的私钥文件路径
dh dh2048.pem         #指定迪菲赫尔曼参数的文件路径
server 10.18.0.0 255.255.255.0   #指定虚拟局域网占用的IP地址段和子网掩码,此处配置的服务器自身占用该网段第一个ip。
ifconfig-pool-persist ipp.txt   #服务器自动给客户端分配IP后,客户端下次连接时,仍然采用上次的IP地址(第一次分配的IP保存在ipp.txt中,下一次分配其中保存的IP)。
tls-auth ta.key 0     #开启TLS-auth,使用ta.key防御攻击。服务器端的第二个参数值为0,客户端的为1。
keepalive 10 120      #每10秒ping一次,连接超时时间设为120秒。
comp-lzo              #开启VPN连接压缩,如果服务器端开启,客户端也必须开启
client-to-client      #允许客户端与客户端相连接,默认情况下客户端只能与服务器相连接
persist-key
persist-tun           #持久化选项可以尽量避免访问在重启时由于用户权限降低而无法访问的某些资源。
status openvpn-status.log    #指定记录OpenVPN状态的日志文件路径
log openvpn.log              #日志文件
verb 3                #指定日志文件的记录详细级别,可选0-9,等级越高日志内容越详细
(6)配置证书文件
mkdir -p  /etc/openvpn/easy-rsa/keys
cp  -a  /usr/share/easy-rsa/2.0/*  /etc/openvpn/easy-rsa/
cat   /etc/openvpn/easy-rsa/vars(若该处未编辑,则生成证书的时候输入)
export KEY_COUNTRY="CN"
export KEY_PROVINCE="ZJ"
export KEY_CITY="HZ"
export KEY_ORG="mytijian"    #定义所在的组织
export KEY_EMAIL="[email protected]"
export KEY_OU="mytijian"    #定义所在的单位
export KEY_NAME="server"    #定义openvpn服务器的名称
export KEY_CN="mytijian"    #这行配置,需要将前边的“#”号注释去掉。
(7)制作证书
cp  /etc/openvpn/easy-rsa/openssl-1.0.0.cnf  /etc/openvpn/easy-rsa/openssl.cnf
cd /etc/openvpn/easy-rsa    #切换工作目录
source ./vars    #让配置文件生效
./clean-all
./build-ca    #之前已配置好相关参数,故执行命令后,按回车键,一直到结束即可。
(8)创建服务端证书
./build-key-server server  #创建通用名(common name)为"server"的证书文件
cd  /etc/openvpn/easy-rsa/keys
cp  dh2048.pem  ca.crt  server.crt  server.key  ta.key   /etc/openvpn   #复制相关文件到openvpn文件夹
cd  /etc/openvpn/easy-rsa
./build-key client  #创建一个通用名(common name)为 client的客户端证书

systemctl -f enable [email protected]或到相应openvpn目录执行 openvpn server.conf &      #设置启动文件
systemctl start [email protected]          #启动openvpn的命令
至此服务端配置完毕
客户端配置
(1)将ca.crt,client.crt,client.key,ta.key复制到客户端/etc/openvpn/下(注意与server.conf相对应)
cat client.conf
client
dev tun
proto tcp
remote 服务器IP 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert client.crt
key client.key
remote-cert-tls server
tls-auth ta.key 1
cipher AES-256-CBC
comp-lzo
verb 3
mute 20
(2)开启openvpn服务

在/etc/openvpn/下执行

openvpn client.conf &
openvpn工作原理
在Linux2.4版本以上,操作系统支持一个名为tun的设备,tun设备的驱动程序中包含两个部分,一部分是字符设备驱动,一部分是网卡驱动。openvpn安装后在机器上生成一个虚拟网卡(如果是windows,会安装一个TAP-WINDOW).
作为系统的虚拟网卡,也具有和物理网卡相同的特点:能够配置IP地址和路由。对虚拟网卡的使用是OpenVPN实现其SSL VPN功能的关键。
客户端与服务端安全连接的建立
OpenVPN的服务器和客户端支持tcp和udp两种连接方式,只需在服务端和客户端预先定义好使用的连接方式(tcp或udp)和端口号,客户端和服务端在这个连接的基础上进行SSL握手。连接过程包括SSL的握手以及虚拟网络上的管理信息,OpenVPN将虚拟网上的网段、地址、路由发送给客户端。连接成功后,客户端和服务端建立起SSL安全连接,客户端和服务端的数据都流入虚拟网卡做SSL的处理,再在tcp或udp的连接上从物理网卡发送出去。
OpenVPN提供tun和tap两种工作模式。在tun模式下,从虚拟网卡上收到的是不含物理帧头IP数据包,SSL处理模块对IP包进行SSL封装;在tap模式下,从虚拟网卡上收到的是包含物理帧头的数据包,SSL处理模块对整个物理帧进行SSL封装。Tap模式称为网桥模式,整个虚拟的网络就像网桥方式连接的物理网络。这种模式可以传输以太网帧、IPX、NETBIOS等数据包,应用范围更广。
本次实现了服务器上openvpn ip自动分配,取代了以往通过excel表格记录分配ip的方式。
(1)服务器中建立openvpn ip库,根据客户端其所在的省份编码定义网段,如浙江(17)。
(2)根据省份创建记录文件,记录已生成openvpn ip的客户端的id和openvpn ip。
(3)脚本代码根据已生成的客户端个数,相应去取openvpn ip库中的ip并输出到/etc/openvpn/ccd/客户端id文件中。
(4)打包服务端证书,客户端证书和秘钥。
(5)为了防止省份以及客户端id参数的输入错误,加入了省份以及id的校验,若不通过或检测输入错误则不运行代码;增加了已生成的openvpn客户端再次生成覆盖的用户交互,询问是否再次生成。
优点:
(1)根据已生成的客户端个数分配ip,不需要过多的人为加入,只需通过代码判断。
(2)原本的excel记录方式是事先将ip排序,等待各省份医院的申请,依次对应医院id与openvpn ip,这样会导致某网段的ip的浪费与闲置。
(3)生成过程更加便捷快速并且准确。
缺点:
(1)若撤销某个openvpn证书,未在省份记录中删除,这样会导致统计不准确。
(2)省份中如江苏、浙江等存在代理,其原本所配置的ip较大,则再分配这些省份的时候需要特殊注意,先将代理信息剪切,再生成医院ip,之后再将代理信息复制回去,有些繁琐。
(3)一旦有一些统计不准确,则后期难以维护。

目录展示

/etc/openvpn

这里写图片描述

/opt/openvpn2017

这里写图片描述

说明:
01_createCrt.sh 生成openvpn客户端。执行方法:./01_createCrt.sh hospital_id 省份拼音(如:zhejiang)或 ./01_createCrt.sh 用户名 company
02_revokeCrt,sh 撤销openvpn客户端。执行方法:./02_revokeCrt.sh hospital_id(或用户名)

流程展示

这里写图片描述

问题总结


(1)测试环境expect免密交互失败
下载expect指令,使用expect,spawn指令实现免密交互
(2)重复生成openvpn客户端时,报验证错误
原因为代码脚本中未对原有客户端在index.txt(openvpn数据库)中做处理,导致重复
解决方法为:根据hospital_id或用户名匹配数据库,删除数据库中原本生成的客户端

删除原本生成

sed -i “${index_line_num}d” ${server_database_path}/index.txt
rm -f ${client_database_path}/private/${hospital_id}.key ${server_database_path}/reqs/${hospital_id}.req

脚本展示

01_createCrt.sh(用于生成openvpn客户端)
#!/bin/bash
#according to address_id,insert hospital_id to provinces

path=`pwd`
#医院id
hospital_id=$1
ccd_path="/etc/openvpn/"
#由医院id查出省份
province_name=$2 #省份
ip_num=64 #一个段ip个数
#判断是否为数字,0为医院
isNumber=`./scriptDir/isNumber.sh $1`
#取行数,代表相应id
line_num=`cat ${path}/province/${province_name} |wc -l`
provinceRight=`./scriptDir/provinceRule.sh $province_name`
proxyRight=`./scriptDir/proxyRule.sh $hospital_id`
server_database_path="${path}/server/easyrsa3/pki"
client_database_path="${path}/client/easyrsa3/pki"


#判断参数个数
if [ $# != 2 ];then
    echo "please enter hosptal_id and (province_name or company)"
    exit 1
fi

#创建文件
if [ ! -d ${path}/tmp/ ];then
        mkdir ${path}/tmp/
fi

if [ ! -f ${path}/tmp/tmp.txt ];then
        touch ${path}/tmp/tmp.txt
fi

if [ ! -d ${path}/province/ ];then
        mkdir ${path}/province/
fi

#参数规范
if [ $isNumber -eq 0 ];then
        #hospital
        if [ $provinceRight -eq 0 ];then
                #后面为省份
                echo ""
        else
                echo "error" && exit 1
        fi
else
        #hospital_id不为数字
        if [ $provinceRight -eq 0 ];then
                #proxy
                if [ $proxyRight -eq 0 ];then
                        echo ""
                else
                        echo "error" && exit 1
                fi
        else
                if [ "$province_name" == "company" ] && [ "$proxyRight" != "0" ];then
                        echo ""
                else
                        echo "error" && exit 1
                fi
        fi
fi

#省份编码
getAddressCode=`./scriptDir/getAddressCode.sh ${province_name}`

#判断是否生成过该文件
if [ -f ${ccd_path}ccd/${hospital_id} ];then
    echo "File exist!!!Are you want to continue?(yes or no)"
    read answer
    if [ $answer == "yes" ];then
        index_line_num=`cat -n ${server_database_path}/index.txt |grep -w ${hospital_id}|awk -F ' ' '{print $1}'`
        #删除原本生成
        sed -i "${index_line_num}d" ${server_database_path}/index.txt       
        rm -f ${client_database_path}/private/${hospital_id}.key ${server_database_path}/reqs/${hospital_id}.req

    else
        echo done && exit 1
    fi  
fi
        #生成ccd文件
        touch ${ccd_path}/ccd/${hospital_id}
        if [ $line_num -lt 64 ];then
            #小于64
            let line_num=line_num+1
            openvpn_ip_x=`sed -n "${line_num},1p" ${path}/openvpnip_manifest`
        else
            #取出对应行数的ip
            let line_num=line_num-63
            openvpn_ip_x=`sed -n "${line_num},1p" ${path}/openvpnip_manifest `
        fi

        #插入临时文件,每次覆盖取
        echo "$openvpn_ip_x" > ${path}/tmp/tmp.txt
        #赋值ip
        sed -i "1s/x/${getAddressCode}/" ${path}/tmp/tmp.txt
        openvpnip=`cat ${path}/tmp/tmp.txt | awk -F ' ' '{print $1}'`

#JUDGE PARAMETERS

        #截取ip最后一位
        openvpnip_suffix=${openvpnip##*.}
        #截取ip前缀
        openvpnip_prefix=${openvpnip%.*}
        gateway_suffix=`expr ${openvpnip_suffix} + 1`
        gateway=${openvpnip_prefix}.${gateway_suffix}       
        #echo "ifconfig-push $openvpnip $gateway" > ${ccd_path}/ccd/${hospital_id}
        isHospitalidExist=`cat province/${province_name} | awk -F ' ' '{print $1}' | grep -w ${hospital_id}`#取第一列字段(id)进行精确查找
        if [ "X$isHospitalidExist" == "X" ];then
            echo "$hospital_id $openvpnip" >> ${path}/province/${province_name}
            echo "ifconfig-push $openvpnip $gateway" > ${ccd_path}/ccd/${hospital_id}
        fi  
        #调用脚本
        if [ $isNumber -eq 0 ];then
            ./scriptDir/createCrt.sh $hospital_id hospital 
        else
            if [ "$province_name" == "company" ];then
                ./scriptDir/createCrt.sh $hospital_id company
                #sudo rm -f ${ccd_path}/ccd/${hospital_id}
            else
                ./scriptDir/createCrt.sh $hospital_id proxy
            fi
        fi
createCrt.sh 根据01_createCrt.sh分配的id和ip生成证书并发送到spi上
#!/bin/bash
if [ $# != 2 ];then
    echo "Usage:$0 id type(hospital/company/proxy)"
    exit 1
fi
#set environment
currDir=`pwd`
clientEasyrsaDir=$currDir/client/easyrsa3
serverEasyrsaDir=$currDir/server/easyrsa3

#判断是否已经生成过配置
#if [ -f $serverEasyrsaDir/pki/reqs/${1}.req ];then
#    rm -f $serverEasyrsaDir/pki/reqs/${1}.req
#    echo "已生成"
#    exit 0
#fi

cd $clientEasyrsaDir
#easyrsa initializtion
if [ ! -d  pki ];then
./easyrsa init-pki
fi

#create client certificate
expect<<EOF
spawn ./easyrsa gen-req  $1 nopass
expect {
    "*$1]:*" {
      send "\r"
      exp_continue

    }

}
EOF
cd $serverEasyrsaDir
#import client certificate
./easyrsa import-req $clientEasyrsaDir/pki/reqs/${1}.req $1

expect<<EOF
spawn ./easyrsa sign client $1
expect {
    "*details:*" {
      send "yes\r"
      exp_continue
    }
    "*ca.key:*" {
      send "mytijian2017\r"
      exp_continue
    }
}
EOF

#打包客户端证书和配置
cd $currDir
if [ ! -d  ovpnclient/$1 ];then
    mkdir -p ovpnclient/$1
fi

cd ovpnclient/$1
\cp $serverEasyrsaDir/pki/ca.crt .
\cp $serverEasyrsaDir/pki/issued/${1}.crt .
\cp $clientEasyrsaDir/pki/private/${1}.key .
sed "s/hospital/$1/g" $currDir/client.ovpn > client.ovpn

zip ${1}_config.zip *

cd $currDir
if [ "X$2" != "Xcompany" ];then
    ./scriptDir/noLoading.sh $1
fi
cd ovpnclient/$1
rm -f ca.crt ${1}.* client.ovpn openvpn.log
getAddressCode.sh 根据输入的省份名称生成省份编码
#/bin/bash
province_name=$1

function getAddressCode(){
case $province_name in
    110 )
        x=1
        break;
    ;;

    120 )
        x=2
                break;
        ;;

    130 )
        x=3
                break;
        ;;

    140 | 141 )
        x=4
                break;
        ;;

    150 | 152 )
                x=57
                break;
        ;;

    210 | 211 )
                x=9
                break;
        ;;

    220 | 222 )
                x=8
                break;
        ;;

    230 | 231 )
                x=7
                break;
        ;;

    310 )
                x=16 
                break;
        ;;

    320 | 321 )
                x=18
                break;
        ;;

        330 | 331 )
                x=17
                break;
        ;;

    340 | 341 )
                x=19
                break;
        ;;

    360 | 361 )
                x=37
                break;
        ;;

    370 | 371 )
                x=6
                break;
        ;;

    410 | 411 )
                x=5
                break;
        ;;

    420 | 421 | 422 )
                x=20
                break;
        ;;

    430 | 431 )
                x=35
                break;
        ;;

    440 | 441 | 442 | 445 )
            x=32
                break;
        ;;

    450 | 451 )
        x=33
        break;
    ;;

    460 | 469 )
                x=34
                break;
        ;;

    500 )
                x=49
                break;
        ;;

    510 | 511 | 512 | 513 )
                x=48
                break;
        ;;

    530 | 532 | 533 )
                x=54
                break;
        ;;

    540 | 542 )
                x=53
                break;
        ;;

    610 | 611 )
                x=50
                break;
        ;;

    620 | 621 | 622 | 623 )
                x=56
                break;
        ;;

    630 | 632 )
                x=52
                break;
        ;;

    640 )
                x=55
                break;
        ;;

    650 | 652 | 653 | 654 | 659 )
                x=51
                break;
        ;;

    350 )
                x=36
                break;
        ;;

esac

echo $x
}

function getAddCode(){
#判断省份
case $province_name in
        beijing )
                x=1
                break;
        ;;

        tianjin )
                x=2
                break;
        ;;

        hebei )
                x=3
                break;
        ;;

        jin )
                x=4
                break;
        ;;

        henan )
                x=5
                break;
        ;;

        shandong )
                x=6
                break;
        ;;

        heilongjiang )
                x=7
                break;
        ;;

        jilin )
                x=8
                break;
        ;;

        liaoning )
                x=9 
                break;
        ;;

        shanghai )
                x=16
                break;
        ;;

        zhejiang )
                x=17
                break;
        ;;

        jiangsu )
                x=18
                break;
        ;;

        anhui )
                x=19
                break;
        ;;

        hubei )
                x=20
                break;
        ;;

        guangdong )
                x=32
                break;
        ;;

        guangxi )
                x=33
                break;
        ;;

        hainan )
                x=34
                break;
        ;;

        hunan )
                x=35
                break;
        ;;

        jiangxi )
                x=37;
                break;
        ;;

        fujian )
                x=36
                break;
        ;;

        sichuan )
                x=48
                break;
        ;;

        chongqing )
                x=49
                break;
        ;;

        shanxi )
                x=50
                break;
        ;;

        xinjiang )
                x=51
                break;
        ;;

        qinghai )
                x=52
                break;
        ;;

        xizang )
                x=53
                break;
        ;;

        yunnan )
                x=54
                break;
        ;;

        ningxia )
                x=55
                break;
        ;;

        gansu )
                x=56
                break;
        ;;

        neimenggu )
                x=57
                break;
        ;;
    company )
        x=60
        break;
    ;;
esac
    echo $x
}

getAddCode
判断省份记录文件中是否已生成该客户端
#!/bin/bash

province=$1

cat province/$province | while read provinceLine 
do
        province_id=`echo $provinceLine | awk -F ' ' '{print $1}'`
        echo $province_id
done
isNumber.sh 判断输入是否为数字
#!/bin/bash

num=$1
function isNumber(){
        #expr $1 "+" 10 &> /dev/null
        if [ -n "$(echo $num| sed -n "/^[0-9]\+$/p")" ];then
                number=0
        else
                number=1
        fi
        #if [ $? -eq 0 ];then
        #       number=0
        #else
  #             number=1
        #fi
        echo $number
}

isNumber
judgeParam 判断输入参数是否符合要求
#!/bin/bash

hospital_id=$1
province_name=$2
isNumber=`./isNumber.sh $hospital_id`
isParam=`./ProvinceRule.sh $province_name`
function judgeParam(){
        if [ $isNumber -eq 0 ];then
                if [ $isParam -eq 0 ];then
                        echo ""
                else
                        echo "when hospital_id is a number,province should be in list"
                fi
        else

        fi

}
judgeParam
noLoading.sh免密发送证书到spi上
#!/usr/bin/expect
set hospital_id [lrange $argv 0 0]
cd /opt/openvpn2017/ovpnclient/${hospital_id}
spawn scp /opt/openvpn2017/ovpnclient/${hospital_id}/${hospital_id}_config.zip wusheng@mediator1:/mytijian/agent/tools/ovpnconf/hospital/
expect {
        "password" {
                send "mytijian2015\r"
                interact
        }
}
provinceRule 判断省份是否符合规范
#!/bin/bash

province_name=$1
function provinceRule(){
        province=(beijing tianjin hebei shanxi henan-shandong heilongjiang jilin liaoning shanghai zhejiang jiangsu anhui hubei guangdong guangxi hainan hunan fujian jiangxi sichuan chongqing shanxi xinjiang qinghai xizang yunnan ningxia gansu neimenggu)

        if echo "${province[@]}" | grep -w "$province_name" &>/dev/null; then
                right=0
        else
                right=1
        fi
        echo $right
}

provinceRule
proxyRule 判断输入代理是否规范
#!/bin/bash

hospital_id=$1
function proxyRule(){
        proxy=(proxy1 proxy2 proxy3 proxy4 proxy5 proxy6 proxy7 proxy8 proxy9 proxy10)

        if echo "${proxy[@]}" | grep -w "$hospital_id" &>/dev/null; then
                cc=0
        else
                cc=1
        fi
        echo $cc
}
proxyRule
timingBackUp.sh 实时备份,并保持备份数量为5,避免文件冗余
#!/bin/bash
#定时备份vpn证书

currdir="/opt/openvpn2017" #/opt/openvpn
#定义备份文件夹名称
backupfilename=`date +%Y-%m-%d`
logfile="${backupfilename}"
MaxFileNum=10 #目录最大文件数 
DeleteFileNum=6 #达到最大目录数后删除数目

#ccd路径
ccd_path="/etc/openvpn/ccd/"
#备份路径
backup_path="${currdir}/backup"  #/opt/openvpn2017/backup
openvpn_path="${backup_path}/openvpn" #/opt/openvpn2017/backup/openvpn
crt_path="${backup_path}/crt"   ##/opt/openvpn2017/backup/crt

#创建备份文件路径
if [ ! -d ${backup_path}/${logfile} ];then
mkdir -p ${backup_path}/${logfile}/  #mkdir /opt/openvpn2017/backup/2017-12-18
fi

if [ ! -d ${backup_path}/${logfile}/crt ];then
        mkdir -p ${backup_path}/${logfile}/crt/ #mkdir /opt/openvpn2017/backup/2017-12-18/crt
fi

if [ ! -d ${backup_path}/${logfile}/openvpn ];then
        mkdir -p ${backup_path}/${logfile}/openvpn/ #mkdir /opt/openvpn2017/backup/2017-12-18/openvpn
fi



#执行备份操作
#cd ${ccd_path}
cd ${currdir}
zip -r openvpn.zip /etc/openvpn/ccd/* #打包openvpn ip
mv openvpn.zip ${backup_path}/${logfile}/openvpn/

if [ $? -eq 0 ];then
    #备份成功
    echo "${backupfilename}:ccd backup successfully"  >> ${backup_path}/${logfile}.log
else
    #失败
    echo "${backupfilename}:ccd backup failly,please try again!!!!"  >> ${backup_path}/${logfile}.log
fi

cd ${currdir}/ovpnclient/
zip -r crt.zip * #打包证书i
mv crt.zip ${backup_path}/${logfile}/crt/

if [ $? -eq 0 ];then
        #备份成功
        echo "${backupfilename}:crt backup successfully"  >> ${backup_path}/${logfile}.log
else
        #失败
        echo "${backupfilename}:crt backup failly,please try again!!!!"  >> ${backup_path}/${logfile}.log
fi


#删除早期证书与日志
cd ${currdir}/backup/
FileNumber=`ls -l | grep - | wc -l`
if [ $FileNumber -ge $MaxFileNum ];then
    ls -al | awk -F ' '  '{print $9}' |grep - | head -n $DeleteFileNum | xargs rm -rf 
fi

猜你喜欢

转载自blog.csdn.net/qq_33633013/article/details/81912771