Managing database users and their passwords can be a hassle. Sometimes, they could even wait in various configuration files, hardcoded. Using certificates can help you avoid the toil of managing, rotating, and securing user passwords, so let’s see how to have x509 certificate authentication with the Percona Server for MongoDB Operator and cert-manager.
cert-manager is our recommended way to manage TLS certificates on Kubernetes clusters. The operator is already integrated with it to generate certificates for TLS and cluster member authentication. We’re going to leverage cert-manager APIs to generate valid certificates for MongoDB clients.
There are rules to follow to have a valid certificate for user authentication:
- A single Certificate Authority (CA) MUST sign all certificates.
- The certificate’s subject MUST be unique.
- The certificate MUST not be expired.
For the complete requirements, check the MongoDB docs.
Creating Valid Certificates for Clients
Let’s check our current certificates:
1 2 3 4 | $ kubectl get cert NAME READY SECRET AGE cluster1-ssl True cluster1-ssl 17h cluster1-ssl-internal True cluster1-ssl-internal 17h |
The operator configures MongoDB nodes to use “cluster1-ssl-internal” as the certificate authority. We’re going to use it to sign the client certificates to conform to Rule 1.
First, we need to create an Issuer:
1 2 3 4 5 6 7 8 9 | $ kubectl apply -f - <<EOF apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: cluster1-psmdb-x509-ca spec: ca: secretName: cluster1-ssl-internal EOF |
Then, our certificate:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | $ kubectl apply -f - <<EOF apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: cluster1-psmdb-egegunes spec: secretName: cluster1-psmdb-egegunes isCA: false commonName: egegunes subject: organizations: - percona organizationalUnits: - cloud usages: - digital signature - client auth issuerRef: name: cluster1-psmdb-x509-ca kind: Issuer group: cert-manager.io EOF |
The “usages” field is important. You shouldn’t touch its values. You can change the “subject” and “commonName” fields as you wish. They’re going to construct the Distinguished Name (DN) and DN will be the username.
1 2 3 4 5 6 | $ kubectl get secret cluster1-psmdb-egegunes -o yaml \ | yq3 r - 'data."tls.crt"' \ | base64 -d \ | openssl x509 -subject -noout subject=O = percona, OU = cloud, CN = egegunes |
Let’s create the user:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | rs0:PRIMARY> db.getSiblingDB("$external").runCommand( { createUser: "CN=egegunes,OU=cloud,O=percona", roles: [{ role: 'readWrite', db: 'test' }] } ) { "ok" : 1, "$clusterTime" : { "clusterTime" : Timestamp(1643099623, 3), "signature" : { "hash" : BinData(0,"EdPrmPJqfgRpMEZwGMeKNLdCe10="), "keyId" : NumberLong("7056790236952526853") } }, "operationTime" : Timestamp(1643099623, 3) } |
We’re creating the user in the “$external” database. You need to use “$external” as your authentication source. Note that we’re reversing the subject fields, this is important.
Authenticating With the Certificate
I have created a simple Go application to show how you can use x509 certificates to authenticate. It’s redacted here for brevity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // ca.crt is mounted from secret/cluster1-ssl caFilePath := "/etc/mongodb-ssl/ca.crt" // tls.pem consists of tls.key and tls.crt, they're mounted from secret/cluster1-psmdb-egegunes certKeyFilePath := "/tmp/tls.pem" endpoint := "cluster1-rs0.psmdb.svc.cluster.local" uri := fmt.Sprintf( "mongodb+srv://%s/?tlsCAFile=%s&tlsCertificateKeyFile=%s", endpoint, caFilePath, certKeyFilePath, ) credential := options.Credential{ AuthMechanism: "MONGODB-X509", AuthSource: "$external", } opts := options.Client().SetAuth(credential).ApplyURI(uri) client, _ := mongo.Connect(ctx, opts) |
The important part is using “MONGODB-X509” as the authentication mechanism. We also need to pass the CA and client certificate in the MongoDB URI.
1 2 3 4 5 6 | $ kubectl logs psmdb-x509-tester-688c989567-rmgxv 2022/01/25 07:50:09 Connecting to database 2022/01/25 07:50:09 URI: mongodb+srv://cluster1-rs0.psmdb.svc.cluster.local/?tlsCAFile=/etc/mongodb-ssl/ca.crt&tlsCertificateKeyFile=/tmp/tls.pem 2022/01/25 07:50:09 Username: O=percona,OU=cloud,CN=egegunes 2022/01/25 07:50:09 Connected to database 2022/01/25 07:50:09 Successful ping |
You can see the complete example in this repository. If you have any questions, please add a comment or create a topic in the Percona Forums.