#!/bin/sh
#
# General start/stop script:
# - Set the correct name and path below
# - Set USAGE_STRING below and check the allowed states in the case block
# - adjust check_config to fail or copy a config file if it is missing
# - finally adjust everything if really necessary...
#
SERVICE_NAME="openvpn"
BINARY_FILE_NAME=openvpn
BINARY_FILE_PATH="/usr/sbin"
CONFIG_FILE_NAME=server.conf
CONFIG_FILE_PATH="/etc/openvpn"
PRE_START_SCRIPT="/opt/userdata/etc/openvpn/iptables_start.sh"
POST_STOP_SCRIPT="/opt/userdata/etc/openvpn/iptables_stop.sh"

EASYRSA_BIN=/usr/bin/easyrsa
IPTABLES_BIN=/usr/sbin/iptables

# if service check is true, start is only possible, if the enable file is present
# remember to activate allowed states enable / disable
CHECK_SERVICE_ENABLED=true
# The allowed services for the usage message. Eg. "start|stop|restart|enable|disable|status"
# restart_on_failure is only used internally, if the IscWebService is configured properly.
USAGE_STRING="start|stop|restart|reload|enable|disable|status"


#### user parameter
export EASYRSA=/etc/openvpn/easy-rsa
SERVICE_MANUFACTURER=$(ls /opt/extparam/service*.crt | cut -d _ -f 2 | cut -d - -f 1)
SERVICE_SERIAL=$(ls /opt/extparam/service*.crt | cut -d _ -f 2 | cut -d - -f 2)
export EASYRSA_REQ_CN="${SERVICE_MANUFACTURER}-${SERVICE_SERIAL}"
export EASYRSA_REQ_COUNTRY=""
export EASYRSA_REQ_PROVINCE=""
export EASYRSA_REQ_ORG="ise KNX Remote Access"
export EASYRSA_REQ_OU=""
export EASYRSA_REQ_EMAIL=""
export EASYRSA_CERT_EXPIRE=3650
export EASYRSA_CRL_DAYS=3650 

