Skip to content

Cryptode: managing your VPN connections (CLI for Linux and macOS)

Notifications You must be signed in to change notification settings


Repository files navigation

Cryptode: Cryptode protects you using a hidden path

Build Status Coverity Scan

Cryptode is a command-line driven OpenVPN client for macOS (Sierra and High Sierra) and (WIP: RHEL/CentOS 7 + Ubuntu) with two focus areas:

  • CLI usage for automation

  • Security for protection of your OpenVPN private keys + certificates and ensuring that only approved routes are added to your route table

NOTE1: Let us know if you have any comments, suggestions for improvement, found a bug or identified a security vulnerability.

NOTE2: We are open to pull requests!

Quick Install

On macOS:

brew tap cryptode/cryptode
brew install cryptode
sudo /usr/local/bin/
Remember to follow instructions given in the "caveats" section during brew install!
Take a look at the Cryptode homebrew formula if you want to know more.


sudo yum install<TODO>


$ cryptode
Usage: cryptode [options]
    connect         <all|connection name>                                        : connect to a VPN with given name (default:all)
    disconnect      <all|connection name>                                        : disconnect VPN with given name (default:all)
    reconnect       <all|connection name>                                        : reconnect VPN with given name (default:all)
    status          <all|connection name>                                        : get status of VPN connection with given name (default:all)
    json                                                                         : display output as JSON format
    edit            <connection name> <auto-connect|pre-exec-cmd|profile> <value>: edit VPN connection with given name
    remove          <connection name> [force]                                    : remove VPN connection (sudo required)
    import          <new-from-tblk|new-from-ovpn> <path>                         : import VPN connection (sudo required)
    reload                                                                       : reload configuration (sudo required)
    dns-override    <enable <DNS serve IP list>|disable|status>                  : override DNS settings (sudo required)
    script-security <enable|disable>                                             : enable/disable script security
    version                                                                      : print version
    help                                                                         : print help message

Add VPN Connection

Follow these steps to setup a new VPN connection. The name of the VPN is derived from the filename.

Add the OpenVPN configuration (a .ovpn file):

sudo vi /opt/cryptode/etc/vpn.d/vpn1.ovpn

Example OpenVPN file (with empty <cert>, <ca> and <key>, these obviously needs to be in there when actually adding the VPN):

<Cryptode path>/etc/vpn.d/vpn1.ovpn
dev tun
proto tcp
remote 1194
resolv-retry infinite
remote-cert-tls server
verb 3
auth SHA512
cipher AES-256-CBC
tls-version-min 1.2


(OPTIONAL) Add the NOC configuration:

sudo vi /opt/cryptode/etc/vpn.d/vpn1.noc
<Cryptode path>/etc/vpn.d/vpn1.noc
  "auto-connect": false,
  "pre-connect-exec": "",
  "pre-connect-exec-interval": ""

Set the correct permissions on both files:

sudo chmod 500 /opt/cryptode/etc/vpn.d/vpn1.ovpn
sudo chmod 500 /opt/cryptode/etc/vpn.d/vpn1.noc

After adding the new VPN called vpn1 by adding the .ovpn + .noc files to <Cryptode path>/etc/vpn.d you need to reload cryptoded.

Reload cryptoded:

$ sudo cryptode reload
Sending reload signal to cryptoded process '3667' has succeeded.

Check status:

$ cryptode status vpn1
name: vpn1
	config-status: NOC configuration exists
	profile: /opt/cryptode/etc/vpn.d/vpn1.ovpn
	openvpn-status: DISCONNECTED
	in-total: 0
	out-total: 0
	timestamp: 1512720431
	auto-connect: Disabled

Connect A VPN Connection

$ cryptode connect vpn1
name: vpn1
	config-status: NOC configuration exists
	profile: /opt/cryptode/etc/vpn.d/vpn1.ovpn
	openvpn-status: DISCONNECTED
	in-total: 0
	out-total: 0
	timestamp: 1512720713
	auto-connect: Disabled

$ cryptode status vpn1
name: vpn1
	config-status: NOC configuration exists
	profile: /opt/cryptode/etc/vpn.d/vpn1.ovpn
	status: CONNECTED
	openvpn-status: CONNECTED
	in-total: 2293
	out-total: 2419
	connected-time: 1512720716
	in-current: 2293
	out-current: 2419
	timestamp: 1512720719
	auto-connect: Disabled

Check Status Of A VPN Connection

$ cryptode status vpn1
name: vpn1
	config-status: NOC configuration exists
	profile: /opt/cryptode/etc/vpn.d/vpn1.ovpn
	status: CONNECTED
	openvpn-status: CONNECTED
	in-total: 3036
	out-total: 3153
	connected-time: 1512720716
	in-current: 3036
	out-current: 3153
	timestamp: 1512720769
	auto-connect: Disabled

Disconnect A VPN Connection

$ cryptode disconnect vpn1
name: vpn1
	config-status: NOC configuration exists
	profile: /opt/cryptode/etc/vpn.d/vpn1.ovpn
	openvpn-status: CONNECTED
	in-total: 3226
	out-total: 3358
	timestamp: 1512720820
	auto-connect: Disabled

$ cryptode status vpn1
name: vpn1
	config-status: NOC configuration exists
	profile: /opt/cryptode/etc/vpn.d/vpn1.ovpn
	openvpn-status: DISCONNECTED
	in-total: 3226
	out-total: 3358
	timestamp: 1512720824
	auto-connect: Disabled


Cryptode has the following structure:

  • <Cryptode path>/bin/cryptoded: the daemon that is responsible for starting and stopping VPN connections

  • <Cryptode path>/bin/cryptode: the client that is used to make cryptoded connect/disconnect to VPNs

  • <Cryptode path>/etc/cryptoded.conf: the main configuration file for cryptoded

  • <Cryptode path>/etc/vpn.d: the directory in which .ovpn and .noc files are stored

  • /var/run/cryptoded: the socket that cryptode uses to communicate with cryptoded

  • /var/log/cryptoded/cryptoded.log: the log file from cryptoded, use this for troubleshooting

Mandatory VPN configuration files:

  • <Cryptode path>/etc/vpn.d/<vpn>.ovpn: the OpenVPN file that contains the configuration of the VPN, private key, client certificate and CA certificate

Optional VPN configuration files:

  • <Cryptode path>/etc/vpn.d/<vpn>.noc: the cryptoded configuration of this particular VPN

VPN log files:

  • /var/log/cryptoded/<vpn>.ovpn.log: VPN log file

Platform Specific Paths and Dependencies: macOS

Cryptode path


launchd cryptoded plist



  • /opt/openvpn/sbin/openvpn: a copy of the OpenVPN executable that is owned by root

Platform Specific Paths and Dependencies: RHEL/CentOS

Cryptode path


systemd unit file



  • /usr/sbin/openvpn: the location of the OpenVPN executable as installed via yum


Cryptode has HCL-based configuration format which is parsed by libnereon

Global Configuration

The <Cryptode path>/etc/cryptoded.conf configuration file looks like this on macOS:

<Cryptode path>/etc/cryptoded.conf
global {
  user_id = 501
  restrict_socket = true
  log_directory = "/var/log/cryptoded"
  vpn_config_paths = "/opt/cryptode/etc/vpn.d"

openvpn {
  sbin_path = "/opt/openvpn/sbin/openvpn"
  root_check = true
  enable_updown_scripts = false

the location of the OpenVPN executable. Since this executable will run as uid 0 it is important to place this executable in a directory not writable by unprivileged users.

On macOS OpenVPN will be most likely installed by brew in /usr/local/sbin and for security purposes therefore must be copied to /opt/openvpn/sbin. If you wish to have cryptoded use the OpenVPN executable in /usr/local/sbin then you can, but this is not advised as a local attacker typically can replace anything in /usr/local/.

cryptoded can perform a check whether the OpenVPN executable is owned by root. On macOS cryptoded will expect OpenVPN to live in /opt/openvpn/sbin which must be owned by root. In case you want to use the OpenVPN executable in another directory such as /usr/local/bin then you can disable this check, but this is not advised.


OpenVPN allows to run up and down scripts to set routes and perform MFA actions. By default this behaviour is disabled and up scripts are handled by cryptoded on a per VPN basis with the pre-connect-exec statement in the VPN .noc file. It is not advised to enable the ovpn_up_down_scripts globally unless you really need this and know what you are doing.


this is the UID of the unprivileged user cryptoded will execute pre-connect-exec scripts as. Also the socket of cryptoded will only be writable to by this UID.


cryptoded by default only accepts cryptode socket connections from the UID set in user_id. This is to prevent access to your VPN connections on multi-user systems. Disabling this restriction is not advised.


this is the log file cryptoded will write to.


cryptoded stores OpenVPN files on macOS in /opt/cryptode/etc/vpn.d and on RHEL/CentOS in /usr/local/etc/vpn.d/.

This file is mandatory.

Per-VPN Configuration

Example cryptoded configuration for a VPN: <Cryptode path>/etc/vpn.d/vpn1.noc.

<Cryptode path>/etc/vpn.d/vpn1.noc
  "auto-connect": false,
  "pre-connect-exec": "",
  "pre-connect-exec-interval": ""

This file is optional.


Set this to true when you want to automatically connect to a VPN when cryptoded starts. This is useful when you have Jenkins slaves auto connecting to VPNs upon boot.


Run a script or executable before connecting to the VPN. This can be used to execute a script for MFA purposes.


Repeat the execution of the pre-connect-exec at set intervals. This is useful for continuous MFA keep alive.

The .noc configuration file for a VPN is optional. You should only create one if you need auto-connect and/or a pre-connect-exec script to run.

Security Architecture And Considerations

The architecture of Cryptode is designed to be seamlessly used and managed from the command line, but kept as secure as possible.

You need sudo for operations that require access to root owned directories and files.

macOS clients are typically GUI based and require you to enter a password every time you want to change something. This approach makes it impossible to automate VPN management and operation. Cryptode is created to fix this for macOS OpenVPN connection management.


| launchd/systemd |
+----------------------------------+  +-main configuration--------------+
| <Cryptode path>/bin/cryptoded +->| <Cryptode path>/etc/cryptoded.conf |
+-+----+---------------------------+  +---------------------------------+
  |        +-cryptoded VPN configuration file----+
  |     +->| <Cryptode path>/etc/vpn.d/<vpn>.noc |
  |     |  +-------------------------------------+
  |     |  +-OpenVPN configuration file-----------+
  |     +->| <Cryptode path>/etc/vpn.d/<vpn>.ovpn |<-+
  |        +--------------------------------------+  |
  |                                                  |
  |      +-cryptoded log--------------------+        +----+
  +----->| /var/log/cryptoded/cryptoded.log |        |
  |      +----------------------------------+        |
  |                                         |
  |      +-OpenVPN started by cryptoded----------+-------------------------------+
  +----->| <OpenVPN path>/openvpn --config <Cryptode path>/etc/vpn.d/<vpn>.ovpn  |
  |      +                        --log-append /var/log/cryptoded/<vpn>.ovpn.log |
  |      +-------------------------------------+---------------------------------+
  |                                            |
  |                              +-------------+
  |                              |
  |      +-socket-------------+  |  +-VPN log file----------------------+
  +----->| /var/run/cryptoded |  +->| /var/log/cryptoded/<vpn>.ovpn.log |
         +--------------------+     +-----------------------------------+
| <Cryptode path>/bin/cryptode +

Cryptoded Binary Ownership

cryptoded is owned by root:wheel and has the following permissions: -r-x------. cryptoded is meant to be only executed by launchd or systemd. So don’t start it manually. Upon starting cryptoded will create a socket in /var/run/cryptoded which will be writable only by a predefined userid that is set in <Cryptode path>/etc/cryptoded.conf.

It looks like this:

$ ls -la /var/run/cryptoded
srw-------  1 test  wheel  0 Sep 19 15:52 /var/run/cryptoded
$ id test
uid=501(test) gid=20(staff) groups=20(staff),401(,12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),501(access_bpf),701(,33(_appstore),100(_lpoperator),204(_developer),395(,398(,399(,402(

Cryptode Binary Ownerships

cryptode is owned by root:wheel and has the following permissions: -r-xr-xr-x. cryptode can be executed by any user but the socket cryptode connects to can only be written to a predefined userid. This restricts the connecting/disconnecting of VPNs to a single userid. Sending a reload signal to cryptoded using cryptode requires sudo.

On macOS Brew and/or a manual make install installs cryptode to /usr/local/bin, you MUST follow the instructions to install the executables in /opt/cryptode/bin using

cryptode performs a check whether it is executed from /opt/cryptode/bin or not. If it isn’t then it will exit. This will force you to put /opt/cryptode/bin in the beginning of your PATH. This is to prevent you from running sudo on a backdoored cryptode that was placed in /usr/local/bin by a local attacker.

OpenVPN Files

OpenVPN configuration files are stored in <Cryptode path>/etc/vpn.d which is owned by root:wheel and has drwxr-xr-x permissions.

The per-connection OpenVPN files are stored as <Cryptode path>/etc/vpn.d/<vpn>.ovpn, owned by root:wheel and have -rw------- permissions.

The cryptoded VPN configuration are stored as <Cryptode path>/etc/vpn.d/<vpn>.noc, owned by root:wheel and have -rw------- permissions.

This strict permission and owner scheme is to prevent your private keys being leaked and/or your VPN configurations modified by a local attacker.

If cryptoded were to be allowed to use any OpenVPN file then a local attacker could potentially change the routes to the system’s DNS servers to an attacker controlled IP.

cryptoded only accepts OpenVPN files that are owned by root and are not readable by others:

$ ls -la /opt/cryptode/etc/vpn.d
total 144
drwxr-xr-x  14 root  wheel   476 Sep 15 13:28 .
drwxr-xr-x   4 root  wheel   136 Sep 15 16:48 ..
-rw-------   1 root  wheel   146 Sep 11 13:50 vpn1.noc
-rw-------   1 root  wheel  7240 Sep 11 13:50 vpn1.ovpn

Per-VPN Configuration

VPNs do not not require a .noc cryptoded configuration file. By default VPN connections will not auto-connect and no pre-connect-exec will be executed.

Pre-Connect Scripts

VPNs can be configured that a script is executed before OpenVPN will connect. This is defined in pre-connect-exec in <Cryptode path>/etc/vpn.d/<vpn>.noc.

As cryptoded runs as root it will drop its root privileges to the UID defined with user_id in <Cryptode path>/etc/cryptoded.conf.

Log Files

OpenVPN will be executed as root but log files will be owned by user_id. This is to ensure that your desktop user can access and delete the log files of his/her VPNs.

The following code in src/vpn.c is responsible for this:

OpenVPN Executable

On macOS Brew installs OpenVPN in /usr/local/sbin. This allows a local attacker to replace the openvpn executable with something malicious. Therefore during installation of Cryptode a root-owned copy of openvpn needs to be placed in /opt/openvpn/sbin.

Upon start, cryptoded will perform the root check on the openvpn executable before it actually runs it.


Installation via source on macOS

Install dependencies:

brew install openvpn

Manual compilation and installation:

git clone
cd cryptode
make install
sudo /usr/local/bin/