3ca9af7858
Wget writes out a zero-byte file on certain failures such as 404. We should check its exit code instead of checking whether the file exists.
416 lines
13 KiB
Bash
416 lines
13 KiB
Bash
#!/bin/sh
|
|
#
|
|
# Script for automatic setup of an IPsec/L2TP VPN server on Ubuntu LTS and Debian 8.
|
|
# Works on dedicated servers and any KVM- or Xen-based Virtual Private Server (VPS).
|
|
#
|
|
# DO NOT RUN THIS SCRIPT ON YOUR PC OR MAC! THIS IS MEANT TO BE RUN
|
|
# ON YOUR DEDICATED SERVER OR VPS!
|
|
#
|
|
# Copyright (C) 2014-2016 Lin Song
|
|
# Based on the work of Thomas Sarlandie (Copyright 2012)
|
|
#
|
|
# 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!
|
|
|
|
# ------------------------------------------------------------
|
|
|
|
# Define your own values for these variables
|
|
# - All values MUST be quoted using 'single quotes'
|
|
# - DO NOT use these characters inside values: \ " '
|
|
|
|
IPSEC_PSK='your_ipsec_pre_shared_key'
|
|
VPN_USER='your_vpn_username'
|
|
VPN_PASSWORD='your_very_secure_password'
|
|
|
|
# Please read IMPORTANT NOTES at:
|
|
# https://github.com/hwdsl2/setup-ipsec-vpn#important-notes
|
|
|
|
# ------------------------------------------------------------
|
|
|
|
if [ "$(uname)" = "Darwin" ]; then
|
|
echo 'DO NOT run this script on your Mac! It should only be used on a server.'
|
|
exit 1
|
|
fi
|
|
|
|
os_type="$(lsb_release -si 2>/dev/null)"
|
|
if [ "$os_type" != "Ubuntu" ] && [ "$os_type" != "Debian" ]; then
|
|
echo "This script only supports Ubuntu or Debian systems."
|
|
exit 1
|
|
fi
|
|
|
|
if [ -f /proc/user_beancounters ]; then
|
|
echo "This script does NOT support OpenVZ VPS."
|
|
echo "Try alternative: https://github.com/Nyr/openvpn-install"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$(id -u)" != 0 ]; then
|
|
echo "Script must be run as root. Try 'sudo sh $0'"
|
|
exit 1
|
|
fi
|
|
|
|
if [ ! -f /sys/class/net/eth0/operstate ]; then
|
|
echo "Network interface 'eth0' is not available. Aborting."
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$IPSEC_PSK" ] || [ -z "$VPN_USER" ] || [ -z "$VPN_PASSWORD" ]; then
|
|
echo "VPN credentials cannot be empty. Edit the script and re-enter."
|
|
exit 1
|
|
fi
|
|
|
|
# Create and change to working dir
|
|
mkdir -p /opt/src
|
|
cd /opt/src || { echo "Failed to change working dir to /opt/src. Aborting."; exit 1; }
|
|
|
|
# Update package index and install Wget and dig (dnsutils)
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
apt-get -y update
|
|
apt-get -y install wget dnsutils
|
|
|
|
echo
|
|
echo 'Trying to find Public/Private IP of this server...'
|
|
echo
|
|
echo 'In case the script hangs here for more than a few minutes, press Ctrl-C to interrupt.'
|
|
echo 'Then edit it and follow instructions to manually enter server IPs.'
|
|
echo
|
|
|
|
# In Amazon EC2, these two variables will be retrieved from metadata.
|
|
# For all other servers, you may replace them with actual IPs,
|
|
# or comment them out to use auto-detection in the next section.
|
|
# If your server only has a public IP, put that IP on both lines.
|
|
PUBLIC_IP=$(wget --retry-connrefused -t 3 -T 15 -qO- 'http://169.254.169.254/latest/meta-data/public-ipv4')
|
|
PRIVATE_IP=$(wget --retry-connrefused -t 3 -T 15 -qO- 'http://169.254.169.254/latest/meta-data/local-ipv4')
|
|
|
|
# Try to determine IPs for non-EC2 servers
|
|
[ -z "$PUBLIC_IP" ] && PUBLIC_IP=$(dig +short myip.opendns.com @resolver1.opendns.com)
|
|
[ -z "$PUBLIC_IP" ] && PUBLIC_IP=$(wget -t 3 -T 15 -qO- http://ipv4.icanhazip.com)
|
|
[ -z "$PUBLIC_IP" ] && PUBLIC_IP=$(wget -t 3 -T 15 -qO- http://ipecho.net/plain)
|
|
[ -z "$PRIVATE_IP" ] && PRIVATE_IP=$(ip -4 route get 1 | awk '{print $NF;exit}')
|
|
[ -z "$PRIVATE_IP" ] && PRIVATE_IP=$(ifconfig eth0 | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*')
|
|
|
|
# Check IPs for correct format
|
|
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])$"
|
|
if ! printf %s "$PUBLIC_IP" | grep -Eq "$IP_REGEX"; then
|
|
echo "Cannot find valid public IP. Edit the script and manually enter."
|
|
exit 1
|
|
fi
|
|
if ! printf %s "$PRIVATE_IP" | grep -Eq "$IP_REGEX"; then
|
|
echo "Cannot find valid private IP. Edit the script and manually enter."
|
|
exit 1
|
|
fi
|
|
|
|
# Install necessary packages
|
|
apt-get -y install libnss3-dev libnspr4-dev pkg-config libpam0g-dev \
|
|
libcap-ng-dev libcap-ng-utils libselinux1-dev \
|
|
libcurl4-nss-dev flex bison gcc make sed \
|
|
libunbound-dev libnss3-tools libevent-dev
|
|
apt-get -y --no-install-recommends install xmlto
|
|
apt-get -y install xl2tpd
|
|
|
|
# Install Fail2Ban to protect SSH
|
|
apt-get -y install fail2ban
|
|
|
|
# Compile and install Libreswan
|
|
SWAN_VER=3.17
|
|
SWAN_FILE="libreswan-${SWAN_VER}.tar.gz"
|
|
SWAN_URL="https://download.libreswan.org/$SWAN_FILE"
|
|
wget -t 3 -T 30 -nv -O "$SWAN_FILE" "$SWAN_URL"
|
|
[ "$?" != "0" ] && { echo "Cannot retrieve Libreswan source file. Aborting."; exit 1; }
|
|
/bin/rm -rf "/opt/src/libreswan-$SWAN_VER"
|
|
tar xvzf "$SWAN_FILE" && /bin/rm -f "$SWAN_FILE"
|
|
cd "libreswan-$SWAN_VER" || { echo "Failed to enter Libreswan source dir. Aborting."; exit 1; }
|
|
# Workaround for Libreswan compile issues
|
|
cat > Makefile.inc.local <<EOF
|
|
WERROR_CFLAGS =
|
|
EOF
|
|
make programs && make install
|
|
|
|
# Check if Libreswan install was successful
|
|
/usr/local/sbin/ipsec --version 2>/dev/null | grep -qs "$SWAN_VER"
|
|
[ "$?" != "0" ] && { echo; echo "Sorry, Libreswan $SWAN_VER failed to build. Aborting."; exit 1; }
|
|
|
|
# Prepare various config files
|
|
# Create IPsec (Libreswan) config
|
|
SYS_DT="$(date +%Y-%m-%d-%H:%M:%S)"
|
|
/bin/cp -f /etc/ipsec.conf "/etc/ipsec.conf.old-$SYS_DT" 2>/dev/null
|
|
cat > /etc/ipsec.conf <<EOF
|
|
version 2.0
|
|
|
|
config setup
|
|
virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:!192.168.42.0/24
|
|
protostack=netkey
|
|
nhelpers=0
|
|
interfaces=%defaultroute
|
|
|
|
conn vpnpsk
|
|
auto=add
|
|
left=$PRIVATE_IP
|
|
leftid=$PUBLIC_IP
|
|
leftsubnet=$PRIVATE_IP/32
|
|
leftnexthop=%defaultroute
|
|
leftprotoport=17/1701
|
|
rightprotoport=17/%any
|
|
right=%any
|
|
rightsubnetwithin=0.0.0.0/0
|
|
forceencaps=yes
|
|
authby=secret
|
|
pfs=no
|
|
type=transport
|
|
auth=esp
|
|
ike=3des-sha1,aes-sha1
|
|
phase2alg=3des-sha1,aes-sha1
|
|
rekey=no
|
|
keyingtries=5
|
|
dpddelay=30
|
|
dpdtimeout=120
|
|
dpdaction=clear
|
|
EOF
|
|
|
|
# Specify IPsec PSK
|
|
/bin/cp -f /etc/ipsec.secrets "/etc/ipsec.secrets.old-$SYS_DT" 2>/dev/null
|
|
cat > /etc/ipsec.secrets <<EOF
|
|
$PUBLIC_IP %any : PSK "$IPSEC_PSK"
|
|
EOF
|
|
|
|
# Create xl2tpd config
|
|
/bin/cp -f /etc/xl2tpd/xl2tpd.conf "/etc/xl2tpd/xl2tpd.conf.old-$SYS_DT" 2>/dev/null
|
|
cat > /etc/xl2tpd/xl2tpd.conf <<EOF
|
|
[global]
|
|
port = 1701
|
|
|
|
[lns default]
|
|
ip range = 192.168.42.10-192.168.42.250
|
|
local ip = 192.168.42.1
|
|
require chap = yes
|
|
refuse pap = yes
|
|
require authentication = yes
|
|
name = l2tpd
|
|
pppoptfile = /etc/ppp/options.xl2tpd
|
|
length bit = yes
|
|
EOF
|
|
|
|
# Specify xl2tpd options
|
|
/bin/cp -f /etc/ppp/options.xl2tpd "/etc/ppp/options.xl2tpd.old-$SYS_DT" 2>/dev/null
|
|
cat > /etc/ppp/options.xl2tpd <<EOF
|
|
ipcp-accept-local
|
|
ipcp-accept-remote
|
|
ms-dns 8.8.8.8
|
|
ms-dns 8.8.4.4
|
|
noccp
|
|
auth
|
|
crtscts
|
|
idle 1800
|
|
mtu 1280
|
|
mru 1280
|
|
lock
|
|
lcp-echo-failure 10
|
|
lcp-echo-interval 60
|
|
connect-delay 5000
|
|
EOF
|
|
|
|
# Create VPN credentials
|
|
/bin/cp -f /etc/ppp/chap-secrets "/etc/ppp/chap-secrets.old-$SYS_DT" 2>/dev/null
|
|
cat > /etc/ppp/chap-secrets <<EOF
|
|
# Secrets for authentication using CHAP
|
|
# client server secret IP addresses
|
|
"$VPN_USER" l2tpd "$VPN_PASSWORD" *
|
|
EOF
|
|
|
|
# Update sysctl settings for VPN and performance
|
|
if ! grep -qs "hwdsl2 VPN script" /etc/sysctl.conf; then
|
|
/bin/cp -f /etc/sysctl.conf "/etc/sysctl.conf.old-$SYS_DT" 2>/dev/null
|
|
cat >> /etc/sysctl.conf <<EOF
|
|
|
|
# Added by hwdsl2 VPN script
|
|
kernel.msgmnb = 65536
|
|
kernel.msgmax = 65536
|
|
kernel.shmmax = 68719476736
|
|
kernel.shmall = 4294967296
|
|
|
|
net.ipv4.ip_forward = 1
|
|
net.ipv4.tcp_syncookies = 1
|
|
net.ipv4.conf.all.accept_source_route = 0
|
|
net.ipv4.conf.default.accept_source_route = 0
|
|
net.ipv4.conf.all.accept_redirects = 0
|
|
net.ipv4.conf.default.accept_redirects = 0
|
|
net.ipv4.conf.all.send_redirects = 0
|
|
net.ipv4.conf.default.send_redirects = 0
|
|
net.ipv4.conf.lo.send_redirects = 0
|
|
net.ipv4.conf.eth0.send_redirects = 0
|
|
net.ipv4.conf.all.rp_filter = 0
|
|
net.ipv4.conf.default.rp_filter = 0
|
|
net.ipv4.conf.lo.rp_filter = 0
|
|
net.ipv4.conf.eth0.rp_filter = 0
|
|
net.ipv4.icmp_echo_ignore_broadcasts = 1
|
|
net.ipv4.icmp_ignore_bogus_error_responses = 1
|
|
|
|
net.core.wmem_max = 12582912
|
|
net.core.rmem_max = 12582912
|
|
net.ipv4.tcp_rmem = 10240 87380 12582912
|
|
net.ipv4.tcp_wmem = 10240 87380 12582912
|
|
EOF
|
|
fi
|
|
|
|
# Create basic IPTables rules. First check if there are existing rules.
|
|
# 1. If IPTables is "empty", write out the new set of rules.
|
|
# 2. If *not* empty, insert new rules and save them together with existing ones.
|
|
if ! grep -qs "hwdsl2 VPN script" /etc/iptables.rules; then
|
|
/bin/cp -f /etc/iptables.rules "/etc/iptables.rules.old-$SYS_DT" 2>/dev/null
|
|
service fail2ban stop >/dev/null 2>&1
|
|
if [ "$(iptables-save | grep -c '^\-')" = "0" ]; then
|
|
cat > /etc/iptables.rules <<EOF
|
|
# Added by hwdsl2 VPN script
|
|
*filter
|
|
:INPUT ACCEPT [0:0]
|
|
:FORWARD ACCEPT [0:0]
|
|
:OUTPUT ACCEPT [0:0]
|
|
-A INPUT -m conntrack --ctstate INVALID -j DROP
|
|
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
-A INPUT -i lo -j ACCEPT
|
|
-A INPUT -d 127.0.0.0/8 -j REJECT
|
|
-A INPUT -p icmp -j ACCEPT
|
|
-A INPUT -p udp --dport 67:68 --sport 67:68 -j ACCEPT
|
|
-A INPUT -p tcp --dport 22 -j ACCEPT
|
|
-A INPUT -p udp -m multiport --dports 500,4500 -j ACCEPT
|
|
-A INPUT -p udp --dport 1701 -m policy --dir in --pol ipsec -j ACCEPT
|
|
-A INPUT -p udp --dport 1701 -j DROP
|
|
-A INPUT -j DROP
|
|
-A FORWARD -m conntrack --ctstate INVALID -j DROP
|
|
-A FORWARD -i eth+ -o ppp+ -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
-A FORWARD -i ppp+ -o eth+ -j ACCEPT
|
|
# If you wish to allow traffic between VPN clients themselves, uncomment this line:
|
|
# -A FORWARD -i ppp+ -o ppp+ -s 192.168.42.0/24 -d 192.168.42.0/24 -j ACCEPT
|
|
-A FORWARD -j DROP
|
|
COMMIT
|
|
*nat
|
|
:PREROUTING ACCEPT [0:0]
|
|
:INPUT ACCEPT [0:0]
|
|
:OUTPUT ACCEPT [0:0]
|
|
:POSTROUTING ACCEPT [0:0]
|
|
-A POSTROUTING -s 192.168.42.0/24 -o eth+ -j SNAT --to-source "$PRIVATE_IP"
|
|
COMMIT
|
|
EOF
|
|
|
|
else
|
|
|
|
iptables -I INPUT 1 -p udp -m multiport --dports 500,4500 -j ACCEPT
|
|
iptables -I INPUT 2 -p udp --dport 1701 -m policy --dir in --pol ipsec -j ACCEPT
|
|
iptables -I INPUT 3 -p udp --dport 1701 -j DROP
|
|
iptables -I FORWARD 1 -m conntrack --ctstate INVALID -j DROP
|
|
iptables -I FORWARD 2 -i eth+ -o ppp+ -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
iptables -I FORWARD 3 -i ppp+ -o eth+ -j ACCEPT
|
|
# iptables -I FORWARD 4 -i ppp+ -o ppp+ -s 192.168.42.0/24 -d 192.168.42.0/24 -j ACCEPT
|
|
iptables -A FORWARD -j DROP
|
|
iptables -t nat -I POSTROUTING -s 192.168.42.0/24 -o eth+ -j SNAT --to-source "$PRIVATE_IP"
|
|
|
|
echo "# Modified by hwdsl2 VPN script" > /etc/iptables.rules
|
|
iptables-save >> /etc/iptables.rules
|
|
fi
|
|
# Update rules for iptables-persistent
|
|
if [ -f /etc/iptables/rules.v4 ]; then
|
|
/bin/cp -f /etc/iptables/rules.v4 "/etc/iptables/rules.v4.old-$SYS_DT"
|
|
/bin/cp -f /etc/iptables.rules /etc/iptables/rules.v4
|
|
fi
|
|
fi
|
|
|
|
# Create basic IP6Tables (IPv6) rules
|
|
if ! grep -qs "hwdsl2 VPN script" /etc/ip6tables.rules; then
|
|
/bin/cp -f /etc/ip6tables.rules "/etc/ip6tables.rules.old-$SYS_DT" 2>/dev/null
|
|
cat > /etc/ip6tables.rules <<EOF
|
|
# Added by hwdsl2 VPN script
|
|
*filter
|
|
:INPUT ACCEPT [0:0]
|
|
:FORWARD DROP [0:0]
|
|
:OUTPUT ACCEPT [0:0]
|
|
-A INPUT -i lo -j ACCEPT
|
|
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
-A INPUT -m rt --rt-type 0 -j DROP
|
|
-A INPUT -s fe80::/10 -j ACCEPT
|
|
-A INPUT -p ipv6-icmp -j ACCEPT
|
|
-A INPUT -j DROP
|
|
COMMIT
|
|
EOF
|
|
if [ -f /etc/iptables/rules.v6 ]; then
|
|
/bin/cp -f /etc/iptables/rules.v6 "/etc/iptables/rules.v6.old-$SYS_DT"
|
|
/bin/cp -f /etc/ip6tables.rules /etc/iptables/rules.v6
|
|
fi
|
|
fi
|
|
|
|
# Load IPTables rules at system boot
|
|
cat > /etc/network/if-pre-up.d/iptablesload <<EOF
|
|
#!/bin/sh
|
|
iptables-restore < /etc/iptables.rules
|
|
exit 0
|
|
EOF
|
|
|
|
cat > /etc/network/if-pre-up.d/ip6tablesload <<EOF
|
|
#!/bin/sh
|
|
ip6tables-restore < /etc/ip6tables.rules
|
|
exit 0
|
|
EOF
|
|
|
|
# Update rc.local to start services at boot
|
|
if ! grep -qs "hwdsl2 VPN script" /etc/rc.local; then
|
|
/bin/cp -f /etc/rc.local "/etc/rc.local.old-$SYS_DT" 2>/dev/null
|
|
sed --follow-symlinks -i -e '/^exit 0/d' /etc/rc.local
|
|
cat >> /etc/rc.local <<EOF
|
|
|
|
# Added by hwdsl2 VPN script
|
|
service fail2ban restart || /bin/true
|
|
service ipsec start
|
|
service xl2tpd start
|
|
echo 1 > /proc/sys/net/ipv4/ip_forward
|
|
exit 0
|
|
EOF
|
|
fi
|
|
|
|
# Initialize Libreswan DB
|
|
if [ ! -f /etc/ipsec.d/cert8.db ] ; then
|
|
echo > /var/tmp/libreswan-nss-pwd
|
|
certutil -N -f /var/tmp/libreswan-nss-pwd -d /etc/ipsec.d
|
|
/bin/rm -f /var/tmp/libreswan-nss-pwd
|
|
fi
|
|
|
|
# Reload sysctl.conf
|
|
sysctl -p
|
|
|
|
# Update file attributes
|
|
chmod +x /etc/rc.local
|
|
chmod +x /etc/network/if-pre-up.d/iptablesload
|
|
chmod +x /etc/network/if-pre-up.d/ip6tablesload
|
|
chmod 600 /etc/ipsec.secrets* /etc/ppp/chap-secrets*
|
|
|
|
# Apply new IPTables rules
|
|
iptables-restore < /etc/iptables.rules
|
|
ip6tables-restore < /etc/ip6tables.rules >/dev/null 2>&1
|
|
|
|
# Restart services
|
|
service fail2ban stop >/dev/null 2>&1
|
|
service ipsec stop >/dev/null 2>&1
|
|
service xl2tpd stop >/dev/null 2>&1
|
|
service fail2ban start
|
|
service ipsec start
|
|
service xl2tpd start
|
|
|
|
echo
|
|
echo '============================================================'
|
|
echo 'IPsec/L2TP VPN server setup is complete!'
|
|
echo
|
|
echo 'Connect to your new VPN with these details:'
|
|
echo
|
|
echo "Server IP: $PUBLIC_IP"
|
|
echo "IPsec PSK: $IPSEC_PSK"
|
|
echo "Username: $VPN_USER"
|
|
echo "Password: $VPN_PASSWORD"
|
|
echo '============================================================'
|
|
echo
|
|
echo 'Please read IMPORTANT NOTES at:'
|
|
echo 'https://github.com/hwdsl2/setup-ipsec-vpn#important-notes'
|
|
echo
|
|
|
|
exit 0
|