6bf4dedafc
Change all titles to sentence style capitalization (some were already, majority not) Adjust project and service name spelling. Minor edits Fix links to TPM section Change-Id: Ic8cc709b068d2273762f074daa5ac30ebe9aaf20 Partial-Bug: #1217503
256 lines
14 KiB
XML
256 lines
14 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<chapter xmlns:xi="http://www.w3.org/2001/XInclude"
|
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
xmlns="http://docbook.org/ns/docbook"
|
|
version="5.0"
|
|
xml:id="ch025_web-dashboard">
|
|
<?dbhtml stop-chunking?>
|
|
<title>Dashboard</title>
|
|
<para>Horizon is the OpenStack dashboard that provides users a self-service
|
|
portal to provision their own resources within the limits set by
|
|
administrators. These include provisioning users, defining instance flavors,
|
|
uploading VM images, managing networks, setting up security groups, starting
|
|
instances, and accessing the instances via a console.</para>
|
|
<para>The dashboard is based on the Django web framework, therefore
|
|
secure deployment practices for Django apply directly to horizon.
|
|
This guide provides a popular set of Django security
|
|
recommendations, further information can be found by reading the
|
|
<link
|
|
xlink:href="https://docs.djangoproject.com/en/1.5/#security"
|
|
>Django deployment and security documentation</link>.</para>
|
|
<para>The dashboard ships with reasonable default security settings,
|
|
and has good <link
|
|
xlink:href="http://docs.openstack.org/developer/horizon/topics/deployment.html"
|
|
>deployment and configuration documentation</link>.</para>
|
|
<section xml:id="ch025_web-dashboard-idp237648">
|
|
<title>Basic web server configuration</title>
|
|
<para>The dashboard should be deployed as a Web Services Gateway
|
|
Interface (WSGI) application behind an HTTPS proxy such as
|
|
Apache or nginx. If Apache is not already in use, we recommend
|
|
nginx since it is lighter weight and easier to configure
|
|
correctly.</para>
|
|
<para>When using nginx, we recommend <link
|
|
xlink:href="http://docs.gunicorn.org/en/latest/deploy.html"
|
|
>gunicorn</link> as the wsgi host with an appropriate number
|
|
of synchronous workers. We strongly advise against deployments
|
|
using fastcgi, scgi, or uWSGI. We strongly advise against the
|
|
use of synthetic performance benchmarks when choosing a wsgi
|
|
server.</para>
|
|
<para>When using Apache, we recommend <link
|
|
xlink:href="https://docs.djangoproject.com/en/1.5/howto/deployment/wsgi/modwsgi/"
|
|
>mod_wsgi</link> to host dashboard.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp240704">
|
|
<title>HTTPS</title>
|
|
<para>The dashboard should be deployed behind a secure HTTPS
|
|
server using 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.</para>
|
|
<para>HTTP requests to the dashboard domain should be configured
|
|
to redirect to the fully qualified HTTPS URL.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp242624">
|
|
<title>HTTP Strict Transport Security (HSTS)</title>
|
|
<para>It is highly recommended to use HTTP Strict Transport
|
|
Security (HSTS).</para>
|
|
<para>NOTE: If you are using an HTTPS proxy in front of your web
|
|
server, rather than using an HTTP server with HTTPS
|
|
functionality, follow the <link
|
|
xlink:href="https://docs.djangoproject.com/en/1.5/ref/settings/#secure-proxy-ssl-header"
|
|
>Django documentation on modifying the SECURE_PROXY_SSL_HEADER
|
|
variable</link>.</para>
|
|
<para>See the chapter on PKI/SSL Everywhere for more specific
|
|
recommendations and server configurations for HTTPS
|
|
configurations, including the configuration of HSTS.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp245456">
|
|
<title>Front end caching</title>
|
|
<para>Since dashboard is rendering dynamic content passed directly
|
|
from OpenStack API requests, we do not recommend front end
|
|
caching layers such as varnish. In Django, static media is
|
|
directly served from Apache or nginx and already benefits from
|
|
web host caching.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp246880">
|
|
<title>Domain names</title>
|
|
<para>Many organizations typically deploy web applications at
|
|
subdomains of an overarching organization domain. It is natural
|
|
for users to expect a domain of the form
|
|
<uri>openstack.example.org</uri>. In this context, there are
|
|
often many other applications deployed in the same second-level
|
|
namespace, often serving user-controlled content. This name
|
|
structure is convenient and simplifies name server
|
|
maintenance.</para>
|
|
<para>We strongly recommend deploying horizon to a
|
|
<emphasis>second-level domain</emphasis>, such as
|
|
<uri>https://example.com</uri>, and advise against deploying
|
|
horizon on a <emphasis>shared subdomain</emphasis> of any level,
|
|
for example <uri>https://openstack.example.org</uri> or
|
|
<uri>https://horizon.openstack.example.org</uri>. We also
|
|
advise against deploying to bare internal domains like
|
|
<uri>https://horizon/</uri>.</para>
|
|
<para>This recommendation is based on the limitations browser
|
|
same-origin-policy. The recommendations in this guide cannot
|
|
effectively protect users against known attacks if dashboard is
|
|
deployed on a domain which also hosts user-generated content,
|
|
such as scripts, images, or uploads of any kind, even if the
|
|
user-generated content is on a different subdomain. This
|
|
approach is used by most major web presences, such as
|
|
googleusercontent.com, fbcdn.com, github.io, and twimg.com, to
|
|
ensure that user generated content stays separate from cookies
|
|
and security tokens.</para>
|
|
<para>Additionally, if you decline to follow this recommendation
|
|
above about second-level domains, it is vital that you avoid the
|
|
cookie backed session store and employ HTTP Strict Transport
|
|
Security (HSTS). When deployed on a subdomain, dashboard's
|
|
security is only as strong as the weakest application deployed
|
|
on the same second-level domain.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp251760">
|
|
<title>Static media</title>
|
|
<para>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.</para>
|
|
<para>Django media settings are documented at <link
|
|
xlink:href="https://docs.djangoproject.com/en/1.5/ref/settings/#static-root"
|
|
>https://docs.djangoproject.com/en/1.5/ref/settings/#static-root</link>.</para>
|
|
<para>Dashboard's default configuration uses <link
|
|
xlink:href="http://django-compressor.readthedocs.org/"
|
|
>django_compressor</link> to compress and minify css and
|
|
JavaScript content before serving it. This process should be
|
|
statically done before deploying 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, we recommend disabling
|
|
resource compression entirely. Online compression dependencies
|
|
(less, nodejs) should not be installed on production
|
|
machines.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp255696">
|
|
<title>Secret key</title>
|
|
<para>Dashboard depends on a shared SECRET_KEY setting for some
|
|
security functions. It should be a randomly generated string at
|
|
least 64 characters long. It must be shared across all active
|
|
dashboard instances. Compromise of this key may 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.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp257248">
|
|
<title>Session back-end</title>
|
|
<para>Horizon's default session back-end
|
|
(<emphasis>django.contrib.sessions.backends.signed_cookies</emphasis>)
|
|
stores user data in <emphasis>signed</emphasis> but
|
|
<emphasis>unencrypted </emphasis>cookies stored in the
|
|
browser. This approach allows the most simple session backend
|
|
scaling since each dashboard instance is stateless, but it comes
|
|
at the cost of <emphasis>storing sensitive access tokens in the
|
|
client browser</emphasis> and transmitting them with every
|
|
request. This backend ensures that session data has not been
|
|
tampered with, but the data itself is not encrypted other than
|
|
the encryption provided by HTTPS.</para>
|
|
<para>If your architecture allows it, we recommend using
|
|
<emphasis>django.contrib.sessions.backends.cache</emphasis> as
|
|
your session backend with memcache as the cache. Memcache must
|
|
not be exposed publicly, and should communicate over a secured
|
|
private channel. If you choose to use the signed cookies
|
|
backend, refer to the Django documentation understand the
|
|
security trade-offs.</para>
|
|
<para>For further details, consult the <link
|
|
xlink:href="https://docs.djangoproject.com/en/1.5/topics/http/sessions/#configuring-the-session-engine"
|
|
>Django session backend documentation</link>.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp262288">
|
|
<title>Allowed hosts</title>
|
|
<para>Configure the ALLOWED_HOSTS setting with the domain or
|
|
domains where the dashboard is available. Failure to configure this
|
|
setting (especially if not following the recommendation above
|
|
regarding second level domains) opens the dashboard to a number of
|
|
serious attacks. Wild card domains should be avoided.</para>
|
|
<para>For further details, see the <link
|
|
xlink:href="https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts"
|
|
>Django documentation on settings</link>.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp264272">
|
|
<title>Cookies</title>
|
|
<para>Session Cookies should be set to HTTPONLY:</para>
|
|
<programlisting>SESSION_COOKIE_HTTPONLY = True</programlisting>
|
|
<para>Never configure CSRF or session cookies to have a wild card
|
|
domain with a leading dot. Horizon's session and CSRF cookie
|
|
should be secured when deployed with HTTPS:</para>
|
|
<programlisting>Code CSRF_COOKIE_SECURE = True
|
|
SESSION_COOKIE_SECURE = True</programlisting>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp266976">
|
|
<title>Password auto complete</title>
|
|
<para>We recommend that implementers do not change the default
|
|
password auto complete behavior. Users choose stronger passwords
|
|
in environments that allow them to use the secure browser
|
|
password manager. Organizations which forbid the browser
|
|
password manager should enforce this policy at the desktop
|
|
level.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp268448">
|
|
<title>Cross Site Request Forgery (CSRF)</title>
|
|
<para>Django has a dedicated middleware for <link
|
|
xlink:href="https://docs.djangoproject.com/en/1.5/ref/contrib/csrf/#how-it-works"
|
|
>cross-site request forgery</link> (CSRF).</para>
|
|
<para>Dashboard is designed to discourage developers from
|
|
introducing cross-site scripting vulnerabilities with custom
|
|
dashboards. However, it is important to audit custom dashboards,
|
|
especially ones that are javascript-heavy for inappropriate use
|
|
of the @csrf_exempt decorator. Dashboards which do not follow
|
|
these recommended security settings should be carefully
|
|
evaluated before restrictions are relaxed.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp270608">
|
|
<title>Cross Site Scripting (XSS)</title>
|
|
<para>Unlike many similar systems, OpenStack dashboard allows the
|
|
entire Unicode character set in most fields. This means
|
|
developers have less latitude to make escaping mistakes that
|
|
open attack vectors for cross-site scripting (XSS).</para>
|
|
<para>Dashboard provides tools for developers to avoid creating
|
|
XSS vulnerabilities, but they only work if developers use them
|
|
correctly. Audit any custom dashboards, paying particular
|
|
attention to use of the mark_safe function, use of is_safe with
|
|
custom template tags, the safe template tag, anywhere auto escape
|
|
is turned off, and any JavaScript which might evaluate
|
|
improperly escaped data.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp272832">
|
|
<title>Cross Origin Resource Sharing (CORS)</title>
|
|
<para>Configure your web server to send a restrictive CORS header
|
|
with each response, allowing only the dashboard domain and
|
|
protocol:</para>
|
|
<programlisting>Access-Control-Allow-Origin: https://example.com/</programlisting>
|
|
<para>Never allow the wild card origin.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp275056">
|
|
<title>Horizon image upload</title>
|
|
<para>We recommend that implementers <link
|
|
xlink:href="http://docs.openstack.org/developer/horizon/topics/deployment.html#file-uploads"
|
|
>disable HORIZON_IMAGES_ALLOW_UPLOAD</link> unless they have
|
|
implemented a plan to prevent resource exhaustion and denial of
|
|
service.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp276864">
|
|
<title>Upgrading</title>
|
|
<para>Django security releases are generally well tested and
|
|
aggressively backwards compatible. In almost all cases, new
|
|
major releases of Django are also fully backwards compatible
|
|
with previous releases. Dashboard implementers are strongly
|
|
encouraged to run the latest stable release of Django with
|
|
up-to-date security releases.</para>
|
|
</section>
|
|
<section xml:id="ch025_web-dashboard-idp278672">
|
|
<title>Debug</title>
|
|
<para>Make sure DEBUG is set to False in production. In Django,
|
|
DEBUG displays stack traces and sensitive web server state
|
|
information on any exception.</para>
|
|
</section>
|
|
</chapter>
|