diff --git a/CHANGELOG.md b/CHANGELOG.md index a9675708..e125c8d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,16 @@ repo support previously. (Bugfix) Contributed by @nmaludy +- Change the way we handle the `nginx` config from just copying a config file to + using the native resource types provided by the `puppet-nginx` module. + Users can now configure the utilized SSL protocol and ciphers along with client + max body size directly from the `st2` class using the following new parameters: + - `nginx_client_max_body_size` + - `nginx_ssl_ciphers` + - `nginx_ssl_port` + - `nginx_ssl_protocols` + Contributed by @nmaludy + ## 1.7.0 (Jun 26, 2020) - Refactored the system StackStorm repository handling. This replaces the `PackageCloud` diff --git a/REFERENCE.md b/REFERENCE.md index 8b3c71a6..83ba1085 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -553,6 +553,55 @@ Set this to false when you have your own repositories for nginx Default value: `true` +##### `nginx_ssl_ciphers` + +Data type: `Any` + +String or list of strings of acceptable SSL ciphers to configure nginx with. +@see http://nginx.org/en/docs/http/ngx_http_ssl_module.html +Note: the defaults are setup to restrict to TLSv1.2 and TLSv1.3 secure ciphers only + (secure by default). The secure ciphers for each protocol were obtained via: + @see https://wiki.mozilla.org/Security/Server_Side_TLS + +Default value: $::st2::params::nginx_ssl_ciphers + +##### `nginx_ssl_protocols` + +Data type: `Any` + +String or list of strings of acceptable SSL protocols to configure nginx with. +@see http://nginx.org/en/docs/http/ngx_http_ssl_module.html +Note: the defaults are setup to restrict to TLSv1.2 and TLSv1.3 only (secure by default) + +Default value: $::st2::params::nginx_ssl_protocols + +##### `nginx_ssl_port` + +Data type: `Any` + +What port should nginx listen on publicly for new connections (default: 443) + +Default value: $::st2::params::nginx_ssl_port + +##### `nginx_client_max_body_size` + +Data type: `Any` + +The maximum size of the body for a request allow through nginx. +We default this to '0' to allow for large messages/payloads/inputs/results +to be passed through nginx as is normal in the StackStorm context. +@see http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size + +Default value: $::st2::params::nginx_client_max_body_size + +##### `web_root` + +Data type: `Any` + +Directory where the StackStorm WebUI site lives on the filesystem + +Default value: $::st2::params::web_root + ##### `timersengine_enabled` Data type: `Any` @@ -2479,13 +2528,66 @@ class { 'st2::profile::web': } ``` +##### Change the SSL protocols and ciphers + +```puppet +class { 'st2::profile::web': + nginx_ssl_protocols => ['TLSv1.2'], + nginx_ssl_ciphers => [ + 'ECDHE-ECDSA-AES256-GCM-SHA384', + 'ECDHE-ECDSA-AES256-SHA384', + ], +} +``` + #### Parameters The following parameters are available in the `st2::profile::web` class. +##### `nginx_ssl_ciphers` + +Data type: `Variant[Array[String], String]` + +String or list of strings of acceptable SSL ciphers to configure nginx with. +@see http://nginx.org/en/docs/http/ngx_http_ssl_module.html +Note: the defaults are setup to restrict to TLSv1.2 and TLSv1.3 secure ciphers only + (secure by default). The secure ciphers for each protocol were obtained via: + @see https://wiki.mozilla.org/Security/Server_Side_TLS + +Default value: $::st2::nginx_ssl_ciphers + +##### `nginx_ssl_protocols` + +Data type: `Variant[Array[String], String]` + +String or list of strings of acceptable SSL protocols to configure nginx with. +@see http://nginx.org/en/docs/http/ngx_http_ssl_module.html +Note: the defaults are setup to restrict to TLSv1.2 and TLSv1.3 only (secure by default) + +Default value: $::st2::nginx_ssl_protocols + +##### `nginx_ssl_port` + +Data type: `Stdlib::Port` + +What port should nginx listen on publicly for new connections (default: 443) + +Default value: $::st2::nginx_ssl_port + +##### `nginx_client_max_body_size` + +Data type: `String` + +The maximum size of the body for a request allow through nginx. +We default this to '0' to allow for large messages/payloads/inputs/results +to be passed through nginx as is normal in the StackStorm context. +@see http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size + +Default value: $::st2::nginx_client_max_body_size + ##### `ssl_cert_manage` -Data type: `Any` +Data type: `Boolean` Boolean to determine if this module should manage the SSL certificate used by nginx. @@ -2493,7 +2595,7 @@ Default value: $::st2::ssl_cert_manage ##### `ssl_dir` -Data type: `Any` +Data type: `Stdlib::Absolutepath` Directory where st2web will look for its SSL info. (default: /etc/ssl/st2) @@ -2502,7 +2604,7 @@ Default value: $::st2::ssl_dir ##### `ssl_cert` -Data type: `Any` +Data type: `String` Path to the file where the StackStorm SSL cert will be generated. (default: /etc/ssl/st2/st2.crt) @@ -2511,7 +2613,7 @@ Default value: $::st2::ssl_cert ##### `ssl_key` -Data type: `Any` +Data type: `String` Path to the file where the StackStorm SSL key will be generated. (default: /etc/ssl/st2/st2.key) @@ -2520,12 +2622,20 @@ Default value: $::st2::ssl_key ##### `version` -Data type: `Any` +Data type: `String` Version of StackStorm WebUI to install Default value: $::st2::version +##### `web_root` + +Data type: `String` + +Directory where the StackStorm WebUI site lives on the filesystem + +Default value: $::st2::web_root + ### st2::repo Manages the installation of st2 required repos for installing the StackStorm packages. diff --git a/manifests/init.pp b/manifests/init.pp index d0b498d0..04ef0e4a 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -125,6 +125,25 @@ # @param nginx_manage_repo # Set this to false when you have your own repositories for nginx # (default: true) +# @param nginx_ssl_ciphers +# String or list of strings of acceptable SSL ciphers to configure nginx with. +# @see http://nginx.org/en/docs/http/ngx_http_ssl_module.html +# Note: the defaults are setup to restrict to TLSv1.2 and TLSv1.3 secure ciphers only +# (secure by default). The secure ciphers for each protocol were obtained via: +# @see https://wiki.mozilla.org/Security/Server_Side_TLS +# @param nginx_ssl_protocols +# String or list of strings of acceptable SSL protocols to configure nginx with. +# @see http://nginx.org/en/docs/http/ngx_http_ssl_module.html +# Note: the defaults are setup to restrict to TLSv1.2 and TLSv1.3 only (secure by default) +# @param nginx_ssl_port +# What port should nginx listen on publicly for new connections (default: 443) +# @param nginx_client_max_body_size +# The maximum size of the body for a request allow through nginx. +# We default this to '0' to allow for large messages/payloads/inputs/results +# to be passed through nginx as is normal in the StackStorm context. +# @see http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size +# @param web_root +# Directory where the StackStorm WebUI site lives on the filesystem # @param timersengine_enabled # Set to true if the st2timersengine service should be enabled # on this node (default: true) @@ -277,6 +296,11 @@ $datastore_keys_dir = $::st2::params::datstore_keys_dir, $datastore_key_path = "${::st2::params::datstore_keys_dir}/datastore_key.json", $nginx_manage_repo = true, + $nginx_client_max_body_size = $::st2::params::nginx_client_max_body_size, + $nginx_ssl_ciphers = $::st2::params::nginx_ssl_ciphers, + $nginx_ssl_port = $::st2::params::nginx_ssl_port, + $nginx_ssl_protocols = $::st2::params::nginx_ssl_protocols, + $web_root = $::st2::params::web_root, $rabbitmq_username = $::st2::params::rabbitmq_username, $rabbitmq_password = $::st2::params::rabbitmq_password, $rabbitmq_hostname = $::st2::params::rabbitmq_hostname, diff --git a/manifests/params.pp b/manifests/params.pp index 7ee329b9..6594bc18 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -43,6 +43,9 @@ # API settings $api_port = 9101 + # stream settings + $stream_port = 9102 + # Non-user configurable parameters $repository = 'stable' $conf_dir = '/etc/st2' @@ -119,23 +122,34 @@ $scheduler_gc_interval = 10 $scheduler_pool_size = 10 - ## nginx default config - $nginx_default_conf = $::osfamily ? { - 'Debian' => '/etc/nginx/conf.d/default.conf', - 'RedHat' => '/etc/nginx/conf.d/default.conf', - } - ## nginx conf.d directory in /etc - $nginx_conf_d = $::osfamily ? { - 'Debian' => '/etc/nginx/conf.d', - 'RedHat' => '/etc/nginx/conf.d', - } - # nginx config for StackStorm (installed with the st2 packages) - $nginx_st2_conf = '/usr/share/doc/st2/conf/nginx/st2.conf' + ## nginx + $nginx_ssl_port = 443 + $nginx_ssl_protocols = [ + 'TLSv1.2', + 'TLSv1.3', + ] + $nginx_ssl_ciphers = [ + # TLSv1.3 + 'TLS_AES_128_GCM_SHA256', + 'TLS_AES_256_GCM_SHA384', + 'TLS_CHACHA20_POLY1305_SHA256', + # TLSv1.2 + 'ECDHE-ECDSA-AES128-GCM-SHA256', + 'ECDHE-ECDSA-AES128-SHA256', + 'ECDHE-ECDSA-AES256-GCM-SHA384', + 'ECDHE-ECDSA-AES256-SHA384', + 'ECDHE-ECDSA-CHACHA20-POLY1305', + 'ECDHE-RSA-AES128-GCM-SHA256', + 'ECDHE-RSA-AES128-SHA256', + 'ECDHE-RSA-AES256-GCM-SHA384', + 'ECDHE-RSA-AES256-SHA384', + 'ECDHE-RSA-CHACHA20-POLY1305', + ] + # no max on the body size for large workflow support + $nginx_client_max_body_size = '0' - # st2web certs - $st2web_ssl_dir = '/etc/ssl/st2' - $st2web_ssl_cert = "${st2web_ssl_dir}/st2.crt" - $st2web_ssl_key = "${st2web_ssl_dir}/st2.key" + # st2web + $web_root = '/opt/stackstorm/static/webui/' ## MongoDB Data $mongodb_admin_username = 'admin' diff --git a/manifests/profile/nginx.pp b/manifests/profile/nginx.pp index 66ddc837..9188a70e 100644 --- a/manifests/profile/nginx.pp +++ b/manifests/profile/nginx.pp @@ -6,11 +6,16 @@ # @example Basic Usage # include st2::profile::nginx # +# @example Disable manging the nginx repo so you can manage it yourself +# class { 'st2::profile::nginx': +# manage_repo => false, +# } +# class st2::profile::nginx ( $manage_repo = $::st2::nginx_manage_repo ) inherits st2 { class { 'nginx': manage_repo => $manage_repo, - confd_purge => false, + confd_purge => true, } } diff --git a/manifests/profile/web.pp b/manifests/profile/web.pp index dfa196bb..6ad96190 100644 --- a/manifests/profile/web.pp +++ b/manifests/profile/web.pp @@ -1,5 +1,22 @@ # @summary Profile to install, configure and manage StackStorm web UI (st2web). # +# @param nginx_ssl_ciphers +# String or list of strings of acceptable SSL ciphers to configure nginx with. +# @see http://nginx.org/en/docs/http/ngx_http_ssl_module.html +# Note: the defaults are setup to restrict to TLSv1.2 and TLSv1.3 secure ciphers only +# (secure by default). The secure ciphers for each protocol were obtained via: +# @see https://wiki.mozilla.org/Security/Server_Side_TLS +# @param nginx_ssl_protocols +# String or list of strings of acceptable SSL protocols to configure nginx with. +# @see http://nginx.org/en/docs/http/ngx_http_ssl_module.html +# Note: the defaults are setup to restrict to TLSv1.2 and TLSv1.3 only (secure by default) +# @param nginx_ssl_port +# What port should nginx listen on publicly for new connections (default: 443) +# @param nginx_client_max_body_size +# The maximum size of the body for a request allow through nginx. +# We default this to '0' to allow for large messages/payloads/inputs/results +# to be passed through nginx as is normal in the StackStorm context. +# @see http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size # @param ssl_cert_manage # Boolean to determine if this module should manage the SSL certificate used by nginx. # @param ssl_dir @@ -13,6 +30,8 @@ # be generated. (default: /etc/ssl/st2/st2.key) # @param version # Version of StackStorm WebUI to install +# @param web_root +# Directory where the StackStorm WebUI site lives on the filesystem # # @example Basic Usage # include st2::profile::web' @@ -31,12 +50,27 @@ # ssl_cert_manage => false, # } # +# +# @example Change the SSL protocols and ciphers +# class { 'st2::profile::web': +# nginx_ssl_protocols => ['TLSv1.2'], +# nginx_ssl_ciphers => [ +# 'ECDHE-ECDSA-AES256-GCM-SHA384', +# 'ECDHE-ECDSA-AES256-SHA384', +# ], +# } +# class st2::profile::web( - $ssl_cert_manage = $::st2::ssl_cert_manage, - $ssl_dir = $::st2::ssl_dir, - $ssl_cert = $::st2::ssl_cert, - $ssl_key = $::st2::ssl_key, - $version = $::st2::version, + Variant[Array[String], String] $nginx_ssl_ciphers = $::st2::nginx_ssl_ciphers, + Variant[Array[String], String] $nginx_ssl_protocols = $::st2::nginx_ssl_protocols, + Stdlib::Port $nginx_ssl_port = $::st2::nginx_ssl_port, + String $nginx_client_max_body_size = $::st2::nginx_client_max_body_size, + Boolean $ssl_cert_manage = $::st2::ssl_cert_manage, + Stdlib::Absolutepath $ssl_dir = $::st2::ssl_dir, + String $ssl_cert = $::st2::ssl_cert, + String $ssl_key = $::st2::ssl_key, + String $version = $::st2::version, + String $web_root = $::st2::web_root, ) inherits st2 { # include nginx here only # if we include this in st2::profile::fullinstall Anchor['pre_reqs'] then @@ -47,8 +81,10 @@ ## Install the packages package { $::st2::params::st2_web_packages: - ensure => $version, - tag => ['st2::packages', 'st2::web::packages'], + ensure => $version, + tag => ['st2::packages', 'st2::web::packages'], + require => Package['nginx'], + notify => Service['nginx'], # notify to force a refresh if the package is updated } ## Create ssl cert directory @@ -59,38 +95,200 @@ ## optionally manage the SSL certificate used by nginx if $ssl_cert_manage { ## Generate SSL certificates - $_ssl_subj = "/C=US/ST=California/L=Palo Alto/O=StackStorm/OU=Information Technology/CN=${::fqdn}" + $_ssl_subj = "/C=US/ST=California/L=Palo Alto/O=StackStorm/OU=Information Technology/CN=${$trusted['certname']}" exec { "generate ssl cert ${ssl_cert}": command => "openssl req -x509 -newkey rsa:2048 -keyout ${ssl_key} -out ${ssl_cert} -days 365 -nodes -subj \"${_ssl_subj}\"", creates => $ssl_cert, path => ['/usr/bin', '/bin'], require => File[$ssl_dir], - notify => File["${::st2::params::nginx_conf_d}/st2.conf"], + notify => Nginx::Resource::Server['st2webui'], } } - ## st2 nginx config - file { "${::st2::params::nginx_conf_d}/st2.conf": - ensure => 'file', - owner => 'root', - group => 'root', - mode => '0644', - source => "file:///${::st2::params::nginx_st2_conf}", - subscribe => Package[$::st2::params::st2_server_packages], - notify => Service['nginx'], + # http redirect to https + $add_header = { + 'Front-End-Https' => 'on', + 'X-Content-Type-Options' => 'nosniff', + } + nginx::resource::server { 'st2webui': + ensure => present, + listen_port => 80, + access_log => "${nginx::config::log_dir}/st2webui.access.log", + error_log => "${nginx::config::log_dir}/st2webui.error.log", + ssl_redirect => true, + add_header => $add_header, + tag => ['st2', 'st2::frontend', 'st2::frontend::http'], } - ## Remove the default nginx config - file { $::st2::params::nginx_default_conf: - ensure => 'absent', - require => Package['nginx'], - notify => Service['nginx'], + # convert arrays into strings if necessary + $nginx_ssl_ciphers_str = $nginx_ssl_ciphers ? { + Array[String] => $nginx_ssl_ciphers.join(':'), + String => $nginx_ssl_ciphers, + } + $nginx_ssl_protocols_str = $nginx_ssl_protocols ? { + Array[String] => $nginx_ssl_protocols.join(' '), + String => $nginx_ssl_protocols, + } + + $ssl_server = 'ssl-st2webui' + nginx::resource::server { $ssl_server: + ensure => present, + listen_port => $nginx_ssl_port, + index_files => [ 'index.html' ], + access_log => "${nginx::config::log_dir}/${ssl_server}.access.log", + error_log => "${nginx::config::log_dir}/${ssl_server}.error.log", + # disable the built-in 'location /' (in puppet-nginx) so we can define our own below + use_default_location => false, + ssl => true, + ssl_cert => $ssl_cert, + ssl_key => $ssl_key, + ssl_port => $nginx_ssl_port, + ssl_ciphers => $nginx_ssl_ciphers_str, + ssl_protocols => $nginx_ssl_protocols_str, + client_max_body_size => $nginx_client_max_body_size, + add_header => $add_header, + # For backward compatibility reasons, rewrite requests from "/api/stream" + # to "/stream/v1/stream" and "/api/v1/stream" to "/stream/v1/stream" + server_cfg_append => { + 'rewrite' => [ + '^/api/stream/?$ /stream/v1/stream break', + '^/api/(v\d)/stream/?$ /stream/$1/stream break', + ], + }, + tag => ['st2', 'st2::frontend', 'st2::frontend::https'], + } + + # default settings for all locations + $location_defaults = { + ensure => present, + server => $ssl_server, + # need to ensure both ssl and ssl_true are set so that these locations are ONLY + # added to the ssl site above + ssl => true, + ssl_only => true, + index_files => [ ], + } + + # the proxy locations contain all of the location settings plus some common + # proxy settings used commonly across all of them, rather than copy paste + # we use some hash merges to make this more compact and easier to add common + # settings in the future + $proxy_defaults = $location_defaults + { + proxy_read_timeout => '90', + proxy_connect_timeout => '90', + proxy_redirect => 'off', + proxy_set_header => [ + 'Host $host', + 'X-Real-IP $remote_addr', + 'X-Forwarded-For $proxy_add_x_forwarded_for', + 'X-Forwarded-Proto "https"', + 'Connection \'\'', + ], + # Disable buffering and chunked encoding. + # In the stream case we want to receive the whole payload at once, we don't + # want multiple chunks. + proxy_buffering => 'off', + proxy_cache => 'off', + } + + # root website location for st2webui + nginx::resource::location { '/': + * => $location_defaults + { + index_files => [ 'index.html' ], + www_root => $web_root, + location_cfg_append => { + 'sendfile' => 'on', + 'tcp_nopush' => 'on', + 'tcp_nodelay' => 'on', + }, + tag => ['st2', 'st2::backend', 'st2::backend::webui'], + }, + } + + nginx::resource::location { '@apiError': + * => $location_defaults + { + add_header => { + 'Content-Type' => 'application/json always', + }, + location_cfg_append => { + 'return' => '503 \'{ "faultstring": "Nginx is unable to reach st2api. Make sure service is running." }\'', + }, + tag => ['st2', 'st2::backend', 'st2::backend::apierror'], + }, + } + + nginx::resource::location { '/api/': + * => $proxy_defaults + { + rewrite_rules => [ + '^/api/(.*) /$1 break', + ], + proxy => "http://127.0.0.1:${st2::params::api_port}", + location_cfg_append => { + 'error_page' => '502 = @apiError', + 'chunked_transfer_encoding' => 'off', + }, + tag => ['st2', 'st2::backend', 'st2::backend::api'], + }, + } + + nginx::resource::location { '@streamError': + * => $location_defaults + { + add_header => { + 'Content-Type' => 'text/event-stream', + }, + location_cfg_append => { + 'return' => '200 "retry: 1000\n\n"', + }, + tag => ['st2', 'st2::backend', 'st2::backend::streamerror'], + }, + } + + nginx::resource::location { '/stream/': + * => $proxy_defaults + { + rewrite_rules => [ + '^/stream/(.*) /$1 break', + ], + proxy => "http://127.0.0.1:${st2::params::stream_port}", + location_cfg_append => { + 'error_page' => '502 = @streamError', + 'chunked_transfer_encoding' => 'off', + 'sendfile' => 'off', + 'tcp_nopush' => 'off', + 'tcp_nodelay' => 'off', + }, + tag => ['st2', 'st2::backend', 'st2::backend::stream'], + }, + } + + nginx::resource::location { '@authError': + * => $location_defaults + { + add_header => { + 'Content-Type' => 'application/json always', + }, + location_cfg_append => { + 'return' => '503 \'{ "faultstring": "Nginx is unable to reach st2auth. Make sure service is running." }\'', + }, + tag => ['st2', 'st2::backend', 'st2::backend::autherror'], + }, + } + + nginx::resource::location { '/auth/': + * => $proxy_defaults + { + rewrite_rules => [ + '^/auth/(.*) /$1 break', + ], + proxy => "http://127.0.0.1:${st2::params::auth_port}", + proxy_pass_header => [ + 'Authorization', + ], + location_cfg_append => { + 'error_page' => '502 = @authError', + 'chunked_transfer_encoding' => 'off', + }, + tag => ['st2', 'st2::backend', 'st2::backend::auth'], + }, } ## Dependencies - Package['nginx'] - -> Package<| tag == 'st2::web::packages' |> - -> File[$ssl_dir] - -> File["${::st2::params::nginx_conf_d}/st2.conf"] - ~> Service['nginx'] # notify to force a refresh + ## Note: nginx automatically configures the dependencies correctly for its configuration } diff --git a/spec/classes/profile/nginx_spec.rb b/spec/classes/profile/nginx_spec.rb new file mode 100644 index 00000000..36a96229 --- /dev/null +++ b/spec/classes/profile/nginx_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe 'st2::profile::nginx' do + on_supported_os.each do |os, os_facts| + let(:facts) do + os_facts.merge( + sudoversion: '1.8.23', + ) + end + + context "on #{os}" do + context 'with default options' do + it { is_expected.to compile.with_all_deps } + it do + is_expected.to contain_class('nginx') + .with(manage_repo: true, + confd_purge: true) + end + end # context 'with default options' + + context 'with manage_repo=false' do + let(:params) { { manage_repo: false } } + + it { is_expected.to compile.with_all_deps } + it do + is_expected.to contain_class('nginx') + .with(manage_repo: false, + confd_purge: true) + end + end # context 'with manage_repo=false' + end # context 'on #{os}' + end # on_supported_os +end # describe 'st2::profile::nginx' diff --git a/spec/classes/profile/server_spec.rb b/spec/classes/profile/server_spec.rb index aa7f98ef..94550d76 100644 --- a/spec/classes/profile/server_spec.rb +++ b/spec/classes/profile/server_spec.rb @@ -1,20 +1,7 @@ require 'spec_helper' describe 'st2::profile::server' do - all_os = { - supported_os: [ - { - 'operatingsystem' => 'RedHat', - 'operatingsystemrelease' => ['7'], - }, - { - 'operatingsystem' => 'Ubuntu', - 'operatingsystemrelease' => ['16.04', '18.04'], - }, - ], - } - - on_supported_os(all_os).each do |os, os_facts| + on_supported_os.each do |os, os_facts| let(:facts) do os_facts.merge( sudoversion: '1.8.23', diff --git a/spec/classes/profile/web_spec.rb b/spec/classes/profile/web_spec.rb new file mode 100644 index 00000000..f642eaae --- /dev/null +++ b/spec/classes/profile/web_spec.rb @@ -0,0 +1,381 @@ +require 'spec_helper' + +describe 'st2::profile::web' do + on_supported_os.each do |os, os_facts| + let(:facts) { os_facts } + let(:ssl_dir) { '/etc/ssl/st2' } + let(:add_header) do + { + 'Front-End-Https' => 'on', + 'X-Content-Type-Options' => 'nosniff', + } + end + let(:ssl_ciphers) do + [ + # TLSv1.3 + 'TLS_AES_128_GCM_SHA256', + 'TLS_AES_256_GCM_SHA384', + 'TLS_CHACHA20_POLY1305_SHA256', + # TLSv1.2 + 'ECDHE-ECDSA-AES128-GCM-SHA256', + 'ECDHE-ECDSA-AES128-SHA256', + 'ECDHE-ECDSA-AES256-GCM-SHA384', + 'ECDHE-ECDSA-AES256-SHA384', + 'ECDHE-ECDSA-CHACHA20-POLY1305', + 'ECDHE-RSA-AES128-GCM-SHA256', + 'ECDHE-RSA-AES128-SHA256', + 'ECDHE-RSA-AES256-GCM-SHA384', + 'ECDHE-RSA-AES256-SHA384', + 'ECDHE-RSA-CHACHA20-POLY1305', + ].join(':') + end + let(:ssl_protocols) do + [ + 'TLSv1.2', + 'TLSv1.3', + ].join(' ') + end + + context "on #{os}" do + context 'with default options' do + it { is_expected.to compile.with_all_deps } + it { is_expected.to contain_class('st2::profile::nginx') } + it do + is_expected.to contain_package('st2web') + .with(ensure: 'present', + tag: ['st2::packages', 'st2::web::packages']) + .that_requires('Package[nginx]') + .that_notifies('Service[nginx]') + end + it { is_expected.to contain_file(ssl_dir).with(ensure: 'directory') } + it do + is_expected.to contain_exec('generate ssl cert /etc/ssl/st2/st2.crt') + .with(creates: '/etc/ssl/st2/st2.crt', + path: ['/usr/bin', '/bin']) + .that_requires('File[/etc/ssl/st2]') + .that_notifies('Nginx::Resource::Server[st2webui]') + end + it do + is_expected.to contain_nginx__resource__server('st2webui') + .with(ensure: 'present', + listen_port: 80, + access_log: '/var/log/nginx/st2webui.access.log', + error_log: '/var/log/nginx/st2webui.error.log', + ssl_redirect: true, + add_header: add_header, + tag: ['st2', 'st2::frontend', 'st2::frontend::http']) + end + it do + is_expected.to contain_nginx__resource__server('ssl-st2webui') + .with(ensure: 'present', + listen_port: 443, + index_files: ['index.html'], + access_log: '/var/log/nginx/ssl-st2webui.access.log', + error_log: '/var/log/nginx/ssl-st2webui.error.log', + use_default_location: false, + ssl: true, + ssl_cert: '/etc/ssl/st2/st2.crt', + ssl_key: '/etc/ssl/st2/st2.key', + ssl_port: 443, + ssl_ciphers: ssl_ciphers, + ssl_protocols: ssl_protocols, + client_max_body_size: '0', + add_header: add_header, + server_cfg_append: { + 'rewrite' => [ + '^/api/stream/?$ /stream/v1/stream break', + '^/api/(v\d)/stream/?$ /stream/$1/stream break', + ], + }, + tag: ['st2', 'st2::frontend', 'st2::frontend::https']) + end + it do + is_expected.to contain_nginx__resource__location('/') + .with(ensure: 'present', + server: 'ssl-st2webui', + ssl: true, + ssl_only: true, + index_files: ['index.html'], + www_root: '/opt/stackstorm/static/webui/', + location_cfg_append: { + 'sendfile' => 'on', + 'tcp_nopush' => 'on', + 'tcp_nodelay' => 'on', + }, + tag: ['st2', 'st2::backend', 'st2::backend::webui']) + end + it do + is_expected.to contain_nginx__resource__location('@apiError') + .with(ensure: 'present', + server: 'ssl-st2webui', + ssl: true, + ssl_only: true, + index_files: [], + add_header: { + 'Content-Type' => 'application/json always', + }, + location_cfg_append: { + 'return' => '503 \'{ "faultstring": "Nginx is unable to reach st2api. Make sure service is running." }\'', + }, + tag: ['st2', 'st2::backend', 'st2::backend::apierror']) + end + it do + is_expected.to contain_nginx__resource__location('/api/') + .with(ensure: 'present', + server: 'ssl-st2webui', + ssl: true, + ssl_only: true, + index_files: [], + proxy_read_timeout: '90', + proxy_connect_timeout: '90', + proxy_redirect: 'off', + proxy_set_header: [ + 'Host $host', + 'X-Real-IP $remote_addr', + 'X-Forwarded-For $proxy_add_x_forwarded_for', + 'X-Forwarded-Proto "https"', + 'Connection \'\'', + ], + proxy_buffering: 'off', + proxy_cache: 'off', + rewrite_rules: [ + '^/api/(.*) /$1 break', + ], + proxy: 'http://127.0.0.1:9101', + location_cfg_append: { + 'error_page' => '502 = @apiError', + 'chunked_transfer_encoding' => 'off', + }, + tag: ['st2', 'st2::backend', 'st2::backend::api']) + end + it do + is_expected.to contain_nginx__resource__location('@streamError') + .with(ensure: 'present', + server: 'ssl-st2webui', + ssl: true, + ssl_only: true, + index_files: [], + add_header: { + 'Content-Type' => 'text/event-stream', + }, + location_cfg_append: { + 'return' => '200 "retry: 1000\n\n"', + }, + tag: ['st2', 'st2::backend', 'st2::backend::streamerror']) + end + it do + is_expected.to contain_nginx__resource__location('/stream/') + .with(ensure: 'present', + server: 'ssl-st2webui', + ssl: true, + ssl_only: true, + index_files: [], + proxy_read_timeout: '90', + proxy_connect_timeout: '90', + proxy_redirect: 'off', + proxy_set_header: [ + 'Host $host', + 'X-Real-IP $remote_addr', + 'X-Forwarded-For $proxy_add_x_forwarded_for', + 'X-Forwarded-Proto "https"', + 'Connection \'\'', + ], + proxy_buffering: 'off', + proxy_cache: 'off', + rewrite_rules: [ + '^/stream/(.*) /$1 break', + ], + proxy: 'http://127.0.0.1:9102', + location_cfg_append: { + 'error_page' => '502 = @streamError', + 'chunked_transfer_encoding' => 'off', + 'sendfile' => 'off', + 'tcp_nopush' => 'off', + 'tcp_nodelay' => 'off', + }, + tag: ['st2', 'st2::backend', 'st2::backend::stream']) + end + it do + is_expected.to contain_nginx__resource__location('@authError') + .with(ensure: 'present', + server: 'ssl-st2webui', + ssl: true, + ssl_only: true, + index_files: [], + add_header: { + 'Content-Type' => 'application/json always', + }, + location_cfg_append: { + 'return' => '503 \'{ "faultstring": "Nginx is unable to reach st2auth. Make sure service is running." }\'', + }, + tag: ['st2', 'st2::backend', 'st2::backend::autherror']) + end + it do + is_expected.to contain_nginx__resource__location('/auth/') + .with(ensure: 'present', + server: 'ssl-st2webui', + ssl: true, + ssl_only: true, + index_files: [], + proxy_read_timeout: '90', + proxy_connect_timeout: '90', + proxy_redirect: 'off', + proxy_set_header: [ + 'Host $host', + 'X-Real-IP $remote_addr', + 'X-Forwarded-For $proxy_add_x_forwarded_for', + 'X-Forwarded-Proto "https"', + 'Connection \'\'', + ], + proxy_buffering: 'off', + proxy_cache: 'off', + rewrite_rules: [ + '^/auth/(.*) /$1 break', + ], + proxy: 'http://127.0.0.1:9100', + proxy_pass_header: [ + 'Authorization', + ], + location_cfg_append: { + 'error_page' => '502 = @authError', + 'chunked_transfer_encoding' => 'off', + }, + tag: ['st2', 'st2::backend', 'st2::backend::auth']) + end + end # context 'on #{os} with default options' + + context 'when specifying custom nginx_ssl_ciphers and nginx_ssl_protocols as strings' do + let(:params) do + { + nginx_ssl_ciphers: 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384', + nginx_ssl_protocols: 'TLSv1.1 TLSv1.2', + } + end + + it { is_expected.to compile.with_all_deps } + it do + is_expected.to contain_nginx__resource__server('ssl-st2webui') + .with(ensure: 'present', + listen_port: 443, + index_files: ['index.html'], + access_log: '/var/log/nginx/ssl-st2webui.access.log', + error_log: '/var/log/nginx/ssl-st2webui.error.log', + use_default_location: false, + ssl: true, + ssl_cert: '/etc/ssl/st2/st2.crt', + ssl_key: '/etc/ssl/st2/st2.key', + ssl_port: 443, + ssl_ciphers: 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384', + ssl_protocols: 'TLSv1.1 TLSv1.2', + client_max_body_size: '0', + add_header: add_header, + server_cfg_append: { + 'rewrite' => [ + '^/api/stream/?$ /stream/v1/stream break', + '^/api/(v\d)/stream/?$ /stream/$1/stream break', + ], + }, + tag: ['st2', 'st2::frontend', 'st2::frontend::https']) + end + end # context 'when specifying custom nginx_ssl_ciphers and nginx_ssl_protocols as strings' + + context 'when specifying custom nginx_ssl_ciphers and nginx_ssl_protocols as arrays' do + let(:params) do + { + nginx_ssl_ciphers: ['ECDHE-ECDSA-AES128-GCM-SHA256', 'ECDHE-ECDSA-AES256-SHA384'], + nginx_ssl_protocols: ['TLSv1.1', 'TLSv1.2'], + } + end + + it { is_expected.to compile.with_all_deps } + it do + is_expected.to contain_nginx__resource__server('ssl-st2webui') + .with(ensure: 'present', + listen_port: 443, + index_files: ['index.html'], + access_log: '/var/log/nginx/ssl-st2webui.access.log', + error_log: '/var/log/nginx/ssl-st2webui.error.log', + use_default_location: false, + ssl: true, + ssl_cert: '/etc/ssl/st2/st2.crt', + ssl_key: '/etc/ssl/st2/st2.key', + ssl_port: 443, + ssl_ciphers: 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384', + ssl_protocols: 'TLSv1.1 TLSv1.2', + client_max_body_size: '0', + add_header: add_header, + server_cfg_append: { + 'rewrite' => [ + '^/api/stream/?$ /stream/v1/stream break', + '^/api/(v\d)/stream/?$ /stream/$1/stream break', + ], + }, + tag: ['st2', 'st2::frontend', 'st2::frontend::https']) + end + end # context 'when specifying custom nginx_ssl_ciphers and nginx_ssl_protocols as arrays' + + context 'when specifying custom nginx_ssl_port and nginx_client_max_body_size' do + let(:params) do + { + nginx_ssl_port: 8443, + nginx_client_max_body_size: '8k', + } + end + + it { is_expected.to compile.with_all_deps } + it do + is_expected.to contain_nginx__resource__server('ssl-st2webui') + .with(ensure: 'present', + listen_port: 8443, + index_files: ['index.html'], + access_log: '/var/log/nginx/ssl-st2webui.access.log', + error_log: '/var/log/nginx/ssl-st2webui.error.log', + use_default_location: false, + ssl: true, + ssl_cert: '/etc/ssl/st2/st2.crt', + ssl_key: '/etc/ssl/st2/st2.key', + ssl_port: 8443, + ssl_ciphers: ssl_ciphers, + ssl_protocols: ssl_protocols, + client_max_body_size: '8k', + add_header: add_header, + server_cfg_append: { + 'rewrite' => [ + '^/api/stream/?$ /stream/v1/stream break', + '^/api/(v\d)/stream/?$ /stream/$1/stream break', + ], + }, + tag: ['st2', 'st2::frontend', 'st2::frontend::https']) + end + end # context 'when specifying custom nginx_ssl_port and nginx_client_max_body_size' + + context 'with web_root=/some/other/dir/' do + let(:params) { { web_root: '/some/other/dir/' } } + + it { is_expected.to compile.with_all_deps } + it do + is_expected.to contain_nginx__resource__location('/') + .with(ensure: 'present', + server: 'ssl-st2webui', + ssl: true, + ssl_only: true, + index_files: ['index.html'], + www_root: '/some/other/dir/', + location_cfg_append: { + 'sendfile' => 'on', + 'tcp_nopush' => 'on', + 'tcp_nodelay' => 'on', + }, + tag: ['st2', 'st2::backend', 'st2::backend::webui']) + end + end # context 'with web_root=/some/other/dir/' + + context 'with ssl_cert_manage=false' do + let(:params) { { ssl_cert_manage: false } } + + it { is_expected.to compile.with_all_deps } + it { is_expected.not_to contain_exec('generate ssl cert /etc/ssl/st2/st2.crt') } + end # context 'with ssl_cert_manage=false' + end # context 'on #{os}' + end # on_supported_os(all_os) +end # describe 'st2::profile::server' diff --git a/test/integration/stackstorm/controls/st2web_test.rb b/test/integration/stackstorm/controls/st2web_test.rb index 769cffaf..86241503 100644 --- a/test/integration/stackstorm/controls/st2web_test.rb +++ b/test/integration/stackstorm/controls/st2web_test.rb @@ -70,7 +70,16 @@ its('validity_in_days') { should be > 90 } end - describe file('/etc/nginx/conf.d/st2.conf') do + describe file('/etc/nginx/sites-available/st2webui.conf') do + it { should exist } + end + describe file('/etc/nginx/sites-available/ssl-st2webui.conf') do + it { should exist } + end + describe file('/etc/nginx/sites-enabled/st2webui.conf') do + it { should exist } + end + describe file('/etc/nginx/sites-enabled/ssl-st2webui.conf') do it { should exist } end @@ -85,7 +94,7 @@ end describe http('http://localhost/', enable_remote_worker: true) do - its('status') { should eq 308 } + its('status') { should eq 301 } end describe http('https://localhost/', ssl_verify: false, enable_remote_worker: true) do