Chapter 19. An Introduction to MongoDB Security

To protect your MongoDB cluster and the data it holds, you will want to employ the following security measures:

  • Enable authorization and enforce authentication

  • Encrypt communication

  • Encrypt data

This chapter demonstrates how to address the first two security measures with a tutorial on using MongoDB’s support for x.509 to configure authentication and transport layer encryption to ensure secure communications among clients and servers in a MongoDB replica set. We will touch on encrypting data at the storage layer in a later chapter.

MongoDB Authentication and Authorization

While authentication and authorization are closely connected, it is important to note that authentication is distinct from authorization. The purpose of authentication is to verify the identity of a user, while authorization determines the verified user’s access to resources and operations.

Authentication Mechanisms

Enabling authorization on a MongoDB cluster enforces authentication and ensures users can only perform actions they are authorized for, as determined by their roles. The Community version of MongoDB provides support for SCRAM (Salted Challenge Response Authentication Mechanism) and x.509 certificate authentication. In addition to SCRAM and x.509, MongoDB Enterprise supports Kerberos authentication and LDAP proxy authentication. See the documentation for details on the various authentication mechanisms that MongoDB supports. In this chapter, we will focus on x.509 authentication. An x.509 digital certificate uses the widely accepted x.509 public key infrastructure (PKI) standard to verify that a public key belongs to the presenter.

Authorization

When adding a user in MongoDB, you must create the user in a specific database. That database is the authentication database for the user; you can use any database for this purpose. The username and authentication database serves as a unique identifier for a user. However, a user’s privileges are not limited to their authentication database. When creating a user, you can specify the operations the user may perform on any resources to which they should have access. Resources include the cluster, databases, and collections.

MongoDB provides a number of built-in roles that grant commonly needed permissions for database users. These include the following:

read

Read data on all nonsystem collections and on the following system collections: system.indexes, system.js, and system.namespaces.

readWrite

Provides same privileges as read, plus the ability to modify data on all nonsystem collections and the system.js collection.

dbAdmin

Perform administrative tasks such as schema-related tasks, indexing, and gathering statistics (does not grant privileges for user and role management).

userAdmin

Create and modify roles and users on the current database.

dbOwner

Combines the privileges granted by the readWrite, dbAdmin, and userAdmin roles.

clusterManager

Perform management and monitoring actions on the cluster.

clusterMonitor

Provides read-only access to monitoring tools such as the MongoDB Cloud Manager and Ops Manager monitoring agent.

hostManager

Monitor and manage servers.

clusterAdmin

Combines the privileges granted by the clusterManager, clusterMonitor, and hostManager roles, plus the dropDatabase action.

backup

Provides sufficient privileges to use the MongoDB Cloud Manager backup agent or the Ops Manager backup agent, or to use mongodump to back up an entire mongod instance.

restore

Provides privileges needed to restore data from backups that do not include system.profile collection data.

readAnyDatabase

Provides same privileges as read on all databases except local and config, plus the listDatabases action on the cluster as a whole.

readWriteAnyDatabase

Provides same privileges as readWrite on all databases except local and config, plus the listDatabases action on the cluster as a whole.

userAdminAnyDatabase

Provides same privileges as userAdmin on all databases except local and config (effectively a superuser role).

dbAdminAnyDatabase

Provides same privileges as dbAdmin on all databases except local and config, plus the listDatabases action on the cluster as a whole.

root

Provides access to the operations and all the resources of the readWriteAnyDatabase, dbAdminAnyDatabase, userAdminAnyDatabase, clusterAdmin, restore, and backup roles combined.

You may also create what are known as “user-defined roles,” which are custom roles that group together authorization to perform specific operations and label them with a name so that you may grant this set of permissions to multiple users easily.

A deep dive on built-in roles or user-defined roles is beyond the scope of this chapter. However, this introduction should give you a pretty good idea of what’s possible with MongoDB authorization. For greater detail, please see the authorization section of the MongoDB documentation.

To ensure that you can add new users as needed, you must first create an admin user. MongoDB does not create a default root or admin user when enabling authentication and authorization, regardless of the authentication mode you are using (x.509 is no exception).

In MongoDB, authentication and authorization are not enabled by default. You must explicitly enable them by using the --auth option to the mongod command or specifying a value of "enabled" for the security.authorization setting in a MongoDB config file.

To configure a replica set, first bring it up without authentication and authorization enabled, then create the admin user and the users you’ll need for each client.

