#!/bin/bash -e

fatal() { echo "FATAL: $@" 1>&2; exit 1; }
warn() { echo "WARN: $@"; }
info() { echo "INFO: $@"; }

usage() {
cat<<EOF
Syntax: $0 client-name client-email [private-subnet] [--pass] [--auth-nocache]
Generate keys and configuration for a new client

Arguments:

    client-name         Unique name for client
    client-email        Client email address
    private-subnet      CIDR subnet behind client (optional)

    --pass              Protect client keys with a password

    --auth-nocache      This will force OpenVPN to immediately forget username/password
                        inputs after they are used. As a result, when OpenVPN needs a
                        username/password, it will prompt for input, which may be
                        multiple times during the duration of an OpenVPN session.

EOF
exit 1
}

expand_cidr() {
    addr=$(ipcalc -n $1 | grep Address | awk '{print $2}')
    mask=$(ipcalc -n $1 | grep Netmask | awk '{print $2}')
    echo "$addr $mask"
}
which ipcalc >/dev/null || fatal "ipcalc is not installed"

if [[ "$#" < "2" ]] || [[ "$#" > "5" ]]; then
    usage
fi

client_name=$1
client_email=$2
shift; shift # remove processed arguments

unset password auth_nocache private_subnet
for item in "$@"; do
    case $item in
        --pass)
            password='--pass'
            shift;;
        --auth-nocache)
            auth_nocache="auth-nocache"
            shift;;
        *)
            if [[ -z "$private_subnet" ]]; then
                private_subnet=$1
            else
                usage
            fi
            shift;;
    esac
done

EASY_RSA=/etc/openvpn/easy-rsa
SERVER_CFG=/etc/openvpn/server.conf
SERVER_CCD=/etc/openvpn/server.ccd

SERVER_ADDR=$(grep PUBLIC_ADDRESS $SERVER_CFG | awk '{print $3}')
[ "$SERVER_ADDR" ] || fatal "unable to determine PUBLIC_ADDRESS from $SERVER_CFG"

source $EASY_RSA/vars
[ -d $KEY_DIR ] || mkdir "$KEY_DIR"
[ -e $KEY_DIR/$client_name.ovpn ] && fatal "$KEY_DIR/$client_name.ovpn exists"

KEY_CN=$client_name KEY_EMAIL=$client_email $EASY_RSA/pkitool $password

if [ "$private_subnet" ]; then
cat >> $SERVER_CFG <<EOF
# subnet behind a client: $client_name
route $(expand_cidr $private_subnet)

EOF

cat > $SERVER_CCD/$client_name <<EOF
iroute $(expand_cidr $private_subnet)
EOF
warn "openvpn needs to be restarted for changes to take effect."
fi

cat > $KEY_DIR/$client_name.ovpn <<EOF
remote $SERVER_ADDR 1194
proto udp
remote-cert-tls server
$auth_nocache

client
dev tun
resolv-retry infinite
keepalive 10 120
nobind
comp-lzo
verb 3

;user nobody
;group nogroup

<ca>
$(cat $KEY_DIR/ca.crt)
</ca>

key-direction 1
<tls-auth>
$(cat $KEY_DIR/ta.key)
</tls-auth>

<cert>
$(cat $KEY_DIR/$client_name.crt)
</cert>

<key>
$(cat $KEY_DIR/$client_name.key)
</key>
EOF

echo
info "generated $KEY_DIR/$client_name.ovpn"

