Use TLS proxy for Redis' internal TLS
authorMartin André <m.andre@redhat.com>
Wed, 23 Aug 2017 10:44:42 +0000 (12:44 +0200)
committerEmilien Macchi <emilien@redhat.com>
Tue, 5 Sep 2017 22:55:43 +0000 (22:55 +0000)
This uses the tls_proxy resource in front of the Redis server when
internal TLS is enabled.

bp tls-via-certmonger

Co-Authored-By: Juan Antonio Osorio Robles <jaosorior@redhat.com>
Change-Id: Ia50933da9e59268b17f56db34d01dcc6b6c38147
(cherry picked from commit 2d1d7875aa6f0b68005c84189627bc0716a7693f)

manifests/certmonger/redis.pp [new file with mode: 0644]
manifests/haproxy.pp
manifests/profile/base/aodh/evaluator.pp
manifests/profile/base/ceilometer/agent/central.pp
manifests/profile/base/ceilometer/agent/polling.pp
manifests/profile/base/certmonger_user.pp
manifests/profile/base/database/redis.pp
manifests/profile/base/gnocchi/api.pp
manifests/profile/pacemaker/database/redis.pp

diff --git a/manifests/certmonger/redis.pp b/manifests/certmonger/redis.pp
new file mode 100644 (file)
index 0000000..1b3b119
--- /dev/null
@@ -0,0 +1,72 @@
+# Copyright 2017 Red Hat, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# == Class: tripleo::certmonger::redis
+#
+# Request a certificate for RabbitMQ and do the necessary setup.
+#
+# === Parameters
+#
+# [*hostname*]
+#   The hostname of the node. this will be set in the CN of the certificate.
+#
+# [*service_certificate*]
+#   The path to the certificate that will be used for TLS in this service.
+#
+# [*service_key*]
+#   The path to the key that will be used for TLS in this service.
+#
+# [*certmonger_ca*]
+#   (Optional) The CA that certmonger will use to generate the certificates.
+#   Defaults to hiera('certmonger_ca', 'local').
+#
+# [*postsave_cmd*]
+#   (Optional) Specifies the command to execute after requesting a certificate.
+#   If nothing is given, it will default to: "systemctl restart ${service name}"
+#   Defaults to undef.
+#
+# [*principal*]
+#   (Optional) The service principal that is set for the service in kerberos.
+#   Defaults to undef
+#
+class tripleo::certmonger::redis (
+  $hostname,
+  $service_certificate,
+  $service_key,
+  $certmonger_ca = hiera('certmonger_ca', 'local'),
+  $postsave_cmd  = undef,
+  $principal     = undef,
+) {
+  include ::certmonger
+
+  certmonger_certificate { 'redis' :
+    ensure       => 'present',
+    certfile     => $service_certificate,
+    keyfile      => $service_key,
+    hostname     => $hostname,
+    dnsname      => $hostname,
+    principal    => $principal,
+    postsave_cmd => $postsave_cmd,
+    ca           => $certmonger_ca,
+    wait         => true,
+    require      => Class['::certmonger'],
+  }
+
+  file { $service_certificate :
+    require => Certmonger_certificate['redis'],
+  }
+  file { $service_key :
+    require => Certmonger_certificate['redis'],
+  }
+}
index e41f0e6..9386036 100644 (file)
@@ -1373,11 +1373,19 @@ class tripleo::haproxy (
   }
 
   if $redis {
+    if $enable_internal_tls {
+      $redis_tcp_check_ssl_options = ['connect ssl']
+      $redis_ssl_member_options = ['check-ssl', "ca-file ${ca_bundle}"]
+    } else {
+      $redis_tcp_check_ssl_options = []
+      $redis_ssl_member_options = []
+    }
     if $redis_password {
-      $redis_tcp_check_options = ["send AUTH\\ ${redis_password}\\r\\n"]
+      $redis_tcp_check_password_options = ["send AUTH\\ ${redis_password}\\r\\n"]
     } else {
-      $redis_tcp_check_options = []
+      $redis_tcp_check_password_options = []
     }
+    $redis_tcp_check_options = union($redis_tcp_check_ssl_options, $redis_tcp_check_password_options)
     haproxy::listen { 'redis':
       bind             => $redis_bind_opts,
       options          => {
@@ -1397,7 +1405,8 @@ class tripleo::haproxy (
       ports             => '6379',
       ipaddresses       => hiera('redis_node_ips', $controller_hosts_real),
       server_names      => hiera('redis_node_names', $controller_hosts_names_real),
-      options           => $haproxy_member_options,
+      options           => union($haproxy_member_options, $redis_ssl_member_options),
+      verifyhost        => false,
     }
     if $manage_firewall {
       include ::tripleo::firewall
index 1b25b37..9b3462f 100644 (file)
 #
 # === Parameters
 #
+# [*enable_internal_tls*]
+#   (Optional) Whether TLS in the internal network is enabled or not.
+#   Defaults to hiera('enable_internal_tls', false)
+#
 # [*step*]
 #   (Optional) The current step in deployment. See tripleo-heat-templates
 #   for more details.
 #   Defaults to hiera('step')
 #
 class tripleo::profile::base::aodh::evaluator (
-  $step = Integer(hiera('step')),
+  $enable_internal_tls = hiera('enable_internal_tls', false),
+  $step                = Integer(hiera('step')),
 ) {
 
   include ::tripleo::profile::base::aodh
+  if $enable_internal_tls {
+    $tls_query_param = '?ssl=true'
+  } else {
+    $tls_query_param = ''
+  }
 
   if $step >= 4 {
     class { '::aodh::evaluator':
-      coordination_url => join(['redis://:', hiera('aodh_redis_password'), '@', normalize_ip_for_uri(hiera('redis_vip')), ':6379/']),
+      coordination_url => join(['redis://:', hiera('aodh_redis_password'), '@', normalize_ip_for_uri(hiera('redis_vip')), ':6379/', $tls_query_param]),
     }
   }
 
index b8f5d07..955917c 100644 (file)
 #
 # === Parameters
 #
+# [*enable_internal_tls*]
+#   (Optional) Whether TLS in the internal network is enabled or not.
+#   Defaults to hiera('enable_internal_tls', false)
+#
 # [*step*]
 #   (Optional) The current step in deployment. See tripleo-heat-templates
 #   for more details.
 #   Defaults to hiera('step')
 #
 class tripleo::profile::base::ceilometer::agent::central (
-  $step = Integer(hiera('step')),
+  $enable_internal_tls = hiera('enable_internal_tls', false),
+  $step                = Integer(hiera('step')),
 ) {
   include ::tripleo::profile::base::ceilometer
 
+  if $enable_internal_tls {
+    $tls_query_param = '?ssl=true'
+  } else {
+    $tls_query_param = ''
+  }
+
   if $step >= 4 {
     include ::ceilometer::agent::auth
     class { '::ceilometer::agent::central':
-      coordination_url => join(['redis://:', hiera('ceilometer_redis_password'), '@', normalize_ip_for_uri(hiera('redis_vip')), ':6379/']),
+      coordination_url => join(['redis://:', hiera('ceilometer_redis_password'), '@', normalize_ip_for_uri(hiera('redis_vip')), ':6379/', $tls_query_param]),
     }
   }
 
index 84f5e46..043b5cd 100644 (file)
 #   (Optional) Use compute namespace for polling agent.
 #   Defaults to false.
 #
+# [*enable_internal_tls*]
+#   (Optional) Whether TLS in the internal network is enabled or not.
+#   Defaults to hiera('enable_internal_tls', false)
+#
 # [*ipmi_namespace*]
 #   (Optional) Use ipmi namespace for polling agent.
 #   Defaults to false.
@@ -44,6 +48,7 @@
 class tripleo::profile::base::ceilometer::agent::polling (
   $central_namespace         = hiera('central_namespace', false),
   $compute_namespace         = hiera('compute_namespace', false),
+  $enable_internal_tls       = hiera('enable_internal_tls', false),
   $ipmi_namespace            = hiera('ipmi_namespace', false),
   $ceilometer_redis_password = hiera('ceilometer_redis_password', undef),
   $redis_vip                 = hiera('redis_vip', undef),
@@ -55,13 +60,19 @@ class tripleo::profile::base::ceilometer::agent::polling (
     include ::tripleo::profile::base::ceilometer::upgrade
   }
 
+  if $enable_internal_tls {
+    $tls_query_param = '?ssl=true'
+  } else {
+    $tls_query_param = ''
+  }
+
   if $step >= 4 {
     include ::ceilometer::agent::auth
     class { '::ceilometer::agent::polling':
       central_namespace => $central_namespace,
       compute_namespace => $compute_namespace,
       ipmi_namespace    => $ipmi_namespace,
-      coordination_url  => join(['redis://:', $ceilometer_redis_password, '@', normalize_ip_for_uri($redis_vip), ':6379/']),
+      coordination_url  => join(['redis://:', $ceilometer_redis_password, '@', normalize_ip_for_uri($redis_vip), ':6379/', $tls_query_param]),
     }
   }
 }
index cc29cd5..54d9e15 100644 (file)
 #   it will create.
 #   Defaults to hiera('tripleo::profile::base::rabbitmq::certificate_specs', {}).
 #
+# [*redis_certificate_specs*]
+#   (Optional) The specifications to give to certmonger for the certificate(s)
+#   it will create.
+#   Defaults to hiera('redis_certificate_specs', {}).
+#
 # [*etcd_certificate_specs*]
 #   (Optional) The specifications to give to certmonger for the certificate(s)
 #   it will create.
@@ -93,6 +98,7 @@ class tripleo::profile::base::certmonger_user (
   $mongodb_certificate_specs  = hiera('mongodb_certificate_specs',{}),
   $mysql_certificate_specs    = hiera('tripleo::profile::base::database::mysql::certificate_specs', {}),
   $rabbitmq_certificate_specs = hiera('tripleo::profile::base::rabbitmq::certificate_specs', {}),
+  $redis_certificate_specs    = hiera('redis_certificate_specs', {}),
   $etcd_certificate_specs     = hiera('tripleo::profile::base::etcd::certificate_specs', {}),
 ) {
   unless empty($haproxy_certificates_specs) {
@@ -137,6 +143,9 @@ class tripleo::profile::base::certmonger_user (
   unless empty($rabbitmq_certificate_specs) {
     ensure_resource('class', 'tripleo::certmonger::rabbitmq', $rabbitmq_certificate_specs)
   }
+  unless empty($redis_certificate_specs) {
+    ensure_resource('class', 'tripleo::certmonger::redis', $redis_certificate_specs)
+  }
   unless empty($etcd_certificate_specs) {
     ensure_resource('class', 'tripleo::certmonger::etcd', $etcd_certificate_specs)
   }
index e357359..8d4ed94 100644 (file)
 #   (Optional) Hostname of Redis master
 #   Defaults to hiera('bootstrap_nodeid')
 #
+# [*certificate_specs*]
+#   (Optional) The specifications to give to certmonger for the certificate(s)
+#   it will create.
+#   Example with hiera:
+#     redis_certificate_specs:
+#       hostname: <overcloud controller fqdn>
+#       service_certificate: <service certificate path>
+#       service_key: <service key path>
+#       principal: "haproxy/<overcloud controller fqdn>"
+#   Defaults to hiera('redis_certificate_specs', {}).
+#
+# [*enable_internal_tls*]
+#   (Optional) Whether TLS in the internal network is enabled or not.
+#   Defaults to hiera('enable_internal_tls', false)
+#
+# [*redis_network*]
+#   (Optional) The network name where the redis endpoint is listening on.
+#   This is set by t-h-t.
+#   Defaults to hiera('redis_network', undef)
+#
 # [*redis_node_ips*]
 #   (Optional) List of Redis node ips
 #   Defaults to hiera('redis_node_ips')
 #   for more details.
 #   Defaults to hiera('step')
 #
+# [*tls_proxy_bind_ip*]
+#   IP on which the TLS proxy will listen on. Required only if
+#   enable_internal_tls is set.
+#   Defaults to undef
+#
+# [*tls_proxy_fqdn*]
+#   fqdn on which the tls proxy will listen on. required only used if
+#   enable_internal_tls is set.
+#   defaults to undef
+#
+# [*tls_proxy_port*]
+#   port on which the tls proxy will listen on. Only used if
+#   enable_internal_tls is set.
+#   defaults to 6379
+#
 class tripleo::profile::base::database::redis (
-  $bootstrap_nodeid = hiera('bootstrap_nodeid'),
-  $redis_node_ips   = hiera('redis_node_ips'),
-  $step             = Integer(hiera('step')),
+  $bootstrap_nodeid    = hiera('bootstrap_nodeid'),
+  $certificate_specs  = hiera('redis_certificate_specs', {}),
+  $enable_internal_tls = hiera('enable_internal_tls', false),
+  $redis_network       = hiera('redis_network', undef),
+  $redis_node_ips      = hiera('redis_node_ips'),
+  $step                = Integer(hiera('step')),
+  $tls_proxy_bind_ip   = undef,
+  $tls_proxy_fqdn      = undef,
+  $tls_proxy_port      = 6379,
 ) {
   if $step >= 2 {
+    if $enable_internal_tls {
+      if !$redis_network {
+        fail('redis_network is not set in the hieradata.')
+      }
+      if !$tls_proxy_bind_ip {
+        fail('tls_proxy_bind_ip is not set in the hieradata.')
+      }
+      if !$tls_proxy_fqdn {
+        fail('tls_proxy_fqdn is required if internal TLS is enabled.')
+      }
+      $tls_certfile = $certificate_specs['service_certificate']
+      $tls_keyfile = $certificate_specs['service_key']
+
+      include ::tripleo::stunnel
+
+      ::tripleo::stunnel::service_proxy { 'redis':
+        accept_host  => $tls_proxy_bind_ip,
+        accept_port  => $tls_proxy_port,
+        connect_port => $tls_proxy_port,
+        certificate  => $tls_certfile,
+        key          => $tls_keyfile,
+        notify       => Class['::redis'],
+      }
+    }
     if downcase($bootstrap_nodeid) == $::hostname {
       $slaveof = undef
     } else {
index 88177fd..c958359 100644 (file)
@@ -84,9 +84,11 @@ class tripleo::profile::base::gnocchi::api (
     }
     $tls_certfile = $certificates_specs["httpd-${gnocchi_network}"]['service_certificate']
     $tls_keyfile = $certificates_specs["httpd-${gnocchi_network}"]['service_key']
+    $tls_query_param = '?ssl=true'
   } else {
     $tls_certfile = undef
     $tls_keyfile = undef
+    $tls_query_param = ''
   }
 
   if $step >= 4 and $sync_db {
@@ -104,11 +106,11 @@ class tripleo::profile::base::gnocchi::api (
 
   if $step >= 4 {
     class { '::gnocchi::storage':
-      coordination_url => join(['redis://:', $gnocchi_redis_password, '@', normalize_ip_for_uri($redis_vip), ':6379/']),
+      coordination_url => join(['redis://:', $gnocchi_redis_password, '@', normalize_ip_for_uri($redis_vip), ':6379/', $tls_query_param]),
     }
 
     class { '::gnocchi::storage::incoming::redis':
-      redis_url => join(['redis://:', $gnocchi_redis_password, '@', normalize_ip_for_uri($redis_vip), ':6379/']),
+      redis_url => join(['redis://:', $gnocchi_redis_password, '@', normalize_ip_for_uri($redis_vip), ':6379/', $tls_query_param]),
     }
 
     case $gnocchi_backend {
index bc91be7..e6a2bf8 100644 (file)
 #   (Optional) The hostname of the node responsible for bootstrapping tasks
 #   Defaults to hiera('redis_short_bootstrap_node_name')
 #
+# [*certificate_specs*]
+#   (Optional) The specifications to give to certmonger for the certificate(s)
+#   it will create.
+#   Example with hiera:
+#     redis_certificate_specs:
+#       hostname: <overcloud controller fqdn>
+#       service_certificate: <service certificate path>
+#       service_key: <service key path>
+#       principal: "haproxy/<overcloud controller fqdn>"
+#   Defaults to hiera('redis_certificate_specs', {}).
+#
+# [*enable_internal_tls*]
+#   (Optional) Whether TLS in the internal network is enabled or not.
+#   Defaults to hiera('enable_internal_tls', false)
+#
 # [*enable_load_balancer*]
 #   (Optional) Whether load balancing is enabled for this cluster
 #   Defaults to hiera('enable_load_balancer', true)
 #   https://github.com/arioch/puppet-redis/pull/192. Set redis::ulimit via hiera
 #   to control this limit.
 #
+# [*redis_network*]
+#   (Optional) The network name where the redis endpoint is listening on.
+#   This is set by t-h-t.
+#   Defaults to hiera('redis_network', undef)
+#
 # [*pcs_tries*]
 #   (Optional) The number of times pcs commands should be retried.
 #   Defaults to hiera('pcs_tries', 20)
 #
+# [*tls_proxy_bind_ip*]
+#   IP on which the TLS proxy will listen on. Required only if
+#   enable_internal_tls is set.
+#   Defaults to undef
+#
+# [*tls_proxy_fqdn*]
+#   fqdn on which the tls proxy will listen on. required only used if
+#   enable_internal_tls is set.
+#   defaults to undef
+#
+# [*tls_proxy_port*]
+#   port on which the tls proxy will listen on. Only used if
+#   enable_internal_tls is set.
+#   defaults to 6379
+#
 class tripleo::profile::pacemaker::database::redis (
+  $certificate_specs   = hiera('redis_certificate_specs', {}),
+  $enable_internal_tls  = hiera('enable_internal_tls', false),
   $bootstrap_node       = hiera('redis_short_bootstrap_node_name'),
   $enable_load_balancer = hiera('enable_load_balancer', true),
   $step                 = Integer(hiera('step')),
   $redis_file_limit     = undef,
+  $redis_network        = hiera('redis_network', undef),
   $pcs_tries            = hiera('pcs_tries', 20),
+  $tls_proxy_bind_ip    = undef,
+  $tls_proxy_fqdn       = undef,
+  $tls_proxy_port       = 6379,
 ) {
   if $::hostname == downcase($bootstrap_node) {
     $pacemaker_master = true
@@ -57,6 +98,30 @@ class tripleo::profile::pacemaker::database::redis (
   }
 
   if $step >= 1 {
+    if $enable_internal_tls {
+      if !$redis_network {
+        fail('redis_network is not set in the hieradata.')
+      }
+      if !$tls_proxy_bind_ip {
+        fail('tls_proxy_bind_ip is not set in the hieradata.')
+      }
+      if !$tls_proxy_fqdn {
+        fail('tls_proxy_fqdn is required if internal TLS is enabled.')
+      }
+      $tls_certfile = $certificate_specs['service_certificate']
+      $tls_keyfile = $certificate_specs['service_key']
+
+      include ::tripleo::stunnel
+
+      ::tripleo::stunnel::service_proxy { 'redis':
+        accept_host  => $tls_proxy_bind_ip,
+        accept_port  => $tls_proxy_port,
+        connect_port => $tls_proxy_port,
+        certificate  => $tls_certfile,
+        key          => $tls_keyfile,
+        notify       => Class['::redis'],
+      }
+    }
     # If the old hiera key exists we use that to set the ulimit in order not to break
     # operators which set it. We might remove this in a later release (post pike anyway)
     $old_redis_file_limit = hiera('redis_file_limit', undef)