diff --git a/deployment_scripts/puppet/manifests/firewall.pp b/deployment_scripts/puppet/manifests/firewall.pp index 6273fa6..ebce588 100644 --- a/deployment_scripts/puppet/manifests/firewall.pp +++ b/deployment_scripts/puppet/manifests/firewall.pp @@ -14,6 +14,9 @@ notice('fuel-plugin-lma-infrastructure-alerting: firewall.pp') +$nagios_ui = hiera_hash('lma::infrastructure_alerting::nagios_ui') +$apache_port = $nagios_ui['apache_port'] + class {'::firewall':} firewall { '000 accept all icmp requests': @@ -52,7 +55,7 @@ firewall { '114 corosync-input': } firewall { '300 nagios cgi': - port => hiera('lma::infrastructure_alerting::apache_port'), + port => $apache_port, proto => 'tcp', action => 'accept', } diff --git a/deployment_scripts/puppet/manifests/hiera.pp b/deployment_scripts/puppet/manifests/hiera.pp index 337d55d..87a6169 100644 --- a/deployment_scripts/puppet/manifests/hiera.pp +++ b/deployment_scripts/puppet/manifests/hiera.pp @@ -24,6 +24,34 @@ $alerting_vip = $network_metadata['vips']['infrastructure_alerting_mgmt_vip'] $alerting_ui_vip = $network_metadata['vips']['infrastructure_alerting_ui']['ipaddr'] $listen_address = get_network_role_property('infrastructure_alerting', 'ipaddr') +$plugin = hiera('lma_infrastructure_alerting') +$tls_enabled = $plugin['tls_enabled'] + +if $tls_enabled { + $nagios_ui_hostname = $plugin['nagios_hostname'] + if $plugin['nagios_ssl_cert'] and $plugin['nagios_ssl_cert']['content'] { + $nagios_ui_ssl_cert = $plugin['nagios_ssl_cert']['content'] + $nagios_ui_ssl_cert_path = "/etc/apache2/certs/${$plugin['nagios_ssl_cert']['name']}" + + file { '/etc/apache2': + ensure => directory, + mode => '0755', + } -> + file { '/etc/apache2/certs': + ensure => directory, + mode => '0750', + } -> + file { $nagios_ui_ssl_cert_path: + ensure => present, + mode => '0700', + content => $nagios_ui_ssl_cert, + } + } + $ui_scheme = 'https' +} else { + $ui_scheme = 'http' +} + $kibana_port = hiera('lma::elasticsearch::kibana_port', 80) $es_port = hiera('lma::elasticsearch::rest_port', 9200) $grafana_port = hiera('lma::influxdb::grafana_port', 8000) @@ -34,15 +62,22 @@ lma::corosync_roles: - infrastructure_alerting - primary-infrastructure_alerting lma::infrastructure_alerting::listen_address: <%= @listen_address %> -lma::infrastructure_alerting::apache_port: 8001 lma::infrastructure_alerting::vip: <%= @alerting_vip %> -lma::infrastructure_alerting::vip_ui: <%= @alerting_ui_vip %> lma::infrastructure_alerting::vip_ns: infrastructure_alerting lma::infrastructure_alerting::kibana_port: <%= @kibana_port %> lma::infrastructure_alerting::es_port: <%= @es_port %> lma::infrastructure_alerting::grafana_port: <%= @grafana_port %> lma::infrastructure_alerting::influxdb_port: <%= @influxdb_port %> lma::infrastructure_alerting::cluster_ip: 127.0.0.1 +lma::infrastructure_alerting::nagios_ui: + vip: <%= @alerting_ui_vip %> + apache_port: 8001 + scheme: <%= @ui_scheme %> + tls_enabled: <%= @tls_enabled %> +<% if @tls_enabled -%> + hostname: <%= @nagios_ui_hostname %> + ssl_cert_path: <%= @nagios_ui_ssl_cert_path %> +<% end -%> ') file { $hiera_file: diff --git a/deployment_scripts/puppet/manifests/nagios.pp b/deployment_scripts/puppet/manifests/nagios.pp index 20d84f7..891cc36 100644 --- a/deployment_scripts/puppet/manifests/nagios.pp +++ b/deployment_scripts/puppet/manifests/nagios.pp @@ -43,9 +43,13 @@ if $notify_warning == false and $notify_unknown = $plugin['notify_unknown'] $notify_recovery = $plugin['notify_recovery'] } -$apache_port = hiera('lma::infrastructure_alerting::apache_port') $nagios_vip = hiera('lma::infrastructure_alerting::vip') -$nagios_ui_vip = hiera('lma::infrastructure_alerting::vip_ui') + +$nagios_ui = hiera_hash('lma::infrastructure_alerting::nagios_ui') +$nagios_ui_vip = $nagios_ui['vip'] +$apache_port = $nagios_ui['apache_port'] + +$tls_enabled = $nagios_ui['tls_enabled'] $lma_collector = hiera_hash('lma_collector', {}) @@ -63,10 +67,14 @@ if $lma_collector['gse_cluster_node'] { # Install and configure nagios server for StackLight class { 'lma_infra_alerting::nagios': - http_password => $password, - http_port => $apache_port, - nagios_ui_address => $nagios_ui_vip, - nagios_address => $nagios_vip, + http_password => $password, + http_port => $apache_port, + nagios_ui_address => $nagios_ui_vip, + nagios_address => $nagios_vip, + ui_tls_enabled => $tls_enabled, + ui_certificate_filename => $nagios_ui['ssl_cert_path'], + ui_certificate_hostname => $nagios_ui['hostname'], + } class { 'lma_infra_alerting::nagios::vhost': diff --git a/deployment_scripts/puppet/manifests/nagios_dashboard_url.pp b/deployment_scripts/puppet/manifests/nagios_dashboard_url.pp index 66c200a..f0a9cfe 100644 --- a/deployment_scripts/puppet/manifests/nagios_dashboard_url.pp +++ b/deployment_scripts/puppet/manifests/nagios_dashboard_url.pp @@ -16,11 +16,23 @@ notice('fuel-plugin-lma-infrastructure-alerting: nagios_dashboard_url.pp') $deployment_id = hiera('deployment_id') $master_ip = hiera('master_ip') -$vip = hiera('lma::infrastructure_alerting::vip_ui') -$port = hiera('lma::infrastructure_alerting::apache_port') +$nagios_ui = hiera_hash('lma::infrastructure_alerting::nagios_ui') +$vip = $nagios_ui['vip'] +$scheme = $nagios_ui['scheme'] +$tls_enabled = $nagios_ui['tls_enabled'] +$port = $nagios_ui['apache_port'] + +if $tls_enabled { + $host_name = $nagios_ui['hostname'] + $link = "${scheme}://${$host_name}:${port}/" + $text = "Dashboard for visualizing alerts (${host_name}: ${vip})" +} else { + $link = "${scheme}://${vip}:${port}/" + $text = 'Dashboard for visualizing alerts' +} $nagios_link_data = "{\"title\":\"Nagios\",\ -\"description\":\"Dashboard for visualizing alerts\",\ -\"url\":\"http://${vip}:${port}/\"}" +\"description\":\"${text}\",\ +\"url\":\"${link}\"}" $nagios_link_created_file = '/var/cache/nagios_link_created' exec { 'notify_nagios_url': diff --git a/deployment_scripts/puppet/manifests/validate_certificate.pp b/deployment_scripts/puppet/manifests/validate_certificate.pp new file mode 100644 index 0000000..0a944c2 --- /dev/null +++ b/deployment_scripts/puppet/manifests/validate_certificate.pp @@ -0,0 +1,26 @@ +# Copyright 2016 Mirantis, 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. + +notice('fuel-plugin-lma-infrastructure-alerting: validate_certificate.pp') + +$nagios_ui = hiera('lma::infrastructure_alerting::nagios_ui') +$certificate = $nagios_ui['ssl_cert'] +$certificate_path = $nagios_ui['ssl_cert_path'] +$common_name = $nagios_ui['hostname'] + +if $nagios_ui['tls_enabled'] { + if validate_ssl_certificate($certificate_path, $common_name) { + notice("Certificate ${certificate_path} for '${common_name}' is valid") + } +} diff --git a/deployment_scripts/puppet/modules/lma_infra_alerting/lib/puppet/parser/functions/validate_ssl_certificate.rb b/deployment_scripts/puppet/modules/lma_infra_alerting/lib/puppet/parser/functions/validate_ssl_certificate.rb new file mode 100644 index 0000000..3e3e779 --- /dev/null +++ b/deployment_scripts/puppet/modules/lma_infra_alerting/lib/puppet/parser/functions/validate_ssl_certificate.rb @@ -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 content 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 diff --git a/deployment_scripts/puppet/modules/lma_infra_alerting/manifests/nagios.pp b/deployment_scripts/puppet/modules/lma_infra_alerting/manifests/nagios.pp index 18b39f6..7cae290 100644 --- a/deployment_scripts/puppet/modules/lma_infra_alerting/manifests/nagios.pp +++ b/deployment_scripts/puppet/modules/lma_infra_alerting/manifests/nagios.pp @@ -23,6 +23,9 @@ class lma_infra_alerting::nagios ( $http_port, $nagios_ui_address, $nagios_address, + $ui_tls_enabled = false, + $ui_certificate_filename = undef, + $ui_certificate_hostname = undef, ) inherits lma_infra_alerting::params { include nagios::params @@ -48,12 +51,15 @@ class lma_infra_alerting::nagios ( } class { '::nagios::cgi': - user => $http_user, - password => $http_password, - http_port => $http_port, - vhost_listen_ip => $nagios_ui_address, - wsgi_vhost_listen_ip => $nagios_address, - require => Class[nagios], + user => $http_user, + password => $http_password, + http_port => $http_port, + vhost_listen_ip => $nagios_ui_address, + wsgi_vhost_listen_ip => $nagios_address, + ui_tls_enabled => $ui_tls_enabled, + ui_certificate_filename => $ui_certificate_filename, + ui_certificate_hostname => $ui_certificate_hostname, + require => Class[nagios], } $cron_bin = $lma_infra_alerting::params::update_configuration_script diff --git a/deployment_scripts/puppet/modules/nagios/manifests/cgi.pp b/deployment_scripts/puppet/modules/nagios/manifests/cgi.pp index 5448b64..5537158 100644 --- a/deployment_scripts/puppet/modules/nagios/manifests/cgi.pp +++ b/deployment_scripts/puppet/modules/nagios/manifests/cgi.pp @@ -23,15 +23,24 @@ class nagios::cgi ( $password = $nagios::params::cgi_password, $htpasswd_file = $nagios::params::cgi_htpasswd_file, $http_port = $nagios::params::cgi_http_port, + $ui_tls_enabled = false, + $ui_certificate_filename = undef, + $ui_certificate_hostname = undef, $wsgi_process_service_checks_location = '/status', $wsgi_process_service_checks_script = '/usr/local/bin/nagios-process-service-checks.wsgi', $wsgi_processes = 2, $wsgi_threads = 15, - ) inherits nagios::params { validate_integer($wsgi_processes) validate_integer($wsgi_threads) + + if $ui_tls_enabled { + $apache_modules = ['php', 'cgi', 'authn_file', 'auth_basic', 'authz_user', 'wsgi', 'ssl'] + } else { + $apache_modules = ['php', 'cgi', 'authn_file', 'auth_basic', 'authz_user', 'wsgi'] + } + ## Configure apache class { 'apache': # be good citizen by not erasing other configurations @@ -40,7 +49,7 @@ class nagios::cgi ( default_vhost => false, # prerequists for Nagios CGI mpm_module => 'prefork', - default_mods => ['php', 'cgi', 'authn_file', 'auth_basic', 'authz_user', 'wsgi'], + default_mods => $apache_modules, # allow to use the Puppet user resource later in the manifest manage_group => false, manage_user => false, diff --git a/deployment_scripts/puppet/modules/nagios/templates/apache_vhost_ubuntu.conf.erb b/deployment_scripts/puppet/modules/nagios/templates/apache_vhost_ubuntu.conf.erb index 2904859..e0be719 100644 --- a/deployment_scripts/puppet/modules/nagios/templates/apache_vhost_ubuntu.conf.erb +++ b/deployment_scripts/puppet/modules/nagios/templates/apache_vhost_ubuntu.conf.erb @@ -4,7 +4,21 @@ # ************************************ :<%= @http_port %>> +<% if @ui_tls_enabled -%> + ServerName <%= @ui_certificate_hostname %> + SSLEngine on + SSLCertificateFile "<%= @ui_certificate_filename %>" + + ## Configuration from mozilla.github.io/server-side-tls/ssl-config-generator/ + # For Apache 2.4.7 with "modern" profile. + Header always set Strict-Transport-Security "max-age=15768000" + SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 + SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256 + SSLHonorCipherOrder on + SSLCompression off +<% else -%> ServerName nagios-ui +<% end -%> DocumentRoot "/usr/share/nagios3/htdocs" # Alias for UI diff --git a/deployment_tasks.yaml b/deployment_tasks.yaml index b3fd1af..b5d6a69 100644 --- a/deployment_tasks.yaml +++ b/deployment_tasks.yaml @@ -3,7 +3,7 @@ type: group version: 2.0.0 role: [primary-infrastructure_alerting] - tasks: &common_tasks + tasks: - hiera - setup_repositories - fuel_pkgs @@ -13,6 +13,7 @@ - netconfig - hosts - lma-alerting-hiera + - lma-alerting-validate-certificate - lma-alerting-firewall - lma-alerting-cluster - lma-alerting-vip @@ -26,7 +27,19 @@ type: group version: 2.0.0 role: [infrastructure_alerting] - tasks: *common_tasks + tasks: + - hiera + - setup_repositories + - fuel_pkgs + - globals + - tools + - logging + - netconfig + - hosts + - lma-alerting-hiera + - lma-alerting-firewall + - lma-alerting-cluster + - lma-alerting-vip required_for: [deploy_end] requires: [deploy_start, primary-infrastructure_alerting] parameters: @@ -45,6 +58,19 @@ puppet_modules: puppet/modules:/etc/puppet/modules timeout: 120 +- id: lma-alerting-validate-certificate + type: puppet + version: 2.0.0 + requires: [lma-alerting-hiera] + required_for: [logging, lma-alerting-firewall] + parameters: + puppet_manifest: "puppet/manifests/validate_certificate.pp" + puppet_modules: puppet/modules:/etc/puppet/modules + timeout: 120 + # reexecute_on is needed for scale-down operations + reexecute_on: + - deploy_changes + - id: lma-alerting-firewall type: puppet version: 2.0.0 diff --git a/environment_config.yaml b/environment_config.yaml index 14ff3d0..4caa995 100644 --- a/environment_config.yaml +++ b/environment_config.yaml @@ -101,3 +101,30 @@ attributes: weight: 52 type: "password" restrictions: *no_auth + + tls_enabled: + value: false + label: 'Enable TLS for Nagios' + description: '' + weight: 60 + type: "checkbox" + + nagios_hostname: + value: 'nagios.fuel.local' + label: 'DNS hostname for Nagios UI' + description: 'Your DNS entries should point to this name' + weight: 61 + type: "text" + restrictions: + - condition: "settings:lma_infrastructure_alerting.tls_enabled.value == false" + action: "hide" + + nagios_ssl_cert: + value: '' + label: 'Certificate for Nagios UI' + description: 'Certificate and private key data, concatenated into a single file' + weight: 62 + type: "file" + restrictions: + - condition: "settings:lma_infrastructure_alerting.tls_enabled.value == false" + action: "hide"