Secure - How to OpenVPN server & client

From Linux - Help
Jump to navigation Jump to search
Openvpn.png

Goal: Understand how to configure a good OpenVPN server & client, this document is not intended to just copy past and hope that it works ^^.

Server configuration example:

This example is a server with mode Remote Access (SSL/TLS + User Auth).

Here bellow is an example for a OpenVPN server configuration (this was configured with Pfsense that I recommand to use as Firewall / Openvpn server box:

 dev ovpns
 verb n
 dev-type tun
 dev-node /dev/tun1
 writepid /var/run/openvpn_server.pid
 #user nobody
 #group nobody
 script-security 3
 daemon
 keepalive 10 60
 ping-timer-rem
 persist-tun
 persist-key
 proto udp4
 cipher AES-256-CBC
 auth SHA1
 up cmd
 down cmd
 client-connect script
 client-disconnect script
 learn-address "cmd example.com"
 local host
 tls-server
 server IP Netmask 
 client-config-dir dir
 username-as-common-name
 auth-user-pass-verify script via-env
 tls-verify "script tls 'FQDN' 1"
 lport port
 management IP port [pw-file]
 max-clients n
 push "route IP Netmask"
 push "dhcp-option DOMAIN example.com"
 push "dhcp-option DNS your DNS / Public DNS"
 client-to-client
 ca /path/to/file.ca
 cert /path/to/file.cert
 key /path/to/file.key
 dh /path/to/file.2048
 tls-auth file [direction]
 ncp-ciphers AES-256-GCM:AES-128-GCM
 persist-remote-ip
 float
 topology subnet
  • dev: TUN/TAP virtual network device ( X can be omitted for a dynamic device.) You must use either tun devices on both ends of the connection or tap devices on both ends. You cannot mix them, as they represent different underlying protocols. tun devices encapsulate IPv4 while tap devices encapsulate ethernet 802.3.
  • verb n: Set output verbosity to n (default=1). Each level shows all info from the previous levels. Level 3 is recommended if you want a good summary of what's happening without being swamped by output.
 0       -- No output except fatal errors. 
 1 to 4  -- Normal usage range. 
 5       -- Output R and W characters to the console for each packet read and write, uppercase is used for TCP/UDP packets and lowercase is used for TUN/TAP packets. 
 6 to 11 -- Debug info range (see errlevel.h for additional information on debug levels).
  • dev-type: Which device type are we using? device-type should be tun or tap. Use this option only if the TUN/TAP device used with --dev does not begin with tun or tap.
  • dev-node: Explicitly set the device node rather than using /dev/net/tun, /dev/tun, /dev/tap, etc. If OpenVPN cannot figure out whether node is a TUN or TAP device based on the name, you should also specify --dev-type tun or --dev-type tap.
  • writepid: Write OpenVPN's main process ID to file.
  • script-security:
  • daemon: Become a daemon after all initialization functions are completed. This option will cause all message and error output to be sent to the syslog file (such as /var/log/messages).
  • keepalice: A helper directive designed to simplify the expression of --ping and --ping-restart in server mode configurations.

For example, --keepalive 10 60 expands as follows:

 if mode server:
    ping 10
    ping-restart 120
    push "ping 10"
    push "ping-restart 60"
 else
    ping 10
    ping-restart 60
  • ping-timer-rem: Run the --ping-exit / --ping-restart timer only if we have a remote address. Use this option if you are starting the daemon in listen mode (i.e. without an explicit --remote peer), and you don't want to start clocking timeouts until a remote peer connects.
  • persist-tun: it is important (in the event of client restart) that the server knows to give it the same virtual IP address as it was using before.
  • persist-key: This option can be combined with --user nobody to allow restarts triggered by the SIGUSR1 signal. Normally if you drop root privileges in OpenVPN, the daemon cannot be restarted since it will now be unable to re-read protected key files. This option solves the problem by persisting keys across SIGUSR1 resets, so they don't need to be re-read.
  • proto p: Use protocol p for communicating with remote host. p can be udp, tcp-client, or tcp-server.

The default protocol is udp when --proto is not specified.

NOTE: For UDP operation, --proto udp should be specified on both peers.

NOTE: For TCP operation, one peer must use --proto tcp-server and the other must use --proto tcp-client. A peer started with tcp-server will wait indefinitely for an incoming connection. A peer started with tcp-client will attempt to connect, and if that fails, will sleep for 5 seconds (adjustable via the --connect-retry option) and try again. Both TCP client and server will simulate a SIGUSR1 restart signal if either side resets the connection.

NOTE: OpenVPN is designed to operate optimally over UDP, but TCP capability is provided for situations where UDP cannot be used. In comparison with UDP, TCP will usually be somewhat less efficient and less robust when used over unreliable or congested networks.

NOTE: There are certain cases, however, where using TCP may be advantageous from a security and robustness perspective, such as tunneling non-IP or application-level UDP protocols, or tunneling protocols which don't possess a built-in reliability layer.

  • cipher: Encrypt packets with cipher algorithm alg. The default is BF-CBC, an abbreviation for Blowfish in Cipher Block Chaining mode. Blowfish has the advantages of being fast, very secure, and allowing key sizes of up to 448 bits. Blowfish is designed to be used in situations where keys are changed infrequently. To see other ciphers that are available with OpenVPN, use the --show-ciphers option.
  • auth: Authenticate packets with HMAC using message digest algorithm alg. (The default is SHA1 ). HMAC is a commonly used message authentication algorithm (MAC) that uses a data string, a secure hash algorithm, and a key, to produce a digital signature. OpenVPN's usage of HMAC is to first encrypt a packet, then HMAC the resulting ciphertext.
  • up cmd: Shell command to run after successful TUN/TAP device open (pre --user UID change). The up script is useful for specifying route commands which route IP traffic destined for private subnets which exist at the other end of the VPN connection into the tunnel.
  • down cmd: Shell command to run after TUN/TAP device close (post --user UID change and/or --chroot ). Called with the same parameters and environmental variables as the --up option above.

NOTE: that if you reduce privileges by using --user and/or --group, your --down script will also run at reduced privilege.

  • client-connect script: Run script on client connection. The script is passed the common name and IP address of the just-authenticated client as environmental variables (see environmental variable section below). The script is also passed the pathname of a not-yet-created temporary file as $1 (i.e. the first command line argument), to be used by the script to pass dynamically generated config file directives back to OpenVPN.

NOTE: that the return value of script is significant. If script returns a non-zero error status, it will cause the client to be disconnected.

  • client-disconnect script: Like --client-connect but called on client instance shutdown. Will not be called unless the --client-connect script and plugins (if defined) were previously called on this instance with successful (0) status returns. The exception to this rule is if the --client-disconnect script or plugins are cascaded, and at least one client-connect function succeeded, then ALL of the client-disconnect functions for scripts and plugins will be called on client instance object deletion, even in cases where some of the related client-connect functions returned an error status.
  • learn-address cmd: Run script or shell command cmd to validate client virtual addresses or routes.
  • local host: Local host name or IP address. If specified, OpenVPN will bind to this address only. If unspecified, OpenVPN will bind to all interfaces.
  • client-config-dir: Specify a directory dir for custom client config files. After a connecting client has been authenticated, OpenVPN will look in this directory for a file having the same name as the client's X509 common name. If a matching file exists, it will be opened and parsed for client-specific configuration options. If no matching file is found, OpenVPN will instead try to open and parse a default file called "DEFAULT", which may be provided but is not required.
  • username-as-common-name: For --auth-user-pass-verify authentication, use the authenticated username as the common name, rather than the common name from the client cert.
  • auth-user-pass-verify: Executed in --mode server mode on new client connections, when the client is still untrusted.
  • tls-verify: Execute shell command cmd to verify the X509 name of a pending TLS connection that has otherwise passed all other tests of certification (except for revocation via --crl-verify directive; the revocation test occurs after the --tls-verify test). cmd should return 0 to allow the TLS handshake to proceed, or 1 to fail. cmd is executed as
  • lport port: TCP/UDP port number for local.
  • management IP port [pw-file]: Enable a TCP server on IP:port to handle daemon management functions. pw-file, if specified, is a password file (password on first line) or "stdin" to prompt from standard input. The password provided will set the password which TCP clients will need to provide in order to access management functions. The management interface provides a special mode where the TCP management link can operate over the tunnel itself. To enable this mode, set IP = "tunnel". Tunnel mode will cause the management interface to listen for a TCP connection on the local VPN address of the TUN/TAP interface. While the management port is designed for programmatic control of OpenVPN by other applications, it is possible to telnet to the port, using a telnet client in "raw" mode. Once connected, type "help" for a list of commands.

NOTE: For detailed documentation on the management interface, see the management-notes.txt file in the management folder of the OpenVPN source distribution.

NOTE: It is strongly recommended that IP be set to 127.0.0.1 (localhost) to restrict accessibility of the management server to local clients.

  • max-clients n: Limit server to a maximum of n concurrent clients.
  • push "route IP Netmask":
  • push "dhcp-option DOMAIN example.com":
  • push "dhcp-option DNS Internal DNS / Public DNS":
  • client-to-client: Because the OpenVPN server mode handles multiple clients through a single tun or tap interface, it is effectively a router. The --client-to-client flag tells OpenVPN to internally route client-to-client traffic rather than pushing all client-originating traffic to the TUN/TAP interface. When this option is used, each client will "see" the other clients which are currently connected. Otherwise, each client will only see the server. Don't use this option if you want to firewall tunnel traffic using custom, per-client rules.
  • ca flie: Certificate authority (CA) file in .pem format, also referred to as the root certificate. This file can have multiple certificates in .pem format, concatenated together. You can construct your own certificate authority certificate and private key by using a command such as:
 # openssl req -nodes -new -x509 -keyout tmp-ca.key -out tmp-ca.crt

Then edit your openssl.cnf file and edit the certificate variable to point to your new root certificate tmp-ca.crt.

NOTE: For testing purposes only, the OpenVPN distribution includes a sample CA certificate (tmp-ca.crt). Of course you should never use the test certificates and test keys distributed with OpenVPN in a production environment, since by virtue of the fact that they are distributed with OpenVPN, they are totally insecure.

  • cert file: Local peer's signed certificate in .pem format -- must be signed by a certificate authority whose certificate is in --ca file. Each peer in an OpenVPN link running in TLS mode should have its own certificate and private key file. In addition, each certificate should have been signed by the key of a certificate authority whose public key resides in the --ca certificate authority file. You can easily make your own certificate authority (see above) or pay money to use a commercial service such as thawte.com (in which case you will be helping to finance the world's second space tourist :). To generate a certificate, you can use a command such as:

openssl req -nodes -new -keyout mycert.key -out mycert.csr

If your certificate authority private key lives on another machine, copy the certificate signing request (mycert.csr) to this other machine (this can be done over an insecure channel such as email). Now sign the certificate with a command such as:

 $ openssl ca -out mycert.crt -in mycert.csr

Now copy the certificate (mycert.crt) back to the peer which initially generated the .csr file (this can be over a public medium). Note that the openssl ca command reads the location of the certificate authority key from its configuration file such as /usr/share/ssl/openssl.cnf -- note also that for certificate authority functions, you must set up the files index.txt (may be empty) and serial (initialize to 01 ).

  • key file: Local peer's private key in .pem format. Use the private key which was generated when you built your peer's certificate (see -cert file above).
  • dh flie: File containing Diffie Hellman parameters in .pem format (required for --tls-server only). Use
 # openssl dhparam -out dh1024.pem 1024

to generate your own, or use the existing dh1024.pem file included with the OpenVPN distribution. Diffie Hellman parameters may be considered public.

  • -tls-auth file [direction]: Add an additional layer of HMAC authentication on top of the TLS control channel to protect against DoS attacks. In a nutshell, --tls-auth enables a kind of "HMAC firewall" on OpenVPN's TCP/UDP port, where TLS control channel packets bearing an incorrect HMAC signature can be dropped immediately without response.