Using x.509 Certificates to Authenticate Both Members and Clients

Given that all production MongoDB clusters are composed of multiple members, to secure a cluster, it is essential that all services communicating within the cluster authenticate with one another. Each member of a replica set must authenticate with the others in order to exchange data. Likewise, clients must authenticate with the primary and any secondaries that they communicate with.

For x.509, it’s necessary that a trusted certification authority (CA) sign all certificates. Signing certifies that the named subject of a certificate owns the public key associated with that certificate. A CA acts as a trusted third party to prevent man-in-the-middle attacks.

Figure 19-1 depicts x.509 authentication used to secure a three-member MongoDB replica set. Note the authentication among the client and members of the replica set and the trust relationships with the CA.

Figure 19-1. Overview of the trust hierarchy for X.509 authentication for the three-member replica set used in this chapter

The members and the client each have their own certificate signed by the CA. For production use, your 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 you can use certificates generated by a third-party TLS/SSL vendor.

We will refer to certificates used for internal authentication to verify membership in a cluster as member certificates. Both member certificates and client certificates (used to authenticate clients) have a structure resembling the following:

Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 1 (0x1)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=NY, L=New York, O=MongoDB, CN=CA-SIGNER
        Validity
            Not Before: Nov 11 22:00:03 2018 GMT
            Not After : Nov 11 22:00:03 2019 GMT
        Subject: C=US, ST=NY, L=New York, O=MongoDB, OU=MyServers, CN=server1
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:d3:1c:29:ba:3d:29:44:3b:2b:75:60:95:c8:83:
                    fc:32:1a:fa:29:5c:56:f3:b3:66:88:7f:f9:f9:89:
                    ff:c2:51:b9:ca:1d:4c:d8:b8:5a:fd:76:f5:d3:c9:
                    95:9c:74:52:e9:8d:5f:2e:6b:ca:f8:6a:16:17:98:
                    dc:aa:bf:34:d0:44:33:33:f3:9d:4b:7e:dd:7a:19:
                    1b:eb:3b:9e:21:d9:d9:ba:01:9c:8b:16:86:a3:52:
                    a3:e6:e4:5c:f7:0c:ab:7a:1a:be:c6:42:d3:a6:01:
                    8e:0a:57:b2:cd:5b:28:ee:9d:f5:76:ca:75:7a:c1:
                    7c:42:d1:2a:7f:17:fe:69:17:49:91:4b:ca:2e:39:
                    b4:a5:e0:03:bf:64:86:ca:15:c7:b2:f7:54:00:f7:
                    02:fe:cf:3e:12:6b:28:58:1c:35:68:86:3f:63:46:
                    75:f1:fe:ac:1b:41:91:4f:f2:24:99:54:f2:ed:5b:
                    fd:01:98:65:ac:7a:7a:57:2f:a8:a5:5a:85:72:a6:
                    9e:fb:44:fb:3b:1c:79:88:3f:60:85:dd:d1:5c:1c:
                    db:62:8c:6a:f7:da:ab:2e:76:ac:af:6d:7d:b1:46:
                    69:c1:59:db:c6:fb:6f:e1:a3:21:0c:5f:2e:8e:a7:
                    d5:73:87:3e:60:26:75:eb:6f:10:c2:64:1d:a6:19:
                    f3:0b
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         5d:dd:b2:35:be:27:c2:41:4a:0d:c7:8c:c9:22:05:cd:eb:88:
         9d:71:4f:28:c1:79:71:3c:6d:30:19:f4:9c:3d:48:3a:84:d0:
         19:00:b1:ec:a9:11:02:c9:a6:9c:74:e7:4e:3c:3a:9f:23:30:
         50:5a:d2:47:53:65:06:a7:22:0b:59:71:b0:47:61:62:89:3d:
         cf:c6:d8:b3:d9:cc:70:20:35:bf:5a:2d:14:51:79:4b:7c:00:
         30:39:2d:1d:af:2c:f3:32:fe:c2:c6:a5:b8:93:44:fa:7f:08:
         85:f0:01:31:29:00:d4:be:75:7e:0d:f9:1a:f5:e9:75:00:9a:
         7b:d0:eb:80:b1:01:00:c0:66:f8:c9:f0:35:6e:13:80:70:08:
         5b:95:53:4b:34:ec:48:e3:02:88:5c:cd:a0:6c:b4:bc:65:15:
         4d:c8:41:9d:00:f5:e7:f2:d7:f5:67:4a:32:82:2a:04:ae:d7:
         25:31:0f:34:e8:63:a5:93:f2:b5:5a:90:71:ed:77:2a:a6:15:
         eb:fc:c3:ac:ef:55:25:d1:a1:31:7a:2c:80:e3:42:c2:b3:7d:
         5e:9a:fc:e4:73:a8:39:50:62:db:b1:85:aa:06:1f:42:27:25:
         4b:24:cf:d0:40:ca:51:13:94:97:7f:65:3e:ed:d9:3a:67:08:
         79:64:a1:ba
