OpenVPN

2020-02-19 0 条评论 2.14k 次阅读 3 人点赞

分享这个文章的初衷是昨天一个朋友问到了关于OpenVPN连接后无法访问其内网的其他主机.记得之前自己把整个流程跑通了,但是却没能回答上来朋友的问题,正值新冠肺炎期间,因此又重新过了一遍,并整理了这篇博客,正愁着解决公司远程办公的小伙伴也可以仔细研究一下,看能不能解决你的问题

VPN简介

众所周知,出于一些未知的问题,国外的一些网站我们是无法正常访问的,为了能继续访问,于是VPN这个词出现在了很多人的眼前,提到VPN很多人也与翻墙或者看小电影联系起来了,最近年对于VPN的封锁力度还是相当大的,就连发个微博,博客之类的这些关键字也会被限制

VPN(Virtual Private Network)中文释义虚拟专用网络,是一种常用于连接中、大型企业或团体与团体间的私人网络的通讯方法。它利用隧道协议(Tunneling Protocol)来达到保密、发送端认证、消息准确性消息依然有被窃取的危险。

我想说的是VPN并不是什么不好的技术,只是很多人利用这项技术做了一些不该做的事情

常用的实现方案有OpenVPN,IPSec等,这篇文章介绍的是OpenVPN

由于OpenVPN的官网我们访问不了可以访问其GitHub地址下载源代码,客户端可以从这里下载,这是我直接从官网下载的,没有做过任何操作,可以放心使用,不放心可以到其官网下载

原理解读

  1. OpenVPN的核心功能

    OpenVPN的核心功能就是启用一个网络,客户端通过连接服务端从而加入到这个网络中,进而通过这个网络与服务端的网络进行通信,从而可以在连接成功之后从客户端访问服务端内网的所有主机

  2. OpenVPN的原理

    这个说实话我也是了解一个大概,其主要是通过tcp或者upd协议在应用层传输VPN网段的网络层或者链路层的数据包,数据包到达后通过一个虚拟的网卡去处理,从而达到将互联网上不同地方的两个内网打造成一个内网而进行通信,而且这些VPN的数据包在传输的时候都是加密的

  3. VPN网络的原理

VPN.png

首先,客户端连接到VPN,此时客户端与服务端之间可以使用VPN网段进行正常通信,我们可以在配置的时候,添加客户端连接成功后服务端会自动将内网网段的路由push给客户端的配置项,因此客户端访问服务端服务器的地址,服务端是可以正常收到请求的,但是他没有办法将数据包传回,因此我们需要给服务端开启核心转发的功能,使其能够正常与客户端通过其内网地址通信

这时,如果用客户端去访问服务端内网的其他主机的时候,此时也可以正常将数据包发往对应的主机,但是该主机在回包的时候是找不到VPN所在的网段的路由,因此他无法将数据包返回到客户端,因此我们还需要做一个策略,那就是在VPN服务器上做一条iptables的SNAT的规则

使其接收到的源地址为VPN网段的地址目标地址不是VPN网段的地址都将其数据包中的源地址替换为为VPN内网的地址,然后将数据包转发出去,然后对应的内网主机收到该数据包,此时该数据包的源地址就是VPN的内网地址,这个主机在发包时时也就将这个数据包发给了VPN服务器,VPN服务器收到该包后,会将目标地址替换为本身的数据包然后将这个数据包转发出去,这种SNAT的好处是只需要配置VPN服务器即可,其他内网主机不需要任何配置

