Chapter 9. Hardening the Dashboard service The Dashboard service (horizon) gives users a self-service portal for provisioning their own resources within the limits set by administrators. Manage the security of the Dashboard service with the same sensitivity as the OpenStack APIs. 9.1. Debugging the Dashboard service The default value for the DEBUG parameter is False . Keep the default value in your production environment. Change this setting only during investigation. When you change the value of the DEBUG parameter to True , Django can output stack straces to browser users that contain sensitive web server state information. When the value of the DEBUG parameter is True , the ALLOWED_HOSTS settings are also disabled. For more information on configuring ALLOWED_HOSTS , see Configure ALLOWED_HOSTS . 9.2. Selecting a domain name It is a best practice to deploy the Dashboard service (horizon) to a second level domain, as opposed to a shared domain on any level. Examples of each are provided below: Second level domain: https://example.com Shared subdomain: https://example.public-url.com Deploying the Dashboard service to a dedicated second level domain isolates cookies and security tokens from other domains, based on browsers' same-origin policy. When deployed on a subdomain, the security of the Dashboard service is equivalent to the least secure application deployed on the same second-level domain. You can further mitigate this risk by avoiding a cookie-backed session store, and configuring HTTP Strict Transport Security (HSTS) (described in this guide). Note Deploying the Dashboard service on a bare domain, like https://example/ , is unsupported. 9.3. Configure ALLOWED_HOSTS Horizon is built on the python Django web framework, which requires protection against security threats associated with misleading HTTP Host headers. To apply this protection, configure the ALLOWED_HOSTS setting to use the FQDN that is served by the OpenStack dashboard. When you configure the ALLOWED_HOSTS setting, any HTTP request with a Host header that does not match the values in this list is denied, and an error is raised. Procedure Under parameter_defaults in your templates, set the value of the HorizonAllowedHosts parameter: Replace <value> with the FQDN that is served by the OpenStack dashboard. Deploy the overcloud with the modified template, and all other templates required for your environment. 9.4. Cross Site Scripting (XSS) The OpenStack Dashboard accepts the entire Unicode character set in most fields. Malicious actors can attempt to use this extensibility to test for cross-site scripting (XSS) vulnerabilities. The OpenStack Dashboard service (horizon) has tools that harden against XSS vulnerabilites. It is important to ensure the correct use of these tools in custom dashboards. When you perform an audit against custom dashboards, pay attention to the following: The mark_safe function. is_safe - when used with custom template tags. The safe template tag. Anywhere auto escape is turned off, and any JavaScript which might evaluate improperly escaped data. 9.5. Cross Site Request Forgery (CSRF) Dashboards that use multiple JavaScript instances should be audited for vulnerabilities such as inappropriate use of the @csrf_exempt decorator. Evaluate any dashboard that does not follow recommended security settings before lowering CORS (Cross Origin Resource Sharing) restrictions. Configure your web server to send a restrictive CORS header with each response. Allow only the dashboard domain and protocol, for example: Access-Control-Allow-Origin: https://example.com/ . You should never allow the wild card origin. 9.6. Allow iframe embedding The DISALLOW_IFRAME_EMBED setting disallows Dashboard from being embedded within an iframe. Legacy browsers can still be vulnerable to Cross-Frame Scripting (XFS) vulnerabilities, so this option adds extra security hardening for deployments that do not require iframes. The setting is set to True by default, however it can be disabled using an environment file, if needed. Procedure You can allow iframe embedding using the following parameter: Note These settings should only be set to False once the potential security impacts are fully understood. 9.7. Using HTTPS encryption for Dashboard traffic It is recommended you use HTTPS to encrypt Dashboard traffic. You can do this by configuring it to use a valid, trusted certificate from a recognized certificate authority (CA). Private organization-issued certificates are only appropriate when the root of trust is pre-installed in all user browsers. Configure HTTP requests to the dashboard domain to redirect to the fully qualified HTTPS URL. For more information, see Implementing TLS-e with Ansible 9.8. HTTP Strict Transport Security (HSTS) HTTP Strict Transport Security (HSTS) prevents browsers from making subsequent insecure connections after they have initially made a secure connection. If you have deployed your HTTP services on a public or an untrusted zone, HSTS is especially important. For director-based deployments, this setting is enabled by default in the /usr/share/openstack-tripleo-heat-templates/deployment/horizon/horizon-container-puppet.yaml file: Verification After the overcloud is deployed, check the local_settings file for Red Hat OpenStack Dashboard (horizon) for verification. Use ssh to connect to a controller: USD ssh heat-admin@controller-0 Check that the SECURE_PROXY_SSL_HEADER parameter has a value of ('HTTP_X_FORWARDED_PROTO', 'https') : sudo egrep ^SECURE_PROXY_SSL_HEADER /var/lib/config-data/puppet-generated/horizon/etc/openstack-dashboard/local_settings SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 9.9. Front-end caching It is not recommended to use front-end caching tools with the Dashboard, as it renders dynamic content resulting directly from OpenStack API requests. As a result, front-end caching layers such as varnish can prevent the correct content from being displayed. The Dashboard uses Django, which serves static media directly served from the web service and already benefits from web host caching. 9.10. Session backend For director-based deployments, the default session backend for horizon is django.contrib.sessions.backends.cache , which is combined with memcached. This approach is preferred to local-memory cache for performance reasons, is safer for highly-available and load balanced installs, and has the ability to share cache over multiple servers, while still treating it as a single cache. You can review these settings in director's horizon.yaml file: 9.11. Reviewing the secret key The Dashboard depends on a shared SECRET_KEY setting for some security functions. The secret key should be a randomly generated string at least 64 characters long, which must be shared across all active dashboard instances. Compromise of this key might allow a remote attacker to execute arbitrary code. Rotating this key invalidates existing user sessions and caching. Do not commit this key to public repositories. For director deployments, this setting is managed as the HorizonSecret value. 9.12. Configuring session cookies The Dashboard session cookies can be open to interaction by browser technologies, such as JavaScript. For director deployments with TLS everywhere, you can harden this behavior using the HorizonSecureCookies setting. Note Never configure CSRF or session cookies to use a wildcard domain with a leading dot. 9.13. Static media The dashboard's static media should be deployed to a subdomain of the dashboard domain and served by the web server. The use of an external content delivery network (CDN) is also acceptable. This subdomain should not set cookies or serve user-provided content. The media should also be served with HTTPS. Dashboard's default configuration uses django_compressor to compress and minify CSS and JavaScript content before serving it. This process should be statically done before deploying the dashboard, rather than using the default in-request dynamic compression and copying the resulting files along with deployed code or to the CDN server. Compression should be done in a non-production build environment. If this is not practical, consider disabling resource compression entirely. Online compression dependencies (less, Node.js) should not be installed on production machines. 9.14. Validating password complexity The OpenStack Dashboard (horizon) can use a password validation check to enforce password complexity. You can specify a regular expression for password validation, as well as help text to be displayed for failed tests. The following example requires users to create a password of between 8 to 18 characters in length: To apply this change to your deployment, save the settings as a file called horizon_password.yaml , and then pass it to the overcloud deploy command as follows. The <full environment> indicates that you must still include all of your original deployment parameters. For example: 9.15. Enforce the administrator password check The following setting is set to True by default, however it can be disabled using an environment file, if needed. Note These settings should only be set to False once the potential security impacts are fully understood. The ENFORCE_PASSWORD_CHECK setting in Dashboard's local_settings.py file displays an Admin Password field on the Change Password form, which helps verify that an administrator is initiating the password change. You can disable ENFORCE_PASSWORD_CHECK using an environment file: 9.16. Disable password reveal The following setting is set to True by default, however it can be disabled using an environment file, if needed. Note These settings should only be set to False once the potential security impacts are fully understood. The password reveal button allows a user at the Dashboard to view the password they are about to enter. This option can be toggled using the DISABLE_PASSWORD_REVEAL parameter: 9.17. Displaying a logon banner for the Dashboard Regulated industries such as HIPAA, PCI-DSS, and the US Government require you to display a user logon banner. The Red Hat OpenStack Platform (RHOSP) dashboard (horizon) uses a default theme (RCUE), which is stored inside the horizon container. Within the custom Dashboard container, you can create a logon banner by manually editing the /usr/share/openstack-dashboard/openstack_dashboard/themes/rcue/templates/auth/login.html file: Procedure Enter the required logon banner just before the {% include 'auth/_login.html' %} section. HTML tags are allowed: The above example produces a dashboard similar to the following: Additional resources Customizing the dashboard 9.18. Limiting the size of file uploads You can optionally configure the dashboard to limit the size of file uploads; this setting might be a requirement for various security hardening policies. LimitRequestBody - This value (in bytes) limits the maximum size of a file that you can transfer using the Dashboard, such as images and other large files. Important This setting has not been formally tested by Red Hat. It is recommended that you thoroughly test the effect of this setting before deploying it to your production environment. Note File uploads will fail if the value is too small. For example, this setting limits each file upload to a maximum size of 10 GB ( 10737418240 ). You will need to adjust this value to suit your deployment. /var/lib/config-data/puppet-generated/horizon/etc/httpd/conf/httpd.conf /var/lib/config-data/puppet-generated/horizon/etc/httpd/conf.d/10-horizon_vhost.conf /var/lib/config-data/puppet-generated/horizon/etc/httpd/conf.d/15-horizon_ssl_vhost.conf Note These configuration files are managed by Puppet, so any unmanaged changes are overwritten whenever you run the openstack overcloud deploy process.

