Apple Event: May 7th at 7 am PT

Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

OpenVPN VPN Server on OS X Server with Mavericks, pfctl, and Tunnelblick

Here are notes on how to build an OpenVPN VPN server on OS X Server with Mavericks, pfctl, and Tunnelblick. Previous OpenVPN server configurations on OS X Server rely upon using the now deprecated natd and ipfw to route VPN traffic, and this solution no longer works. This post describes a replacement using the now preferred pfctl OpenBSD packet filter, which comes with its own NAT. This setup will provide a TLS-based VPN server using 4096-bit certificates and UDP port 443, accessible by any OpenVPN client, especially iOS with the OpenVPN app.


Why would you want to build your own VPN server when OS X server already comes with a VPN service? First, the latest Server.app version 3 breaks VPN to mobile devices. This problem is known and will undoubtedly be fixed soon; however, the VPN technology used by OS X Server is broken and should be avoided altogether (Microsoft’s PPTP: ("PPTP traffic should be considered unencrypted"), or is under a cloud (L2TP/Ipsec with pre-shared keys and MS-CHAPv2 authentication: "IPSEC-PSK is arguably worse than PPTP ever was for a dictionary-based attack vector"). So if you’re going to use OS X Server’s native VPN service, make sure that you use a really long *random* PSK. If you want secure certificate-based VPN between OS X Server and iOS, OpenVPN is the only option.


Furthermore, OS X Server has its firewall turned off by default, assuming that the server lives behind the router's firewall and NAT. Integrating OpenVPN access within a working OS X Server firewall provides greater security than OS X Server's default configuration.


Here’s how to build a VPN Server on OS X Mavericks:


Step 1. Get OpenVPN on OSX and configure it.


1A. I like Macports, so assuming that you've downloaded and installed Xcode from the App Store, installed Macports, run:


sudo port selfupdate

sudo port install openvpn


Step 2. Get Tunnelblick on OS X and configure it.


2A. <http://code.google.com/p/tunnelblick/>


2B. Create your certificates with Easy-RSA


mkdir -p ~/Backups/OpenVPN/easy-rsa-tunnelblick

sudo rsync -va /Applications/Tunnelblick.app/Contents/Resources/easy-rsa-tunnelblick/ ~/Backups/OpenVPN/easy-rsa-tunnelblick

cd ~/Backups/OpenVPN/easy-rsa-tunnelblick

su root

# edit script defaults like KEY_CN == Common Name

vi ./vars


export KEY_SIZE=4096

export KEY_EMAIL="admin@domainname.com"

export KEY_CN=Domainname


mkdir -m go-rwx ./keys

touch ./keys/index.txt

echo 1 > ./keys/serial

. ./vars

./clean-all

./build-ca --pass

./build-key-server server-domainname

# choose a unique Common Name (CN) for each client

./build-key client-domainname

./build-dh

openvpn2 --genkey --secret ./keys/ta.key


# Notes:

# Use the domain name "domainname.com" for the common name

# Contact email "admin@domainname.com" must match name in CA;

# otherwise, there will be some X509 error.


# For the server-domainname cert, use the default common name

# "server-domainname".This must also match the client configuration

# setting:

# tls-remote domainname.com


# Unnecessary if you already signed with ./build-key[-server]

# ./sign-req server-domainname

# ./sign-req client-domainname


cd keys

openssl verify -CAfile ca.crt ca.crt

openssl verify -CAfile ca.crt server-domainname.crt

openssl verify -CAfile ca.crt client-domainname.crt

exit # root


2C. Create a .tblk directory for your VPN server with all the necessary files. I'll demo a tun setup. The LAN here looks like a router on 10.0.1.1 and a OS X Server on 10.0.1.3.


mkdir ~/Desktop/Domainname_tun.tblk

cd ~/Backups/OpenVPN/easy-rsa-tunnelblick/keys

sudo cp -p ca.crt dh4096.pem server-domainname.crt server-domainname.key ta.key ~/Desktop/Domainname_tun.tblk

sudo chown -R username ~/Desktop/Domainname_tun.tblk

cd ~/Desktop/Domainname_tun.tblk

vi config.ovpn


local 10.0.1.3

port 443

proto udp


dev tun

;dev tap


ca ca.crt

cert server-domainname.crt

key server-domainname.key


tls-auth ta.key 0


dh dh4096.pem


server 10.2.0.0 255.255.255.0


;server-bridge 10.0.1.3 255.255.255.0 10.0.1.50 10.0.1.90


;topology subnet


# Push routes to the client to allow it

# to reach other private subnets behind

# the server. Remember that these

# private subnets will also need

# to know to route the OpenVPN client

# address pool (10.2.0.0/255.255.255.0)

# back to the OpenVPN server.

;push "route 10.0.1.0/255.255.255.0"


push "redirect-gateway def1"

push "dhcp-option DNS 8.8.8.8"

;push "dhcp-option DNS 10.0.1.3”

;push "dhcp-option DOMAIN domainname.com."


client-to-client


keepalive 10 120


;cipher BF-CBC


comp-lzo


max-clients 10


user nobody

group nobody


persist-key

persist-tun


ifconfig-pool-persist ipp.txt


verb 3



2D. Forward UDP port 443 from your router to you server at 10.0.1.3. (You may also wish to forward the default OpenVPN port 1194 as well to test other configurations.)


2E. Doubleclick on ~/Desktop/Domainname_tun.tblk within Finder, which should start Tunnelblick and import your configuration. Then,


Tunnelblick>VPN Details ... >Configuration>Settings>Set Nameserver (3.1)


You may also wish to launch this service at boot.


Hit connect and Tunnelblick should launch your OpenVPN server on UDP port 443.


2F. Get OpenVPN from the iOS App store and securely transfer a client.ovpn file to it:


client.ovpn:


####

## Domain Mac UDP client configuration version 2.0

####

client

dev tun

proto udp

remote server.domainname.com 443

redirect-gateway def1

resolv-retry infinite

nobind

;user nobody

;group nobody

persist-key

persist-tun

;ca ca.crt

;cert iPad-domainname.crt

;key iPad-domainname.key

<ca>

-----BEGIN CERTIFICATE-----

MY CERT from ca.crt

-----END CERTIFICATE-----

</ca>

ns-cert-type server

;tls-auth ta.key 1

key-direction 1

<tls-auth>

#

# 4096 bit OpenVPN static key

#

-----BEGIN OpenVPN Static key V1-----

MY CERT from ta.key

-----END OpenVPN Static key V1-----

</tls-auth>

;auth-user-pass

;tls-remote server-domainname

tls-ciphe

r DHE-RSA-AES256-SHA

;cipher BF-CBC

;client-http-proxy 10.0.1.3 3128

comp-lzo

verb 3



Step 3. Configure pfctl on OS X to route your VPN packets correctly.


3.A. Set up sysctl variables at boot:


sudo mkdir -p /Library/Application\ Support/vpn

sudo vi /Library/Application\ Support/vpn/enable-vpn-forward-nat.sh


#!/bin/bash

#

# References: The Book of PF, p. 21; https://forums.openvpn.net/topic11401.html

#

# Sleep is necessary cause network has to be up at the time of following commands

# Otherwise the network will not work at all

#

sleep 15

#

/usr/sbin/sysctl -w net.inet.ip.fw.enable=1

/usr/sbin/sysctl -w net.inet.ip.forwarding=1

/usr/sbin/sysctl -w net.inet6.ip6.forwarding=1


# natd and ipfw are DEPRECATED. Use pfctl(8) instead with nat, e.g.

# nat on en0 from 10.0.0.0/8 to any -> (en0)

###/usr/sbin/natd -interface en0

###/sbin/ipfw add divert natd ip from any to any via en0


sudo vi /Library/LaunchDaemons/net.openvpn.enable-vpn-forward-nat.plist


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd >

<plist version="1.0">

<dict>

<key>Label</key>

<string>net.openvpn.enable-vpn-forward-nat</string>

<key>ProgramArguments</key>

<array>

<string>/Library/Application Support/vpn/enable-vpn-forward-nat.sh</string>

</array>

<key>RunAtLoad</key>

<true/>

</dict>

</plist>


sudo launchctl load -w /Library/LaunchDaemons/net.openvpn.enable-vpn-forward-nat.plist


3.B. Integrate a NAT within the existing OS X Server pfctl rules in /etc/pf.conf and set up a launch daemon for pf. The essential pfctl NAT and filter rules are


nat on en0 from 10.0.0.0/8 to any -> (en0)

pass from { lo0, 10.0.0.0/8 } to any keep state


sudo vi /etc/pf.conf


# References for modifications:

# The Book of PF by Peter N.M. Hansteen

# http://hints.macworld.com/article.php?story=20121011004626997

# http://blog.scottlowe.org/2013/05/15/using-pf-on-os-x-mountain-lion/

# http://krypted.com/mac-security/a-cheat-sheet-for-using-pf-in-os-x-lion-and-up/


# Options

set block-policy drop

set fingerprints "/etc/pf.os"

set ruleset-optimization basic

set skip on lo0


# Normalization

# Scrub incoming packets

scrub in all no-df


#

# com.apple anchor point

#

scrub-anchor "com.apple/*"


# Queueing


# Translation


# OpenVPN Server NAT

#

# The Book of PF, p. 21

int_if = "en0" # macro for internal interface

localnet = "10.0.0.0/8"

nat on $int_if from $localnet to any -> ($int_if)


nat-anchor "com.apple/*"

rdr-anchor "com.apple/*"

dummynet-anchor "com.apple/*"

anchor "com.apple/*"

load anchor "com.apple" from "/etc/pf.anchors/com.apple"


# Filtering

lan_server = 10.0.1.3


# Antispoof

antispoof log quick for { lo0 en0 }


# Block by default

block in log


# Allow outgoing traffic from NAT'd { lo0, $localnet }

# The Book of PF, p. 21

pass from { lo0, $localnet } to any keep state


# Block to/from illegal destinations or sources

block in log quick from no-route to any


# Allow critical system traffic

pass in quick inet proto udp from any port 67 to any port 68


# Allow ICMP from home LAN

pass in log proto icmp from $lan_server:network


# Allow outgoing traffic

pass out inet proto tcp from any to any keep state

pass out inet proto udp from any to any keep state


# Internet services

internet_udp_services = "{ https, 500, 1194, 1701, 4500, 5060, 5190, 5297, 5298, 5678, 16384 }"

internet_tcp_services = "{ ssh, smtp, https, 143, 587, 993, 995, 1640, 2170, 2195, 2196, 4190,\

5218, 5223, 5190, 5220, 5222, 5298, 8008, 8443, 8800, 8843 }"

pass in quick inet proto tcp from any to { lo0, $lan_server } port $internet_tcp_services

pass in quick inet proto udp from any to { lo0, $lan_server } port $internet_udp_services


# LAN services: block access, except from localnet

lan_udp_services = "{ 5001 }"

lan_tcp_services = "{ domain, auth, nntp, www, 311, 3128, 5001, 5900:5909, 8118, 8123 }"

pass in quick inet proto tcp from { lo0, $localnet } to { lo0, $lan_server } port $lan_tcp_services

pass in quick inet proto udp from { lo0, $localnet } to { lo0, $lan_server } port $lan_udp_services


sudo vi /Library/LaunchDaemons/net.openbsd.pf.plist


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

<key>Label</key>

<string>net.openbsd.pf.plist</string>

<key>Program</key>

<string>/sbin/pfctl</string>

<key>ProgramArguments</key>

<array>

<string>/sbin/pfctl</string>

<string>-e</string>

<string>-f</string>

<string>/etc/pf.conf</string>

</array>

<key>RunAtLoad</key>

<true/>

<key>ServiceDescription</key>

<string>OpenBSD Packet Filter (pf)</string>

<key>StandardErrorPath</key>

<string>/var/log/pf.log</string>

<key>StandardOutPath</key>

<string>/var/log/pf.log</string>

