Enable secure communication over HTTPS for Kibana

This patch configures HAProxy to terminate SSL connection when the
support for SSL/TLS is enabled in StackLight.

DocImpact Add support for TLS
Change-Id: Icdff278875d7daf928fa036ec1e4905205791546
Implements: blueprint support-secure-communication
This commit is contained in:
Guillaume Thouvenin 2016-06-09 18:10:35 +02:00
parent 746d71328b
commit 15d65ae5b7
7 changed files with 191 additions and 35 deletions

View File

@ -14,10 +14,18 @@
notice('fuel-plugin-elasticsearch-kibana: check_environment_configuration.pp')
$elasticsearch_kibana = hiera('elasticsearch_kibana')
# Check that JVM size doesn't exceed the physical RAM size
$jvmsize_mb = ($elasticsearch_kibana['jvm_heap_size'] + 0.0) * 1024
$jvm_heap_size = hiera('lma::elasticsearch::jvm_size')
$jvmsize_mb = ($jvm_heap_size + 0.0) * 1024
if $jvmsize_mb >= $::memorysize_mb {
fail("The configured JVM size (${ $elasticsearch_kibana['jvm_heap_size'] } GB) is greater than the system RAM (${ ::memorysize }).")
fail("The configured JVM size (${jvm_heap_size} GB) is greater than the system RAM (${::memorysize}).")
}
if hiera('lma::kibana::tls::enabled') {
$certificate = hiera('lma::kibana::tls::cert_file_path')
$common_name = hiera('lma::kibana::tls::hostname')
# function validate_ssl_certificate() must be the value of a statement, so
# we must use it in a statement.
$not_used = validate_ssl_certificate($certificate, $common_name)
}

View File

