porta icon indicating copy to clipboard operation
porta copied to clipboard

THREESCALE-8404: Add TLS and ACL support for Redis

Open jlledom opened this issue 2 years ago • 9 comments

What this PR does / why we need it:

This implements support for ACL and TLS credentials for Redis from Porta.

Which issue(s) this PR fixes

THREESCALE-8404

Verification steps

This requires three steps:

  1. Generate TLS certificates and keys for Redis.
  2. Configure Redis to enable ACL and TLS, and use the generated keys.
  3. Configure Porta. 3.1. TLS Client 3.2. Mutual TLS Client

Generate keys and certificates:

The first thing we need is a certification authority. For that, we need to create its key and certificate.

  • key: openssl genrsa -out ca-root-key.pem 4096
  • cert: openssl req -new -x509 -days 365 -key ca-root-key.pem -out ca-root-cert.pem

Then we'll have to create a key and certificate for the server, and sign it with our CA. Ensure setting localhost as CN, if that's the domain you're going to use to connect to Redis:

  • key: openssl genrsa -out redis.key 4096
  • cert request: openssl req -new -key redis.key -out redis.csr
  • signed cert: openssl x509 -req -days 365 -in redis.csr -CA ca-root-cert.pem -CAkey ca-root-key.pem -CAcreateserial -out redis.crt

Configure Redis:

First, we need a Redis configuration file which enables ACL and TLS, defines the users and sets the certificate the server will use and the CA the server will trust. This is the minimal redis.conf for that purpose:

port 6379
tls-port 26379
tls-cert-file /etc/redis.crt
tls-key-file /etc/redis.key
tls-ca-cert-file /etc/ca-root-cert.pem
tls-auth-clients optional
user default off
user porta on >sup3rS3cre1! ~* &* +@all
user apisonator on >secret#Passw0rd ~* &* +@all

Next step is to launch a container that has access to the certificates, keys and Redis config files created above. We create a volume for each file and modify the container start command to reference the configuration file we created. This is how I configured the redis pod in my docker-compose.yml. But the container can be launched by other ways:

  redis:
    image: redis:6.2-alpine
    container_name: redis-compose-ssl
    ports:
      - "26379:26379"
    volumes:
      - /home/jlledom/redis.conf:/etc/redis.conf:Z
      - /home/jlledom/redis.crt:/etc/redis.crt:z
      - /home/jlledom/redis.key:/etc/redis.key:z
      - /home/jlledom/ca-root-cert.pem:/etc/ca-root-cert.pem:z
    command: [redis-server, /etc/redis.conf]

The :z at the end of the volume definitions is required when using Selinux, for instance in Fedora.

Configure Porta:

Porta can be configured as a regular TLS client, when only the server needs a certificate, or a Mutual TLS Client, where both client and server must provide a certificate.

As TLS Client:

Update our config files to add a new base configuration:

redis-yml:

ssl: &ssl
  url: "<%= ENV.fetch('REDIS_URL', 'rediss://localhost:26379/5') %>"
  pool_size: <%= ENV.fetch('RAILS_MAX_THREADS', 5) %>
  pool_timeout: 5 # this is in seconds
  namespace: "<%= ENV['REDIS_NAMESPACE'] %>"
  sentinels: "<%= ENV['REDIS_SENTINEL_HOSTS'] %>"
  role: <%= ENV['REDIS_SENTINEL_ROLE'] %>
  username: porta
  password: sup3rS3cre1!
  ssl_params:
    ca_file: /home/jlledom/ca-root-cert.pem

backend-redis-yml:

ssl: &ssl
  url: "<%= ENV.fetch('BACKEND_REDIS_URL', 'rediss://localhost:26379/6') %>"
  pool_size: <%= ENV.fetch('RAILS_MAX_THREADS', 5) %>
  pool_timeout: 5 # this is in seconds
  sentinels: "<%= ENV['BACKEND_REDIS_SENTINEL_HOSTS'] %>"
  role: <%= ENV['BACKEND_REDIS_SENTINEL_ROLE'] %>
  username: porta
  password: sup3rS3cre1!
  ssl_params:
    ca_file: /home/jlledom/ca-root-cert.pem

Note how the ssl_params section is not always required. We are using it here because the server is using a certificate signed by an unknown authority we've just created, so we need to explicitly tell Porta to trust that authority. If the server were using a certificate signed by any of the well-known CAs, the ssl_params section could be omitted.