-----BEGIN CERTIFICATE-----
MIIDODCCAiACAQEwDQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCQ04xCzAJBgNV
BAgMAkdEMREwDwYDVQQHDAhTaGVuemhlbjEWMBQGA1UECgwNTW9uZ29EQiBDaGlu
YTESMBAGA1UEAwwJQ0EtU0lHTkVSMB4XDTE4MTExMTIyMDAwM1oXDTE5MTExMTIy
MDAwM1owazELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkdEMREwDwYDVQQHDAhTaGVu
emhlbjEWMBQGA1UECgwNTW9uZ29EQiBDaGluYTESMBAGA1UECwwJTXlTZXJ2ZXJz
MRAwDgYDVQQDDAdzZXJ2ZXIxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA0xwpuj0pRDsrdWCVyIP8Mhr6KVxW87NmiH/5+Yn/wlG5yh1M2Lha/Xb108mV
nHRS6Y1fLmvK+GoWF5jcqr800EQzM/OdS37dehkb6zueIdnZugGcixaGo1Kj5uRc
9wyrehq+xkLTpgGOCleyzVso7p31dsp1esF8QtEqfxf+aRdJkUvKLjm0peADv2SG
yhXHsvdUAPcC/s8+EmsoWBw1aIY/Y0Z18f6sG0GRT/IkmVTy7Vv9AZhlrHp6Vy+o
pVqFcqae+0T7Oxx5iD9ghd3RXBzbYoxq99qrLnasr219sUZpwVnbxvtv4aMhDF8u
jqfVc4c+YCZ1628QwmQdphnzCwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBd3bI1
vifCQUoNx4zJIgXN64idcU8owXlxPG0wGfScPUg6hNAZALHsqRECyaacdOdOPDqf
IzBQWtJHU2UGpyILWXGwR2FiiT3Pxtiz2cxwIDW/Wi0UUXlLfAAwOS0dryzzMv7C
xqW4k0T6fwiF8AExKQDUvnV+Dfka9el1AJp70OuAsQEAwGb4yfA1bhOAcAhblVNL
NOxI4wKIXM2gbLS8ZRVNyEGdAPXn8tf1Z0oygioErtclMQ806GOlk/K1WpBx7Xcq
phXr/MOs71Ul0aExeiyA40LCs31emvzkc6g5UGLbsYWqBh9CJyVLJM/QQMpRE5SX
f2U+7dk6Zwh5ZKG6
-----END CERTIFICATE-----

For use with x.509 authentication in MongoDB, member certificates must have the following properties:

  • A single CA must issue all x.509 certificates for the members of the cluster.

  • The Distinguished Name (DN), found in the subject of the member certificate, must specify a nonempty value for at least one of the following attributes: Organization (O), Organizational Unit (OU), or Domain Component (DC).

  • The O, OU, and DC attributes must match those from the certificates for the other cluster members.

  • The Common Name (CN) or a Subject Alternative Name (SAN) must match the hostname of the server used by the other members of the cluster.

A Tutorial on MongoDB Authentication and Transport Layer Encryption

In this tutorial we will set up a root CA and an intermediate CA. Best practice recommends signing the server and client certificates with the intermediate CA.

Establish a CA

Before we can generate signed certificates for the members of our replica set, we must first address the issue of a certificate authority. As mentioned previously, we can either generate and maintain an independent certificate authority or use certificates generated by a third-party TLS/SSL vendor. We will generate our own CA to use for the running example in this chapter. Note that you may access all the code examples in this chapter from the GitHub repository maintained for this book. The examples are drawn from a script you can use to deploy a secure replica set. You’ll see comments from this script throughout these examples.

Generate a root CA

To generate our CA, we will use OpenSSL. To follow along, please make sure you have access to OpenSSL on your local machine.

