2017.05 - by Krzysztof "Chris" Pfaff - guide for OpenBSD 6.1, openvpn-2.4.1, easy-rsa-3.0.1 (Initially created for OpenBSD 5.5 with separate EasyRsa3.0.0rc2 then updated)
It should also work for future versions - might just need some modifications.
NO WARRANTY, NO RESPONSIBILITY - you are fully responsible for the systems you configure/maintain/change. This guide is only for information purposes. Some errors and non-working commands or unpredicted side-effects are possible.
BSD License applies for this guide. Feel free to distribute, edit, re-use the document as needed, but please acknowledge the author and source of document in the web.
pkg_add openvpn
could be installed also from ports, but that is not needed
In short - it is necessary to create:
Going step by step, first prepare the directories. Execute as root:
install -m 700 -d /etc/openvpn/private
install -m 700 -d /etc/openvpn/private-client-conf
install -m 755 -d /etc/openvpn/certs
install -m 755 -d /var/log/openvpn
Prepare dirs and symlinks allowing to run openvpn chrooted or not-chrooted. Some files should be located under chroot jail directory and /etc/openvpn should have symlinks to them (so you can run chrooted or not and it will open the same files):
install -m 755 -d /var/openvpn/chrootjail/etc/openvpn
install -m 755 -d /etc/openvpn/chrootjail/etc/openvpn/ccd # client custom configuration dir
install -m 755 -d /var/openvpn/chrootjail/var/openvpn
mv /etc/openvpn/ccd/ /etc/openvpn/crl.pem /var/openvpn/chrootjail/etc/openvpn/
ln -s /var/openvpn/chrootjail/etc/openvpn/crl.pem /etc/openvpn/crl.pem
ln -s /var/openvpn/chrootjail/etc/openvpn/ccd/ /etc/openvpn/
ln -s /var/openvpn/chrootjail/etc/openvpn/replay-persist-file /etc/openvpn/replay-persist-file
ls -alpd /etc/openvpn/certs /etc/openvpn/ccd /var/openvpn/chrootjail /var/openvpn/chrootjail/etc /var/openvpn/chrootjail/etc/openvpn /etc/openvpn/private /etc/openvpn/private-client-conf /var/log/openvpn
The output should look like this:
lrwxr-xr-x 1 root bin 40 Nov 30 09:52 /etc/openvpn/ccd -> /var/openvpn/chrootjail/etc/openvpn/ccd/ drwxr-xr-x 2 root wheel 512 Nov 23 11:51 /etc/openvpn/certs/ drwx------ 2 root wheel 512 Nov 23 11:50 /etc/openvpn/private/ drwx------ 2 root bin 512 Nov 25 20:55 /etc/openvpn/private-client-conf/ drwxr-xr-x 2 root wheel 512 Mar 4 2013 /var/log/openvpn/ drwxr-xr-x 4 root wheel 512 Nov 30 09:51 /var/openvpn/chrootjail/ drwxr-xr-x 3 root wheel 512 Nov 30 09:46 /var/openvpn/chrootjail/etc/ drwxr-xr-x 3 root wheel 512 Nov 30 09:25 /var/openvpn/chrootjail/etc/openvpn/
Guide below is using easy-rsa 3.
In case it is no longer included in openvpn package, needs to be downloaded separately and untared
EasyRsa can be downloaded from https://github.com/OpenVPN/easy-rsa/releases
pkg_add easy-rsa
cd /usr/local/share/easy-rsa/
ls -alpd easyrsa vars*
less vars.example
Review the vars.example, but defaults should be ok. Make sure that CA, certificate and CRL lifetime is set to at least 10 years (unless you want to regenerate them more often):
#set_var EASYRSA_CA_EXPIRE 3650 #set_var EASYRSA_CERT_EXPIRE 3650 #set_var EASYRSA_CRL_DAYS 3650
If it is necessary to change the parameters - then create vars file:
if [ -f /usr/local/share/easy-rsa/vars ]; then
echo "File already exists - verify: /usr/local/share/easy-rsa/vars";
cat /usr/local/share/easy-rsa/vars;
else
printf "set_var EASYRSA_CA_EXPIRE 3650\nset_var EASYRSA_CERT_EXPIRE 3650\nset_var EASYRSA_CRL_DAYS 3650" > /usr/local/share/easy-rsa/vars;
cat /usr/local/share/easy-rsa/vars;
fi
If you have certificates and keys and client certificates created by easy-rsa2 then you might want to use the existing CA and certificates (because using new CA would make old client certificates useless - and they would need to be regenerated).
In such case search forward in this document for Appendix A - migrate EasyRsa2 certificates to EasyRsa3
If you get an error with easyrsa like 4098901262292:error:0E065068:configuration file routines:STR_COPY:variable has no value:/usr/src/lib/libcrypto/crypto/../../libssl/src/crypto/conf/conf_def.c:573:line 3
or Expected to find openssl command at: openssl
then search forward in this document for Appendix B - fixing error STR_COPY variable has no value
That was needed in November 2016 with OpenBsd 5.9 and EasyRSA-3.0.0-rc2
If you get an error with easyrsa like Missing or invalid OpenSSL
or Expected to find openssl command at: openssl
then search forward in this document for Appendix C - fixing error Missing or invalid OpenSSL
That was needed in November 2014 with OpenBsd 5.6 and EasyRSA-3.0.0-rc2
pkiDir="/etc/openvpn/easy-rsa-pki/"
mkdir ${pkiDir}
easyrsaDir="/usr/local/share/easy-rsa/"
cd ${easyrsaDir}
ls -alp /etc/openvpn/private/vpn-ta.key || openvpn --genkey --secret /etc/openvpn/private/vpn-ta.key
./easyrsa --batch=0 --pki-dir=${pkiDir} init-pki # creates empty dirs ${pkiDir}/ ${pkiDir}/private/ ${pkiDir}/reqs/ . batch=1 - overwrite/delete without asking
ls -alpd ${pkiDir} ${pkiDir}/*
./easyrsa --batch=1 --pki-dir=${pkiDir} gen-dh
ls -alpd ${pkiDir}/dh.pem
./easyrsa --batch=1 --pki-dir=${pkiDir} --req-cn=vpn-ca build-ca nopass
ls -alpd ${pkiDir}/ca.crt ${pkiDir}/private/ca.key ${pkiDir}/index.txt ${pkiDir}/serial
openssl x509 -in ${pkiDir}/ca.crt -text -noout
openssl rsa -in ${pkiDir}/private/ca.key -check -noout
alternatively - vpnserver admin does
./easyrsa gen-req _UNIQUE_SERVER_SHORT_NAME_
and send the .req to CA just to sign/accept.
./easyrsa --batch=1 --pki-dir=${pkiDir} --req-cn=vpnserver gen-req vpnserver nopass
ls -alpd ${pkiDir}/reqs/vpnserver.req ${pkiDir}/private/vpnserver.key
openssl req -in ${pkiDir}/reqs/vpnserver.req -text -noout
openssl rsa -in ${pkiDir}/private/vpnserver.key -check -noout
./easyrsa --batch=1 --pki-dir=${pkiDir} show-req vpnserver
./easyrsa --batch=1 --pki-dir=${pkiDir} sign server vpnserver # server - only for server
ls -alpd ${pkiDir}/issued/vpnserver.crt ${pkiDir}/certs_by_serial/01.pem
openssl x509 -in ${pkiDir}/issued/vpnserver.crt -text -noout
echo "Last added cert in db: `cat ${pkiDir}/index.txt|tail -1`"
echo "Next added cert will have number: `cat ${pkiDir}/serial`"
Revoked certificates can be added later. Initially creating file with no revocated certificates. This file must exist, otherwise openvpn will complain.
./easyrsa --batch=1 --pki-dir=${pkiDir} gen-crl
# make sure openvpn process can read this file - otherwise it will crash
chown :_openvpn ${pkiDir}/crl.pem; chmod g+r ${pkiDir}/crl.pem
ls -alF ${pkiDir}/crl.pem
openssl crl -in ${pkiDir}/crl.pem -text -noout
cp -p ${pkiDir}/ca.crt /etc/openvpn/certs/vpn-ca.crt
cp -p ${pkiDir}/private/ca.key /etc/openvpn/private/vpn-ca.key
cp -p ${pkiDir}/issued/vpnserver.crt /etc/openvpn/certs/vpnserver.crt
cp -p ${pkiDir}/private/vpnserver.key /etc/openvpn/private/vpnserver.key
cp -p ${pkiDir}/dh.pem /etc/openvpn/dh.pem
cp -p ${pkiDir}/crl.pem /etc/openvpn/crl.pem
openssl x509 -in /etc/openvpn/certs/vpn-ca.crt -text -noout
openssl x509 -in /etc/openvpn/certs/vpnserver.crt -text -noout
openssl crl -in /etc/openvpn/crl.pem -text -noout
openssl rsa -in /etc/openvpn/private/vpn-ca.key -check -noout
openssl rsa -in /etc/openvpn/private/vpnserver.key -check -noout
OpenVpn had management interface where admin can connect and check status, active clients, etc.
It is nice feature, but the communication is clear text - so do NOT make it available from the network, but only internally for admin on localhost. Limit it on the firewall. Then later, when openvpn is running, connect with ssh to the openvpn server and then connect to openvpn management console using telnet localhost 1195
In the next step the password for such interface will be set-up.
test -f /etc/openvpn/private/mgmt.pwd || touch /etc/openvpn/private/mgmt.pwd
chown root:wheel /etc/openvpn/private/mgmt.pwd; chmod 600 /etc/openvpn/private/mgmt.pwd
vi /etc/openvpn/private/mgmt.pwd # insert one line of text with clear-text password for management console
Set the password in this file - insert one line of text with clear-text password for management console. You can note the password in safe place - or - if you forget it, look inside this file, password is stored in clear text.
In this example openvpn will run on tun0 with IPs in range 10.8/16 and will provide access to local network 192.168.0/24. Server config filename is server_tun0.conf
test -f /etc/openvpn/server_tun0.conf || touch /etc/openvpn/server_tun0.conf
chown root:_openvpn /etc/openvpn/server_tun0.conf; chmod 640 /etc/openvpn/server_tun0.conf
vi /etc/openvpn/server_tun0.conf # setup OpenVpn server
Edit the config file according to your needs. The openvpn's example/default server.conf
itself contains quite nice descriptions.
You can use the example described below for setting of parameters - this list all parameters that I changed.
You can use that as server_tun0.conf
- or edit your server_tun0.conf
using below as an example:
cat /etc/openvpn/server_tun0.conf | grep '^[^#;]' | grep CUSTOMIZED
vi /etc/openvpn/server_tun0.conf
ca /etc/openvpn/certs/vpn-ca.crt # CUSTOMIZED by adminOfMine #
cert /etc/openvpn/certs/vpnserver.crt # CUSTOMIZED by adminOfMine #
key /etc/openvpn/private/vpnserver.key # CUSTOMIZED by adminOfMine #
dh /etc/openvpn/dh.pem # CUSTOMIZED by adminOfMine #
ifconfig-pool-persist /var/openvpn/ipp.txt # CUSTOMIZED by adminOfMine # put it to var as it is dynamic file
tls-auth /etc/openvpn/private/vpn-ta.key 0 # This file is secret # CUSTOMIZED by adminOfMine #
replay-persist /etc/openvpn/replay-persist-file # CUSTOMIZED by adminOfMine #
max-clients 100 # CUSTOMIZED by adminOfMine #
status /var/log/openvpn/openvpn-status.log # CUSTOMIZED by adminOfMine #
log-append /var/log/openvpn/openvpn.log # CUSTOMIZED by adminOfMine #
proto tcp #or use udp if works better # CUSTOMIZED by adminOfMine #
port 1194 # CUSTOMIZED by adminOfMine #
management 127.0.0.1 1195 /etc/openvpn/private/mgmt.pwd # CUSTOMIZED by adminOfMine #
daemon openvpn # CUSTOMIZED by adminOfMine #
chroot /var/openvpn/chrootjail # CUSTOMIZED by adminOfMine #
crl-verify /etc/openvpn/crl.pem # CUSTOMIZED by adminOfMine #
float # CUSTOMIZED by adminOfMine #
persist-key # CUSTOMIZED by adminOfMine #
persist-tun # CUSTOMIZED by adminOfMine #
#Additional authorization options - needs to be configured - read further before enabling this
;auth-user-pass-verify /var/openvpn/custom-simple-auth via-env # CUSTOMIZED by adminOfMine #
;script-security 3 # CUSTOMIZED by adminOfMine #
# The following options were set by default
keepalive 10 120
comp-lzo
user _openvpn
group _openvpn
verb 3
# If you would prefer to start openvpn directly and NOT via /etc/hostname - then you need to edit and uncomment those parameters
;dev tun0 # CUSTOMIZED by admin-of-mine #
;server 10.8.0.0 255.255.255.0 # Address range for the tun(4)interfaces # CUSTOMIZED by adminOfMine #
# Add a route to the local network to the client's routing table
;push "route 192.168.0.0 255.255.255.0" # CUSTOMIZED by adminOfMine #
# If you want to push DNS server setting to VPN clients - uncomment below and see openvpn documentation for more info
;push "dhcp-option DNS 192.168.0.x" # CUSTOMIZED by adminOfMine #
# If you use samba and want to have wins (windows netbios) name resolution then add WINS (samba nmbd) server IP
;push "dhcp-option WINS 192.168.0.x" # CUSTOMIZED by adminOfMine #
The example below allows vpn clients to access the whole internal network and gives limited access to the openvpn-host.
It is not complete pf.conf, it is just showing example openvpn related entries.
Adjust the file to your configuration and needs. Restrict the rules if necessary.
log_opt = "log" # Set to ="log" to enable pflog for rulesfiles that use this macro. Setting to ="" will do no logging to pflog
int_if="em0"
tun_if="tun0" # open-vpn interface
ext_if="re0" # external connections are forwarded to this interface
allowIncoming_ifs="{" $int_if $tun_if "}" # interfaces where (internal) incoming traffic is allowed
table <myself> const { self }
table <myBroadcast> const { self:broadcast }
table <private> const { 10/8, 172.16/12, 192.168/16 }
table <internet> const {0.0.0.0/0, !10/8, !172.16/12, !192.168/16 }
vpnNetwork="10.8/16"
table <vpnNet> const { $vpnNetwork }
table <allowedIncomingLan> const { self:network $vpnNetwork }
table <allowedIncomingExternal> const { 0/0 } # these will be public IPs from the outside
SpecialAddressReachableViaVpn="192.168.99.xx"
lan_tcp_services="{ 22, 53, 80, 139, 445, 113, 1194 }"
lan_udp_services="{ 53, 137:138, 1194 }"
#... other rules, scrub, antispoof, etc.
## From LAN and VPN to OPENVPN-SERVER -- INCOMING
# allow incoming TCP to certain services - including OPENVPN port 1194
pass in quick $log_opt on $allowIncoming_ifs proto tcp from <allowedIncomingLan> to <myself> port $lan_tcp_services label "incomingToHost"
# allow incoming UDP to certain services - including OPENVPN port 1194:
pass in quick $log_opt on $allowIncoming_ifs proto udp from <allowedIncomingLan> to <myself> port $lan_udp_services label "incomingToHost"
pass in quick $log_opt on $allowIncoming_ifs proto udp from <allowedIncomingLan> to <myBroadcast> port { 137:138 } # netbios name/datagram broadcasts
# allow incoming ping
pass in quick $log_opt on $allowIncoming_ifs inet proto icmp from { <allowedIncomingLan> } to <myself> icmp-type 8 code 0 label "icmp-in"
# anything else is explicitly blocked here (so that rules allowing for routing and vpn will not allow what is not wanted)
# block access to this host - whatever was not matched earlier
block in quick $log_opt on $allowIncoming_ifs from { <allowedIncomingLan> } to <myself>
## From LAN (VPN) to LAN -- TRAVERSING - INBOUND
# allow VPN access the LAN
# Note: there is no network defined on $tun_if, so cannot use syntax like: pass ... from ($tun_if:network:0) to ...
pass in quick $log_opt on $tun_if from <vpnNet> to ($int_if:network) label "vpn-in"
pass in quick $log_opt on $tun_if from <vpnNet> to $SpecialAddressReachableViaVpn label "vpn-in-special"
## From OPENVPN-SERVER -- OUTGOING - and - From LAN (VPN) to LAN -- TRAVERSING - OUTBOUND
# allow and do NAT for VPN - the VPN clients will connect to other hosts in internal network using IP of this host as source IP
# nat is not needed if the openvpnserver is the default router for all computers on LAN.
pass out quick $log_opt on $int_if to ($int_if:network) received-on $tun_if nat-to ($int_if:0) label "vpn-out-to-network"
# INTERNET -- OUTGOING - allow LAN hosts to reach internet, etc. Do modulate state for tcp.
##match out on egress inet from !(egress:network) to any nat-to (egress:0)
pass out quick $log_opt on $ext_if from <hostsAllowedToAccessInternet> to <internet> nat-to ($ext_if:0) modulate state label "lan-out-to-internet"
pass out quick $log_opt on $tun_if from <vpnNet> to $SpecialAddressReachableViaVpn label "vpn-out-special"
## INTERNET -- INCOMING -- CAREFULLY
# allow incoming connection from the internet - allow to connect to OPENVPN on UDP and TCP
pass in quick $log_opt on $allowIncomingExternal_ifs proto udp from <allowedIncomingExternal> to <myself> port { 1194 } label "incoming-external:$srcaddr"
pass in quick $log_opt on $allowIncomingExternal_ifs proto tcp from <allowedIncomingExternal> to <myself> port { 1194 } label "incoming-external:$srcaddr"
block $log_opt
make sure you have firewall correctly setup to block unwanted traffic
vi /etc/sysctl.conf
and enable ip forwarding - change corresponding line to:
net.inet.ip.forwarding=1
The change above will activate only after next reboot, so start ip-forwarding also in currently running system with command:
sysctl net.inet.ip.forwarding=1
Execute (adjust parameters if necessary):
/usr/local/sbin/openvpn --daemon --config /etc/openvpn/server_tun0.conf --dev tun0 --server 10.8.0.0 255.255.255.0 --push "route 192.168.0.0 255.255.255.0"
Check:
Interface should be up and have IPs assigned. Check device that you have put in config file, eg tun0
ifconfig tun0
process must be running
pgrep -fl openvpn.*server
Verify if process is listening on proper interface (reachable from outside), proper protocol (tcp/udp) and port:
netstat -an|grep '\.1194 '
# put port defined in server_tun0.conf
check logs
ls -al /var/log/openvpn/*
If something goes wrong, try with
/usr/local/sbin/openvpn --daemon --verb 11 --config /etc/openvpn/server_tun0.conf
and check logs.
list running processes:
pgrep -fl openvpn
pgrep -xl openvpn
stop all running openvpn processes:
pkill -x openvpn
/etc/hostname.*
It is recommended to start openvpn from /etc/hostname.*
file. Also IP addresses and other network parameters are normally configured in /etc/hostname.*
, so it would be nice to configure it there.
On the other hand - it is also comfortable to use rc scripts to start and stop components.
The Solution presented below will combine both. The openvpn will be started and partially configured from /etc/hostname.*
. And rc script can be used for starting and stopping openvpn. The rc script will use /etc/hostname.*
files to start openvpn(s).
Multiple openvpns can be started - with different config - running either as server or client.
/etc/hostname.*
Define openvpn tunnels in /etc/hostname.*
following the example below
vi /etc/hostname.tun0
up
group openvpn
description "OpenVPN to local net 192.168.0.x"
!/usr/local/sbin/openvpn --daemon --config /etc/openvpn/server_$if.conf --dev $if --server 10.8.0.0 255.255.255.0 --push "route 192.168.0.0 255.255.255.0" & false
# If you want to push DNS server setting to VPN clients (see openvpn documentation for more info) then add to openvpn parameters above:
# --push "dhcp-option DNS 192.168.0.x"
# If you use samba and want to have wins (windows netbios) name resolution then add WINS (samba nmbd) server IP the add above also:
# --push "dhcp-option WINS 192.168.0.x"
#
# WARNING:
# BOOTING process can be blocked, sshd not started and remote access blocked if this script fails!!!
# If you change anything in script, test with: sh /etc/netstart tunX; echo "exitCode=$?"
# Replace tunX with proper network interface name. Verify that process started.
# Make sure script runs non-interactively and the exit code is 0.
# Otherwise it will interrupt booting process!!!
# Use '& false' at the end. The '&' prevents interactive data input, eg passwords.
# The 'false' command makes sure that no error status code will be returned.
# do not use exit command in here - or rc booting would be interrupted.
#
# You can use variable $if = interface, eg $if=tun0, so you can use --config /etc/openvpn/server_$if.conf
#
# The file /etc/openvpn/server_$if.conf must exist
# If you change binary name or --daemon option in command above - also change matching pattern in /etc/rc.d/openvpn script
#
#
# In case you need more /dev/tun devices, eg tun4, do: cd /dev && { ./MAKEDEV tun4; cd - ; }
# For debug - you can add/uncomment command like:
# !echo "Starting openvpn on $if"
#
Note that --dev and --server and --push route options are defined directly in /etc/hostname file, so that network config is listed in hostname file. It is also possible to move those options into openvpn server.conf if preferred.
WARNING: It is important to add || false
so that even if openvpn fails to start, the booting process will continue. Without this command, in case of error (eg. during system update when openvpn package was temporarily uninstalled) - the rc script would switch to admin console during booting - blocking startup of ssh and further network. And that could be very bad on remotely-managed systems.
Also, use || false
and do not try to use exit
command in /etc/hostname as it would interrupt execution of rc script during startup and sshd would not be started.
and example for client openvpn to be started during startup or via rc script (but make sure it needs no manual entering of password):
vi /etc/hostname.tun3
up
group openvpnclient
description "OpenVPN client to local openvpn server"
!/usr/local/sbin/openvpn --daemon --config /etc/openvpn/vpnclient-unixlocaltest/vpnclient-unixlocaltest.conf.ovpn --dev $if & false
#!/usr/local/sbin/openvpn --daemon --config /etc/openvpn/server_$if.conf --dev $if --server 10.8.0.0 255.255.255.0 --push "route 192.168.0.0 255.255.255.0" & false
#
# WARNING:
# BOOTING process can be blocked, sshd not started and remote access blocked if this script fails!!!
# If you change anything in script, test with: sh /etc/netstart tunX; echo "exitCode=$?"
# Replace tunX with proper network interface name. Verify that process openvpn started.
# Make sure script run NON-interactively and the exit code is 0.
# Otherwise it will interrupt booting process!!!
# Use '& false' at the end. The '&' prevents interactive data input, eg passwords.
# The 'false' command makes sure that no error status code will be returned.
# do not use exit command in here - or rc booting would be interrupted.
#
# You can use variable if = interface, eg if=tun0, so you can use --config /etc/openvpn/server_$if.conf
#
# The file /etc/openvpn/server_$if.conf must exist
# If you change binary name or --daemon option in command above - also change matching pattern in /etc/rc.d/openvpn script
#
#
# In case you need more /dev/tun devices, eg tun4, do: cd /dev && { ./MAKEDEV tun4; cd - ; }
# For debug - you can add/uncomment command like:
# !echo "Starting openvpn on $if"
#
Once this is configured then:
pkill -x openvpn
sh /etc/netstart tun0
or sh /etc/netstart tun0 tun1 tun2 tun3
/etc/rc.d/openvpn
- OPTIONALThis is optional step - if you want to use /etc/rc.d/openvpn script to start or stop VPNs then continue.
The openvpn script in rc framework can be used to start/stop multiple openvpns.
Create /etc/rc.d/openvpn
In case file exists - review it
If such file does not exist - create it as shown below.
cat /etc/rc.d/openvpn && echo && echo "**** File already exists - just review if it is OK"
vi /etc/rc.d/openvpn
The script will act in similar way like normal rc scripts.
The script might need adjustments in Openbsd versions newer than 5.6, especially if /etc/rc.d/rc.subr has changed. Correct the script if necessary
Put the following content to /etc/rc.d/openvpn
:
#!/bin/sh
#
# openvpn rc.d script for OpenBSD - by Chris Pfaff
#
# This script acts like normal rc script if the openvpn_flags in rc.conf.local are set to typical openvpn parameters
# But if openvpn_flags are set to special string "-use-etc-hostname-" then script acts in special way:
# It starts the openvpns configured in /etc/hostname.if (eg in /etc/hostname.tun0)
# It will start multiple openvpn - each with its own config defined in /etc/hostname.tun*
# If script detects that a process is already running - it does not try to start again but returns with ok status
# It supports usual parameters like: start, check, stop, restart, reload
#
# -d = debug mode - use to see verbose information about executed commands
#
daemon="/usr/local/sbin/openvpn"
. /etc/rc.d/rc.subr
if [ "$daemon_flags" != "-use-etc-hostname-" ]; then
# handle as usual
rc_cmd $1
else
#special unusual handling
#start openvpns defined in /etc/hostname.*
# Edit if necessary
# This pattern is being used for scanning /etc/hostname.tun*
# and for finding running openvpn processes
# and for searching individual openvpn process - then patter is appended with: [^a-zA-Z]${hnif}([^0-9]|$)
processpattern='openvpn.*--daemon.*'
[ -n "${_RC_DEBUG}" ] || _n="-n"
PGREPFLAG="-f"
PKILLFLAG="-f"
[ -z "${_RC_DEBUG}" ] && PKILLFLAG="$PGREPFLAG -q" # quiet if not debug mode
[ -z "${_RC_DEBUG}" ] && PGREPFLAG="$PGREPFLAG -q" # quiet if not debug mode
[ -n "${_RC_DEBUG}" ] && PGREPFLAG="$PGREPFLAG -l" # list process name in debug mode
case "$1" in
start|check)
[ "$1" == "start" ] && echo $_n "${INRC:+ }${_name} "
if [ X"${daemon_flags}" = X"NO" ]; then
_rc_err "$0: need -f to force $1 since ${_name}_flags=NO"
exit 1
fi
allprocstatus=0
# search all hostname.tun* files which contain openvpn startup command
hostnamepattern="/etc/hostname.tun*"
for hostnamefile in $hostnamepattern; do
if [ "$hostnamefile" = "$hostnamepattern" ]; then
[ -n "${_RC_DEBUG}" ] && echo "no files $hostnamepattern"
[ "$2" == "quiet" ] && exit 1
_rc_exit failed
fi
[ -n "${_RC_DEBUG}" ] && echo
if grep -v "^[ ]*#" "$hostnamefile" | grep -q "$processpattern" ; then
[ -n "${_RC_DEBUG}" ] && echo "File $hostnamefile is openvpn file - will be started/checked"
else
[ -n "${_RC_DEBUG}" ] && echo "File $hostnamefile is not related to openvpn - skipping"
continue
fi
# extract interface name from filename
hnif=${hostnamefile##/etc/hostname\.}
if [ -z "$hnif" ]; then
[ -n "${_RC_DEBUG}" ] && echo "Cannot get interface name from $hostnamefile"
hnifstatus=1
allprocstatus=1
continue
fi
# check if process is already running for this network interface - if yes, do nothing and result is ok
hnifprocesspattern="${processpattern}[^a-zA-Z]${hnif}([^0-9]|$)" # eg server_tun0.conf or client_tun0.conf or -dev tun0
[ -n "${_RC_DEBUG}" ] && echo "Executing: pgrep ${PGREPFLAG} \"$hnifprocesspattern\""
pgrep ${PGREPFLAG} "$hnifprocesspattern" && continue
if [ "$1" == "check" ]; then
[ -n "${_RC_DEBUG}" ] && echo "Missing openvpn process for ${hnif} - see $hostnamefile"
hnifstatus=1
else
echo -n "$hnif " # write name of started if
# if process is not running for this interface - start it
[ -n "${_RC_DEBUG}" ] && echo "Executing: /bin/sh /etc/netstart $hnif "
/bin/sh /etc/netstart $hnif; hnifstatus=$?
[ -n "${_RC_DEBUG}" ] && echo " status: $hnifstatus" # etc/hostname scripts may return 0 even if error occurred
fi
[ $hnifstatus -ne 0 ] && allprocstatus=1
done
# if ANY failed then return failure - (simple exit if check in quiet mode)
[ "$2" == "quiet" -a $allprocstatus -ne 0 ] && exit 1
[ $allprocstatus -ne 0 ] && _rc_exit failed
[ "$2" == "quiet" ] && exit 0
_rc_exit ok
;;
stop)
echo $_n "${INRC:+ }${_name} "
[ -n "${_RC_DEBUG}" ] && echo "Executing: pkill ${PKILLFLAG} \"$processpattern\""
pkill ${PKILLFLAG} "$processpattern"
countdown=10
while [ "((countdown--))" -gt 0 ]; do
pgrep ${PKILLFLAG} "$processpattern" >/dev/null || continue
sleep 1;
done;
[ -n "${_RC_DEBUG}" ] && echo "Executing: $0 ${_RC_DEBUG} ${_RC_FORCE} check quiet"
$0 ${_RC_DEBUG} ${_RC_FORCE} check quiet && _rc_exit failed
_rc_exit ok
;;
reload)
echo $_n "${INRC:+ }${_name} "
[ -n "${_RC_DEBUG}" ] && echo "WARNING: Reload might crash the process if process is chrooted"
[ -n "${_RC_DEBUG}" ] && echo "Executing: pkill -HUP ${PKILLFLAG} \"$processpattern\""
pkill -HUP ${PKILLFLAG} "$processpattern" || _rc_exit failed
sleep 10;
[ -n "${_RC_DEBUG}" ] && echo "Executing: $0 ${_RC_DEBUG} ${_RC_FORCE} check quiet"
[ -n "${_RC_DEBUG}" ] && echo "WARNING: Reload might crash the process if process is chrooted"
$0 ${_RC_DEBUG} ${_RC_FORCE} check quiet || _rc_exit failed
_rc_exit ok
;;
restart)
$0 ${_RC_DEBUG} ${_RC_FORCE} stop &&
$0 ${_RC_DEBUG} ${_RC_FORCE} start
;;
*)
_rc_usage
;;
esac
fi
/etc/rc.conf.local
- OPTIONALThe /etc/rc.d/openvpn
rc script works only if openvpn_flags are defined in rc.conf.local
In order to have the rc openvpn script compatible with /etc/hostname.* files, set the openvpn_flags to special hardcoded value -use-etc-hostname-
Add to /etc/rc.conf.local
:
openvpn_flags="-use-etc-hostname-"# then rc script is compatible with /etc/hostname.tunX to start openvpn (or check if they are running).
And add (append) openvpn to variable pkg_scripts. Or if such variable does not exist - add it as in example below:
pkg_scripts="... openvpn"
In case you do not want to use /etc/hostname files, but only use rc script to run one openvpn process, then instead of special string, put normal startup parameters for openvpn, for example:
#To run openvpn not from /etc/hostname, but directly from rc script - use line below # And make sure parameters like dev, server, push "route...", etc are defined in server.conf #openvpn_flags="--daemon --config /etc/openvpn/server.conf --dev tun0" # if there would be only one openvpn and not started from hostname.if
First create/edit a client config template: vi /etc/openvpn/private/vpnclient.conf.template
Correct the template below to your needs.
Especially change the line remote openbsd.example.com 1194
to the address (host.domain or IP) of your openvpn. This address must be reachable from the outside (from Internet).
##############################################
# Sample client-side OpenVPN 2.0 config file #
# for connecting to multi-client server. #
# #
# This configuration can be used by multiple #
# clients, however each client should have #
# its own cert and key files. #
# #
# On Windows, you might want to rename this #
# file so it has a .ovpn extension #
##############################################
# Specify that we are a client and that we
# will be pulling certain config file directives
# from the server.
client
# Use the same setting as you are using on
# the server.
# On most systems, the VPN will not function
# unless you partially or fully disable
# the firewall for the TUN/TAP interface.
;dev tap
;dev tun
# Windows needs the TAP-Win32 adapter name
# from the Network Connections panel
# if you have more than one. On XP SP2,
# you may need to disable the firewall
# for the TAP adapter.
;dev-node MyTap
# Are we connecting to a TCP or
# UDP server? Use the same setting as
# on the server.
;proto tcp
;proto udp
# The hostname/IP and port of the server.
# You can have multiple remote entries
# to load balance between the servers.
;remote my-server-1 1194
;remote my-server-2 1194
# Choose a random host from the remote
# list for load-balancing. Otherwise
# try hosts in the order specified.
;remote-random
# Keep trying indefinitely to resolve the
# host name of the OpenVPN server. Very useful
# on machines which are not permanently connected
# to the internet such as laptops.
resolv-retry infinite
# Most clients don't need to bind to
# a specific local port number.
nobind
# Downgrade privileges after initialization (non-Windows only)
;user _openvpn
;group _openvpn
# Try to preserve some state across restarts.
persist-key
persist-tun
# If you are connecting through an
# HTTP proxy to reach the actual OpenVPN
# server, put the proxy server/IP and
# port number here. See the man page
# if your proxy server requires
# authentication.
;http-proxy-retry # retry on connection failures
;http-proxy [proxy server] [proxy port #]
# Wireless networks often produce a lot
# of duplicate packets. Set this flag
# to silence duplicate packet warnings.
;mute-replay-warnings
# SSL/TLS parms.
# See the server config file for more
# description. It's best to use
# a separate .crt/.key file pair
# for each client. A single ca
# file can be used for all clients.
;ca ca.crt
;cert client.crt
;key client.key
# Verify server certificate by checking
# that the certificate has the nsCertType
# field set to "server". This is an
# important precaution to protect against
# a potential attack discussed here:
# http://openvpn.net/howto.html#mitm
#
# To use this feature, you will need to generate
# your server certificates with the nsCertType
# field set to "server". The build-key-server
# script in the easy-rsa folder will do this.
#OBSOLETE# ns-cert-type server # this was replaced by: remote-cert-tls server
# If a tls-auth key is used on the server
# then every client must also have the key.
;tls-auth ta.key 1
# Select a cryptographic cipher.
# If the cipher option is used on the server
# then you must also specify it here.
;cipher x
# Enable compression on the VPN link.
# Don't enable this unless it is also
# enabled in the server config file.
comp-lzo
# Set log file verbosity.
verb 3
# Silence repeating messages
;mute 20
# CUSTOMIZATIONS by adminOfMine # all lines below:
#OBSOLETE# ns-cert-type server # this was replaced by: remote-cert-tls server
# remote-cert-tls server - additional server certificate check, replaces obsolete ns-cert-type server
remote-cert-tls server
# Use those settings for UNIX:
#UNIX# daemon openvpn
#UNIX# user _openvpn
#UNIX# group _openvpn
#UNIX# chroot /var/empty
remote openbsd.example.com 1194
;proto udp
proto tcp
dev tun1
;dev-node TAP
float
auth-user-pass
# tls-auth is provided inline, so skip option tls-auth. But it is necessary to define the direction - using key-direction
key-direction 1
#TEMPLATE# do NOT put certificate config lines into the template
#TEMPLATE# such lines will be added later - as commands with proper filename or as inline
The openvpn certificates/keys/config need to be generated for each user.
Note: If you create a separate openvpn certificate+key not only for each user but also for each device that connects - then it is easier to revoke the certificate (=deny access) for just this one device.
generate certificate-request for client:
alternatively - vpn user himself does something like:
./easyrsa init-pki; ./easyrsa gen-req UNIQUE_CLIENT_SHORT_NAME
and sends the cert-request to openvpn ca admin just to sign/accept.
vpnclientuser=vpn-user-uniq-name
./easyrsa --batch=1 --pki-dir=${pkiDir} --req-cn=${vpnclientuser} gen-req ${vpnclientuser} nopass
ls -alpd ${pkiDir}/reqs/${vpnclientuser}.req ${pkiDir}/private/${vpnclientuser}.key
openssl req -in ${pkiDir}/reqs/${vpnclientuser}.req -text -noout
openssl rsa -in ${pkiDir}/private/${vpnclientuser}.key -check -noout
openvpn administrator must sign the certificate (on the openvpn serer - with proper CA):
./easyrsa --batch=1 --pki-dir=${pkiDir} show-req ${vpnclientuser}
./easyrsa --batch=1 --pki-dir=${pkiDir} sign client ${vpnclientuser} # client - only for client
openssl x509 -in ${pkiDir}/issued/${vpnclientuser}.crt -text -noout
#cp -p ${pkiDir}/issued/${vpnclientuser}.crt /etc/openvpn/certs/${vpnclientuser}.crt # no need to copy if using in-line
#cp -p ${pkiDir}/private/${vpnclientuser}.key /etc/openvpn/private/${vpnclientuser}.key # no need to copy if using in-line
copy the prepared-before vpnclient.conf.template to new file, eg:
cp -p /etc/openvpn/private/vpnclient.conf.template /etc/openvpn/private-client-conf/vpnclient-abc.conf.ovpn
And then add individual certificate/keyfiles - as described below
It is possible to embed all ca, certificates and keys into the config file - and then just one file is enough.
To embed all files for ca/cert/key/ta inside config - add the following at the end of the client config file (eg to: /etc/openvpn/private-client-conf/vpnclient-abc.conf.ovpn
)
put here the content of real vpn-ca.crt/vpnclient.ca/vpnclient.key/vpn-ta.key
# tls-auth is provided inline, so skip option tls-auth. But it is necessary to define the direction - using key-direction
key-direction 1
<ca>
-----BEGIN CERTIFICATE-----
...content from vpn-ca.crt...
-----END CERTIFICATE-----
</ca>
<cert>
-----BEGIN CERTIFICATE-----
...content from vpnclient-abc.crt...
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN RSA PRIVATE KEY-----
...content from vpnclient-abc.key...
-----END RSA PRIVATE KEY-----
</key>
<tls-auth>
...content from vpn-ta.key...
</tls-auth>
Send the *.ovpn
config file to the vpn user.
add the lines below and remember that vpn usr must get the vpn client config file together with separate ca/certificate/key/ta files.
you might also need to remove line key-direction 1
from config file
Add the following at the end of the client config file (eg to: /etc/openvpn/private-client-conf/vpnclient-abc.conf.ovpn
):
# tls-auth needs to have direction. For inline certificates it would need to be replaced with key-direction 1
tls-auth vpn-ta.key 1
ca vpn-ca.crt
cert vpnclient.crt
key vpnclient.key
Send the *.ovpn
config file and all the files listed in block above to the vpn user.
Example set of commands (for easy-rsa3):
The target certificate must be still existing on the file under easy-rsa, eg in ${pkiDir}/issued/vpnclient-olduser.crt
. Keyfile for this user is not needed, but certificate must be present
To revoke (block, disallow) the certificate:
cd /etc/openvpn/easy-rsa/3;
./easyrsa revoke vpnclient-olduser
./easyrsa gen-crl
after running command above you should see message like Revoking Certificate 04. Data Base Updated
verify that certificate as revoked in ${pkiDir}/index.txt
egrep "vpnclient-olduser" ${pkiDir}/index.txt
gives something like: R 240425145061Z 140428150632Z 05 unknown /CN=vpnclient-olduser
make sure openvpn process can read this file - otherwise it will crash:
chown :_openvpn ${pkiDir}/crl.pem; chmod g+r ${pkiDir}/crl.pem
make sure CRL is being used in openvpn's server.conf:
egrep '^ *crl-verify.*crl.pem' /etc/openvpn/server.conf
Copy file to location listed in openvpn's server.conf. if you use chroot - copy crl.pem to location visible after chrooting (/etc/openvpn/crl.pem might be a symlink - do not change that)
cp -p ${pkiDir}/crl.pem /etc/openvpn/crl.pem
ls -alF ${pkiDir}/crl.pem /etc/openvpn/crl.pem /var/openvpn/chrootjail/etc/openvpn/crl.pem # all files should be the same
openssl crl -in /etc/openvpn/crl.pem -text
After you update the CRL file, make sure openvpn process is able to read it.
Test if revoked user is no longer able to connect.
Then test if valid (not-revoked) users can still connect, to make sure everything is ok.
The creation of config/keys/certificates has been described above in ADD VPN USER = generate CLIENT config/key/certificate
In order to proceed, you need to have the *.ovpn
config file with certificates/keyfiles/ta - either embedded in *.ovpn
or as separate files (then put these separate files in the same directory as *.ovpn
)
Verify *.ovpn
and adjust to your system. Especially check the parameters:
An example content of *.ovpn
can be something like (below is just an example - adjust the config for each client if necessary):
client
resolv-retry infinite
nobind
persist-key
persist-tun
comp-lzo
verb 3
# remote-cert-tls server - additional server certificate check, replaces obsolete ns-cert-type server
remote-cert-tls server
remote <server IP or vpnserver.domain.com> 1194 # set proper address of VPN server
proto udp # tcp or udp. Make sure server-side also supports the protocol
dev tun1 # adjust device name here.
float # check if/how that works for float. Consider disabling persist-key/tun when having problems with reconnect from different IP.
# ask for username/password
auth-user-pass # disable if not required by server
# other options to be considered if needed:
# auth-user-pass # ask for username/password
# auth-user-pass /etc/openvpn/user-pass.auth
# auth-retry nointeract
#Uncomment lines marked with #UNIX# if you run on unix system
#UNIX# daemon openvpn
#UNIX# user _openvpn
#UNIX# group _openvpn
#UNIX# chroot /var/empty
And it should define certificate/keys - either inline:
key-direction 1
<ca>
-----BEGIN CERTIFICATE-----
...content from vpn-ca.crt...
-----END CERTIFICATE-----
</ca>
<cert>
-----BEGIN CERTIFICATE-----
...content from vpnclient-abc.crt...
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN RSA PRIVATE KEY-----
...content from vpnclient-abc.key...
-----END RSA PRIVATE KEY-----
</key>
<tls-auth>
...content from vpn-ta.key...
</tls-auth>
Or as separate file - but then you MUST also have those separate files and they need to be copied to openvpn client config directory together with the *.ovpn
file.
Definition of separate files in *.ovpn
would look like this:
tls-auth vpn-ta.key 1
ca vpn-ca.crt
cert vpnclient.crt
key vpnclient.key
WINDOWS - download OpenVPN client from OpenVPN website.
Copy vpnclient-my-user.conf.ovpn
to openvpn client config dir after installation - eg. if Openvpn is installed in C:\Prog\OpenVPN - put it in C:\Prog\OpenVPN\config
.
The OpenVPN-GUI process must be started as Admin (you might need to create a link and edit its properties to always run as Admin).
Warning: If you do not run OpenVPN-GUI process as Admin - it will seem to work, but after establishing the connection, it will not be able to change the routing and it will not be possible to connect to the machines on other end of the VPN tunnel (unless you manually change the routing - as admin).
MAC - install TunnelBlick. Copy vpnclient-user.conf.ovpn as instructed by tunnelblick.
Note: it might be necessary to copy vpn-ta.key, as current version (Tunnelblick 3.4.1 (build 4054) from year 2014) has problems to read the inline vpn-ta.
I had to copy vpn-ta.key
directly to ~/Library/Application Support/Tunnelblick/Configurations/vpnclient-my-user.tblk/Contents/Resources
and then it worked.
UNIX/LINUX - install openvpn package, (openvpn client package might be the same as or included in openvpn server).
WINDOWS - you can use VPN GUI - it must be run as admin to set proper routing after connection is established. The OpenVPN-GUI process must be started as Admin (you might need to create a link and edit its properties to always run as Admin). If you do not run OpenVPN-GUI process as Admin - it will seem to work, but after establishing the connection, it will not be able to cahnge the routing and it will not be possible to connect to the machines on other end of the VPN tunnel (unless you manually change the routing - as admin).
MAC - use TunnelBlick GUI.
If you get connection problems - make sure you have copied vpn-ta.key
to ~/Library/Application Support/Tunnelblick/Configurations/vpnclient-my-user.tblk/Contents/Resources
UNIX/LINUX - use commands like below:
openvpn --config /etc/openvpn/vpnclient-my-user.conf.ovpn ifconfig tun1 # interface should be up and have IPs assigned. Check device that you have put in config file, eg. tun1 pgrep -fl openvpn.*client less /var/log/messages # search for entries from "^openvpn:"
It is also possible to make openvpn server require username/password (in addition to certificate/keyfiles)
Note: This is NOT password for key-file, this is authentication performed on the openvpn server side
Username/password are independent of certificate CN name and config file name
Authentication can be performed:
Under chroot-ed openvpn it is difficult to perform the unix/bsd or ldap authentication.
So in next examples below there are 2 variants:
Note: This method can be used with or without chroot-ing
List accepted password files in /etc/openvpn/custom-simple-auth.conf
There is no verification of vpn certificate username against the usernames here.
prepare the symlink for chrooted env (so openvpn can be started with or without chroot and reach the same file):
ln -s /var/openvpn/chrootjail/etc/openvpn/custom-simple-auth.conf /etc/openvpn/custom-simple-auth.conf
vi /etc/openvpn/custom-simple-auth.conf
# This file contains authentication data for simple authentication script
# each line should contain 2 values (separated by spaces or tab):
# username password
#
# auth-check script gets given username/password from environment
# then it reads list of valid username/password from this config file
# If the given username/password matches any of the username/password from this config file then authentication is OK.
#
# comments can be made by adding # as the FIRST character in the line
# commenteduser commentedpassword
vpn vpn
prepare the symlink for chrooted env (so openvpn can be started with or without chroot and reach the same file):
ln -s /var/openvpn/chrootjail/var/openvpn/custom-simple-auth /var/openvpn/custom-simple-auth
Create simple C program to do authentication check
mkdir -p /etc/openvpn/additional-files/
cd /etc/openvpn/additional-files/
vi custom-simple-auth.c
add content as in attached file:
#include <sys/param.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
/*
# Chris Pfaff
# This is a simple script to do custom authentication for openvpn.
#
# It gets given username/password from environment
# It reads list of valid usernames/password from config file
# If the given username/password matches any of the username/password from the config file then authentication is OK.
#
# If it is static then it can be executed under chrooted environment
# then move output to final location:
# compile this file using -static option:
#
gcc -static -o /var/openvpn/chrootjail/var/openvpn/custom-simple-auth /etc/openvpn/additional-files/custom-simple-auth.c
ln -s /var/openvpn/chrootjail/var/openvpn/custom-simple-auth /var/openvpn/custom-simple-auth # for chrooted env
# Test with command like: (adjust the username, password):
username=vpn password=vpn /var/openvpn/custom-simple-auth # should be ok with valid credentials
username=wrong password=wrong /var/openvpn/custom-simple-auth # should fail with wrong credentials
For openvpn - you need to edit the server.conf and add lines like this:
###############
#### AUTHENTICATION of user - requiring username/password in addition to VPN client certificates
###############
# Use custom static C executable that reads username/passwords from /etc/openvpn/custom-simple-auth.conf
# It works when chrooted (compile as static executable, no libraries needed)
auth-user-pass-verify /var/openvpn/custom-simple-auth via-env
# It is possible to use the script in non-chrooted environment or chrooted env
chroot /var/openvpn/chrootjail
*/
char configFile[] = "/etc/openvpn/custom-simple-auth.conf";
int DEBUG=3;
int verifyAgainstFile(char* fileName, char* u, char* p) { // check password against the strings configured in config file
FILE *fp;
char ch;
const int maxLineLen=255;
char line[maxLineLen];
char ru[maxLineLen];
char rp[maxLineLen];
fp = fopen(fileName,"r"); // read mode
if( fp == NULL ) {
printf("Cannot open config file: %s\n",fileName);
perror("Error while opening the config file.\n");
exit(EXIT_FAILURE);
}
(DEBUG >= 3) && printf("The contents of %s file are :\n", fileName);
while (!feof(fp)) {
(DEBUG >= 3) && printf("Processing line... ");
if (fgets(line, maxLineLen, fp) ) {
(DEBUG >= 3) && printf(" '%s' ", line);
if (line[0] == '#') {
(DEBUG >= 3) && printf(" - this line is a comment - ignoring\n");
} else if (sscanf(line, "%s %s", ru, rp) == 2) {
(DEBUG >= 3) && printf("%s %s - ", ru, rp);
if ( (strncmp(u, ru, maxLineLen) == 0) && (strncmp(p, rp, maxLineLen) == 0) ) {
(DEBUG >= 1) && printf("auth ok\n");
return 1; // auth ok
} else {
(DEBUG >= 1) && printf("failed auth\n");
}
} else {
(DEBUG >= 2) && printf("failed scanf: %s %s\n", ru, rp);
// ru , rp - still might contain values from previous line, do not use them if sscanf failed
}
} else {
(DEBUG >= 3) && printf("failed with fgets - end of file\n");
}
}
fclose(fp);
return 0;
}
int main() {
int result;
char *username;
char *password;
if(getenv("username") != NULL) {
username = getenv("username");
} else {
printf("no username environmental variable set\n");
return 1;
}
if(getenv("password") != NULL) {
password = getenv("password");
} else {
printf("no password environmental variable set\n");
return 1;
}
// EXAMPLE: Use system BSD authentication
// This requires also the includes:
// #include <login_cap.h>
// #include <bsd_auth.h>
//result = auth_userokay(username, NULL, NULL, password); // add auth libraries
// Check given username/password against list of username/passwords stored in config file
result = verifyAgainstFile(configFile, username, password);
if(result == 0) {
printf("authentication failed\n");
} else {
return 0;
}
return 1;
}
then compile using static option (-static = no libraries dependencies - needed for chrooted env):
gcc -static -o /var/openvpn/chrootjail/var/openvpn/custom-simple-auth /etc/openvpn/additional-files/custom-simple-auth.c
ln -s /var/openvpn/chrootjail/var/openvpn/custom-simple-auth /var/openvpn/custom-simple-auth # for chrooted env
Test with command like: (adjust the username, password):
username=vpn password=vpn /var/openvpn/custom-simple-auth # should be ok with valid credentials username=wrong password=wrong /var/openvpn/custom-simple-auth # should fail with wrong credentials
WARNING: Remember - after each OpenBsd Operating System upgrade:
Also, create informational file to explain what is this binary for:
vi /var/openvpn/chrootjail/var/openvpn/custom-simple-auth.txt
# Chris Pfaff
# This is a simple script to do custom authentication for openvpn.
#
# It gets given username/password from environment
# It reads list of valid usernames/password from config file
# If the given username/password matches any of the username/password from the config file then authentication is OK.
#
# If it is static then it can be executed under chrooted environment
# then move output to final location:
# compile this file using -static option:
#
gcc -static -o /var/openvpn/chrootjail/var/openvpn/custom-simple-auth /etc/openvpn/additional-files/custom-simple-auth.c
ln -s /var/openvpn/chrootjail/var/openvpn/custom-simple-auth /var/openvpn/custom-simple-auth # for chrooted env
# Test with command like: (adjust the username, password):
username=vpn password=vpn /var/openvpn/custom-simple-auth # should be ok with valid credentials
username=wrong password=wrong /var/openvpn/custom-simple-auth # should fail with wrong credentials
ln -s /var/openvpn/chrootjail/var/openvpn/custom-simple-auth.txt /var/openvpn/custom-simple-auth.txt # for chrooted env
vi /etc/openvpn/server.conf
###############
#### AUTHENTICATION of user - requiring username/password in addition to VPN client certificates
# VARIANT 1A - using static executable
# Use custom static C executable that reads username/passwords from /etc/openvpn/custom-simple-auth.conf
# It works also when chrooted (this is static executable, no libraries needed)
script-security 3 # CUSTOMIZED by adminOfMine #
auth-user-pass-verify /var/openvpn/custom-simple-auth via-env # CUSTOMIZED by adminOfMine #
# It is possible to use the script in non-chrooted environment or chrooted env
chroot /var/openvpn/chrootjail # CUSTOMIZED by adminOfMine #
after changing the server.conf - restart openvpn and test if it works
check /var/log/openvpn/openvpn.log
If you require authentication then also enable it on the client side by changing the openvpn client config file (*.ovpn) and add:
# ask for username/password
auth-user-pass # CUSTOMIZED by adminOfMine #
This authentication method can be used with chroot-ing or without.
With this setup the username/password for openvpn is independent form system user/passwords and independent of the openvpn certificate (so all certificates can use the same passwords).
There is room for improvement, but I find it good enough (and better than just keyfile with no password at all):
Keep in mind that openvpn stores these username/passwords in its config files (in cleartext) and if anyone compromises openvpn then the passwords are visible, so - in worst case - better compromise different vpn password and not real unix users/passwords.
The script and username/password handling can be extended.
It is possible to store sha/md5 of user's password (or export is from ldap/samba/master.passwd).
eg with:cat /etc/master.passwd | grep "^$user" | cut ...
Then check user's password given to script in cleartext, make the same md5/sha and verify against stored value.
If they match then authentication is ok.
But there is not much point in that, because openvpn gives passwords to the authentication-checking binary in cleartext (via env or file).
Execute the following commands to prepare basic files under chrooted env:
This step might need adjustments - depending on the OpenBSD version
cd /var/openvpn/chrootjail
filesToCopy="
/bin/csh
/usr/bin/head
/usr/bin/tail
/usr/bin/ldd
/bin/echo
/usr/lib/libc.so.65.0
/usr/libexec/ld.so
"
for f in $filesToCopy; do
f2=${f:1:255}
f2dir=${f2%/*}
echo "mkdir -p /var/openvpn/chrootjail/$f2dir; cp $f /var/openvpn/chrootjail/$f2; cp $f /var/openvpn/chrootjail/"
mkdir -p /var/openvpn/chrootjail/$f2dir; cp $f /var/openvpn/chrootjail/$f2; cp $f /var/openvpn/chrootjail/
done
cat '#!/bin/csh
setenv username "vpn"
setenv password "vpn"
/var/openvpn/custom-simple-auth-script.csh && echo "OK" || echo "error"
' > test-chroot-auth.csh
chmod u+x test-chroot-auth.csh
Prepare script to check authentication: custom-simple-auth-script.csh ln -s /var/openvpn/chrootjail/var/openvpn/custom-simple-auth-script.csh /var/openvpn/custom-simple-auth-script.csh vi /var/openvpn/chrootjail/var/openvpn/custom-simple-auth-script.csh
Put the following content in the file
#! /bin/csh
# AUTH via-env - sets following variables
# username=user
# password=pass
# AUTH via-file
# filename is given in $1
# file contains 2 lines with username (1st line) and with cleartext password (2nd line)
#username
#password
# read expected (required) username/password from config file. VPN user must give the same username/password.
set requiredUsername=`egrep -v '^#|$^' /etc/openvpn/custom-simple-auth.conf | head -1 | cut -f 1`
set requiredPassword=`egrep -v '^#|$^' /etc/openvpn/custom-simple-auth.conf | head -1 | cut -f 2`
# if via-file then load file content into internal variables
if ( "$1" != "" ) then
if ( -e "$1" ) then
#read from file
set username=`head -1 $1`
set password=`head -2 $1 | tail -1`
# echo username=$username
# echo password=$password
else
exit 2 # no file given in $1
# echo no file found
endif
endif
# next part is common for via-file (variables loaded from file) or via-env (variables set by calling script)
if ( "$username" == "$requiredUsername" && "$password" == "$requiredPassword" ) then
# echo ok
exit 0 # auth ok
else
# echo err
exit 1 # file auth failed
endif
exit 10 # shall never happen, but just in case
List accepted password files in /etc/openvpn/custom-simple-auth.conf
There is no verification of vpn certificate username against the usernames here.
ln -s /var/openvpn/chrootjail/etc/openvpn/custom-simple-auth.conf /etc/openvpn/custom-simple-auth.conf
vi /etc/openvpn/custom-simple-auth.conf
# The required username/password given in /etc/openvpn/custom-simple-auth.conf will be required by all the vpn users (the same for all users).
# Only first line from that file will be taken into account
vpn vpn
edit parameters (username password) in /etc/openvpn/custom-simple-auth.conf
vi /etc/openvpn/server.conf
###############
#### AUTHENTICATION of user - requiring username/password in addition to VPN client certificates
# VARIANT 1B - authenticate under chroot using custom script
# This script could work with chroot but it would need csh under chrooted dir and all necessary binaries and libraries
# It is using simple hardcoded authentication
script-security 3 # CUSTOMIZED by adminOfMine #
# script must be available under that path after chrooting and necessary shell/libs must be present under chrooted dir
auth-user-pass-verify /var/openvpn/custom-simple-auth-script.csh via-env # CUSTOMIZED by adminOfMine #
#
# and via file
#tmp-dir /tmp/openvpnauth # CUSTOMIZED by adminOfMine #
#auth-user-pass-verify /var/openvpn/custom-simple-auth-script.csh via-file # CUSTOMIZED by adminOfMine #
# It is possible to use the script in both non-chrooted environment or chrooted env
after changing the server.conf - restart openvpn and test if it works
check /var/log/openvpn/openvpn.log
If you require authentication then also enable it on the client side by changing the openvpn client config file (*.ovpn) and add:
# ask for username/password
auth-user-pass # CUSTOMIZED by adminOfMine #
Note: This method seems to be less secure:
This also requires that the authenticating user be put into the _openvpnusers group.
More information can be found here: man openvpn_bsdauth
To use this variant - prepare directory to store data (need to be done in startup script to create dir under fresh /tmp):
run as root: install -d -m 700 -o _openvpn -g _openvpn /tmp/openvpnauth;
Directory should exist and be empty and readable for _openvpn group:
ls -alpd /tmp/openvpnauth; ls /tmp/openvpnauth
Add the following lines to /etc/openvpn/server.conf: vi /etc/openvpn/server.conf
###############
# VARIANT 2 - authenticate using system defined users - BSD and/or LDAP users:
###############
# VARIANT 2A - authenticate using bsd authentication
# # It does NOT work with chrooted openvpn
#
# This also requires that the authenticating user be put into the _openvpnusers group.
#
# # For via-env use script-security 3, for via-file 2 should be enough
script-security 3 # CUSTOMIZED by adminOfMine #
auth-user-pass-verify /usr/local/libexec/openvpn_bsdauth via-env # CUSTOMIZED by adminOfMine #
#
# # You can switch to authentication via file (then no-one can peek password in openvpn_bsdauth process environment)
# # But make sure no-one can read that file except root and _openvpn user.
# # If you intend to use via-file then make sure directory is created (if in /tmp/ then it must be created after each reboot)
# # use command: install -d -m 700 -o _openvpn -g _openvpn /tmp/openvpnauth; ll -d /tmp/openvpnauth
#auth-user-pass-verify /usr/local/libexec/openvpn_bsdauth via-file # CUSTOMIZED by adminOfMine #
#tmp-dir /tmp/openvpnauth # CUSTOMIZED by adminOfMine #
#script-security 2 # CUSTOMIZED by adminOfMine #
Remember that if you use auth via file then create directory in /tmp/ - and it needs to be recreated every time after boot. And use install -d -m ...
command (listed above) so that files under the directory cannot be read by other users
Keep in mind that openvpn uses cleartext passwords and if anyone compromises openvpn then the passwords are visible
And remember that authenticating user might need to belong to the _openvpnusers group.
To test authentication, use command like this (give username/password in environment variables):
username=someuser password=passstring /usr/local/libexec/openvpn_bsdauth; echo $?
I could not make it work under chrooted env, although it could be possible - connection to ldap is done via TCP connection. But this would need to setup the environment for ldap client, etc under chroot-ed environment.
To use this variant - first prepare directory to store data (need to be done in startup script to create dir under fresh /tmp):
run as root: install -d -m 700 -o _openvpn -g _openvpn /tmp/openvpnauth;
Directory should exist and be empty and readable for _openvpn group:
ls -alpd /tmp/openvpnauth; ls /tmp/openvpnauth
create auth-ldap.conf
test -e /etc/openvpn/auth-ldap.conf || cp -p /usr/local/share/examples/openvpn-auth-ldap/auth-ldap.conf /etc/openvpn/auth-ldap.conf
Adjust the /etc/openvpn/auth-ldap.conf. Set section LDAP based on /etc/openldap/ldap.conf, set Authorization based on /etc/login.conf
Here showing just a short simple example
It is recommended to extend it and add setup for TLS, requirements for users to belong to certain group, etc.
Runvi /etc/openvpn/auth-ldap.conf
and edit based on example below:<LDAP> URL ldap://localhost Timeout 15 TLSEnable no </LDAP> <Authorization> BaseDN "ou=Users,dc=domainofmine,dc=local" SearchFilter "(&(objectclass=posixAccount)(uid=%u))" RequireGroup false </Authorization>
Add the following lines to /etc/openvpn/server.conf: vi /etc/openvpn/server.conf
###############
# VARIANT 2B - authenticate using ldap authentication plugin
# # It does NOT work with chrooted openvpn
#
# # script-security 3, 2 should be enough if doing via file
script-security 3 # CUSTOMIZED by adminOfMine #
# give full plugin path and then ldap config file. Edit this config file to your environment
plugin /usr/local/lib/openvpn-auth-ldap.so /etc/openvpn/auth-ldap.conf # CUSTOMIZED by adminOfMine #
# # If you intend to use via-file then make sure directory is created (if in /tmp/ then it must be created after each reboot)
# # use command: install -d -m 700 -o _openvpn -g _openvpn /tmp/openvpnauth; ll -d /tmp/openvpnauth
tmp-dir /tmp/openvpnauth # CUSTOMIZED by adminOfMine #
Remember that if you create directory in /tmp/ - then it needs to be recreated every time after boot. And use install -d -m ...
command (listed above) so that files under the directory cannot be read by other users
Keep in mind that openvpn uses cleartext passwords and if anyone compromises openvpn then the passwords are visible
after changing the server.conf - restart openvpn and test if it works, eg:
/etc/rc.d/openvpn stop; sleep 1; /etc/rc.d/openvpn start; less /var/log/openvpn/openvpn.log
ifconfig tun0 # put device defined in server.conf
pgrep -fl openvpn.*server
netstat -an|grep '\.1194 ' # put port defined in server.conf
ls -al /var/log/openvpn/* # check logs
Remember - if you require authentication on server side then also enable it on the client side by changing the openvpn client config file (*.ovpn) - add: # ask for username/password auth-user-pass # other options to be considered if needed: # auth-user-pass /etc/openvpn/user-pass.auth # auth-retry nointeract
Test connecting the client, eg by using:
# test openvpn connection - establish vpn connection locally on the server
openvpn --config /etc/openvpn/vpnclient-unixlocaltest/vpnclient-unixlocaltest.conf.ovpn;
# then stop local vpn tunnel with Ctrl+C if in foreground
In case vpn client runs in background:
tail -f /var/log/messages # if daemonized - check logfile
# if no longer necessary then stop, eg. with:
pgrep -fl 'openvpn'; pkill -f 'openvpn.*client'; echo "---"; sleep 1; pgrep -fl 'openvpn';
If you have certificates and keys and client certificates created by easy-rsa2 then you might want to use the existing CA and certificates (because using new CA would make old client certificates useless - and they would need to be regenerated).
Assumptions:
To migrate the keys from easy-rsa2 to easy-rsa3, use the guide below.
pkiDir="/etc/openvpn/easy-rsa-pki/"
easyrsaDir="/usr/local/share/easy-rsa/"
easyrsa2OldDir="/etc/openvpn/easy-rsa/2.0"
cd ${easyrsaDir}
./easyrsa --batch=1 --pki-dir=${pkiDir} init-pki
./easyrsa --batch=1 --pki-dir=${pkiDir} --req-cn=vpn-ca build-ca nopass; rm ${pkiDir}/ca.crt ${pkiDir}/private/ca.key # this removes unwanted new ca but leaves dir structure
ll -d ${pkiDir} ${pkiDir}/* ${pkiDir}/*/*
# drwx------ 6 root bin 512 Apr 28 23:17 /etc/openvpn/easy-rsa-pki/
# drwx------ 2 root bin 512 Apr 28 23:11 /etc/openvpn/easy-rsa-pki/certs_by_serial/
# -rw------- 1 root bin 0 Apr 28 23:11 /etc/openvpn/easy-rsa-pki/index.txt
# drwx------ 2 root bin 512 Apr 28 23:11 /etc/openvpn/easy-rsa-pki/issued/
# drwx------ 2 root bin 512 Apr 28 23:17 /etc/openvpn/easy-rsa-pki/private/
# drwx------ 2 root bin 512 Apr 28 23:11 /etc/openvpn/easy-rsa-pki/reqs/
# -rw------- 1 root bin 3 Apr 28 23:11 /etc/openvpn/easy-rsa-pki/serial
#move files from easyrsa20 to easyrsa30
cp -p /etc/openvpn/dh.pem ${pkiDir}/ # move current openvpn dh file in there
mv ${easyrsa2OldDir}/keys/crl.pem ${pkiDir}/ # move if file exists
mv ${easyrsa2OldDir}/keys/index.txt* ${pkiDir}/
mv ${easyrsa2OldDir}/keys/serial* ${pkiDir}/
mv ${easyrsa2OldDir}/keys/*.key ${pkiDir}/private/
mv ${easyrsa2OldDir}/keys/ca.crt ${pkiDir}/
mv ${easyrsa2OldDir}/keys/*.crt ${pkiDir}/issued/
mv ${easyrsa2OldDir}/keys/*.csr ${pkiDir}/reqs/
mv ${easyrsa2OldDir}/keys/??.pem ${pkiDir}/certs_by_serial/
In case you would need to re-create the server certificate (and still using deprecated ns-cert option for clients that still require this) then use commands like:
./easyrsa --batch=1 --pki-dir=${pkiDir} --ns-cert="yes" --ns-comment="Easy-RSA Generated Server Certificate" --req-cn=vpnserver gen-req vpnserver nopass openssl req -in ${pkiDir}/reqs/vpnserver.req -text -noout ./easyrsa --batch=1 --pki-dir=${pkiDir} --ns-cert="yes" --ns-comment="Easy-RSA Generated Server Certificate" sign server vpnserver # server - only for server openssl x509 -in ${pkiDir}/issued/vpnserver.crt -text -noout cp -p ${pkiDir}/issued/vpnserver.crt /etc/openvpn/certs/vpnserver.crt; cp -p ${pkiDir}/private/vpnserver.key /etc/openvpn/private/vpnserver.key
And remember to restart openvpn so that it reads new certificate. Then test with existing clients!!!
If you get an error with easyrsa like 4098901262292:error:0E065068:configuration file routines:STR_COPY:variable has no value:/usr/src/lib/libcrypto/crypto/../../libssl/src/crypto/conf/conf_def.c:573:line 3
or Expected to find openssl command at: openssl
then make sure you use the easyrca installed from the openbsd packages.
That was needed in November 2016 with OpenBsd 5.9 and EasyRSA-3.0.0
If you get an error with easyrsa like:
Easy-RSA error: Missing or invalid OpenSSL Expected to find openssl command at: openssl
Check the openssl version
openssl version
LibreSSL 2.0
If the version does not start with OpenSSL - then might be not recognized by easyrsa script and then needs correction.
That was the case for OpenBsd version 5.6 and EasyRSA-3.0.0-rc2.
The fix is quite simple (but verify that all further executions of easyrsa are working correctly).
cp -p easyrsa easyrsa.old
## cat easyrsa.old | perl -pe 's/(\[ "\$\{val.. \*\}" = ")OpenSSL(" ] \|\| die)/$1LibreSSL$2/' > easyrsa
cat easyrsa.old | perl -pe 's/(\[)( "\$\{val.. \*\}" = ")(OpenSSL)(")( ] \|\| die)/$1$2$3$4 -o $2LibreSSL$4$5/' > easyrsa;
diff easyrsa easyrsa.old
291c291 < [ "${val%% *}" = "LibreSSL" ] || die "\ --- [ "${val%% *}" = "OpenSSL" -o "${val%% *}" = "LibreSSL" ] || die "\
This issue was observerd after upgrade to OpenVPN 2.4. If clients cannot connect and you get an error in openvpn.log on the server side saying "CRL has expired" - then simply regenerate the CRL certificate.
First - set the parameters and directory for further commands
pkiDir="/etc/openvpn/easy-rsa-pki/"
easyrsaDir="/usr/local/share/easy-rsa/"
cd ${easyrsaDir}
ls -al vars.example
egrep 'EASYRSA_CA_EXPIRE|EASYRSA_CERT_EXPIRE|EASYRSA_CRL_DAYS' vars.example
echo
ls -al vars
egrep 'EASYRSA_CA_EXPIRE|EASYRSA_CERT_EXPIRE|EASYRSA_CRL_DAYS' vars
Review the vars.example or vars (if exists). Make sure that CA, certificate and CRL lifetime is set to at least 10 years (unless you want to regenerate them more often):
set_var EASYRSA_CA_EXPIRE 3650 set_var EASYRSA_CERT_EXPIRE 3650 set_var EASYRSA_CRL_DAYS 3650
If it is necessary to change the parameters - then create/modify vars file:
if [ -f ${easyrsaDir}/vars ]; then
echo "File already exists - verify: /usr/local/share/easy-rsa/vars";
ls -al ${easyrsaDir}/vars
cat ${easyrsaDir}/vars;
echo
else
printf "set_var EASYRSA_CA_EXPIRE\t3650\nset_var EASYRSA_CERT_EXPIRE\t3650\nset_var EASYRSA_CRL_DAYS\t3650" > ${easyrsaDir}/vars;
ls -al ${easyrsaDir}/vars
cat ${easyrsaDir}/vars;
echo
fi
Next step is to regenerate the CRL file:
./easyrsa --batch=1 --pki-dir=${pkiDir} gen-crl
# make sure openvpn process can read this file - otherwise it will crash
chown :_openvpn ${pkiDir}/crl.pem; chmod g+r ${pkiDir}/crl.pem
ls -alF ${pkiDir}/crl.pem
openssl crl -in ${pkiDir}/crl.pem -text -noout
# Copy final certs/keys to openvpn config locations
cp -p ${pkiDir}/crl.pem /etc/openvpn/crl.pem
openssl crl -in /etc/openvpn/crl.pem -text | egrep -i 'next update|last update'
Verify certificate - next-update should be in far future.
Now you can re-connect with openvpn clients and verify if everything works.
There shall be no restart of openvpn needed, new CRL shall be re-read by openvpn.
http://www.openbsdsupport.org/
https://openvpn.net/index.php/open-source/documentation/howto.html
https://community.openvpn.net/openvpn/wiki/EasyRSA3-OpenVPN-Howto
http://www.kernel-panic.it/openbsd/vpn/vpn4.html
NO WARRANTY, NO RESPONSIBILITY - you are fully responsible for the systems you configure/maintain/change. This guide is only for information purposes. Some errors and non-working commands or unpredicted side-effects are possible.
BSD License applies for this guide. Feel free to distribute, edit, re-use the document as needed, but please acknowledge the author and source of document in the web.