NOTE: --tls-auth is recommended when you are running OpenVPN in a mode where it is listening for packets from any IP address, such as when --remote is not specified, or --remote is specified with --float.

  • ncp-ciphers:
  • persist-remote-ip: Preserve most recently authenticated remote IP address and port number across SIGUSR1 or --ping-restart restarts.
  • float: Allow remote peer to change its IP address and/or port number, such as due to DHCP (this is the default if --remote is not used). --float when specified with --remote allows an OpenVPN session to initially connect to a peer at a known address, however if packets arrive from a new address and pass all authentication tests, the new address will take control of the session. This is useful when you are connecting to a peer which holds a dynamic address such as a dial-in user or DHCP client. Essentially, --float tells OpenVPN to accept authenticated packets from any address, not only the address which was specified in the --remote option.
  • topology subnet:

auth-user-pass-verify & tls-verify script:

As example, find here bellow the auth-user-pass-verify & tls-verify script used by Pfsense:

NOTE: the script used for auth-user-pass-verify & tls-verify are the same ^^.

 #!/bin/sh
 #
 # ovpn_auth_verify
 #
 # part of pfSense (https://www.pfsense.org)
 # Copyright (c) 2004-2018 Rubicon Communications, LLC (Netgate)
 # All rights reserved. 
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 #
 # http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing, software
 # distributed under the License is distributed on an "AS IS" BASIS,
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
 if [ "$1" = "tls" ]; then
         RESULT=$(/usr/local/sbin/fcgicli -f /etc/inc/openvpn.tls-verify.php -d   "servercn=$2&depth=$3&certdepth=$4&certsubject=$5")
 else
         # Single quoting $password breaks getting the value from the variable.
         # Base64 and urlEncode usernames and passwords
         password=$(echo -n "${password}" | openssl enc -base64 | sed -e 's_=_%3D_g;s_+_%2B_g;s_/_%2F_g')
         username=$(echo -n "${username}" | openssl enc -base64 | sed -e   's_=_%3D_g;s_+_%2B_g;s_/_%2F_g')
         RESULT=$(/usr/local/sbin/fcgicli -f /etc/inc/openvpn.auth-user.php -d       "username=$username&password=$password&cn=$common_name&strictcn=$3&authcfg=$2&modeid=$4&nas _port=$5")
 fi
 if [ "${RESULT}" = "OK" ]; then
         exit 0
 fi
 exit 1


