Getting MongooseServerSelectionError: Hostname/IP does not match certificate's altnames: IP: xxx.xx.xx.xx is not in the cert's list: - node.js

I have created self signed certificate in my linux machine where i have given certificate CN same as IP of that linux
I have added them in mongodb.conf and restarted the server
i am able to connecte via command
mongo --ssl --sslPEMKeyFile /etc/ssl/mongodbcerts/mongodb.pem --sslCAFile /etc/ssl/mongodbcerts/ca.pem
But when i am trying to connect from nodeJS mongoose i am getting error like
MongooseServerSelectionError: Hostname/IP does not match certificate's altnames: IP: XXX.xx.x.xx is not in the cert's list:
My nodejs code for connecting mongodb as follows
const connectionOptions = { useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false ,
server:{
ssl: true,
sslValidate:true,
sslCA: require('fs').readFileSync("/etc/ssl/mongodbcerts/ca.pem"),
sslKey:require('fs').readFileSync("/etc/ssl/mongodbcerts/mongodb.key"),
sslCert:require('fs').readFileSync("/etc/ssl/mongodbcerts/mongodb.crt")
}
};
let mongo_url="mongodb://username:password#IPaddress/DB"
console.log(mongo_url)
mongoose.connect(mongo_url,connectionOptions).then(() => console.log( 'Database Connected' ))
.catch(err => console.log( err ));;
Please let me know the error

I faced this issue recently. Starting in MongoDB 4.2, when performing comparison of SAN, MongoDB supports comparison of IP addresses as well. You can use IP address in the CN field but make sure your openssl configuration file contains your servers IP address in the alt_names section.
Here's a sample cnf file provided in the official MongoDB docs -
# NOT FOR PRODUCTION USE. OpenSSL configuration file for testing.
[ req ]
default_bits = 4096
default_keyfile = myTestServerCertificateKey.pem ## The default private key file name.
default_md = sha256
distinguished_name = req_dn
req_extensions = v3_req
[ v3_req ]
subjectKeyIdentifier = hash
basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
nsComment = "OpenSSL Generated Certificate for TESTING only. NOT FOR PRODUCTION USE."
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = #alt_names
[ alt_names ]
DNS.1 = ##TODO: Enter the DNS names if using hostname, otherwise remove this line
IP.1 = ##TODO: Enter the IP address if using IP address
[ req_dn ]
countryName = Country Name (2 letter code)
countryName_default = TestServerCertificateCountry
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = TestServerCertificateState
stateOrProvinceName_max = 64
localityName = Locality Name (eg, city)
localityName_default = TestServerCertificateLocality
localityName_max = 64
organizationName = Organization Name (eg, company)
organizationName_default = TestServerCertificateOrg
organizationName_max = 64
organizationalUnitName = Organizational Unit Name (eg, section)
organizationalUnitName_default = TestServerCertificateOrgUnit
organizationalUnitName_max = 64
commonName = Common Name (eg, YOUR name)
commonName_max = 64

Mistake i did was I have created self signed certificate with Common Name(CN) as IP address (XXX.xx.x.xx) but we need to create the self signed certificates with CN as hostname.
To get the hostname open mongo shell and execute below command:
>getHostName()
You will hostname of that VM and create self signed certificates with same host name and try connect with mongoose nodejs. It will work.
Support Doc: https://mongoosejs.com/docs/tutorials/ssl.html

Related

IXWebSocket wss c++ client cannot connect to Node.js wss server using an ip address

