To save my fingers hammering out letters to form the words that describe why, lets just accecpt that using SSL is a good thing.

If you’re using Aura then you can skip reading my prose and do something more interesting instead as Aura only uses SSL. For everyone else you will need to configure SSL.

The following is a guide to doing this with LetsEncrypt, a free source of certificates, and an AWS EC2 running docker with Neo4j. I run that combination on a frequent basis - it’s my goto for trying stuff out.

Neo4j supports SSL across these communication channels.

  • bolt (port - 7687)
  • https (port - 7473)
  • cluster (ports - 5000, 6000, 7000, and 7688)
  • backups (port - 6362)

It’s entirely possible to selectively turn on SSL - you don’t have to do everything.

I’m going to look at Bolt + HTTPS. Why both? For reasons that I’ve not yet discovered, to use HTTPS you also must have Bolt using SSL.

Enabling HTTPS covers everything that uses HTTP operations including the Query API

For this, I’ll be using

  • AWS
  • GoDaddy for DNS
  • Docker
  • Lets Encrypt

and a fair bit of typing at the command shell

Lets get started

Provision and configure an EC2 image with elastic IP address

You will need to provision an EC2 machine for running Neo4j. Ideally this would be circa 4gb with a couple of vCPU or better. Give it 250Gb of storage.

An elastic IP address is being used to avoid IP address changes on restarts etc.. as this gets messy with then having to adjust the public DNS entries.

I’m using AWS Linux 2 for the image.

Note: Do not use aws linux 2023 as it does not fully support snapd which is used to install certbot for certificate generation with Lets Encrypt

Adjust security group

Once the EC2 machine is provisioned, adjust the security group to allow all inbound TCP from your IP address to the EC2 machine

Then connect using SSH to the EC2 Machine

Install any updates

Type at the command prompt on the EC2 machine

sudo yum update -y

Install Docker

Type at the command prompt on the EC2 machine

sudo yum install docker -y

Enable docker service at AMI boot time

Type at the command prompt on the EC2 machine

sudo chkconfig docker on

Start the docker service

sudo service docker start

Add the ec2-user to the dockergroup

This allows ec2-user to execute Docker commands without using sudo.

sudo usermod -a -G docker ec2-user

Log out, log back in

Confirm docker commands can be run without sudo - this will throw an error if the user was not added or not logged out and back in

docker info

You should see a data set that contains information about the version of Docker that has been installed

Update DNS

I’m assuming that you’ve already got DNS configured somewhere. To that configuration you need to add an ‘A’ record using the elastic IP address of your EC2 host and give it a hostname. When creating a certificate for our EC2 machine, Certbot will lookup our entry and will fail if DNS resolution fails. If you do not have a DNS provider, you can use the free DuckDNS service. I’d avoid doing this for any production as DuckDNS system contains many entries that are used for malware. Be careful.

Lets Encrypt

Let’s Encrypt is a free, automated, and open certificate authority (CA), run for the public’s benefit. It is a service provided by the Internet Security Research Group (ISRG). You can read more about Let’s Encrypt on their website Lets Encrypt website.

Lets Encrpt offers a CLI, Certbot , that takes care of getting a certificate for us and also can be used for renewals, something I discuss at the end.

Go ahead and SSH to the EC2 machine

Pre-installation requirement

Certbot uses the snap system for installation. There are other methods but this is what we are using here. We’ll need to install snap first.

On the EC2 machine, perform the following at the command prompt

sudo yum update 
sudo wget -O /etc/yum.repos.d/snapd.repo \
    https://bboozzoo.github.io/snapd-amazon-linux/amzn2/snapd.repo
sudo yum install snapd -y
sudo systemctl enable --now snapd.socket
sudo systemctl start snapd

Install Certbot

On the EC2 machine, perform the following at the command prompt

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Allow all traffic from anywhere

For the moment adjust inbound security group rules to allow HTTP from anywhere. Certbot will fail if this step is not done.

Obtain a certificate

This is simply a matter of running Certbot from the command line with a few parameters and then answering a few basic prompts.

You can leave out -v as this is just to provide verbose output so you can see what’s going on in detail.

Note: Make sure you include –key-type rsa in the Certbot command line as this is the certificate format expected by Neo4j

On the EC2 machine, perform the following at the command prompt

sudo certbot certonly --key-type rsa --standalone -d neo4j.giffard.xyz -v

If everything works ok, you will see something like this

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator standalone, Installer None
Requesting a certificate for neo4j.giffard.xyz
Performing the following challenges:
http-01 challenge for neo4j.giffard.xyz
Waiting for verification...
Cleaning up challenges

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/neo4j.giffard.xyz/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/neo4j.giffard.xyz/privkey.pem
This certificate expires on 2025-02-05.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Keys from CertBot are stored at

  • /etc/letsencrypt/live/neo4j.giffard.xyz/fullchain.pem
  • /etc/letsencrypt/live/neo4j.giffard.xyz/privkey.pem

Adjust inbound security group rules

Now Cerbot has run, you can put the inbound security rules back to allow only traffic from your IP address

Adjust certificates file permissions

We’re going to use symlinks from our folders to the actual certs. If we don’t use symlinks, then we would need to copy the certs to the folders where Neo4j expects them to be. Symlinks avoids this.

The permission change - Make sure all directories and files are ‘others’ readable for letsencrpt folders & files

On the EC2 machine, perform the following at the command prompt

sudo chmod -R o+rx /etc/letsencrypt/ *

Install Neo4j Docker image

Note: If you are not comfortable with the values used for the username & password , change NEO4J_AUTH=neo4j/password to something that works for you.

On the EC2 machine, perform the following at the command prompt

docker run -dt \
    --name=neo4jDb \
    --publish=7474:7474 \
    --publish=7687:7687 \
    --publish=5001:5000 \
    --publish=80:80 \
    --publish=443:443 \
    --volume=$HOME/neo4j/data:/data \
    --volume=$HOME/neo4j/conf:/conf \
    --volume=$HOME/neo4j/logs:/logs \
    --volume=$HOME/neo4j/certificates:/ssl \
    --volume=/etc/letsencrypt:/etc/letsencrypt \
    --env=NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env=NEO4J_AUTH=neo4j/password \
   neo4j:enterprise

Note: For a container to follow a symlink it must have access to the destination. Here it means that we need to attach /etc/letsencrypt on the host to the container. This is why we have –volume=/etc/letsencrypt:/etc/letsencrypt so the container can reach the certs.

Check Neo4j is up

Use your browser to connect to the IP address of the EC machine like so

http://EC2_DNS:7474

This should show you the Browser console for Neo4j. Check you can login with neo4j / password

Switch Neo4j to use SSL certs

Folder structure

Neo4j requires a certain folder structure to be configured for SSL certificates. As mentioned at the beginning, Neo4j allows for SSL to enabled independently for various networking components and they can use different certificates. The structure is

Path Purpose
/certificates/COMPONENT The base directory under which cryptographic objects are searched for by default.
/certificates/COMPONENT/trusted A directory populated with certificates of trusted parties.
/certificates/COMPONENT/revoked A directory populated with certificate revocation lists (CRLs).

Where COMPONENT is one from bolt, https, cluster or backup

On the EC2 machine, perform the following at the command prompt to create the folder structure for bolt and https

sudo mkdir -p ~/neo4j/certificates/https/trusted; sudo mkdir -p ~/neo4j/certificates/https/revoked
sudo mkdir -p ~/neo4j/certificates/bolt/trusted; sudo mkdir -p ~/neo4j/certificates/bolt/revoked

The nice part about our symlink structure 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 are able to automate keeping certificates fresh with a cron job and certbot ( See end of this blog ).

On the EC2 machine, perform the following at the command prompt

   sudo ln -s /etc/letsencrypt/live/neo4j.giffard.xyz/fullchain.pem  ~/neo4j/certificates/bolt/neo4j.cert
   sudo ln -s /etc/letsencrypt/live/neo4j.giffard.xyz/privkey.pem ~/neo4j/certificates/bolt/neo4j.key
   sudo ln -s /etc/letsencrypt/live/neo4j.giffard.xyz/fullchain.pem ~/neo4j/certificates/bolt/trusted/neo4j.cert 
   sudo ln -s /etc/letsencrypt/live/neo4j.giffard.xyz/fullchain.pem  ~/neo4j/certificates/https/neo4j.cert
   sudo ln -s /etc/letsencrypt/live/neo4j.giffard.xyz/privkey.pem ~/neo4j/certificates/https/neo4j.key
   sudo ln -s /etc/letsencrypt/live/neo4j.giffard.xyz/fullchain.pem ~/neo4j/certificates/https/trusted/neo4j.cert

