1
0
mirror of synced 2024-11-25 22:36:04 +03:00
setup-ipsec-vpn/extras/ikev2setup.sh
2021-01-03 00:35:24 -06:00

605 lines
16 KiB
Bash

#!/bin/bash
#
# Script to set up IKEv2 on Ubuntu, Debian, CentOS/RHEL and Amazon Linux 2
#
# The latest version of this script is available at:
# https://github.com/hwdsl2/setup-ipsec-vpn
#
# Copyright (C) 2020-2021 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
SYS_DT=$(date +%F-%T | tr ':' '_')
exiterr() { echo "Error: $1" >&2; exit 1; }
bigecho() { echo; echo "## $1"; echo; }
bigecho2() { echo; echo "## $1"; }
check_ip() {
IP_REGEX='^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
printf '%s' "$1" | tr -d '\n' | grep -Eq "$IP_REGEX"
}
check_dns_name() {
FQDN_REGEX='^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'
printf '%s' "$1" | tr -d '\n' | grep -Eq "$FQDN_REGEX"
}
new_client() {
bigecho2 "Generating client certificate..."
sleep $((RANDOM % 3 + 1))
certutil -z <(head -c 1024 /dev/urandom) \
-S -c "IKEv2 VPN CA" -n "$client_name" \
-s "O=IKEv2 VPN,CN=$client_name" \
-k rsa -g 4096 -v "$client_validity" \
-d sql:/etc/ipsec.d -t ",," \
--keyUsage digitalSignature,keyEncipherment \
--extKeyUsage serverAuth,clientAuth -8 "$client_name" >/dev/null || exit 1
if [ "$export_ca" = "1" ]; then
bigecho "Exporting CA certificate 'IKEv2 VPN CA'..."
if [ "$in_container" = "0" ]; then
certutil -L -d sql:/etc/ipsec.d -n "IKEv2 VPN CA" -a -o ~/"ikev2vpnca-$SYS_DT.cer" || exit 1
else
certutil -L -d sql:/etc/ipsec.d -n "IKEv2 VPN CA" -a -o "/etc/ipsec.d/ikev2vpnca-$SYS_DT.cer" || exit 1
fi
fi
bigecho "Exporting .p12 file..."
cat <<'EOF'
Enter a *secure* password to protect the exported .p12 file.
This file contains the client certificate, private key, and CA certificate.
When importing into an iOS or macOS device, this password cannot be empty.
EOF
if [ "$in_container" = "0" ]; then
pk12util -d sql:/etc/ipsec.d -n "$client_name" -o ~/"$client_name-$SYS_DT.p12" || exit 1
else
pk12util -d sql:/etc/ipsec.d -n "$client_name" -o "/etc/ipsec.d/$client_name-$SYS_DT.p12" || exit 1
fi
}
ikev2setup() {
if [ "$(id -u)" != 0 ]; then
exiterr "Script must be run as root. Try 'sudo bash $0'"
fi
ipsec_ver=$(/usr/local/sbin/ipsec --version 2>/dev/null)
swan_ver=$(printf '%s' "$ipsec_ver" | sed -e 's/Linux //' -e 's/Libreswan //' -e 's/ (netkey) on .*//')
if ( ! grep -qs "hwdsl2 VPN script" /etc/sysctl.conf && ! grep -qs "hwdsl2" /opt/src/run.sh ) \
|| ! printf '%s' "$ipsec_ver" | grep -q "Libreswan" \
|| [ ! -f /etc/ppp/chap-secrets ] || [ ! -f /etc/ipsec.d/passwd ]; then
cat 1>&2 <<'EOF'
Error: Your must first set up the IPsec VPN server before setting up IKEv2.
See: https://github.com/hwdsl2/setup-ipsec-vpn
EOF
exit 1
fi
in_container=0
if grep -qs "hwdsl2" /opt/src/run.sh; then
in_container=1
fi
case "$swan_ver" in
3.19|3.2[01235679]|3.3[12]|4.1)
/bin/true
;;
*)
cat 1>&2 <<EOF
Error: Libreswan version '$swan_ver' is not supported.
This script requires one of these versions:
3.19-3.23, 3.25-3.27, 3.29, 3.31-3.32 or 4.1
To upgrade Libreswan, see:
https://github.com/hwdsl2/setup-ipsec-vpn#upgrade-libreswan
EOF
exit 1
;;
esac
command -v certutil >/dev/null 2>&1 || exiterr "'certutil' not found. Abort."
command -v pk12util >/dev/null 2>&1 || exiterr "'pk12util' not found. Abort."
if grep -qs "conn ikev2-cp" /etc/ipsec.conf || [ -f /etc/ipsec.d/ikev2.conf ]; then
echo "It looks like IKEv2 has already been set up on this server."
printf "Do you want to add a new VPN client? [y/N] "
read -r response
case $response in
[yY][eE][sS]|[yY])
echo
;;
*)
echo "Abort. No changes were made."
exit 1
;;
esac
echo "Provide a name for the IKEv2 VPN client."
echo "Use one word only, no special characters except '-' and '_'."
read -rp "Client name: " client_name
while [ -z "$client_name" ] || [ "${#client_name}" -gt "64" ] \
|| printf '%s' "$client_name" | LC_ALL=C grep -q '[^A-Za-z0-9_-]\+' \
|| certutil -L -d sql:/etc/ipsec.d -n "$client_name" >/dev/null 2>&1; do
if [ -z "$client_name" ] || [ "${#client_name}" -gt "64" ] \
|| printf '%s' "$client_name" | LC_ALL=C grep -q '[^A-Za-z0-9_-]\+'; then
echo "Invalid client name."
else
echo "Invalid client name. Client '$client_name' already exists."
fi
read -rp "Client name: " client_name
done
echo
echo "Specify the validity period (in months) for this VPN client certificate."
read -rp "Enter a number between 1 and 120: [120] " client_validity
[ -z "$client_validity" ] && client_validity=120
while printf '%s' "$client_validity" | LC_ALL=C grep -q '[^0-9]\+' \
|| [ "$client_validity" -lt "1" ] || [ "$client_validity" -gt "120" ] \
|| [ "$client_validity" != "$((10#$client_validity))" ]; do
echo "Invalid validity period."
read -rp "Enter a number between 1 and 120: [120] " client_validity
[ -z "$client_validity" ] && client_validity=120
done
echo
echo "The CA certificate was exported during initial IKEv2 setup. Required for iOS clients only."
printf "Do you want to export the CA certificate again? [y/N] "
read -r response
case $response in
[yY][eE][sS]|[yY])
export_ca=1
;;
*)
export_ca=0
;;
esac
# Create client configuration
new_client
cat <<EOF
=============================================================
New IKEv2 VPN client "$client_name" added!
Client configuration is available at:
EOF
if [ "$in_container" = "0" ]; then
printf '%s\n' ~/"$client_name-$SYS_DT.p12"
if [ "$export_ca" = "1" ]; then
printf '%s\n' ~/"ikev2vpnca-$SYS_DT.cer (for iOS clients)"
fi
else
printf '%s\n' "/etc/ipsec.d/$client_name-$SYS_DT.p12"
if [ "$export_ca" = "1" ]; then
printf '%s\n' "/etc/ipsec.d/ikev2vpnca-$SYS_DT.cer (for iOS clients)"
fi
fi
cat <<'EOF'
Next steps: Configure IKEv2 VPN clients. See:
https://git.io/ikev2clients
To add more IKEv2 VPN clients, run this script again.
=============================================================
EOF
exit 0
fi
if certutil -L -d sql:/etc/ipsec.d -n "IKEv2 VPN CA" >/dev/null 2>&1; then
exiterr "Certificate 'IKEv2 VPN CA' already exists. Abort."
fi
clear
cat <<'EOF'
Welcome! Use this script to set up IKEv2 after setting up your own IPsec VPN server.
Alternatively, you may manually set up IKEv2. See: https://git.io/ikev2
I need to ask you a few questions before starting setup.
You can use the default options and just press enter if you are OK with them.
EOF
echo "Do you want IKEv2 VPN clients to connect to this server using a DNS name,"
printf "e.g. vpn.example.com, instead of its IP address? [y/N] "
read -r response
case $response in
[yY][eE][sS]|[yY])
use_dns_name=1
echo
;;
*)
use_dns_name=0
echo
;;
esac
# Enter VPN server address
if [ "$use_dns_name" = "1" ]; then
read -rp "Enter the DNS name of this VPN server: " server_addr
until check_dns_name "$server_addr"; do
echo "Invalid DNS name. You must enter a fully qualified domain name (FQDN)."
read -rp "Enter the DNS name of this VPN server: " server_addr
done
else
echo "Trying to auto discover IP of this server..."
echo
public_ip=$(dig @resolver1.opendns.com -t A -4 myip.opendns.com +short)
check_ip "$public_ip" || public_ip=$(wget -t 3 -T 15 -qO- http://ipv4.icanhazip.com)
read -rp "Enter the IPv4 address of this VPN server: [$public_ip] " server_addr
[ -z "$server_addr" ] && server_addr="$public_ip"
until check_ip "$server_addr"; do
echo "Invalid IP address."
read -rp "Enter the IPv4 address of this VPN server: [$public_ip] " server_addr
[ -z "$server_addr" ] && server_addr="$public_ip"
done
fi
if certutil -L -d sql:/etc/ipsec.d -n "$server_addr" >/dev/null 2>&1; then
exiterr "Certificate '$server_addr' already exists. Abort."
fi
# Enter client name
echo
echo "Provide a name for the IKEv2 VPN client."
echo "Use one word only, no special characters except '-' and '_'."
read -rp "Client name: [vpnclient] " client_name
[ -z "$client_name" ] && client_name=vpnclient
while [ "${#client_name}" -gt "64" ] \
|| printf '%s' "$client_name" | LC_ALL=C grep -q '[^A-Za-z0-9_-]\+' \
|| certutil -L -d sql:/etc/ipsec.d -n "$client_name" >/dev/null 2>&1; do
if [ "${#client_name}" -gt "64" ] \
|| printf '%s' "$client_name" | LC_ALL=C grep -q '[^A-Za-z0-9_-]\+'; then
echo "Invalid client name."
else
echo "Invalid client name. Client '$client_name' already exists."
fi
read -rp "Client name: [vpnclient] " client_name
[ -z "$client_name" ] && client_name=vpnclient
done
# Enter validity period
echo
echo "Specify the validity period (in months) for this VPN client certificate."
read -rp "Enter a number between 1 and 120: [120] " client_validity
[ -z "$client_validity" ] && client_validity=120
while printf '%s' "$client_validity" | LC_ALL=C grep -q '[^0-9]\+' \
|| [ "$client_validity" -lt "1" ] || [ "$client_validity" -gt "120" ] \
|| [ "$client_validity" != "$((10#$client_validity))" ]; do
echo "Invalid validity period."
read -rp "Enter a number between 1 and 120: [120] " client_validity
[ -z "$client_validity" ] && client_validity=120
done
# Enter custom DNS servers
echo
echo "By default, clients are set to use Google Public DNS when the VPN is active."
printf "Do you want to specify custom DNS servers for IKEv2? [y/N] "
read -r response
case $response in
[yY][eE][sS]|[yY])
use_custom_dns=1
;;
*)
use_custom_dns=0
dns_server_1=8.8.8.8
dns_server_2=8.8.4.4
dns_servers="8.8.8.8 8.8.4.4"
;;
esac
if [ "$use_custom_dns" = "1" ]; then
read -rp "Enter primary DNS server: " dns_server_1
until check_ip "$dns_server_1"; do
echo "Invalid DNS server."
read -rp "Enter primary DNS server: " dns_server_1
done
read -rp "Enter secondary DNS server (Enter to skip): " dns_server_2
until [ -z "$dns_server_2" ] || check_ip "$dns_server_2"; do
echo "Invalid DNS server."
read -rp "Enter secondary DNS server (Enter to skip): " dns_server_2
done
if [ -n "$dns_server_2" ]; then
dns_servers="$dns_server_1 $dns_server_2"
else
dns_servers="$dns_server_1"
fi
else
echo "Using Google Public DNS (8.8.8.8, 8.8.4.4)."
fi
# Check for MOBIKE support
mobike_support=0
case "$swan_ver" in
3.2[35679]|3.3[12]|4.1)
mobike_support=1
;;
esac
if uname -m | grep -qi -e '^arm' -e '^aarch64'; then
mobike_support=0
fi
if [ "$mobike_support" = "1" ]; then
if [ "$in_container" = "0" ]; then
os_type="$(lsb_release -si 2>/dev/null)"
if [ -z "$os_type" ]; then
[ -f /etc/os-release ] && os_type="$(. /etc/os-release && printf '%s' "$ID")"
[ -f /etc/lsb-release ] && os_type="$(. /etc/lsb-release && printf '%s' "$DISTRIB_ID")"
[ "$os_type" = "ubuntu" ] && os_type=Ubuntu
fi
[ -z "$os_type" ] && [ -f /etc/redhat-release ] && os_type=CentOS/RHEL
grep -qs "Amazon Linux release 2" /etc/system-release && os_type=Amzn
# Linux kernels on Ubuntu do not support MOBIKE
if [ -z "$os_type" ] || [ "$os_type" = "Ubuntu" ]; then
mobike_support=0
fi
fi
fi
echo
echo -n "Checking for MOBIKE support... "
if [ "$mobike_support" = "1" ]; then
if [ "$in_container" = "0" ]; then
echo "yes"
else
echo "running in a container, see notes below"
fi
else
echo "no"
fi
mobike_enable=0
if [ "$mobike_support" = "1" ]; then
echo
echo "The MOBIKE IKEv2 extension allows VPN clients to change network attachment points,"
echo "e.g. switch between mobile data and Wi-Fi and keep the IPsec tunnel up on the new IP."
if [ "$in_container" = "0" ]; then
echo
printf "Do you want to enable MOBIKE support? [Y/n] "
read -r response
case $response in
[yY][eE][sS]|[yY]|'')
mobike_enable=1
;;
*)
mobike_enable=0
;;
esac
else
echo
echo "IMPORTANT: *DO NOT* enable MOBIKE support, if your Docker host runs Ubuntu Linux."
printf "Do you want to enable MOBIKE support? [y/N] "
read -r response
case $response in
[yY][eE][sS]|[yY])
mobike_enable=1
;;
*)
mobike_enable=0
;;
esac
fi
fi
cat <<EOF
Below are the IKEv2 setup options you selected.
Please double check before continuing!
================================================
VPN server address: $server_addr
VPN client name: $client_name
EOF
if [ "$client_validity" = "1" ]; then
echo "Client cert valid for: 1 month"
else
echo "Client cert valid for: $client_validity months"
fi
if [ "$mobike_support" = "1" ]; then
if [ "$mobike_enable" = "1" ]; then
echo "MOBIKE support: Enable"
else
echo "MOBIKE support: Disable"
fi
else
echo "MOBIKE support: Not available"
fi
cat <<EOF
DNS server(s): $dns_servers
================================================
EOF
printf "We are ready to set up IKEv2 now. Do you want to continue? [y/N] "
read -r response
case $response in
[yY][eE][sS]|[yY])
echo
;;
*)
echo "Abort. No changes were made."
exit 1
;;
esac
bigecho2 "Generating CA certificate..."
certutil -z <(head -c 1024 /dev/urandom) \
-S -x -n "IKEv2 VPN CA" \
-s "O=IKEv2 VPN,CN=IKEv2 VPN CA" \
-k rsa -g 4096 -v 120 \
-d sql:/etc/ipsec.d -t "CT,," -2 >/dev/null <<ANSWERS || exit 1
y
N
ANSWERS
sleep $((RANDOM % 3 + 1))
bigecho2 "Generating VPN server certificate..."
if [ "$use_dns_name" = "1" ]; then
certutil -z <(head -c 1024 /dev/urandom) \
-S -c "IKEv2 VPN CA" -n "$server_addr" \
-s "O=IKEv2 VPN,CN=$server_addr" \
-k rsa -g 4096 -v 120 \
-d sql:/etc/ipsec.d -t ",," \
--keyUsage digitalSignature,keyEncipherment \
--extKeyUsage serverAuth \
--extSAN "dns:$server_addr" >/dev/null || exit 1
else
certutil -z <(head -c 1024 /dev/urandom) \
-S -c "IKEv2 VPN CA" -n "$server_addr" \
-s "O=IKEv2 VPN,CN=$server_addr" \
-k rsa -g 4096 -v 120 \
-d sql:/etc/ipsec.d -t ",," \
--keyUsage digitalSignature,keyEncipherment \
--extKeyUsage serverAuth \
--extSAN "ip:$server_addr,dns:$server_addr" >/dev/null || exit 1
fi
# Create client configuration
export_ca=1
new_client
echo
bigecho "Adding a new IKEv2 connection..."
if ! grep -qs '^include /etc/ipsec\.d/\*\.conf$' /etc/ipsec.conf; then
echo >> /etc/ipsec.conf
echo 'include /etc/ipsec.d/*.conf' >> /etc/ipsec.conf
fi
cat > /etc/ipsec.d/ikev2.conf <<EOF
conn ikev2-cp
left=%defaultroute
leftcert=$server_addr
leftid=@$server_addr
leftsendcert=always
leftsubnet=0.0.0.0/0
leftrsasigkey=%cert
right=%any
rightid=%fromcert
rightaddresspool=192.168.43.10-192.168.43.250
rightca=%same
rightrsasigkey=%cert
narrowing=yes
dpddelay=30
dpdtimeout=120
dpdaction=clear
auto=add
ikev2=insist
rekey=no
pfs=no
fragmentation=yes
ike=aes256-sha2,aes128-sha2,aes256-sha1,aes128-sha1,aes256-sha2;modp1024,aes128-sha1;modp1024
phase2alg=aes_gcm-null,aes128-sha1,aes256-sha1,aes128-sha2,aes256-sha2
encapsulation=yes
EOF
case "$swan_ver" in
3.2[35679]|3.3[12]|4.1)
if [ -n "$dns_server_2" ]; then
cat >> /etc/ipsec.d/ikev2.conf <<EOF
modecfgdns="$dns_servers"
EOF
else
cat >> /etc/ipsec.d/ikev2.conf <<EOF
modecfgdns=$dns_server_1
EOF
fi
if [ "$mobike_enable" = "1" ]; then
echo " mobike=yes" >> /etc/ipsec.d/ikev2.conf
else
echo " mobike=no" >> /etc/ipsec.d/ikev2.conf
fi
;;
3.19|3.2[012])
if [ -n "$dns_server_2" ]; then
cat >> /etc/ipsec.d/ikev2.conf <<EOF
modecfgdns1=$dns_server_1
modecfgdns2=$dns_server_2
EOF
else
cat >> /etc/ipsec.d/ikev2.conf <<EOF
modecfgdns1=$dns_server_1
EOF
fi
;;
esac
bigecho "Restarting IPsec service..."
mkdir -p /run/pluto
service ipsec restart
cat <<EOF
=============================================================
IKEv2 VPN setup is now complete!
VPN server address: $server_addr
VPN client name: $client_name
Client configuration is available at:
EOF
if [ "$in_container" = "0" ]; then
printf '%s\n' ~/"$client_name-$SYS_DT.p12"
printf '%s\n' ~/"ikev2vpnca-$SYS_DT.cer (for iOS clients)"
else
printf '%s\n' "/etc/ipsec.d/$client_name-$SYS_DT.p12"
printf '%s\n' "/etc/ipsec.d/ikev2vpnca-$SYS_DT.cer (for iOS clients)"
fi
cat <<'EOF'
Next steps: Configure IKEv2 VPN clients. See:
https://git.io/ikev2clients
To add more IKEv2 VPN clients, run this script again.
=============================================================
EOF
}
## Defer setup until we have the complete script
ikev2setup "$@"
exit 0