Getting Certificates for Neo4j with LetsEncrypt

David Allen
Sep 17, 2018 · 6 min read

Using Neo4j’s cloud VMs, a common question is how to set up valid SSL to protect data in transit. This article will cover how to do it with LetsEncrypt, a popular free certificate authority.

The instructions below will work with most any public cloud-hosted instance of Neo4j, so let’s get started.

Come along and ride on a fantastic voyage

Why are we doing this?

You need valid SSL certificates in order for the browser and various client applications to trust that your site is what it says that it is. If you’ve created a Neo4j instance in a public cloud and you’ve seen browser warnings about “this site is untrusted” or “add a special exception” — valid certificates solve this.

The surefire way to know that you don’t have a valid certificate

Additionally, some common connection errors for Neo4j and cypher-shell derive from the lack of a valid certificate. So here we go.

Prerequisites

  • Your machine must have a valid DNS address in order to have a valid SSL certificate. Certificates typically aren’t granted for bare IP addresses because it’s a lot harder to prove that you own/control a bare IP address.
  • Important: If you’re doing this for a causal cluster, you must do it for each machine in the cluster, not just one.
  • This tutorial doesn’t cover how to get a valid DNS address, as the process is different for each cloud, and each network setup. Before using these instructions, ensure that you have a DNS name for your machine, and verify that it resolves correctly to the IP of your machine before proceeding.

When I do this, I register domains with Google Domains, (let’s say mydomain.com) and I use Google Cloud DNS to map my neo4j machines (say, machine1.cluster.mydomain.com) to the IP address of the VM on Google Cloud. But there are many different possible ways to accomplish this part that you can find online.

Install LetsEncrypt

On each machine, we’re going to install certbot, a tool which generates certificates. To do that, we’ll pull some commands from the certbot install guidelines.

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install -y certbot

What is Let’s Encrypt?

Let’s Encrypt is a certificate authority that provides free certificates to help encourage people to use encryption across the Web. Encryption makes the web safer and more secure for everyone. If you need more background on certificate authorities, have a look here.

Generate a certificate

Certbot will generate the certificate for us. In order for it to work, and to verify that you have the domain you claim, we’ll need to ensure via the firewall that port 80 is open (just temporarily) so the tool can do its job. Port 80 is not normally open for Neo4j cloud instances, so make sure to set that up as a separate step.

Here is how we generate the certificate - sudo certbot certonly. As shown in the following, we’re specifying option 1 (we want to create a temporary webserver on port 80 to verify ourselves) and providing the domain name we’re generating a cert for (node2.cluster.graph.center)

$ sudo certbot certonly
Saving debug log to /var/log/letsencrypt/letsencrypt.log
How would you like to authenticate with the ACME CA?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Spin up a temporary webserver (standalone)
2: Place files in webroot directory (webroot)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 1
Plugins selected: Authenticator standalone, Installer None
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org
Please enter in your domain name(s) (comma and/or space separated) (Enter 'c'to cancel): node2.cluster.graph.center
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for node2.cluster.graph.centerWaiting for verification...
Cleaning up challenges
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/node2.cluster.graph.center/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/node2.cluster.graph.center/privkey.pem
Your cert will expire on 2018-12-16. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew"
- If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le
$

The success message at the bottom says we now have certificates located in the /etc/letsencrypt/live/* subdirectories.

Configuring neo4j

LetsEncrypt maintains these certificates in a directory called “live”. These are actually symlinks to files in another directory. This layer of indirection allows the certbot program to update the certificates periodically as needed, so that you don’t have to change cert locations.

So when we place these into the neo4j structure, we’re going to be careful to create symlinks and not copy the files. By doing so, we can take advantage of the refresh abilities of Let’s Encrypt we might want later on.

The first thing we have to do is adjust the very tight permissions on the certificates directory, by changing group ownership to neo4jand to make them readable by Neo4j.

# Change group of all letsencrypt files to neo4j
sudo chgrp -R neo4j /etc/letsencrypt/*
# Make sure all directories and files are group readable.
sudo chmod -R g+rx /etc/letsencrypt/*

Next, we set up symlinks and the directory structure neo4j expects.

cd /var/lib/neo4j/certificates
sudo mkdir revoked trusted bak
# Move old generated certificates into a backup directory
sudo mv neo4j.* bak
export MY_DOMAIN=graph.somehost.com# Configure cert neo4j will use
sudo ln -s /etc/letsencrypt/live/$MY_DOMAIN/fullchain.pem neo4j.cert
# Configure private key neo4j will use
sudo ln -s /etc/letsencrypt/live/$MY_DOMAIN/privkey.pem neo4j.key
# Indicate that this cert is trusted for neo4j
sudo ln -s /etc/letsencrypt/live/$MY_DOMAIN/fullchain.pem trusted/neo4j.cert

Finally, we make adjustments to our neo4j configuration file. (Keeping in mind that if you’re using cloud VM images, this is /etc/neo4j/neo4j.template, and in other environments it’s /etc/neo4j/neo4j.conf)

dbms.connectors.default_listen_address=0.0.0.0
dbms.connectors.default_advertised_address=your.hostname.com
bolt.ssl_policy=default
dbms.ssl.policy.default.base_directory=/var/lib/neo4j/certificates
dbms.ssl.policy.default.allow_key_generation=false
dbms.ssl.policy.default.private_key=/var/lib/neo4j/certificates/neo4j.key
dbms.ssl.policy.default.public_certificate=/var/lib/neo4j/certificates/neo4j.cert
dbms.ssl.policy.default.revoked_dir=/var/lib/neo4j/certificates/revoked
dbms.ssl.policy.default.client_auth=NONE

Let’s work through explanations of what these do and why you need them. You can find a complete reference in the Neo4j SSL Framework documentation.

  • The default advertised address for the node is set to the public DNS you configured. This is important so that clients can connect to that DNS name, and also so that when the Neo4j Browser application does redirects, it does so to a trusted DNS entry with a signed cert. The Neo4j Browser application tends to issue redirects to the default_advertised_address. As a result browsers will get confused if this is misconfigured, because it will look like an SSL site trying to redirect to some other, untrusted site (for example, a bare IP address).
  • We configure the bolt transport to use the “default” policy for SSL.
  • We define the “default” policy with dbms.ssl.policy.default.*, including:
  • The directory where everything is stored
  • The private key
  • The public certificate
  • Finally, client_auth=NONE means that the client does not have to similarly auth with a valid cert (i.e. it is OK if the client doesn’t have a cert)

Restart neo4j

After all of this configuration, we need to restart neo4j

systemctl restart neo4j

Renewing LetsEncrypt certificates on a schedule

By running certbot with a renew flag, you can update certificates to push out the expiry date. Doing this for example, monthly via a cron job keeps the certificate fresh.

You can check that this will work by running:

sudo certbot renew --dry-run

And check that the output has no errors.

The nice part about our symlink structure above is that our links are always pointing to the certs in the “live” directory, which themselves are symlinks to whatever the latest certbot has created. For that reason, we’ve automated keeping our certificates fresh with certbot!

Remember though that for the challenge/response, we do need to keep port 80 open so that the periodic renewal can function.

Happy Graph Hacking!

Neo4j Developer Blog

Developer Content around Graph Databases, Neo4j, Cypher, Data Science, Graph Analytics, GraphQL and more.

Thanks to Elaine Rosenberg.

David Allen

Written by

Neo4j Developer Blog

Developer Content around Graph Databases, Neo4j, Cypher, Data Science, Graph Analytics, GraphQL and more.