Confirm synmlinks can be followed

The symlinks need to be followed from within the container. We can do this by getting an interactive shell in the container and trying to read the certs

On the EC2 machine, perform the following at the command prompt

docker exec -it neo4jDb /bin/bash
cat /var/lib/neo4j/certificates/bolt/trusted/neo4j.cert
cat /var/lib/neo4j/certificates/https/trusted/neo4j.cert

Enter exit to leave the container interactive shell

After each cat you should see a wall of text that represents the certificate. If you do not

  • Check that the container has –volume=/etc/letsencrypt:/etc/letsencrypt
  • Check the file permissions. Did you grant ‘other’ to fhe file?

Update neo4j.conf file to use certificates

Using your favourite text editor, change the neo4j.conf file located in $HOME/neo4j/conf:/conf on the EC2 machine

# Bolt SSL configuration
dbms.ssl.policy.bolt.enabled=true
dbms.ssl.policy.bolt.base_directory=certificates/bolt
dbms.ssl.policy.bolt.private_key=neo4j.key
dbms.ssl.policy.bolt.public_certificate=neo4j.cert
dbms.ssl.policy.bolt.client_auth=NONE
dbms.ssl.policy.bolt.trusted_dir=trusted
dbms.ssl.policy.bolt.revoked_dir=revoked

# Https SSL configuration
dbms.ssl.policy.https.enabled=true
dbms.ssl.policy.https.base_directory=certificates/https
dbms.ssl.policy.https.private_key=neo4j.key
dbms.ssl.policy.https.public_certificate=neo4j.cert
dbms.ssl.policy.https.client_auth=NONE
dbms.ssl.policy.https.trusted_dir=trusted
dbms.ssl.policy.https.revoked_dir=revoked

Change network connector settings to use SSL for bolt and https with https being accessed on port 443. The use of port 80 for http will be disabled.

#*****************************************************************
# Network connector configuration
#*****************************************************************


# Bolt connector
server.bolt.enabled=true
server.bolt.tls_level=REQUIRED
server.bolt.listen_address=:7687
server.bolt.advertised_address=:7687

# HTTP Connector. There can be zero or one HTTP connectors.
server.http.enabled=false
#server.http.listen_address=:80
#server.http.advertised_address=:80

# HTTPS Connector. There can be zero or one HTTPS connectors.
server.https.enabled=true
server.https.listen_address=:443
server.https.advertised_address=:443

Restart neo4j docker container

For the changes made in neo4j.conf to take effect, the Neo4j container needs to be restarted

On the EC2 machine, type this at a command prompt

docker restart neo4jDb

We now should have a neo4j system running in AWS using SSL

Confirm use of SSL

Use your browser to connect using HTTPS

HTTPS://EC2_DNS/browser

Query API with SSL

This just requires use of HTTPS rather than HTTP in the URL. Using curl on our local machine to connect to the EC2 we can confirm this

Swap out EC2_DNS, YOUR_NEO4j_USERNAME, and YOUR_NEO4j_PASSWORD for your values

curl --location 'https://EC2_DNS/db/neo4j/query/v2' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
-u YOUR_NEO4j_USERNAME: YOUR_NEO4j_PASSWORD\
--data '{ "statement": "MATCH (n) RETURN n LIMIT 1" }'

This should return a single entry from your graph across a HTTPS connection

Renewing LetsEncrypt certificates on a schedule

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 are able to automate keeping certificates fresh with a cron job and certbot.

By running certbot with a renew flag, you can update certificates to push out the expiry date. Certbot will only do this if the certificate is expired.

You can check that this will work by running on the EC2 machine

sudo certbot renew --dry-run

And check that the output has no errors.

For a new cert to work, you will need to restart Neo4j. certbot does allow for this by using –deploy-hook option that enables certbot to run a command. With our example that uses a docker container to run Neo4j , we would create a cron job that runs fairly frequently to check if the certs need renewing that runs certbot like this

sudo certbot renew --deploy-hook "docker restart neo4jDb"

<
Previous Post
Considering a move to the Neo4j Query API - Partie Un
>
Next Post
Build a Neo4j cluster