Secure SSL Redis connections

ServiceStack.Redis client includes support for SSL making it suitable for accessing remote Redis server instances over a secure SSL connection.

Redis is normally used as a back-end datastore whose access is typically limited to Internal networks or authorized networks protected via firewalls, when communicating outside of your VPC you'll typically want to communicate over SSL.

General SSL Info

ServiceStack.Redis uses .NET's SslStream to establish its SSL connection where you can configure its RemoteCertificateValidationCallback to validate whether to accept the specified certificate for authentication:

RedisConfig.CertificateValidationCallback = (object sender,
    X509Certificate certificate,
    X509Chain chain,
    SslPolicyErrors sslPolicyErrors) => {
    //...
};

And a LocalCertificateSelectionCallback to select the local SSL certificate used for authentication:

RedisConfig.CertificateSelectionCallback = (object sender,
    string targetHost,
    X509CertificateCollection localCertificates,
    X509Certificate remoteCertificate,
    string[] acceptableIssuers) => {
    //...
}

Specify SSL and TLS Version

You can specify to use SSL and what TLS version to use on your connection string, e.g:

var connString = $"redis://{Host}?ssl=true&sslprotocols=Tls12&password={Password.UrlEncode()}";
var redisManager = new RedisManagerPool(connString);
using var client = redisManager.GetClient();
//...

If using RedisSentinel SSL needs to be specified on both host connection string and HostFilter, e.g:

var sentinel = new RedisSentinel(connString, masterName) {
    HostFilter = host => $"{host}?ssl=true&sslprotocols=Tls12"
};
container.Register(c => sentinel.Start());

Secure SSL Redis connections to Azure Redis

The SSL Support in the Redis Client also enables secure access to a redis-server instance over the Internet and public networks as well, a scenario that's been recently popularized by Cloud hosting environments like Azure Redis Cache.

Getting Started

First we'll need a Redis instance to connect to, for this example we will be using Azure, so you will need an active account which you can create at the Azure home page.

Once you have access to the Azure portal, we can create a new Redis instance with 3 easy steps:

1 . Click New at the bottom left of the portal

New

2 . Select Redis Cache from the list

Redis instance menu item

3 . Specify a DNS name, size, resource group and location

Create instance

It may take a while for the Redis Cache instance to be ready to use, from experience with a 1GB Basic instance, it takes around 15+ minutes.

Once the instance is ready, we can grab the password generated for us from the Keys tile. If we copy the key, and store it somewhere our host can access it, we can start connecting to Redis.

Created Redis Instance and Keys menu

Configuring our AppHost

We have all the require information and our Redis Cache instance is running, we can now configure our AppHost to use it!

To make it simple to use the IRedisClient from our web service methods, we can register an IRedisClientsManager to handle the clients for us. This way, we can use the base.Redis property from our services to access the Redis server instance.

public override void Configure(Container container)
{
    var connString = AppSettings.GetString("RedisConnectionString");
    container.Register<IRedisClientsManager>(c => 
        new RedisManagerPool(connectionString));
}

We are getting our Redis connection string from our Web.Config <appSettings/>. The Redis connection string is made up of the domain name of the instance parsing query string parameters to specify other configuration options. In our example the format of the connection string looks like:

{AzureRedisKey}@servicestackdemo.redis.cache.windows.net?ssl=true 

Accessing Redis from a Web Service

We can now try a simple example to test that our instance is working. Here's an example of a simple "Todo" service that persists items to our Redis instance:

// Create your ServiceStack Web Service
public class TodoService : Service
{
    public object Get(Todo todo)
    {
        //Return a single Todo if the id is provided.
        if (todo.Id != default(long))
            return Redis.As<Todo>().GetById(todo.Id);

        //Return all Todos items.
        return Redis.As<Todo>().GetAll();
    }

    // Handles creating the Todo items.
    public Todo Post(Todo todo)
    {
        var todos = Redis.As<Todo>();

        //Get next id for new todo
        todo.Id = todos.GetNextSequence();

        todos.Store(todo);

        return todo;
    }

    // Handles updating the Todo items.
    public Todo Put(Todo todo)
    {
        Redis.As<Todo>().Store(todo);
        return todo;
    }

    // Handles Deleting the Todo item
    public void Delete(Todo todo)
    {
        Redis.As<Todo>().DeleteById(todo.Id);
    }
}

[Route("/todo")]
[Route("/todo/{Id}")]
public class Todo
{
    public long Id { get; set; }
    public string Content { get; set; }
    public int Order { get; set; }
    public bool Done { get; set; }
}

You can try a redis-powered TODO's web app like this in our todos.netcore.io Live Demo. A more comprehensive example can be seen in the Repository.cs used to power the RedisStackOverflow Live Demo.

Another property automatically injected when registering our RedisManagerPool is base.Cache which provides a substitutable Caching abstraction our Services can use to cache results. E.g. we can make use of ServiceStack's high-level Caching API in Request.ToOptimizedResultUsingCache to enable maximum performance of your Services:

public object Get(CachedCustomers request)
{
    return base.Request.ToOptimizedResultUsingCache(this.Cache, 
        "urn:customers", () =>
            this.ResolveService<CustomersService>()
            .Get(new Customers()));
}

This snippet is from the Northwind example.

Troubleshooting

Add Required SSL Certificates to work in Mono

Connecting to Azure Redis Cache via SSL requires Microsoft SSL Certificates. The required certificates can be imported and registered with Mono by running the commands below:

$ sudo mozroots --import --machine --sync
$ sudo certmgr -ssl -m https://go.microsoft.com
$ sudo certmgr -ssl -m https://nugetgallery.blob.core.windows.net
$ sudo certmgr -ssl -m https://nuget.org