系统安全-VPN

网友投稿 1031 2022-09-02

系统安全-VPN

系统安全-VPN

VPN直译就是虚拟专用通道,是提供给企业之间或者个人与公司之间安全数据传输的隧道,OpenVPN无疑是Linux下开源VPN的先锋,提供了良好的性能和友好的用户GUI。该软件最早由James Yonan编写。OpenVPN允许参与建立VPN的单点使用预设的私钥。第三方证书,或者用户名/密码来进行身份验证。它大量使用了OpenSSL加密库,以及SSlv3/TLSv1协议。OPenVPN能在Linux、xBSD、MacOS X与Windows 2000/XP上运行。它并不是一个基于Web的VPN软件,也不与IPsec以及其他VPN软件包兼容。

在centos7下安装和配置OpenVPN

介绍:

我们将在Centos 7服务器上安装和配置openvpn。我们还将讨论如何将客户端连接到windows、os x和Linux上的服务器。openvpn是一个开放源码的VPN应用程序,允许您在公共互联网上安全地创建和连接专用网络。

步骤一:安装OpenVPN

首先,我们需要安装openvpn。我们还将安装轻松的rsa来生成SSL密钥对,这将保护我们的VPN连接。

[root@localhost ~]# yum instally已加载插件:fastestmirror, langpacksLoading mirror speeds from cached hostfileepel/x86_64/metalink | 8.0 kB 00:00:00 * base: mirrors.aliyun.com * epel: mirrors.aliyun.com * extras: mirrors-99.com * updates: mirrors.huaweicloud.combase | 3.6 kB 00:00:00 epel | 3.2 kB 00:00:00 extras | 3.4 kB 00:00:00 mysql-connectors-community | 2.5 kB 00:00:00 mysql-tools-community | 2.5 kB 00:00:00 mysql56-community | 2.5 kB 00:00:00 updates | 3.4 kB 00:00:00 正在解决依赖关系--> 正在检查事务---> 软件包 easy-rsa.noarch.0.3.0.3-1.el7 将被 安装---> 软件包 openvpn.x86_64.0.2.4.6-1.el7 将被 安装--> 正在处理依赖关系 libpkcs11-helper.so.1()(64bit),它被软件包 openvpn-2.4.6-1.el7.x86_64 需要--> 正在检查事务---> 软件包 pkcs11-helper.x86_64.0.1.11-3.el7 将被 安装--> 解决依赖关系完成依赖关系解决======================================================================================================================= Package 架构 版本 源 大小=======================================================================================================================正在安装: easy-rsa noarch 3.0.3-1.el7 epel 31 k openvpn x86_64 2.4.6-1.el7 epel 518 k为依赖而安装: pkcs11-helper x86_64 1.11-3.el7 epel 56 k事务概要=======================================================================================================================安装 2 软件包 (+1 依赖软件包)总-量:605 k安装大小:1.4 MDownloading packages:(1/3): easy-rsa-3.0.3-1.el7.noarch.rpm | 31 kB 00:00:00 (2/3): pkcs11-helper-1.11-3.el7.x86_64.rpm | 56 kB 00:00:00 (3/3): openvpn-2.4.6-1.el7.x86_64.rpm | 518 kB 00:00:01 -----------------------------------------------------------------------------------------------------------------------总计 331 kB/s | 605 kB 00:00:01 Running transaction checkRunning transaction testTransaction test succeededRunning transaction 正在安装 : pkcs11-helper-1.11-3.el7.x86_64 1/3 正在安装 : openvpn-2.4.6-1.el7.x86_64 2/3 正在安装 : easy-rsa-3.0.3-1.el7.noarch 3/3 验证中 : pkcs11-helper-1.11-3.el7.x86_64 1/3 验证中 : easy-rsa-3.0.3-1.el7.noarch 2/3 验证中 : openvpn-2.4.6-1.el7.x86_64 3/3 已安装: easy-rsa.noarch 0:3.0.3-1.el7 openvpn.x86_64 0:2.4.6-1.el7 作为依赖被安装: pkcs11-helper.x86_64 0:1.11-3.el7 完毕![root@localhost ~]#

步骤二:配置OpenVPN

openvpn的文档目录中有示例配置文件。我们将复制示例server.conf文件作为我们自己的配置文件的起点。

[root@localhost ~]# cp /usr/share/doc/openvpn-2.4.6/sample/sample-config-files/server.conf /etc/openvpn/[root@localhost ~]# # 先从默认目录拷贝一份出来

[root@localhost ~]# cat /etc/openvpn/server.conf ################################################## Sample OpenVPN 2.0 config file for ## multi-client server. ## ## This file is for the server side ## of a many-clients <-> one-server ## OpenVPN configuration. ## ## OpenVPN also supports ## single-machine <-> single-machine ## configurations (See the Examples page ## on the web site for more info). ## ## This config should work on Windows ## or Linux/BSD systems. Remember on ## Windows to quote pathnames and use ## double backslashes, e.g.: ## "C:\\Program Files\\OpenVPN\\config\\foo.key" ## ## Comments are preceded with '#' or ';' ################################################### Which local IP address should OpenVPN# listen on? (optional);local a.b.c.d# Which TCP/UDP port should OpenVPN listen on?# If you want to run multiple OpenVPN instances# on the same machine, use a different port# number for each one. You will need to# open up this port on your firewall.port 1194# TCP or UDP server?;proto tcpproto udp# "dev tun" will create a routed IP tunnel,# "dev tap" will create an ethernet tunnel.# Use "dev tap0" if you are ethernet bridging# and have precreated a tap0 virtual interface# and bridged it with your ethernet interface.# If you want to control access policies# over the VPN, you must create firewall# rules for the the TUN/TAP interface.# On non-Windows systems, you can give# an explicit unit number, such as tun0.# On Windows, use "dev-node" for this.# On most systems, the VPN will not function# unless you partially or fully disable# the firewall for the TUN/TAP interface.;dev tapdev tun# Windows needs the TAP-Win32 adapter name# from the Network Connections panel if you# have more than one. On XP SP2 or higher,# you may need to selectively disable the# Windows firewall for the TAP adapter.# Non-Windows systems usually don't need this.;dev-node MyTap# SSL/TLS root certificate (ca), certificate# (cert), and private key (key). Each client# and the server must have their own cert and# key file. The server and all clients will# use the same ca file.## See the "easy-rsa" directory for a series# of scripts for generating RSA certificates# and private keys. Remember to use# a unique Common Name for the server# and each of the client certificates.## Any X509 key management system can be used.# OpenVPN can also use a PKCS #12 formatted key file# (see "pkcs12" directive in man page).ca ca.crtcert server.crtkey server.key # This file should be kept secret# Diffie hellman parameters.# Generate your own with:# openssl dhparam -out dh2048.pem 2048dh dh2048.pem# Network topology# Should be subnet (addressing via IP)# unless Windows clients v2.0.9 and lower have to# be supported (then net30, i.e. a /30 per client)# Defaults to net30 (not recommended);topology subnet# Configure server mode and supply a VPN subnet# for OpenVPN to draw client addresses from.# The server will take 10.8.0.1 for itself,# the rest will be made available to clients.# Each client will be able to reach the server# on 10.8.0.1. Comment this line out if you are# ethernet bridging. See the man page for more info.server 10.8.0.0 255.255.255.0# Maintain a record of client <-> virtual IP address# associations in this file. If OpenVPN goes down or# is restarted, reconnecting clients can be assigned# the same virtual IP address from the pool that was# previously assigned.ifconfig-pool-persist ipp.txt# Configure server mode for ethernet bridging.# You must first use your OS's bridging capability# to bridge the TAP interface with the ethernet# NIC interface. Then you must manually set the# IP/netmask on the bridge interface, here we# assume 10.8.0.4/255.255.255.0. Finally we# must set aside an IP range in this subnet# (start=10.8.0.50 end=10.8.0.100) to allocate# to connecting clients. Leave this line commented# out unless you are ethernet bridging.;server-bridge 10.8.0.4 255.255.255.0 10.8.0.50 10.8.0.100# Configure server mode for ethernet bridging# using a DHCP-proxy, where clients talk# to the OpenVPN server-side DHCP server# to receive their IP address allocation# and DNS server addresses. You must first use# your OS's bridging capability to bridge the TAP# interface with the ethernet NIC interface.# Note: this mode only works on clients (such as# Windows), where the client-side TAP adapter is# bound to a DHCP client.;server-bridge# Push routes to the client to allow it# to reach other private subnets behind# the server. Remember that these# private subnets will also need# to know to route the OpenVPN client# address pool (10.8.0.0/255.255.255.0)# back to the OpenVPN server.;push "route 192.168.10.0 255.255.255.0";push "route 192.168.20.0 255.255.255.0"# To assign specific IP addresses to specific# clients or if a connecting client has a private# subnet behind it that should also have VPN access,# use the subdirectory "ccd" for client-specific# configuration files (see man page for more info).# EXAMPLE: Suppose the client# having the certificate common name "Thelonious"# also has a small subnet behind his connecting# machine, such as 192.168.40.128/255.255.255.248.# First, uncomment out these lines:;client-config-dir ccd;route 192.168.40.128 255.255.255.248# Then create a file ccd/Thelonious with this line:# iroute 192.168.40.128 255.255.255.248# This will allow Thelonious' private subnet to# access the VPN. This example will only work# if you are routing, not bridging, i.e. you are# using "dev tun" and "server" directives.# EXAMPLE: Suppose you want to give# Thelonious a fixed VPN IP address of 10.9.0.1.# First uncomment out these lines:;client-config-dir ccd;route 10.9.0.0 255.255.255.252# Then add this line to ccd/Thelonious:# ifconfig-push 10.9.0.1 10.9.0.2# Suppose that you want to enable different# firewall access policies for different groups# of clients. There are two methods:# (1) Run multiple OpenVPN daemons, one for each# group, and firewall the TUN/TAP interface# for each group/daemon appropriately.# (2) (Advanced) Create a script to dynamically# modify the firewall in response to access# from different clients. See man# page for more info on learn-address script.;learn-address ./script# If enabled, this directive will configure# all clients to redirect their default# network gateway through the VPN, causing# all IP traffic such as web browsing and# and DNS lookups to go through the VPN# (The OpenVPN server machine may need to NAT# or bridge the TUN/TAP interface to the internet# in order for this to work properly).;push "redirect-gateway def1 bypass-dhcp"# Certain Windows-specific network settings# can be pushed to clients, such as DNS# or WINS server addresses. CAVEAT:# The addresses below refer to the public# DNS servers provided by opendns.com.;push "dhcp-option DNS 208.67.222.222";push "dhcp-option DNS 208.67.220.220"# Uncomment this directive to allow different# clients to be able to "see" each other.# By default, clients will only see the server.# To force clients to only see the server, you# will also need to appropriately firewall the# server's TUN/TAP interface.;client-to-client# Uncomment this directive if multiple clients# might connect with the same certificate/key# files or common names. This is recommended# only for testing purposes. For production use,# each client should have its own certificate/key# pair.## IF YOU HAVE NOT GENERATED INDIVIDUAL# CERTIFICATE/KEY PAIRS FOR EACH CLIENT,# EACH HAVING ITS OWN UNIQUE "COMMON NAME",# UNCOMMENT THIS LINE OUT.;duplicate-cn# The keepalive directive causes ping-like# messages to be sent back and forth over# the link so that each side knows when# the other side has gone down.# Ping every 10 seconds, assume that remote# peer is down if no ping received during# a 120 second time period.keepalive 10 120# For extra security beyond that provided# by SSL/TLS, create an "HMAC firewall"# to help block DoS attacks and UDP port flooding.## Generate with:# openvpn --genkey --secret ta.key## The server and each client must have# a copy of this key.# The second parameter should be '0'# on the server and '1' on the clients.tls-auth ta.key 0 # This file is secret# Select a cryptographic cipher.# This config item must be copied to# the client config file as well.# Note that v2.4 client/server will automatically# negotiate AES-256-GCM in TLS mode.# See also the ncp-cipher option in the manpagecipher AES-256-CBC# Enable compression on the VPN link and push the# option to the client (v2.4+ only, for earlier# versions see below);compress lz4-v2;push "compress lz4-v2"# For compression compatible with older clients use comp-lzo# If you enable it here, you must also# enable it in the client config file.;comp-lzo# The maximum number of concurrently connected# clients we want to allow.;max-clients 100# It's a good idea to reduce the OpenVPN# daemon's privileges after initialization.## You can uncomment this out on# non-Windows systems.;user nobody;group nobody# The persist options will try to avoid# accessing certain resources on restart# that may no longer be accessible because# of the privilege downgrade.persist-keypersist-tun# Output a short status file showing# current connections, truncated# and rewritten every minute.status openvpn-status.log# By default, log messages will go to the syslog (or# on Windows, if running as a service, they will go to# the "\Program Files\OpenVPN\log" directory).# Use log or log-append to override this default.# "log" will truncate the log file on OpenVPN startup,# while "log-append" will append to it. Use one# or the other (but not both).;log openvpn.log;log-append openvpn.log# Set the appropriate level of log# file verbosity.## 0 is silent, except for fatal errors# 4 is reasonable for general usage# 5 and 6 can help to debug connection problems# 9 is extremely verboseverb 3# Silence repeating messages. At most 20# sequential messages of the same message# category will be output to the log.;mute 20# Notify the client that when the server restarts so it# can automatically reconnect.explicit-exit-notify 1[root@localhost ~]#

在这整个文件中,只需要修改几行配置就可以了(上边没有注释的)。

步骤三:生成秘钥和证书

现在服务器已经配置好了,我们需要生成密钥和证书。轻松的rsa安装了一些脚本来生成这些密钥和证书。让我们为要进入的键创建一个目录。

[root@localhost ~]# mkdir -p /etc/openvpn/easy-rsa/keys[root@localhost ~]# # 生成一个目录,存放证书相关

[root@localhost ~]# cp -rf /usr/share/easy-rsa/3.0/ /etc/openvpn/easy-rsa/[root@localhost ~]# #为了使我们自己更容易,我们将编辑缺省值-脚本 使用,这样我们就不必每次输入我们的信息了。此信息存储在

[root@localhost ~]# cp -rf /usr/share/easy-rsa/3.0/ /etc/openvpn/easy-rsa/

--req-cn=NAME : default CN to use--subca-len=# : path length of signed sub-CA certs; must be >= 0 if used--subject-alt-name : Add a subjectAltName. For more info and syntax, see: ./easyrsa help altname--use-algo=ALG : crypto alg to use: choose rsa (default) or ec--curve=NAME : for elliptic curve, sets the named curve to useOrganizational DN options: (only used with the 'org' DN mode) (values may be blank for org DN options)--req-c=CC : country code (2-letters)--req-st=NAME : State/Province--req-city=NAME : City/Locality--req-org=NAME : Organization--req-email=NAME : Email addresses--req-ou=NAME : Organizational UnitDeprecated features:--ns-cert=YESNO : yes or no to including deprecated NS extensions--ns-comment=COMMENT : NS comment to include (value may be blank)"} # => opt_usage()# Wrapper around printf - clobber print since it's not POSIX anywayprint() { printf "%s\n" "$*"; }# Exit fatally with a message to stderr# present even with EASYRSA_BATCH as these are fatal problemsdie() { print "Easy-RSA error:$1" 1>&2 exit ${2:-1}} # => die()# non-fatal warning outputwarn() { [ ! $EASYRSA_BATCH ] && \ print "$1" 1>&2} # => warn()# informational notices to stdoutnotice() { [ ! $EASYRSA_BATCH ] && \ print "$1"} # => notice()# yes/no case-insensitive match (operates on stdin pipe)# Returns 0 when input contains yes, 1 for no, 2 for no match# If both strings are present, returns 1; first matching line returns.awk_yesno() { local awkscript='BEGIN {IGNORECASE=1; r=2}{ if(match($0,"no")) {r=1; exit} if(match($0,"yes")) {r=0; exit}} END {exit r}' awk "$awkscript"} # => awk_yesno()# intent confirmation helper func# returns without prompting in EASYRSA_BATCHconfirm() { [ $EASYRSA_BATCH ] && return local prompt="$1" value="$2" msg="$3" input print "$msgType the word '$value' to continue, or any other input to abort." printf %s " $prompt" read input [ "$input" = "$value" ] && return notice "Aborting without confirmation." exit 9} # => confirm()# remove temp filesclean_temp() { for f in "$EASYRSA_TEMP_FILE" "$EASYRSA_TEMP_FILE_2" "$EASYRSA_TEMP_FILE_3" do [ -f "$f" ] && rm "$f" 2>/dev/null done} # => clean_temp()vars_source_check() { # Check for defined EASYRSA_PKI [ -n "$EASYRSA_PKI" ] || die "\EASYRSA_PKI env-var undefined" # Verify EASYRSA_OPENSSL command gives expected output if [ -z "$EASYRSA_SSL_OK" ]; then local val="$("$EASYRSA_OPENSSL" version)" case "${val%% *}" in OpenSSL|LibreSSL) ;; *) die "\Missing or invalid OpenSSLExpected to find openssl command at: $EASYRSA_OPENSSL" esac fi EASYRSA_SSL_OK=1 # Verify EASYRSA_SSL_CONF file exists [ -f "$EASYRSA_SSL_CONF" ] || die "\The OpenSSL config file cannot be found.Expected location: $EASYRSA_SSL_CONF"} # => vars_source_check()# Verify supplied curve exists and generate curve file if neededverify_curve() { if ! "$EASYRSA_OPENSSL" ecparam -name "$EASYRSA_CURVE" > /dev/null; then die "\Curve $EASYRSA_CURVE not found. Run openssl ecparam -list_curves to show alist of supported curves." fi # Check that the ecparams dir exists [ -d "$EASYRSA_EC_DIR" ] || mkdir "$EASYRSA_EC_DIR" || die "\Failed creating ecparams dir (permissions?) at:$EASYRSA_EC_DIR" # Check that the required ecparams file exists local out="$EASYRSA_EC_DIR/${EASYRSA_CURVE}.pem" [ -f "$out" ] && return 0 "$EASYRSA_OPENSSL" ecparam -name "$EASYRSA_CURVE" -out "$out" || die "\Failed to generate ecparam file (permissions?) when writing to:$out" # Explicitly return success for caller return 0}# Basic sanity-check of PKI init and complain if missingverify_pki_init() { local help_note="Run easyrsa without commands for usage and command help." # check that the pki dir exists vars_source_check [ -d "$EASYRSA_PKI" ] || die "\EASYRSA_PKI does not exist (perhaps you need to run init-pki)?Expected to find the EASYRSA_PKI at: $EASYRSA_PKI$help_note" # verify expected dirs present: for i in private reqs; do [ -d "$EASYRSA_PKI/$i" ] || die "\Missing expected directory: $i (perhaps you need to run init-pki?)$help_note" done} # => verify_pki_init()# Verify core CA files presentverify_ca_init() { local help_note="Run without commands for usage and command help." # First check the PKI has been initialized verify_pki_init # verify expected files present: for i in serial index.txt ca.crt private/ca.key; do if [ ! -f "$EASYRSA_PKI/$i" ]; then [ "$1" = "test" ] && return 1 die "\Missing expected CA file: $i (perhaps you need to run build-ca?)$help_note" fi done # When operating in 'test' mode, return success. # test callers don't care about CA-specific dir structure [ "$1" = "test" ] && return 0 # verify expected CA-specific dirs: for i in issued certs_by_serial; do [ -d "$EASYRSA_PKI/$i" ] || die "\Missing expected CA dir: $i (perhaps you need to run build-ca?)$help_note" done # explicitly return success for callers return 0} # => verify_ca_init()# init-pki backend:init_pki() { vars_source_check # If EASYRSA_PKI exists, confirm before we rm -rf (skiped with EASYRSA_BATCH) if [ -e "$EASYRSA_PKI" ]; then confirm "Confirm removal: " "yes" "WARNING!!!You are about to remove the EASYRSA_PKI at: $EASYRSA_PKIand initialize a fresh PKI here." # now remove it: rm -rf "$EASYRSA_PKI" || die "Removal of PKI dir failed. Check/correct errors above" fi # new dirs: for i in private reqs; do mkdir -p "$EASYRSA_PKI/$i" || die "Failed to create PKI file structure (permissions?)" done notice "\init-pki complete; you may now create a CA or requests.Your newly created PKI dir is: $EASYRSA_PKI" return 0} # => init_pki()# build-ca backend:build_ca() { local opts= sub_ca= while [ -n "$1" ]; do case "$1" in nopass) opts="$opts -nodes" ;; subca) sub_ca=1 ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done verify_pki_init [ "$EASYRSA_ALGO" = "ec" ] && verify_curve # setup for the simpler sub-CA situation and overwrite with root-CA if needed: local out_file="$EASYRSA_PKI/reqs/ca.req" local out_key="$EASYRSA_PKI/private/ca.key" if [ ! $sub_ca ]; then out_file="$EASYRSA_PKI/ca.crt" opts="$opts -x509 -days $EASYRSA_CA_EXPIRE" fi # Test for existing CA, and complain if already present if verify_ca_init test; then die "\Unable to create a CA as you already seem to have one set up.If you intended to start a new CA, run init-pki first." fi # If a private key exists here, a sub-ca was created but not signed. # Notify the user and require a signed ca.crt or a init-pki: [ -f "$out_key" ] && \ die "\A CA private key exists but no ca.crt is found in your PKI dir of:$EASYRSA_PKIRefusing to create a new CA keypair as this operation would overwrite yourcurrent CA keypair. If you intended to start a new CA, run init-pki first." # create necessary files and dirs: local err_file="Unable to create necessary PKI files (permissions?)" for i in issued certs_by_serial; do mkdir -p "$EASYRSA_PKI/$i" || die "$err_file" done printf "" > "$EASYRSA_PKI/index.txt" || die "$err_file" print "01" > "$EASYRSA_PKI/serial" || die "$err_file" # Default CN only when not in global EASYRSA_BATCH mode: [ $EASYRSA_BATCH ] && opts="$opts -batch" || export EASYRSA_REQ_CN="Easy-RSA CA" out_key_tmp="$(mktemp -u "$out_key.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$out_key_tmp" out_file_tmp="$(mktemp -u "$out_file.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_3="$out_file_tmp" # create the CA keypair: "$EASYRSA_OPENSSL" req -utf8 -new -newkey $EASYRSA_ALGO:"$EASYRSA_ALGO_PARAMS" \ -config "$EASYRSA_SSL_CONF" -keyout "$out_key_tmp" -out "$out_file_tmp" $opts || \ die "Failed to build the CA" mv "$out_key_tmp" "$out_key"; EASYRSA_TEMP_FILE_2= mv "$out_file_tmp" "$out_file"; EASYRSA_TEMP_FILE_3= # Success messages if [ $sub_ca ]; then notice "\NOTE: Your sub-CA request is at $out_fileand now must be sent to you parent CA for signing. Place your resulting certat $EASYRSA_PKI/ca.crt prior to signing operations." else notice "\CA creation complete and you may now import and sign cert requests.Your new CA certificate file for publishing is at:$out_file" fi return 0} # => build_ca()# gen-dh backend:gen_dh() { verify_pki_init local out_file="$EASYRSA_PKI/dh.pem" "$EASYRSA_OPENSSL" dhparam -out "$out_file" $EASYRSA_KEY_SIZE || \ die "Failed to build DH params" notice "\DH parameters of size $EASYRSA_KEY_SIZE created at $out_file" return 0} # => gen_dh()# gen-req backend:gen_req() { # pull filename base and use as default interactive CommonName: [ -n "$1" ] || die "\Error: gen-req must have a file base as the first argument.Run easyrsa without commands for usage and commands." local key_out="$EASYRSA_PKI/private/$1.key" local req_out="$EASYRSA_PKI/reqs/$1.req" [ ! $EASYRSA_BATCH ] && EASYRSA_REQ_CN="$1" shift # function opts support local opts= while [ -n "$1" ]; do case "$1" in nopass) opts="$opts -nodes" ;; # batch flag supports internal callers needing silent operation batch) local EASYRSA_BATCH=1 ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done verify_pki_init [ "$EASYRSA_ALGO" = "ec" ] && verify_curve # don't wipe out an existing private key without confirmation [ -f "$key_out" ] && confirm "Confirm key overwrite: " "yes" "\WARNING!!!An existing private key was found at $key_outContinuing with key generation will replace this key." # When EASYRSA_EXTRA_EXTS is defined, append it to openssl's [req] section: if [ -n "$EASYRSA_EXTRA_EXTS" ]; then # Setup & insert the extra ext data keyed by a magic line local extra_exts="req_extensions = req_extra[ req_extra ]$EASYRSA_EXTRA_EXTS" local awkscript='{if ( match($0, "^#%EXTRA_EXTS%") ) { while ( getline<"/dev/stdin" ) {print} next } {print}}' print "$extra_exts" | \ awk "$awkscript" "$EASYRSA_SSL_CONF" \ > "$EASYRSA_TEMP_FILE" \ || die "Copying SSL config to temp file failed" # Use this new SSL config for the rest of this function local EASYRSA_SSL_CONF="$EASYRSA_TEMP_FILE" fi key_out_tmp="$(mktemp -u "$key_out.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$key_out_tmp" req_out_tmp="$(mktemp -u "$req_out.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_3="$req_out_tmp" # generate request [ $EASYRSA_BATCH ] && opts="$opts -batch" "$EASYRSA_OPENSSL" req -utf8 -new -newkey $EASYRSA_ALGO:"$EASYRSA_ALGO_PARAMS" \ -config "$EASYRSA_SSL_CONF" -keyout "$key_out_tmp" -out "$req_out_tmp" $opts \ || die "Failed to generate request" mv "$key_out_tmp" "$key_out"; EASYRSA_TEMP_FILE_2= mv "$req_out_tmp" "$req_out"; EASYRSA_TEMP_FILE_3= notice "\Keypair and certificate request completed. Your files are:req: $req_outkey: $key_out" return 0} # => gen_req()# common signing backendsign_req() { local crt_type="$1" opts= local req_in="$EASYRSA_PKI/reqs/$2.req" local crt_out="$EASYRSA_PKI/issued/$2.crt" # Randomize Serial number local i= serial= check_serial= for i in 1 2 3 4 5; do "$EASYRSA_OPENSSL" rand -hex 16 -out "$EASYRSA_PKI/serial" serial="$(cat "$EASYRSA_PKI/serial")" check_serial="$("$EASYRSA_OPENSSL" ca -config "$EASYRSA_SSL_CONF" -status "$serial" 2>&1)" case "$check_serial" in *"not present in db"*) break ;; *) continue ;; esac done # Support batch by internal caller: [ "$3" = "batch" ] && local EASYRSA_BATCH=1 verify_ca_init # Check argument sanity: [ -n "$2" ] || die "\Incorrect number of arguments provided to sign-req:expected 2, got $# (see command help for usage)" # Cert type must exist under the EASYRSA_EXT_DIR [ -r "$EASYRSA_EXT_DIR/$crt_type" ] || die "\Unknown cert type '$crt_type'" # Request file must exist [ -f "$req_in" ] || die "\No request found for the input: '$2'Expected to find the request at: $req_in" # Confirm input is a cert req verify_file req "$req_in" || die "\The certificate request file is not in a valid X509 request format.Offending file: $req_in" # Display the request subject in an easy-to-read format # Confirm the user wishes to sign this request confirm "Confirm request details: " "yes" "You are about to sign the following certificate.Please check over the details shown below for accuracy. Note that this requesthas not been cryptographically verified. Please be sure it came from a trustedsource or that you have verified the request checksum with the sender.Request subject, to be signed as a $crt_type certificate for $EASYRSA_CERT_EXPIRE days:$(display_dn req "$req_in")" # => confirm end # Generate the extensions file for this cert: { # Append first any COMMON file (if present) then the cert-type extensions cat "$EASYRSA_EXT_DIR/COMMON" cat "$EASYRSA_EXT_DIR/$crt_type" # Support a dynamic CA path length when present: [ "$crt_type" = "ca" ] && [ -n "$EASYRSA_SUBCA_LEN" ] && \ print "basicConstraints = CA:TRUE, pathlen:$EASYRSA_SUBCA_LEN" # Deprecated Netscape extension support, if enabled if print "$EASYRSA_NS_SUPPORT" | awk_yesno; then [ -n "$EASYRSA_NS_COMMENT" ] && \ print "nsComment = \"$EASYRSA_NS_COMMENT\"" case "$crt_type" in server) print "nsCertType = server" ;; client) print "nsCertType = client" ;; ca) print "nsCertType = sslCA" ;; esac fi # If type is server and no subjectAltName was requested, # add one to the extensions file if [[ "$crt_type" == 'server' ]] then echo "$EASYRSA_EXTRA_EXTS" | grep -q subjectAltName || print $(default_server_san "$req_in") fi # Add any advanced extensions supplied by env-var: [ -n "$EASYRSA_EXTRA_EXTS" ] && print "$EASYRSA_EXTRA_EXTS" : # needed to keep die from inherting the above test } > "$EASYRSA_TEMP_FILE" || die "\Failed to create temp extension file (bad permissions?) at:$EASYRSA_TEMP_FILE" # sign request crt_out_tmp="$(mktemp -u "$crt_out.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$crt_out_tmp" "$EASYRSA_OPENSSL" ca -utf8 -in "$req_in" -out "$crt_out_tmp" -config "$EASYRSA_SSL_CONF" \ -extfile "$EASYRSA_TEMP_FILE" -days $EASYRSA_CERT_EXPIRE -batch $opts \ || die "signing failed (openssl output above may have more detail)" mv "$crt_out_tmp" "$crt_out"; EASYRSA_TEMP_FILE_2= notice "\Certificate created at: $crt_out" return 0} # => sign_req()# common build backend# used to generate+sign in 1 stepbuild_full() { verify_ca_init # pull filename base: [ -n "$2" ] || die "\Error: didn't find a file base name as the first argument.Run easyrsa without commands for usage and commands." local crt_type="$1" name="$2" local req_out="$EASYRSA_PKI/reqs/$2.req" local key_out="$EASYRSA_PKI/private/$2.key" local crt_out="$EASYRSA_PKI/issued/$2.crt" shift 2 # function opts support local req_opts= while [ -n "$1" ]; do case "$1" in nopass) req_opts="$req_opts nopass" ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done # abort on existing req/key/crt files local err_exists="\file already exists. Aborting build to avoid overwriting this file.If you wish to continue, please use a different name or remove the file.Matching file found at: " [ -f "$req_out" ] && die "Request $err_exists $req_out" [ -f "$key_out" ] && die "Key $err_exists $key_out" [ -f "$crt_out" ] && die "Certificate $err_exists $crt_out" # create request EASYRSA_REQ_CN="$name" gen_req "$name" batch $req_opts # Sign it sign_req "$crt_type" "$name" batch} # => build_full()# revoke backendrevoke() { verify_ca_init # pull filename base: [ -n "$1" ] || die "\Error: didn't find a file base name as the first argument.Run easyrsa without commands for usage and command help." local crt_in="$EASYRSA_PKI/issued/$1.crt" verify_file x509 "$crt_in" || die "\Unable to revoke as the input file is not a valid certificate. Unexpectedinput in file: $crt_in" # confirm operation by displaying DN: confirm "Continue with revocation: " "yes" "Please confirm you wish to revoke the certificate with the following subject:$(display_dn x509 "$crt_in")" # => confirm end # referenced cert must exist: [ -f "$crt_in" ] || die "\Unable to revoke as no certificate was found. Certificate was expectedat: $crt_in" "$EASYRSA_OPENSSL" ca -utf8 -revoke "$crt_in" -config "$EASYRSA_SSL_CONF" || die "\Failed to revoke certificate: revocation command failed." notice "\IMPORTANT!!!Revocation was successful. You must run gen-crl and upload a CRL to yourinfrastructure in order to prevent the revoked cert from being accepted." # => notice end return 0} #= revoke()# gen-crl backendgen_crl() { verify_ca_init local out_file="$EASYRSA_PKI/crl.pem" out_file_tmp="$(mktemp -u "$out_file.XXXXXXXXXX")"; EASYRSA_TEMP_FILE_2="$out_file_tmp" "$EASYRSA_OPENSSL" ca -utf8 -gencrl -out "$out_file_tmp" -config "$EASYRSA_SSL_CONF" || die "\CRL Generation failed." mv "$out_file_tmp" "$out_file"; EASYRSA_TEMP_FILE_2= notice "\An updated CRL has been created.CRL file: $out_file" return 0} # => gen_crl()# import-req backendimport_req() { verify_pki_init # pull passed paths local in_req="$1" short_name="$2" local out_req="$EASYRSA_PKI/reqs/$2.req" [ -n "$short_name" ] || die "\Unable to import: incorrect command syntax.Run easyrsa without commands for usage and command help." verify_file req "$in_req" || die "\The input file does not appear to be a certificate request. Aborting import.Offending file: $in_req" # destination must not exist [ -f "$out_req" ] && die "\Unable to import the request as the destination file already exists.Please choose a different name for your imported request file.Existing file at: $out_req" # now import it cp "$in_req" "$out_req" notice "\The request has been successfully imported with a short name of: $short_nameYou may now use this name to perform signing operations on this request." return 0} # => import_req()# export pkcs#12 or pkcs#7export_pkcs() { local pkcs_type="$1" shift [ -n "$1" ] || die "\Unable to export p12: incorrect command syntax.Run easyrsa without commands for usage and command help." local short_name="$1" local crt_in="$EASYRSA_PKI/issued/$1.crt" local key_in="$EASYRSA_PKI/private/$1.key" local crt_ca="$EASYRSA_PKI/ca.crt" shift verify_pki_init # opts support local want_ca=1 local want_key=1 while [ -n "$1" ]; do case "$1" in noca) want_ca= ;; nokey) want_key= ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done local pkcs_opts= if [ $want_ca ]; then verify_file x509 "$crt_ca" || die "\Unable to include CA cert in the $pkcs_type output (missing file, or use noca option.)Missing file expected at: $crt_ca" pkcs_opts="$pkcs_opts -certfile $crt_ca" fi # input files must exist verify_file x509 "$crt_in" || die "\Unable to export $pkcs_type for short name '$short_name' without the certificate.Missing cert expected at: $crt_in" case "$pkcs_type" in p12) local pkcs_out="$EASYRSA_PKI/private/$short_name.p12" if [ $want_key ]; then [ -f "$key_in" ] || die "\Unable to export p12 for short name '$short_name' without the key(if you want a p12 without the private key, use nokey option.)Missing key expected at: $key_in" else pkcs_opts="$pkcs_opts -nokeys" fi # export the p12: "$EASYRSA_OPENSSL" pkcs12 -in "$crt_in" -inkey "$key_in" -export \ -out "$pkcs_out" $pkcs_opts || die "\Export of p12 failed: see above for related openssl errors." ;; p7) local pkcs_out="$EASYRSA_PKI/issued/$short_name.p7b" # export the p7: "$EASYRSA_OPENSSL" crl2pkcs7 -nocrl -certfile "$crt_in" \ -out "$pkcs_out" $pkcs_opts || die "\Export of p7 failed: see above for related openssl errors." ;;esac notice "\Successful export of $pkcs_type file. Your exported file is at the followinglocation: $pkcs_out" return 0} # => export_pkcs()# set-pass backendset_pass() { verify_pki_init # key type, supplied internally from frontend command call (rsa/ec) local key_type="$1" # values supplied by the user: local raw_file="$2" local file="$EASYRSA_PKI/private/$raw_file.key" [ -n "$raw_file" ] || die "\Missing argument to 'set-$key_type-pass' command: no name/file supplied.See help output for usage details." # parse command options shift 2 local crypto="-aes256" while [ -n "$1" ]; do case "$1" in nopass) crypto= ;; file) file="$raw_file" ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done [ -f "$file" ] || die "\Missing private key: expected to find the private key component at:$file" notice "\If the key is currently encrypted you must supply the decryption passphrase.${crypto:+You will then enter a new PEM passphrase for this key.$NL}" "$EASYRSA_OPENSSL" $key_type -in "$file" -out "$file" $crypto || die "\Failed to change the private key passphrase. See above for possible opensslerror messages." notice "Key passphrase successfully changed" } # => set_pass()# update-db backendupdate_db() { verify_ca_init "$EASYRSA_OPENSSL" ca -utf8 -updatedb -config "$EASYRSA_SSL_CONF" || die "\Failed to perform update-db: see above for related openssl errors." return 0} # => update_db()# display cert DN info on a req/X509, passed by full pathnamedisplay_dn() { local format="$1" path="$2" print "$("$EASYRSA_OPENSSL" $format -in "$path" -noout -subject -nameopt multiline)"} # => display_dn()# generate default SAN from req/X509, passed by full pathnamedefault_server_san() { local path="$1" local cn=$( "$EASYRSA_OPENSSL" req -in "$path" -noout -subject -nameopt sep_multiline | awk -F'=' '/^ *CN=/{print $2}' ) echo "$cn" | egrep -q '^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$' if [[ $? -eq 0 ]] then print "subjectAltName = IP:$cn" else print "subjectAltName = DNS:$cn" fi} # => default_server_san()# verify a file seems to be a valid req/X509verify_file() { local format="$1" path="$2" "$EASYRSA_OPENSSL" $format -in "$path" -noout 2>/dev/null || return 1 return 0} # => verify_file()# show-* command backend# Prints req/cert details in a readable formatshow() { local type="$1" name="$2" in_file format [ -n "$name" ] || die "\Missing expected filename_base argument.Run easyrsa without commands for usage help." shift 2 # opts support local opts="-${type}opt no_pubkey,no_sigdump" while [ -n "$1" ]; do case "$1" in full) opts= ;; *) warn "Ignoring unknown command option: '$1'" ;; esac shift done # Determine cert/req type if [ "$type" = "cert" ]; then verify_ca_init in_file="$EASYRSA_PKI/issued/${name}.crt" format="x509" else verify_pki_init in_file="$EASYRSA_PKI/reqs/${name}.req" format="req" fi # Verify file exists and is of the correct type [ -f "$in_file" ] || die "\No such $type file with a basename of '$name' is present.Expected to find this file at:$in_file" verify_file $format "$in_file" || die "\This file is not a valid $type file:$in_file" notice "\Showing $type details for '$name'.This file is stored at:$in_file" "$EASYRSA_OPENSSL" $format -in "$in_file" -noout -text\ -nameopt multiline $opts || die "\OpenSSL failure to process the input"} # => show()# vars setup# Here sourcing of 'vars' if present occurs. If not present, defaults are used# to support running without a sourced config formatvars_setup() { # Try to locate a 'vars' file in order of location preference. # If one is found, source it local vars= # set up program path local prog_vars="${0%/*}/vars" # set up PKI path local pki_vars="${EASYRSA_PKI:-$PWD/pki}/vars" # command-line path: if [ -f "$EASYRSA_VARS_FILE" ]; then vars="$EASYRSA_VARS_FILE" # PKI location, if present: elif [ -f "$pki_vars" ]; then vars="$pki_vars" # EASYRSA, if defined: elif [ -n "$EASYRSA" ] && [ -f "$EASYRSA/vars" ]; then vars="$EASYRSA/vars" # program location: elif [ -f "$prog_vars" ]; then vars="$prog_vars" fi # If a vars file was located, source it # If $EASYRSA_NO_VARS is defined (not blank) this is skipped if [ -z "$EASYRSA_NO_VARS" ] && [ -n "$vars" ]; then EASYRSA_CALLER=1 . "$vars" notice "\Note: using Easy-RSA configuration from: $vars" fi # Set defaults, preferring existing env-vars if present set_var EASYRSA "${0%/*}" set_var EASYRSA_OPENSSL openssl set_var EASYRSA_PKI "$PWD/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 me@example- set_var EASYRSA_REQ_OU "My Organizational Unit" set_var EASYRSA_ALGO rsa set_var EASYRSA_KEY_SIZE 2048 set_var EASYRSA_CURVE secp384r1 set_var EASYRSA_EC_DIR "$EASYRSA_PKI/ecparams" set_var EASYRSA_CA_EXPIRE 3650 set_var EASYRSA_CERT_EXPIRE 3650 set_var EASYRSA_CRL_DAYS 180 set_var EASYRSA_NS_SUPPORT no set_var EASYRSA_NS_COMMENT "Easy-RSA Generated Certificate" set_var EASYRSA_TEMP_FILE "$EASYRSA_PKI/extensions.temp" set_var EASYRSA_TEMP_FILE_2 "" set_var EASYRSA_TEMP_FILE_3 "" set_var EASYRSA_REQ_CN ChangeMe set_var EASYRSA_DIGEST sha256 # Detect openssl config, preferring EASYRSA_PKI over EASYRSA if [ -f "$EASYRSA_PKI/openssl-1.0-f" ]; then set_var EASYRSA_SSL_CONF "$EASYRSA_PKI/openssl-1.0-f" else set_var EASYRSA_SSL_CONF "$EASYRSA/openssl-1.0-f" fi # Same as above for the x509-types extensions dir if [ -d "$EASYRSA_PKI/x509-types" ]; then set_var EASYRSA_EXT_DIR "$EASYRSA_PKI/x509-types" else set_var EASYRSA_EXT_DIR "$EASYRSA/x509-types" fi # EASYRSA_ALGO_PARAMS must be set depending on selected algo if [ "ec" = "$EASYRSA_ALGO" ]; then EASYRSA_ALGO_PARAMS="$EASYRSA_EC_DIR/${EASYRSA_CURVE}.pem" elif [ "rsa" = "$EASYRSA_ALGO" ]; then EASYRSA_ALGO_PARAMS="${EASYRSA_KEY_SIZE}" else die "Alg '$EASYRSA_ALGO' is invalid: must be 'rsa' or 'ec'" fi # Setting OPENSSL_CONF prevents bogus warnings (especially useful on win32) export OPENSSL_CONF="$EASYRSA_SSL_CONF"} # vars_setup()# variable assignment by indirection when undefined; merely exports# the variable when it is already defined (even if currently null)# Sets $1 as the value contained in $2 and exports (may be blank)set_var() { local var=$1 shift local value="$*" eval "export $var=\"\${$var-$value}\""} #=> set_var()######################################### Invocation entry point:NL=''# Be secure with a restrictive umask[ -z "$EASYRSA_NO_UMASK" ] && umask 077# Parse optionswhile :; do # Separate option from value: opt="${1%%=*}" val="${1#*=}" empty_ok= # Empty values are not allowed unless excepted case "$opt" in --days) export EASYRSA_CERT_EXPIRE="$val" export EASYRSA_CA_EXPIRE="$val" export EASYRSA_CRL_DAYS="$val" ;; --pki-dir) export EASYRSA_PKI="$val" ;; --use-algo) export EASYRSA_ALGO="$val" ;; --keysize) export EASYRSA_KEY_SIZE="$val" ;; --curve) export EASYRSA_CURVE="$val" ;; --dn-mode) export EASYRSA_DN="$val" ;; --req-cn) export EASYRSA_REQ_CN="$val" ;; --digest) export EASYRSA_DIGEST="$val" ;; --req-c) empty_ok=1 export EASYRSA_REQ_COUNTRY="$val" ;; --req-st) empty_ok=1 export EASYRSA_REQ_PROVINCE="$val" ;; --req-city) empty_ok=1 export EASYRSA_REQ_CITY="$val" ;; --req-org) empty_ok=1 export EASYRSA_REQ_ORG="$val" ;; --req-email) empty_ok=1 export EASYRSA_REQ_EMAIL="$val" ;; --req-ou) empty_ok=1 export EASYRSA_REQ_OU="$val" ;; --ns-cert) export EASYRSA_NS_SUPPORT="$val" ;; --ns-comment) empty_ok=1 export EASYRSA_NS_COMMENT="$val" ;; --batch) empty_ok=1 export EASYRSA_BATCH=1 ;; --subca-len) export EASYRSA_SUBCA_LEN="$val" ;; --vars) export EASYRSA_VARS_FILE="$val" ;; --subject-alt-name) export EASYRSA_EXTRA_EXTS="\$EASYRSA_EXTRA_EXTSsubjectAltName = $val" ;; *) break ;; esac # fatal error when no value was provided if [ ! $empty_ok ] && { [ "$val" = "$1" ] || [ -z "$val" ]; }; then die "Missing value to option: $opt" fi shiftdone# Intelligent env-var detection and auto-loading:vars_setup# Register clean_temp on EXITtrap "clean_temp" EXIT# determine how we were called, then hand off to the function responsiblecmd="$1"[ -n "$1" ] && shift # scrape off commandcase "$cmd" in init-pki|clean-all) init_pki "$@" ;; build-ca) build_ca "$@" ;; gen-dh) gen_dh ;; gen-req) gen_req "$@" ;; sign|sign-req) sign_req "$@" ;; build-client-full) build_full client "$@" ;; build-server-full) build_full server "$@" ;; gen-crl) gen_crl ;; revoke) revoke "$@" ;; import-req) import_req "$@" ;; export-p12) export_pkcs p12 "$@" ;; export-p7) export_pkcs p7 "$@" ;; set-rsa-pass) set_pass rsa "$@" ;; set-ec-pass) set_pass ec "$@" ;; update-db) update_db ;; show-req) show req "$@" ;; show-cert) show cert "$@" ;; ""|help|-h|--help|--usage) cmd_help "$1" exit 0 ;; *) die "Unknown command '$cmd'. Run without commands for usage help." ;;esac# vim: ft=sh nu ai sw=8 ts=8 noet[root@localhost ~]#

我们还将消除OpenSSL配置由于版本无法检测而无法加载的可能性。我们将通过复制所需的配置文件并删除版本号来做到这一点。

[root@localhost ~]# cp /etc/openvpn/easy-rsa/3.0/openssl-1.0-f /etc/openvpn/easy-rsa/openssl-f[root@localhost ~]# #要开始生成密钥和证书,我们需要移动到新变量中的Easy-rsa目录和源代码中

步骤四:路由

为了保持简单,我们将直接使用iptable来进行路由,而不是使用新的Firewalld。

[root@localhost 3.0]# yum install iptables-services -y已加载插件:fastestmirror, langpacksLoading mirror speeds from cached hostfile * base: mirrors.aliyun.com * epel: mirrors.aliyun.com * extras: mirrors-99.com * updates: mirrors.huaweicloud.com正在解决依赖关系--> 正在检查事务---> 软件包 iptables-services.x86_64.0.1.4.21-24.1.el7_5 将被 安装--> 解决依赖关系完成依赖关系解决======================================================================================================================= Package 架构 版本 源 大小=======================================================================================================================正在安装: iptables-services x86_64 1.4.21-24.1.el7_5 updates 51 k事务概要=======================================================================================================================安装 1 软件包总-量:51 k安装大小:25 kDownloading packages:iptables-services-1.4.21-24.1.el7_5.x86_64.rpm | 51 kB 00:00:01 Running transaction checkRunning transaction testTransaction test succeededRunning transaction 正在安装 : iptables-services-1.4.21-24.1.el7_5.x86_64 1/1 验证中 : iptables-services-1.4.21-24.1.el7_5.x86_64 1/1 已安装: iptables-services.x86_64 0:1.4.21-24.1.el7_5 完毕![root@localhost 3.0]#

[root@localhost 3.0]# systemctl mask firewalldCreated symlink from /etc/systemd/system/firewalld.service to /dev/null.[root@localhost 3.0]# systemctl enable iptablesCreated symlink from /etc/systemd/system/basic.target.wants/iptables.service to /usr/lib/systemd/system/iptables.service.[root@localhost 3.0]# systemctl stop firewalld[root@localhost 3.0]# systemctl start iptables[root@localhost 3.0]# iptables --flush[root@localhost 3.0]#

接下来,我们将向iptable添加一个规则,将路由转发到openvpn子网,并保存此规则。

[root@localhost 3.0]# iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE[root@localhost 3.0]# iptables-save > /etc/sysconfig/iptables[root@localhost 3.0]# vim /etc/sysctl.conf # net.ipv4.ip_forward = 1[root@localhost 3.0]#

[root@localhost 3.0]# iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE[root@localhost 3.0]# iptables-save > /etc/sysconfig/iptables[root@localhost 3.0]# vim /etc/sysctl.conf [root@localhost 3.0]# systemctl restart networknetwork-online.target network.service [root@localhost 3.0]# systemctl restart networknetwork-online.target network.service [root@localhost 3.0]# systemctl restart networknetwork-online.target network.service [root@localhost 3.0]# systemctl restart network.service

我这里采用虚拟机配置,不知怎么的用不了了。。下次在阿里云服务器重新测试一下。。。

作者:​​前方、有光​​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:HTML+CSS基础知识(2)选择器的使用、盒子模型的讲解、列表的使用
下一篇:实现日历签到功能,操作步骤详解(日历签到app)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~