Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

https interface for user management #648

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 24 additions & 9 deletions Dockerfile
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,31 +1,46 @@
# Original credit: https://github.com/jpetazzo/dockvpn
# Original credit: https://github.com/kylemanna/docker-openvpn

# Smallest base image
FROM alpine:latest

LABEL maintainer="Kyle Manna <[email protected]>"
LABEL maintainer="Nanda Bhikkhu <[email protected]>"

# Testing: pamtester
RUN echo "http:https://dl-cdn.alpinelinux.org/alpine/edge/testing/" >> /etc/apk/repositories && \
apk add --update openvpn iptables bash easy-rsa openvpn-auth-pam google-authenticator pamtester libqrencode && \
apk add --update openvpn iptables bash easy-rsa openvpn-auth-pam google-authenticator pamtester libqrencode lighttpd lighttpd-mod_auth coreutils && \
ln -s /usr/share/easy-rsa/easyrsa /usr/local/bin && \
rm -rf /tmp/* /var/tmp/* /var/cache/apk/* /var/cache/distfiles/*

# Needed by scripts
ENV OPENVPN=/etc/openvpn
ENV EASYRSA=/usr/share/easy-rsa \
EASYRSA_CRL_DAYS=3650 \
EASYRSA_PKI=$OPENVPN/pki
EASYRSA_CERT_RENEW=3650 \
EASYRSA_PKI=$OPENVPN/pki \
EASYRSA_BATCH=1 \
OVPN_USER=openvpn \
OVPN_GROUP=openvpn

ADD ./lighttpd/htdocs/ /var/www/localhost/htdocs/
ADD ./lighttpd/config/* /etc/lighttpd/
RUN OPENVPN=$(echo $OPENVPN | sed -e 's/\//\\\//g') && sed -i /etc/lighttpd/lighttpd.conf -e 's/server\.username.*/server.username\ =\ "'$OVPN_USER'"/' \
-e 's/server\.groupname.*/server.groupname\ =\ "'$OVPN_GROUP'"/' \
-e 's/^var\.ovpndir.*$/var.ovpndir\ =\ "'${OPENVPN}'"/' && \
chown -R ${OVPN_USER}:${OVPN_GROUP} /var/www/localhost/htdocs /etc/lighttpd /var/log/lighttpd

