[ale] HomeVPN

Phil Turmel philip at turmel.org
Fri Nov 9 12:50:00 EST 2012


On 11/09/2012 10:54 AM, Robert L. Harris wrote:
> I will look into those android apps yet, haven't found one I like.
> 
> I've been using SSH since the early 90's,  no worries there.  I want to
> setup and play with a VPN connection for remote devices.  I have OpenVPN
> running out of a linux VM dedicated so i can lock it down and if need be
> wipe it without loss of sleep.
> 
> As my original thread went,  "Has anyone else done this?  Anyone up for
> sharing configs/discussing?"

I've been running OpenVPN on my office server to support home and road
warrior usage for about eight or nine years.  Using my own certificate
authority for the past five years or so to minimize router configuration
requirements.  Works well with both windows and linux client machines.

I've attached my config files for your edification (lightly sanitized).

The last attachment is my personal script for managing my certificate
authority

HTH,

Phil
-------------- next part --------------
#
# Road warrior VPN based on certificates instead
# of pre-shared private keys.
#
log-append /var/log/openvpn/warriors
status /var/log/openvpn/warrior-status 10
verb 1
local router.example.com
lport 1194
#mlock

# Configure basic server settings, including the
# subnet, protocol, and activity tracking.
dev tun
proto tcp
topology subnet
keepalive 44 180
float
server 192.168.18.0 255.255.255.0
route-gateway 192.168.18.1
push "route-gateway dhcp"
tun-mtu 1500
push "tun-mtu 1500"

# Security setup
ca /etc/ssl/certs/example-ca.pem
dh /etc/openvpn/dh1024.pem
cert /etc/ssl/certs/server-vpn.pem
key /etc/ssl/private/server-vpn.key
persist-key
persist-tun
push "persist-tun"
client-config-dir /etc/openvpn/warriors
ccd-exclusive
user openvpn
group openvpn

# Remember client addresses through service restarts
ifconfig-pool-persist /etc/openvpn/warrior-pool.txt

# Add routes to the client that point to subnets
# reachable through this server.
push "route 192.168.16.0 255.255.240.0"

# Preset routes to subnets that are behind
# specific clients (matching the "iroute" in
# the per-client config file.  Also push them
# down to other connected clients.
#route 192.168.2.0 255.255.255.0
#push "route 192.168.2.0 255.255.255.0"

# Allow clients to "see" each other
client-to-client
-------------- next part --------------
# Road Warrior VPN configuration.  You must have a X509 certificate
# signed by the Certificate Authority to authenticate your connection.
# The certificate's CN must also have a config file in the server's
# client-config-dir folder.  Zero-length is acceptable.

log-append /var/log/openvpn/warriors

# Common settings for all road warriors
remote router.example.com 1194
proto tcp
dev tun
client
verb 1
auth-retry nointeract
ca    /etc/ssl/certs/example-ca.pem

# Client-specific identification
cert  /etc/ssl/certs/client1-vpn.pem
key   /etc/ssl/private/client1-vpn.key
-------------- next part --------------
#! /bin/bash
#
# Alternate Mini Certificate Authority
# Copyright (C) 2010 Philip J. Turmel <philip at turmel.org>
#
# kate: tab-width 4; indent-width 4; tab-indents on; dynamic-word-wrap off;

SDIR=/etc/ssl
CAname=example-ca
SERIAL="$SDIR/CAserial.txt"
REVOKE="$SDIR/CArevoke.txt"