Another possible situation is when the server is using a self-signed certificate. When this happens, there's no trusted CA to add to ssl_params, so the only way to go is to skip certificate validation. Unfortunately we don't support this yet because the redis-client gem still haven't added support for verify modes (see: https://github.com/redis-rb/redis-client/issues/133) so we can't set SSL_VERIFY_NONE to the client. Anyway this is only useful for development purposes, we'll have to use our own CA for development for the moment.

As Mutual TLS Cliet:

Generate a key and a singed certificate for Porta:

  • key: openssl genrsa -out porta.key 4096
  • cert request: openssl req -new -key porta.key -out porta.csr
  • signed cert: openssl x509 -req -days 365 -in porta.csr -CA ca-root-cert.pem -CAkey ca-root-key.pem -CAcreateserial -out porta.crt

Update our config files to add a new base configuration:

redis-yml:

ssl: &ssl
  url: "<%= ENV.fetch('REDIS_URL', 'rediss://localhost:26379/5') %>"
  pool_size: <%= ENV.fetch('RAILS_MAX_THREADS', 5) %>
  pool_timeout: 5 # this is in seconds
  namespace: "<%= ENV['REDIS_NAMESPACE'] %>"
  sentinels: "<%= ENV['REDIS_SENTINEL_HOSTS'] %>"
  role: <%= ENV['REDIS_SENTINEL_ROLE'] %>
  username: porta
  password: sup3rS3cre1!
  ssl_params:
    ca_file: /home/jlledom/ca-root-cert.pem
    cert: /home/jlledom/porta.crt
    key: /home/jlledom/porta.key

backend-redis-yml:

ssl: &ssl
  url: "<%= ENV.fetch('BACKEND_REDIS_URL', 'rediss://localhost:26379/6') %>"
  pool_size: <%= ENV.fetch('RAILS_MAX_THREADS', 5) %>
  pool_timeout: 5 # this is in seconds
  sentinels: "<%= ENV['BACKEND_REDIS_SENTINEL_HOSTS'] %>"
  role: <%= ENV['BACKEND_REDIS_SENTINEL_ROLE'] %>
  username: porta
  password: sup3rS3cre1!
  ssl_params:
    ca_file: /home/jlledom/ca-root-cert.pem
    cert: /home/jlledom/porta.crt
    key: /home/jlledom/porta.key

As mentioned above, the ca_file field is only required when the server isn't using a certificate signed by one of the well-known CAs.

Enable the TLS Client:

You can switch between TLS and non-TLS client by updating the development environment to use the new base, on both files:

development:
  <<: *ssl

At this point it should work. You can verify the connection using RedisInsight or trying with invalid certs or keys from the porta side, to verify it works fine.

jlledom avatar Sep 27 '23 07:09 jlledom

@akostadinov @mayorova I added some comments

jlledom avatar Sep 28 '23 14:09 jlledom

I updated the PR description to add the use case when Porta doesn't provide a certificate.

jlledom avatar Oct 03 '23 15:10 jlledom

This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Oct 19 '23 03:10 github-actions[bot]

Except for the comment I left here: https://github.com/3scale/porta/pull/3572#discussion_r1372985920 everything seems to be working well! Nice :clap:

mayorova avatar Oct 26 '23 14:10 mayorova

This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Nov 14 '23 03:11 github-actions[bot]

This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Dec 06 '23 03:12 github-actions[bot]

This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Dec 30 '23 03:12 github-actions[bot]

This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Jan 15 '24 03:01 github-actions[bot]

This PR is stale because it has not received activity for more than 14 days. Remove stale label or comment or this will be closed in 7 days.

github-actions[bot] avatar Feb 13 '24 03:02 github-actions[bot]

This PR is stale because it has not received activity for more than 30 days. Remove stale label or comment or this will be closed in 15 days.

github-actions[bot] avatar Apr 11 '24 03:04 github-actions[bot]

Another possible situation is when the server is using a self-signed certificate. When this happens, there's no trusted CA to add to ssl_params, so the only way to go is to skip certificate validation.

With self-signed certificate, you should be able to use the certificate itself as CA too. Should work OOB or maybe you need also to mark it as CA when creating.

akostadinov avatar Apr 26 '24 06:04 akostadinov