user_post_start() {
	# Enable net.ipv4.ip_forward for the system
	echo 1 > /proc/sys/net/ipv4/ip_forward
	# enable default ise table
	/etc/init.d/S18iptables restart
	# Autodetect IP address
	IP=$(ip addr | grep 'inet' | grep -v inet6 | grep -vE '127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | head -1)
	# Set NAT for the VPN subnet
	${IPTABLES_BIN} -t nat -A POSTROUTING -s 10.8.0.0/24 ! -d 10.8.0.0/24 -j SNAT --to $IP
	if ${IPTABLES_BIN} -L -n | grep -qE '^(REJECT|DROP)'; then
		# If iptables has at least one REJECT rule, we asume this is needed.
		# Not the best approach but I can't think of other and this shouldn't
		# cause problems.
		${IPTABLES_BIN} -I INPUT -p tcp --dport 1194 -j ACCEPT
		${IPTABLES_BIN} -I FORWARD -s 10.8.0.0/24 -j ACCEPT
		${IPTABLES_BIN} -I FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
	fi
}

user_post_stop() {
	# Disable net.ipv4.ip_forward for the system
	echo 0 > /proc/sys/net/ipv4/ip_forward
	# enable default ise table
	/etc/init.d/S18iptables restart
}

user_enable_script() {
	if [ -f ${SERVICE_ENABLED} ]; then
      # already configured
      exit 0
	fi
	# OpenVPN setup
	# Autodetect IP address
	IP=$(ip addr | grep 'inet' | grep -v inet6 | grep -vE '127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | head -1)
	# create KIM file structure
	rm -rf /opt/userdata/etc/openvpn
	mkdir -p /opt/userdata/etc/openvpn/clientprofiles
	cp -r /etc/easy-rsa /opt/userdata/etc/openvpn
	cd /etc/openvpn/easy-rsa
	# Create the PKI, set up the CA and the server and client certificates
	${EASYRSA_BIN} init-pki
	${EASYRSA_BIN} --batch build-ca nopass
	${EASYRSA_BIN} build-server-full server nopass
	${EASYRSA_BIN} gen-crl
	# Move the stuff we need
	cp pki/ca.crt pki/private/ca.key pki/issued/server.crt pki/private/server.key pki/crl.pem /etc/openvpn
	# CRL is read with each client connection, when OpenVPN is dropped to nobody
	chown nobody:nogroup /etc/openvpn/crl.pem
	# Generate key for tls-auth
	${BINARY_FILE_PATH}/${BINARY_FILE_NAME} --genkey --secret /etc/openvpn/ta.key
	# Create the DH parameters file using the predefined ffdhe2048 group
	echo '-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----' > /etc/openvpn/dh.pem
	# Generate server.conf
	echo "port 1194
proto tcp
dev tun
sndbuf 0
rcvbuf 0
ca ca.crt
cert server.crt
key server.key
dh dh.pem
auth SHA512
tls-auth ta.key 0
topology subnet
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt" > /etc/openvpn/server.conf
	echo 'push "redirect-gateway def1 bypass-dhcp"' >> /etc/openvpn/server.conf
	# DNS
	RESOLVCONF='/etc/resolv.conf'
	# Obtain the resolvers from resolv.conf and use them for OpenVPN
	grep -v '#' $RESOLVCONF | grep 'nameserver' | grep -E -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' | while read line; do
      echo "push \"dhcp-option DNS $line\"" >> /etc/openvpn/server.conf
	done
	echo "keepalive 10 120
cipher AES-256-CBC
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn-status.log
verb 3
crl-verify crl.pem" >> /etc/openvpn/server.conf
	
	# client-common.txt as template to add users
	echo "client
dev tun
proto tcp
sndbuf 0
rcvbuf 0
remote vpn.${SERVICE_MANUFACTURER}-${SERVICE_SERIAL}.devsda.ise.de 1194 tcp
http-proxy devvpnproxy.ise.de 80
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
auth SHA512
cipher AES-256-CBC
setenv opt block-outside-dns
key-direction 1
verb 3" > /etc/openvpn/client-common.txt
}

user_disable_script() {
	# remove openvpn configuration    
	rm -rf /etc/openvpn/*
	# Disable net.ipv4.ip_forward for the system
	echo 0 > /proc/sys/net/ipv4/ip_forward
	# enable default ise table
	/etc/init.d/S18iptables restart
}


#### general parameter

CONFIG_FILE="${CONFIG_FILE_PATH}/${CONFIG_FILE_NAME}"
BINARY_FILE="${BINARY_FILE_PATH}/${BINARY_FILE_NAME}"
SERVICE_ENABLED="/opt/userdata/.${SERVICE_NAME}-enabled"
PID_FILE="/var/run/${SERVICE_NAME}.server.pid"

START_PARAMETER="--daemon --writepid ${PID_FILE} --config $CONFIG_FILE_PATH/$CONFIG_FILE_NAME --cd $CONFIG_FILE_PATH --log-append /var/log/openvpn.log"

log() {
	if [ "$(type logger)" != "" ]; then
		logger -t "${SERVICE_NAME}" "$1"
	else
		echo "$1"
	fi
}

log_std() {
	if [ "$(type logger)" != "" ]; then
		logger -t "${SERVICE_NAME}" "$1"
		echo "$1"
	else
		echo "$1"
	fi
}

# Check that the service exists.
if [ ! -f ${BINARY_FILE} ]; then
	log "Service not installed!"
	log_std "failure"
	exit 1
fi

check_config() {
	# if directory is missing, create it... the next test will fail.
	# check config directory
	if [ -d "${CONFIG_FILE_PATH}" ]; then
		log "${CONFIG_FILE_PATH} found."
	else
		log "${CONFIG_FILE_PATH} not found."
		log "Creating ${CONFIG_FILE_PATH}."
		mkdir -p "${CONFIG_FILE_PATH}"
	fi
	# check for valid configuration file; fail if not present
	if [ ! -f ${CONFIG_FILE} ]; then
		log "Configfile ${CONFIG_FILE} not found - can not start the service!"
		log_std "failure"
		exit 1
	fi
	log "Configfile ${CONFIG_FILE} found."
}

start() {
	log "Try to start service ${SERVICE_NAME}."
	if [ "${CHECK_SERVICE_ENABLED}" == "true" ]; then
		if [ ! -f ${SERVICE_ENABLED} ]; then
			log "Service is disabled!"
			log_std "failure"
			exit 1
		fi
	fi
	check_config
	log "Try stop previous ${SERVICE_NAME}."
	stop
	log "Starting ${SERVICE_NAME}"
	${BINARY_FILE} ${START_PARAMETER}
	RETVAL=$?
	pidof ${BINARY_FILE_NAME} > ${PID_FILE}
	if [ "$?" != "0" ]; then
		log_std "failed"
		return $RETVAL
	fi
	user_post_start
	log_std "done"
	return $RETVAL
}

stop() {
	log "Stopping service ${BINARY_FILE_NAME}."
	rm -f ${PID_FILE}
	killall -9 ${BINARY_FILE_NAME}
	RETVAL=$?
	user_post_stop
	log_std "done"
	return $RETVAL
}

restart() {
	log "Restarting service ${BINARY_FILE_NAME}."
	stop
	start
	return $?
}

reload() {
	log "Reloading service ${BINARY_FILE_NAME}."
	if [ -f $PID_FILE ]; then
            kill -HUP $(cat $PID_FILE) || true
            return $?
        fi
        return 1
}

disable() {
	log "Disable and stopping service ${SERVICE_NAME}."
	stop
	user_disable_script
	if [ "${CHECK_SERVICE_ENABLED}" == "true" ]; then
		rm -rf ${SERVICE_ENABLED}
		if [ "$?" == "0" ]; then
			log_std "done"
		else
			log_std "failure"
			exit 1
		fi
	else
		log_std "not available"
	fi
}

enable() {
	log "Enable service ${SERVICE_NAME}."
	user_enable_script
	if [ "${CHECK_SERVICE_ENABLED}" == "true" ]; then
		touch ${SERVICE_ENABLED}
		if [ "$?" == "0" ]; then
			log_std "done"
		else
			log_std "failure"
			exit 1
		fi
	else
		log_std "not available"
	fi
}

status() {
	SERVICE_STATUS="stopped"
	SERVICE_MODUS="disabled"
	RETVAL=0
	if [ "${CHECK_SERVICE_ENABLED}" == "true" ]; then
		if [ -f ${SERVICE_ENABLED} ]; then
			SERVICE_MODUS="enabled"
		fi
	fi
	CHECK=$(pidof ${BINARY_FILE_NAME})
	if [ "${CHECK}" != "" ]; then
		# assume running because we found a pid
		SERVICE_STATUS="running"
		# save the pid if we have no pid file:
		if [ ! -f ${PID_FILE} ]; then
				pidof ${BINARY_FILE_NAME} > ${PID_FILE}
		fi
	else
		# ok, not running... should it run?
		if [ -f ${PID_FILE} ]; then
			# ups should run but not running
			SERVICE_STATUS="failure"
			RETVAL=1
		fi
	fi
	# status did not report in /var/log/messages!
	if [ "${CHECK_SERVICE_ENABLED}" == "true" ]; then
		echo "${SERVICE_MODUS} / ${SERVICE_STATUS}"
	else
		echo "${SERVICE_STATUS}"
	fi
	exit ${RETVAL}
}

restart_on_failure() {
	SERVICE_STATUS="stopped"
	SERVICE_MODUS="disabled"
	RETVAL=0
	if [ "${CHECK_SERVICE_ENABLED}" == "true" ]; then
		if [ -f ${SERVICE_ENABLED} ]; then
			SERVICE_MODUS="enabled"
		fi
	fi
	CHECK=$(pidof ${BINARY_FILE_NAME})
	if [ "${CHECK}" != "" ]; then
		# assume running because we found a pid
		SERVICE_STATUS="running"
		# save the pid if we have no pid file:
		if [ ! -f ${PID_FILE} ]; then
				pidof ${BINARY_FILE_NAME} > ${PID_FILE}
		fi
	else
		# ok, not running... should it run?
		if [ -f ${PID_FILE} ]; then
			SERVICE_STATUS="restarted"
			log_std "Restarted due to failure."
			restart
			RETVAL=$?
		fi
	fi
	# restart_on_failure did not report in /var/log/messages!
	if [ "${CHECK_SERVICE_ENABLED}" == "true" ]; then
		echo "${SERVICE_MODUS} / ${SERVICE_STATUS}"
	else
		echo "${SERVICE_STATUS}"
	fi
	exit 0
}

case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart)
        restart
        ;;
  reload)
        reload
        ;;
  enable)
        enable
        ;;
  disable)
        disable
        ;;
  status)
        status
        ;;
  restart_on_failure)
        restart_on_failure
        ;;
  *)
        log_std "Usage: $0 {${USAGE_STRING}}"
        exit 1
esac

exit $?
