Difference between revisions of "MikrotTik: OpenVPN"

From Luky-Wiki
Jump to: navigation, search
(Time / Date)
(Before you start)
 
(116 intermediate revisions by the same user not shown)
Line 1: Line 1:
'''Attention: this page is work in progress.'''
+
'''Attention:''' OpenVPN with certificates is broken in ROS 6.11. The option "Require Client Certificate" in version 6.12 works but you may need to re-import certificates if you are upgrading from 6.10 or earlier. Check [http://forum.mikrotik.com/viewtopic.php?uid=69664&f=2&t=83203&start=0 forum] for more details.
 +
 
 +
'''Note:''' I updated configuration of VPN to address CPU utilization and packet fragmentation problems. (2014-09-21)
 +
 
 +
=== Before you start ===
  
 
I am using OVPN client / server on MikroTik to connect several network/location. This document describe my findings and my way of configuration.
 
I am using OVPN client / server on MikroTik to connect several network/location. This document describe my findings and my way of configuration.
  
While i was designing my network i found following limitations (features ?) of OVPN implementation:
+
While I was designing my network I found following limitations (features ?) of OVPN implementation:
  
* It is not possible to create connection without CA imported in each MikroTik. If "require-client-certificate" is set then also valid certificate chain for both client and server is required.
+
* "require-client-certificate" is working only since ROS 6.12. It is possible to select CA+user+pass authorization method or simple user+pass.
* It is not possible to create "password less" connection. Each connection is authorized by certificate, username and password
+
* It is not possible to create "password less" connection. Each connection is authorized by certificate (optional), username and password.
* Only TCP connection as base channel for OVPN is supported by MikroTik (v 6.4)
+
* Only TCP connection as base channel for OVPN is supported by MikroTik (ROS 6.18).
* Prior to version 6.4 it was not possible to specify server by FQDN. Only IP address was supported in previous versions of firmware
+
* Server can be specified by FQDN only in ROS 6.4 and later.
* It is required that client address is managed and assigned by OVPN Server. (e.g. it is not possible to set IP on server and client outside of OVPN configuration)
+
* It is required that client address is managed and assigned by OVPN Server (e.g. it is not possible to set IP on server and client outside of OVPN configuration).
  
Steps to successfully create OVPN connection:
 
 
== Generate valid certificates ==
 
== Generate valid certificates ==
 
I used "valid" in name of this section because I get wrongly generated certificates using "pkitool". I am not sure if this was fail of this tool or my fail but right now i am using different way to generate certificate. Maybe in future I'll debug what was wrong with pkitool.
 
I used "valid" in name of this section because I get wrongly generated certificates using "pkitool". I am not sure if this was fail of this tool or my fail but right now i am using different way to generate certificate. Maybe in future I'll debug what was wrong with pkitool.
Line 38: Line 41:
  
 
=== Variables configuration (e.g. "vars") ===
 
=== Variables configuration (e.g. "vars") ===
Before certificates can be generated it is necessary to customize "vars" file inside new ca directory ("ovpn" in this example)
+
Before certificates can be generated it is necessary to customize "vars" file inside new ca directory ("ovpn" in this example).
  
I prefer strict security so i changed key size from 1024 to 2048
+
I prefer strict security so i changed key size from 1024 to 4096.
  
 
<pre>
 
<pre>
export KEY_SIZE=2048
+
export KEY_SIZE=4096
 
</pre>
 
</pre>
  
Line 57: Line 60:
 
export KEY_NAME="changeme"
 
export KEY_NAME="changeme"
 
export KEY_EMAIL="myself@mydomain.tld"
 
export KEY_EMAIL="myself@mydomain.tld"
 +
export KEY_ALTNAMES="something"
 
</pre>
 
</pre>
  
 
CN and NAME will be different for each certificate so i left it as "changeme". Easy-rsa will ask to confirm each KEY_* variable during certificate generation, so it is possible to change both values for each certificate.
 
CN and NAME will be different for each certificate so i left it as "changeme". Easy-rsa will ask to confirm each KEY_* variable during certificate generation, so it is possible to change both values for each certificate.
 +
 +
=== Certificate Revocation List (optional) ===
 +
CRL configuration is directly in <code>openssl.cnf</code>. CA directory contain several configuration files so it is necessary to check which one is use:
 +
<pre>
 +
$ . ./vars
 +
NOTE: If you run ./clean-all, I will be doing a rm -rf on /home/lukas/Desktop/ovpn/keys
 +
$ ./whichopensslcnf .
 +
./openssl-1.0.0.cnf
 +
</pre>
 +
 +
File <code>openssl-1.0.0.cnf</code> contain several section. It is necessary to add CRL in:
 +
* <code>[ usr_cert ]</code> for client certificate
 +
* <code>[ server ]</code> for server certificate
 +
* <code>[ v3_ca ]</code> for CA certificate
 +
CRL configuration line is in following format (single host or multiple hosts):
 +
<pre>
 +
crlDistributionPoints = URI:http://www.example.com/crl.pem
 +
</pre>
 +
or
 +
<pre>
 +
crlDistributionPoints = URI:http://www-1.example.com/crl.pem,URI:http://www-2.example.com/crl.pem
 +
</pre>
 +
 +
''Note:'' ROS 6.11 support only HTTP.
 +
 +
CRL is single point of failure for OVPN so make sure you use domain hosted on HA solution. CRL is renewed once per hour (ROS 6.11) and if it fail then trusted certificate is no longer usable.
  
 
=== CA directory cleanup ===
 
=== CA directory cleanup ===
Even when you are creating certificates for first time it is good practice to call cleanup command. Later if you recreate certificates from scratch this will ensure that you are working in clean CA directory. Inside CA directory execute:
+
Easy-rsa require "clean" even if CA directory is new and currently empty:
 
 
 
<pre>
 
<pre>
 
$ . ./vars  
 
$ . ./vars  
Line 72: Line 101:
  
 
=== Generate CA certificate ===
 
=== Generate CA certificate ===
'''Note:''' configuration is loaded as variables in shell environment. If you experiment with configuration then don't forget to overwrite/remove variables from shell. If you are unsure then simply close terminal session and start with clean one. After that start with "<code>. ./vars</code>" command.
+
'''Note:''' configuration is loaded as variables in shell environment. If you experiment with configuration then don't forget to overwrite/remove variables from shell. If you are unsure then simply close terminal session and start with clean one. After that start with "<code>. ./vars</code>" and <code>./clean-all</code> command.
  
 
<pre>
 
<pre>
Line 244: Line 273:
  
 
For me my VPN network is important so I'll generate all keys in memory (in /dev/shm or other FS mounted as tmpfs). During this operation I'll disable swap and after I generate all required key's I'll destroy ca.key. Keys are small but there is still possibility that they will be swapped out by kernel. Trace of them can remain on system for some time if they was stored in swap even if keys are removed on OS level.
 
For me my VPN network is important so I'll generate all keys in memory (in /dev/shm or other FS mounted as tmpfs). During this operation I'll disable swap and after I generate all required key's I'll destroy ca.key. Keys are small but there is still possibility that they will be swapped out by kernel. Trace of them can remain on system for some time if they was stored in swap even if keys are removed on OS level.
 +
 +
=== CRL verification (optional) ===
 +
It is good practice to review certificate after issuing it and this review also show if CRL is in place. Following command show details about certificate in "human" readable form:
 +
<pre>
 +
openssl x509 -in keys/ca.crt -noout -text
 +
</pre>
 +
 +
There should be following part:
 +
<pre>
 +
X509v3 extensions:
 +
    X509v3 CRL Distribution Points:
 +
 +
        Full Name:
 +
          URI:http://www-1.example.com/crl.pem
 +
 +
        Full Name:
 +
          URI:http://www-2.example.com/crl.pem
 +
</pre>
 +
''Note:'' repeat this step for all certificates (server + client)
 +
 +
=== CRL file (optional) ===
 +
At this point only one step is missing for certificates. File <code>crl.pem</code> is not generated automatically by commands used in previous steps.
 +
 +
There are two ways how to get <code>crl.pem</code>:
 +
* use openssl directly and generate empty one
 +
* use easy-rsa and revoke certificate
 +
 +
I'll describe second approach. This ensure that certification revoke procedure is working fine.
 +
 +
First of all we need "dummy" certificate:
 +
<pre>
 +
./build-key dummy
 +
</pre>
 +
 +
This "dummy" certificate is clearly marked as "<code>V</code>" in <code>keys/index.txt</code>. This mean it is valid so lets revoke it:
 +
<pre>
 +
$ ./revoke-full dummy
 +
Using configuration from /home/lukas/Desktop/ovpn/openssl-1.0.0.cnf
 +
Revoking Certificate 07.
 +
Data Base Updated
 +
Using configuration from /home/lukas/Desktop/ovpn/openssl-1.0.0.cnf
 +
dummy.crt: <details stripped>
 +
error 23 at 0 depth lookup:certificate revoked
 +
</pre>
 +
 +
Error on last line is certificate validation. Failure is expected as certificate get revoked. Certificate is now marked as "<code>R</code>" (revoked in) <code>keys/index.txt</code>. As a part of certificate revocation procedure <code>crl.pem</code> is updated or generated. This file can be now uploaded to web server acting as CRL distribution point.
 +
 +
'''Note:''' make sure you upload modified <code>crl.pem</code> after each certificate revoke action to CRL distribution point (web server).
  
 
== OVPN Common configuration ==
 
== OVPN Common configuration ==
Line 253: Line 330:
 
<pre>
 
<pre>
 
/system ntp client
 
/system ntp client
set enabled=yes mode=unicast primary-ntp=<IP> secondary-ntp=<IP>
+
set enabled=yes primary-ntp=<IP> secondary-ntp=<IP>
 
</pre>
 
</pre>
  
 
=== Firewall rules ===
 
=== Firewall rules ===
Network interfaces for VPN connection are not present on system all the time due to way how RouterOS handle VPN connection. In case of problem VPN interface disappear, IP address is deconfigured and VPN traffic can hit default gateway. Even if the connection can't be established with such a condition it is security risk. I don't accept that my internal traffic can go outside of my network. To prevent VPN traffic going unencrypted to default gateway I added following rule to each device acting as gateway for some network (i am using 10.0.0.0/8 network for internal IP allocation and in this particular example "eth10-wan" is my "out" interface to internet. You should modify it to reflect your network configuration)
+
Network interfaces for VPN connection are not present on system all the time due to way how RouterOS handle VPN connection. In case of problem VPN interface disappear, IP address is deconfigured and VPN traffic can hit default gateway. Even if the connection can't be established with such a condition it is security risk. I don't accept that my internal traffic can go outside of my network. To prevent VPN traffic going unencrypted to default gateway I added following rule to each device acting as gateway for network (i am using 10.0.0.0/8 network for internal IP allocation and in this particular example "eth10-wan" is my "out" interface to internet. You should modify it to reflect your network configuration)
  
 
<pre>
 
<pre>
Line 281: Line 358:
  
 
== OVPN Server configuration ==
 
== OVPN Server configuration ==
=== Import certificates ===
+
=== Import certificates (server) ===
To MikroTik with role server upload ca.crt, server.domain.tld.crt and server.domain.tld.key. Import them using following commands:
+
As mentioned earlier certificates are required. MikroTik device acting as OVPN server require following files: <code>ca.crt</code>, <code>server.domain.tld.crt</code> and <code>server.domain.tld.key</code>. Once uploaded they can be imported using following command:
  
 
<pre>
 
<pre>
[admin@mikrotikA] > /certificate  
+
> /certificate  
[admin@mikrotikA] /certificate> import passphrase="" file-name=ca.crt  
+
/certificate> import passphrase="" file-name=ca.crt  
 
     certificates-imported: 1
 
     certificates-imported: 1
 
     private-keys-imported: 0
 
     private-keys-imported: 0
Line 293: Line 370:
 
   keys-with-no-certificate: 0
 
   keys-with-no-certificate: 0
  
[admin@mikrotikA] /certificate> import passphrase="" file-name=server.domain.tld.crt  
+
/certificate> import passphrase="" file-name=server.domain.tld.crt  
 
     certificates-imported: 1
 
     certificates-imported: 1
 
     private-keys-imported: 0
 
     private-keys-imported: 0
Line 300: Line 377:
 
   keys-with-no-certificate: 0
 
   keys-with-no-certificate: 0
  
[admin@mikrotikA] /certificate> import passphrase="" file-name=server.domain.tld.key  
+
/certificate> import passphrase="" file-name=server.domain.tld.key  
 
     certificates-imported: 0
 
     certificates-imported: 0
 
     private-keys-imported: 1
 
     private-keys-imported: 1
Line 307: Line 384:
 
   keys-with-no-certificate: 0
 
   keys-with-no-certificate: 0
  
[admin@mikrotikA] /certificate>  
+
/certificate>  
 
</pre>
 
</pre>
 +
'''Note:''' If you don't select pass phrase then MikroTik ask for it, even if key is not encrypted. In that case just hit return.
  
Normally certificates get name "certX" where X represent sequential number. For better handling i recommend to rename them. Details chan be checked by <code>/certificate print</code> and then certificates can be renamed using:
+
Normally certificates get name "certX" where X represent sequential number. For better handling i recommend to rename them. Details can be checked by <code>/certificate print</code> and then certificates can be renamed using:
 +
 
 +
<pre>
 +
/certificate
 +
set cert1 name=CA
 +
set cert2 name=server.domain.tld
 +
</pre>
  
 +
'''Note:''' CRL is indicated in <code>/certificate print</code> by <code>L</code> in front of certificate.
 +
=== Cleanup ===
 +
As of ROS 6.6 certificates are stored in trust store. It is not necessary to keep them also as files in ROS file system. I recommend to remove them from file system once they are imported to trust store. It is easy to remove "files" from router:
 
<pre>
 
<pre>
[admin@mikrotikA] /certificate> set cert1 name=CA
+
/file remove ca.crt
[admin@mikrotikA] /certificate> set cert2 name=server.domain.tld  
+
/file remove server.domain.tld.crt
 +
/file remove server.domain.tld.key
 
</pre>
 
</pre>
 +
After that verify file listing by issuing <code>/file print</code>
  
 
=== Allow VPN connection to pass through firewall ===
 
=== Allow VPN connection to pass through firewall ===
Line 326: Line 415:
  
 
=== Create VPN profile ===
 
=== Create VPN profile ===
MikroTik use little bit specific implementation of profiles. This can be confusing for beginners but it is not so hard to understand it.  
+
MikroTik use little bit specific implementation of profiles. This can be confusing for beginners but it is not so hard to understand it. Basically there are 3 sources of configuration for connection:
 +
* interface default profile
 +
* connection specific profile
 +
* user specific entry
  
There are 3 sources of configuration for connection:
+
Some configuration attributes are present on all levels, for example remote IP address (pool). You can specify it per user, per profile or per interface. Some configuration attributes are present only on specific level, for example encryption type. It don't make sense to configure encryption per user as security should be consistent (and also it is not possible technically).
- interface default profile
 
- connection specific profile
 
- user specific entry
 
  
Some configuration attributes are present on all levels, for example remote IP address. You can specify it per user, per profile or per interface. Some configuration attributes are present only on specific level, for example encryption type. It don't make sense to configure it per user as security should be consistent (and also it is not possible technically).
+
So first of all we need VPN profile, which will set defaults for incoming connections.
 +
<pre>
 +
/ppp profile
 +
add change-tcp-mss=yes dns-server=<dns ip> local-address=<vpn ip> name=OVPN-server \
 +
    only-one=yes use-compression=no use-encryption=required use-mpls=no \
 +
    use-vj-compression=no
 +
</pre>
  
So first of all we need VPN profile, which will set default for incoming connections.
+
'''Note1:''' Van Jacobson compression is not recommended, so enable it only if you really need it.  
<pre>
 
/ppp profile
 
  
add dns-server=192.168.1.1 local-address=192.168.1.1 only-one=yes \
+
'''Note2:''' Enable compression only if you are transferring compressible data. (HTTPs, SSH, video files, JPG images, ... can't be compressed so it waste CPU cycles on router board.)
    use-compression=yes use-encryption=required use-mpls=no \
 
    use-vj-compression=no name=OVPN-server
 
</pre>
 
  
'''Note:''' Van Jacobson compression is not recommended, so enable it only if you really need it. I would like to test compression of data, so i keep it enabled for this time. As i would like to enforce encryption i set it to "required".
+
'''Note3:''' <code>local-address</code> is address of VPN interface, not address of physical interface where OVPN server is listening
  
 
=== Add VPN user (client) ===
 
=== Add VPN user (client) ===
 +
Password is required for successful connection so it is necessary to create user entry in security table. If static allocation is in place (as in this example) then <code>/ppp secret</code> is also place for user->IP definition.
  
 +
<pre>
 +
/ppp secret
 +
add name=<client> password=<password> profile=OVPN-server \
 +
    remote-address=<VPN client IP> service=ovpn
 +
</pre>
 +
 +
'''Note:''' <code>remote-address</code> is IP address provided for client to be used on VPN interface, not IP address of client itself.
 +
 +
=== Fix name of user in configuration (interface name) ===
 +
VPN interface is also managed dynamically on OVPN server. This mean that connected user get auto-generated interface name. If per user (connection) filtering is required then it is necessary to "fix" name of interface on server. In this example every time "client" connect to OVPN Server interface with name "vpn-client" is associated to it's connection:
  
 
<pre>
 
<pre>
/ppp secret add password=pass1234 profile=OVPN-server remote-address=192.168.1.2 service=ovpn name=client
+
/interface ovpn-server
 +
add name=vpn-client user=client  
 
</pre>
 
</pre>
  
=== Fix name of user in configuration (interface name) ===
+
I am using following naming scheme:
 +
 
 
<pre>
 
<pre>
/interface ovpn-server> add user=client name=vpn-client
+
/interface ovpn-server
 +
add name=vpn-from-lukas user=lukas
 
</pre>
 
</pre>
  
 
=== Setup VPN server and enable it ===
 
=== Setup VPN server and enable it ===
 +
MikroTik support 4 types of encryption (ROS 6.18). You can select one or more from following list: aes128, aes192, aes256, blowfish128. If server allow more that one cipher then it is up to client to select one. Initially I configured aes256 only, but I faced performance problem with it. While I was doing research I found that none of them are "un-secure" and preference is more marketing than real reasons. Therefore i returned configuration back to default (blowfish128,aes128) which gained me additional ~6Mbit/s on line. It is not so much but on long running job it make significant difference.
 +
 
<pre>
 
<pre>
/interface ovpn-server server> set auth=sha1 certificate=server.domain.tld cipher=aes256 default-profile=OVPN-server enabled=yes keepalive-timeout=30 mode=ip netmask=24 require-client-certificate=yes
+
/interface ovpn-server server
 +
set auth=sha1 certificate=server.domain.tld cipher=blowfish128,aes128 \
 +
    default-profile=OVPN-server enabled=yes keepalive-timeout=60 \
 +
    max-mtu=1400 require-client-certificate=yes
 
</pre>
 
</pre>
 +
 +
'''Note1:''' Encryption is enforced via profile added earlier (OVPN-server).
 +
 +
'''Note2:''' default option for VPN type is "ip" in most situation it is better to use L3 VPN connection rather that L2 called "ethernet". This article describe "ip" connection using VPN.
  
 
=== Grant access for VPN clients through firewall ===
 
=== Grant access for VPN clients through firewall ===
 +
Depending on firewall configuration it is necessary to add rules also for connected VPN clients. This may vary depending on configuration so i keep this step only as note here.
  
 
== OVPN Client configuration ==
 
== OVPN Client configuration ==
=== Import certificates ===
+
=== Import certificates (client) ===
 +
Client need following files: <code>ca.crt</code>, <code>client.crt</code> and <code>client.key</code>. Keys can be imported in same way as on server:
  
ca.crt   
+
<pre>
client.crt
+
> /certificate
pub     
+
/certificate> import passphrase="" file-name=ca.crt  
client.key
 
 
 
[admin@mikrotikB] /certificate> import passphrase="" file-name=ca.crt  
 
 
     certificates-imported: 1
 
     certificates-imported: 1
 
     private-keys-imported: 0
 
     private-keys-imported: 0
Line 380: Line 492:
 
   keys-with-no-certificate: 0
 
   keys-with-no-certificate: 0
  
[admin@mikrotikB] /certificate> import passphrase="" file-name=client.crt  
+
/certificate> import passphrase="" file-name=client.crt  
 
     certificates-imported: 1
 
     certificates-imported: 1
 
     private-keys-imported: 0
 
     private-keys-imported: 0
Line 387: Line 499:
 
   keys-with-no-certificate: 0
 
   keys-with-no-certificate: 0
  
[admin@mikrotikB] /certificate> import passphrase="" file-name=client.key  
+
/certificate> import passphrase="" file-name=client.key  
 
     certificates-imported: 0
 
     certificates-imported: 0
 
     private-keys-imported: 1
 
     private-keys-imported: 1
Line 394: Line 506:
 
   keys-with-no-certificate: 0
 
   keys-with-no-certificate: 0
  
[admin@mikrotikB] /certificate>
+
/certificate>
 +
</pre>
  
[admin@mikrotikB] /certificate> set cert1 name=CA
+
Renaming of certificates is also same as on server:
[admin@mikrotikB] /certificate> set cert2 name=client
+
 
 +
<pre>
 +
/certificate
 +
set cert1 name=CA
 +
set cert2 name=client
 +
</pre>
  
 
=== Configure client ===
 
=== Configure client ===
/interface ovpn-client> add auth=sha1 cipher=aes256 certificate=client disabled=no mode=ip name=vpn-to-server password=pass1234 profile=default connect-to=192.168.56.101 user=client
+
Configuration on client is inhered from server (options which can be signaled from server). Profile is optional, but to be 100% sure that configuration is same i created profile also on client:
 +
<pre>
 +
/ppp profile
 +
add change-tcp-mss=yes name=OVPN-client use-compression=no \
 +
    use-encryption=required use-mpls=no use-vj-compression=no
 +
</pre>
  
TODO: profile !!!
+
VPN connection is finalized by adding OVPN client details:
 +
 
 +
<pre>
 +
/interface ovpn-client
 +
add add-default-route=no auth=sha1 certificate=<client> cipher=blowfish128 \
 +
    connect-to=<OVPN server wan IP> disabled=no name=vpn-to-server \
 +
    password=<password> profile=OVPN-client user=<client>
 +
</pre>
 +
 
 +
'''Note1:''' by default interface is disabled, so add <code>disabled=no</code> or enable interface after adding it.
 +
 
 +
'''Note2:''' select chiper according to your preference.
  
 
=== Verify connection ===
 
=== Verify connection ===
[admin@mikrotikB] /interface ovpn-client> monitor 0
+
At this point VPN connection should be established and working simple check can be performed by <code>monitor</code> command:
 +
 
 +
<pre>
 +
/interface ovpn-client
 +
monitor 0
 +
</pre>
 +
 
 +
Output of it should show connected and correct "encoding" (encryption type). For example like this:
 +
 
 +
<pre>
 
     status: connected
 
     status: connected
 
     uptime: 1m24s
 
     uptime: 1m24s
 
   encoding: AES-256-CBC/SHA1
 
   encoding: AES-256-CBC/SHA1
 
       mtu: 1500
 
       mtu: 1500
 +
</pre>
 +
 +
Details can be checked also in server and client logs. It should contain something like this:
 +
 +
* Server:
 +
<pre>
 +
ovpn,info TCP connection established from 192.168.56.102
 +
ovpn,info <ovpn-0>: dialing...
 +
ovpn,info <ovpn-0>: using encoding - AES-256-CBC/SHA1
 +
ovpn,info vpn-client: connected
 +
ovpn,info,account client logged in, 192.168.1.2
 +
</pre>
 +
 +
* Client:
 +
<pre>
 +
ovpn,info vpn-to-server: initializing...
 +
ovpn,info vpn-to-server: dialing...
 +
ovpn,info vpn-to-server: using encoding - AES-256-CBC/SHA1
 +
ovpn,info vpn-to-server: connected
 +
</pre>
 +
 +
There should be IP address dynamically assigned by server on client:
  
19:48:35 ovpn,info vpn-to-server: initializing...  
+
<pre>
19:48:35 ovpn,info vpn-to-server: dialing...  
+
/ip address print
19:48:35 system,info device added by admin
+
 
19:48:35 ovpn,info vpn-to-server: using encoding - AES-256-CBC/SHA1
+
Flags: X - disabled, I - invalid, D - dynamic
19:48:35 ovpn,info vpn-to-server: connected
+
#  ADDRESS            NETWORK        INTERFACE                                                     
 +
0  192.168.56.102/24  192.168.56.0    ether1                                                       
 +
1 D 192.168.1.2/32    192.168.1.1    vpn-to-server                                                
 +
 
 +
</pre>
  
19:48:36 ovpn,info TCP connection established from 192.168.56.102  
+
In this example <code>192.168.56.102</code> was set manually as interface address and <code>192.168.1.2</code> dynamically by VPN server on VPN interface (client side).
19:48:36 ovpn,info <ovpn-0>: dialing...
 
19:48:36 ovpn,info <ovpn-0>: using encoding - AES-256-CBC/SHA1
 
19:48:36 ovpn,info vpn-client: connected
 
19:48:36 ovpn,info,account client logged in, 192.168.1.2
 

Latest revision as of 18:18, 13 February 2016

Attention: OpenVPN with certificates is broken in ROS 6.11. The option "Require Client Certificate" in version 6.12 works but you may need to re-import certificates if you are upgrading from 6.10 or earlier. Check forum for more details.

Note: I updated configuration of VPN to address CPU utilization and packet fragmentation problems. (2014-09-21)

Before you start

I am using OVPN client / server on MikroTik to connect several network/location. This document describe my findings and my way of configuration.

While I was designing my network I found following limitations (features ?) of OVPN implementation:

  • "require-client-certificate" is working only since ROS 6.12. It is possible to select CA+user+pass authorization method or simple user+pass.
  • It is not possible to create "password less" connection. Each connection is authorized by certificate (optional), username and password.
  • Only TCP connection as base channel for OVPN is supported by MikroTik (ROS 6.18).
  • Server can be specified by FQDN only in ROS 6.4 and later.
  • It is required that client address is managed and assigned by OVPN Server (e.g. it is not possible to set IP on server and client outside of OVPN configuration).

Generate valid certificates

I used "valid" in name of this section because I get wrongly generated certificates using "pkitool". I am not sure if this was fail of this tool or my fail but right now i am using different way to generate certificate. Maybe in future I'll debug what was wrong with pkitool.

easy-rsa installation

Gentoo users:

emerge app-crypt/easy-rsa

Ubuntu users:

apt-get install easy-rsa

Other users: please try to find easy-rsa using your distributions package manager or download it from GitHub

Prepare CA directory

Ubuntu have additional command to create CA directory. Select empty directory and then use (I selected "ovpn"):

make-cadir ovpn

Gentoo users can use following rsync command:

rsync -av /usr/share/easy-rsa/ ovpn/

On other Linux distribution you should find easy-rsa installation and copy it to desired working directory.

Variables configuration (e.g. "vars")

Before certificates can be generated it is necessary to customize "vars" file inside new ca directory ("ovpn" in this example).

I prefer strict security so i changed key size from 1024 to 4096.

export KEY_SIZE=4096

There are details about certificates at end of "vars" file. It is required that you modify it to reflect your settings. For example like this:

export KEY_COUNTRY="SK"
export KEY_PROVINCE="SK"
export KEY_CITY="Bratislava"
export KEY_ORG="My company name"
export KEY_OU="MikroTik OVPN"
export KEY_CN="changeme"
export KEY_NAME="changeme"
export KEY_EMAIL="myself@mydomain.tld"
export KEY_ALTNAMES="something"

CN and NAME will be different for each certificate so i left it as "changeme". Easy-rsa will ask to confirm each KEY_* variable during certificate generation, so it is possible to change both values for each certificate.

Certificate Revocation List (optional)

CRL configuration is directly in openssl.cnf. CA directory contain several configuration files so it is necessary to check which one is use:

$ . ./vars 
NOTE: If you run ./clean-all, I will be doing a rm -rf on /home/lukas/Desktop/ovpn/keys
$ ./whichopensslcnf .
./openssl-1.0.0.cnf

File openssl-1.0.0.cnf contain several section. It is necessary to add CRL in:

  • [ usr_cert ] for client certificate
  • [ server ] for server certificate
  • [ v3_ca ] for CA certificate

CRL configuration line is in following format (single host or multiple hosts):

crlDistributionPoints = URI:http://www.example.com/crl.pem

or

crlDistributionPoints = URI:http://www-1.example.com/crl.pem,URI:http://www-2.example.com/crl.pem

Note: ROS 6.11 support only HTTP.

CRL is single point of failure for OVPN so make sure you use domain hosted on HA solution. CRL is renewed once per hour (ROS 6.11) and if it fail then trusted certificate is no longer usable.

CA directory cleanup

Easy-rsa require "clean" even if CA directory is new and currently empty:

$ . ./vars 
NOTE: If you run ./clean-all, I will be doing a rm -rf on /.../.../ovpn
$ ./clean-all 
$

Generate CA certificate

Note: configuration is loaded as variables in shell environment. If you experiment with configuration then don't forget to overwrite/remove variables from shell. If you are unsure then simply close terminal session and start with clean one. After that start with ". ./vars" and ./clean-all command.

$ ./build-ca 
Generating a 2048 bit RSA private key
...........+++
.............+++
writing new private key to 'ca.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [SK]:
State or Province Name (full name) [SK]:
Locality Name (eg, city) [Bratislava]:
Organization Name (eg, company) [My company Name]:
Organizational Unit Name (eg, section) [MikroTik OVPN]:
Common Name (eg, your name or your server's hostname) [changeme]:server.domain.tld
Name [changeme]:server.domain.tld
Email Address [myself@mydomain.tld]:
$

I changed server name simply by typing it while tool was asking for it. Other variables are confirmed by hitting return.

Generate Diffie–Hellman

Note 1: This step is necessary only if you are running OpenVPN server also on Linux. If you are running only MikroTik OVPN and OpenVPN clients then you can skip this step.

Note 2: Without proper HW random generator this can take long time. Grab cup of coffee or try to generate some entropy on your machine while DH is genererated.

(output reduced)

$ ./build-dh 
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
...............................
$ 

Generate server certificate

It is important that all certificates under one CA chain have unique Name/Common Name. I need client and server certificate for one machine so I will generate certificates using FQDN for servers and using hostname for clients. This will also help to simply identify type of certificate without tools.

$ ./build-key-server server.domain.tld
Generating a 2048 bit RSA private key
..................................................................................+++
..........+++
writing new private key to 'server.domain.tld.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [SK]:
State or Province Name (full name) [SK]:
Locality Name (eg, city) [Bratislava]:
Organization Name (eg, company) [My company Name]:
Organizational Unit Name (eg, section) [MikroTik OVPN]:
Common Name (eg, your name or your server's hostname) [server.domain.tld]:
Name [changeme]:server.domain.tld
Email Address [myself@mydomain.tld]:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /home/lukas/Desktop/a/ovpn/openssl-1.0.0.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'SK'
stateOrProvinceName   :PRINTABLE:'SK'
localityName          :PRINTABLE:'Bratislava'
organizationName      :PRINTABLE:'My company Name'
organizationalUnitName:PRINTABLE:'MikroTik OVPN'
commonName            :PRINTABLE:'server.domain.tld'
name                  :PRINTABLE:'server.domain.tld'
emailAddress          :IA5STRING:'myself@mydomain.tld'
Certificate is to be certified until Oct  4 18:10:34 2023 GMT (3650 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
$ 

Again Name/Common Name should be changed from "changeme" to something useful.

Note: Watch if database was successfully updated and certificate signed (last lines of output). It will prevent you from receiving strange errors later.

Generate client certificate

Client certificate is generated in same way as server:

$ ./build-key client
Generating a 2048 bit RSA private key
.............................................+++
.+++
writing new private key to 'client.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [SK]:
State or Province Name (full name) [SK]:
Locality Name (eg, city) [Bratislava]:
Organization Name (eg, company) [My company Name]:
Organizational Unit Name (eg, section) [MikroTik OVPN]:
Common Name (eg, your name or your server's hostname) [client]:
Name [changeme]:client
Email Address [myself@mydomain.tld]:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
Using configuration from /home/lukas/Desktop/a/ovpn/openssl-1.0.0.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'SK'
stateOrProvinceName   :PRINTABLE:'SK'
localityName          :PRINTABLE:'Bratislava'
organizationName      :PRINTABLE:'My company Name'
organizationalUnitName:PRINTABLE:'MikroTik OVPN'
commonName            :PRINTABLE:'client'
name                  :PRINTABLE:'client'
emailAddress          :IA5STRING:'myself@mydomain.tld'
Certificate is to be certified until Oct  4 18:55:35 2023 GMT (3650 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
$ 

Note: Watch if database was successfully updated and certificate signed (last lines of output). It will prevent you from receiving strange errors later.

Content of CA directory

ca.key - private CA Certificate key. Keep this file in safe

ca.crt - CA Certificate key.

server.domain.tld.crt
server.domain.tld.key - public and private key for OVPN server

client.crt
client.key - public and private key for OVPN client

dh2048.pem - Diffie–Hellman key for OpenVPN server on Linux

Note: "public" is only name of role. All files should be handled with care and keep in safe. To keep overall integrity of network it is good practice to follow these rules:

  • ca.key is key used to generate all other keys. Protect it in most possible paranoid level. If necessary then store and access it on separate computer with no access to network of any kind
  • protect ca.key with strong password and also all other keys if necessary
  • upload only required keys to each device (e.g. don't upload all of them to each device)

For me my VPN network is important so I'll generate all keys in memory (in /dev/shm or other FS mounted as tmpfs). During this operation I'll disable swap and after I generate all required key's I'll destroy ca.key. Keys are small but there is still possibility that they will be swapped out by kernel. Trace of them can remain on system for some time if they was stored in swap even if keys are removed on OS level.

CRL verification (optional)

It is good practice to review certificate after issuing it and this review also show if CRL is in place. Following command show details about certificate in "human" readable form:

openssl x509 -in keys/ca.crt -noout -text

There should be following part:

X509v3 extensions:
    X509v3 CRL Distribution Points: 

        Full Name:
          URI:http://www-1.example.com/crl.pem

        Full Name:
          URI:http://www-2.example.com/crl.pem

Note: repeat this step for all certificates (server + client)

CRL file (optional)

At this point only one step is missing for certificates. File crl.pem is not generated automatically by commands used in previous steps.

There are two ways how to get crl.pem:

  • use openssl directly and generate empty one
  • use easy-rsa and revoke certificate

I'll describe second approach. This ensure that certification revoke procedure is working fine.

First of all we need "dummy" certificate:

./build-key dummy

This "dummy" certificate is clearly marked as "V" in keys/index.txt. This mean it is valid so lets revoke it:

$ ./revoke-full dummy
Using configuration from /home/lukas/Desktop/ovpn/openssl-1.0.0.cnf
Revoking Certificate 07.
Data Base Updated
Using configuration from /home/lukas/Desktop/ovpn/openssl-1.0.0.cnf
dummy.crt: <details stripped>
error 23 at 0 depth lookup:certificate revoked

Error on last line is certificate validation. Failure is expected as certificate get revoked. Certificate is now marked as "R" (revoked in) keys/index.txt. As a part of certificate revocation procedure crl.pem is updated or generated. This file can be now uploaded to web server acting as CRL distribution point.

Note: make sure you upload modified crl.pem after each certificate revoke action to CRL distribution point (web server).

OVPN Common configuration

Time / Date

First of all don't try to synchronize time over VPN connection. Most of the MikroTik RouterBoard devices don't have RTC (real time clock) and depend on NTP. If you try to synchronize time over VPN then you end up with chicken / egg problem (e.g. for successful VPN connection correct time is necessary but it is not possible to synchronize time because VPN connection is not running). Please note that MikroTik is ignoring time source with LI_ALARM set. If your NTP source was restarted recently then it will take a moment to synchronize time.

Select time source reachable without VPN and set it using following command. I am using my own NTP server, so for me Secondary NTP is optional. If You don't have NTP server then select two reliable servers in different location (for locations in Slovakia I am configuring Primary as stratum 1 NTP server in Bratislava and Secondary of same stratum in Prague)

/system ntp client
set enabled=yes primary-ntp=<IP> secondary-ntp=<IP>

Firewall rules

Network interfaces for VPN connection are not present on system all the time due to way how RouterOS handle VPN connection. In case of problem VPN interface disappear, IP address is deconfigured and VPN traffic can hit default gateway. Even if the connection can't be established with such a condition it is security risk. I don't accept that my internal traffic can go outside of my network. To prevent VPN traffic going unencrypted to default gateway I added following rule to each device acting as gateway for network (i am using 10.0.0.0/8 network for internal IP allocation and in this particular example "eth10-wan" is my "out" interface to internet. You should modify it to reflect your network configuration)

/ip firewall filter
add action=reject chain=forward dst-address=10.0.0.0/8 out-interface=eth10-wan

Note: for security purpose this rule should be first in firewall configuration. e.g. it is evaluated at first for each packet.

On other hand it is good practice to have similar rule in firewall. In case of problem with connection well explainable ICMP message is returned:

 $ ping -c 3 10.10.10.10
PING 10.10.10.10 (10.10.10.10) 56(84) bytes of data.
From 10.1.1.1: icmp_seq=1 Destination Net Unreachable
From 10.1.1.1: icmp_seq=2 Destination Net Unreachable
From 10.1.1.1: icmp_seq=3 Destination Net Unreachable

--- 10.10.10.10 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 1999ms
$

OVPN Server configuration

Import certificates (server)

As mentioned earlier certificates are required. MikroTik device acting as OVPN server require following files: ca.crt, server.domain.tld.crt and server.domain.tld.key. Once uploaded they can be imported using following command:

> /certificate 
/certificate> import passphrase="" file-name=ca.crt 
     certificates-imported: 1
     private-keys-imported: 0
            files-imported: 1
       decryption-failures: 0
  keys-with-no-certificate: 0

/certificate> import passphrase="" file-name=server.domain.tld.crt 
     certificates-imported: 1
     private-keys-imported: 0
            files-imported: 1
       decryption-failures: 0
  keys-with-no-certificate: 0

/certificate> import passphrase="" file-name=server.domain.tld.key 
     certificates-imported: 0
     private-keys-imported: 1
            files-imported: 1
       decryption-failures: 0
  keys-with-no-certificate: 0

/certificate> 

Note: If you don't select pass phrase then MikroTik ask for it, even if key is not encrypted. In that case just hit return.

Normally certificates get name "certX" where X represent sequential number. For better handling i recommend to rename them. Details can be checked by /certificate print and then certificates can be renamed using:

/certificate
set cert1 name=CA
set cert2 name=server.domain.tld 

Note: CRL is indicated in /certificate print by L in front of certificate.

Cleanup

As of ROS 6.6 certificates are stored in trust store. It is not necessary to keep them also as files in ROS file system. I recommend to remove them from file system once they are imported to trust store. It is easy to remove "files" from router:

/file remove ca.crt
/file remove server.domain.tld.crt
/file remove server.domain.tld.key

After that verify file listing by issuing /file print

Allow VPN connection to pass through firewall

Each my firewall is configured in way that i start with "drop all" for everything and then add rules for allowed communication. I recommend this approach as unclasified (unknown) traffic is simply dropped. If some connection is required then it should be explicitly defined. VPN connection should be allowed by using this rule (or something similar):

/ip firewall filter
add chain=input comment=OpenVPN dst-port=1194 protocol=tcp

Note: this rule should be placed on reasonable position in firewall rules. Exact position depend on specific implementation. Use move after adding this rule or place it directly on correct position in firewall rule list.

Create VPN profile

MikroTik use little bit specific implementation of profiles. This can be confusing for beginners but it is not so hard to understand it. Basically there are 3 sources of configuration for connection:

  • interface default profile
  • connection specific profile
  • user specific entry

Some configuration attributes are present on all levels, for example remote IP address (pool). You can specify it per user, per profile or per interface. Some configuration attributes are present only on specific level, for example encryption type. It don't make sense to configure encryption per user as security should be consistent (and also it is not possible technically).

So first of all we need VPN profile, which will set defaults for incoming connections.

/ppp profile
add change-tcp-mss=yes dns-server=<dns ip> local-address=<vpn ip> name=OVPN-server \
    only-one=yes use-compression=no use-encryption=required use-mpls=no \
    use-vj-compression=no

Note1: Van Jacobson compression is not recommended, so enable it only if you really need it.

Note2: Enable compression only if you are transferring compressible data. (HTTPs, SSH, video files, JPG images, ... can't be compressed so it waste CPU cycles on router board.)

Note3: local-address is address of VPN interface, not address of physical interface where OVPN server is listening

Add VPN user (client)

Password is required for successful connection so it is necessary to create user entry in security table. If static allocation is in place (as in this example) then /ppp secret is also place for user->IP definition.

/ppp secret
add name=<client> password=<password> profile=OVPN-server \
    remote-address=<VPN client IP> service=ovpn 

Note: remote-address is IP address provided for client to be used on VPN interface, not IP address of client itself.

Fix name of user in configuration (interface name)

VPN interface is also managed dynamically on OVPN server. This mean that connected user get auto-generated interface name. If per user (connection) filtering is required then it is necessary to "fix" name of interface on server. In this example every time "client" connect to OVPN Server interface with name "vpn-client" is associated to it's connection:

/interface ovpn-server
add name=vpn-client user=client 

I am using following naming scheme:

/interface ovpn-server
add name=vpn-from-lukas user=lukas

Setup VPN server and enable it

MikroTik support 4 types of encryption (ROS 6.18). You can select one or more from following list: aes128, aes192, aes256, blowfish128. If server allow more that one cipher then it is up to client to select one. Initially I configured aes256 only, but I faced performance problem with it. While I was doing research I found that none of them are "un-secure" and preference is more marketing than real reasons. Therefore i returned configuration back to default (blowfish128,aes128) which gained me additional ~6Mbit/s on line. It is not so much but on long running job it make significant difference.

/interface ovpn-server server
set auth=sha1 certificate=server.domain.tld cipher=blowfish128,aes128 \
    default-profile=OVPN-server enabled=yes keepalive-timeout=60 \
    max-mtu=1400 require-client-certificate=yes

Note1: Encryption is enforced via profile added earlier (OVPN-server).

Note2: default option for VPN type is "ip" in most situation it is better to use L3 VPN connection rather that L2 called "ethernet". This article describe "ip" connection using VPN.

Grant access for VPN clients through firewall

Depending on firewall configuration it is necessary to add rules also for connected VPN clients. This may vary depending on configuration so i keep this step only as note here.

OVPN Client configuration

Import certificates (client)

Client need following files: ca.crt, client.crt and client.key. Keys can be imported in same way as on server:

> /certificate
/certificate> import passphrase="" file-name=ca.crt 
     certificates-imported: 1
     private-keys-imported: 0
            files-imported: 1
       decryption-failures: 0
  keys-with-no-certificate: 0

/certificate> import passphrase="" file-name=client.crt 
     certificates-imported: 1
     private-keys-imported: 0
            files-imported: 1
       decryption-failures: 0
  keys-with-no-certificate: 0

/certificate> import passphrase="" file-name=client.key 
     certificates-imported: 0
     private-keys-imported: 1
            files-imported: 1
       decryption-failures: 0
  keys-with-no-certificate: 0

/certificate>

Renaming of certificates is also same as on server:

/certificate
set cert1 name=CA
set cert2 name=client

Configure client

Configuration on client is inhered from server (options which can be signaled from server). Profile is optional, but to be 100% sure that configuration is same i created profile also on client:

/ppp profile
add change-tcp-mss=yes name=OVPN-client use-compression=no \
    use-encryption=required use-mpls=no use-vj-compression=no

VPN connection is finalized by adding OVPN client details:

/interface ovpn-client
add add-default-route=no auth=sha1 certificate=<client> cipher=blowfish128 \
    connect-to=<OVPN server wan IP> disabled=no name=vpn-to-server \
    password=<password> profile=OVPN-client user=<client>

Note1: by default interface is disabled, so add disabled=no or enable interface after adding it.

Note2: select chiper according to your preference.

Verify connection

At this point VPN connection should be established and working simple check can be performed by monitor command:

/interface ovpn-client
monitor 0

Output of it should show connected and correct "encoding" (encryption type). For example like this:

    status: connected
    uptime: 1m24s
  encoding: AES-256-CBC/SHA1
       mtu: 1500

Details can be checked also in server and client logs. It should contain something like this:

  • Server:
ovpn,info TCP connection established from 192.168.56.102 
ovpn,info <ovpn-0>: dialing... 
ovpn,info <ovpn-0>: using encoding - AES-256-CBC/SHA1 
ovpn,info vpn-client: connected 
ovpn,info,account client logged in, 192.168.1.2
  • Client:
ovpn,info vpn-to-server: initializing... 
ovpn,info vpn-to-server: dialing... 
ovpn,info vpn-to-server: using encoding - AES-256-CBC/SHA1 
ovpn,info vpn-to-server: connected 

There should be IP address dynamically assigned by server on client:

/ip address print

Flags: X - disabled, I - invalid, D - dynamic 
 #   ADDRESS            NETWORK         INTERFACE                                                      
 0   192.168.56.102/24  192.168.56.0    ether1                                                         
 1 D 192.168.1.2/32     192.168.1.1     vpn-to-server                                                  

In this example 192.168.56.102 was set manually as interface address and 192.168.1.2 dynamically by VPN server on VPN interface (client side).