软件安装

  1. 依赖安装

    基础环境使用CentOS7.4

    yum install gcc gcc-c++ systemd-devel pkcs11-helper-devel pam-devel git iproute
  2. lzo(用于传输压缩)

    cd /usr/local/src/
    wget http://www.oberhumer.com/opensource/lzo/download/lzo-2.10.tar.gz
    tar xf lzo-2.10.tar.gz
    cd lzo-2.10
    ./configure --prefix=/usr/local/lzo
    make && make install
    echo /usr/local/lzo/lib/ >>/etc/ld.so.conf.d/env.conf
    ldconfig
    ln -sv /usr/local/lzo/include/lzo/ /usr/local/include/
  3. OpenSSL(用于传输加密)

    cd /usr/local/src/
    wget https://github.com/openssl/openssl/archive/OpenSSL_1_1_1d.tar.gz
    tar xf  OpenSSL_1_1_1d.tar.gz
    cd openssl-OpenSSL_1_1_1d
    ./config --prefix=/usr/local/openssl shared
    make && make install
    echo /usr/local/openssl/lib/ >>/etc/ld.so.conf.d/env.conf
    ldconfig
    echo 'export PATH=/usr/local/openssl/bin:$PATH' >> /etc/profile.d/env.sh
    . /etc/profile.d/env.sh
  4. OpenVPN

    wget https://dl.smartyhero.com/openvpn-2.4.8.tar.xz
    tar xf openvpn-2.4.8.tar.xz
    cd openvpn-2.4.8
    export LZO_LIBS=/usr/local/lzo/lib/liblzo2.a
    export PKG_CONFIG_PATH=/usr/local/openssl/lib/pkgconfig
    ./configure --prefix=/usr/local/openvpn  \
    --enable-pkcs11 \
    --enable-iproute2 \
    --enable-selinux \
    --enable-systemd \
    --enable-async-push \
    --enable-shared \
    --enable-static  \
    --with-crypto-library=openssl
    make && make install
    echo 'export PATH=/usr/local/openvpn/sbin:$PATH' >> /etc/profile.d/env.sh
    . /etc/profile.d/env.sh
    openvpn --version

证书环境配置

  1. 获取配置证书工具

    git clone https://github.com/OpenVPN/easy-rsa.git
    cd easy-rsa/easyrsa3
  2. 创建配置文件 vars

    vim vars
    set_var EASYRSA_DN  "org"
    set_var EASYRSA_REQ_COUNTRY "CN"
    set_var EASYRSA_REQ_PROVINCE    "BJ"
    set_var EASYRSA_REQ_CITY    "DC"
    set_var EASYRSA_REQ_ORG "SmartyHero"
    set_var EASYRSA_REQ_EMAIL   "uuidxiaozhu@163.com"

创建服务端秘钥

  1. 初始化CA

    ./easyrsa init-pki # 初始化,会在当前目录创建PKI目录,用于存储一些中间变量及最终生成的证书
    ./easyrsa build-ca # 需要输入ca相关信息,第一个需要输入的是密码,用于签发证书时使用且必须输入,其他的直接回车就可以,用于之后签发证书使用
  2. 创建服务器端证书

    ./easyrsa gen-req server nopass # 创建服务端证书,私钥
    ./easyrsa sign server server # 使用刚才生成的CA签发证书,看到 Confirm request details提示时需要输入yes,之后需要输入创建CA的密码
  3. 创建Diffie-Hellman,时间会比较长

    ./easyrsa gen-dh

创建客户端秘钥

  1. 将easyrsa3目录复制一份出来

    cd ..
    cp -r easyrsa3 easyrsa3_client
    cd easyrsa3_client
    rm -rf pki
    ./easyrsa init-pki
  1. 创建客户端证书

    ./easyrsa gen-req client_01 nopass # 生成私钥及请求文件
    cd ../easyrsa3
    ./easyrsa import-req ../easyrsa3_client/pki/reqs/client_01.req client # 导入证书请求,最后一个client最好和第一步的client_01保持一致
    ./easyrsa sign client client # 签发证书,第一个client是证书类型,可以是server,client,第二个client是文件名称,可以根据不同的人名签发证书,证书有效期为三年(825 days),如果想修改有效期可以修改openssl-easyrsa.cnf
  2. 证书有效期

    默认有效期为825天,想修改这个值,可以修改创建服务端秘钥的目录下的easyrsa命令(这个命令就是个shell脚本) 找到文件中的EASYRSA_CERT_EXPIRE将其修改为想要的值就可以了

  3. 拷贝证书

    mkdir -pv /usr/local/openvpn/etc/ssl
    cp /usr/local/easy-rsa/easyrsa3/pki/ca.crt /usr/local/openvpn/etc/ssl
    cp /usr/local/easy-rsa/easyrsa3/pki/issued/server.crt /usr/local/openvpn/etc/ssl
    cp /usr/local/easy-rsa/easyrsa3/pki/private/server.key /usr/local/openvpn/etc/ssl
    cp /usr/local/easy-rsa/easyrsa3/pki/dh.pem /usr/local/openvpn/etc/ssl/

