OpenVPN on ChromeOS




Setting up a VPN server

Setting up your home network for the VPN

Testing the VPN server


Importing Certificates into ChromeOS

Importing the CA Certificate

Importing the client certificate

Removing Bad Certs

ChromeOS VPN ONC block


Importing the ONC file

Removing a bad ONC network config.


Other topics

Ooops, I lost my laptop. Help?


Links & Misc:


This document describes how to configure OpenVPN on ChromeOS to use features that you may wish to have, are supported by openvpn itself, but not currently reachable from the user interface at this time (hopefully in the future).

The type of setup described here is a routed, certificate based VPN, with LZO compression and TLS authentication enabled, I have also chosen to route all traffic through the VPN, which has some advantages if you are travelling to some locations. This method, which initially follows the openvpn documentation, creates a self-signed Certificate Authority, and creates server / client certificates signed by that CA.  The certificates are imported into the ChromeOS TPM (a secure piece of HW on the Chromebook) and then an ONC config is created with the full VPN option set required, and this is imported into the chromebook.

These instructions knit together several different bits of documentation from various places. I have tried to explain all the steps so it’s not just a recipe.  This way you can adapt it to your configuration.


You will need:


Setting up a VPN server

To get openvpn running on the server machine, follow the instructions here:


Some tips to follow during those instructions:

For the server.conf file, I have these options set  (all comments removed for brevity):

port 1194

proto udp

dev tun

ca ca.crt

cert server.crt

key server.key  # This file should be kept secret

dh dh2048.pem


ifconfig-pool-persist ipp.txt

push "redirect-gateway def1"

push "dhcp-option DNS"

push "dhcp-option DNS"

keepalive 10 120

tls-auth ta.key 0 # Change this “0” to “1” on the client


user nobody

group nogroup



status openvpn-status.log

verb 3

The client config matches the stock one for openvpn, with the same settings needed for comp-lzo and the tls-auth key direction is set 1, as noted above. We don’t need the client.conf file for ChromeOS, but you can use it on the linux laptop for testing.

Setting up your home network for the VPN

To set up your home network for the VPN, there are 2 things that are typically necessary.

Testing the VPN server

Optional.  It can be helpful to generate a client config and certificates for a linux laptop per the instructions, and attempt to connect.  This can provide easy access to error messages and logs, but is not strictly necessary.  If successful, you should be able to connect to your VPN, and ping the VPN server, another machine on your home network, and a machine on the public internet (such as, or, a google public DNS server).


At this point we should have a working VPN server set up and a public key infrastructure, that has been tested to work on a linux laptop. Files needed for the client are:

Copy all 3 of these files to a Google Drive share so you can get at them from the chromebook.

Importing Certificates into ChromeOS

We have to import 2 certificates into ChromeOS and load them into the TPM Hardware that ChromeOS uses as its certificate store. To do this, make sure you copy the ca.crt and the client.crt  into a Google Drive share or onto a USB stick, or other secure method of getting them to the device.

Importing the CA Certificate

Browse to the following URL:  chrome://settings/certificates  

Select the “Authorities” tab as shown above, and then “Import”  Browse to where you have saved the CA.crt and click “Open”  at the popup, make sure that the box saying

“Trust this certificate for identifying websites” is checked:

You should see your CA listed in the “Authorities” tab now.

Importing the client certificate

The next step is to import the pkcs12 certificate that you created above.  From chrome://settings/certificates, and look for the “Your Certificates” tab:

Make sure to click “Import and Bind to Device”, and not “Import” to put the cert in the TPM.  Browse to the client-cert.p12 file you created earlier. If you used a password to encrypt the cert file, enter it here, if not, just press enter. At this point you should see a certificate loaded in this window with the comment “(hardware-backed)”:

I’ve pixelated my CA name cause gimp is fun :)

Removing Bad Certs

If you have imported an incorrect cert, or wish to update it by importing a revised one, you can see in the screen shots above both the CA cert import and the client cert import have delete options as well.  

ChromeOS VPN ONC block

Now for the hard part.  We are using a configuration that is supported by OpenVPN on ChromeOS, but not yet fully supported by the UI.  

Fortunately there is a way to get more configuration options available via an “under the hood” method, using “Open Network Config” blocks, which are JSON opjects (JavaScript Object Notation).  Normally these are pushed down via the enterprise control panel for enterprised enrolled devices, but for non enterprise accounts, we can inject an ONC config via an internal URL.


