Browse Source

Added L2TP/IPSec VPN Server Docker Image

Signed-off-by: Teddysun <[email protected]>
Teddysun 7 years ago
parent
commit
890f0473c0
7 changed files with 871 additions and 0 deletions
  1. 3 0
      docker/l2tp/.dockerignore
  2. 25 0
      docker/l2tp/Dockerfile
  3. 128 0
      docker/l2tp/README.md
  4. 304 0
      docker/l2tp/ipsec
  5. 16 0
      docker/l2tp/l2tp.env
  6. 264 0
      docker/l2tp/l2tp.sh
  7. 131 0
      docker/l2tp/l2tpctl.sh

+ 3 - 0
docker/l2tp/.dockerignore

@@ -0,0 +1,3 @@
+.dockerignore
+README.md
+l2tp.env

+ 25 - 0
docker/l2tp/Dockerfile

@@ -0,0 +1,25 @@
+# Dockerfile for L2TP/IPSec VPN Server
+# Copyright (C) 2018 Teddysun <[email protected]>
+
+FROM debian:stretch
+MAINTAINER Teddysun <[email protected]>
+
+RUN set -ex \
+    && printf "deb http://deb.debian.org/debian sid main" > /etc/apt/sources.list.d/sid.list \
+    && apt-get update \
+    && apt-get -t sid install -y --no-install-recommends libreswan xl2tpd \
+    && apt-get install -y --no-install-recommends wget iproute2 openssl ca-certificates kmod net-tools iptables \
+    && apt-get -y autoremove \
+    && apt-get -y clean \
+    && rm -rf /var/lib/apt/lists/*
+
+COPY ./ipsec /etc/init.d/ipsec
+COPY ./l2tp.sh /usr/bin/l2tp
+COPY ./l2tpctl.sh /usr/bin/l2tpctl
+RUN chmod 755 /etc/init.d/ipsec /usr/bin/l2tp /usr/bin/l2tpctl
+
+VOLUME /lib/modules
+
+EXPOSE 500/udp 4500/udp
+
+CMD [ "/usr/bin/l2tp" ]

+ 128 - 0
docker/l2tp/README.md

@@ -0,0 +1,128 @@
+## L2TP/IPsec VPN Server Docker Image by Teddysun
+
+Docker image to run a L2TP/IPsec VPN Server, with both `L2TP/IPsec PSK` and `IPSec Xauth PSK`.
+Based on Debian 9 (Stretch) with latest libreswan (IPsec VPN software) and xl2tpd (L2TP daemon).
+
+Docker images are built for quick deployment in various computing cloud providers.
+For more information on docker and containerization technologies, refer to [official document][1].
+
+## Prepare the host
+
+If you need to install docker by yourself, follow the [official installation guide][2].
+
+## Pull the image
+
+```bash
+$ docker pull teddysun/l2tp
+```
+
+This pulls the latest release of shadowsocks-libev.
+It can be found at [Docker Hub][3].
+
+## Start a container
+
+You **must create a environment file**  `/etc/l2tp.env` in host at first, and sample value is below:
+
+```
+VPN_IPSEC_PSK=teddysun.com
+VPN_USER=vpnuser
+VPN_PASSWORD=vpnpassword
+VPN_PUBLIC_IP=
+VPN_L2TP_NET=
+VPN_L2TP_LOCAL=
+VPN_L2TP_REMOTE=
+VPN_XAUTH_NET=
+VPN_XAUTH_REMOTE=
+VPN_DNS1=
+VPN_DNS2=
+```
+
+This will create a default user account for L2TP/IPsec VPN login, which can be used by your **multiple devices**. 
+The IPSec PSK (pre-shared key) is specified by the `VPN_IPSEC_PSK` environment variable. 
+The username is specified in `VPN_USER` environment variable.
+and password is specified in `VPN_PASSWORD` environment variable.
+If your VPS has multiple public IP addresses, maybe public IP need to specified in `VPN_PUBLIC_IP` environment variable.
+
+There is an example to start a container:
+
+```bash
+$ docker run -d --privileged -p 500:500/udp -p 4500:4500/udp --name l2tp --env-file /etc/l2tp.env -v /lib/modules:/lib/modules teddysun/l2tp
+```
+
+**Note**: The UDP port number `500` and `4500` must be opened in firewall.
+
+## Check container details
+
+If you want to view the container logs:
+
+```bash
+$ docker logs l2tp
+```
+
+Output log like below:
+
+```
+L2TP/IPsec VPN Server with the Username and Password is below:
+
+Server IP: Your Server public IP
+IPSec PSK: IPSec PSK (pre-shared key)
+Username : VPN username
+Password : VPN password
+
+Redirecting to: /etc/init.d/ipsec start
+Starting pluto IKE daemon for IPsec: Initializing NSS database
+
+xl2tpd[1]: Not looking for kernel SAref support.
+xl2tpd[1]: Using l2tp kernel support.
+xl2tpd[1]: xl2tpd version xl2tpd-1.3.12 started on 1d20eaecd9f2 PID:1
+xl2tpd[1]: Written by Mark Spencer, Copyright (C) 1998, Adtran, Inc.
+xl2tpd[1]: Forked by Scott Balmos and David Stipp, (C) 2001
+xl2tpd[1]: Inherited by Jeff McAdams, (C) 2002
+xl2tpd[1]: Forked again by Xelerance (www.xelerance.com) (C) 2006-2016
+xl2tpd[1]: Listening on IP address 0.0.0.0, port 1701
+```
+
+To check the status of your L2TP/IPSec VPN server, you can confirm `ipsec status` to your container like below:
+
+```bash
+$ docker exec -it l2tp ipsec status
+```
+
+## Manage VPN Users
+
+If you want to add, modify or remove user accounts, please do it simple like below:
+
+### List all users
+
+```bash
+$ docker exec -it l2tp l2tpctl -l
+```
+
+### Add a user
+
+```bash
+$ docker exec -it l2tp l2tpctl -a
+```
+
+### Delete a user
+
+```bash
+$ docker exec -it l2tp l2tpctl -d
+```
+
+### Modify a user password
+
+```bash
+$ docker exec -it l2tp l2tpctl -m
+```
+
+### Print help information
+
+```bash
+$ docker exec -it l2tp l2tpctl -h
+```
+
+
+[1]: https://docs.docker.com/
+[2]: https://docs.docker.com/install/
+[3]: https://hub.docker.com/r/teddysun/l2tp/

+ 304 - 0
docker/l2tp/ipsec

@@ -0,0 +1,304 @@
+#!/bin/sh
+# IPsec startup and shutdown script
+#
+### BEGIN INIT INFO
+# Provides: ipsec
+# Required-Start: $network $remote_fs $syslog $named
+# Required-Stop: $syslog $remote_fs
+# Default-Start: 
+# Default-Stop: 0 1 6
+# Short-Description: Start Libreswan IPsec at boot time
+# Description: Enable automatic key management for IPsec (KLIPS and NETKEY)
+### END INIT INFO
+#
+### see https://bugzilla.redhat.com/show_bug.cgi?id=636572
+### Debian and Fedora interpret the LSB differently for Default-Start:
+
+# Copyright (C) 1998, 1999, 2001  Henry Spencer.
+# Copyright (C) 2002              Michael Richardson <[email protected]>
+# Copyright (C) 2006              Michael Richardson <[email protected]>
+# Copyright (C) 2008              Michael Richardson <[email protected]>
+# Copyright (C) 2008-2015         Tuomo Soini <[email protected]>
+# Copyright (C) 2012              Paul Wouters <[email protected]>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# ipsec		sysv style init.d script for starting and stopping
+#		the IPsec security subsystem (KLIPS and Pluto).
+#
+# This script becomes /etc/init.d/ipsec
+# and is also accessible as "ipsec setup"
+#
+# The startup and shutdown times are a difficult compromise (in particular,
+# it is almost impossible to reconcile them with the insanely early/late
+# times of NFS filesystem startup/shutdown).  Startup is after startup of
+# syslog and pcmcia support; shutdown is just before shutdown of syslog.
+#
+# chkconfig: - 47 76
+# description: IPsec provides encrypted and authenticated communications; \
+# NETKEY/KLIPS is the kernel half of it, Pluto is the user-level management daemon.
+
+test ${IPSEC_INIT_SCRIPT_DEBUG} && set -v -x
+
+# Source function library.
+if [ -f /etc/init.d/functions ]; then
+    . /etc/init.d/functions
+elif [ -f /lib/lsb/init-functions ]; then
+    . /lib/lsb/init-functions
+fi
+
+# Check that networking is up.
+[ "${NETWORKING}" = "no" ] && exit 6
+
+if [ $(id -u) -ne 0 ]; then
+    echo "permission denied (must be superuser)" | \
+	logger -s -p daemon.error -t ipsec_setup 2>&1
+    exit 4
+fi
+
+if [ $(ip addr list | grep -c cipsec) -ne 0 ]; then
+    echo "Cisco IPsec client is already loaded, aborting! (cipsec# device found)"
+    exit 1
+fi
+
+# where the private directory and the config files are
+IPSEC_CONF="${IPSEC_CONF:-/etc/ipsec.conf}"
+IPSEC_EXECDIR="${IPSEC_EXECDIR:-/usr/lib/ipsec}"
+IPSEC_SBINDIR="${IPSEC_SBINDIR:-/usr/sbin}"
+unset PLUTO_OPTIONS
+
+rundir=/var/run/pluto
+plutopid=${rundir}/pluto.pid
+plutoctl=${rundir}/pluto.ctl
+lockdir=/var/lock/subsys
+lockfile=${lockdir}/ipsec
+ipsecversion=/proc/net/ipsec_version
+kamepfkey=/proc/net/pfkey
+
+# /etc/resolv.conf related paths
+LIBRESWAN_RESOLV_CONF=${rundir}/libreswan-resolv-conf-backup
+ORIG_RESOLV_CONF=/etc/resolv.conf
+
+# there is some confusion over the name - just do both
+[ -f /etc/sysconfig/ipsec ] && . /etc/sysconfig/ipsec
+[ -f /etc/sysconfig/pluto ] && . /etc/sysconfig/pluto
+
+# misc setup
+umask 022
+
+# standardize PATH, and export it for everything else's benefit
+PATH="${IPSEC_SBINDIR}:/sbin:/usr/sbin:/usr/local/bin:/bin:/usr/bin"
+export PATH
+
+mkdir -p ${rundir}
+chmod 700 ${rundir}
+
+verify_config() {
+    [ -f ${IPSEC_CONF} ] || exit 6
+
+    config_error=$(ipsec addconn --config ${IPSEC_CONF} --checkconfig 2>&1)
+    RETVAL=$?
+    if [ ${RETVAL} -gt 0 ]; then
+	echo "Configuration error - the following error occurred:"
+	echo ${config_error}
+	echo "IKE daemon status was not modified"
+	exit ${RETVAL}
+    fi
+}
+
+start() {
+    echo -n "Starting pluto IKE daemon for IPsec: "
+    ipsec _stackmanager start
+
+    # pluto searches the current directory, so this is required for making it selinux compliant
+    cd /
+    # Create nss db or convert from old format to new sql format
+    ipsec --checknss
+    # Enable nflog if configured
+    ipsec --checknflog > /dev/null
+    # This script will enter an endless loop to ensure pluto restarts on crash
+    ipsec _plutorun --config ${IPSEC_CONF} --nofork ${PLUTO_OPTIONS} &
+    [ -d ${lockdir} ] || mkdir -p ${lockdir}
+    touch ${lockfile}
+    # Because _plutorun starts pluto at background we need to make sure pluto is started
+    # before we know if start was successful or not
+    for waitsec in 1 2 3 4 5; do
+	if status >/dev/null; then
+	    RETVAL=0
+	    break
+	else
+	    echo -n "."
+	    sleep 1
+	    RETVAL=1
+	fi
+    done
+    if [ ${RETVAL} -ge 1 ]; then
+	rm -f ${lockfile}
+    fi
+    echo
+    return ${RETVAL}
+}
+
+
+stop() {
+    if [ -e ${plutoctl} ]; then
+	echo "Shutting down pluto IKE daemon"
+	ipsec whack --shutdown 2>/dev/null
+	# don't use seq, might not exist on embedded
+	for waitsec in 1 2 3 4 5 6 7 8 9 10; do
+	    if [ -s ${plutopid} ]; then
+		echo -n "."
+		sleep 1
+	    else
+		break
+	    fi
+	done
+	echo
+	rm -f ${plutoctl} # we won't be using this anymore
+    fi
+    if [ -s ${plutopid} ]; then
+	# pluto did not die peacefully
+	pid=$(cat ${plutopid})
+	if [ -d /proc/${pid} ]; then
+	    kill -TERM ${pid}
+	    RETVAL=$?
+	    sleep 5;
+	    if [ -d /proc/${pid} ]; then
+		kill -KILL ${pid}
+		RETVAL=$?
+	    fi
+	    if [ ${RETVAL} -ne 0 ]; then
+		echo "Kill failed - removing orphaned ${plutopid}"
+	    fi
+	else
+	    echo "Removing orphaned ${plutopid}"
+	fi
+	rm -f ${plutopid}
+    fi
+
+    ipsec _stackmanager stop
+    ipsec --stopnflog > /dev/null
+
+    # cleaning up backup resolv.conf
+    if [ -e ${LIBRESWAN_RESOLV_CONF} ]; then
+	if grep 'Libreswan' ${ORIG_RESOLV_CONF} > /dev/null 2>&1; then
+	    cp ${LIBRESWAN_RESOLV_CONF} ${ORIG_RESOLV_CONF}
+	fi
+	rm -f  ${LIBRESWAN_RESOLV_CONF}
+    fi
+
+    rm -f ${lockfile}
+    return ${RETVAL}
+}
+
+restart() {
+    verify_config
+    stop
+    start
+    return $?
+}
+
+status() {
+    local RC
+    if [ -f ${plutopid} ]; then
+	if [ -r ${plutopid} ]; then
+	    pid=$(cat ${plutopid})
+	    if [ -n "$pid" -a -d /proc/${pid} ]; then
+		RC=0	# running
+	    else
+		RC=1	# not running but pid exists
+	    fi
+	else
+	    RC=4	# insufficient privileges
+	fi
+    fi
+    if [ -z "${RC}" ]; then
+	if [ -f ${lockfile} ]; then
+	    RC=2
+	else
+	    RC=3
+	fi
+    fi
+    case "${RC}" in
+	0)
+	    echo "ipsec: pluto (pid ${pid}) is running..."
+	    return 0
+	    ;;
+	1)
+	    echo "ipsec: pluto dead but pid file exits"
+	    return 1
+	    ;;
+	2)
+	    echo "ipsec: pluto dead but subsys locked"
+	    return 2
+	    ;;
+	4)
+	    echo "ipsec: pluto status unknown due to insufficient privileges."
+	    return 4
+	    ;;
+    esac
+    echo "ipsec: pluto is stopped"
+    return 3
+}
+
+condrestart() {
+    verify_config
+    RETVAL=$?
+    if [ -f ${lockfile} ]; then
+	restart
+	RETVAL=$?
+    fi
+    return ${RETVAL}
+}
+
+version() {
+    ipsec version
+    return $?
+}
+
+
+# do it
+case "$1" in
+    start)
+	start
+	RETVAL=$?
+	;;
+    stop)
+	stop
+	RETVAL=$?
+	;;
+    restart)
+	restart
+	RETVAL=$?
+	;;
+    reload|force-reload)
+	restart
+	RETVAL=$?
+	;;
+    condrestart|try-restart)
+	condrestart
+	RETVAL=$?
+	;;
+    status)
+	status
+	RETVAL=$?
+	${IPSEC_EXECDIR}/whack --status 2>/dev/null | grep Total | sed 's/^000\ Total\ //'
+	;;
+    version)
+	version
+	RETVAL=$?
+	;;
+    *)
+	echo "Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status|version}"
+	RETVAL=2
+esac
+
+exit ${RETVAL}

+ 16 - 0
docker/l2tp/l2tp.env

@@ -0,0 +1,16 @@
+# This file is part of L2TP/IPSec VPN Server Docker image.
+# Define your own values for these environment variables.
+# DO NOT put "" or '' around values, or add space around =
+# DO NOT use these special characters within values: \ " '
+
+VPN_IPSEC_PSK=teddysun.com
+VPN_USER=vpnuser
+VPN_PASSWORD=vpnpassword
+VPN_PUBLIC_IP=
+VPN_L2TP_NET=
+VPN_L2TP_LOCAL=
+VPN_L2TP_REMOTE=
+VPN_XAUTH_NET=
+VPN_XAUTH_REMOTE=
+VPN_DNS1=
+VPN_DNS2=

+ 264 - 0
docker/l2tp/l2tp.sh

@@ -0,0 +1,264 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+#
+# This is a Shell script for configure and start L2TP/IPSec VPN server with Docker image
+# 
+# Copyright (C) 2018 Teddysun <[email protected]>
+#
+# Reference URL:
+# https://github.com/libreswan/libreswan
+# https://github.com/xelerance/xl2tpd
+
+if [ ! -f "/.dockerenv" ]; then
+    echo "Error: This script must be run in a Docker container." >&2
+    exit 1
+fi
+
+if ip link add dummy0 type dummy 2>&1 | grep -q "not permitted"; then
+    echo "Error: This Docker image must be run in privileged mode." >&2
+    exit 1
+fi
+
+ip link delete dummy0 >/dev/null 2>&1
+
+rand(){
+    index=0
+    str=""
+    for i in {a..z}; do arr[index]=${i}; index=$(expr ${index} + 1); done
+    for i in {A..Z}; do arr[index]=${i}; index=$(expr ${index} + 1); done
+    for i in {0..9}; do arr[index]=${i}; index=$(expr ${index} + 1); done
+    for i in {1..10}; do str="$str${arr[$RANDOM%$index]}"; done
+    echo ${str}
+}
+
+is_64bit(){
+    if [ "$(getconf WORD_BIT)" == "32" ] && [ "$(getconf LONG_BIT)" == "64" ]; then
+        return 0
+    else
+        return 1
+    fi
+}
+
+# Environment file name
+l2tp_env_file="/etc/l2tp.env"
+# Auto generated
+if [ -z "${VPN_IPSEC_PSK}" ] && [ -z "${VPN_USER}" ] && [ -z "${VPN_PASSWORD}" ]; then
+    if [ -f "${l2tp_env_file}" ]; then
+        echo "Loading previously generated environment variables for L2TP/IPSec VPN Server..."
+        . "${l2tp_env_file}"
+    else
+        echo "L2TP/IPSec VPN Server environment variables is not set. Use default environment variables..."
+        VPN_IPSEC_PSK="teddysun.com"
+        VPN_USER="vpnuser"
+        VPN_PASSWORD="$(rand)"
+        echo "VPN_IPSEC_PSK=${VPN_IPSEC_PSK}" > ${l2tp_env_file}
+        echo "VPN_USER=${VPN_USER}" >> ${l2tp_env_file}
+        echo "VPN_PASSWORD=${VPN_PASSWORD}" >> ${l2tp_env_file}
+        chmod 600 ${l2tp_env_file}
+    fi
+fi
+
+# Environment variables:
+# VPN_IPSEC_PSK
+# VPN_USER
+# VPN_PASSWORD
+if [ -z "${VPN_IPSEC_PSK}" ] || [ -z "${VPN_USER}" ] || [ -z "${VPN_PASSWORD}" ]; then
+    echo "Error: Environment variables must be specified. please edit your environment file and retry again." >&2
+    exit 1
+fi
+
+if printf '%s' "${VPN_IPSEC_PSK} ${VPN_USER} ${VPN_PASSWORD}" | LC_ALL=C grep -q '[^ -~]\+'; then
+    echo "Error: Environment variables must not contain non-ASCII characters." >&2
+    exit 1
+fi
+
+case "${VPN_IPSEC_PSK} ${VPN_USER} ${VPN_PASSWORD}" in
+    *[\\\"\']*)
+    echo "Error: Environment variables must not contain these special characters like: \\ \" '"
+    exit 1
+    ;;
+esac
+
+# Environment variables:
+# VPN_PUBLIC_IP
+PUBLIC_IP=${VPN_PUBLIC_IP:-''}
+
+[ -z "${PUBLIC_IP}" ] && PUBLIC_IP=$( wget -qO- -t1 -T2 ipv4.icanhazip.com )
+[ -z "${PUBLIC_IP}" ] && PUBLIC_IP=$( wget -qO- -t1 -T2 ipinfo.io/ip )
+
+# Environment variables:
+# VPN_L2TP_NET
+# VPN_L2TP_LOCAL
+# VPN_L2TP_REMOTE
+# VPN_XAUTH_NET
+# VPN_XAUTH_REMOTE
+# VPN_DNS1
+# VPN_DNS2
+L2TP_NET=${VPN_L2TP_NET:-'192.168.18.0/24'}
+L2TP_LOCAL=${VPN_L2TP_LOCAL:-'192.168.18.1'}
+L2TP_REMOTE=${VPN_L2TP_REMOTE:-'192.168.18.10-192.168.18.250'}
+XAUTH_NET=${VPN_XAUTH_NET:-'192.168.20.0/24'}
+XAUTH_REMOTE=${VPN_XAUTH_REMOTE:-'192.168.20.10-192.168.20.250'}
+DNS1=${VPN_DNS1:-'8.8.8.8'}
+DNS2=${VPN_DNS2:-'8.8.4.4'}
+
+# Create IPSec config
+cat > /etc/ipsec.conf <<EOF
+version 2.0
+
+config setup
+    protostack=netkey
+    nhelpers=0
+    uniqueids=no
+    interfaces=%defaultroute
+    virtual-private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:!${L2TP_NET},%v4:!${XAUTH_NET}
+
+conn shared
+    left=%defaultroute
+    leftid=${PUBLIC_IP}
+    right=%any
+    encapsulation=yes
+    authby=secret
+    pfs=no
+    rekey=no
+    keyingtries=5
+    dpddelay=30
+    dpdtimeout=120
+    dpdaction=clear
+    ike=3des-sha1,3des-sha2,aes-sha1,aes-sha1;modp1024,aes-sha2,aes-sha2;modp1024
+    phase2alg=3des-sha1,3des-sha2,aes-sha1,aes-sha2,aes256-sha2_512
+    sha2-truncbug=yes
+
+conn l2tp-psk
+    auto=add
+    leftprotoport=17/1701
+    rightprotoport=17/%any
+    type=transport
+    phase2=esp
+    also=shared
+
+conn xauth-psk
+    auto=add
+    leftsubnet=0.0.0.0/0
+    rightaddresspool=${XAUTH_REMOTE}
+    modecfgdns=${DNS1},${DNS2}
+    leftxauthserver=yes
+    rightxauthclient=yes
+    leftmodecfgserver=yes
+    rightmodecfgclient=yes
+    modecfgpull=yes
+    xauthby=file
+    ike-frag=yes
+    ikev2=never
+    cisco-unity=yes
+    also=shared
+EOF
+
+cat > /etc/xl2tpd/xl2tpd.conf <<EOF
+[global]
+port = 1701
+
+[lns default]
+local ip = ${L2TP_LOCAL}
+ip range = ${L2TP_REMOTE}
+require chap = yes
+refuse pap = yes
+require authentication = yes
+name = l2tpd
+pppoptfile = /etc/ppp/options.xl2tpd
+length bit = yes
+EOF
+
+cat > /etc/ppp/options.xl2tpd <<EOF
++mschap-v2
+ipcp-accept-local
+ipcp-accept-remote
+ms-dns ${DNS1}
+ms-dns ${DNS2}
+noccp
+auth
+mtu 1280
+mru 1280
+proxyarp
+lcp-echo-failure 4
+lcp-echo-interval 30
+connect-delay 5000
+EOF
+
+cat > /etc/ipsec.secrets <<EOF
+%any  %any  : PSK "${VPN_IPSEC_PSK}"
+EOF
+
+cat > /etc/ppp/chap-secrets <<EOF
+${VPN_USER} l2tpd ${VPN_PASSWORD} *
+EOF
+
+VPN_PASSWORD_ENC=$(openssl passwd -1 "${VPN_PASSWORD}")
+cat > /etc/ipsec.d/passwd <<EOF
+${VPN_USER}:${VPN_PASSWORD_ENC}:xauth-psk
+EOF
+
+chmod 600 /etc/ipsec.secrets /etc/ppp/chap-secrets /etc/ipsec.d/passwd
+
+# Update sysctl settings
+if is_64bit; then
+    SHM_MAX=68719476736
+    SHM_ALL=4294967296
+else
+    SHM_MAX=4294967295
+    SHM_ALL=268435456
+fi
+
+sysctl -eqw kernel.msgmnb=65536
+sysctl -eqw kernel.msgmax=65536
+sysctl -eqw kernel.shmmax=${SHM_MAX}
+sysctl -eqw kernel.shmall=${SHM_ALL}
+sysctl -eqw net.ipv4.ip_forward=1
+sysctl -eqw net.ipv4.conf.all.accept_source_route=0
+sysctl -eqw net.ipv4.conf.all.accept_redirects=0
+sysctl -eqw net.ipv4.conf.all.send_redirects=0
+sysctl -eqw net.ipv4.conf.all.rp_filter=0
+sysctl -eqw net.ipv4.conf.default.accept_source_route=0
+sysctl -eqw net.ipv4.conf.default.accept_redirects=0
+sysctl -eqw net.ipv4.conf.default.send_redirects=0
+sysctl -eqw net.ipv4.conf.default.rp_filter=0
+sysctl -eqw net.ipv4.conf.eth0.send_redirects=0
+sysctl -eqw net.ipv4.conf.eth0.rp_filter=0
+
+# Create iptables rules
+iptables -I INPUT 1 -p udp --dport 1701 -m policy --dir in --pol none -j DROP
+iptables -I INPUT 2 -m conntrack --ctstate INVALID -j DROP
+iptables -I INPUT 3 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+iptables -I INPUT 4 -p udp -m multiport --dports 500,4500 -j ACCEPT
+iptables -I INPUT 5 -p udp --dport 1701 -m policy --dir in --pol ipsec -j ACCEPT
+iptables -I INPUT 6 -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 "${L2TP_NET}" -d "${L2TP_NET}" -j ACCEPT
+iptables -I FORWARD 5 -i eth+ -d "${XAUTH_NET}" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
+iptables -I FORWARD 6 -s "${XAUTH_NET}" -o eth+ -j ACCEPT
+iptables -A FORWARD -j DROP
+iptables -t nat -I POSTROUTING -s "${XAUTH_NET}" -o eth+ -m policy --dir out --pol none -j MASQUERADE
+iptables -t nat -I POSTROUTING -s "${L2TP_NET}" -o eth+ -j MASQUERADE
+
+cat <<EOF
+
+L2TP/IPsec VPN Server with the Username and Password is below:
+
+Server IP: ${PUBLIC_IP}
+IPSec PSK: ${VPN_IPSEC_PSK}
+Username : ${VPN_USER}
+Password : ${VPN_PASSWORD}
+
+EOF
+
+# Load IPsec kernel module
+modprobe af_key
+
+# Start services
+mkdir -p /run/pluto /var/run/pluto /var/run/xl2tpd
+rm -f /run/pluto/pluto.pid /var/run/pluto/pluto.pid /var/run/xl2tpd.pid
+/usr/sbin/ipsec start
+exec /usr/sbin/xl2tpd -D -c /etc/xl2tpd/xl2tpd.conf

+ 131 - 0
docker/l2tp/l2tpctl.sh

@@ -0,0 +1,131 @@
+#!/bin/bash
+PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
+export PATH
+#
+# This is a Shell script for configure and start L2TP/IPSec VPN server with Docker image
+# 
+# Copyright (C) 2018 Teddysun <[email protected]>
+#
+# Reference URL:
+# https://github.com/libreswan/libreswan
+# https://github.com/xelerance/xl2tpd
+
+rand(){
+    index=0
+    str=""
+    for i in {a..z}; do arr[index]=${i}; index=$(expr ${index} + 1); done
+    for i in {A..Z}; do arr[index]=${i}; index=$(expr ${index} + 1); done
+    for i in {0..9}; do arr[index]=${i}; index=$(expr ${index} + 1); done
+    for i in {1..10}; do str="$str${arr[$RANDOM%$index]}"; done
+    echo ${str}
+}
+
+list_users(){
+    if [ ! -f /etc/ppp/chap-secrets ];then
+        echo "Error: /etc/ppp/chap-secrets file not found."
+        exit 1
+    fi
+    local line="+-------------------------------------------+\n"
+    local string=%20s
+    printf "${line}|${string} |${string} |\n${line}" Username Password
+    grep -v "^#" /etc/ppp/chap-secrets | awk '{printf "|'${string}' |'${string}' |\n", $1,$3}'
+    printf ${line}
+}
+
+add_user(){
+    while :
+    do
+        read -p "Please enter Username:" user
+        if [ -z ${user} ]; then
+            echo "Username can not be empty"
+        else
+            grep -w "${user}" /etc/ppp/chap-secrets > /dev/null 2>&1
+            if [ $? -eq 0 ];then
+                echo "Username (${user}) already exists. Please re-enter your username."
+            else
+                break
+            fi
+        fi
+    done
+    pass="$(rand)"
+    echo "Please enter ${user}'s password:"
+    read -p "(Default Password: ${pass}):" tmppass
+    [ ! -z ${tmppass} ] && pass=${tmppass}
+    pass_enc=$(openssl passwd -1 "${pass}")
+    echo "${user} l2tpd ${pass} *" >> /etc/ppp/chap-secrets
+    echo "${user}:${pass_enc}:xauth-psk" >> /etc/ipsec.d/passwd
+    echo "Username (${user}) add completed."
+}
+
+del_user(){
+    while :
+    do
+        read -p "Please enter Username you want to delete it:" user
+        if [ -z ${user} ]; then
+            echo "Username can not be empty"
+        else
+            grep -w "${user}" /etc/ppp/chap-secrets >/dev/null 2>&1
+            if [ $? -eq 0 ];then
+                break
+            else
+                echo "Username (${user}) is not exists. Please re-enter your username."
+            fi
+        fi
+    done
+    sed -i "/^\<${user}\>/d" /etc/ppp/chap-secrets
+    sed -i "/^\<${user}\>/d" /etc/ipsec.d/passwd
+    echo "Username (${user}) delete completed."
+}
+
+mod_user(){
+    while :
+    do
+        read -p "Please enter Username you want to change password:" user
+        if [ -z ${user} ]; then
+            echo "Username can not be empty"
+        else
+            grep -w "${user}" /etc/ppp/chap-secrets >/dev/null 2>&1
+            if [ $? -eq 0 ];then
+                break
+            else
+                echo "Username (${user}) is not exists. Please re-enter your username."
+            fi
+        fi
+    done
+    pass="$(rand)"
+    echo "Please enter ${user}'s new password:"
+    read -p "(Default Password: ${pass}):" tmppass
+    [ ! -z ${tmppass} ] && pass=${tmppass}
+    pass_enc=$(openssl passwd -1 "${pass}")
+    sed -i "/^\<${user}\>/d" /etc/ppp/chap-secrets
+    sed -i "/^\<${user}\>/d" /etc/ipsec.d/passwd
+    echo "${user} l2tpd ${pass} *" >> /etc/ppp/chap-secrets
+    echo "${user}:${pass_enc}:xauth-psk" >> /etc/ipsec.d/passwd
+    echo "Username ${user}'s password has been changed."
+}
+
+action=$1
+case ${action} in
+    -l|--list)
+        list_users
+        ;;
+    -a|--add)
+        add_user
+        ;;
+    -d|--del)
+        del_user
+        ;;
+    -m|--mod)
+        mod_user
+        ;;
+    -h|--help)
+        echo "Usage: `basename $0` -l,--list   List all users"
+        echo "       `basename $0` -a,--add    Add a user"
+        echo "       `basename $0` -d,--del    Delete a user"
+        echo "       `basename $0` -m,--mod    Modify a user password"
+        echo "       `basename $0` -h,--help   Print this help information"
+        ;;
+    *)
+        echo "Usage: `basename $0` [-l,--list|-a,--add|-d,--del|-m,--mod|-h,--help]" && exit
+        ;;
+esac