# Display usage assistance, clean up if needed, and exit.
function usage() {
	cat <<EOF
Usage:
	ca -h
	ca certname
	ca [-p] -c certname subj-item [subj-item ...]
	ca [-p] -r certname [subj-item ...]
	ca -x certname

Form #1 displays this help.
Form #2 displays details of an existing certificate name 'certname'.
Form #3 creates a new, signed certificate with the name 'certname'
	constructed from the given subject items.  Each subject item
	must be in the form 'name=value'.  Subject items may also be
	concatenated with '/', as they will be in the certificate.
Form #4 renews and signs a certificate with the name 'certname',
	optionally replacing or adding subject items.  Replaced subject
	items will remain in their original order.  If a subject item
	occurs multiple times (like nested OUs) in the original, and
	fewer replacements are given, the last new value replaces the
	last old value.
Form #5 revokes the certificate named 'certname' by creating a
	revocation certificate for it, and putting it on the revocation
	list.

The [-p] option requests pass-phrase protection for the new private
key.  It will be prompted for.

If 'certname' matches the configured signing certificate name, a new
or renewed signing certificate will be created, and self-signed.
EOF

	if [[ -n "$PFILE" ]] ; then
		rm "$PFILE"
	fi
	exit $1
}

# Shared code to combine existing subject strings with new subject
# items, remove unwanted subject items, and verify the presence of
# required subject items.
#
function mksubject() {
	local -a original replaced mods rmods checks
	local op key keyo keym
	# Parse the existing subject into individual items, in reverse
	# order.
	echo 'Subject:' $subject
	while [[ "$subject" =~ ^(.*)/([^/]+)$ ]] ; do
		original+=("${BASH_REMATCH[2]}")
		subject="${BASH_REMATCH[1]}"
#		echo 'Subject:' $subject
	done
	# Examine all the modifications and checks, executing the deletions
	# immediately, and storing the mods and checks for later use.
	for x in "$@" ; do
		if [[ "$x" =~ ^([+-])([^/=]+)$ ]] ; then
			op="${BASH_REMATCH[1]}"
			key="${BASH_REMATCH[2]}"
			if [[ "$op" == "+" ]] ; then
				checks+=("$key")
			else
				for y in "${!original[@]}" ; do
					subjitem="${original[$y]}"
					if [[ "$subjitem" =~ ^([^=]+)=(.+)$ ]] ; then
						if [[ "$key" == "${BASH_REMATCH[1]}" ]] ; then
							unset original[$y]
							break
						fi
					fi
				done
			fi
		else
			while [[ "$x" =~ ^([^/]+)(/(.+))?$ ]] ; do
				mods+=("${BASH_REMATCH[1]}")
				x="${BASH_REMATCH[3]}"
			done
			if [[ -n "$x" ]] ; then
				echo "Unrecognized subject item: $x !"
				exit 1
			fi
		fi
	done
	# Reverse the mods
	for x in "${!mods[@]}" ; do
		i=$(( 1000 - $x ))
		rmods[$i]="${mods[$x]}"
	done
	echo
	rmods=("${rmods[@]}")
	unset mods
	echo "Originals: ${original[*]}"
	echo "Reversed Mods: ${rmods[*]}"
	echo "Post-checks: ${checks[*]}"
	# Replace originals with mods, moving them to 'replaced' as we go.
	for x in "${!rmods[@]}" ; do
		if [[ "${rmods[$x]}" =~ ^([^=]+)=(.+)$ ]] ; then
			keym="${BASH_REMATCH[1]}"
			for y in "${!original[@]}" ; do
				if [[ "${original[$y]}" =~ ^([^=]+)=(.+)$ ]] ; then
					keyo="${BASH_REMATCH[1]}"
					if [[ "$keym" == "$keyo" ]] ; then
						replaced[$y]="${rmods[$x]}"
						unset original[$y]
						unset rmods[$x]
						break
					fi
				fi
			done
			if [[ -n "${rmods[$x]}" ]] ; then
				for y in "${!replaced[@]}" ; do
					z[$(( 1000 - $y ))]=$y
				done
				for y in "${z[@]}" ; do
					if [[ "${replaced[$y]}" =~ ^([^=]+)=(.+)$ ]] ; then
						keyo="${BASH_REMATCH[1]}"
						if [[ "$keym" == "$keyo" ]] ; then
							t="${replaced[$y]}"
							replaced[$y]="${rmods[$x]}"
							rmods[$x]="$t"
						fi
					fi
				done
				unset z
			fi
		fi
	done
	echo "Originals: ${original[*]}"
	echo "Replacements: ${replaced[*]}"
	echo "Reversed Mods: ${rmods[*]}"
	echo "Post-checks: ${checks[*]}"
	# Recombine the originals and replacements into one array.
	for y in "${!replaced[@]}" ; do
		echo "Reinserting  $y: ${replaced[$y]}, now: ${original[*]}"
		original[$y]="${replaced[$y]}"
	done
	# Reverse the remaining mods again (back to forward order)
	for x in "${!rmods[@]}" ; do
		i=$(( 1000 - $x ))
		mods[$i]="${rmods[$x]}"
	done
	mods=("${mods[@]}")
	unset rmods
	# Assemble original and replaced subject items into a single
	# subject string.  Delete satisfied checks while at it.
	subject=""
	for x in "${original[@]}" ; do
		subjitem="$x"
		if [[ "$x" =~ ^([^=]+)=(.+)$ ]] ; then
			keyo="${BASH_REMATCH[1]}"
			for y in "${!mods[@]}" ; do
				if [[ "${mods[$y]}" =~ ^([^=]+)=(.+)$ ]] ; then
					keym="${BASH_REMATCH[1]}"
					if [[ "$keyo" == "$keym" ]] ; then
						subjitem="$subjitem/${mods[$y]}"
						unset mods[$y]
					fi
				fi
			done
			for y in "${!checks[@]}" ; do
				if [[ "$keyo" == "${checks[$y]}" ]] ; then
					unset checks[$y]
				fi
			done
		fi
		subject="/${subjitem}${subject}"
	done
	# Assemble remaining subject items to the end of the subject
	# string, again deleting satisfied checks.
	for x in "${mods[@]}" ; do
		if [[ "$x" =~ ^([^=]+)=(.+)$ ]] ; then
			keym="${BASH_REMATCH[1]}"
			for y in "${!checks[@]}" ; do
				if [[ "$keym" == "${checks[$y]}" ]] ; then
					unset checks[$y]
				fi
			done
		fi
		subject="$subject/$x"
	done
	checks=("${checks[@]}")
	if [[ -n "${checks[*]}" ]] ; then
		echo -e "Subject string did not pass the required verification!"
		echo -e "The following subject items are missing: ${checks[*]}"
		exit 1
	fi
}

# Shared code to obtain an encryption passphrase for the new
# private key.
#
function getphrase() {
	read -p "Enter the desired private key passphrase: " -s pass1
	echo
	read -p "Re-enter the desired passphrase: " -s pass2
	echo
	if [[ "$pass1" != "$pass2" ]] ; then
		echo "Pass phrases don't match!"
		exit 1
	fi
	if [[ -z "$pass1" ]] ; then
		echo "No passphrase supplied!  If you really don't want one,"
		echo "omit the '-p' option from the command."
		exit 1
	fi
	chmod 0600 "$PFILE"
	echo -n "$pass1" >"$PFILE"
}

# Shared code to create a private key, with ".tmp" tacked onto the
# filename.
#
function mkprivate() {
	local PPP
	[[ -n "$PFILE" ]] && PPP="-des -passout file:$PFILE"
	echo "Creating new private key ..."
	if openssl genrsa $PPP -out "$PRIV.tmp" 2048 ; [[ $? -ge 1 ]] ; then
		echo "Unable to create the private key!"
		exit 1
	fi
	chmod 600 "$PRIV.tmp"
}

# Shared code to create and sign a certificate, with ".tmp" tacked
# on, presuming the key has already been created by the mkprivate
# function.  The subject items must already be concatenated into a
# single argument to this function.
#
function mkcertificate() {
	local PPP
	newserial=$(( 1 + $(< "$SERIAL" ) ))
	[[ -n "$PFILE" ]] && PPP="-passin file:$PFILE"
	if [[ "$CERT" == "$CACERT" ]] ; then
		echo "Creating and self signing $CERT for subject: $1"
		openssl req -new -batch \
			-subj "$1" \
			-set_serial $newserial \
			-x509 -days 3650 \
			-out "$CERT.tmp" \
			-key "$PRIV.tmp" $PPP
	else
		echo -e "Creating request $CERT.req for subject: $1"
		if openssl req -new -batch \
			-subj "$1" \
			-out "$CERT.req" \
			-key "$PRIV.tmp" $PPP ; [[ $? -ge 1 ]]
		then
			echo "Unable to create certificate request!"
			exit 1
		fi
		echo "CA signing $CERT ..."
		openssl x509 -req \
			-in "$CERT.req" \
			-out "$CERT.tmp" -outform PEM \
			-set_serial $newserial \
			-days 730 \
			-CA "$CACERT" \
			-CAkey "$CAPRIV"
	fi
	if [[ $? -ge 1 ]] ; then
		echo "Unable to create certificate!"
		exit 1
	fi
	echo -n $newserial >$SERIAL
	mv -f "$CERT.tmp" "$CERT"
	mv -f "$PRIV.tmp" "$PRIV"
	rm -f "$CERT.req"
	echo -e "Installed $CERT and $PRIV\n"
}