This method of importing the ONC is available on Verified Boot mode devices (no need to be in dev mode)  but is not officially supported. There is work underway to make the config UI more rich, but at the moment this is the way to go. I add this warning to make sure that while this method works, it would be bad if it got too wide spread and broke. Please use some discretion in sharing this document.  

To make the ONC block, we need 2 GUIDs (though I suspect we can use random strings).  I got 2 from they are just random.  Just hit reload to get another.

To make this block, copy the template below into a text editor and fill in the appropriate fields.

There is more documentation on the ONC format here:   But some things are a bit trial and error, or require searching the source code.  The Links section at the end of this document has more info.

Here, on the next page, is a template, and things between “<” and “>”  will be filled in as noted below.



      "Certificates": [ {

      "GUID": "{<GUID#1>}",

      "Type": "Authority",

      "X509": "<CA_CERT>"

   } ],

    "NetworkConfigurations": [ {

      "GUID": "{<GUID#2>}",

      "Name": "<VPN_NAME>",

      "Type": "VPN",

      "VPN": {

          "Type": "OpenVPN",

          "Host": "<HOSTHAME>",

          "OpenVPN": {

                        "ServerCARef": "{<GUID#1>}",

                    "AuthRetry": "interact",

                    "ClientCertType": "Pattern",

                    "ClientCertPattern": {              

                          "IssuerCARef": [ "{<GUID#1>}" ]


                    "CompLZO": "true",

                    "Port": 1194,

                    "Proto": "udp",


                    "RemoteCertEKU": "TLS Web Server Authentication",

                    "SaveCredentials": false,

                    "ServerPollTimeout": 10,

                    "Username": "<USERNAME>",





                               } ]


OK, this is a bit of a mess, but we will take it one field at a time:

GUID#1  just a random string as an identifier, it’s used in 3 places to link the network config to the CA for that config.  If you want to generate a GUID, you can get one from the above website, and it will look something like:  48944528-58fa-401e-8cea-7a75e4305592,  or you can perhaps just use “MY-CERT-AUTHORITY”  without the quotes.  

GUID#2 is another identifier, to label the VPN config, and is not actually used, but we need an identifier here to make things work.

VPN_NAME: This can be anything you like, it will be used in the UI for selecting the VPN.  If you have a name for your network e.g., “Walter White’s Home VPN” you can use it here.

CA-CERT: this is the contents of the CA.crt, without the header lines, on one long line, so it will be one long string of base64 encoded ascii, typically begining with “MII” and continuing on for some lines, remove the newlines in the cert.  The footer line “-----END CERTIFICATE-----” is also not included.

HOSTNAME:  This is simply the hostname of your VPN server, e.g.  

USERNAME:  Is your username on the vpn server.

TLS_AUTH_KEY:  This one is the TLS auth key, but there is a catch.  We need to remove the comment lines, but DO need to include the header and footer lines “-----BEGIN OpenVPN Static Key V1-----” and the footer line “-----END OpenVPN Static Key V1-----”, but we replace all the newlines with literal “\n” characters.  This is not documented, but can be seen in the test cases for the ChromeOS VPN support.

You can do this with this shell command:  

grep -v '#' ta-demo.key | perl -p -e 's/\n/\\n/' -

Yeah, it’s a bit of a Yak shave, I am sure there is a more optimum way to do this in sed / awk / perl, but this works, and we only need it once. Copy the output of the command into the TLS auth key value as shown:

Here is what the key looks like before and after:



# 2048 bit OpenVPN static key


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

















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

And After:

-----BEGIN OpenVPN Static key V1-----\nad81f4aafe33ecbbc68ae88536ccd8d4\n9c929dfdd6d57aff5e082a37da9a827c\nca3f3db0815b1ae268bb106946c6e757\n4f5e624824b5e3c62c02a6098f1d4efe\n8d9858df2a73c5ec1a3b6e3901f1d70d\n3e16a318999d6515f3a7f1b0971ebe48\ne59145aa968c8c2b69926a78ce6ddf5f\nf5df09d1340bd3227ed65c294fe15273\nfc142b05a4bce36395c86727825c378a\n56ca3d32ccc888172f4549334835cae2\n39d7348daccba3c2131f6e62e85873aa\nbe8c79a342f64335963825468b262789\ne94148ea636272928002770262b345d7\na3bcf8637c2138ffebe47ac879755a5d\n51cfa985db7d56006e4d865dd0487a12\n55bfe0b9d162e0dc54457a9bb9bbeaaf\n-----END OpenVPN Static key V1-----\n

Note the “\n” characters buried inside. so the final value looks like:

“TLSAuthContents”:”-----BEGIN OpenVPN Static key V1-----\nad81f4aafe33ecbbc68ae88536ccd8d4\n9c929dfdd6d57aff5e082a37da9a827c\nca3f3db0815b1ae268bb106946c6e757\n4f5e624824b5e3c62c02a6098f1d4efe\n8d9858df2a73c5ec1a3b6e3901f1d70d\n3e16a318999d6515f3a7f1b0971ebe48\ne59145aa968c8c2b69926a78ce6ddf5f\nf5df09d1340bd3227ed65c294fe15273\nfc142b05a4bce36395c86727825c378a\n56ca3d32ccc888172f4549334835cae2\n39d7348daccba3c2131f6e62e85873aa\nbe8c79a342f64335963825468b262789\ne94148ea636272928002770262b345d7\na3bcf8637c2138ffebe47ac879755a5d\n51cfa985db7d56006e4d865dd0487a12\n55bfe0b9d162e0dc54457a9bb9bbeaaf\n-----END OpenVPN Static key V1-----\n”

Save the edited file, as filename.onc, and copy it to Google Drive, or get it on the Chromebook somehow. You should treat it as secure since it has a TLS auth key inside.

TODO: Learn enough javascript  (or AppsScript) / python to automate creation of an ONC blob.

Importing the ONC file

Browse to chrome://net-internals, and select ChromeOS from the dropdown (you may be able to browse to chrome://net-internals/#chromeos,

Note the highlighted section (in pink above).  Click “Choose File” and browse to your ONC file.

Note you will see an import error, this is normal the moment, but if you then click on the VPN menu item in the launcher menu, you should see the VPN and be able to connect (make sure you have started the vpn server.

Removing a bad ONC network config.

If you have imported a bad ONC config (or want to update an existing one) this is easy, but the UI is hard to discover.  

[TODO: add screenshots]

Go to chrome://settings, and under the heading “Internet Connection”  click the down arrow next to “Private Network”,  you will see your VPN config listed there.  Select “Preferred Networks”  and a pop up will appear with your networks listed.  Mouse over the one you want to delete (it should highlight) and you will see an “x” on that line.  Click that “x” to delete the network config (Note this is not the “x” to close the popup, it’s on the same line as the VPN name.  


There are several debugging tips that can help :

The url: chrome://system  has log entries under “netlog” and network-services”
The ONC importer will dump log messages to the category “chrome_user_log” or “Profile [*] chrome_user_log” depending on which version of ChromeOS you have.

The network-services item in chrome://system will have a list of all known connection methods one entry for each wifi network, any cellular networks if available and any configured VPNs, so you can see if something didn’t get imported correctly.

Dump cert contents to check for things like Ext. Key Usage settings , and names etc.

openssl x509 -in <filename.crt> -noout -text

Other topics

Ooops, I lost my laptop. Help?

No fear, the easy-rsa package has a revoke-full script that will remove a certificate. You can also stop the VPN server remotely via ssh if you have such access.

Multiple VPNs

If you have multiple VPNs to set up (say one to home, and one to a virtual machine someplace else), you can create 2 ONC files and import them one at a time.  Alternatively, the “Network Configurations” block is an array, and if you are careful with the JSON syntax you could duplicate the configuration item inside the outer “[“ “]”  section. Be sure to create unique GUID#2s for each of the configurations,  GUID#1 should be the same since that refers to the CA cert that is used to sign all of them.  If you have unique CAs for each VPNs then it might be cleaner to put them in individual ONC files.  

This is as yet un-tested, but should work.


Note that the certs above are not my real certificates, and the TA key was generated just for this demo.

The use of the separate client certificate (rather than putting the client cert into the ONC file) is nice since the same ONC file will work on any machine in your network, so you only have to do the painful bit once. The client cert is the only per-machine bit needed.

Links & Misc:

 Autotest for openvpn:  

These test cases have some example ONC configs that can be useful.

Howto use net-internals, from the University of Cambridge:

ONC Spec:

ONC examples:

UUID gen:  (not really needed)

Bugs to watch:

Spigot (ONC Generator, needs a little updating to work as a Chrome extension ):;a=tree;f=chromeapps/spigots;h=a34cff9848a4bbe36ae846af3ced212606df6008;hb=HEAD
The manifest.json file is missing a field.  However this doesn’t yet support full editing of all the options we need here, so manual editing would be needed anyway, so I didn’t use it.

Todo: add RemoteCertEKU and such to onc like: