MogoDB driver connection to MongoDB Atlas with X509 certificate doesn't work

Hi there.

I’m stuck at connecting to MongoDB Atlas with X509 auth mechanism and a certificate.

We are running an MongoDB v6 Atlas cluster and I try to connect with the mongodb_driver (0.9.2) package.

I try to authenticate via X509 by providing a .pem certificate.

The config looks like this:

config :myapp, :mongo_config,
  name: :myapp,
  appname: "myapp",
  url: "mongodb+srv://something.mongodb.net/db",
  username: "myuser",
  auth_mechanism: :x509,
  ssl: true,
  ssl_opts: [
    certfile: Path.join([cert_dir, "mycert.pem"])
  ]

And I start it with

{Mongo, Application.fetch_env!(:myapp, :mongo_config)}

in my application.ex start function.

If I try a simple ping in an iex session, it works:

iex> Mongo.ping(:myapp)

{:ok,
 %{
   "$clusterTime" => %{
     "clusterTime" => #BSON.Timestamp<1665059449:1>,
     "signature" => %{
       "hash" => #BSON.Binary<aca626330d4014449835f347178287ec49985029>,
       "keyId" => 7148690837596012550
     }
   },
   "ok" => 1.0,
   "operationTime" => #BSON.Timestamp<1665059449:1>
 }}

But if I try to list the collections, it fails:

iex> Mongo.show_collections(:myapp)

#Stream<[
  enum: {:error,
   %Mongo.Error{
     code: 13,
     error_labels: [],
     fail_command: false,
     host: nil,
     message: "command listCollections requires authentication",
     not_writable_primary_or_recovering: false,
     resumable: false,
     retryable_reads: false,
     retryable_writes: false
   }},
  funs: [#Function<39.108234003/1 in Stream.filter/2>,
   #Function<47.108234003/1 in Stream.map/2>]
]>

The user has readWrite@db rights, which should be enough.

I’m not sure if I correctly pass the certfile into the right config or if I need to do anything else.

To be sure that I can reach the MongoDB Atlas cluster I temporarily created a user with a password and tried to connect to the cluster by only providing the url mongodb+srv://test:password@something.mongodb.net/db in the config which works and I got the collections available in these database.

Also, trying both auth mechanisms in MongoDB Compass works without problems.

So I think it is something with the config I constructed with the X509 auth mechanism and the certificate file.
But I can’t figure out what I do wrong.

Did someone of you know what I do wrong?

Cheers
Frank

Did you follow the instructions described here: https://www.mongodb.com/docs/v4.4/tutorial/configure-x509-client-authentication/

As you can see, there are a lot of steps to be done and each step can fail, so it is hard to guess what is going wrong.

Try to use the mongosh with your configuration. If this is working, then go to check the configuration of the driver.

Hi.

Yes, I tried basically everything left and right, mongosh, compass, golang and all works.

But I found out what I did wrong with my config and now have it working with the certificate.

First, the username needs to be the certificate subject.
This is different than the other methods I tried. They’re probably doing some magic behind the scenes.

And I need to set a password, even if there is none needed.

After that, the authentication with the certificate works.

Cheers
Frank

1 Like

@suchasurge, can you please share how you implemented it with some code?

Hi @adengopaul.

Sure. I basically did it the following way…

in my config file:

config :myapp, :mongo_config,
  name: :myapp,
  url: "mongodb+srv://something.mongodb.net/db",
  username: "CN=myusername",
  password: "needs_to_be_set",
  database: "mymongodb_database",
  auth_mechanism: :x509,
  ssl_opts: [
    certfile: "/path/to/certificate,
    verify: :verify_none
  ]

For setup the certificate stuff for mongodb you can follow: https://www.mongodb.com/docs/v6.0/tutorial/configure-x509-client-authentication/

But I use terraform for this.

My fault was that the username you set in the config file needs to be the certification subject. CN=myusername in my example.
Also a password needs to be set even the certificate is used. So I set a random string for this.

Hope this helps.

Cheers
Frank

Thank you for your response. This is what I have for the repo config;

config :my_app, MyApp.Repo,
  show_sensitive_data_on_connection_error: true,
  url:
    "mongodb+srv://cluster.mongodb.net/app_dev?authSource=%24external&retryWrites=true&w=majority",
  # timeout: 60_000,
  # idle_interval: 10_000,
  # queue_target: 5_000,
  ssl: true,
  username: "CN=clustername",
  password: "password",
  auth_mechanism: :x509,
  ssl_opts: [
    verify: :verify_peer,
    cacertfile: to_charlist(CAStore.file_path()),
    certfile: "priv/cert/domain.key",
    customize_hostname_check: [
      match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
    ]
  ]

The compiler suggested I set show_sensitive_data_on_connection_error to true after getting the error below;

[error] GenServer #PID<0.820.0> terminating
** (RuntimeError) connect raised ArgumentError exception. The exception details are hidden, as they may contain sensitive data such as database credentials. You may 
set :show_sensitive_data_on_connection_error to true when starting your connection if you wish to see all of the details
    erlang.erl:4713: :erlang.atom_to_binary/1
    (stdlib 5.1) erl_posix_msg.erl:175: :erl_posix_msg.message_1/1
    (stdlib 5.1) erl_posix_msg.erl:29: :erl_posix_msg.message/1
    (ssl 11.0.3) ssl.erl:2802: :ssl.do_format_error/1
    (mongodb_driver 1.0.3) lib/mongo/error.ex:108: Mongo.Error.exception/1
    (mongodb_driver 1.0.3) lib/mongo/mongo_db_connection.ex:155: Mongo.MongoDBConnection.ssl/2
    (mongodb_driver 1.0.3) lib/mongo/mongo_db_connection.ex:109: Mongo.MongoDBConnection.connect/2
    (db_connection 2.4.3) lib/db_connection/connection.ex:82: DBConnection.Connection.connect/2
    (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib 5.1) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
Last message: nil
State: Mongo.MongoDBConnection

But now I get “1st argument: not an atom” error

[error] GenServer #PID<0.826.0> terminating
** (ArgumentError) errors were found at the given arguments:

  * 1st argument: not an atom

    erlang.erl:4713: :erlang.atom_to_binary({:keyfile, :function_clause})
    (stdlib 5.1) erl_posix_msg.erl:175: :erl_posix_msg.message_1/1
    (stdlib 5.1) erl_posix_msg.erl:29: :erl_posix_msg.message/1
    (ssl 11.0.3) ssl.erl:2802: :ssl.do_format_error/1
    (mongodb_driver 1.0.3) lib/mongo/error.ex:108: Mongo.Error.exception/1
    (mongodb_driver 1.0.3) lib/mongo/mongo_db_connection.ex:155: Mongo.MongoDBConnection.ssl/2
    (mongodb_driver 1.0.3) lib/mongo/mongo_db_connection.ex:109: Mongo.MongoDBConnection.connect/2
    (db_connection 2.4.3) lib/db_connection/connection.ex:82: DBConnection.Connection.connect/2
    (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5
    (stdlib 5.1) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
Last message: nil
State: Mongo.MongoDBConnection

I am using “mongodb_driver” dependency with “Mongo.Repo” module in repo.ex
My config is in runtime.exs because I had to access the “CAStore” module in config

When I disable line “certfile: “priv/cert/domain.key”,”, I get no compile errors but mongo errors on calling mongo actions such as user is not allowed to do action [find]

I used a self-signed certificate and key

Hello @suchasurge , am new to elixir been practicing for the last couple of months.
I left my response in the reply above. Thank you.

Solved.

I had to set up X.509 authentication for atlas

1 Like