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:
For the complete requirements, check the MongoDB docs.
Let’s check our current certificates:
|
1 |
$ kubectl get cert<br>NAME READY SECRET AGE<br>cluster1-ssl True cluster1-ssl 17h<br>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 |
$ kubectl apply -f - <<EOF<br>apiVersion: cert-manager.io/v1<br>kind: Issuer<br>metadata:<br> name: cluster1-psmdb-x509-ca<br>spec:<br> ca:<br> secretName: cluster1-ssl-internal<br>EOF |
Then, our certificate:
|
1 |
$ kubectl apply -f - <<EOF<br>apiVersion: cert-manager.io/v1<br>kind: Certificate<br>metadata:<br> name: cluster1-psmdb-egegunes<br>spec:<br> secretName: cluster1-psmdb-egegunes<br> isCA: false<br> commonName: egegunes<br> subject:<br> organizations:<br> - percona<br> organizationalUnits:<br> - cloud<br> usages:<br> - digital signature<br> - client auth<br> issuerRef:<br> name: cluster1-psmdb-x509-ca<br> kind: Issuer<br> group: cert-manager.io<br>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 |
$ kubectl get secret cluster1-psmdb-egegunes -o yaml <br> | yq3 r - 'data."tls.crt"' <br> | base64 -d <br> | openssl x509 -subject -noout<br><br>subject=O = percona, OU = cloud, CN = egegunes |
Let’s create the user:
|
1 |
rs0:PRIMARY> db.getSiblingDB("$external").runCommand(<br> {<br> createUser: "CN=egegunes,OU=cloud,O=percona",<br> roles: [{ role: 'readWrite', db: 'test' }]<br> }<br>)<br><br>{<br> "ok" : 1,<br> "$clusterTime" : {<br> "clusterTime" : Timestamp(1643099623, 3),<br> "signature" : {<br> "hash" : BinData(0,"EdPrmPJqfgRpMEZwGMeKNLdCe10="),<br> "keyId" : NumberLong("7056790236952526853")<br> }<br> },<br> "operationTime" : Timestamp(1643099623, 3)<br>} |
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.
I have created a simple Go application to show how you can use x509 certificates to authenticate. It’s redacted here for brevity:
|
1 |
// ca.crt is mounted from secret/cluster1-ssl<br>caFilePath := "/etc/mongodb-ssl/ca.crt"<br><br>// tls.pem consists of tls.key and tls.crt, they're mounted from secret/cluster1-psmdb-egegunes<br>certKeyFilePath := "/tmp/tls.pem"<br><br>endpoint := "cluster1-rs0.psmdb.svc.cluster.local"<br><br>uri := fmt.Sprintf(<br> "mongodb+srv://%s/?tlsCAFile=%s&tlsCertificateKeyFile=%s",<br> endpoint,<br> caFilePath,<br> certKeyFilePath,<br>)<br><br>credential := options.Credential{<br> AuthMechanism: "MONGODB-X509",<br> AuthSource: "$external",<br>}<br><br>opts := options.Client().SetAuth(credential).ApplyURI(uri)<br><br>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 |
$ kubectl logs psmdb-x509-tester-688c989567-rmgxv<br>2022/01/25 07:50:09 Connecting to database<br>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<br>2022/01/25 07:50:09 Username: O=percona,OU=cloud,CN=egegunes<br>2022/01/25 07:50:09 Connected to database<br>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.
Resources
RELATED POSTS