· 6 years ago · Sep 18, 2019, 09:40 AM
1
2Sunday, 30 April 2017
3BE YOUR OWN VPN PROVIDER WITH OPENBSD (v2)
4
5
6
7INTRODUCTION :
8___________________________________________________
9I did a previous article in 2015 about this very subject, and explained how to build your VPN server on a Virtual Private Server (VPS). It was based on OpenBSD 5.6 and Vultr VPS provider. Since then, VPN has remained a hot subject of interest for a lot of people, especially after learning about all of the spying around (NSA's leaks, Wikileaks, etc...). Around me I have seen more people starting to use a VPN, and I received some questions since my last article. The main need remains the same however: if you neither trust your ISP nor a dedicated VPN provider (log or security wise), your best option is to be your own VPN provider.
10
11This new article to build your own VPN server is an upgrade of the previous one. At a glance, the upgrades are:
12- Full Disk Encryption (FDE)
13- OpenBSD 6.1 and new syspatch utility
14- Comparison of OpenVPN and IPSEC
15- Stricter firewall rules marking all inbound traffic to be blacklisted
16- Scheduled Python script (instead of bash) to blacklist the intruders
17- Separate CA/signing machine (optional)
18- Multiple DNSCrypt proxy instances for failover
19- OpenVPN: Certificate Revocation List/CRL (optional)
20- OpenVPN: TLS 1.2 only
21- OpenVPN: TLS cipher based on AES-256-GCM only
22- OpenVPN: HMAC-SHA512 instead of HMAC-SHA1
23- OpenVPN: TLS encryption of control channel (makes it harder to identify OpenVPN traffic)
24
25The others OpenVPN's encryption parameters are identical: Diffie Helman 4096 bits, server's private RSA key of 4096 bits, TLS key of 2048 bits for HMAC, AES-256-CBC used for encrypting the traffic, and Perfect Forward Secrecy (PFS).
26
27The steps described in this article have been made on a VPS from Vultr provider, with which I have no affiliation of any kind, I merely use it because it fits my needs: possibility to boot a custom ISO of any OS, console access, Two Factor Authentication with Yubikey, snapshots, and an affordable starting plan of 2.5$/month (512Mo RAM, 1 CPU, 20GB SSD, and 500GB of traffic). You are free to follow this guide and installation steps on any VPS provider of course! :-)
28
29In what follows, I expect that you were able to boot on an OpenBSD 6.1 amd64 ISO, ready to install the OS. Also, as I did it on Vultr on a SSD VPS, drive letters will be specific to it. If you install your server on a mechanical HDD, instead of /dev/sd0a you will have /dev/wd0a, and instead of a RAID /dev/sd1 you will have /dev/sd0. Therefore you must adapt the commands from this article to your particular environement.
30
31
32
33
34
35SUMMARY :
36___________________________________________________
37
38 INTRODUCTION
39
40 IPSEC OR OPENVPN
41 FULL DISK ENCRYPTION
42 2.1 ENCRYPTING THE FIRST TIME
43 2.2 UPGRADING AN ALREADY ENCRYPTED OPENBSD
44 PROTECTING YOUR NAKED SERVER
45 SYSTEM & NETWORK
46 FIREWALL
47 DNS
48 CREATING CA AND CERTIFICATES
49 VPN SERVER
50 8.1 SERVER
51 8.2 CLIENT
52 8.3 MOBILE CLIENT
53
54 CONCLUSION
55 LINKS
56
57
58
59
601. IPSEC OR OPENVPN
61___________________________________________________
62I have been asked which was better, the most secure, which one should we choose to make our VPN server. I do not intend to start a war as to which is best, and to make the suspens short, both are good. Both can be used to encrypt traffic with AES-256, they both can use certificates as well, and can work for remote "road warrior" nomade users, or mobile phones. Both use HMAC authentication for incomming packets, allowing them to drop "valid" IPSEC/OpenVPN packet which are however not signed. Both can be weaken by choosing weak encryption algorithm and/or weak passwords/keys. Fact is, I'm using both for professional use, it all depends on the context.
63
64That being said, we can still find differences between both, which may help you choose.
65
66IPSEC: has two easily identifiables UDP ports, 500 by default, and 4500 for NAT Traversal (NAT-T), and uses AH/ESP protocols. This means it can potentially be blocked voluntarily or unvoluntarily by an ISP or by a WIFI or hotel network. Also, my personal experience with IPSEC is that it is "sensitive" and can have packet loss when SA rekey timeout is reached, or when there is packet fragmentation. It can be difficult to diagnose when unexpected issues appear, such as packets not coming back despite VPN tunnel still being up, no timeout is reached, and no error messages from iked. On the other hand, OpenIKED on OpenBSD has native commands to create a CA and certificates, which is very handy. It enables us to choose from a wide selection of authentication and encryption algorithm, and the iked.conf syntax is rather easy. I find it generaly more suited for site to site VPN. Obviously IPSEC can be made perfectly stable and secure, however in my experience it requires more troubleshooting when something goes wrong.
67
68OpenVPN: CA and certificates management require an external tool, either OpenSSL or a package such as Easy-RSA. About ports, a single one needs to be used, and we are free to choose the one we want, as well as the TCP or UDP protocol. OpenVPN is a SSL VPN and does not use a separate VPN protocol. This point allows you to circumvent potential network restrictions, as at worst you will always have at least ports 80, 443, and 53 available. OpenVPN has no problem with NAT and firewalls, and works flawlesly. In my experience, I found OpenVPN perfectly stable after 2 years of usage.
69
70To be honest, when I started my new VPN server to make this article, I wanted to do it with IPSEC as iked was native to OpenBSD. I did manage to have a functional tunnel, however it never was stable no matter what I tried. It is very likely a configuration error on my side, but still it was pretty frustrating. When I finally went back to OpenVPN, I had it working at first try with a perfect stability. I personaly prefer OpenVPN, and it can also be configured to be pretty secure as well.
71
72
73back to summary
74
75
76
772. FULL DISK ENCRYPTION
78___________________________________________________
79In my previous article, I did set up a scenario where everything was in your hands: the server, the encryption algorithm, the keys and certificates, the firewall rules. It was pretty secure, in part because of running on OpenBSD which enables a lot of security features by default. However, as some people mentioned it to me, if the VPS provider is either malicious, or hacked, or for any reason has to give access to your server or give a copy of it, all of your security falls flat. Indeed, your certificates, keys, and may be even the Certificate Authority, are all stored on the server, which is not encrypted. There is therefore in this scenario a required trust toward the VPS provider.
80
81By doing a Full Disk Encryption (FDE), you are further hardening your VPN server from unauthorized access. Fortunately, FDE is natively supported on OpenBSD. From the official FAQ, OpenBSD uses AES in XTS mode. It is not specified if it is AES-128 or AES-256 though at this link. However I found a detailed analysis of OpenBSD encryption source code, and it clearly shows that AES-256 XTS is used.
82
83Please note however that while it is an improvement to setup a FDE, and an absolute security requirement in my opinion nowadays, it is still not 100% secure as the VPS provider can do a snapshot of the running system and extract information from memory.
84
85
86
872.1 ENCRYPTING THE FIRST TIME
88
89When you first boot onto the ISO to install OpenBSD, you end up on this screen:
90
91We need to create an encrypted volume before proceeding with the installation. For that, we must escape to a Shell first (last option). The OpenBSD FAQ linked above advises to write random data first to the whole drive. Indeed, if we do not, it may be possible for an adversary to tell the difference between used and unused space. It costs nothing to do it, except some patience, and if we encrypt the whole drive it's better to do it right. As a reminder, drive paths from now on are based on a SSD of Vultr VPS, if you use a classical HDD, you will have to adjust the commands.
92# dd if=/dev/random of=/dev/rsd0c bs=1m
93
94This command reads data from /dev/random and writes it to the whole /dev/rds0c device. It takes 5 minutes on my server's 20GB SSD, but it can take a lot more time on bigger drives, especially on HDD. Now we need to initialise the disk and create label for it:
95# fdisk -iy sd0
96# disklabel -E sd0
97
98Here, choose to create a partition using the entire disk, this way is easier. Notice we choose a "RAID" filesystem here, as it is what will allow us to enable encryption afterwards:
99> a a
100offset: [64]
101size: [41929586] *
102FS type: [4.2BSD] RAID
103> w
104> q
105No label changes.
106
107Now we will create an encrypted volume. By default, from the bioctl man page, there is 16 rounds of the KDF algorithm used when converting your passphrase into key. This is a defense against bruteforce, as it requires, for every passphrase to try, to make 16 rounds of it. It increases required CPU power, and will increase the time necessary to crack the passphrase. However, 16 rounds, althought conservative and being able to run on even less powerful systems, is not that much against someone having great CPU power to attack your passphrase. On a decent and recent system with fast SSD, upping it to 8192 rounds is transparent with no noticable delay after having typed the passphrase to boot the server (on my VPS server). However if I test it on a local VM on my computer, boot is delayed by 55 seconds, so it really depends on your hardware.
108
109Choose here a strong passphrase! Having a high number of rounds with a weak passphrase is useless. If in the future you want to modify your passphrase, from a user shell you will have to type this command:
110$ doas bioctl -P sd1
111
112Before coming back to the setup, as we created a new sd1 device, we have to make sure the nodes are created for it:
113# cd /dev
114# sh MAKEDEV sd1
115
116Last step, also from the official FAQ, is to zeroing the first chunk of the new device, where should normally be a Master Boot Record and disklabel instead of some garbage:
117# dd if=/dev/zero of=/dev/rsd1c bs=1m count=1
118# exit
119
120Now you come back to the setup prompt, with an sd0 device filled with an sd1 encrypted volume. You just need to answer the questions, essentially:
121Initial prompt : I (Install)
122Keyboard layout: fr (for instance)
123Hostname : MY-SERVER
124Network : keep defaults and dhcp
125Root password : choose a strong password, different than the boot passphrase
126Start sshd : yes
127Run X Window : no
128Setup a user : no
129Allow root ssh login : no
130Timezone : choose your own (e.g Europe/Paris)
131
132We do not create another user for now, and we deny root from connecting to SSH. This implies that for the begining of the setup which follows, you have to have an access to the server's console, that Vultr for instance provides.
133
134Be carefull when the disk to install to is asked for, remember to change the default from the unencrypted device to the encrypted volume:
135Which disk is the root disk? ('?' for details) [sd0] sd1
136
137Then, for the following questions:
138Use Whole disk : whole
139Use Auto layout : a
140Location of sets : http
141Package selection : -g* [enter]
142Package selection : done
143Location of sets : done
144# reboot
145
1462.2 UPGRADING AN ALREADY ENCRYPTED OPENBSD
147
148When you want to upgrade an existing system, always first read the official upgrade guide pertaining to your upgrade (here from 6.0 to 6.1). For instance, before upgrading from OpenBSD 6.0 to 6.1, some cleanup have to be made first, before booting onto the new version ISO.
149
150If you already have an encrypted server, or if you want to know how you will update it when the next OpenBSD will be released, the install steps are different. The obvious way seems to boot onto the ISO, at the setup prompt drop to a shell, mount the encrypted volume, and continue with the setup. That is in fact correct, but the bioctl manpage, althought having a parameter to "detach" a volume, does not mention how to "attach" or "mount" an existing one. The parameter to use, althought not explicitely written in the manpage, is the "-c" parameter we used to "create" our volume. If the volume already exists, bioctl will just mount it, not overwrite it:
151Once the volume is attached as sd1, as before we need to create nodes for it:
152# cd /dev
153# sh MAKEDEV sd1
154
155Then quit shell with CTRL+D, and at the setup prompt select "(U)pgrade". Once done and your upgraded server is rebooted, follow the post-upgrade steps given at the official upgrade guide.
156
157
158back to summary
159
160
161
1623. PROTECTING YOUR NAKED SERVER
163___________________________________________________
164OpenBSD default settings are very secure, and no services are listening on the outside except SSH. However SSH is listening on the default 22 port, accepting password authentication. Before configuring our server, it is best to block any inbound access except from our computer public IP, and then take our time to lock down SSH. Let's start by a basic pf ruleset:
165# vi /etc/pf.conf
166block in quick from ! x.x.x.x # your public IP address
167pass out quick
168
169Replace "x.x.x.x" by your computer public IP address you are connecting from. You can check on http://whoer.net/ if you don't know it. Of course this ruleset is temporary. Now apply it:
170# pfctl -f /etc/pf.conf
171
172Now we will create a regular user, that we will use afterwards. I'm creating a user named "guillaume" just for the example, but pick the one you want:
173# adduser
174
175First time this command is ran, some general questions are asked, keep the defaults. Then when a username is asked to create a user, enter yours, and keep the defaults for all other questions regarding your user. When that is done, we want to be able to run commands as root, in same way we used "sudo" in previous OpenBSD versions. Since 5.8, we have to use "doas" instead, which has an easier configuration syntax:
176# vi /etc/doas.conf
177permit persist guillaume as root
178
179The "persist" option makes doas command request the password the first time, and then not asking it again for a period of time. If you do not use this option, a password is requested everytime doas command is used (which will makes you crazy while configuring a server!). Now, logout from root, and try your fresh new user and enjoy the doas command from now on :-) Of course this configuration is only temporary, afterwards you should restrict the commands your user is allowed to do. You can check the doas.conf manpage for more information.
180
181We now have few things to do: create an SSH key, disable root login and password base authentication, and make SSH listening on another port as a bonus (to avoid automated scans adding "noise" to our logs). For Linux/BSD clients you can create a key with -t ed25519, but for Windows your SSH client may only be compatible with keys created using -t rsa. In the following I will suppose you have a Windows client.
182$ cd /home/guillaume
183$ ssh-keygen -t rsa
184
185Then copy your public key to ~/.ssh/authorized_keys :
186$ cd .ssh
187$ cp id_rsa.pub authorized_keys
188
189Copy the content of your private key "~/.ssh/id_rsa" (not id_rsa.pub) on the remote computer you will use to connect to your router, and set strict permissions on it. If on Linux or BSD, do a "chmod 600", and you will be able to connect later with "ssh -i your_private_key your_server_ip". On Windows, I advise you to run a recent client such as Royal TS to SSH into your server. This software is free under 10 connections, but a licence is needed if you have more. A regular Putty could do the trick, but last time I tried it would not connect (with the regular ppk file converted with puttygen), and RoyalTS has some additional interesting features such as encrypting the database, multi-tab window, custom macro, and the absolutely vital possibility to select custom icons :-)
190
191
192Now modify your SSH server with this temporary configuration, the TCP port 21598 being an example, choose the port you want. You can choose a higher port to avoid scans, or choose a port such as 443 to be able to connect to your server from everywhere, when outbound TCP port 22 could be blocked:
193$ doas vi /etc/ssh/sshd_config
194# Modify default listening port
195Port 21598
196
197# Authentication
198PasswordAuthentication yes # temporary
199PermitRootLogin no
200AllowUsers YOUR_USER
201AuthorizedKeysFile .ssh/authorized_keys
202AllowTcpForwarding no
203UsePrivilegeSeparation sandbox # Default for new installations.
204Subsystem sftp /usr/libexec/sftp-server
205
206Restart sshd :
207$ doas rcctl restart sshd
208
209Now you can connect by SSH with your user and its password, and then retrieve your id_rsa private key file. You will then need to convert it to a ".ppk" file to be able to use it to connect from Windows. One easy way to do it is to use puttygen. Just run this utility, open your id_rsa file from it, and convert it into id_rsa.ppk.
210
211Connect from your remote computer with the private key, check that connecting with your regular user and with the SSH key fully works. Once it's working, modify the following line in /etc/ssh/sshd_config :
212PasswordAuthentication no
213
214Restart sshd :
215$ doas rcctl restart sshd
216
217On the client side, if you are using a Linux/BSD client, you can enable SSH key fingerprint visual display in /etc/ssh/ssh_config which displays your SSH key in hex format and an ACSCII graphic everytime you connect. Once you get used to the ASCII graphic of your server, you should notice if all of a sudden it is completely different (probably a man-in-the-middle):
218$ doas vi /etc/ssh/ssh_config
219# Display fingerprint in hex and ASCII graphic when connecting
220VisualHostKey yes
221
222You now have a SSH listening on a non default port, root denied from connecting in, password authentication disabled, and authencation based on SSH keys and passphrase. For further SSH hardening, read the following excellent article describing which protocols and ciphers to use for optimum security. I may in the future update the SSH settings given in this article to follow some of the advices from this page.
223
224Finally, to update the system and packages easily, I really liked to use "openup" from M:Tier. However, starting with OpenBSD 6.1, a new "syspatch" command appears to easily update the base system. It won't take care of the additinal packages however, which have to be taken care of with the ports tree if you want to rely only on OpenBSD official commands and repositories. You can learn more details at the patches FAQ. "pkg_add -u" only upgrades packages if you are running the -current flavor, not the -release flavor we are running in our example. If you want the easiest way you can use openup from M:Tier, which is a third party not related to OpenBSD project, so you have to decide if you wish to trust their repository or not. If you want to use "openup":
225$ ftp https://stable.mtier.org/openup
226$ chmod +x openup
227$ doas openup
228===> Checking for openup update
229===> Installing/updating binpatch(es)
230===> Updating package(s)
231
232If however you want to use syspatch instead as I do, start it this way, and then update the external packages using the ports tree:
233$ doas syspatch
234
235To fetch the ports tree, if you don't use openup, you can follow the steps outlined in the patches FAQ link given above. Basically fetch the ports tree with CVS, run the "out-of-date" script to know which package needs an update, and run a "make update" in the affected port directories. I'll try to detail these steps later.
236
237Here we go, a fully updated system. If you use the syspatch option "-c" it will list available updates. You can read syspatch manpage for more information. Let's continue to the next part.
238
239
240back to summary
241
242
243
2444. SYSTEM & NETWORK
245___________________________________________________
246Before going further, as "vi" is not my favorite text editor, let's install "nano" instead:
247$ doas pkg_add nano
248
249A few tweaks we can make before configuring further our server. As Vultr VPS are hosted on SSD, it is a good idea to add in the fstab file the mount options "softdep" and "noatime". The first one increase disk performance, while the second will prevent the "last access time" file properties to be written:
250$ sed 's/rw/rw,softdep,noatime/g' /etc/fstab > ./fstab_tmp
251$ doas mv fstab_tmp /etc/fstab
252$ cat /etc/fstab
253
254Now, remember we are running on a fully encrypted volume. However OpenBSD also encrypt the swap by default, therefore you can disable it as it is not necessary in our context:
255$ doas nano /etc/sysctl.conf
256# Disable swap encryption (whole disk is already encrypted)
257vm.swapencrypt.enable=0
258
259Your server has booted with DHCP and has acquired its network configuration from your VPS provider. As the server IP address is fixed, I prefer setting manually the network configuration to avoid relying on another server (VPS provider's DHCP and DNS) I do not control. Modify the following files according to your server public ip address, mask, and gateway:
260$ doas nano /etc/hostname.vio0
261inet server_public_ip server_netmask
262
263Add the gateway :
264$ doas nano /etc/mygate
265server_gateway_ip
266
267Our server will forward traffic between its VPN interface and its default network interface. We have to enable forwarding:
268$ doas sysctl net.inet.ip.forwarding=1
269$ doas nano /etc/sysctl.conf
270# Enable forwarding
271net.inet.ip.forwarding=1
272
273At this point you still rely on your provider DNS server in /etc/resolv.conf, we will take care of that later.
274
275back to summary
276
277
278
2795. FIREWALL
280___________________________________________________
281The pf ruleset below does many things:
282- denies all inbound, except SSH and OpenVPN on non default ports
283- protects SSH from SYN flood, and bruteforce
284- detects accesses to all other ports and blacklist the miscreants for 24H (you cannot expect less from a Blowfish mascot!)
285- allows VPN clients to make DNS requests to unbound on localhost (which uses dnscrypt, as it will be setup in the next part)
286- does not log blocked network traffic from the provider's DHCP
287
288You should modify this ruleset according to your chosen SSH and VPN listening ports (we will setup OpenVPN later, but you can choose a random port now). The scan detection and blacklisting concept can theoretically backfire, if someone sends spoofed packets with trusted IPs. However, as it is implemented, only incoming traffic will match the blacklist table. Therefore, if a spoofed packet is sent with a website IP you trust, it will not prevent you to access that website at all as your traffic will be outbound. Also, as we will write a script to parse pf logs and add IPs into the blacklist, we will add an exception for our own trusted client computer public IP address to avoid being locked out. As a last resort the remote console access from the VPS provider could allow us to login, if we were totally locked out for any reason. This adaptative behavior is not mandatory for your VPN server to be operational, but it is useful to block automatic scans on the Internet from querying your SSH and VPN ports once they hit your server on another port.
289
290$ doas cp /etc/pf.conf /etc/pf.allow.conf
291$ doas nano /etc/pf.conf
292# Title: "Being your own VPN provider with OpenBSD v2"
293# Author: Guillaume Kaddouch: http://networkfilter.blogspot.com/
294# Date: ruleset last modified: 2017 April 17 for OpenBSD 6.1
295# Github: https://github.com/gkweb76/networkfilter/blob/master/pf.conf
296
297# VARIABLES, MACRO, AND TABLES
298# ---------------------------------------------------------------------------------------
299vpn="tun0"
300vpn_ip="10.8.0.1"
301all_networks="0.0.0.0/0"
302private_networks="10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16"
303ssh_port="21598" # just a random example, modify to match your chosen SSH port
304vpn_port="21599" # just a random example, modify to match your chosen OpenVPN port
305
306bad_ports="{ 1:66, 69:21597, 21600:65535 }" # adjust according to your SSH and VPN ports (+ DHCP)
307
308table <internet> const { $all_networks, !self, !$private_networks }
309table <myself> const { self }
310table <bruteforce> persist
311table <badguys> persist
312
313# GLOBAL POLICY
314# ---------------------------------------------------------------------------------------
315set block-policy drop
316set loginterface egress
317set skip on lo
318block log all
319match in all scrub (no-df max-mss 1440 random-id)
320block in log quick from <bruteforce> label "bruteforce"
321block in log quick from <badguys> label "old_guys"
322
323# DEFAULT TRAFFIC TAGGING
324# --------------------------------------------------------------------------------
325match in on egress proto tcp from <internet> to port $ssh_port
326match in on egress proto { udp tcp } from <internet> to port $bad_ports
327match in on egress proto udp to port $vpn_port
328match in on $vpn proto { icmp udp tcp }
329match in on $vpn proto { udp tcp } to $vpn_ip port domain
330match out on egress tagged VPN_TUN_IN
331match out on egress proto tcp from <myself> to port { http https }
332match out on egress proto { udp tcp } from <myself> to port domain
333match out on egress proto udp from <myself> to port https
334match out on egress proto udp from <myself> to port ntp
335match in on egress from { no-route urpf-failed } to any
336match out on egress from any to no-route
337match inet6 all
338 tag SSH_IN
339 tag BAD_GUYS
340 tag VPN_EGRESS_IN
341 tag VPN_TUN_IN
342 tag VPN_DNS_IN
343 tag VPN_FORWARD
344 tag HTTP_OUT
345 tag DNS_OUT
346 tag DNS_OUT
347 tag NTP_OUT
348 tag BAD_PACKET
349 tag BAD_PACKET
350 tag IPV6
351
352# POLICY ENFORCEMENT
353# ---------------------------------------------------------------------------------------
354match in tagged VPN_EGRESS_IN set tos lowdelay set prio 6
355match out tagged VPN_FORWARD nat-to (egress) set prio 6
356
357# Blocking spoofed or malformed packets, IPv6, and some bad traffic
358antispoof log quick for egress label "antispoof"
359block quick log tagged BAD_PACKET label "noroute_urpf"
360block quick log tagged IPV6 label "ipv6"
361block quick log tagged BAD_GUYS label "new_guy"
362
363# Standard rules
364# protect SSH from SYN flood and bruteforce
365pass in quick tagged SSH_IN synproxy state \
366(max-src-conn 10, max-src-conn-rate 5/5, overload <bruteforce> flush global)
367
368# Redirect VPN clients DNS requests to unbound
369pass in quick inet tagged VPN_DNS_IN rdr-to 127.0.0.1 port domain
370
371pass in quick tagged VPN_EGRESS_IN
372pass in quick tagged VPN_TUN_IN
373
374pass out quick tagged HTTP_OUT
375pass out quick tagged DNS_OUT
376pass out quick tagged VPN_FORWARD modulate state
377pass out quick tagged NTP_OUT
378
379# no log for
380block in quick proto udp from 0.0.0.0 port bootpc to port bootps
381block in quick proto udp from any port bootps to 255.255.255.255 port bootpc
382
383
384If you prefer, you can now fetch this ruleset from this Github link.
385
386Check the ruleset syntax, with the first command, and then apply it if no error returned:
387$ doas pfctl -nf /etc/pf.conf
388$ doas pfctl -f /etc/pf.conf
389
390Now we have to make our script which will look for IPs blocked, because they dare approaching our server, labeled "new_guy". Script will be in /home/user/scripts:
391$ doas pkg_add python3.5
392$ cd ~
393$ mkdir scripts
394$ cd scripts
395$ doas nano ./block_intruders.py
396#!/usr/local/bin/python3.5
397# File: block_intruders.py
398# Version: 1.0
399# Date: 2017/04/21
400# Blog: https://networkfilter.blogspot.com
401import subprocess
402
403# Modify below your home path, and your trusted public IP address you connect from
404badguys_file = '/home/guillaume/badguys.txt'
405pf_file = '/etc/pf.conf'
406rule_tag = 'new_guy'
407rule_id = 0
408get_ruleset = 'pfctl -sr'
409get_blocked_ips = 'tcpdump -enr /var/log/pflog'
410block_badguys = 'pfctl -t badguys -T add -f ' + badguys_file
411counter = 0
412trusted = ['YOUR_PUBLIC_IP_HERE']
413
414def execute(cmd):
415 "Execute the provided commmand and return its output "
416 output = subprocess.getoutput(cmd)
417 return output.split('\n')
418
419# Retrieve loaded rules with 'pfctl -sr'
420ruleset = execute(get_ruleset)
421
422# Locate our blacklisting rule ID
423for rule in ruleset:
424 counter += 1
425 if rule_tag in rule:
426 rule_id = counter - 1
427 break
428
429# Retrieve all blocked IPs from /var/log/pflog
430blocked_ips = execute(get_blocked_ips)
431
432# Match only IPs blocked by our blacklisting rule
433badguys = []
434for logline in range(len(blocked_ips)):
435 if ('rule ' + str(rule_id) + '/(match) block') in blocked_ips[logline]:
436 ip = blocked_ips[logline].split()[7] # retrieve ip.port
437 ip = ip.split('.')[0] + '.' + ip.split('.')[1] + '.' + ip.split('.')[2] + '.' + ip.split('.')[3]
438 badguys.append(ip)
439
440# Remove duplicate IPs
441badguys = list(set(badguys))
442
443# Remove your trusted IPs from the list!
444for good_ip in trusted:
445 for bad_ip in badguys:
446 if bad_ip == good_ip:
447 badguys.remove(bad_ip)
448
449# Writing the list of bad IPs to a file
450with open(badguys_file, 'w') as file:
451 for ip in badguys:
452 file.write(ip + '\n')
453
454# Finally blocking the badguys ;-(
455execute(block_badguys)
456view raw
457block_intruders.py hosted with ❤ by GitHub
458If you do not see the embedded source code frame above (you are blocking scripts from https://gist.github.com), you can access with this direct link the script source code.
459
460Edit the crontab to schedule execution of this script, as well as check every hour to expire table entries older than 24 hours. Be sure to make the script readable, executable, and writable only by root, as it will run in root's crontab. Replace "/home/your_user" below with your username:
461$ doas chown root:wheel block_intruders.py
462$ doas chmod 700 block_intruders.py
463$ doas crontab -e
464# add badguys to the pf table to be blocked
465*/5 * * * * python3.5 /home/guillaume/scripts/block_intruders.py
466
467# Clear pf tables
4680 * * * * pfctl -t bruteforce -T expire 86400
4690 * * * * pfctl -t badguys -T expire 86400
470
471You can check with another script the state of your blacklist/badguys table, ports blocked to them once blacklisted , and number of blocked IP in your bruteforce table. Here is the script example, you can freely modify:
472$ doas nano ./pf_show_tables.py
473#!/usr/local/bin/python3.5
474# File: pf_show_tables.py
475# Version: 1.0
476# Date: 2017/04/21
477# Blog: https://networkfilter.blogspot.com
478import subprocess
479import re
480from collections import defaultdict, OrderedDict
481
482
483pf_file = '/etc/pf.conf'
484rule_tag_new = 'new_guy'
485rule_tag_old = 'old_guy'
486rule_id = 0
487pf_ruleset = 'pfctl -sr'
488pf_badguys = 'pfctl -t badguys -T show'
489pf_bruteforce = 'pfctl -t bruteforce -T show'
490pf_stats = 'pfctl -sl'
491
492get_blocked_ips = 'tcpdump -enr /var/log/pflog'
493counter = 0
494
495def execute(cmd):
496 "Execute the provided commmand and return its output "
497 output = subprocess.getoutput(cmd)
498 return output.split('\n')
499
500def getKey(item):
501 "Return the item to base the sort on, used by sorted() function"
502 return item[1]
503
504
505# Retrieve all blocked IPs from /var/log/pflog
506blocked_ips = execute(get_blocked_ips)
507
508# Parse blocked IPs and ports and sort them out
509blocked_list = defaultdict(list)
510stats_ip = defaultdict(list)
511stats_ports = defaultdict(list)
512ip = ''
513
514total_block = 0
515for logline in range(len(blocked_ips)):
516 if ('/(match) block in on') in blocked_ips[logline]: # if an inbound block was logged
517 ip = blocked_ips[logline].split()[7] # retrieve ip.port
518 total_block += 1
519
520 if not 'icmp' in blocked_ips[logline]:
521 port = ip.split('.')[4]
522 else:
523 port = 'icmp'
524
525 ip = ip.split('.')[0] + '.' + ip.split('.')[1] + '.' + ip.split('.')[2] + '.' + ip.split('.')[3]
526
527 # Keep track of IP:ports tried, e.g: 127.0.0.1 : 21, 80, 443, 443, 443, 8080, ...
528 blocked_list[ip].append(port)
529
530 # Keep track of how many deny per IP, e.g: 127.0.0.1 : 5, 127.0.0.2 : 1, ...
531 if stats_ip.get(ip, 'NA') == 'NA':
532 stats_ip[ip] = 1
533 else:
534 stats_ip[ip] = stats_ip[ip] + 1
535
536 # Keep track of how many deny per port, e.g: 80 : 5, 443 : 20, ...
537 if stats_ports.get(port, 'NA') == 'NA':
538 stats_ports[port] = 1
539 else:
540 stats_ports[port] = stats_ports[port] + 1
541
542# Final ordered lists, about top blocked IPs and ports
543top_ports = OrderedDict(sorted(stats_ports.items(), key=getKey, reverse=True))
544top_ips = OrderedDict(sorted(stats_ip.items(), key=getKey, reverse=True))
545
546print('\nStatistics:')
547print('-------------')
548print('Blacklisted IPs: %d' % len(execute(pf_badguys)))
549print('Blocks : %d' % total_block)
550
551print('\nTOP blocked IPs:')
552print('------------------')
553count = 0
554max = 5
555for ip in top_ips:
556 if top_ips[ip] > 1:
557 print('%s : %d times ' % (ip, top_ips[ip]))127.0.0.1 : 5, 127.0.0.2 : 1, ...
558 count +=1
559 if count == max: break
560
561print('\nTOP blocked ports:')
562print('--------------------')
563count = 0
564for port in top_ports:
565 if top_ports[port] > 1:
566 print('%s : %d times ' % (port, top_ports[port]))
567 count +=1
568 if count == max: break
569view raw
570pf_show_tables.py hosted with ❤ by GitHub
571If you do not see the embedded source code frame above (you are blocking scripts from https://gist.github.com), you can access with this direct link the script source code.
572
573Below is also an example of a possible output from the second script:
574$ doas chown root:wheel pf_show_tables.py
575$ doas chmod 700 pf_show_tables.py
576$ doas ./pf_show_tables.py
577
578
579You now have a nice adaptative firewall, however as warned above, be careful as it can backfire if trusted IPs are not excluded and become blacklisted. It is a working configuration I had no trouble with, in my context, but in your situation it may have to be modified to fit your case.
580
581
582back to summary
583
584
585
5866. DNS
587___________________________________________________
588We will use DNSCrypt to make our DNS requests encrypted, and Unbound to have a local DNS cache. This will allow us to avoid using our VPS provider DNS servers, and will also be useful to your future VPN clients which will be able to use your VPN server as their DNS server too, if they wish too (e.g mobile phones). Both dnscrypt and unbound will listen on the localhost only, not to the outside. They will be reachable nonetheless later to your VPN clients trough the VPN tunnel, using a firewall redirection.
589
590$ doas pkg_add dnscrypt-proxy
591$ doas rcctl enable dnscrypt_proxy
592$ doas rcctl set dnscrypt_proxy flags -a 127.0.0.1:40 -l /dev/null -R dnscrypt.eu-dk
593
594You can choose your dnscrypt enabled DNS server at the following list (choose a logless DNSSEC enabled one).
595
596Now let's start it:
597$ doas rcctl start dnscrypt_proxy
598
599Before configuring Unbound, which is the local DNS cache which will make requests to dnscrypt_proxy, we can configure an additional dnscrypt instance, as explained in the pkg readme. Indeed, dnscrypt DNS servers being public ones, they often goes into maintenance, become offline or temporarily unreachable. To address this issue, it is possible to setup multiple dnscrypt instances. Below are the steps to follow to add one, but you can add more if you wish. Notice the different local port 41 and different DNS server:
600$ doas ln -s dnscrypt_proxy /etc/rc.d/dnscrypt_proxy2
601$ doas rcctl enable dnscrypt_proxy2
602$ doas rcctl set dnscrypt_proxy2 flags -a 127.0.0.1:41 -l /dev/null -R dnscrypt.eu-nl
603$ doas rcctl start dnscrypt_proxy2
604$ ps aux | grep dnscrypt
605
606You should see now two dnscrypt processes running, listening on ports 40 and 41, and linked to a different remote DNS server. This setup will be reflected in Unbound configuration below.
607
608We now configure and enable unbound, already included in the base system. Unbound will drop privileges to user _unbound and will be chrooted in /var/unbound by default. Also by default, only localhost is allowed and everything else is refused. This is why it is unnecessary to specify the username/directory/chroot options below, or to define a default access-control. We just add what we need:
609$ doas nano /var/unbound/etc/unbound.conf
610server:
611do-not-query-localhost: no
612interface: 127.0.0.1
613access-control: 10.8.0.0/24 allow
614hide-identity: yes
615hide-version: yes
616auto-trust-anchor-file: "/var/unbound/db/root.key"
617
618forward-zone:
619name: "." # use for ALL queries
620forward-addr: 127.0.0.1@40 # dnscrypt-proxy
621forward-addr: 127.0.0.1@41 # dnscrypt-proxy failover
622
623Do not forget to modify your /etc/resolv.conf:
624$ doas nano /etc/resolv.conf
625nameserver 127.0.0.1 # unbound is listening there at port 53
626
627To prevent dhclient from overwriting our nameserver in resolv.conf, add this line to dhclient.conf:
628$ doas nano /etc/dhclient.conf
629supersede domain-name-servers 127.0.0.1
630
631Run Unbound, and enable it to launch at startup :
632$ doas rcctl enable unbound
633$ doas rcctl start unbound
634
635Now that the outbound DNS requests are only made using dnscrypt on UDP port 443, we can comment/disable/remove the rule that allows outbound DNS requests on UDP/TCP 53:
636$ doas nano /etc/pf.conf
637#match out on egress proto { udp tcp } from <myself> to port domain tag DNS_OUT
638
639$ doas pfctl -nf /etc/pf.conf
640$ doas pfctl -f /etc/pf.conf
641
642Test that your DNS chain is working:
643$ host openbsd.org
644openbsd.org has address 129.128.5.194
645openbsd.org mail is handled by 6 shear.ucar.edu.
646openbsd.org mail is handled by 10 cvs.openbsd.org.
647
648Unbound is listening on locahost port 53, and when contacted is forwarding to dnscrypt listening on locahost port 40, itself contacting an external dnscrypt enabled DNS server.
649
650
651back to summary
652
653
654
6557. CREATING CA AND CERTIFICATES
656___________________________________________________
657We need to create a Certificate Authority (CA) which will enable us to create certificates for our VPN server, our home router, and any other client we may have. A Certificate Authority will also be able to revoke certificates, which will prevent unused or lost ones to be accepted by our VPN server.
658
659This CA, as you can see, has a critical role. A breach of the CA would means an attacker could steal certificates and private keys and spy on our communications, and could create certificates for himself. Therefore, I strongly advise you to create a dedicated CA server, that you can store and run on your local computer as a virtual machine for instance that you can start on demand. It is possible though to follow the steps below directly on your VPN server, but I do not recommend it.
660
661Ideally, from a dedicated OpenBSD 6.1 CA host:
662$ doas pkg_add easy-rsa
663$ cd /usr/local/share/easy-rsa/
664$ doas cp vars.example vars
665$ doas nano vars
666# 2048 - > 4096
667set_var EASYRSA_KEY_SIZE 4096
668# sha256 -> sha512
669set_var EASYRSA_DIGEST "sha512"
670
671Now it's time to create our new Public Key Infrastructure (PKI) and CA :
672$ doas easyrsa init-pki
673$ doas easyrsa build-ca
674
675Choose your CA's passphrase wisely, preferably a strong one, it will be requested for every subsequent certificates you will want to create.
676
677Then, from easy rsa readme, the recommanded steps to create certificates is to install easy-rsa on every requesting hosts, server and clients, generate a request with "./easyrsa init-pki" and "./easy-rsa gen-req", import it on the CA server, and sign them. Then transport the newly created certs to each hosts. However this is not always doable, for instance if the client is a mobile phone.
678
679If you prefer to do it all at once on the CA signing server directly:
680$ doas easyrsa build-server-full your-server-name nopass
681$ doas easyrsa build-client-full your-home-router-name nopass
682
683The "nopass" option creates certificates without password, to allow openvpn to start automatically with the certificate without additionally prompting for a password. Then, we will create a Certificate Revocation List (CRL). This is optional, but I highly recommend it, as it will enable you to revoke certificates in the future if you need it. Without it, if you loose a certificate (stolen phone) or if you want to blacklist a certificate for any reason, the only possibility will be to create a whole new PKI and CA, and recreate all certificates. Better be safe than sorry and create a CRL:
684$ doas easyrsa gen-crl
685
686This will create the file /usr/local/share/easy-rsa/pki/crl.pem
687
688Now we have to build 4096 bits Diffie-Hellman parameters, be warned it can take a while!
689$ doas easyrsa gen-dh
690$ doas mv ./pki/dh.pem ./pki/dh4096.pem
691
692You can additionally generate an OpenVPN Pre-Shared Key (PSK) that will have to be copied to all server and clients:
693$ doas pkg_add openvpn
694$ doas mkdir -p /usr/local/etc/openvpn/secret
695$ cd /usr/local/etc/openvpn/secret
696$ doas openvpn --genkey --secret ta.key
697
698On server, we will need to copy :
699- ca.crt : Root CA certificate [.../easy-rsa/pki/]
700- crl.pem : Certificate Revocation List [.../easy-rsa/pki/] (optional)
701- dh4096.pem : Diffie Hellman parameters [.../easy-rsa/pki/]
702- server.crt : Server Certificate [.../easy-rsa/pki/issued/]
703- server.key : Server Key (private) [.../easy-rsa/pki/private/]
704- ta.key : OpenVPN TLS PSK [/usr/local/etc/openvpn/secret]
705
706On clients, we will need to copy:
707- ca.crt : Root CA certificate [.../easy-rsa/pki/]
708- client.crt : Client Certificate [.../easy-rsa/pki/issued/]
709- client.key : Client Key (private) [.../easy-rsa/pki/private/]
710- ta.key : OpenVPN TLS PSK [/usr/local/etc/openvpn/secret]
711
712This way, your CA private key stays on your signing host, if you choosed a separate host as advised.
713
714To transfer the required files to the aformentioned hosts, server and clients, an encrypted channel must be used. If you have network access from your signing host to your server and clients, you can do it directly with SCP. Or if you prefer doing it from an intermediate Windows machine, you can use WinSCP to connect simultaneously to the signing machine, server, and client, and transfer the files this way.
715
716
717back to summary
718
719
720
7218. VPN SERVER
722___________________________________________________
7238.1 - SERVER
724
725Now that our server is up and running, and our CA and certificates are created, we can finally setup OpenVPN on our server. Let's download our packages and create the required directories:
726$ doas pkg_add openvpn
727$ doas mkdir -p /usr/local/etc/openvpn/{public,secret}
728
729Do not forget to copy your certificates and keys, by changing directory to the folder you copied them to earlier:
730$ cd folder_where_certs_and_keys_are
731$ doas mv *.crt /usr/local/etc/openvpn/public/
732$ doas mv *.key /usr/local/etc/openvpn/secret/
733
734Making sure these secret files are only accessible to root:
735$ doas chmod -Rf 600 /usr/local/etc/openvpn/secret/
736$ doas chown -Rf root:wheel /usr/local/etc/openvpn/secret/
737
738Let's create the main OpenVPN server configuration file:
739$ doas nano /usr/local/etc/openvpn/server.conf
740# Server configuration
741# SSL/TLS certificate and keys, PFS enabled by default
742
743# Since OpenVPN 2.4, "tls-crypt" can be used instead of "tls-auth", as it does authentication and
744# encryption of all control channel packets. Purpose of encrypting these packets: provides more
745# privacy, makes it harder to identify OpenVPN traffic, and masks the pre-shared key
746# (if I understand the official OpenVPN 2.4 manpage)
747
748# ** WARNING ** as of 28 May 2017, "OpenVPN Connect" v1.1.1 app on iOS/iPhone does not
749# yet support "tls-crypt" option. Consequently, if you have mobile phone clients,
750# you should use "tls-auth /your_path/ta.key 0" instead on the server.
751
752ca "/usr/local/etc/openvpn/public/ca.crt"
753crl-verify "public/crl.pem" # Certificate Revocation List (optional)
754cert "/usr/local/etc/openvpn/public/server.crt"
755dh "/usr/local/etc/openvpn/public/dh4096.pem" # Diffie Helman 4096 bits
756key "/usr/local/etc/openvpn/secret/server.key" # RSA 4096 bits
757tls-crypt "/usr/local/etc/openvpn/secret/ta.key" # TLS 2048 bits for HMAC and encryption
758
759# Protocols and ciphers
760cipher AES-256-CBC # AES 256 bits
761tls-version-min 1.2 # Only allow TLS 1.2
762tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384
763auth SHA512 # HMAC-SHA512 (default is SHA1)
764
765# Network parameters
766port 21600 # as an example, pick your own port
767proto udp
768dev tun
769tls-server
770server 10.8.0.0 255.255.255.0
771ifconfig-pool-persist ipp.txt
772push "redirect-gateway def1 bypass-dhcp"
773
774# push our DNS server to clients accepting it (will not override a home router DNS configuration
775# with fixed DNS settings). Usefull for mobile phones for instance, where installing
776# dnscrypt requires a rooted phone
777push "dhcp-option DNS 10.8.0.1"
778
779keepalive 10 120
780comp-lzo yes
781
782# Limits
783max-clients 5 # change this value if you plan on connecting from more clients
784
785# Privileges, chroot
786chroot /var/openvpn
787user _openvpn
788group _openvpn
789persist-key
790persist-tun
791
792# LOG
793verb 4
794mute 20
795
796Now we should prepare the chroot environnement, as OpenVPN will chroot itself after being started. We must copy the CRL file in the chroot folder as it is checked after OpenVPN has chrooted its process, making the initial "/usr/local/etc/openvpn/" directory not accessible. That's also why the CRL path in the previous configuration file is not a full path, as the root folder changes after initilization:
797$ doas mkdir -p /var/openvpn/{tmp, public}
798$ doas cp public/crl.pem /var/openvpn/public
799
800It is time to start our server and to make it start at boot time:
801$ doas /usr/local/sbin/openvpn --config /usr/local/etc/openvpn/server.conf --daemon
802$ doas nano /etc/rc.local
803# OpenVPN
804/usr/local/sbin/openvpn --config /usr/local/etc/openvpn/server.conf --daemon
805
806You should check with tail -f /var/log/messages that OpenVPN started successfully. If something went wrong, check your files and folders ownership and rights. Make also sure that both client and server time are close enough, to avoid any trouble with your certficates.
807
8088.2 - CLIENT
809
810In my OpenBSD router article there is a chapter about an OpenVPN client installation and configuration. Below I will just provide the OpenVPN configuration file to use on the client side:
811$ doas nano /usr/local/etc/openvpn/client.conf
812# Client configuration (router, computer)
813# SSL/TLS certificate and keys
814ca "/usr/local/etc/openvpn/ca.crt" # public
815cert "/usr/local/etc/openvpn/myhome.crt" # public
816key "/usr/local/etc/openvpn/myhome.key" # secret
817tls-crypt "/usr/local/etc/openvpn/ta.key" # secret (OpenVPN 2.4 or higher)
818
819# Protocols and ciphers
820cipher AES-256-CBC # AES 256 bits
821tls-version-min 1.2 # Only allow TLS 1.2
822tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384
823auth SHA512 # HMAC-SHA512 (default is SHA1)
824
825remote-cert-tls server
826client
827dev tun
828proto udp
829resolv-retry infinite
830nobind
831comp-lzo yes
832
833# VPS OpenBSD
834remote YOUR_SERVER_IP 21600
835
836# Privileges, chroot
837user _openvpn
838group _openvpn
839chroot /var/empty
840persist-key
841persist-tun
842
843# LOG
844verb 3
845explicit-exit-notify 5
846
847
848Client keys must be copied from the CA server to the client over a secure channel, for instance by using SCP/WinSCP to transfer them trough SSH, as advised earlier.
849
8508.3 - MOBILE CLIENT
851
852A smartphone such as Android or iOS can download and install "OpenVPN Connect". Then you will have to transfer on the phone an OpenVPN configuration file, which will require to have the whole configuration in it, including certificates and keys. The configuration file has the certificates and keys inside it. Basically you just copy/paste the content of the required files and insert them between tags like below (as of 28 May 2017, "OpenVPN Connect" v1.1.1 app on iOS/iPhone does not yet support "tls-crypt". We should use tls-auth on both client and server) :
853# Protocols and ciphers
854cipher AES-256-CBC # AES 256 bits
855tls-version-min 1.2 # Only allow TLS 1.2
856tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384
857auth SHA512 # HMAC-SHA512 (default is SHA1)
858
859# Global options
860remote-cert-tls server
861client
862dev tun
863proto udp
864resolv-retry infinite
865nobind
866comp-lzo yes
867
868remote YOUR_SERVER_IP 21600
869
870persist-key
871persist-tun
872key-direction 1
873
874# LOG
875verb 3
876explicit-exit-notify 5
877# ca.crt below, just a random example. Full extract is above 35 lines
878<ca>
879-----BEGIN CERTIFICATE-----
880ffidLLDKSJskfjf56s/smdjdhQSDOQSLDJLQSJDQSd45454QMDMSQMDMklajzEd4
881.
882.
883.
884.
885sqd54dLLDKSJskfjf56ssmdjdsqdqsdSDL
886-----END CERTIFICATE-----
887</ca>
888
889# myphone.crt below, just a random example. Full extract is above 35 lines
890<cert>
891Certificate:
892Data:
893Version: 3 (0x2)
894Serial Number: 4 (0x4)
895Signature Algorithm: sha512WithRSAEncryption
896.
897.
898.
899.
900-----BEGIN CERTIFICATE-----
901ffidLLDKSJskfjf56s/smdjdhQSDOQSLDJLQSJDQSd45454QMDMSQMDMklajzEd4
902.
903.
904.
905.
906sqd54dLLDKSJskfjf56ssmdjdsqdqsdSDL
907-----END CERTIFICATE-----
908</cert>
909
910# myphone.key below, just a random example. Full extract is above 35 lines
911<key>
912-----BEGIN CERTIFICATE-----
913ffidLLDKSJskfjf56s/smdjdhQSDOQSLDJLQSJDQSd45454QMDMSQMDMklajzEd4
914.
915.
916.
917.
918sqd54dLLDKSJskfjf56ssmdjdsqdqsdSDL
919-----END CERTIFICATE-----
920</key>
921
922# ta.key below, just a random example. Full extract is above 20 lines
923<tls-auth>
924#
925# 2048 bit OpenVPN static key
926#
927-----BEGIN OpenVPN Static key V1-----
928ffidLLDKSJskfjf56s/smdjdhQSDOQSL
929.
930.
931.
932.
933sqd54dLLDKSJskfjf56ssmdjdsqdqsdS
934-----END OpenVPN Static key V1-----
935</tls-auth>
936
937This configuration file, you can name "myserver.ovpn" for instance, must be transfered into the phone via a secure channel. As I'm using SpiderOak I used the Hive folder (which is a synchronized folder among many clients/devices) to retrieve the file. However, once you copied with SCP the phone keys and certificate on your computer, I guess a classical USB transfer will do the job as well :-)
938
939Your VPN server is now finished!
940
941back to summary
942
943
944
945CONCLUSION :
946___________________________________________________
947We have seen how to build an OpenVPN server based on OpenBSD with Full Disk Encryption, to benefit from OpenBSD's memory protection, randomness implementation, LibreSSL, and secure by default philosophy. I find the VPS server to be a cost effective way to build our own VPN server, with many benefits such as snapshot before an upgrade, full access to boot the wanted ISO, remote console access, datacenter country location choice, and Two-Factor authentication with Yubikey. It is a way to be in full control of your computer, your home router, and your VPN server at the other end.
948
949I used previously systrace which was removed starting from OpenBSD 6.0, because an application could circumvent it. Systrace was replaced by Pledge which is aimed at being more secure, but it works differently. Now with pledge, programmers have to include pledge API calls inside their code to be able to benefits from Pledge. Basically an application can request a mode allowing it to open files, or manage memory, but as soon as it will try for instance a network action not previously requested, the process will be killed. Theo de Raadt himself presented this new security feature at Hackfest 2015, you can find in the following links the slides and the video. A lot of base programs are already pledge protected: cat, chmod, mkdir, tar, gzip, ping, ftp, doas, nc, openssl, tcpdump, ntpd, httpd, smtpd, etc... From this 2015 slide, more than 400 programs were converted in 6 months. Apparently however, if I'm not mistaken, OpenVPN is not yet in the list unfortunately.
950
951Also, in my previous article I talked about Security vs Anonymity, I won't include it here but you can jump to this link if you never read it.
952
953In the end, there is still benefits of using an external VPN provider such as AirVPN. You may spend less money yearly depending on your required traffic volume, and spend less time as you do not have to maintain a server or upgrade it.
954
955By using an external VPN provider you choose convenience, whereas by managing your own server you prioritize control and trust. Make your choice!
956
957
958LINKS
959___________________________________________________
960http://www.openbsd.org
961https://www.vultr.com
962http://xn--thibaud-dya.fr/
963http://whoer.net/
964https://www.royalapplications.com/
965https://the.earth.li/~sgtatham/putty/
966https://stribika.github.io/
967https://stable.mtier.org/
968https://github.com/gkweb76/networkfilter/
969https://github.com/jedisct1/dnscrypt-proxy/blob/master/dnscrypt-resolvers.csv
970https://spideroak.com
971http://daemonforums.org/
972https://airvpn.org
973
974Follow me @gkweb76
975
976
977
978Posted by Guillaume Kaddouch at 18:03 Email ThisBlogThis!Share to TwitterShare to FacebookShare to Pinterest
97926 comments:
980
981 Anonymous10 May 2017 at 20:01
982
983 A treasure, printing this out
984 Your mastery is apparent
985 skrp
986 Reply
987 Replies
988 Guillaume Kaddouch10 May 2017 at 22:09
989
990 Thanks for your comments, and glad you like this article :-)
991
992 Regards,
993 Guillaume
994 Reply
995 Anonymous29 May 2017 at 15:57
996
997 Magic!
998 Reply
999 Anonymous10 July 2017 at 05:49
1000
1001 This is indeed a detailed and great guide.
1002 However, I've run into an issue and appreciate some insight.
1003 I used Vultr, and custom ISO, And went through the installation process, seemingly with no errors, but after rebooting, I'm back at the prompt for installation!
1004 Do I need to mount sd1, or something?
1005 Thanks in advance, for any advice.
1006 Reply
1007 Replies
1008 Dariusz Fedejko11 July 2017 at 09:19
1009
1010 Hey,
1011
1012 just go to the settings of your vps and eject iso image.
1013 Guillaume Kaddouch11 July 2017 at 21:11
1014
1015 Check your ISO is dismounted, if not the server will start again on the ISO automatically.
1016
1017 Guillaume
1018 Anonymous13 July 2017 at 23:59
1019
1020 Thank you gentlemen; I realized my mistake and all is good now.
1021
1022 Guillaume, I'm wondering why you created the SSH keys on the server, instead of the client side (i.e. just having the private key on the client machine); is it because you have a Windows client, or was there another reason?!
1023
1024 Nevertheless, I'm behind Dariusz in my setup and will get to the DNS setup this weekend; when I'm done, I leave a comment about my experience and if I had issues with dnscrypt.
1025
1026 Thanks.
1027 Reply
1028 Dariusz Fedejko10 July 2017 at 14:00
1029
1030 Unfortunately, the part with unbound and dnscrypt doesn't work for me. If I change the nameserver address in resolv.conf to 127.0.0.1 I can't resolve names. If I change back to my previous nameserver - it works but it doesn't go through local unbound + dnscrypt. If you're willing to take a closer look at my case, I'll paste all config files here.
1031 Reply
1032 Replies
1033 Guillaume Kaddouch11 July 2017 at 21:08
1034
1035 You have to check step by step: did you miss any step provided? Are unbound and dnscrypt listening (netstat)? If yes, does the dnscrypt servers you choose are down? Does your firewall rules allow outbound dnscrypt traffic? etc... By checking all those points and with the help of tcpdump, you should be able to see what's wrong.
1036
1037 Guillaume
1038 Dariusz Fedejko12 July 2017 at 12:30
1039
1040 Hi,
1041
1042 Before writing my previous comment, I followed all steps three times: on my home OpenBSD router, on Vultr VPS and on a VirtualBox VM.
1043
1044 And then I did it fourth time on Vultr VPS - I reinstalled the system, but this time without full-disk encryption, with default pf settings, one dnscrypt_proxy instance and with no python scripts - to keep things simple.
1045
1046 I used port 4040 for dnscrypt_proxy, nestat shows it's listening. rcctl ls on shows both services are running, DNS server in Danemark is up (77.66.84.233). Previously I was using port 40 - the result was the same.
1047
1048 I don't get it. For a moment it worked - nslookup showed I was using 127.0.0.1 for name resolution, I could ping other hosts, but after I did rcctl stop unbound and rcctl start unbound (both with messages "unbound(ok)") - it stopped working.
1049
1050 Here are my config files: https://pastebin.com/raw/iwL2DGWP
1051
1052 I'll be most obliged if you help me solve this mystery.
1053 Dariusz Fedejko13 July 2017 at 21:13
1054
1055 Strange thing happened - after I installed everything for the fourth time and it still didn't work, I decided to create a snapshot and then I restored the server from it. Now it works nice, with two instances of dnscrypt_proxy. Instead of VPN, I'm using an SSH tunnel - only for viewing full content on Netflix. And here's my other challenge - would it be possible to set a rule in pf (on my home OpenBSD router) to redirect all traffic from one IP address (chromecast device) to the tunnel which connects to my VPS?
1056 Guillaume Kaddouch13 July 2017 at 21:15
1057
1058 Sometimes DNS crypt servers are "up" (answer to pings) but do not reply any more to DNS queries. That's why I advvise to setup at least two dnsproxy daemon, I even have 3 on my setup. Add more as shown in the article, and try again.
1059
1060 Also, what is your pf ruleset ? If unbound is listening on 127.0.0.1:53 and is forwarding DNS requests to dnscrypt on 127.0.0.1:4040, then dnscrypt should be able to go out on UDP 443, if the firewall allows it. Hence knowing your pf ruleset would be useful.
1061
1062 Alternatively, for debugging purpose, you can try with a basic pf ruleset :
1063 pass all
1064 match out on egress nat-to egress
1065
1066 Guillaume
1067 Reply
1068 Anonymous19 July 2017 at 19:23
1069
1070 "OpenVPN is a SSL VPN and does not use a separate VPN protocol."
1071
1072 This is quite difficult to understand, perhaps you could rephrase it. Thanks for the article.
1073 Reply
1074 Replies
1075 Guillaume Kaddouch20 July 2017 at 21:01
1076
1077 As OpenVPN is explained after IPSEC which uses more than one protocol (IKE/AH/ESP), this means that OpenVPN only uses TLS which is the same protocol you use when accessing HTTPS websites.
1078
1079 Guillaume
1080 Reply
1081 Anonymous9 August 2017 at 15:44
1082
1083 Awesome, thank U
1084 Reply
1085 Thomas17 August 2017 at 18:07
1086
1087 Hi, and thanks for writing this post. It really inspired me to do some long overdue changes and updates for my OpenBSD box.
1088
1089 I did have an issue with pf_show_tables.py and one correction:
1090
1091 When I run the script, I get the following error:
1092
1093 Traceback (most recent call last):
1094 File "./pf_show_tables_orig.py", line 49, in
1095 port = ip.split('.')[4]
1096 IndexError: list index out of range
1097
1098 My python-foo is rather rusty, but it maybe has something do with the ip not being formatted as expected? x.x.x.x:port and not x.x.x.x.port? I'm probably way off but anyway, some hints or a fix would be much appreciated!
1099
1100 On line 85, you probably want a comment before 127.0.0.1 etc., because otherwise it results in a syntax error.
1101
1102 Thanks again!
1103 Reply
1104 Joe Kings23 August 2017 at 00:35
1105
1106 Thanks a lot for this, how can I remove dnscrypt-proxy servers that have gone bad?
1107 Reply
1108 Anonymous29 December 2017 at 17:00
1109
1110 Hi i just went through your tutorial and had some issues. In the end i could finish the installation and can connect via my iphone. Could we somehow get in contact so i can give you some update information for the tutorial based on OpenBSD 6.2. I really like your tutorial very well written. Best, Alex
1111 Reply
1112 Anonymous17 February 2018 at 12:35
1113
1114 Awesome article, thanks for it !
1115
1116 Instead of use unbound with forward-addr to 127.0.0.1@, I create 2 loopback interface and set forward-addr to 127.0.0.[23].
1117 I got some troubles with unbound because of DNSSEC so I want to add the dnscrypt-proxy in the resolv.conf file.
1118
1119 It's work ! :)
1120
1121 So, what do you thin about it ?
1122
1123 After I disable auto-trust-anchor-file: in unbound conf than unbound work great again.
1124
1125 Thank you again
1126
1127 What do you think about it ?
1128 Reply
1129 Anonymous26 February 2018 at 04:05
1130
1131 This tutorial is exactly what I was looking for. It is incredibly detailed, but I can't quite get it to function correctly. Almost there I hope. Regarding pf_show_tables.py, I had to follow Thomas advice regarding line 85 and changed line 96 to "if count == max: sys.exit()". Don't listen to me though. Mine still doesn't work and I think I put this comment here for me, not for you.
1132 Thank You!
1133 Reply
1134 Anonymous22 May 2018 at 03:07
1135
1136 Hello,
1137 Fantastic article and very detailed. Very much appreciate the input and your expertise.
1138 I am having quite bit trouble with the Firewall and wanted to learn more about how PF works. Could the "pf.conf" be written in such a way that it is rules based only? I am getting confused by the "TAG" statements. Especially how the NAT is working with "tun0" to allow vpn clients to connect to the internet. Many thanks
1139 Reply
1140 Replies
1141 Guillaume Kaddouch22 May 2018 at 19:19
1142
1143 Hi,
1144
1145 Thanks for your comments :) Yes it is possible to write a pf.conf without relying on tags. Tags are easy to understand 1) you tag traffic based on some criteria (this does not allow or drop any traffic) 2) you filter your traffic based on tags. For instance, instead or filtering from X IP with X protocol and X port toward X destination but not Y destination, you filter on the "LAN_OUT" tag. That is purely a personal preference, and is by no mean the only way to filter. You can filter/NAT directly what is going out of the tun0 interface, if that is clearer to you.
1146
1147 Guillaume
1148 Reply
1149 Scott Wells23 May 2018 at 15:32
1150
1151 Hi Guillaume...do you know if this is still applicable for OpenBSD 6.3?
1152
1153 Thanks
1154 Reply
1155 Replies
1156 Guillaume Kaddouch23 May 2018 at 18:46
1157
1158 Hi Scott,
1159
1160 I have not tried with OpenBSD 6.3, but while minor adaptations are to be expected, I suppose it is still applicable.
1161
1162 Guillaume
1163 Reply
1164 ssh24 May 2018 at 15:58
1165
1166 Hi,
1167
1168 I think, there are small fixes for your article:
1169
1170 $ doas mkdir -p /var/openvpn/{tmp,_public}
1171
1172 Also I think that you have to set owner as _openvpn for /var/openvpn/public/crl.pem.
1173
1174 Your howto works for 6.3 perfectly. Thank you for your work!
1175 Reply
1176 Replies
1177 Guillaume Kaddouch24 May 2018 at 20:13
1178
1179 Thanks for your comments ! I'll look into the suggestions when I'll check again that setup, but I'm on something else now( will be a future article in June or July ;-)
1180
1181 Thanks for the information that it is still ok for 6.3.
1182 Reply