Client configuration example:

Here bellow is an example for a OpenVPN client configuration:

 daemon
 verb n
 dev tun
 persist-tun
 persist-key
 cipher AES-256-CBC
 user nobody
 auth SHA1
 tls-client
 client
 resolv-retry infinite
 remote IP PORT PROTO
 verify-x509-name "example.com" name
 auth-user-pass  /path/to/your_username/and/password/userpass.txt
 remote-cert-tls server
 writepid /var/run/openvpn.pid
 status /var/run/openvpn.status
 <ca>
 -----BEGIN CERTIFICATE-----
 MIIEjzCCA3egAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCQkUx
 E5LK1qnxhNOziSxpgrvhaTFNfenSE2itBVXcnxzrIiqCuLeHNMWX0u7qEnZtPHvt
 m54wCCFP+36ZADdpFoT9aY5zSByhKLUhKS8seTRtOmx59nhl2vkwdaoMRz4Im6w7
 Xs/9Me9+wrB8M3/UthFFQpQjvg/aBIF8j0apmNPUKT5yU0mwKHVNl1LDQUdRSHEA
 avIeDZyb66C1HIy/vfP5Vozslg==
 -----END CERTIFICATE-----
 </ca>
 <cert>
 -----BEGIN CERTIFICATE-----
 MIIFDjCCA/agAwIBAgIBAzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCQkUx
 EDAOBgNVBAgTB0JlbGdpdW0xETAPBgNVBAcTCEJydXNzZWxzMRIwEAYDVQQKEwlP
 /s1Hz8v8kAj24S5Rv50E+31jy2F3E5kQYfcc9XqslCVmuWBEKN8zv7EcBVrjIWWp
 hvcNAQkBFhFyb290QG9zd2luY29ycC5wdzEbMBkGA1UEAxMSb3N3aW4ub3N3aW5j
 c3dpbmNvcnAxIDAeBgkqhkiG9w0BCQEWEXJvb3RAb3N3aW5jb3JwLnB3MRQwEgYD
 slA=
 -----END CERTIFICATE-----
 </cert>
 <key>
 -----BEGIN PRIVATE KEY-----
 MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCVDXoH6srGqzDJ
 L0h51SSYngJucCOh8NEwaewl+vhLUZ7+G4BZ39RtDEDitJ2qSaRtwI2MLUCCpoPD
 vlWoyGbjHNI00eL8t60YPXXcgwKBgQDBaGVP66ekQG372RXnWeJ/LFHFsDgv8q3g
 GxjL4pJWOi5gbtwx2AnqAZzGI6XKnU2U0IiZQk2kGJzhq+DE3LLImmK2/SMJVlzH
 Y3PAiVNbGMFyMUxf9Eia7klP
 -----END PRIVATE KEY-----
 </key>
 <tls-auth>
 #
 # 2048 bit OpenVPN static key 
 #
 -----BEGIN OpenVPN Static key V1-----
 8c42238b42e06c32502cb2baa2a48a01
 1d1ea526bd82c754f3215f7affe53e5d
 92cd874eff58abb1018055eceb54031c
 -----END OpenVPN Static key V1-----
 </tls-auth>
 key-direction 1

