I seem to be a bit confused regarding the x509 certificate authentication in MongoDB.
TLDR:
I Created server- and client-certificates (signed by the same CA, but different CN and OU), created a user in the MongoDB using the subject name as username and successfully connected using the c# client + client certificates.
With the MongoDB Compass I was able to connect to and read from the server, using the server certificates as client certificates.
Why was I able to do authenticate using the wrong certificate? Is it not possible to control permissions on a per-certificate-basis?
Extended:
I Created a Self-Signed Root-CA using OpenSSL, which signed another certificate which is my Signing-CA.
Using this Signing-CA I created two more Certificates:
Server certificate (CN=Server1,OU=Servers,O=project,ST=SH,C=DE)
Client certificate (CN=Client1,OU=Clients,O=project,ST=SH,C=DE)
Having those certificates in place I started the MongoDB instance without authentication, initiated the replicaSet and created a user for the certificate using this command:
db.getSiblingDB("$external").runCommand({createUser: "CN=Client1,OU=Clients,O=project,ST=SH,C=DE",roles: [{role: "readWrite", db: "admin"}, {role: "userAdminAnyDatabase", db: "admin"}, {role: "clusterAdmin", db: "admin"}, {role: "root", db: "admin"}]});.
I restarded the server, this time using some more parameters to start with enabled authentication:
--replSet *replicaSetName* --port *port* --dbpath *path* --logpath *path* --tlsMode requireTLS --clusterAuthMode x509 --tlsCAFile *path* --tlsCertificateKeyFile *path* --tlsClusterFile *path* --auth
I was able to connect without an issue using the C# client, the MongoDB Compass worked aswell.
But when I tested other certificates to verify the security, I noticed that it was absolutely possible to use the server certificate and key file to connect to the server using the MongoDB compass.
I Could not only connect, but browse and modify data aswell.
I was under the impression that every client certificate has to have an associated account in the $external database and thus only has the permissions/roles I assigned/granted to this specific user account.
Is this behavior supposed to be happening?
Is it possible to create one user account per client-certificate and grant different permisisons on different databases?
Thanks for your attention and answers, have a good day!
It depends on how you have configured your mongod process. Assuming you have a configuration file for your mongod (default path is /etc/mongod.conf) you would look to see if you have net.tls and security.clusterAuthMode settings..
Example Configuration file with these settings:
storage:
dbPath: /data/db
journal:
enabled: true
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
net:
port: 27017
bindIp: 0.0.0.0
tls:
mode: requireTLS
certificateKeyFile: /etc/ssl/node1.pem
CAFile: /etc/ssl/ca.crt
clusterFile: /etc/ssl/node1.pem
processManagement:
fork: true
pidFilePath: /var/run/mongodb/mongod.pid
timeZoneInfo: /usr/share/zoneinfo
security:
authorization: enabled
clusterAuthMode: x509
replication:
replSetName: replset
Other things
Recently MongoDB switched from SSL to TLS so depending on the version you are using you may find SSL instead of TLS.
Also, you might be using a replica set, or you might not be. If using a replica set you need to decide how a replica set member will authenticate to the other members. Should it use a keyfile, or should it use x509 as well as ordinary database users.
Also, you will need to create at least one named database user. The system will allow root access to the connected user if it is bound to localhost and no other users exist. This is called the localhost exception. Missing these steps is an incomplete and insecure installation.
Socket/TLS connection and authentication are separate steps in the high level connection establishment process.
The word "connection" is used by people to refer to multiple separate operations and processes and when troubleshooting any of these you need to be very clear about what it is you are looking at/asking about:
Socket connection from driver to server
Authentication by the driver (doesn't happen on monitoring connections which still go through the socket connection process)
Client object creation (not strictly a connection at all, though many people sloppily/erroneously use the word "connection" to refer to the action of creating a client object - creating a client object itself doesn't connect anything to anywhere)
Performing operations (even a server with authentication enabled permits one to create socket connections to it and execute operations like the ismaster command without authentication)
You probably are meaning to ask about #2 but you tested either #3 or #4, which as you should now see isn't necessarily giving you the expected results.
When TLS is enabled and "insecure TLS" isn't, the server will validate the client's TLS certificate (and the client will validate the server's) during the socket connection process. The certificate being validated must be signed by the CA that the validator is configured with. At this point there isn't anything preventing the client from supplying the server's certificate to the server, if you managed to give the client the private key that goes with the server's certificate (which normally shouldn't happen). The server warns about this situation in the logs. Note that no authentication has happened yet.
If you are using X.509 authentication (which must be configured on the client side and is separate from supplying certificates used for the socket connection, e.g. use the authMechanism URI option) then, after a successful socket connection and any associated certificate verification, the driver will perform the authentication. At this point you need to have the server user created that matches the distinguished name on the certificate.
MongoDB has guides for both setting up TLS connections and X.509 auth, read through them and follow them exactly as written and verify each step of the way.
Related
I have forgot my mongodb root user password for the shared cluster of 3 nodes. I have gone through stack overflow for the same issue but was unable to replicate due to different configuration. Below is my configuration
mongodb version 4.4.
replication on 3 servers(nodes) using keyfile authentication.
all nodes are running in docker containers.
In case useful, I have other credentials that were created through root user for backup and read write permission but they dont have access to admin database.
Please guide me if you have any solution. thanks
unable to find anything to try
The official way of doing this is:
Restart the MongoDB without authorization, i.e. mongod --noauth ... or via configuration file
security:
authorization: disabled
Then you can logon without password and change credentials of the root user.
Attention: while the MongoDB is running without authorization, every user connects with root privileges, so you better restart the MongoDB in maintenance mode, i.e.
net:
bindIp: localhost
port: 55555
#replication:
# replSetName: shardA
#sharding:
# clusterRole: shardsvr
setParameter:
skipShardingConfigurationChecks: true
disableLogicalSessionCacheRefresh: true
Then you can connect only from localhost using port 55555 (which is not configured by other cluster members nor known by other users)
You need to do this only on the configuration server, because user accounts are stored there, not on the shards or mongos members.
However, there is a much simpler way to achieve the same, use the keyfile for authentication:
mongosh --authenticationDatabase local -u __system -p "$(tr -d '\011-\015\040' < /path/to/keyfile)"
I Read through many documents in the mongoDB doc, still unclear how authentication works for clients an member of replica set(using x.509).
Found a resource "http://pe-kay.blogspot.in/2016/02/securing-mongodb-using-x509-certificate.html"
which was well documented, still not clear as how authentication happens.
Considering the below mongoConfig and commands for starting server and mongo client:-
mongoConfig.cfg
storage:
dbPath: "../DB"
security:
clusterAuthMode: x509
net:
port: 27001
ssl:
mode: "requireSSL"
PEMKeyFile: "../server/security/one.pem"
clusterFile: "../server/security/one.pem"
CAFile: "../server/security/rootCA.crt"
Commands in Prompt:-
cPrompt> mongod -v --config "../custom/mongoConf.cfg" --replSet "one"
cPrompt> mongo -ssl --sslPEMKeyFile "../client/security/oneHost.pem" --sslCAFile "../client/security/rootCA.crt" --host mylocalhost --port 27001
1) Is it a oneWay or a twoWay SSL/TLS ?
2) How authentication between members of replicaSet takes place (mongod - mongod while doing replication) and between server-client (mongod-mongo say mongoShell or application) ?
3) Which version of TLS is used ?
Can someone explain in detail ?
This is an old question, however I was facing the same issues and it took me a lot of time to get it working. Some items are rather essential but quite hidden in the documentation.
I try to give an overview without doing simple copy/paste from the MongoDB documentation.
In general a x.509 certificate in MongoDB provides these functions:
Generate keys to encrypt the connection
Ensure the connection is established from correct host (i.e. the declared hostname matches the actual hostname)
Authenticate a client (instead of using username + password or keyfile)
As starting point, one should have a look at these tutorials:
TLS/SSL Configuration for Clients
Configure mongod and mongos for TLS/SSL
Use x.509 Certificates to Authenticate Clients
Use x.509 Certificate for Membership Authentication
In general TLS/SSL settings are defined in this configuration file section:
net:
tls:
certificateKeyFile: server.pem
CAFile: server-ca.crt
clusterFile: member.pem
clusterCAFile: cluster-ca.crt
They correspond to command line options
--tlsCertificateKeyFile server.pem
--tlsCAFile server-ca.crt
--tlsClusterFile member.pem
--tlsClusterCAFile cluster-ca.crt
All other TLS/SSL related parameters are well documented and usually they should not cause any confusion or misunderstanding.
Step 1
When a client tries to establish a TLS/SSL enabled connection, then the mongod/mongos server presents a server certificate. The client verifies this certificate with a CA.
A client can be a normal client (e.g. the Mongo shell mongosh) or an internal Replica Set / Sharded Cluster member
The server certificate is always the same, mongod/mongos does not distinct the client types
The server certificate is defined by parameter
net.tls.certificateKeyFile or --tlsCertificateKeyFile
A normal client can verify the server certificate for example with these:
Option --tls --tlsCAFile server-ca.cer
Option --tls --tlsUseSystemCA
Connection string parameter tls=true&tlsCAFile=server-ca.cer
--tlsUseSystemCA exists only as option, you cannot define it in connection string. Would be like this:
mongosh --tlsUseSystemCA "mongodb://localhost/?tls=true"
An internal Replica Set / Sharded Cluster member verifies the server certificate by parameter
net.tls.CAFile or --tlsCAFile
If net.tls.CAFile or --tlsCAFile is not specified and you are not using x.509 authentication, the system-wide CA certificate store will be used. If you use x.509 authentication, then net.tls.CAFile or --tlsCAFile is required.
Step 2
The client presents a client certificate to the mongod/mongos server. The client certificate can be used to authenticate the user. In this case, you don't have to provide a password/keyfile.
For normal client, x.509 authentication is enabled by user creation, e.g.
db.getSiblingDB("$external").runCommand({createUser: "CN=myName,OU=myOrgUnit,O=myOrg,..."})
For Replica Set / Sharded Cluster member, x.509 authentication is enabled by parameter
security.clusterAuthMode: x509
A normal client (e.g. mongosh) provides client certificate for example by these parameters:
mongosh --tls --tlsCertificateKeyFile client.pem (no x.509 authentication)
mongosh --tls --authenticationDatabase $external --authenticationMechanism MONGODB-X509 --tlsCertificateKeyFile client.pem (with x.509 authentication)
mongosh "mongodb://username:secret#localhost/?tls=true&authSource=admin&tlsCertificateKeyFile=client.pem" (no x.509 authentication)
mongosh "mongodb://localhost/?tls=true&authSource=$exernal&tlsCertificateKeyFile=client.pem&authMechanism=MONGODB-X509" (with x.509 authentication)
An internal Replica Set / Sharded Cluster member provides member certificate by parameter net.tls.clusterFile or --tlsClusterFile
The monogd/mongos sever verifies the client/member certificate with Root-CA defined by parameter net.tls.clusterCAFile or --tlsClusterCAFile
The client can be a normal client (e.g. the Mongo shell mongosh) or an internal Replica Set / Sharded Cluster member
The Root-CA is always the same, mongos/mongod does not distinct between client certificate and member certificate
If net.tls.clusterCAFile or --tlsClusterCAFile is not defined, then net.tls.CAFile/--tlsCAFile is used for verification.
Pitfalls:
Some Mongo documents just refer to "client" certificate/connection. Be aware, this means normal clients (e.g. mongosh) as well as internal Replica Set / Sharded Cluster member clients. In this answer I use terms "client certificate" and "member certificate" for better understanding.
Server certificates and Member certificates must have the same O, OU, and DC in their subject name
Client certificates and Member certificates must have different O, OU, and DC in their subject name
Parameter pairs certificateKeyFile/CAFile and clusterFile/clusterCAFile are not used to separate connections from normal clients and connections from Replica Set / Sharded Cluster members. They are used to separate client and server certificates, i.e. incoming and outgoing connections. In my opinion, these names are totally miss-leading.
You can use a common Root-CA, defined by net.tls.CAFile
You can also use the same certificate for client, member and server. This common certificate can be even used for x.509 authentication of Replica Set / Sharded Cluster members. This certificate only provides encrypted connection and x.509 member authentication. Of course, you cannot use it for x.509 authentication of normal clients.
Option tlsAllowInvalidCertificates has no effect on x509 member authentication. For x509 authentication the certificate must be valid. Invalid certificates are only used to encrypt the connection.
Test cases:
openssl verify -CAfile server-ca.crt server.pem
openssl verify -CAfile cluster-ca.crt member.pem
openssl verify -CAfile cluster-ca.crt client.pem
openssl s_server -cert server.pem -CAfile cluster-ca.crt
# open another terminal
openssl s_client -cert member.pem -CAfile server-ca.crt -quiet -no_ign_eof -status <<< Q
openssl s_client -cert client.pem -CAfile server-ca.crt -quiet -no_ign_eof -status <<< Q
Visualization
In the course of migrating off Parse's servers before it shuts down, I'm trying to set up a simple MongoDB instance on Digital Ocean. (I'm using that instead of mLab because my needs are very limited—-a few MB of storage, a few hundred requests per week--and for that mLab's costs are pretty high.)
I've got mongod running, and have made some progress with SSL thanks to this guide using Let's Encrypt, but now I'm stuck. Parse's migration tool says, "No reachable servers," and if I try to connect on the command line like this:
mongo --ssl -u editAdmin -p "<password-here>" --host mydb.myhost.com dbname
I get this error:
MongoDB shell version: 3.2.7
connecting to: mydb.myhost.com:27017/dbname
2016-07-24T10:31:38.814-0700 E QUERY [thread1] Error: network error while attempting to run command 'isMaster' on host 'mydb.myhost.com:27017' :
connect#src/mongo/shell/mongo.js:231:14
#(connect):1:6
exception: connect failed
The server log reports:
2016-07-24T13:32:44.357-0400 I NETWORK [initandlisten] connection accepted from 12.345.67.89:33351 #39 (1 connection now open)
2016-07-24T13:32:44.390-0400 E NETWORK [conn39] no SSL certificate provided by peer; connection rejected
2016-07-24T13:32:44.390-0400 I NETWORK [conn39] end connection 12.345.67.89:33351 (0 connections now open)
So that would suggest the client needs to provide a cert, but (a) I don't know how to provide one, and (b) Parse doesn't provide that as an option so there must be some way not to.
Thanks in advance for your help.
The key error message is this one:
no SSL certificate provided by peer; connection rejected
When you enable TLS/SSL on MongoDB, MongoDB clients can now authenticate that the MongoDB server is who it claims to be by comparing the MongoDB's TLS/SSL certificate (specified by the PEMKeyFile property in the mongod.conf file) against the public Certificate Authority certificate that you provide to the MongoDB client to indicate which Certificate Authority you trust.
But what I just described is sometimes called one-way TLS, whereas, by default, MongoDB enables two-way or mutual TLS authentication. The idea behind this is that maybe the MongoDB doesn't want to accept client requests from just anyone (the way a public website might), but wants to authenticate the clients as well.
In TLS Mutual Auth, the same Certificate Authority I mentioned above will issue client certificates and the MongoDB server will check the client's certificate to make sure it really was issued by the Certificate Authority in question and that it's valid (e.g. hasn't expired).
So this error is saying "Hey, I expect my clients to present a TLS certificate, but you're not presenting anything."
The way to fix it is described at Configure mongod and mongos for TLS/SSL:
If you want to bypass validation for clients that don’t present
certificates, include the allowConnectionsWithoutCertificates run-time
option with mongod and mongos. If the client does not present a
certificate, no validation occurs. These connections, though not
validated, are still encrypted using SSL.
Of course, you can specify this in the mongod.conf file as well: https://docs.mongodb.com/manual/reference/configuration-options/#net.ssl.allowConnectionsWithoutCertificates
My preferred solution looks like this:
net:
port: 27017
bindIp: 172.0.0.1 # Set this to whatever your private IP address is
ssl:
mode: "requireSSL"
PEMKeyFile: "/path/to/tls/private/key"
CAFile: "/path/to/ca/public/cert"
disabledProtocols: "TLS1_0,TLS1_1"
allowConnectionsWithoutCertificates: true # <-- The line to add to your config
You're command is using the SSL option (and I assume Parse is doing the same) so you are trying to connect using SSL. A client must provide a cert when using SSL. https://docs.mongodb.com/manual/tutorial/configure-ssl-clients/ this link explains how to do this and also specifically mentions your issue
Just follow this tutorial, everything is there, I know that for sure cause I was following that and now I got running parse-server without any previous knowledge... I would recommend you using the MongoDB connection without SSL certificate and allow only localhost queries to it - so only the parse-server that runs on the same machine will access this DB...
When trying to connect to Heroku PostgresSQL DB using pgAdmin3 I'm getting the following error:
Error connecting to the server: SSL error: certificate verify failed
The connection is based on pg:credentials output and defined as below:
[Properties]:
Host: <host>
Port: 5432
Service: [blank]
Maintenance DB: <database>
Username: <user>
[SSL]:
SSL: require
Server Root Certificate File: [blank]
Server Certificate Revocation List: [blank]
Client Certificate File: [blank]
Client Key File: [blank]
SSL compression: on
[SSH Tunnel] and [Advanced] left default
so as per Heroku guidelines SSL is enabled (set to: [**require**]).
Any ideas how to provide/fix the certificate referred by the error message?
It's likely that pgAdmin is picking up a bundle of CA certificates that has been configured on your system, in which case require would try to verify the server certificate against that bundle.
Typically, this would be a root.crt file located in %APPDATA%\postgresql\ (C:\Users\YourUserName\AppData\Roaming\postgresql\) under Windows, or in ~/.postgresql/ under Linux.
If there is such a file, try to move it out of the way temporarily to check if it works better.
The problem with moving it out of the way is that you are then no longer verifying any remote PostgreSQL certificates against anything but it still works (with require, it would fail with verify-full).
You can solve this by putting the root.crt file in the right place and adding the server certificate to the list of trusted certificates.
It can be bit tricky to find a remote PostgreSQL certificate, but this simple Python application should let you do it (replace hostname and port as required):
import socket
import ssl
import struct
hostname = '...'
port = 5432
sock = socket.socket()
sock.connect((hostname, port))
# We first connect without encryption and tell the server
# we want to upgrade to SSL/TLS.
initiate_ssl_command = struct.Struct('!ii').pack(8, 80877103)
sock.sendall(initiate_ssl_command)
resp = sock.recv(1)
print "Response should be S: %s" % (resp,)
# We then initiate an SSL/TLS connection on this socket.
ssl_sock = ssl.wrap_socket(sock, cert_reqs=ssl.CERT_NONE)
ssl_sock.do_handshake()
peer_cert = ssl_sock.getpeercert(True)
print ssl.DER_cert_to_PEM_cert(peer_cert)
ssl_sock.close()
(For details about what this code does, see the PostgreSQL protocol documentation, in particular the "SSL Session Encryption" section and the SSLRequest message, which is similar to what STARTTLS does in other protocols such as SMTP.)
Security warning: Here, you're just hoping that this particular connection has not been attacked and returns the genuine certificate the first time. It's what you'll use as a trust anchor for subsequent connections. (It's very similar to accepting an SSH server key for the first connection, it will flag changes to the certificate if it changes.)
It's also worth noting that the certificate Subject DN might not match that of the server you're connecting to, therefore you might not be able to use PostgreSQL's verify-full mode (which is the only really secure mode, since it also verifies the host name as well as the trust anchor).
Ideally, Heroku (or whoever provides this service) should give you that certificate by another secure means, and make sure that the Subject DN in that certificate matches the host name they give you. (I'm not sure if this is the case at the moment, perhaps it is available from the administration interface.)
I am trying to enable x.509 authentication in MongoDB.
I was able to compile with ssl support and was able to connect to the db using ca signed client certificate.
However it looks like all certificates signed by the same CA can connect to the database regardless of the username set in $external database.
Is this expected behaviour?
I thought mongodb would refuse connection from certificates that are not registered in $external database.
In my config file, I have following defined.
net:
ssl:
mode: requireSSL
PEMKeyFile: blah
PEMKeyPassword: blah
CAFile: blah
security:
authorization: enabled
This is as expected.
Anyone that has a certificate signed by the root CA you started mongo with will be able to connect to the database. However, when you auth and take an action (say, query a collection) the supplied cert will be checked against the $external database to see if the client has permissions to take that action.
db.getSiblingDB("$external").auth({ mechanism: "MONGODB-X509", user: "CN=MyCommonName,OU=MyOrgUnit,O=MyOrg,ST=Washington,C=US"})