I have a IXWebSocket c++ wss client connecting to a Node.js wss server(websocket npm package). Everything is fine as long as the client connect to "wss://localhost:8080". Soon as I use the ip address of the Node.js wss server, I have the error "OpenSSL failed - error:0A000086:SSL routines::certificate verify failed"
Certificate chain creation
I created my own private root ca. I used those commands to generate root ca key/certificate and server key/certificate:
$ openssl genpkey -aes256 -out root-ca/private/ca.private.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048
$ openssl req -config root-ca/root-ca.conf -key root-ca\private\ca.private.key -x509 -days 7500 -sha256 -extensions v3_ca -out root-ca\certs\ca.crt
$ openssl genpkey -out server/private/server.private.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048
$ openssl req -key server\private\server.private.key -new -sha256 -out server\csr\server.csr
$ openssl ca -config root-ca\root-ca.conf -extensions server_cert -days 365 -notext -in server\csr\server.csr -out server\certs\server.crt
The configuration has a subjectAltName for both root and server and it looks like this :
[ca]
#\\root\\ca\\root-ca\\root-ca.conf
#see man ca
default_ca = CA_default
[CA_default]
dir = C:\\ca\\root-ca
certs = $dir\\certs
crl_dir = $dir\\crl
new_certs_dir = $dir\\newcerts
database = $dir\\index
serial = $dir\\serial
RANDFILE = $dir\\private\\.rand
private_key = $dir\\private\\ca.private.key
certificate = $dir\\certs\\ca.crt
crlnumber = $dir\\crlnumber
crl = $dir\\crl\\ca.crl
crl_extensions = crl_ext
default_crl_days = 30
default_md = sha256
name_opt = ca_default
cert_opt = ca_default
default_days = 365
preserve = no
policy = policy_loose
[ policy_strict ]
countryName = supplied
stateOrProvinceName = supplied
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ policy_loose ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ req ]
# Options for the req tool, man req.
default_bits = 2048
distinguished_name = req_distinguished_name
string_mask = utf8only
default_md = sha256
# Extension to add when the -x509 option is used.
x509_extensions = v3_ca
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
stateOrProvinceName = State or Province Name
localityName = Locality Name
0.organizationName = Organization Name
organizationalUnitName = Organizational Unit Name
commonName = Common Name
emailAddress = Email Address
countryName_default = CA
stateOrProvinceName_default = Qc
0.organizationName_default = Adacel
[ v3_ca ]
# Extensions to apply when createing root ca
# Extensions for a typical CA, man x509v3_config
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
subjectAltName = #alt_names
[ v3_intermediate_ca ]
# Extensions to apply when creating intermediate or sub-ca
# Extensions for a typical intermediate CA, same man as above
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
#pathlen:0 ensures no more sub-ca can be created below an intermediate
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ server_cert ]
# Extensions for server certificates
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = #alt_names
[ alt_names ]
DNS.1 = localhost
IP.1 = 192.168.230.138
IP.2 = 127.0.0.1
The certificate chain looks valid between my root ca and my server:
$ openssl verify -CAfile root-ca\certs\ca.crt server\certs\server.crt
server\certs\server.crt: OK
Both ca.crt and server.crt have a reference to the wss server IP address, I used the subjectAltName parameter to defined it. I thought that my root ca would need it (I am not even sure that make sense to have domain on the root ca), but it doesn't make any difference.
What is not working
My IXWebSocket c++ client :
ix::initNetSystem();
ix::WebSocket webSocket;
ix::SocketTLSOptions tlsOptions;
tlsOptions.caFile = "ca.crt";
webSocket.setTLSOptions(tlsOptions);
std::string url("wss://localhost:8080");
//std::string url("wss://192.168.230.138:8080"); //Cannot connect to the ip
webSocket.setUrl(url);
What is working
wss javascript client:
I also coded a javascript client (using same npm package as my server, not ) and this little client can connect using the ip address!!
const options = {
ca: fs.readFileSync("./ca.crt"),
};
var client = new WebSocketClient();
client.on("connectFailed", function (error) {
console.log("Connect Error: " + error.toString());
});
client.on("connect", function (connection) {
console.log("WebSocket Client Connected");
connection.on("error", function (error) {
console.log("Connection Error: " + error.toString());
});
connection.on("close", function () {
console.log("echo-protocol Connection Closed");
});
connection.on("message", function (message) {
if (message.type === "utf8") {
console.log("Received: '" + message.utf8Data + "'");
}
});
});
client.connect("wss://192.168.230.138:8080/", null, null, null, options);
My Node.js server :
const httpsSignalServer = https.createServer(
{
key: fs.readFileSync("./server.private.key"),
cert: fs.readFileSync("./server.crt"),
},
(req, res) => {
console.log(`signal server : we have received the request ${req}`);
}
);
const signalWebsocket = new WebSockerServer.server({
httpServer: httpsSignalServer,
});
signalWebsocket.on("request", (request) => onRequest(request));
httpsSignalServer.listen(8080, () =>
console.log("---> My signal server is listening <---")
);
signalWebsocket.on("request", (request) => onRequest(request));
httpsSignalServer.listen(8080, () =>
console.log("---> My signal server is listening <---")
);
Questions :
Any idea why my c++ client cannot connect using an ip address to the server, while the javascript client can? (using the same certificate chain)
If no, any idea how I could debug this?
Would it be possible that the problem is a high level SSL stuff, where you actually need a real hostname and can't use an IP?

Connecting pymongo client to mongodb server with TLS

I have 2 instances on Google Cloud :
Instance A and Instance B - Both have a static external IP address.
Instance A running the community edition of the MongoDB server v4.4.6.
I have generated self-signed certificates to enable TLS
I have set the firewall rules in my cloud network to allow traffic to the MongoDB port from Instance Bs IP address
As such, I am successfully able to use the mongo shell(v4.4.6) in Instance B to connect to the mongo server running on Instance A. This is the command that I use -
mongo --tls --tlsCertificateKeyFile client.pem --tlsCAFile ca.pem <instance_a_ip>:<port>/admin -u <userName> -p
I would like to use the pymongo(v3.11.4) client from Instance B in order to connect to my MongoDB server in Instance A and I have tried that using this in an interactive python shell -
client = MongoClient("mongodb://<instance_a_ip>:<port>/admin", tls=True, tlsCertificateKeyFile='./client.pem', tlsCAFile='./ca.pem', username='<userName>', password='<userPassword>')
However, I am not able to connect and this is the error that I receive -
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/varun/test-env/lib/python3.8/site-packages/pymongo/collection.py", line 1319, in find_one
for result in cursor.limit(-1):
File "/home/varun/test-env/lib/python3.8/site-packages/pymongo/cursor.py", line 1207, in next
if len(self.__data) or self._refresh():
File "/home/varun/test-env/lib/python3.8/site-packages/pymongo/cursor.py", line 1100, in _refresh
self.__session = self.__collection.database.client._ensure_session()
File "/home/varun/test-env/lib/python3.8/site-packages/pymongo/mongo_client.py", line 1816, in _ensure_session
return self.__start_session(True, causal_consistency=False)
File "/home/varun/test-env/lib/python3.8/site-packages/pymongo/mongo_client.py", line 1766, in __start_session
server_session = self._get_server_session()
File "/home/varun/test-env/lib/python3.8/site-packages/pymongo/mongo_client.py", line 1802, in _get_server_session
return self._topology.get_server_session()
File "/home/varun/test-env/lib/python3.8/site-packages/pymongo/topology.py", line 496, in get_server_session
self._select_servers_loop(
File "/home/varun/test-env/lib/python3.8/site-packages/pymongo/topology.py", line 215, in _select_servers_loop
raise ServerSelectionTimeoutError(
pymongo.errors.ServerSelectionTimeoutError: <instance_a_ip>:<port>: ("Invalid DNS pattern b'127.0.0.1'.",), Timeout: 30s, Topology Description: <TopologyDescription id: 60ad03827b267af40c2edf4b, topology_type: Single, servers: [<ServerDescription ('<instance_a_ip>', <port>) server_type: Unknown, rtt: None, error=AutoReconnect('<instance_a_ip>:<port>: ("Invalid DNS pattern b\'127.0.0.1\'.",)')>]>
I am new to MongoDB and not able to figure out how to go about this, help would be greatly appreciated.
Debugged the issue by installing the Nodejs client for MongoDB. The Node client provided a much better message upon failure -
[Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: IP: 34.126.133.72 is not in the cert's list
Thanks to the meaningful error, I read through the OpenSSL configuration file that I had used while creating the self-signed certificates. Rectified the error I made in the config file as such -
Original config file which caused the error
[ v3_req ]
subjectAltName = #alt_names
[ alt_names ]
DNS.1 = 127.0.0.1
DNS.2 = <instance_a_ip>
Rectified config file which now works with all MongoDB clients
[ v3_req ]
subjectAltName = #alt_names
[ alt_names ]
IP.1 = 127.0.0.1
IP.2 = <instance_a_ip>
Your certificate is self-signed, add this option when create MongoClient.
tlsInsecure=True
code will be like this
client = MongoClient(
["<instance_a_ip>:<port>"],
tls=True,
tlsInsecure=True,
tlsCertificateKeyFile='./client.pem',
tlsCAFile='./ca.pem',
username='<userName>',
password='<userPassword>'
)

MySQL xdevapi Node Error: SELF_SIGNED_CERT_IN_CHAIN

Windows 10 Pro
MySQL8.0
node.js v14.15.5
openssl 1.1.1c
I'm trying to connect to a MySQL database using node, I'm able to connect without TLS just fine. I used openssl to create a private key and self signed certificate. When I try to use TLS in my node app, I keep getting the following error:
$ NODE_TLS_REJECT_UNAUTHORIZED=0 node index.js
(node:15480) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
Error: self signed certificate in certificate chain
at TLSSocket.onConnectSecure (_tls_wrap.js:1497:34)
at TLSSocket.emit (events.js:315:20)
at TLSSocket._finishInit (_tls_wrap.js:932:8)
at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:706:12) {
code: 'SELF_SIGNED_CERT_IN_CHAIN'
}
TypeError: Cannot read property 'sql' of undefined
at C:\Users\gregb\Desktop\PROGRAMMING\VS Code Workspace\xdevapi2\index.js:40:32
at processTicksAndRejections (internal/process/task_queues.js:93:5)
[]
[]
Index.js
let mysqlx = require('#mysql/xdevapi');
let fs = require('fs');
require('dotenv').config();
let rows = [];
let columns = [];
// Works without TLS
// let config = {
// host: 'localhost',
// port: 33060,
// user: 'root',
// database: 'user',
// password: process.env.password
// };
const config = {
host: 'localhost',
port: 33060,
user: 'root',
database: 'user',
password: process.env.password,
ssl: true,
tls: {
rejectUnauthorized: false,
key: fs.readFileSync('./privatekey2.pem'),
cert: fs.readFileSync('./example.crt'),
ca: './cacert.pem'
}
};
(async function () {
let session;
try {
session = await mysqlx.getSession(config);
} catch(err) {
console.log(err);
}
try {
let result = await session.sql('SELECT "firstName" AS c1, "lastName" AS c2')
.execute(row => rows.push(row), column => columns = columns.concat(column));
session.close();
} catch(err) {
console.log(err);
}
console.log(rows); // [['foo', 'bar']]
console.log(columns.map(column => column.getColumnLabel())); // ['c1', 'c2']
})();
I also tried:
NODE_TLS_REJECT_UNAUTHORIZED='0' node index.js
How I used openssl:
Create a public/private key file pair:
openssl genrsa -out privkey2.pem 4096
Split into public/private:
openssl ssh-keygen -t rsa -b 4096 -f privatekey2.pem
Create a cert
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout privatekey2.pem -out example.crt
The problem does not seem to be related to the certificate you are using. It should be related to the fact that you provide a path for the certificate authority file (ca). You do this when you want the client to verify that the server certificate was signed using a certificate in the CA chain, which only works if rejectUnauthorized is true, which is automatically set once you provide that valid CA file path, thus, your custom rejectUnauthorized: false is ignored.
Which begs the question, Is the server certificate signed by the CA you are providing? I suspect it is not, in which case, the error is legitimate.
If so, then it is a bug, and you should report it using the public MySQL issue tracker. However, I ran a few tests and I'm convinced there is no issue, but if you are able to produce a server certificate that was, indeed, signed by that particular CA, which allows to reproduce the same exact scenario, maybe we can dig some dirt.
Disclaimer: I'm the lead developer of MySQL X DevAPI Connector for Node.js

Generate SSL certificates for multiple domains in SubjectAltName with Greenlock

I'm using greenlock to generate certificates, I pass it three domains, and only get 2 in my altnames:
const greenlock = Greenlock.create({
agreeTos: true,
email: myemail,
communityMember: false,
version: 'draft-12',
server: 'https://acme-v02.api.letsencrypt.org/directory',
configDir: '/etc/letsencrypt',
debug: true,
log: (debug) => { console.log(debug) },
})
console.log({ domains })
return greenlock.register({
domains,
email: myemail,
challengeType: 'dns-01',
})
.then((result) => {
console.log(result)
})
here are my logs:
{ domains:
[ 'domain1',
'domain3',
'domain2' ] }
true
true
true
{ result:
{
privkey: '-----BEGIN PRIVATE KEY-----\n\n-----END CERTIFICATE-----\n',
chain: '-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----\n',
subject: 'domain2',
altnames: [ 'domain1', 'domain2' ],
_issuedAt: 2018-09-19T14:43:31.000Z,
_expiresAt: 2018-12-18T14:43:31.000Z,
issuedAt: 1537368211000,
expiresAt: 1545144211000 } }
As you can see it's not even my first two domains that end up in my altnames but rather those that where already in the old certificate (not sure this is why tho).
I'm not married to greenlock, if someone as a better alternative I'm listening as well.
I tried passing approveDomains to my greenlock constructor and it doesn't seem to change much.
I still don't have my new domain (domain2) listed in my certificate :
openssl x509 -text < /etc/letsencrypt/live/domain1/fullchain.pem | grep 'DNS:' | sed 's/\s*DNS:\([a-z0-9.\-]*\)[,\s]\?/\1 /g'
domain1 domain3
Use Greenlock v2.7+
All of the code related to certificate generation and domain name and altname association has been updated.
Now when you change the domains array to include more domains it handles them individually rather than as a group.
Also, the information about the certificate is read directly from the certificate, so there can't be a mismatch between the "cache" and "the truth".
If you encounter further issues, please let us know directly:
https://git.rootprojects.org/root/greenlock.js/issues

How to connect from node js to mongodb replica set using SSL

I am trying to connect to a mongodb replica set which is set up to authenticate clients using SSL. I can connect using the mongo shell, but for some reason cannot connect from node.js with the same keys.
I am using mongodb version 3.2.6 and node.js driver version 2.1.18, running on mac.
I followed this article, and was able to setup a cluster on my local machine by running the attached script:
# Prerequisites:
# a. Make sure you have MongoDB Enterprise installed.
# b. Make sure mongod/mongo are in the executable path
# c. Make sure no mongod running on 27017 port, or change the port below
# d. Run this script in a clean directory
##### Feel free to change following section values ####
# Changing this to include: country, province, city, company
dn_prefix="/C=CN/ST=GD/L=Shenzhen/O=MongoDB China"
ou_member="MyServers"
ou_client="MyClients"
mongodb_server_hosts=( "server1" "server2" "server3" )
mongodb_client_hosts=( "client1" "client2" )
mongodb_port=27017
# make a subdirectory for mongodb cluster
kill $(ps -ef | grep mongod | grep set509 | awk '{print $2}')
#rm -Rf db/*
mkdir -p db
echo "##### STEP 1: Generate root CA "
openssl genrsa -out root-ca.key 2048
# !!! In production you will want to use -aes256 to password protect the keys
# openssl genrsa -aes256 -out root-ca.key 2048
openssl req -new -x509 -days 3650 -key root-ca.key -out root-ca.crt -subj "$dn_prefix/CN=ROOTCA"
mkdir -p RootCA/ca.db.certs
echo "01" >> RootCA/ca.db.serial
touch RootCA/ca.db.index
echo $RANDOM >> RootCA/ca.db.rand
mv root-ca* RootCA/
echo "##### STEP 2: Create CA config"
# Generate CA config
cat >> root-ca.cfg <<EOF
[ RootCA ]
dir = ./RootCA
certs = \$dir/ca.db.certs
database = \$dir/ca.db.index
new_certs_dir = \$dir/ca.db.certs
certificate = \$dir/root-ca.crt
serial = \$dir/ca.db.serial
private_key = \$dir/root-ca.key
RANDFILE = \$dir/ca.db.rand
default_md = sha256
default_days = 365
default_crl_days= 30
email_in_dn = no
unique_subject = no
policy = policy_match
[ SigningCA ]
dir = ./SigningCA
certs = \$dir/ca.db.certs
database = \$dir/ca.db.index
new_certs_dir = \$dir/ca.db.certs
certificate = \$dir/signing-ca.crt
serial = \$dir/ca.db.serial
private_key = \$dir/signing-ca.key
RANDFILE = \$dir/ca.db.rand
default_md = sha256
default_days = 365
default_crl_days= 30
email_in_dn = no
unique_subject = no
policy = policy_match
[ policy_match ]
countryName = match
stateOrProvinceName = match
localityName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true
EOF
echo "##### STEP 3: Generate signing key"
# We do not use root key to sign certificate, instead we generate a signing key
openssl genrsa -out signing-ca.key 2048
# !!! In production you will want to use -aes256 to password protect the keys
# openssl genrsa -aes256 -out signing-ca.key 2048
openssl req -new -days 1460 -key signing-ca.key -out signing-ca.csr -subj "$dn_prefix/CN=CA-SIGNER"
openssl ca -batch -name RootCA -config root-ca.cfg -extensions v3_ca -out signing-ca.crt -infiles signing-ca.csr
mkdir -p SigningCA/ca.db.certs
echo "01" >> SigningCA/ca.db.serial
touch SigningCA/ca.db.index
# Should use a better source of random here..
echo $RANDOM >> SigningCA/ca.db.rand
mv signing-ca* SigningCA/
# Create root-ca.pem
cat RootCA/root-ca.crt SigningCA/signing-ca.crt > root-ca.pem
echo "##### STEP 4: Create server certificates"
# Now create & sign keys for each mongod server
# Pay attention to the OU part of the subject in "openssl req" command
# You may want to use FQDNs instead of short hostname
for host in "${mongodb_server_hosts[#]}"; do
echo "Generating key for $host"
openssl genrsa -out ${host}.key 2048
openssl req -new -days 365 -key ${host}.key -out ${host}.csr -subj "$dn_prefix/OU=$ou_member/CN=${host}"
openssl ca -batch -name SigningCA -config root-ca.cfg -out ${host}.crt -infiles ${host}.csr
cat ${host}.crt ${host}.key > ${host}.pem
done
echo "##### STEP 5: Create client certificates"
# Now create & sign keys for each client
# Pay attention to the OU part of the subject in "openssl req" command
for host in "${mongodb_client_hosts[#]}"; do
echo "Generating key for $host"
openssl genrsa -out ${host}.key 2048
openssl req -new -days 365 -key ${host}.key -out ${host}.csr -subj "$dn_prefix/OU=$ou_client/CN=${host}"
openssl ca -batch -name SigningCA -config root-ca.cfg -out ${host}.crt -infiles ${host}.csr
cat ${host}.crt ${host}.key > ${host}.pem
done
echo ""
echo "##### STEP 6: Start up replicaset in non-auth mode"
mport=$mongodb_port
for host in "${mongodb_server_hosts[#]}"; do
echo "Starting server $host in non-auth mode"
mkdir -p ./db/${host}
mongod --replSet set509 --port $mport --dbpath ./db/$host \
--fork --logpath ./db/${host}.log
let "mport++"
done
sleep 3
# obtain the subject from the client key:
client_subject=`openssl x509 -in ${mongodb_client_hosts[0]}.pem -inform PEM -subject -nameopt RFC2253 | grep subject | awk '{sub("subject= ",""); print}'`
echo "##### STEP 7: setup replicaset & initial user role\n"
myhostname=`hostname`
cat > setup_auth.js <<EOF
rs.initiate();
mport=$mongodb_port;
mport++;
rs.add("$myhostname:" + mport);
mport++;
rs.add("$myhostname:" + mport);
sleep(5000);
db.getSiblingDB("\$external").runCommand(
{
createUser: "$client_subject",
roles: [
{ role: "readWrite", db: 'test' },
{ role: "userAdminAnyDatabase", db: "admin" },
{ role: "clusterAdmin", db:"admin"}
],
writeConcern: { w: "majority" , wtimeout: 5000 }
}
);
EOF
cat setup_auth.js
mongo localhost:$mongodb_port setup_auth.js
kill $(ps -ef | grep mongod | grep set509 | awk '{print $2}')
sleep 3
echo "##### STEP 8: Restart replicaset in x.509 mode\n"
mport=$mongodb_port
for host in "${mongodb_server_hosts[#]}"; do
echo "Starting server $host"
mongod --replSet set509 --port $mport --dbpath ./db/$host \
--sslMode requireSSL --clusterAuthMode x509 --sslCAFile root-ca.pem \
--sslAllowInvalidHostnames --fork --logpath ./db/${host}.log \
--sslPEMKeyFile ${host}.pem --sslClusterFile ${host}.pem
let "mport++"
done
# echo "##### STEP 9: Connecting to replicaset using certificate\n"
cat > do_login.js <<EOF
db.getSiblingDB("\$external").auth(
{
mechanism: "MONGODB-X509",
user: "$client_subject"
}
)
EOF
# mongo --ssl --sslPEMKeyFile client1.pem --sslCAFile root-ca.pem --sslAllowInvalidHostnames --shell do_login.js
After running the cluster, I am able to connect to it using the mongo shell with this command (all keys\certs were generated in ./ssl dir):
mongo --ssl --sslPEMKeyFile ssl/client1.pem --sslCAFile ssl/root-ca.pem --sslAllowInvalidHostnames
and authenticate as follows:
db.getSiblingDB("$external").auth(
{
mechanism: "MONGODB-X509",
user: "CN=client1,OU=MyClients,O=MongoDB China,L=Shenzhen,ST=GD,C=CN"
}
)
When I try to connect from node.js I keep failing. I am running the following code to connect to mongo using the native mongo driver:
'use strict';
const mongodb = require('mongodb');
const P = require('bluebird');
const fs = require('fs');
function connect_mongodb() {
let user = 'CN=client1,OU=MyClients,O=MongoDB China,L=Shenzhen,ST=GD,C=CN';
let uri = `mongodb://${encodeURIComponent(user)}#localhost:27017,localhost:27018,localhost:27019/test?replicaSet=set509&authMechanism=MONGODB-X509&ssl=true`;
var ca = [fs.readFileSync("./ssl/root-ca.pem")];
var cert = fs.readFileSync("./ssl/client1.pem");
var key = fs.readFileSync("./ssl/client1.pem");
let options = {
promiseLibrary: P,
server: {
ssl: true,
sslValidate: false,
checkServerIdentity: false,
sslCA: ca,
sslKey: key,
sslCert: cert,
},
replset: {
sslValidate: false,
checkServerIdentity: false,
ssl: true,
sslCA: ca,
sslKey: key,
sslCert: cert,
}
};
return mongodb.MongoClient.connect(uri, options);
}
connect_mongodb();
When running the script I get the following error:
Unhandled rejection MongoError: no valid seed servers in list
When checking the mongodb log I see these errors:
2017-01-17T22:48:54.191+0200 I NETWORK [initandlisten] connection accepted from 127.0.0.1:63881 #99 (5 connections now open)
2017-01-17T22:48:54.207+0200 E NETWORK [conn99] no SSL certificate provided by peer; connection rejected
2017-01-17T22:48:54.207+0200 I NETWORK [conn99] end connection 127.0.0.1:63881 (4 connections now open)
I was trying different options described here, but with no success.
Thanks for the help
Upgrading to mongodb node js driver 2.2.22 solved the problem

Resources