MongoDB supports multiple authentication mechanisms, including the default one SCRAM, LDAP, Kerberos, and x.509 Certificate Authentication.
In the X.509 system, which will be the main point of this blog post, an organization can identify its entities using a pair of certificates and private keys signed and trusted by some Certificate Authority (CA).
This model is well-known in the industry, and it’s quite popular for delegating authentication to 3rd party services. The other, similar way of delegating authentication is LDAP, which is also part of the same X.500 standard group called directory services.
While authentication works fine for MongoDB – and in general for every database system – the problem with authorization remains. A MongoDB cluster still needs to obtain information about resources to which a given user has access. Roles help a lot here, but in a zero-trust model, the practice requires having information duplicated: authentication data is stored in LDAP, X.509, or any other system, and authorization models residing in MongoDB.
And here, a cool feature of X.509 and… an undocumented feature of MongoDB comes into play.
An X.509 certificate is de facto a serialized object. It uses Abstract Syntax Notation One (ASN.1), which is a standard interface description language for defining data structures that can be serialized and deserialized in a cross-platform way. It is broadly used in cryptography.
A sample deserialized certificate looks like below:
|
1 |
# openssl asn1parse -in client.crt <br> 0:d=0 hl=4 l= 957 cons: SEQUENCE <br> 4:d=1 hl=4 l= 677 cons: SEQUENCE <br> 8:d=2 hl=2 l= 3 cons: cont [ 0 ] <br> 10:d=3 hl=2 l= 1 prim: INTEGER :02<br> 13:d=2 hl=2 l= 20 prim: INTEGER :22BC2E27B24B5A47123C6CB2FA0904B4F3663322<br> 35:d=2 hl=2 l= 13 cons: SEQUENCE <br> 37:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption<br> 48:d=3 hl=2 l= 0 prim: NULL <br> 50:d=2 hl=2 l= 82 cons: SEQUENCE <br> 52:d=3 hl=2 l= 11 cons: SET <br> 54:d=4 hl=2 l= 9 cons: SEQUENCE <br> 56:d=5 hl=2 l= 3 prim: OBJECT :countryName<br> 61:d=5 hl=2 l= 2 prim: PRINTABLESTRING :AU<br>… |
The main advantage of the above is that almost any piece of information can be stored inside a certificate. As the Certificate Authority needs to sign it, a user that’s presenting the certificate can’t modify it. That makes it secure. It also allows storing authorization information inside it.
|
1 |
# openssl req -x509 -new -newkey rsa:2048 -nodes -keyout myCA.key -sha256 -days 1825 -out myCA.pem<br>… |
OpenSSL Configuration:
|
1 |
[ req ]<br>default_bits = 2048<br>default_keyfile = server.key<br>encrypt_key = no<br>default_md = sha256<br>prompt = no<br>utf8 = yes<br>distinguished_name = server_req_distinguished_name<br>req_extensions = server_extensions<br><br><br>[ server_req_distinguished_name ]<br>C = AU<br>ST = Some-State<br>O = Percona<br>OU = NA<br>CN = server<br><br>[ server_extensions ]<br>basicConstraints=CA:FALSE<br>subjectKeyIdentifier = hash<br>keyUsage = keyEncipherment, digitalSignature<br>extendedKeyUsage = serverAuth, clientAuth<br><br># openssl req -config server.cnf -new -newkey rsa:2048 -out server.csr<br># openssl x509 -req -in server.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out server.crt -days 825 -sha256 -extensions server_extensions -extfile server.cnf<br># cat server.crt server.key > server.pem |
|
1 |
[ req ]<br>default_bits = 2048<br>default_keyfile = client.key<br>encrypt_key = no<br>default_md = sha256<br>prompt = no<br>utf8 = yes<br>distinguished_name = client_req_distinguished_name<br>req_extensions = client_extensions<br>x509_extensions = client_extensions<br><br><br>[ client_req_distinguished_name ]<br>C = AU<br>ST = Some-State<br>L = X<br>O = Percona<br>OU = Clients<br>CN = client<br><br>[ client_extensions ]<br>basicConstraints=CA:FALSE<br>subjectKeyIdentifier = hash<br>keyUsage = digitalSignature<br>extendedKeyUsage = clientAuth<br>1.3.6.1.4.1.34601.2.1.1= ASN1:SET:grants<br><br><br>[ grants ]<br>grant.1 = SEQUENCE:MongoDBRole<br>grant.2 = SEQUENCE:MongoDBRole2<br><br>[ MongoDBRole ]<br>role = UTF8:backup<br>database = UTF8:admin<br><br>[ MongoDBRole2 ]<br>role = UTF8:readAnyDatabase<br>database = UTF8:admin |
The above configuration will use MongoDBAuthorizationGrant OID 1.3.6.1.4.1.34601.2.1.1 and will grant the user that’s presenting the certificate two roles: backup for database admin and readAnyDatabase for database admin.
It’s important to remember that a client x.509 certificate’s subject, which contains the Distinguished Name (DN), must differ from that of a Member x.509 Certificate. Also, at least one of the Organization (O), Organizational Unit (OU), or Domain Component (DC) attributes in the client certificate must differ from those in the net.tls.clusterFile and net.tls.certificateKeyFile server certificates.
Moreover, a client certificate must contain the following fields:
keyUsage = digitalSignature
extendedKeyUsage = clientAuthIf the above requirements are not met, MongoDB will refuse any client x509 authentication with an error:
|
1 |
AuthenticationFailed: The provided certificate can only be used for cluster authentication, not client authentication. The current configuration does not allow x.509 cluster authentication, check the --clusterAuthMode flag |
Let’s continue generating the request and signing the certificate:
|
1 |
# openssl req -config client.cnf -new -newkey rsa:2048 -out client.csr<br># openssl x509 -req -in client.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out client.crt -days 825 -sha256 -extensions client_extensions -extfile client.cnf<br># cat client.crt client.key > client.pem |
At this stage MongoDB should allow authentication and authorization using the previously generated certificates:
|
1 |
$ mongo --tls --tlsCertificateKeyFile client.pem --tlsCAFile ./myCA.pem --authenticationDatabase '$external' --authenticationMechanism MONGODB-X509 server/<br><br>> db.runCommand({ connectionStatus: 1, showPrivileges: false });<br>{<br> "authInfo" : {<br> "authenticatedUsers" : [<br> {<br> "user" : "CN=client,OU=Clients,O=Percona,L=X,ST=Some-State,C=AU",<br> "db" : "$external"<br> }<br> ],<br> "authenticatedUserRoles" : [<br> {<br> "role" : "backup",<br> "db" : "admin"<br> },<br> {<br> "role" : "readAnyDatabase",<br> "db" : "admin"<br> }<br> ]<br> },<br> "ok" : 1<br>} |
It works without adding a single user!
Percona Distribution for MongoDB is a freely available MongoDB database alternative, giving you a single solution that combines the best and most important enterprise components from the open source community, designed and tested to work together.
Resources
RELATED POSTS