</dict>

</plist>


sudo launchctl load -w /Library/LaunchDaemons/net.openbsd.pf.plist


3.C. Finally, turn on OS X Server’s adaptive firewall if you like:


sudo /Applications/Server.app/Contents/ServerRoot/usr/sbin/serverctl enable service=com.apple.afctl

sudo /Applications/Server.app/Contents/ServerRoot/usr/libexec/afctl -f

Mac mini Server (Mid 2010), OS X Mountain Lion (10.8.2), OS X Server, EyeTV HD, Turbo.264 HD

Posted on Nov 8, 2013 6:44 AM

Reply
71 replies

Dec 19, 2013 6:01 PM in response to essandess

Strange typo. I did of course mean fireWALL rules.


http://ikawnoclast.com/security/mac-os-x-pf-firewall-avoiding-known-bad-guys/


Outlines how to do it


The problem I have is that there is no easy way to log pf's activity in Mavericks (outside of running tcpdump which is not what I had in mind), that's a major feature missing.
Seems that IceFloor gets around that somehow, but I'd prefer not to use that if I don't have to.

Jan 9, 2014 7:05 AM in response to essandess

I'm seeing a similar problem with L2TP IPsec VPN built into OS X Server. Connections hang when connecting to services on the server's private LAN address via the VPN. The pf rules start with:


block in log on en0 proto { tcp udp } from any


I see blocked udp packets coming from the remote address to the external IP of the server. The funny thing was no port number was reported in the log. At first I thought it was a fluke in the log mechanism. But later tried removing the block on all udp traffic. Connections to local services started working. Not satisified with allowing all udp traffic in, I restored the block and tried a number of pass in proto udp rules but none worked. Finally I changed the block rule to:


block in log on en0 proto tcp from any

block in log on en0 proto udp from any to $ext_ip port >0


Which also worked.

Sep 20, 2014 7:53 PM in response to essandess

Addendum:


After upgrading to iOS 8 I had to re-send the client.conf file to my device and realized I neglected to mention that the client certificates should be added to your mobile device keychain by, e.g., sending yourself an email with a PKCS12 attachment. Use the following command to generate the necessary .p12 file:


openssl pkcs12 -export -in my-iPad-OpenVPN.crt -inkey my-iPad-OpenVPN.key -certfile ca.crt -name my-iPad-OpenVPN -out my-iPad-OpenVPN.p12


Add my-iPad-OpenVPN.p12 to your iOS keychain, and tell the OpenVPN client to use this client certificate.


Alternatively, you may add the client certificate and key to the .ovpn file directly within <cert></cert> and <key></key> blocks, but this means handling an unencrypted .ovpn file that contains a sensitive private key, which is less secure than using the encrypted .p12 file.

Nov 11, 2014 10:53 AM in response to amolteni

I'm a newbee and if I understand your post correctly you have succeeded in sharing your OpenVPN client connection on OSX with your iphone via an adhoc wireless connection. If so, or if someone else has managed to get this to work, can you/they share this information. Although I am particularly interested in sharing a tap connection, I would also be interested in how anyone has managed to share a tun connection.

Nov 12, 2014 6:19 AM in response to doubleos

@doubleos:


I normally route all the traffic through the VPN, on both Mac and iPhone.


I do the following steps to share the OpenVPN Connection between the mac and the iPhone (Yosemite version):


- I use Tunnelblick to start the VPN, it uses an utun device on the macbook. At this point the VPN is connected and I can browse the Internet on my MacBook.


- I start an ad-hoc connection between my macbook and my iphone. Both wifi on the mac and on the iPhone should be configured with a manual ip address, the gateway is not needed for the mac wifi network, for instance I use 192.168.10.1 on the and 192.168.10.2 on the iphone, netmask 255.255.255.0. On the iPhone, because I need to use the mac as a gateway, the wifi Router is set to 192.168.10.1 (MBP IP Address) and as DNS I use a public DNS such as 8.8.8.8.

I click on the WiFi symbol on the mac status bar and I do "Create network". This starts the ad-hoc connection.


- I check on the iPhone if the wifi is connected to the Ad-hoc connection. (You should manually select the ad-hoc network, normally its name is your macbook hostname)


- I've configured the Packet Filter to nat the ad-hoc connection to the utun0 device. I've edited the file /etc/pf.conf as root user (sudo vi /etc/pf.conf, then insert your password and modify the file).


My pf.conf looks like this:

-------------------------------------------------------------------------------- ---------------------

#

# Default PF configuration file.

#

# This file contains the main ruleset, which gets automatically loaded

# at startup. PF will not be automatically enabled, however. Instead,

# each component which utilizes PF is responsible for enabling and disabling

# PF via -E and -X as documented in pfctl(8). That will ensure that PF

# is disabled only when the last enable reference is released.

#

# Care must be taken to ensure that the main ruleset does not get flushed,

# as the nested anchors rely on the anchor point defined here. In addition,

# to the anchors loaded by this file, some system services would dynamically

# insert anchors into the main ruleset. These anchors will be added only when

# the system service is used and would removed on termination of the service.

#

# See pf.conf(5) for syntax.

#

# Translation

#

# OpenVPN Server NAT

#

# The Book of PF, p. 21

int_if = "utun0" # macro for internal interface

localnet = "192.168.10.0/24"

nat on $int_if from $localnet to any -> ($int_if)

#

#

nat-anchor "com.apple/*"

rdr-anchor "com.apple/*"

dummynet-anchor "com.apple/*"

anchor "com.apple/*"

load anchor "com.apple" from "/etc/pf.anchors/com.apple"

#

# Filtering

pass all

-------------------------------------------------------------------------------- ---------------------


- I start the packet filter and the packet forwarding with a script that contains these two commands:


nat.sh script

------------------

#!/bin/sh

/sbin/pfctl -e

sysctl -w net.inet.ip.forwarding=1

------------------


It should be launched as root, so I always run in the terminal this command (the script is in my homedir):

# sudo ./nat.sh


- At this point I can see on my Iphone that the WiFi Symbol on the top bar become active and the iphone is connected to the internet. (or to my vpn )


Please let me know if something is not clear.


Regards,

Alessio.

Nov 12, 2014 9:12 PM in response to amolteni

Thank you for the help, but I'm still having problems. The major differences to your setup are that I am on Mavericks, I used tap0 instead of utun0, and I am using a /16 subnet for my adhoc. I am still having no luck with sharing my openvpn connection to my iphone.

sudo pfctl -s nat


No ALTQ support in kernel

ALTQ related functions disabled

nat on tap0 inet from 169.254.0.0/16 to any -> (tap0) round-robin

nat-anchor "com.apple/*" all

rdr-anchor "com.apple/*" all


ifconfig -a


lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384

options=3<RXCSUM,TXCSUM>

inet6 ::1 prefixlen 128

inet 127.0.0.1 netmask 0xff000000

inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1

nd6 options=1<PERFORMNUD>

gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280

stf0: flags=0<> mtu 1280

en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500

ether 70:56:81:c4:7a:61

inet6 fe80::7256:81ff:fec4:7a61%en0 prefixlen 64 scopeid 0x4

inet 169.254.65.167 netmask 0xffff0000 broadcast 169.254.255.255

nd6 options=1<PERFORMNUD>

media: autoselect

status: active

en2: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500

options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>

ether 40:6c:8f:3e:32:4f

inet6 fe80::426c:8fff:fe3e:324f%en2 prefixlen 64 scopeid 0x5

inet 10.56.36.100 netmask 0xffffff00 broadcast 10.56.36.255

nd6 options=1<PERFORMNUD>

media: 100baseTX <full-duplex>

status: active

en4: flags=8963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500

options=60<TSO4,TSO6>

ether 32:00:17:f8:c0:a0

media: autoselect <full-duplex>

status: inactive

bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500

options=63<RXCSUM,TXCSUM,TSO4,TSO6>

ether 72:56:81:4c:c6:00

Configuration:

id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0

maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200

root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0

ipfilter disabled flags 0x2

member: en4 flags=3<LEARNING,DISCOVER>

ifmaxaddr 0 port 6 priority 0 path cost 0

nd6 options=1<PERFORMNUD>

media: <unknown type>

status: inactive

p2p0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 2304

ether 02:56:81:c4:7a:61

media: autoselect

status: inactive

tap0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500

ether 42:06:70:86:a5:71

inet 192.168.0.200 netmask 0xffffff00 broadcast 192.168.0.255

open (pid 5403)


Any ideas?

If this is solved by upgrading to Yosemite, I guess I'll do that. I've just been ****** that Apple broke OSX Server 3.2.x with Yosemite without giving notice to users. It's just the principle. I had just paid for OSX Server 3.2. It's just common courtesy to give notice when you break your own products.

Nov 13, 2014 12:59 AM in response to doubleos

@doubleos


On Mavericks you should use the following commands to nat the vpn device, packet filtering is not needed, you can use the natd process plus ipfw configuration. They changed this in Yosemite, removing the natd binary, so the only way is to use the Packet Filter configuration. I don't know if this works also on a tap devices, I think it depends on your vpn configuration.


sudo /usr/sbin/natd -interface tap0

sudo /usr/sbin/ipfw -f flush

sudo /usr/sbin/ipfw add divert natd ip from any to any via tap0

sudo /usr/sbin/ipfw add pass all from any to any


What's the main problem are you facing?


Why are you using 169.254.0.0/16 on your ad-hoc connection? I think the best way is to use something different, such as 10.0.0.0/24 or 192.168.1.0/24. Are you able to ping your iphone from your mac?

Is the iPhone able to connect to the mac via SSH? (for this I use the app "ServerAuditor" It's a free ssh client and I enable the SSH access on my mac in the Settings --> Sharing --> Flag on Remote Login)

Is the iPhone is able to connect to a device on the VPN?


Why are you using a TAP device? Do you need to bridge your networks? What's your openvpn setup?


I need to know something more to try to solve the issue.


Thanks in advance.


BR,

Alessio.






OpenVPN VPN Server on OS X Server with Mavericks, pfctl, and Tunnelblick

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.