A root CA is at the top of the certificate chain. This is the ultimate source of trust. Ideally, a third-party CA should be used. However, in the case of an isolated network (typical in a large enterprise environment) or for testing purposes, you’ll need to use a local CA.

First, we’ll initialize some variables:

dn_prefix="/C=US/ST=NY/L=New York/O=MongoDB"
ou_member="MyServers"
ou_client="MyClients"
mongodb_server_hosts=( "server1" "server2" "server3" )
mongodb_client_hosts=( "client1" "client2" )
mongodb_port=27017

Then, we’ll create a key pair and store it in the file root-ca.key:

# !!! In production you will want to password-protect the keys
# openssl genrsa -aes256 -out root-ca.key 4096
openssl genrsa -out root-ca.key 4096

Next, we’ll create a configuration file to hold our OpenSSL settings that we will use to generate the certificates:

# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

[ req ]
default_bits        = 4096
default_keyfile     = server-key.pem
default_md      = sha256
distinguished_name  = req_dn
req_extensions = v3_req
x509_extensions = v3_ca # The extensions to add to the self-signed cert

[ v3_req ]
subjectKeyIdentifier  = hash
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
nsComment = "OpenSSL Generated Certificate"
extendedKeyUsage  = serverAuth, clientAuth

[ req_dn ]
countryName = Country Name (2-letter code)
countryName_default = US
countryName_min = 2
countryName_max = 2

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = NY
stateOrProvinceName_max = 64

localityName = Locality Name (eg, city)
localityName_default = New York
localityName_max = 64

organizationName = Organization Name (eg, company)
organizationName_default = MongoDB
organizationName_max = 64

organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = Education
organizationalUnitName_max = 64

commonName = Common Name (eg, YOUR name)
commonName_max = 64

[ v3_ca ]
# Extensions for a typical CA

subjectKeyIdentifier = hash
basicConstraints = critical,CA:true
authorityKeyIdentifier = keyid:always,issuer:always

# Key usage: this is typical for a CA certificate. However, since it will
# prevent it being used as a test self-signed certificate it is best
# left out by default.
keyUsage = critical,keyCertSign,cRLSign

Then, using the openssl req command, we will create the root certificate. Since the root is the very top of the authority chain, we’ll self-sign this certificate using the private key we created in the previous step (stored in root-ca.key). The -x509 option tells the openssl req command we want to self-sign the certificate using the private key supplied to the -key option. The output is a file called root-ca.crt:

openssl req -new -x509 -days 1826 -key root-ca.key -out root-ca.crt 
  -config openssl.cnf -subj "$dn_prefix/CN=ROOTCA"

If you take a look at the root-ca.crt file, you’ll find that it contains the public certificate for the root CA. You can verify the contents by taking a look at a human-readable version of the certificate produced by this command:

openssl x509 -noout -text -in root-ca.crt

The output from this command will resemble the following:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            1e:83:0d:9d:43:75:7c:2b:d6:2a:dc:7e:a2:a2:25:af:5d:3b:89:43
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, ST = NY, L = New York, O = MongoDB, CN = ROOTCA
        Validity
            Not Before: Sep 11 21:17:24 2019 GMT
            Not After : Sep 10 21:17:24 2024 GMT
        Subject: C = US, ST = NY, L = New York, O = MongoDB, CN = ROOTCA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (4096 bit)
                Modulus:
                    00:e3:de:05:ae:ba:c9:e0:3f:98:37:18:77:02:35:
                    e7:f6:62:bc:c3:ae:38:81:8d:04:88:da:6c:e0:57:
                    c2:90:86:05:56:7b:d2:74:23:54:f8:ca:02:45:0f:
                    38:e7:e2:0b:69:ea:f6:c8:13:8f:6c:2d:d6:c1:72:
                    64:17:83:4e:68:47:cf:de:37:ed:6e:38:b2:ab:3a:
                    e4:45:a8:fa:08:90:a0:f3:0d:3a:14:d8:9a:8d:69:
                    e7:cf:93:1a:71:53:4f:13:29:50:b0:2f:b6:b8:19:
                    2a:40:21:15:90:43:e7:d8:d8:f3:51:e5:95:58:87:
                    6c:45:9f:61:fc:b5:97:cf:5b:4e:4a:1f:72:c9:0c:
                    e9:8c:4c:d1:ca:df:b3:a4:da:b4:10:83:81:01:b1:
                    c8:09:22:76:c7:1e:96:c7:e6:56:27:8d:bc:fb:17:
                    ed:d9:23:3f:df:9c:ef:03:20:cc:c3:c4:55:cc:9f:
                    ad:d4:8d:81:95:c3:f1:87:f8:d4:5a:5e:e0:a8:41:
                    27:c8:0d:52:91:e4:2b:db:25:d6:b7:93:8d:82:33:
                    7a:a7:b8:e8:cd:a8:e2:94:3d:d6:16:e1:4e:13:63:
                    3f:77:08:10:cf:23:f6:15:7c:71:24:97:ef:1c:a2:
                    68:0f:82:e2:f7:24:b3:aa:70:1a:4a:b4:ca:4d:05:
                    92:5e:47:a2:3d:97:82:f6:d8:c8:04:a7:91:6c:a4:
                    7d:15:8e:a8:57:70:5d:50:1c:0b:36:ba:78:28:f2:
                    da:5c:ed:4b:ea:60:8c:39:e6:a1:04:26:60:b3:e2:
                    ee:4f:9b:f9:46:3c:7e:df:82:88:29:c2:76:3e:1a:
                    a4:81:87:1f:ce:9e:41:68:de:6c:f3:89:df:ae:02:
                    e7:12:ee:93:20:f1:d2:d6:3d:36:58:ee:71:bf:b3:
                    c5:e7:5a:4b:a0:12:89:ed:f7:cc:ec:34:c7:b2:28:
                    a8:1a:87:c6:8b:5e:d2:c8:25:71:ba:ff:d0:82:1b:
                    5e:50:a9:8a:c6:0c:ea:4b:17:a6:cc:13:0a:53:36:
                    c6:9d:76:f2:95:cc:ac:b9:64:d5:72:fc:ab:ce:6b:
                    59:b1:3a:f2:49:2f:2c:09:d0:01:06:e4:f2:49:85:
                    79:82:e8:c8:bb:1a:ab:70:e3:49:97:9f:84:e0:96:
                    c2:6d:41:ab:59:0c:2e:70:9a:2e:11:c8:83:69:4b:
                    f1:19:97:87:c3:76:0e:bb:b0:2c:92:4a:07:03:6f:
                    57:bf:a9:ec:19:85:d6:3d:f8:de:03:7f:1b:9a:2f:
                    6c:02:72:28:b0:69:d5:f9:fb:3d:2e:31:8f:61:50:
                    59:a6:dd:43:4b:89:e9:68:4b:a6:0d:9b:00:0f:9a:
                    94:61:71
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                8B:D6:F8:BD:B7:82:FC:13:BC:61:3F:8B:FA:84:24:3F:A2:14:C8:27
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Authority Key Identifier:
                keyid:8B:D6:F8:BD:B7:82:FC:13:BC:61:3F:8B:FA:84:24:3F:A2:14:C8:27
                DirName:/C=US/ST=NY/L=New York/O=MongoDB/CN=ROOTCA
                serial:1E:83:0D:9D:43:75:7C:2B:D6:2A:DC:7E:A2:A2:25:AF:5D:3B:89:43

            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
    Signature Algorithm: sha256WithRSAEncryption
         c2:cc:79:40:8b:7b:a1:87:3a:ec:4a:71:9d:ab:69:00:bb:6f:
         56:0a:25:3b:8f:bd:ca:4d:4b:c5:27:28:3c:7c:e5:cf:84:ec:
         2e:2f:0d:37:35:52:6d:f9:4b:07:fb:9b:da:ea:5b:31:0f:29:
         1f:3c:89:6a:10:8e:ae:20:30:8f:a0:cf:f1:0f:41:99:6a:12:
         5f:5c:ce:15:d5:f1:c9:0e:24:c4:81:70:df:ad:a0:e1:0a:cc:
         52:d4:3e:44:0b:61:48:a9:26:3c:a3:3d:2a:c3:ca:4f:19:60:
         da:f7:7a:4a:09:9e:26:42:50:05:f8:74:13:4b:0c:78:f1:59:
         39:1e:eb:2e:e1:e2:6c:cc:4d:96:95:79:c2:8b:58:41:e8:7a:
         e6:ad:37:e4:87:d7:ed:bb:7d:fa:47:dd:46:dd:e7:62:5f:e9:
         fe:17:4b:e3:7a:0e:a1:c5:80:78:39:b7:6c:a6:85:cf:ba:95:
         d2:8d:09:ab:2d:cb:be:77:9b:3c:22:12:ca:12:86:42:d8:c5:
         3c:31:a0:ed:92:bc:7f:3f:91:2d:ec:db:01:bd:26:65:56:12:
         a3:56:ba:d8:d3:6e:f3:c3:13:84:98:2a:c7:b3:22:05:68:fa:
         8e:48:6f:36:8e:3f:e5:4d:88:ef:15:26:4c:b1:d3:7e:25:84:
         8c:bd:5b:d2:74:55:cb:b3:fa:45:3f:ee:ef:e6:80:e9:f7:7f:
         25:a6:6e:f2:c4:22:f7:b8:40:29:02:f1:5e:ea:8e:df:80:e0:
         60:f1:e5:3a:08:81:25:d5:cc:00:8f:5c:ac:a6:02:da:27:c0:
         cc:4e:d3:f3:14:60:c1:12:3b:21:b4:f7:29:9b:4c:34:39:3c:
         2a:d1:4b:86:cc:c7:de:f3:f7:5e:8f:9d:47:2e:3d:fe:e3:49:
         70:0e:1c:61:1c:45:a0:5b:d6:48:49:be:6d:f9:3c:49:26:d8:
         8b:e6:a1:b2:61:10:fe:0c:e8:44:2c:33:cd:3c:1d:c2:de:c2:
         06:98:7c:92:7b:c4:06:a5:1f:02:8a:03:53:ec:bd:b7:fc:31:
         f3:2a:c1:0e:6a:a5:a8:e4:ea:4d:cc:1d:07:a9:3f:f6:0e:35:
         5d:99:31:35:b3:43:90:f3:1c:92:8e:99:15:13:2b:8f:f6:a6:
         01:c9:18:05:15:2a:e3:d0:cc:45:66:d3:48:11:a2:b9:b1:20:
         59:42:f7:88:15:9f:e0:0c:1d:13:ae:db:09:3d:bf:7a:9d:cf:
         b2:41:1e:7a:fa:6b:35:20:03:58:a1:6c:02:19:21:5f:25:fc:
         ba:2f:fc:79:d7:92:e7:37:77:14:10:d9:33:b6:e5:fb:7a:46:
         ab:d1:86:70:88:92:59:c3

