Mutual TLS with your own CA

TeselaDB

TeselaDB

TeselaDB team

40 minAdvanced

Use your CA's certificates on the client port and authenticate an application with its certificate, without a password.

TeselaDB creates its own CA on the first start and uses it between nodes. On the client port you can present your CA's certificates and ask each application for its client certificate, which also authenticates it with MONGODB-X509.

What you will build

A test CA, a server certificate for node-01 and a client certificate for the shop-api application. The node presents its own to clients. The application connects with its certificate, without a password.

Requirements

  • OpenSSL 3 on the machine where you generate the certificates.
  • A TeselaDB node and access to its start options.
  • A user allowed to create users, such as root.

If your company already has a CA, use its certificates and skip steps 1 to 3.

1. Create the CA

The CA key signs every certificate. Keep it off the nodes:

Shell
openssl req -x509 -newkey rsa:3072 -nodes -keyout ca.key -out ca.pem -days 3650 -subj "/CN=Example CA"

2. Create the server certificate

The node's name goes in subjectAltName, which is what clients check, and the last command joins the certificate and its key in server.pem, because the node reads both from a single PEM file:

Shell
printf "subjectAltName=DNS:node-01\n" > server.ext
openssl req -newkey rsa:3072 -nodes -keyout server.key -out server.csr -subj "/CN=node-01"
openssl x509 -req -in server.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out server.crt -days 825 -extfile server.ext
cat server.crt server.key > server.pem

3. Create the application certificate

The certificate's subject is the name of the user you will create in $external in step 5. The commands below sign the certificate with your CA and join it with its key in shop-api.pem:

Shell
printf "extendedKeyUsage=clientAuth\n" > client.ext
openssl req -newkey rsa:3072 -nodes -keyout shop-api.key -out shop-api.csr -subj "/O=Example/OU=apps/CN=shop-api"
openssl x509 -req -in shop-api.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out shop-api.crt -days 825 -extfile client.ext
cat shop-api.crt shop-api.key > shop-api.pem
openssl x509 -in shop-api.crt -noout -subject -nameopt RFC2253

The last command prints the subject in the order the node expects, CN=shop-api,OU=apps,O=Example.

4. Start the node with your certificates

Copy server.pem and ca.pem to /etc/teseladb on the node and start it with MongoDB's TLS options. On the client port, your certificates then take the place of those of the CA that TeselaDB created on the first start:

Shell
teseladb start --dbpath /var/lib/teseladb --tlsCertificateKeyFile /etc/teseladb/server.pem \
  --tlsCAFile /etc/teseladb/ca.pem --tlsAllowConnectionsWithoutCertificates

With --tlsCAFile the node asks clients for a certificate, and --tlsAllowConnectionsWithoutCertificates also lets in those that have none, such as root with its password. Remove it once all your applications use certificates.

5. Create the certificate's user

Connected as root, create the user in $external with the exact subject of the certificate:

JavaScript
db.getSiblingDB("$external").runCommand({
  createUser: "CN=shop-api,OU=apps,O=Example",
  roles: [{ role: "readWrite", db: "shop" }],
});

Try it

Connect with the application's certificate and ask which user you logged in as. The connection string carries no user and no password, because with authMechanism=MONGODB-X509 the node takes the user from the subject of the certificate:

Shell
mongosh "mongodb://node-01:27017/?tls=true&tlsCAFile=./ca.pem&tlsCertificateKeyFile=./shop-api.pem&authMechanism=MONGODB-X509" \
  --eval "db.runCommand({ connectionStatus: 1 }).authInfo.authenticatedUsers"

The answer shows CN=shop-api,OU=apps,O=Example in the $external database, and that connection can read and write in shop.

Clean up

If it was a test, drop the user with db.getSiblingDB("$external").dropUser("CN=shop-api,OU=apps,O=Example") and start the node again without your own TLS options. Keep ca.key off the node machines or delete it.

Next steps