#!/bin/bash # version="V3.3" # # Run this script via sudo After strongSwan is installed # If you intend to send mail to cert recipients, you'll need an email server and web server # These don't need to be installed before running this script, but doing so may simplify redoes based on incorrect assumptions # function errexit() { echo -e "$1" exit 1 } function readconfig() { # # Reads the config from the makeMyCA config file # # $1: Config file # local rpifun value cf="$1" snolq if [ -f $cf ] then while IFS=":" read -r rpifun value do if [[ ! $rpifun =~ ^\ *# && -n $rpifun ]] # skip comment and malformed lines then # Cant' use stripquotes, it's in sdm-cparse, and can't source it w/o processing this first ($sdmdir) #value="${value%%\#*}" # Del EOL comments value="${value%"${value##*[^[:blank:]]}"}" # Del trailing spaces/tabs snolq="${value#\"}" # Get string without open quote if [ "$snolq" != "$value" ] # if lq was there, then update string and also del close quote then value=$snolq value="${value%\"}" fi snolq="${value#\'}" # Get string without open quote if [ "$snolq" != "$value" ] # Ditto double quote comment then value=$snolq value="${value%\'}" fi printf -v "$rpifun" "%s" "$value" #eval "${rpifun}=\"$value\"" fi done < $cf fi return } function askyn() { local ans echo -n "$1" '[y/N]? ' ; read $2 ans [ "$dbl" != "" ] && echo "" case "$ans" in y*|Y*) return 0 ;; *) return 1 ;; esac } function pressenter() { local ans echo -n "$1" ; read $2 ans } function askdefault () { # $1=prompt, $2=return variable $3=default-for-prompt-plus-default $4=default-if-provided local pmpt=$1 dfl="$3" ddfl="$4" tmp="" echo -n "$pmpt [$dfl]: " ; read tmp [ "$dbl" != "" ] && echo "" if [ "$tmp" == "" ] then [ "$ddfl" != "" ] && tmp="$ddfl" || tmp="$dfl" fi eval "${2}=\"${tmp}\"" # Defines a variable with the return value } function getnicip() { # # returns the IP address of the interface, or "" if there isn't one # local ipaddr="" dev=$1 ipaddr="$(ip -o -f inet address show dev "$dev" 2>/dev/null | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}')" echo ${ipaddr%%/*} return } function isipaddr() { # # Returns true if $1 is an IP address # [[ "$1" =~ ^([0-9]{1,3}[\.]){3}[0-9]{1,3} ]] && return 0 || return 1 return } function issubnet() { # # Returns true if $1 is a subnet # [[ "$1" =~ ^([0-9]{1,3}[\.]){3}0\/[0-9]{1,2}$ ]] && return 0 || return 1 return } function isnic() { # # Returns true if $1 is a network adapter (with an IP address) # [ "$(getnicip $1)" != "" ] && return 0 || return 1 } function isdomain() { local domain="$1" [[ "$1" =~ ^([a-zA-Z0-9](([a-zA-Z0-9-]){0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$ ]] && return 0 || return 1 } function pingcheck() { ping -n -c 1 -w 2 $1 >/dev/null 2>&1 } function emitFirewall() { if [ -f $catables ] then [ -f $catables.bak ] && rm -f $catables.bak mv $catables $catables.bak echo "% Saved existing $catables as $catables.bak" fi cat > $catables <> $catables echo "COMMIT" >> $catables if [ "$vpndev" != "$landev" ] then # Add iptables entries to route between VPNeth and LANeth cat >> $catables < /etc/systemd/system/pistrong-iptables-load.service <> $calog < 1 network device: Use the device that is or will be connected to the Internet either directly or indirectly through a router " askdefault "Network device connected to the Internet" vpndev "eth0" isnic $vpndev && break echo $" ? '$vpndev' is not a valid network device name on this system" done while [ 0 ] do echo $" ** Network device connected to the local LAN If your system has: One network device: Use that device > 1 network device: Use the device that is not '$vpndev' " askdefault "Network device connected to the local LAN" landev "eth0" isnic $landev && break echo $" ? '$landev' is not a valid network device name on this system" done # Get LAN IP address and subnet information from ip command ipline="$(ip -o -f inet addr show dev $landev)" [ "$ipline" == "" ] && errexit "? Cannot determine IP address for network device '$landev'" ipaddr="$(echo $ipline | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}')" # Gets xx.xx.xx.xx/xx myipaddr=${ipaddr%%/*} # Get IP address w/o /xx maskbits=${ipaddr##$myipaddr/} # Get xx from /xx subnetbrd=$(read f1 f2 f3 f4 f5 f6 f7 <<< $(echo "$ipline"); echo $f6) # Get xx.xx.xx.255 mysubnetx=${subnetbrd%.255} # Strip the .255 myfullsubnet="${mysubnetx}.0/$maskbits" # ... and add ".0/xx" # myipaddr=, maskbits=, subnetbrd= # mysubnetx=, myfullsubnet="/xx" echo $" ** VPN Server IP configuration" while [ 0 ] do echo $" ** Local LAN IP Address If your system has: One network device: Use the IP Address of that device > 1 network device: Use the IP Address of '$landev' " askdefault "VPN Server Local LAN IP Address" tmyipaddr "$myipaddr" isipaddr $tmyipaddr && break echo $" ? '$tmyipaddr' is not a valid IP Address" done if [ "$tmyipaddr" != "$myipaddr" ] then # set mysubnetx, myfullsubnet mysubnetx=${tmyipaddr%.*} myfullsubnet="${mysubnetx}.0/24" #This is not perfectly correct myipaddr=$tmyipaddr fi # while [ 0 ] do echo $" ** Local LAN Subnet If your system has: One network device: Use the Subnet of that device > 1 network device: Use the Subnet of '$landev' " askdefault "VPN Server Local LAN Subnet" mysubnet "$myfullsubnet" issubnet $mysubnet && break echo $" ? '$mysubnet' is not a valid LAN Subnet" done myfullsubnet=$mysubnet if [ "$vpndev" != "$landev" ] then ipline="$(ip -o -f inet addr show | grep $vpndev)" [ "$ipline" == "" ] && errexit "? Network device '$vpndev' does not have an IP Address" emyipaddr="$(echo $ipline | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}')" emyipaddr=${emyipaddr%%/*} while [ 0 ] do echo $" ** VPN Server Internet-connected network device ($vpndev) IP address Enter the IP Address for the network device connected to the Internet ($vpndev) " askdefault "VPN Server Internet-connected network device ($vpndev) IP address" emyipaddr "$emyipaddr" isipaddr $emyipaddr && break echo $" ? '$emyipaddr' is not a valid IP Address" done else emyipaddr=$myipaddr fi while [ 0 ] do echo $" ** The Subnet for VPN Clients is a virtual subnet used between the VPN Server and Clients It must not be used anywhere on your network and requires iptables configuration You must enable the iptables settings created by this script See https://github.com/gitbls/pistrong/blob/master/README.md#firewall-considerations " askdefault "Subnet for VPN Clients" vpnsubnet "10.1.10.0/24" issubnet $vpnsubnet && break echo $" ? '$vpnsubnet' is not a valid subnet for VPN Clients" done while [ 0 ] do # Default to subnet.1 for DNS, since that's where most people's routers are echo $" ** The DNS Server should be a DNS Server on your network This can be your router (if so configured) or another system on your LAN with a DNS Server " askdefault "DNS Server IP Address for connected VPN Clients" vpndns "${mysubnetx}.1" isipaddr $vpndns && break echo $" ? '$vpndns' is not a valid IP Address" done echo $" ** Common Name (CN) string only used in user certs (e.g., username-device-$thishost@cnsuffix) The CN is used for additional Cert validation and will be visible in the VPN connection log Users will need to type this once when configuring a VPN Client The default is fine, but can be changed if desired If you decide to change it later you will need to rebuild the CA and all Certs " askdefault "Common Name (CN) suffix for your CA" cnsuffix "myvpn.net" echo $" ** File system directory where certs will be shared via your web server Used if/when emailing certs to users with a link to certs instead of a zip file If you are not going to email links to certs, just press Enter This setting can be changed later with 'sudo pistrong config --webdir' " askdefault "webdir for your system" webdir "$webdir" #[ ! -d $webdir ] && echo "% Directory $webdir does not exist. Be sure to create it before using pistrong --mail" echo $" ** URL that will be used in emails to users with their certs Used if/when emailing certs to users with a link to certs in '$webdir' If you are not going to email links to certs, just press Enter This setting can be changed later with 'sudo pistrong config --weburl' " askdefault "weburl for your system" weburl "$weburl" echo $" ** VPN Cert name prefix and SAN Key (remoteid) for Android, iOS/MacOS, Linux, and Windows users The SAN Key used to identify the connecting OS type (Android, iOS/MacOS, Linux, Windows) You assign a SAN Key (remoteid) when adding user Certs The VPN Cert name prefix can be helpful during troubleshooting The defaults are fine, but you may change them if you wish If you decide to change these later you will need to rebuild the CA and all Certs " askdefault "VPN Cert name prefix for Android VPN Cert" android "$android" askdefault "VPN SAN key for Android users" androidkey "$androidkey" askdefault "VPN Cert name prefix for iOS/MacOS VPN Cert" ios "$ios" askdefault "VPN SAN key for iOS/MacOS users" ioskey "$ioskey" askdefault "VPN Cert name prefix for Linux VPN Cert" linux "$linux" askdefault "VPN SAN key for Linux users" linuxkey "$linuxkey" askdefault "VPN Cert name prefix for Windows VPN Cert" windows "$windows" # Get Windows VPN SAN Key even though Windows IPSec stack as of Win10 1903 doesn't use it askdefault "VPN SAN key for Windows users" winkey "$winkey" echo "" isipaddr "$vpnaddr" && dsan="$vpnaddr,$thisfqdn" || dsan="$vpnaddr" echo $"** Default secondary VPN SAN Key(s): '$dsan' You can add additional SAN Keys if desired Precede the string with a '%' (e.g., %newkey.foo.com) to replace the entire string rather than appending the key(s) to '$dsan' Additional VPN SAN keys beyond the defaults are required only for certain advanced configurations If you don't understand what VPN SAN keys are, simply press Enter " askdefault "Additional Secondary VPN SAN Key(s)" san2 "" [ "$san2" == "" ] && san2x="$dsan" || san2x="$dsan,$san2" # If prefixed with "%" strip it and replace san key [ "$san2" != "${san2#%}" ] && san2x=${san2#%} else # Process config file [ -f $cf ] || errexit "? makeMyCA Configuration file '$cf' not found" echo "> Load CA configuration from '$cf'" readconfig $cf thisfqdn="$thishost.$thisdomain" isipaddr "$vpnaddr" && dsan="$vpnaddr,$thisfqdn" || dsan="$vpnaddr" [ "$san2" == "" ] && san2x="$dsan" || san2x="$dsan,$san2" [ "$emyipaddr" == "" ] && emyipaddr=$myipaddr mysubnetx="${myipaddr%.*}" # Strip 4th octed myfullsubnet="${mysubnetx}.0/24" androidkey="android.$cnsuffix" ioskey="ios.$cnsuffix" linuxkey="linux.$cnsuffix" winkey="windows.$cnsuffix" fi echo $" ** This script will create: Connection Config: $caconf CA Cert: strongSwanCACert.pem VPN Cert for Android: $android-strongSwanVPNCert.pem with VPN SAN key '$androidkey' VPN Cert for iOS/MacOS: $ios-strongSwanVPNCert.pem with VPN SAN key '$ioskey' VPN Cert for Linux: $linux-strongSwanVPNCert.pem with VPN SAN key '$linuxkey' VPN Cert for Windows: $windows-strongSwanVPNCert.pem with VPN SAN key '$winkey' For VPN Server LAN IP $myipaddr subnet $myfullsubnet with VPN Client DNS servers $vpndns The VPN will use virtual subnet $vpnsubnet for VPN Clients Each VPN Cert will have '$san2x' as secondary VPN SAN key(s) The default VPN Server Address is '$vpnaddr' This can be changed on a per-user basis on the 'sudo pistrong add' command for a single user using the --vpnaddr new-vpn-address switch You might want to do this, for example, if you're using an IP address to connect externally but also want to test connecting to the VPN server from your internal network See https://github.com/gitbls/pistrong/blob/master/README.md for additional VPN Server IP Address considerations" isipaddr $vpnaddr && echo $" NOTE: You have set your VPN Server Address to '$vpnaddr', which is an IP Address rather than a DNS name. Your VPN WILL fail if the external IP Address of your VPN Server ever changes" echo $" See 'sudo pistrong config --list' for a list of all the pistrong configuration parameters and make any changes necessary for your configuration " writemakeCAlog if [ "$cf" == "" ] then echo $"** If you'd like to change anything, answer N (or Enter) to the next question and restart the script " if ! askyn "Do you want to continue" then exit fi fi # # Set hostname in apache config as a convenience, and make sure that the $webdir directory exists # if [ -d /etc/apache2/conf-enabled ] then if [ ! -f /etc/apache2/conf-enabled/servername.conf ] then echo "ServerName $thisfqdn" > /etc/apache2/conf-enabled/servername.conf fi [ ! -d $webdir ] && mkdir -p $webdir fi echo "Updating pistrong configuration..." pistrong config --myfqdn $thisfqdn pistrong config --vpnaddr $vpnaddr pistrong config --cnsuffix $cnsuffix --mailfrom "PiStrongVPN" --smtpserver 127.0.0.1 pistrong config --webdir $webdir --weburl $weburl # This sets the default SAN Key (remoteid) for the add command pistrong config --vpnsankey $ioskey --vpncertpfx $ios echo "Building the CA and VPN Certs..." # Make the CA and VPN certs (one for each: iOS, Windows, and Linux) pistrong createca --nocamsg --novpncert pistrong makevpncert $android --vpnsankey $androidkey,$san2x pistrong makevpncert $ios --vpnsankey $ioskey,$san2x pistrong makevpncert $linux --vpnsankey $linuxkey,$san2x pistrong makevpncert $windows --vpnsankey $winkey,$san2x echo "Creating '$catables' with firewall rules" emitFirewall echo "" echo "Creating '$caconf' with the new VPN Server definitions" updowns="/usr/lib/ipsec/_updown" # If $updowns not found, strongSwan was built from tarball, so use alternate location [ ! -f $updowns ] && updowns="/libexec/ipsec/_updown" [ ! -f $updowns ] && echo $" % Cannot find ${updowns}; Please file a ticket on the pistrong github " cat > $caconf <