服务端配置

  1. 服务端配置文件参考(很多配置项都是客户端服务端通用的)

    vim /usr/local/openvpn/etc/server.conf 
    local 192.168.1.6 # 本地网卡地址
    port 1194 # OpenVPN监听的端口
    proto udp # 使用的协议(可以选择tcp,udp)
    dev tun   # 模式 tun/tap,tun用于建立IP隧道(相当于三层),tap用于建立以太网桥(相当于二层)
    daemon    # 后台运行
    ca /usr/local/openvpn/etc/ssl/ca.crt # ca证书
    cert /usr/local/openvpn/etc/ssl/server.crt # 服务器端证书
    key /usr/local/openvpn/etc/ssl/server.key # 服务器秘钥
    dh /usr/local/openvpn/etc/ssl/dh.pem # dh
    server 172.16.5.0 255.255.255.0 # 该VPN的网段
    push "route 192.168.132.0 255.255.255.0"  # 给客户端push一条路由,这条路由应该是vpn服务器,物理网络所在的网段,如果有多个网卡应该都加上
    up "/bin/bash /usr/local/openvpn/etc/route.sh" # 启动OpenVPN时要执行的脚本
    down "/bin/bash /usr/local/openvpn/etc/route.sh" # 停止OpenVPN时要执行的脚本
    script-security 2 # 脚本的安全等级,可选值 0,1,2,3 默认是1等级越高可调用的命令越多
    ifconfig-pool-persist /usr/local/openvpn/etc/ipp.txt # 记录了已连接的客户端使用的IP地址,之后的客户端按照这个来分配地址了
    keepalive 10 120 # 每10秒探测一次,120秒都没有响应则认为丢失,重启VPN
    comp-lzo # 使用这种压缩
    # tls-auth ta.key 0 # 在TLS之上添加额外的身份验证层
    cipher AES-256-CBC # 加密方式
    persist-key # 重启的时候不会重读秘钥文件?(不确定)
    persist-tun # 
    status /usr/local/openvpn/logs/openvpn-status.log # openVPN状态文件,每分钟更新一次
    log-append /usr/local/openvpn/logs/openvpn.log # 追加日志,否则每次都重新生成日志
    mute 20 # 日志中重复信息的条数,如果超过此处定义的值将不再写入日志
    verb 3 # 日志级别
    client-to-client # 客户端互相连接
    #duplicate-cn # 同一个账号同时多个地点在线
    route-noexec # 不自动添加路由,而是将一些信息作为环境变量可以通过up选项定义的脚本进行自定义的配置
    writepid /usr/local/openvpn/logs/openvpn.pid # pid文件
  2. 开启服务端核心转发

    echo 1 > /proc/sys/net/ipv4/ip_forward # 临时开启
    # 永久开启
    echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
    sysctl -p
  3. 启动,停止时执行的脚本

    vim /usr/local/openvpn/etc/route.sh 
    #!/bin/sh
    
    if [ "$script_type" = "up" ];then
        /usr/sbin/ip route add  ${route_network_1}/${route_netmask_1} dev ${dev}
        /usr/sbin/iptables -t nat -A POSTROUTING -s ${route_network_1}/${route_netmask_1} ! -d ${route_network_1}/${route_netmask_1} -j MASQUERADE
    elif [ "$script_type" = "down" ];then
        /usr/sbin/ip route del  ${route_network_1}/${route_netmask_1} dev ${dev}
        /usr/sbin/iptables -t nat -D POSTROUTING -s ${route_network_1}/${route_netmask_1} ! -d ${route_network_1}/${route_netmask_1} -j MASQUERADE
    else
            env
    fi

    这个脚本主要做了两件事

    1. 添加VPN网段的路由
    2. 对于源地址是VPN网段的目标地址不是VPN网段的进行SNAT处理,以保证该局域网内的其他主机可以在连入VPN后可以正常访问
  4. 创建日志目录

    mkdir -pv /usr/local/openvpn/logs
  5. 创建service文件

    cp  /usr/local/src/openvpn-2.4.8/distro/systemd/openvpn-server@.service /etc/systemd/system/openvpn-server.service

    可能需要做一个小的修改,以下提供示例

    [Unit]
    Description=OpenVPN service for %I
    After=syslog.target network-online.target
    Wants=network-online.target
    Documentation=man:openvpn(8)
    Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
    Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO
    
    [Service]
    Type=notify
    PrivateTmp=true
    WorkingDirectory=/usr/local/openvpn # 修改这个位置,安装目录
    # ExecStart=/usr/local/openvpn/sbin/openvpn --status %t/openvpn-server/status-%i.log --status-version 2 --suppress-timestamps --con
    fig %i.conf
    ExecStart=/usr/local/openvpn/sbin/openvpn --config /usr/local/openvpn/etc/server.conf # 主要是对ExecStart修改
    CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVER
    RIDE CAP_AUDIT_WRITE
    LimitNPROC=10
    DeviceAllow=/dev/null rw
    DeviceAllow=/dev/net/tun rw
    ProtectSystem=true
    ProtectHome=true
    KillMode=process
    RestartSec=5s
    Restart=on-failure
    
    [Install]
    WantedBy=multi-user.target

    启动服务

    systemctl daemon-reload
    systemctl start openvpn-server
    systemctl enable openvpn-server

客户端配置

  1. 安装客户端

    下载win10版本的客户端

    正常下一步安装即可,记住安装的路径后面需要用,选择安装的内容按以下勾选即可,不用选OpenVPN Service
    openvpn-install.png

  2. 配置文件示例(仅使用证书认证使用,不包含用户名密码登录方式,注意可能需要将注释信息删除,否则可能有问题)

    client
    remote 192.168.1.6 1194 # 服务端地址,端口
    dev tun
    proto udp # 协议,必须和服务端相同
    comp-lzo # 启用压缩
    resolv-retry infinite # 启用该指令,与服务器连接中断后将自动重新连接,这在网络不稳定的情况下(例如:笔记本电脑无线网络)非常有用。
    # redirect-gateway #打开后本地所有流量都会通过VPN服务器转发,
    nobind # 客户端不用绑定特定的端口号
    persist-key  # 持久化选项可以尽量避免访问在重启时由于用户权限降低而无法访问的某些资源。
    persist-tun # 持久化选项可以尽量避免访问在重启时由于用户权限降低而无法访问的某些资源。
    ca ca.crt
    remote-cert-tls server
    cipher AES-256-CBC # 加密算法,必须和服务端相同
    verb 3
    cert client.crt
    key client_01.key
    auth-nocache
  3. 证书准备

    将以下三个文件拷贝到本地

    /usr/local/easy-rsa/easyrsa3/pki/ca.crt # CA证书
    /usr/local/easy-rsa/easyrsa3/pki/issued/client.crt # 客户端公钥证书 
    /usr/local/easy-rsa/easyrsa3_client/pki/private/client_01.key  # 客户端私钥证书

    在OpenVPN客户端的安装目录下的config目录下新建一个文件夹文件名任意,最好使用英文的(不保证中文可以正常使用),然后将以上三个文件以及配置文件拷贝到该目录下,大致结构如下
    openvpn-client-connect.png

    然后就可以打开客户端,在任务栏上右击OpenVPN的图标,选择连接,正常情况下即可连接成功,如果不成功可选择显示日志来进行问题的排除,连接成功后可以试着ping一下VPN服务器中的内网地址,以及其内网的其他地址看一下,如果有问题,可以使用抓包软件抓一下包看一下具体的数据包流向

吊销证书

cd /usr/local/easy-rsa/easyrsa3
./easyrsa revoke cert_basename # 这里是要吊销的证书不含后缀的文件名
./easyrsa gen-crl # 生成吊销列表

使OpenVPN可以获取到已经吊销的证书

vim /usr/local/openvpn/etc/server.conf

加入以下行

crl-verify /usr/local/easy-rsa/easyrsa3/pki/crl.pem

bighero

这个人太懒什么东西都没留下

文章评论(0)