Connecting to IBM Cloud Databases for Redis from Node.js

James Thomas - Jul 22 '19 - - Dev Community

This blog post explains how to connect to an IBM Cloud Databases for Redis instance from a Node.js application. There is a (small) difference between the connection details needed for an IBM Cloud Databases for Redis instance compared to a local instance of the open-source database. This is due to all IBM Cloud Databases using secured TLS connections with self-signed certificates.

I keep running into this issue (and forgetting how to fix it 🤦‍♂️), so I'm documenting the solution here to help myself (and others) who might run into it… 🦸‍♂️

Connecting to Redis (without TLS connections)

Most Node.js application use the redis NPM library to interact with an instance of the database. This library has a createClient method which returns an instance of the client. The Node.js application passes a connection string into the createClient method. This string contains the hostname, port, username and password for the database instance.

const redis = require("redis"),
const url = 'redis://user:secret@localhost:6379/'
const client = redis.createClient(url);
Enter fullscreen mode Exit fullscreen mode

The client fires a connect event once the connection is established or an error event if issues are encountered.

IBM Cloud Databases for Redis Service Credentials

IBM Cloud Databases for Redis provide service credentials through the instance management console. Service credentials are JSON objects with connection properties for client libraries, the CLI and other tools. Connection strings for the Node.js client library are available in the connection.rediss.composed field.

So, I just copy this field value and use with the redis.createClient method? Not so fast...

IBM Cloud Databases for Redis uses TLS to secure all connections to the Redis instances. This is denoted by the connection string using the rediss:// URL prefix, rather than redis://. Using that connection string (without further connection properties), will lead to the following error being thrown by the Node.js application.

Error: Redis connection to <id>.databases.appdomain.cloud:port failed - read ECONNRESET
  at TCP.onread (net.js:657:25) errno: 'ECONNRESET', code: 'ECONNRESET', syscall: 'read'
Enter fullscreen mode Exit fullscreen mode

If the createClient forces a TLS connection to be used createClient(url, { tls: {} }), this error will be replaced with a different one about self-signed certificates.

Error: Redis connection to <id>.databases.appdomain.cloud:port failed failed - self signed certificate in certificate chain
    at TLSSocket.onConnectSecure (_tls_wrap.js:1055:34)
    at TLSSocket.emit (events.js:182:13)
    at TLSSocket._finishInit (_tls_wrap.js:635:8) code: 'SELF_SIGNED_CERT_IN_CHAIN'
Enter fullscreen mode Exit fullscreen mode

Hmmmm, how to fix this? 🤔

Connecting to Redis (with TLS connections)

All connections to IBM Cloud Databases are secured with TLS using self-signed certificates. Public certificates for the signing authorities are provided as Base64 strings in the service credentials. These certificates can be provided in the client constructor to support self-signed TLS connections.

Here are the steps needed to use those self-signed certificates with the client library...

  • Extract the connection.rediss.certificate.certificate_base64 value from the service credentials.

Redis Service Credentials

  • Decode the Base64 string in Node.js to extract the PEM certificate string.
const ca = Buffer.from(cert_base64, 'base64').toString('utf-8')
Enter fullscreen mode Exit fullscreen mode
  • Provide the certificate file string as the ca property in the tls object for the client constructor.
const tls = { ca };
const client = redis.createClient(url, { tls });
Enter fullscreen mode Exit fullscreen mode
  • …Relax! 😎

The tls property is passed through to the tls.connect method in Node.js, which is used to setup the TLS connection. This method supports a ca parameter to extend the trusted CA certificates pre-installed in the system. By providing the self-signed certificate using this property, the errors above will not be seen.

Conclusion

It took me a while to work out how to connect to TLS-secured Redis instances from a Node.js application. Providing the self-signed certificate in the client constructor is a much better solution than having to disable all unauthorised TLS connections!

Since I don't write new Redis client code very often, I keep forgetting the correct constructor parameters to make this work. Turning this solution into a blog post will (hopefully) embed it in my brain (or at least provide a way to find the answer instead of having to grep through old project code). This might even be useful to others Googling for a solution to those error messages...

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .