#!/bin/bash # # Script to set up IKEv2 on Ubuntu, Debian and CentOS/RHEL # # Copyright (C) 2020 Lin Song # # 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) 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" } 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]) /bin/true ;; *) cat 1>&2 </dev/null 2>&1 || { echo >&2 "Error: Command 'certutil' not found. Abort."; exit 1; } command -v pk12util >/dev/null 2>&1 || { echo >&2 "Error: Command 'pk12util' not found. Abort."; exit 1; } 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. Use one word only, no special characters." 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 echo "Invalid client name." read -rp "Client name: " client_name done echo echo "The CA certificate was exported during initial IKEv2 setup. It is needed for iOS clients." 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 bigecho2 "Generating client certificate..." 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 120 \ -d sql:/etc/ipsec.d -t ",," \ --keyUsage digitalSignature,keyEncipherment \ --extKeyUsage serverAuth,clientAuth -8 "$client_name" >/dev/null if [ "$export_ca" = "1" ]; then bigecho "Exporting CA certificate..." if [ "$in_container" = "0" ]; then certutil -L -d sql:/etc/ipsec.d -n "IKEv2 VPN CA" -a -o ~/"vpnca-$SYS_DT.cer" else certutil -L -d sql:/etc/ipsec.d -n "IKEv2 VPN CA" -a -o "/etc/ipsec.d/vpnca-$SYS_DT.cer" 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" else pk12util -d sql:/etc/ipsec.d -n "$client_name" -o "/etc/ipsec.d/$client_name-$SYS_DT.p12" fi cat <> /etc/ipsec.conf echo 'include /etc/ipsec.d/*.conf' >> /etc/ipsec.conf fi cat > /etc/ipsec.d/ikev2.conf <> /etc/ipsec.d/ikev2.conf <<'EOF' modecfgdns="8.8.8.8 8.8.4.4" encapsulation=yes EOF 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]) cat >> /etc/ipsec.d/ikev2.conf <<'EOF' modecfgdns1=8.8.8.8 modecfgdns2=8.8.4.4 encapsulation=yes EOF ;; 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 y N ANSWERS sleep 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 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 fi sleep 1 bigecho2 "Generating client certificate..." 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 120 \ -d sql:/etc/ipsec.d -t ",," \ --keyUsage digitalSignature,keyEncipherment \ --extKeyUsage serverAuth,clientAuth -8 "$client_name" >/dev/null bigecho "Exporting CA certificate..." if [ "$in_container" = "0" ]; then certutil -L -d sql:/etc/ipsec.d -n "IKEv2 VPN CA" -a -o ~/"vpnca-$SYS_DT.cer" else certutil -L -d sql:/etc/ipsec.d -n "IKEv2 VPN CA" -a -o "/etc/ipsec.d/vpnca-$SYS_DT.cer" 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" else pk12util -d sql:/etc/ipsec.d -n "$client_name" -o "/etc/ipsec.d/$client_name-$SYS_DT.p12" fi bigecho "Restarting IPsec service..." mkdir -p /run/pluto service ipsec restart cat <