# Shared code to display a finished certificate in a relatively
# human-friendly format.
#
function showcert() {
	echo -e "# Certificate $CERT :"
	openssl x509 -noout -subject -issuer -dates -fingerprint -in "$CERT" \
		| while read line ; do echo "# $line" ; done
}

# Look for the various operating mode options, and set shell
# variables accordingly.
while getopts ":hpcrx" optchar ; do
	case $optchar in
		h)	usage ;;
		p)	PFILE="`mktemp`" ; chmod 0600 $PFILE ;;
		c)	CAmode="create" ;;
		r)	CAmode="renew" ;;
		x)	CAmode="revoke" ;;
		*)	echo "Unrecognized option '$optchar':" ; usage 1 ;;
	esac
done
shift $(($OPTIND - 1))

# The first remaining option must be the certificate name.
# If no certificate named, bail out with usage instructions.
#
[[ -n "$1" ]] || usage 1
CERT="$SDIR/certs/$1.pem"
PRIV="$SDIR/private/$1.key"
CACERT="$SDIR/certs/$CAname.pem"
CAPRIV="$SDIR/private/$CAname.key"
shift

# Create the serial number and revocation counter files if they
# don't yet exist.
#
[[ -s "$SERIAL" ]] || echo -n 1 >$SERIAL
[[ -s "$REVOKE" ]] || echo -n 1 >$REVOKE

# Create subdirectories if they don't yet exist
#
[[ -d "$SDIR/certs" ]]   || mkdir -p "$SDIR/certs"
[[ -d "$SDIR/crl" ]]     || mkdir -p "$SDIR/crl"
[[ -d "$SDIR/private" ]] || mkdir -p "$SDIR/private"

# Select the appropriate operations for the requested mode.
#
case "$CAmode" in
	create)
		echo "Preparing to create $CERT ..."
		if [[ "$CERT" != "$CACERT" ]] ; then
			subject="`openssl x509 -subject -noout -in \"$CACERT\"`"
			if [[ "$subject" =~ ([^/]+)(/.+) ]] ; then
				subject="${BASH_REMATCH[2]}"
			else
				echo "Unable to extract the baseline subject from the Certificate Authority!"
				exit 1
			fi
		else
			subject=""
		fi
		mksubject -CN -emailAddress "$@" +C +ST +L +O +CN
		[[ -n "$PFILE" ]] && getphrase
		mkprivate
		mkcertificate "$subject"
		;;
	renew)
		subject="`openssl x509 -subject -noout -in \"$CERT\"`"
		if [[ "$subject" =~ ([^/]+)(/.+) ]] ; then
			subject="${BASH_REMATCH[2]}"
		else
			echo "Unable to extract the original subject from $CERT !"
			exit 1
		fi
		mksubject "$@" +C +ST +L +O +CN
		[[ -n "$PFILE" ]] && getphrase
		mkprivate
		mkcertificate "$subject"
		;;
	revoke)
		echo -e "Revoke is not yet implemented! Sorry!"
		exit 1
		;;
esac

# All of the operations except revoke return here to display the
# finished certificate.
#
showcert


More information about the Ale mailing list