Table of Contents
MongoDB is so much more flexible and scalable than SQL databases. MongoDB databases commonly exceed terabytes of data. And when any large amount of data is compromised, such breaches are also highly devastating. Generally, in any domain, customers follow many security measures and compliance. MongoDB offer several built-in security features to mitigate security threats and “sniff” sensitive data.
In this article, we are going to implement X.509 authentication with self-signed certificates. MongoDB supports x.509 certificate authentication for use with a secure TLS/SSL connection. The x.509 client authentication allows clients to provide access to servers with certificates rather than with a username and password.
Introduction
Authentication is the process of verifying the identity of a client. When access control, i.e. authorization, is enabled, MongoDB requires all clients to authenticate themselves in order to determine their access.
Other than enterprise features, MongoDB Supports Multiple authentication mechanisms to verify client’s identity.
- SCRAM (Default)
- X.509 Certificate Authentication
I will cover in detail how to implement x.509 authentication to safeguard “data sniffing”. This will help to achieve intra-node encryption as well as secure communication between client and replication cluster.
An X.509 certificate is a digital certificate that uses the widely accepted international X.509 public key infrastructure (PKI) standard to verify that a public key belongs to the user, computer or service identity contained within the certificate.
An X.509 certificate contains information about the identity to which a certificate is issued and the identity that issued it.
Disclaimer:
On production, MongoDB deployment should use valid certificates generated and signed by a single certificate authority. You or your organization can generate and maintain an independent certificate authority, or use certificates generated by a third-party TLS/SSL vendor.
Environment
I have existing MongoDB replication deployment with three nodes running on my AWS EC2 in Linux Environment and to generate my certificate, I am going to use OpenSSL. To encrypt our replication, we need to enable Role-Based Access Control (RBAC).
In my last article, I have covered How to enable basic SCRAM authentication to secure MongoDB Cluster Deployment from BOT attack.
Generate Certificates
We need a following set of certificates to enable X.509 certificate-based encryption authentication.
- 1x certificate for Root CA and 1x certificate for Signing CA
- 3x server certificates, one for each MongoDB member
- 1x client certificate for one client
In order to use encryption, we need to create certificates on all the nodes and have a certification authority (CA) that signs them.
For testing purpose, you can use self-signed certificates. This solution ensures encryption, but for a production environment, it is better to use valid certificates.
To proceed with certificate generation, we need to have OpenSSL installed on our system and certificates need to satisfy these requirements:
- Any certificate needs to be signed by the same CA.
- The common name (CN) required during the certificate creation must correspond to the hostname of the host.
- Any other field requested in the certificate creation should be a non-empty value and, hopefully, should reflect our organization details.
- It is also very important that all the fields, except the CN, should match those from the certificates for the other cluster members.
Let us proceed with certificate generation.
Step: 1 – Generate a new private key using openssl:
openssl genrsa –out ashnik-ca.key -aes256 8192
openssl genrsa -out ashnik-ca.key -aes256 8192
root@server:~# openssl genrsa -out ashnik-ca.key -aes256 8192
Generating RSA private key, 8192-bit long modulus (2 primes)
………………………………………………………………………………………………………..+++
……………………………………………………………………………+++
e is 65537 (0x010001)
Enter pass phrase for ashnik-ca.key:
Verifying – Enter pass phrase for ashnik-ca.key:
We need to enter a strong password and save it securely. Using this ashnik-ca.key and above entered password, we will sign other replica members’ certificates.
So, do remember or note this password securely.
Step: 2 – Sign a new CA certificate
Now we are going to create our “CA” certificate authority. This CA will be used later to sign each node certificate. During this CA certificate creation, some fields must be filled out. It is always better to fill this information in sync with our organization details.
openssl req -x509 –new -extensions v3_ca -key ashnik-ca.key -days 365 –out ashnik-ca.crt
root@server:~# openssl req -x509 -new -extensions v3_ca -key ashnik-ca.key -days 365 -out ashnik-ca.crt
Enter pass phrase for ashnik-ca.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ‘.’, the field will be left blank.
—–
Country Name (2 letter code) [AU]:SG
State or Province Name (full name) [Some-State]:Singapore
Locality Name (eg, city) []:Singapore
Organization Name (eg, company) [xyx Pty Ltd]:Ashnik Pte Ltd
Organizational Unit Name (eg, section) []:Technology
Common Name (e.g., server FQDN or YOUR name) []:ashnik.mongocluster.com
Email Address []: test@ashnik.com
Step: 3 – Issue self-signed certificates for all nodes
For each node, we need to generate a certificate request and sign it using the CA certificate we created in the previous step.
Remember: Fill out all the fields requested the same for each host, but remember to fill out a different common name (CN) that must correspond to the hostname.
For the first node, issue the following commands:
openssl req -nodes -newkey rsa:4096 -sha256 -keyout mongodb1.key -out mongodb1.csr
sudo openssl x509 -CA ashnik-ca.crt -CAkey ashnik-ca.key -CAcreateserial -req -days 365 -in mongodb1.csr -out mongodb1.crt
cat mongodb1.key mongodb1.crt > mongodb1.pem
For the second node, issue the following commands:
openssl req -nodes -newkey rsa:4096 -sha256 -keyout mongodb2.key -out mongodb2.csr
sudo openssl x509 -CA ashnik-ca.crt -CAkey ashnik-ca.key -CAcreateserial -req -days 365 -in mongodb2.csr -out mongodb2.crt
cat mongodb2.key mongodb2.crt > mongodb2.pem
For the third node, issue the following commands:
openssl req -nodes -newkey rsa:4096 -sha256 -keyout mongodb3.key -out mongodb3.csr
sudo openssl x509 -CA ashnik-ca.crt -CAkey ashnik-ca.key -CAcreateserial -req -days 365 -in mongodb3.csr -out mongodb3.crt
cat mongodb3.key mongodb3.crt > mongodb3.pem
Now we want to securely transport the respective node pem file and ashnik-ca.crt to respective replica members.
- Create on each member a directory that only the MongoDB user can read and copy both files there.
- Copy to each node the CA certificate file: ashnik-ca.crt
- Copy each self-signed certificate
.pem into the relative member.
Step: 4 – Configure a Replication-Set to Support SSL
So, in the previous steps, we are completed with all the required certification to communicate our replication nodes securely. Once all the respective files are copied to each node, we need to make the changes in the configuration file.
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1 # Listen to local interface only, comment to listen on all interfaces.
ssl:
mode: requireSSL
PEMKeyFile: /etc/mongodb/ssl/mongodb1.pem
CAFile: /etc/mongodb/ssl/ashnik-ca.crt
clusterFile: /etc/mongodb/ssl/mongodb1.pem
security:
authorization: enabled
clusterAuthMode: x509
Save the config file and restart the mongodb instance. Do this change in the rest of the secondary nodes as well. This will ensure that internal communication between replica nodes are enabled authentication with SSL certificates.
1. $ sudo systemctl restart mongod
There is now connections between servers use TLS/SSL. For incoming connections, the server accepts both TLS/SSL.
Step: 5 – Validate clusterAuthMode: x509
After restarting the mongod process, we can validate the authentication process on the mongod logs. The error log should look like this:
2021-05-10T01:17:55.984+0000 I ACCESS [conn4] authenticate db: $external { authenticate: 1, user: “emailAddress= nagarajan.p@ashnik.com,CN=node1-mongocluster.ashnik-demo.com,OU= Technology,O= Ashnik Pte Ltd,L= Singapore,ST= Singapore,C= SG”, mechanism: “MONGODB-X509” }
2021-05-10T01:17:55.984+0000 I ACCESS [conn4] Successfully authenticated as principal emailAddress= nagarajan.p@ashnik.com,CN=node2-mongocluster.ashnik-demo.com,OU= Technology,O= Ashnik Pte Ltd,L= Singapore,ST= Singapore,C=SG on $external from client
2021-05-10T01:17:55.984+0000 I ACCESS [conn4] Successfully authenticated as principal emailAddress= nagarajan.p@ashnik.com,CN=node3-mongocluster.ashnik-demo.com,OU= Technology,O= Ashnik Pte Ltd,L= Singapore,ST= Singapore,C=SG on $external from client
Step: 6 – Connect with Nodes using custerAuthMode: x509
sudo mongo node1-mongocluster.ashnik-demo.com:27017 –authenticationDatabase “admin” -u “admin” -p XXXXXXX –ssl –sslCAFile “/etc/mongodb/ssl/ashnik-ca.crt” –sslPEMKeyFile “/etc/mongodb/ssl/mongodb1.pem”
Step: 7 – Create a self-signed certificate for mongo client/driver
Let’s create a certificate for Application Team and DBA Team to access our MongoDB deployment without using username and password.
Important things to remember for Client Certificate:
- Client certificates need to be signed by the same CA who signed all replica member certificate.
- Common name filed can enter wildcard name (*.example.com) or we can use the SAN field which allows multiple name entries on a single certificate.
- we need to specify a different value for O or OU filed compare to the replica set member certificate.
This one certificate helps to connect all the replica set members. So, the common name must be a Wildcard name (*.example.com) or need to specify individual FQDN of each member of the replica set in Subject Alternative Name(SAN) field.
Create a client.conf file with the following content:
distinguished_name = req_distinguished_name
req_extensions = v3_req
default_keyfile = app-client.key
prompt = no
[req_distinguished_name]
C = SG
ST = Singapore
L = Singapore
O = Ashnik Pte Ltd
OU = Technologies-Client
CN = root
emailAddress = nagarajan.p@ashnik.com
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = node1-mongocluster.ashnik-demo.com
DNS.2 = node2-mongocluster.ashnik-demo.com
DNS.3 = node3-mongocluster.ashnik-demo.com
Once client.conf file has been created, create CSR file and private key to be used with a certificate with the following command:
openssl req –new -nodes –out app-client.csr -config client.conf
Sign the client CSR using CA public and private key
openssl x509 -CA ashnik-ca.crt –CAkey ashnik-ca.key –CAcreateserial -req -days 365 –in app-client.csr –out app-client.crt
Concatenate the key and the signed certificate.
cat app-client.key app-client.crt > app-client.pempp-client.crt
Copy “app-client.pem” to the client machine.
Validate certificate for username: We need this to create external user.
openssl x509 -in /apps/app-client.pem -inform PEM -subject -nameopt RFC2253
Copy only the First line from the Certificate:
emailAddress=nagarajan.p@ashnik.com,CN=root,OU=Technologies-Client,O=Ashnik Pte Ltd,L=Singapore,ST=Singapore,C=SG
Create user with above details on External Database:
MongoDB Enterprise ashnik-demo:PRIMARY>db.getSiblingDB(“$external”).runCommand({createUser: “emailAddress=nagarajan.p@ashnik.com,CN=root,OU=Technologies-Client,O=Ashnik Pte Ltd,L=Singapore,ST=Singapore,C=SG”,roles: [{role: “root”, db: “admin” }] })Validate user details:
MongoDB Enterprise ashnik-demo:PRIMARY> use $external
switched to db $external
MongoDB Enterprise ashnik-demo:PRIMARY> db.getUsers()
{
“_id” : “$external.emailAddress=nagarajan.p@ashnik.com,CN=root,OU=Technologies-Client,O=Ashnik Pte Ltd,L=Singapore,ST=Singapore,C=SG”,
“userId” : UUID(“ed83392e-aabf-488e-bf3e-df7b7e52e4ca”),
“user” : “emailAddress=nagarajan.p@ashnik.com,CN=root,OU=Technologies-Client,O=Ashnik Pte Ltd,L=Singapore,ST=Singapore,C=SG”,
“db” : “$external”,
“roles” : [
{
“role” : “root”,
“db” : “admin”
}
],
“mechanisms” : [
“external”
]
}
]
MongoDB Enterprise ashnik-demo:PRIMARY>
Now we can connect with x.509 certificates without any username and password:
[root@node1 apps]# mongo ec2-34–231–251–148.compute-1.amazonaws.com:27017 –ssl –sslCAFile “/apps/mongoCA.crt” –sslPEMKeyFile “/apps/app-client.pem”
With above command will trigger only the connectivity part. We still need to authenticate out external user to perform admin tasks:
To authenticate ,use db.auth() method on our external database:
MongoDB Enterprise ashnik-demo:PRIMARY> db.getSiblingDB(“$external”).auth( { mechanism: “MONGODB-X509”, user: “emailAddress=nagarajan.p@ashnik.com,CN=root,OU=Technologies-Client,O=Ashnik Pte Ltd,L=Singapore,ST=Singapore,C=SG” })
Once authenticated, validate with admin tasks:
MongoDB Enterprise ashnik-demo:PRIMARY> use admin
switched to db admin
MongoDB Enterprise ashnik-demo:PRIMARY> show dbs
admin 0.000GB
config 0.000GB
local 0.001GB
Let us try with some MongoDB admin commands to validate if everything is working fine:
- Syntax to connect MongoDB Replication
mongo –authenticationDatabase=‘$external’ –authenticationMechanism MONGODB-X509 –host ashnik-demo/node1-mongocluster.ashnik-demo.com:27017 node2-mongocluster.ashnik-demo.com:27017, node3-mongocluster.ashnik-demo.com:27017 –ssl –sslCAFile /etc/mongodb/ssl/mongoCA.crt –sslPEMKeyFile /apps/app-client.pem
- Syntax to take mongodump
mongodump –authenticationDatabase=‘$external’ –authenticationMechanism MONGODB-X509 –host ashnik-demo/node1-mongocluster.ashnik-demo.com:27017,node2-mongocluster.ashnik-demo.com:27017,node3-mongocluster.ashnik-demo.com:27017 –ssl –sslCAFile /apps/ashnik-ca.crt –sslPEMKeyFile /apps/app-client.pem —out /apps/dump -u ” emailAddress=nagarajan.p@ashnik.com,CN=root,OU=Technologies-Client,O=Ashnik Pte Ltd,L=Singapore,ST=Singapore,C=SG”
- mongotop
mongotop –authenticationDatabase=‘$external’ –authenticationMechanism MONGODB-X509 –host ashnik-demo/node1-mongocluster.ashnik-demo.com:27017,node2-mongocluster.ashnik-demo.com:27017,node3-mongocluster.ashnik-demo.com:27017 –ssl –sslCAFile /apps/ashnik-ca.crt –sslPEMKeyFile /apps/app-client.pem -u “emailAddress=nagarajan.p@ashnik.com,CN=root,OU=Technologies-Client,O=Ashnik Pte Ltd,L=Singapore,ST=Singapore,C=SG”
- mongostat
mongostat –authenticationDatabase=‘$external’ –authenticationMechanism MONGODB-X509 –host ashnik-demo/node1-mongocluster.ashnik-demo.com:27017,node2-mongocluster.ashnik-demo.com:27017,node3-mongocluster.ashnik-demo.com:27017 –ssl –sslCAFile /apps/ashnik-ca.crt –sslPEMKeyFile /apps/app-client.pem -u “emailAddress=nagarajan.p@ashnik.com,CN=root,OU=Technologies-Client,O=Ashnik Pte Ltd,L=Singapore,ST=Singapore,C=SG “
Conclusion
Securing your database is essential before deploying it into production systems. X.509 is one of the multiple authentication mechanisms supported by MongoDB. For production use, your MongoDB deployment should use valid certificates generated and signed by a single certificate authority. Organization can generate and maintain an independent certificate authority, or use certificates generated by a third-party TLS/SSL vendor.