Explanation of the options:

  • daemon: Become a daemon after all initialization functions are completed. This option will cause all message and error output to be sent to the syslog file (such as /var/log/messages).
  • verb n: Set output verbosity to n (default=1). Each level shows all info from the previous levels. Level 3 is recommended if you want a good summary of what's happening without being swamped by output.
 0 -- No output except fatal errors. 
 1 to 4 -- Normal usage range. 
 5 -- Output R and W characters to the console for each packet read and write, uppercase    is used for TCP/UDP packets and lowercase is used for TUN/TAP packets. 
 6 to 11 -- Debug info range (see errlevel.h for additional information on debug levels).
  • dev: TUN/TAP virtual network device ( X can be omitted for a dynamic device.) You must use either tun devices on both ends of the connection or tap devices on both ends. You cannot mix them, as they represent different underlying protocols. tun devices encapsulate IPv4 while tap devices encapsulate ethernet 802.3.
  • persist-tun: it is important (in the event of client restart) that the server knows to give it the same virtual IP address as it was using before.
  • persist-key: This option can be combined with --user nobody to allow restarts triggered by the SIGUSR1 signal. Normally if you drop root privileges in OpenVPN, the daemon cannot be restarted since it will now be unable to re-read protected key files. This option solves the problem by persisting keys across SIGUSR1 resets, so they don't need to be re-read.
  • cipher: Encrypt packets with cipher algorithm alg. The default is BF-CBC, an abbreviation for Blowfish in Cipher Block Chaining mode. Blowfish has the advantages of being fast, very secure, and allowing key sizes of up to 448 bits. Blowfish is designed to be used in situations where keys are changed infrequently. To see other ciphers that are available with OpenVPN, use the --show-ciphers option.
  • user: Change the user ID of the OpenVPN process to user after initialization, dropping privileges in the process. This option is useful to protect the system in the event that some hostile party was able to gain control of an OpenVPN session. Though OpenVPN's security features make this unlikely, it is provided as a second line of defense.
  • auth: Authenticate packets with HMAC using message digest algorithm alg. (The default is SHA1 ). HMAC is a commonly used message authentication algorithm (MAC) that uses a data string, a secure hash algorithm, and a key, to produce a digital signature. OpenVPN's usage of HMAC is to first encrypt a packet, then HMAC the resulting ciphertext.
  • tls-client: Enable TLS and assume client role during TLS handshake.
  • client: Your are the client ^^.
  • resolv-retry infinite: If hostname resolve fails for --remote, retry resolve for n seconds before failing. Set n to "infinite" to retry indefinitely. By default, --resolv-retry infinite is enabled. You can disable by setting n=0.
  • remote: Remote host name or IP address. On the client, multiple --remote options may be specified for redundancy, each referring to a different OpenVPN server.

NOTE: If --remote is unspecified, OpenVPN will listen for packets from any IP address, but will not act on those packets unless they pass all authentication tests. This requirement for authentication is binding on all potential peers, even those from known and supposedly trusted IP addresses (it is very easy to forge a source IP address on a UDP packet).

NOTE: If host is a DNS name which resolves to multiple IP addresses, one will be randomly chosen, providing a sort of basic load-balancing and failover capability.

  • verify-x509-name: This option works from OpenVPN client 2.4.x, if older implemantation use option -tls-remote: Accept connections only from a host with X509 name or common name equal to name. The remote host must also pass all other tests of verification. Name can also be a common name prefix, for example if you want a client to only accept connections to "Server-1", "Server-2", etc., you can simply use --tls-remote Server

NOTE: Using a common name prefix is a useful alternative to managing a CRL (Certificate Revocation List) on the client, since it allows the client to refuse all certificates except for those associated with designated servers.

  • auth-user-pass: Authenticate with server using username/password. up is a file containing username/password on 2 lines (Note: OpenVPN will only read passwords from a file if it has been built with the --enable-password-save configure option, or on Windows by defining ENABLE_PASSWORD_SAVE in config-win32.h). If up is omitted, username/password will be prompted from the console.
  • remote-cert-tls:
  • writepid: Write OpenVPN's main process ID to file.
  • status file [n]: Write operational status to file every n seconds. Status can also be written to the syslog by sending a SIGUSR2 signal.

Documentation

  • Pfsense OpenVPN Server guide.
  • This article outlines some of problems with tunneling IP over TCP.