Create an intermediate CA for signing

Now that we’ve created our root CA, we will create an intermediate CA for signing member and client certificates. An intermediate CA is nothing more than a certificate signed using our root certificate. It is a best practice to use an intermediate CA to sign server (i.e., member) and client certificates. Typically, a CA will use different intermediate CAs for signing different categories of certificates. If the intermediate CA is compromised and the certificate needs to be revoked, only a portion of the trust tree is affected instead of all certificates signed by the CA, as would be the case if the root CA were used to sign all certificates.

# again, in production you would want to password protect your signing key:
# openssl genrsa -aes256 -out signing-ca.key 4096
openssl genrsa -out signing-ca.key 4096

openssl req -new -key signing-ca.key -out signing-ca.csr 
  -config openssl.cnf -subj "$dn_prefix/CN=CA-SIGNER"
openssl x509 -req -days 730 -in signing-ca.csr -CA root-ca.crt -CAkey 
  root-ca.key -set_serial 01 -out signing-ca.crt -extfile openssl.cnf 
  -extensions v3_ca

Note that in the statements above we are using the openssl req command followed by the openssl ca command to sign our signing certificate using our root certificate. The openssl req command creates a signing request and the openssl ca command uses that request as input to create a signed intermediate (signing) certificate.

As a last step in creating our signing CA, we will concatenate our root certificate (containing our root public key) and signing certificate (containing our signing public key) into a single pem file. This file will be supplied to our mongod or client process later as the value of the --tlsCAFile option.

cat root-ca.crt > root-ca.pem
cat signing-ca.crt >> root-ca.pem

With the root CA and signing CA set up, we are now ready to create the member and client certificates used for authentication in our MongoDB cluster.

Generate and Sign Member Certificates

Member certificates are typically referred to as x.509 server certificates. Use this type of certificate for mongod and mongos processes. Members of a MongoDB cluster use these certificates to verify membership in the cluster. Stated another way, one mongod authenticates itself with other members of a replica set using a server certificate.

To generate certificates for the members of our replica set, we will use a for loop to generate multiple certificates.