Commands:
parameter_defaults: HorizonAllowedHosts: <value>
parameter_defaults: ControllerExtraConfig: horizon::disallow_iframe_embed: false
horizon::enable_secure_proxy_ssl_header: true
ssh heat-admin@controller-0
sudo egrep ^SECURE_PROXY_SSL_HEADER /var/lib/config-data/puppet-generated/horizon/etc/openstack-dashboard/local_settings SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
horizon::cache_backend: django.core.cache.backends.memcached.MemcachedCache horizon::django_session_engine: 'django.contrib.sessions.backends.cache'
parameter_defaults: HorizonPasswordValidator: '^.{8,18}USD' HorizonPasswordValidatorHelp: 'Password must be between 8 and 18 characters.'
openstack overcloud deploy --templates -e <full environment> -e horizon_password.yaml
parameter_defaults: ControllerExtraConfig: horizon::enforce_password_check: false
parameter_defaults: ControllerExtraConfig: horizon::disable_password_reveal: false
<snip> <div class="container"> <div class="row-fluid"> <div class="span12"> <div id="brand"> <img src="../../static/themes/rcue/images/RHOSP-Login-Logo.svg"> </div><!--/#brand--> </div><!--/.span*--> <!-- Start of Logon Banner --> <p>Authentication to this information system reflects acceptance of user monitoring agreement.</p> <!-- End of Logon Banner --> {% include 'auth/_login.html' %} </div><!--/.row-fluid-> </div><!--/.container--> {% block js %} {% include "horizon/_scripts.html" %} {% endblock %} </body> </html>
<Directory /> LimitRequestBody 10737418240 </Directory>
<Directory "/var/www"> LimitRequestBody 10737418240 </Directory>
<Directory "/var/www"> LimitRequestBody 10737418240 </Directory>