380 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			380 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/bin/bash
 | 
						|
 | 
						|
#
 | 
						|
# Generate OpenVPN configs
 | 
						|
#
 | 
						|
 | 
						|
TMP_PUSH_CONFIGFILE=$(mktemp -t vpn_push.XXXXXXX)
 | 
						|
TMP_ROUTE_CONFIGFILE=$(mktemp -t vpn_route.XXXXXXX)
 | 
						|
TMP_EXTRA_CONFIGFILE=$(mktemp -t vpn_extra.XXXXXXX)
 | 
						|
TMP_EXTRA_CLIENT_CONFIGFILE=$(mktemp -t vpn_extra_client.XXXXXXX)
 | 
						|
 | 
						|
#Traceback on Error and Exit come from https://docwhat.org/tracebacks-in-bash/
 | 
						|
set -eu
 | 
						|
 | 
						|
_showed_traceback=f
 | 
						|
 | 
						|
traceback() {
 | 
						|
	# Hide the traceback() call.
 | 
						|
	local -i start=$(( ${1:-0} + 1 ))
 | 
						|
	local -i end=${#BASH_SOURCE[@]}
 | 
						|
	local -i i=0
 | 
						|
	local -i j=0
 | 
						|
 | 
						|
	echo "Traceback (last called is first):" 1>&2
 | 
						|
	for ((i=${start}; i < ${end}; i++)); do
 | 
						|
		j=$(( $i - 1 ))
 | 
						|
		local function="${FUNCNAME[$i]}"
 | 
						|
		local file="${BASH_SOURCE[$i]}"
 | 
						|
		local line="${BASH_LINENO[$j]}"
 | 
						|
		echo "     ${function}() in ${file}:${line}" 1>&2
 | 
						|
	done
 | 
						|
}
 | 
						|
 | 
						|
on_error() {
 | 
						|
  local _ec="$?"
 | 
						|
  local _cmd="${BASH_COMMAND:-unknown}"
 | 
						|
  traceback 1
 | 
						|
  _showed_traceback=t
 | 
						|
  echo "The command ${_cmd} exited with exit code ${_ec}." 1>&2
 | 
						|
}
 | 
						|
trap on_error ERR
 | 
						|
 | 
						|
 | 
						|
on_exit() {
 | 
						|
  echo "Cleaning up before Exit ..."
 | 
						|
  rm -f $TMP_PUSH_CONFIGFILE
 | 
						|
  rm -f $TMP_ROUTE_CONFIGFILE
 | 
						|
  rm -f $TMP_EXTRA_CONFIGFILE
 | 
						|
  rm -f $TMP_EXTRA_CLIENT_CONFIGFILE
 | 
						|
  local _ec="$?"
 | 
						|
  if [[ $_ec != 0 && "${_showed_traceback}" != t ]]; then
 | 
						|
    traceback 1
 | 
						|
  fi
 | 
						|
}
 | 
						|
trap on_exit EXIT
 | 
						|
 | 
						|
# Convert 1.2.3.4/24 -> 255.255.255.0
 | 
						|
cidr2mask()
 | 
						|
{
 | 
						|
    local i
 | 
						|
    local subnetmask=""
 | 
						|
    local cidr=${1#*/}
 | 
						|
    local full_octets=$(($cidr/8))
 | 
						|
    local partial_octet=$(($cidr%8))
 | 
						|
 | 
						|
    for ((i=0;i<4;i+=1)); do
 | 
						|
        if [ $i -lt $full_octets ]; then
 | 
						|
            subnetmask+=255
 | 
						|
        elif [ $i -eq $full_octets ]; then
 | 
						|
            subnetmask+=$((256 - 2**(8-$partial_octet)))
 | 
						|
        else
 | 
						|
            subnetmask+=0
 | 
						|
        fi
 | 
						|
        [ $i -lt 3 ] && subnetmask+=.
 | 
						|
    done
 | 
						|
    echo $subnetmask
 | 
						|
}
 | 
						|
 | 
						|
# Used often enough to justify a function
 | 
						|
getroute() {
 | 
						|
    echo ${1%/*} $(cidr2mask $1)
 | 
						|
}
 | 
						|
 | 
						|
usage() {
 | 
						|
    echo "usage: $0 [-d]"
 | 
						|
    echo "                  -u SERVER_PUBLIC_URL"
 | 
						|
    echo "                 [-e EXTRA_SERVER_CONFIG ]"
 | 
						|
    echo "                 [-E EXTRA_CLIENT_CONFIG ]"
 | 
						|
    echo "                 [-f FRAGMENT ]"
 | 
						|
    echo "                 [-n DNS_SERVER ...]"
 | 
						|
    echo "                 [-p PUSH ...]"
 | 
						|
    echo "                 [-r ROUTE ...]"
 | 
						|
    echo "                 [-s SERVER_SUBNET]"
 | 
						|
    echo
 | 
						|
    echo "optional arguments:"
 | 
						|
    echo " -2    Enable two factor authentication using Google Authenticator."
 | 
						|
    echo " -a    Authenticate  packets with HMAC using the given message digest algorithm (auth)."
 | 
						|
    echo " -c    Enable client-to-client option"
 | 
						|
    echo " -C    A list of allowable TLS ciphers delimited by a colon (cipher)."
 | 
						|
    echo " -d    Disable NAT routing and default route"
 | 
						|
    echo " -D    Do not push dns servers"
 | 
						|
    echo " -m    Set client MTU"
 | 
						|
    echo " -N    Configure NAT to access external server network"
 | 
						|
    echo " -t    Use TAP device (instead of TUN device)"
 | 
						|
    echo " -T    Encrypt packets with the given cipher algorithm instead of the default one (tls-cipher)."
 | 
						|
    echo " -z    Enable comp-lzo compression."
 | 
						|
}
 | 
						|
 | 
						|
process_route_config() {
 | 
						|
  local ovpn_route_config=''
 | 
						|
  ovpn_route_config="$1"
 | 
						|
  # If user passed "0" skip this, assume no extra routes
 | 
						|
  [[ "$ovpn_route_config" == "0" ]] && break;
 | 
						|
  echo "Processing Route Config: '${ovpn_route_config}'"
 | 
						|
  [[ -n "$ovpn_route_config" ]] && echo "route $(getroute $ovpn_route_config)" >> "$TMP_ROUTE_CONFIGFILE"
 | 
						|
}
 | 
						|
 | 
						|
process_push_config() {
 | 
						|
  local ovpn_push_config=''
 | 
						|
  ovpn_push_config="$1"
 | 
						|
  echo "Processing PUSH Config: '${ovpn_push_config}'"
 | 
						|
  [[ -n "$ovpn_push_config" ]] && echo "push $ovpn_push_config" >> "$TMP_PUSH_CONFIGFILE"
 | 
						|
}
 | 
						|
 | 
						|
process_extra_config() {
 | 
						|
  local ovpn_extra_config=''
 | 
						|
  ovpn_extra_config="$1"
 | 
						|
  echo "Processing Extra Config: '${ovpn_extra_config}'"
 | 
						|
  [[ -n "$ovpn_extra_config" ]] && echo "$ovpn_extra_config" >> "$TMP_EXTRA_CONFIGFILE"
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
process_extra_client_config() {
 | 
						|
  local ovpn_extra_config=''
 | 
						|
  ovpn_extra_config="$1"
 | 
						|
  echo "Processing Extra Client Config: '${ovpn_extra_config}'"
 | 
						|
  [[ -n "$ovpn_extra_config" ]] && echo "$ovpn_extra_config" >> "$TMP_EXTRA_CLIENT_CONFIGFILE"
 | 
						|
}
 | 
						|
 | 
						|
if [ "${DEBUG:-}" == "1" ]; then
 | 
						|
  set -x
 | 
						|
fi
 | 
						|
 | 
						|
set -e
 | 
						|
 | 
						|
if [ -z "${OPENVPN:-}" ]; then
 | 
						|
  export OPENVPN="$PWD"
 | 
						|
fi
 | 
						|
if [ -z "${EASYRSA_PKI:-}" ]; then
 | 
						|
    export EASYRSA_PKI="$OPENVPN/pki"
 | 
						|
fi
 | 
						|
 | 
						|
OVPN_ENV=${OPENVPN}/ovpn_env.sh
 | 
						|
OVPN_SERVER=192.168.255.0/24
 | 
						|
OVPN_DEFROUTE=1
 | 
						|
OVPN_NAT=0
 | 
						|
OVPN_DNS=1
 | 
						|
OVPN_DEVICE="tun"
 | 
						|
OVPN_DEVICEN=0
 | 
						|
OVPN_DNS_SERVERS=("8.8.8.8" "8.8.4.4")
 | 
						|
TMP_DNS_SERVERS=()
 | 
						|
OVPN_TLS_CIPHER=''
 | 
						|
OVPN_CIPHER=''
 | 
						|
OVPN_AUTH=''
 | 
						|
OVPN_EXTRA_CONFIG=''
 | 
						|
CUSTOM_ROUTE_CONFIG=''
 | 
						|
 | 
						|
# Import defaults if present
 | 
						|
[ -r "$OVPN_ENV" ] && source "$OVPN_ENV"
 | 
						|
 | 
						|
# Parse arguments
 | 
						|
while getopts ":a:e:E:C:T:r:s:du:cp:n:DNmf:tz2" opt; do
 | 
						|
    case $opt in
 | 
						|
        a)
 | 
						|
            OVPN_AUTH="$OPTARG"
 | 
						|
            ;;
 | 
						|
        e)
 | 
						|
            process_extra_config "$OPTARG"
 | 
						|
            ;;
 | 
						|
        E)
 | 
						|
            process_extra_client_config "$OPTARG"
 | 
						|
            ;;
 | 
						|
        C)
 | 
						|
            OVPN_CIPHER="$OPTARG"
 | 
						|
            ;;
 | 
						|
        T)
 | 
						|
            OVPN_TLS_CIPHER="$OPTARG"
 | 
						|
            ;;
 | 
						|
        r)
 | 
						|
            CUSTOM_ROUTE_CONFIG=1
 | 
						|
            process_route_config "$OPTARG"
 | 
						|
            ;;
 | 
						|
        s)
 | 
						|
            OVPN_SERVER=$OPTARG
 | 
						|
            ;;
 | 
						|
        d)
 | 
						|
            OVPN_DEFROUTE=0
 | 
						|
            ;;
 | 
						|
        u)
 | 
						|
            OVPN_SERVER_URL=$OPTARG
 | 
						|
            ;;
 | 
						|
        c)
 | 
						|
            OVPN_CLIENT_TO_CLIENT=1
 | 
						|
            ;;
 | 
						|
        p)
 | 
						|
            process_push_config "$OPTARG"
 | 
						|
            ;;
 | 
						|
        n)
 | 
						|
            TMP_DNS_SERVERS+=("$OPTARG")
 | 
						|
            ;;
 | 
						|
        D)
 | 
						|
            OVPN_DNS=0
 | 
						|
            ;;
 | 
						|
        N)
 | 
						|
            OVPN_NAT=1
 | 
						|
            ;;
 | 
						|
        m)
 | 
						|
            OVPN_MTU=$OPTARG
 | 
						|
            ;;
 | 
						|
        t)
 | 
						|
            OVPN_DEVICE="tap"
 | 
						|
            ;;
 | 
						|
        z)
 | 
						|
            OVPN_COMP_LZO=1
 | 
						|
            ;;
 | 
						|
        2)
 | 
						|
            OVPN_OTP_AUTH=1
 | 
						|
            ;;
 | 
						|
        f)
 | 
						|
            OVPN_FRAGMENT=$OPTARG
 | 
						|
            ;;
 | 
						|
        \?)
 | 
						|
            set +x
 | 
						|
            echo "Invalid option: -$OPTARG" >&2
 | 
						|
            usage
 | 
						|
            exit 1
 | 
						|
            ;;
 | 
						|
        :)
 | 
						|
            set +x
 | 
						|
            echo "Option -$OPTARG requires an argument." >&2
 | 
						|
            usage
 | 
						|
            exit 1
 | 
						|
            ;;
 | 
						|
    esac
 | 
						|
done
 | 
						|
 | 
						|
# Create ccd directory for static routes
 | 
						|
[ ! -d "${OPENVPN:-}/ccd" ] && mkdir -p ${OPENVPN:-}/ccd
 | 
						|
 | 
						|
# if dns servers were not defined with -n, use google nameservers
 | 
						|
[ ${#TMP_DNS_SERVERS[@]} -gt 0 ] && OVPN_DNS_SERVERS=("${TMP_DNS_SERVERS[@]}")
 | 
						|
 | 
						|
# Server name is in the form "udp://vpn.example.com:1194"
 | 
						|
if [[ "${OVPN_SERVER_URL:-}" =~ ^((udp|tcp|udp6|tcp6)://)?([0-9a-zA-Z\.\-]+)(:([0-9]+))?$ ]]; then
 | 
						|
    OVPN_PROTO=${BASH_REMATCH[2]};
 | 
						|
    OVPN_CN=${BASH_REMATCH[3]};
 | 
						|
    OVPN_PORT=${BASH_REMATCH[5]};
 | 
						|
else
 | 
						|
    set +x
 | 
						|
    echo "Common name not specified, see '-u'"
 | 
						|
    usage
 | 
						|
    exit 1
 | 
						|
fi
 | 
						|
 | 
						|
# Apply defaults
 | 
						|
[ -z "$OVPN_PROTO" ] && OVPN_PROTO=udp
 | 
						|
[ -z "$OVPN_PORT" ] && OVPN_PORT=1194
 | 
						|
[ -z "$CUSTOM_ROUTE_CONFIG" ] && process_route_config "192.168.254.0/24"
 | 
						|
 | 
						|
# Save extra client config from temp file only if temp file is not empty
 | 
						|
if [ -s "$TMP_EXTRA_CLIENT_CONFIGFILE" ]; then
 | 
						|
  OVPN_ADDITIONAL_CLIENT_CONFIG=$(cat $TMP_EXTRA_CLIENT_CONFIGFILE)
 | 
						|
fi
 | 
						|
 | 
						|
export OVPN_SERVER OVPN_ROUTES OVPN_DEFROUTE
 | 
						|
export OVPN_SERVER_URL OVPN_ENV OVPN_PROTO OVPN_CN OVPN_PORT
 | 
						|
export OVPN_CLIENT_TO_CLIENT OVPN_PUSH OVPN_NAT OVPN_DNS OVPN_MTU OVPN_DEVICE
 | 
						|
export OVPN_TLS_CIPHER OVPN_CIPHER OVPN_AUTH
 | 
						|
export OVPN_COMP_LZO
 | 
						|
export OVPN_OTP_AUTH
 | 
						|
export OVPN_FRAGMENT
 | 
						|
export OVPN_ADDITIONAL_CLIENT_CONFIG
 | 
						|
 | 
						|
# Preserve config
 | 
						|
if [ -f "$OVPN_ENV" ]; then
 | 
						|
    bak_env=$OVPN_ENV.$(date +%s).bak
 | 
						|
    echo "Backing up $OVPN_ENV -> $bak_env"
 | 
						|
    mv "$OVPN_ENV" "$bak_env"
 | 
						|
fi
 | 
						|
 | 
						|
# Like `export | grep OVPN_ > "$OVPN_ENV"` but handles multiline variables
 | 
						|
set +u
 | 
						|
while read var ; do
 | 
						|
  eval value=\$$var
 | 
						|
  if [ -n "$value" ]; then
 | 
						|
    echo "declare -x $var=\"$value\"" >> "$OVPN_ENV"
 | 
						|
  else
 | 
						|
    echo "declare -x $var" >> "$OVPN_ENV"
 | 
						|
  fi
 | 
						|
done < <(export | egrep -o '(OVPN_[^=]+)')
 | 
						|
set -u
 | 
						|
 | 
						|
conf=${OPENVPN:-}/openvpn.conf
 | 
						|
if [ -f "$conf" ]; then
 | 
						|
    bak=$conf.$(date +%s).bak
 | 
						|
    echo "Backing up $conf -> $bak"
 | 
						|
    mv "$conf" "$bak"
 | 
						|
fi
 | 
						|
 | 
						|
cat > "$conf" <<EOF
 | 
						|
server $(getroute $OVPN_SERVER)
 | 
						|
verb 3
 | 
						|
key $EASYRSA_PKI/private/${OVPN_CN}.key
 | 
						|
ca $EASYRSA_PKI/ca.crt
 | 
						|
cert $EASYRSA_PKI/issued/${OVPN_CN}.crt
 | 
						|
dh $EASYRSA_PKI/dh.pem
 | 
						|
tls-auth $EASYRSA_PKI/ta.key
 | 
						|
key-direction 0
 | 
						|
keepalive 10 60
 | 
						|
persist-key
 | 
						|
persist-tun
 | 
						|
 | 
						|
proto $OVPN_PROTO
 | 
						|
# Rely on Docker to do port mapping, internally always 1194
 | 
						|
port 1194
 | 
						|
dev $OVPN_DEVICE$OVPN_DEVICEN
 | 
						|
status /tmp/openvpn-status.log
 | 
						|
 | 
						|
user nobody
 | 
						|
group nogroup
 | 
						|
EOF
 | 
						|
 | 
						|
# only block outside dns when we take the default route
 | 
						|
[ "$OVPN_DEFROUTE" == "1" ] && process_push_config "block-outside-dns"
 | 
						|
 | 
						|
[ -n "$OVPN_TLS_CIPHER" ] && echo "tls-cipher $OVPN_TLS_CIPHER" >> "$conf"
 | 
						|
[ -n "$OVPN_CIPHER" ] && echo "cipher $OVPN_CIPHER" >> "$conf"
 | 
						|
[ -n "$OVPN_AUTH" ] && echo "auth $OVPN_AUTH" >> "$conf"
 | 
						|
 | 
						|
[ -n "${OVPN_CLIENT_TO_CLIENT:-}" ] && echo "client-to-client" >> "$conf"
 | 
						|
[ -n "${OVPN_COMP_LZO:-}" ] && echo "comp-lzo" >> "$conf"
 | 
						|
 | 
						|
[ -n "${OVPN_FRAGMENT:-}" ] && echo "fragment $OVPN_FRAGMENT" >> "$conf"
 | 
						|
 | 
						|
[ "$OVPN_DNS" == "1" ] && for i in "${OVPN_DNS_SERVERS[@]}"; do
 | 
						|
  process_push_config "dhcp-option DNS $i"
 | 
						|
done
 | 
						|
 | 
						|
# Append route commands
 | 
						|
echo -e "\n### Route Configurations Below" >> "$conf"
 | 
						|
cat $TMP_ROUTE_CONFIGFILE >> "$conf"
 | 
						|
 | 
						|
# Append push commands
 | 
						|
echo -e "\n### Push Configurations Below" >> "$conf"
 | 
						|
cat $TMP_PUSH_CONFIGFILE >> "$conf"
 | 
						|
 | 
						|
# Optional OTP authentication support
 | 
						|
if [ -n "${OVPN_OTP_AUTH:-}" ]; then
 | 
						|
    echo -e "\n\n# Enable OTP+PAM for user authentication" >> "$conf"
 | 
						|
    echo "plugin /usr/lib/openvpn/plugins/openvpn-plugin-auth-pam.so openvpn" >> "$conf"
 | 
						|
    echo "reneg-sec 0" >> "$conf"
 | 
						|
fi
 | 
						|
 | 
						|
echo -e "\n### Extra Configurations Below" >> "$conf"
 | 
						|
cat $TMP_EXTRA_CONFIGFILE >> "$conf"
 | 
						|
 | 
						|
set +e
 | 
						|
 | 
						|
# Clean-up duplicate configs
 | 
						|
if diff -q "${bak_env:-}" "$OVPN_ENV" 2>/dev/null; then
 | 
						|
    echo "Removing duplicate back-up: $bak_env"
 | 
						|
    rm -fv "$bak_env"
 | 
						|
fi
 | 
						|
if diff -q "${bak:-}" "$conf" 2>/dev/null; then
 | 
						|
    echo "Removing duplicate back-up: $bak"
 | 
						|
    rm -fv "$bak"
 | 
						|
fi
 | 
						|
 | 
						|
echo "Successfully generated config"
 |