# Pay attention to the OU part of the subject in "openssl req" command
for host in "${mongodb_server_hosts[@]}"; do
    echo "Generating key for $host"
    openssl genrsa -out ${host}.key 4096
	openssl req -new -key ${host}.key -out ${host}.csr -config openssl.cnf 
	-subj "$dn_prefix/OU=$ou_member/CN=${host}"
	openssl x509 -req -days 365 -in ${host}.csr -CA signing-ca.crt -CAkey 
	signing-ca.key -CAcreateserial -out ${host}.crt -extfile openssl.cnf 
	-extensions v3_req
    cat ${host}.crt > ${host}.pem
    cat ${host}.key >> ${host}.pem
done

Three steps are involved with each certificate:

  • Use the openssl genrsa command to create a new key pair.

  • Use the openssl req command to generate a signing request for the key.

  • Use the openssl x509 command to sign and output a certificate using the signing CA.

Notice the variable $ou_member. This signifies the difference between server certificates and client certificates. Server and client certificates must differ in the organization part of the Distinguished Names. More specifically, they must differ in at least one of the O, OU, or DC values.

Generate and Sign Client Certificates

Client certificates are used by the mongo shell, MongoDB Compass, MongoDB utilities and tools and, of course, by applications using a MongoDB driver. Generating client certificates follows essentially the same process as for member certificates. The one difference is our use of the variable $ou_client. This ensure that the combination of the O, OU, and DC values will be different from those of the server certificates generated above.

# Pay attention to the OU part of the subject in "openssl req" command
for host in "${mongodb_client_hosts[@]}"; do
    echo "Generating key for $host"
    openssl genrsa -out ${host}.key 4096
    openssl req -new -key ${host}.key -out ${host}.csr -config openssl.cnf 
-subj "$dn_prefix/OU=$ou_client/CN=${host}"
    openssl x509 -req -days 365 -in ${host}.csr -CA signing-ca.crt -CAkey 
      signing-ca.key -CAcreateserial -out ${host}.crt -extfile openssl.cnf 
      -extensions v3_req
    cat ${host}.crt > ${host}.pem
    cat ${host}.key >> ${host}.pem
done

Bring Up the Replica Set Without Authentication and Authorization Enabled

We can start each member of our replica set without auth enabled as follows. Previously, when working with replica sets we’ve not enabled auth so this should look familiar. Here again we are making use of a few variables we defined in “Generate a root CA” (or see the full script for this chapter) and a loop to launch each member (mongod) of our replica set.

mport=$mongodb_port
for host in "${mongodb_server_hosts[@]}"; do
    echo "Starting server $host in non-auth mode"   
    mkdir -p ./db/${host}
    mongod --replSet set509 --port $mport --dbpath ./db/$host 
        --fork --logpath ./db/${host}.log       
    let "mport++"
done

Once each mongod has started, we can then initialize a replica set using these mongods.

myhostname=`hostname`
cat > init_set.js <<EOF
rs.initiate();
mport=$mongodb_port;
mport++;
rs.add("localhost:" + mport);
mport++;
rs.add("localhost:" + mport);
EOF
mongo localhost:$mongodb_port init_set.js

Note that the code above simply constructs a series of commands, stores these commands in a JavaScript file, and then runs the mongo shell to execute the small script that was created. Together, these commands, when executed in the mongo shell, will connect to the mongod running on port 27017 (value of the $mongodb_port variable set in “Generate a root CA”), initiate the replica set, and then add each of the other two mongods (on ports 27018 and 27019) to the replica set.

Create the Admin User

Now, we’ll create an admin user based on one of the client certificates we created in “Generate and Sign Client Certificates”. We will authenticate as this user when connecting from the mongo shell or another client to perform administrative tasks. To authenticate with a client certificate, you must first add the value of the subject from the client certificate as a MongoDB user. Each unique x.509 client certificate corresponds to a single MongoDB user; i.e., you cannot use a single client certificate to authenticate more than one MongoDB user. We must add the user in the $external database; i.e., the authentication database is the $external database.

First, we’ll get the subject from our client certificate using the openssl x509 command.

openssl x509 -in client1.pem -inform PEM -subject -nameopt RFC2253 | grep subject

This should result in the following output:

subject= CN=client1,OU=MyClients,O=MongoDB,L=New York,ST=NY,C=US

To create our admin user, we’ll first connect to the primary of our replica set using the mongo shell.

mongo --norc localhost:27017

From within the mongo shell, we will issue the following command:

db.getSiblingDB("$external").runCommand(
    {
        createUser: "CN=client1,OU=MyClients,O=MongoDB,L=New York,ST=NY,C=US",
        roles: [
             { role: "readWrite", db: 'test' },
             { role: "userAdminAnyDatabase", db: "admin" },
             { role: "clusterAdmin", db:"admin"}
           ],
        writeConcern: { w: "majority" , wtimeout: 5000 }
    }
);

Note the use of the $external database in this command and the fact that we’ve specified the subject of our client certificate as the user name.

Restart the Replica Set with Authentication and Authorization Enabled

Now that we have an admin user, we can restart the replica set with authentication and authorization enabled and connect as a client. Without a user of any kind, it would be impossible to connect to a replica set with auth enabled.

Let’s stop the replica set in it’s current form (without auth enabled).

kill $(ps -ef | grep mongod | grep set509 | awk '{print $2}')

We are now ready to restart the replica set with auth enabled. In a production environment, we would copy each of the certificate and key files to their corresponding hosts. Here we’re doing everything on localhost to make things easier. To initiate a secure replica set we will add the following command-line options to each invocation of mongod:

  • --tlsMode

  • --clusterAuthMode

  • --tlsCAFile—root CA file (root-ca.key)

  • --tlsCertificateKeyFile—certificate file for the mongod

  • --tlsAllowInvalidHostnames—only used for testing; allows invalid hostnames

Here the file we provide as the value of the tlsCAFile option is used to establish a trust chain. As you recall the root-ca.key file contains the certificate of the root CA as well as the signing CA. By providing this file to the mongod process, we are stating our desire to trust the certificate contained in this file as well as all other certificates signed by these certificates.

Okay, let’s do this.

mport=$mongodb_port
for host in "${mongodb_server_hosts[@]}"; do
    echo "Starting server $host"
    mongod --replSet set509 --port $mport --dbpath ./db/$host 
        --tlsMode requireTLS --clusterAuthMode x509 --tlsCAFile root-ca.pem 
        --tlsAllowInvalidHostnames --fork --logpath ./db/${host}.log 
        --tlsCertificateKeyFile ${host}.pem --tlsClusterFile ${host}.pem 
        --bind_ip 127.0.0.1
    let "mport++"
done

And with that, we have a three-member replica set secured using x.509 certificates for authentication and transport-layer encryption. The only thing left to do is to connect with the mongo shell. We’ll use the client1 certificate to authenticate, because that is the certificate for which we created an admin user.

mongo --norc --tls --tlsCertificateKeyFile client1.pem --tlsCAFile root-ca.pem 
--tlsAllowInvalidHostnames --authenticationDatabase "$external" 
--authenticationMechanism MONGODB-X509

Once connected, we encourage you to experiment by inserting some data to a collection. You should also attempt to connect using any other user (e.g., using the client2.pem). Connections attempts will result in errors like the following.

mongo --norc --tls --tlsCertificateKeyFile client2.pem --tlsCAFile root-ca.pem 
--tlsAllowInvalidHostnames --authenticationDatabase "$external" 
--authenticationMechanism MONGODB-X509
MongoDB shell version v4.2.0
2019-09-11T23:18:31.696+0100 W  NETWORK  [js] The server certificate does not 
match the host name. Hostname: 127.0.0.1 does not match
2019-09-11T23:18:31.702+0100 E  QUERY    [js] Error: Could not find user 
"CN=client2,OU=MyClients,O=MongoDB,L=New York,ST=NY,C=US" for db "$external" :
connect@src/mongo/shell/mongo.js:341:17
@(connect):3:6
2019-09-11T23:18:31.707+0100 F  -        [main] exception: connect failed
2019-09-11T23:18:31.707+0100 E  -        [main] exiting with code 1

In the tutorial in this chapter, we’ve looked at an example of using x.509 certificates as a basis for authentication and to encrypt communication among clients and members of a replica set. The same procedure works for sharded clusters as well. With respect to securing a MongoDB cluster, please keep the following in mind:

  • The directories, root CA and signing CA, as well as the host itself where you generate and sign certificates for the member machines or clients, should be protected from unauthorized access.

  • For simplicity, the root CA and signing CA keys are not password protected in this tutorial. In production it is necessary to use passwords to protect the key from unauthorized use.

We encourage you to download and experiment with the demo scripts we have provided for this chapter in the book’s GitHub repository.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.227.194