ADD ./bin /usr/local/bin
RUN chmod 755 /usr/local/bin/* && chown root:${OVPN_GROUP} /usr/local/bin/*

# Add support for OTP authentication using a PAM module
ADD ./otp/openvpn /etc/pam.d/

VOLUME ["/etc/openvpn"]
VOLUME ["/var/log"]

# Internally uses port 1194/udp, remap using `docker run -p 443:1194/tcp`
EXPOSE 1194/udp
EXPOSE 443/tcp

CMD ["ovpn_run"]
USER $OVPN_USER:$OVPN_GROUP

ADD ./bin /usr/local/bin
RUN chmod a+x /usr/local/bin/*

# Add support for OTP authentication using a PAM module
ADD ./otp/openvpn /etc/pam.d/
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ a corresponding [Digital Ocean Community Tutorial](http:https://bit.ly/1AGUZkq).

* Start OpenVPN server process

docker run -v $OVPN_DATA:/etc/openvpn -d -p 1194:1194/udp --cap-add=NET_ADMIN kylemanna/openvpn
docker run --user root -v $OVPN_DATA:/etc/openvpn -v $OVPN_LOGS:/var/log -d -p 1194:1194/udp -p 443:443 --cap-add=NET_ADMIN kylemanna/openvpn

* Generate a client certificate without a passphrase

Expand Down Expand Up @@ -132,6 +132,19 @@ and they might not answer to you. If that happens, use public DNS
resolvers like those of Google (8.8.4.4 and 8.8.8.8) or OpenDNS
(208.67.222.222 and 208.67.220.220).

## Https user admin
If you wish to use https interface for configuring the clients
* Pass -H to ovpn_genconfig
docker run -v $OVPN_DATA:/etc/openvpn -v $OVPN_LOG:/var/log -H --rm kylemanna/openvpn ovpn_genconfig -u udp:https://VPN.SERVERNAME.COM
It will ask you to specify username/password for the interface & for domain name for https certificate
* If you wish to supply your certificate/key for the https server, place it in $OPENVPN/http/ and set ownership and access rights.
cp server.pem server.key $OPENVPN/http/
chown 101:102 $OPENVPN/http/server*
chmod 600 $OPENVPN/http/server.key
chmod 644 $OPENVPN/http/server.cer
* Be aware that the lighttpd log files are not logrotated.

NOTE: requires easy-rsa 3.0.8 or higher (make sure you make the docker image from alpine 3.13 or so)

## Security Discussion

Expand Down
70 changes: 70 additions & 0 deletions bin/openssl-htpasswd
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash
#

print_help()
{
echo "$(basename \"$0\") [-Dci] passwdfile username [password]"
echo -e "\t-D delete given user"
echo -e "\t-c truncate file (will contain only new user"
echo -e "\t-i read password from stdin"
echo -e "\t-h print this help"
}

ensure_pwd()
{
[ "$STDIN" = 1 ] && read -s PWD
[ -z "$PWD" ] && echo "missing or empty password" >&2 && print_help >&2 && exit 1
PWD=$(openssl passwd -6 "$PWD")
}

action="modify"
while getopts "Dcid" arg; do
case $arg in
D)
action="delete"
;;
c)
action="create"
;;
i)
STDIN=1
;;
*)
echo "invalid usage" >&2
print_help >&2
exit 1
;;
esac
done
shift $(($OPTIND - 1))

FILE="$1"
USER="$2"
PWD="$3"

[ -z "$USER" ] && echo "missing username" >&2 && print_help >&2 && exit 1
[ -z "$FILE" ] && echo "missing filename" >&2 && print_help >&2

case "$action" in
delete)
[ ! -f "$FILE" ] && exit 0
tmp=$(mktemp)
grep -v "^$USER:" "$FILE" > "$tmp"
cat "$tmp" > "$FILE" && rm "$tmp" && exit 0
;;

create)
ensure_pwd
echo "$USER:$PWD" > "$FILE" && exit 0
;;

modify)
ensure_pwd
tmp=$(mktemp)
(grep -v "^$USER:" "$FILE" 2>/dev/null; echo "$USER:$PWD") > "$tmp" && cat "$tmp" > "$FILE" && rm "$tmp" && exit 0
;;
esac

# should not reach here
exit 1

30 changes: 29 additions & 1 deletion bin/ovpn_genconfig
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ usage() {
echo " [-p PUSH ...]"
echo " [-r ROUTE ...]"
echo " [-s SERVER_SUBNET]"
echo " [-H]"
echo
echo "optional arguments:"
echo " -2 Enable two factor authentication using Google Authenticator."
Expand All @@ -104,6 +105,7 @@ usage() {
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."
echo " -H Enable admin https server"
}

process_route_config() {
Expand All @@ -129,6 +131,21 @@ process_extra_config() {
[[ -n "$ovpn_extra_config" ]] && echo "$ovpn_extra_config" >> "$TMP_EXTRA_CONFIGFILE"
}

gen_lighttpd_config() {
read -p "https username:" http_user || return 1
read -sp "$http_user password:" http_password || return 1
openssl-htpasswd "$OPENVPN/http/htpasswd" "$http_user" "$http_password"

if [ ! -f "$OPENVPN/http/server.key" ]; then
echo "generating https server cert..."
read -p "https server domain/ip:" cn || return 1
openssl req -x509 -newkey rsa:4096 -nodes -subj "/CN=$cn" -keyout "$OPENVPN/http/server.key" -out "$OPENVPN/http/server.pem" -days +365 || return 1
chown openvpn:openvpn "$OPENVPN"/http/server.*
fi

return 0
}

if [ "${DEBUG:-}" == "1" ]; then
set -x
fi
Expand Down Expand Up @@ -167,12 +184,15 @@ OVPN_ROUTES=()
OVPN_SERVER=192.168.255.0/24
OVPN_SERVER_URL=''
OVPN_TLS_CIPHER=''
OVPN_HTTPS_ADMIN=0
OVPN_USER=openvpn
OVPN_GROUP=openvpn

# Import existing configuration if present
[ -r "$OVPN_ENV" ] && source "$OVPN_ENV"

# Parse arguments
while getopts ":a:e:E:C:T:r:s:du:bcp:n:k:DNm:f:tz2" opt; do
while getopts ":a:e:E:C:T:r:s:du:bcp:n:k:DNm:f:tz2H" opt; do
case $opt in
a)
OVPN_AUTH="$OPTARG"
Expand Down Expand Up @@ -253,6 +273,9 @@ while getopts ":a:e:E:C:T:r:s:du:bcp:n:k:DNm:f:tz2" opt; do
f)
OVPN_FRAGMENT="$OPTARG"
;;
H)
OVPN_HTTPS_ADMIN=1
;;
\?)
set +x
echo "Invalid option: -$OPTARG" >&2
Expand Down Expand Up @@ -409,4 +432,9 @@ if diff -q "${bak:-}" "$conf" 2>/dev/null; then
rm -fv "$bak"
fi

[ "$OVPN_HTTPS_ADMIN" = 1 ] && gen_lighttpd_config

# just to be sure if someone run this as root
chown -R openvpn:openvpn "$OPENVPN" "/var/www" "/etc/lighttpd"

echo "Successfully generated config"
16 changes: 16 additions & 0 deletions bin/ovpn_run
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ addArg "--config" "$OPENVPN/openvpn.conf"

source "$OPENVPN/ovpn_env.sh"

[ -n "${OVPN_USER}" ] && addArg "--user" "${OVPN_USER}"
[ -n "${OVPN_GROUP}" ] && addArg "--group" "${OVPN_GROUP}"

mkdir -p /dev/net
if [ ! -c /dev/net/tun ]; then
mknod /dev/net/tun c 10 200
Expand Down Expand Up @@ -101,5 +104,18 @@ if [ $? = 0 ]; then
fi
fi

chown -R openvpn:openvpn "$OPENVPN"

echo "running https admin iface"
if [ "$OVPN_HTTPS_ADMIN" = 1 ]; then
env | grep EASYRSA | while read line; do echo "export $line"; done > $OPENVPN/http/easyrsa.env && chown openvpn $OPENVPN/http/easyrsa.env
lighttpd -f /etc/lighttpd/lighttpd.conf
fi

# TODO: ovpn user
echo "Running 'openvpn ${ARGS[@]} ${USER_ARGS[@]}'"
exec openvpn ${ARGS[@]} ${USER_ARGS[@]}

echo "shutting down https admin iface"
[ "$OVPN_HTTPS_ADMIN" = 1 ] && [ -f /var/run/lighttpd.pid ] && kill -TERM $(cat /var/run/lighttpd.pid)