@ -44,14 +44,30 @@ openstack::ha::haproxy_service { $es_haproxy_service:
}
}
openstack::ha::haproxy_service { 'kibana':
order => '921',
listen_port => $kibana_frontend_port,
balancermember_port => $kibana_backend_port,
balancermember_options => 'check inter 10s fastinter 2s downinter 3s rise 3 fall 3',
haproxy_config_options => {
'option' => ['httplog', 'http-keep-alive', 'prefer-last-server', 'dontlog-normal'],
'balance' => 'roundrobin',
'mode' => 'http',
if hiera('lma::kibana::tls::enabled') {
openstack::ha::haproxy_service { 'kibana':
order => '921',
internal_ssl => true,
internal_ssl_path => hiera('lma::kibana::tls::cert_file_path'),
listen_port => $kibana_frontend_port,
balancermember_port => $kibana_backend_port,
balancermember_options => 'check inter 10s fastinter 2s downinter 3s rise 3 fall 3',
haproxy_config_options => {
'option' => ['httplog', 'http-keep-alive', 'prefer-last-server', 'dontlog-normal'],
'balance' => 'roundrobin',
'mode' => 'http',
},
}
} else {
openstack::ha::haproxy_service { 'kibana':
order => '921',
listen_port => $kibana_frontend_port,
balancermember_port => $kibana_backend_port,
balancermember_options => 'check inter 10s fastinter 2s downinter 3s rise 3 fall 3',
haproxy_config_options => {
'option' => ['httplog', 'http-keep-alive', 'prefer-last-server', 'dontlog-normal'],
'balance' => 'roundrobin',
'mode' => 'http',
}
}
}

View File

@ -64,6 +64,33 @@ if is_integer($elasticsearch_kibana['recover_after_nodes']) and $elasticsearch_k
$instance_name = 'es-01'
$logs_dir = "/var/log/elasticsearch/${instance_name}"
$tls_enabled = $elasticsearch_kibana['tls_enabled']
if $tls_enabled {
$kibana_hostname = $elasticsearch_kibana['kibana_hostname']
$cert_base_dir = '/etc/haproxy'
$cert_dir = "${cert_base_dir}/certs"
$cert_file_path = "${cert_dir}/${elasticsearch_kibana['kibana_ssl_cert']['name']}"
file { $cert_base_dir:
ensure => directory,
mode => '0755'
}
file { $cert_dir:
ensure => directory,
mode => '0700',
require => File[$cert_base_dir]
}
file { $cert_file_path:
ensure => present,
mode => '0400',
content => $elasticsearch_kibana['kibana_ssl_cert']['content'],
require => File[$cert_dir]
}
}
$calculated_content = inline_template('
---
lma::corosync_roles:
@ -91,6 +118,12 @@ lma::elasticsearch::jvm_size: <%= @elasticsearch_kibana["jvm_heap_size"] %>
lma::elasticsearch::instance_name: <%= @instance_name %>
lma::elasticsearch::node_name: "<%= @fqdn %>_es-01"
lma::elasticsearch::cluster_name: lma
lma::kibana::tls::enabled: <%= @tls_enabled %>
<% if @tls_enabled -%>
lma::kibana::tls::hostname: <%= @kibana_hostname %>
lma::kibana::tls::cert_file_path: <%= @cert_file_path %>
<% end -%>
')
file { $hiera_file:

View File

@ -20,12 +20,21 @@ $vip = hiera('lma::elasticsearch::vip')
$kibana_port = hiera('lma::elasticsearch::kibana_frontend_port')
$es_port = hiera('lma::elasticsearch::rest_port')
$number_of_replicas = hiera('lma::elasticsearch::number_of_replicas')
$kibana_link_data = "{\"title\":\"Kibana\",\
\"description\":\"Dashboard for visualizing logs and notifications\",\
\"url\":\"http://${vip}:${kibana_port}/\"}"
$kibana_link_created_file = '/var/cache/kibana_link_created'
$elasticsearch_kibana = hiera_hash('elasticsearch_kibana')
if hiera('lma::kibana::tls::enabled') {
$protocol = 'https'
$kibana_hostname = hiera('lma::kibana::tls::hostname')
$kibana_link_data = "{\"title\":\"Kibana\",\
\"description\":\"Dashboard for visualizing logs and notifications (${kibana_hostname}: ${vip})\",\
\"url\":\"${protocol}://${kibana_hostname}:${kibana_port}/\"}"
} else {
$protocol = 'http'
$kibana_link_data = "{\"title\":\"Kibana\",\
\"description\":\"Dashboard for visualizing logs and notifications\",\
\"url\":\"${protocol}://${vip}:${kibana_port}/\"}"
}
$kibana_link_created_file = '/var/cache/kibana_link_created'
lma_logging_analytics::es_template { ['log', 'notification']:
number_of_replicas => $number_of_replicas,

View File

@ -0,0 +1,61 @@
# Inspired by cert_date_valid.rb found at
# https://github.com/camptocamp/puppet-openssl
#
# Function: validate_ssl_certificate()
#
# Checks SSL certificate date and CN validity. It also checks that the private
# key is embedded into the certificate.
#
# It raises an exception if:
# - the certificate has no private key
# - the CN of the certificate and the CN provided as argument don't match
# - the date is not found in the certificate
#
# It returns false if the certificate is expired or not yet valid
# Otherwise it returns the number of seconds before the certificate expires
#
# Parameter:
# - the file path of the SSL certificate
# - the expected CN
module Puppet::Parser::Functions
newfunction(:validate_ssl_certificate, :type => :rvalue) do |args|
require 'time'
certfile = args[0]
# Check that file is a valid x509 certificate
err_msg = `openssl x509 -noout -in #{certfile}`
raise "'#{certfile}' is not a valid certificate" unless err_msg.chomp() == ""
dates = `openssl x509 -dates -noout -in #{certfile}`.gsub("\n", '')
subject = `openssl x509 -subject -noout -in #{certfile}`.gsub("\n", '')
pk = `openssl rsa -check -noout -in #{certfile}`.gsub("\n",'')
cn = subject.match(/CN=([^\/]+)/)
cn_found = cn[1] if cn
certbegin = Time.parse(dates.gsub(/.*notBefore=(.+? GMT).*/, '\1'))
certend = Time.parse(dates.gsub(/.*notAfter=(.+? GMT).*/, '\1'))
now = Time.now.utc
raise "The certificate file doesn't contain the private key" unless pk == 'RSA key ok'
raise "Found #{cn_found} as CN whereas '#{args[1]}' was expected" unless cn_found == args[1]
raise "Dates not found in the certificate" unless dates.match(/not(Before|After)=/)
if (now > certend)
Puppet.warning("Certificate has expired. End date: #{certend}")
false
elsif (now < certbegin)
Puppet.warning("Certificate is not yet valid. Start date: #{certbegin}")
false
elsif (certend <= certbegin)
Puppet.warning("Certificate will never be valid")
false
else
# return the number of seconds before the certificate expires
(certend - now).to_i
end
end
end

View File

@ -4,7 +4,7 @@
type: group
version: 2.0.0
role: [primary-elasticsearch_kibana]
tasks: &common_tasks
tasks:
- hiera
- setup_repositories
- fuel_pkgs
@ -60,26 +60,12 @@
# Tasks definitions for the deployment
######################################
# This task needs to be reexecuted to recheck that the configuration parameters
# match the node's characteristics (eg JVM size).
- id: elasticsearch-check-configuration
type: puppet
version: 2.0.0
requires: [netconfig]
required_for: [deploy_end]
parameters:
puppet_manifest: puppet/manifests/check_environment_configuration.pp
puppet_modules: puppet/modules:/etc/puppet/modules
timeout: 120
reexecute_on:
- deploy_changes
# This task needs to be reexecuted to adapt the configuration parameters which
# depend on the number of nodes in the cluster
- id: elasticsearch-hiera
type: puppet
version: 2.0.0
requires: [elasticsearch-check-configuration]
requires: [netconfig]
required_for: [deploy_end]
parameters:
puppet_manifest: "puppet/manifests/hiera_override.pp"
@ -88,11 +74,25 @@
reexecute_on:
- deploy_changes
- id: elasticsearch-firewall
# This task needs to be reexecuted to recheck that the configuration parameters
# match the node's characteristics (eg JVM size).
- id: elasticsearch-check-configuration
type: puppet
version: 2.0.0
requires: [elasticsearch-hiera]
required_for: [deploy_end]
parameters:
puppet_manifest: puppet/manifests/check_environment_configuration.pp
puppet_modules: puppet/modules:/etc/puppet/modules
timeout: 120
reexecute_on:
- deploy_changes
- id: elasticsearch-firewall
type: puppet
version: 2.0.0
requires: [elasticsearch-check-configuration]
required_for: [deploy_end]
parameters:
puppet_manifest: "puppet/manifests/firewall.pp"
puppet_modules: puppet/modules:/etc/puppet/modules

View File

@ -90,3 +90,32 @@ attributes:
restrictions:
- condition: "settings:elasticsearch_kibana.advanced_settings.value == false"
action: hide
# TLS Settings: BEGIN
tls_enabled:
value: false
label: 'Enable TLS for Kibana'
description: ''
weight: 30
type: "checkbox"
kibana_hostname:
value: 'kibana.fuel.local'
label: 'DNS hostname for Kibana'
description: 'Your DNS entries should point to this name.'
weight: 40
type: "text"
restrictions:
- condition: "settings:elasticsearch_kibana.tls_enabled.value == false"
action: "hide"
kibana_ssl_cert:
value: ''
label: 'Certificate for Kibana'
description: 'Certificate and private key data, concatenated into a single file.'
weight: 50
type: "file"
restrictions:
- condition: "settings:elasticsearch_kibana.tls_enabled.value == false"
action: "hide"
# TLS Settings: END