doc/gug/reverse-proxy.html (510 lines of code) (raw):
<!DOCTYPE html>
<html class="writer-html5" lang="en" >
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Proxying Guacamole — Apache Guacamole Manual v1.5.5</title>
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="_static/tabs.css" type="text/css" />
<link rel="stylesheet" href="_static/gug.css" type="text/css" />
<!--[if lt IE 9]>
<script src="_static/js/html5shiv.min.js"></script>
<![endif]-->
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=5929fcd5"></script>
<script src="_static/doctools.js?v=888ff710"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/tabs.js?v=3ee01567"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="Configuring Guacamole" href="configuring-guacamole.html" />
<link rel="prev" title="Installing Guacamole with Docker" href="guacamole-docker.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
Apache Guacamole
</a>
<div class="version">
1.5.5
</div>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<p class="caption" role="heading"><span class="caption-text">Overview</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">User's Guide</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="guacamole-architecture.html">Implementation and architecture</a></li>
<li class="toctree-l1"><a class="reference internal" href="installing-guacamole.html">Installing Guacamole natively</a></li>
<li class="toctree-l1"><a class="reference internal" href="guacamole-docker.html">Installing Guacamole with Docker</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">Proxying Guacamole</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#preparing-your-servlet-container">Preparing your servlet container</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#setting-up-the-remote-ip-valve">Setting up the Remote IP Valve</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#nginx">Nginx</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#proxying-with-nginx">Proxying Guacamole</a></li>
<li class="toctree-l3"><a class="reference internal" href="#changing-the-path">Changing the path</a></li>
<li class="toctree-l3"><a class="reference internal" href="#adjusting-file-upload-limits">Adjusting file upload limits</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="#apache-and-mod-proxy">Apache and mod_proxy</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#proxying-with-apache">Proxying Guacamole</a></li>
<li class="toctree-l3"><a class="reference internal" href="#proxying-the-websocket-tunnel">Proxying the WebSocket tunnel</a></li>
<li class="toctree-l3"><a class="reference internal" href="#changing-path-with-apache">Changing the path</a></li>
<li class="toctree-l3"><a class="reference internal" href="#disabling-logging-of-tunnel-requests">Disabling logging of tunnel requests</a></li>
</ul>
</li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="configuring-guacamole.html">Configuring Guacamole</a></li>
<li class="toctree-l1"><a class="reference internal" href="jdbc-auth.html">Database authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="ldap-auth.html">LDAP authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="vault.html">Retrieving secrets from a vault</a></li>
<li class="toctree-l1"><a class="reference internal" href="duo-auth.html">Duo two-factor authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="totp-auth.html">TOTP two-factor authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="header-auth.html">HTTP header authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="json-auth.html">Encrypted JSON authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="cas-auth.html">CAS Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="openid-auth.html">OpenID Connect Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="saml-auth.html">SAML Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="radius-auth.html">RADIUS Authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="adhoc-connections.html">Ad-hoc Connections</a></li>
<li class="toctree-l1"><a class="reference internal" href="using-guacamole.html">Using Guacamole</a></li>
<li class="toctree-l1"><a class="reference internal" href="recording-playback.html">Viewing session recordings in-browser</a></li>
<li class="toctree-l1"><a class="reference internal" href="administration.html">Administration</a></li>
<li class="toctree-l1"><a class="reference internal" href="troubleshooting.html">Troubleshooting</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Developer's Guide</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="guacamole-protocol.html">The Guacamole protocol</a></li>
<li class="toctree-l1"><a class="reference internal" href="libguac.html">libguac</a></li>
<li class="toctree-l1"><a class="reference internal" href="guacamole-common.html">guacamole-common</a></li>
<li class="toctree-l1"><a class="reference internal" href="guacamole-common-js.html">guacamole-common-js</a></li>
<li class="toctree-l1"><a class="reference internal" href="guacamole-ext.html">guacamole-ext</a></li>
<li class="toctree-l1"><a class="reference internal" href="custom-protocols.html">Adding new protocols</a></li>
<li class="toctree-l1"><a class="reference internal" href="custom-auth.html">Custom authentication</a></li>
<li class="toctree-l1"><a class="reference internal" href="event-listeners.html">Event listeners</a></li>
<li class="toctree-l1"><a class="reference internal" href="writing-you-own-guacamole-app.html">Writing your own Guacamole application</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">Appendices</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="protocol-reference.html">Guacamole protocol reference</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">Apache Guacamole</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Proxying Guacamole</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/reverse-proxy.md.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<section id="proxying-guacamole">
<h1>Proxying Guacamole<a class="headerlink" href="#proxying-guacamole" title="Link to this heading"></a></h1>
<p>Like most web applications, Guacamole can be placed behind a reverse proxy. For
production deployments of Guacamole, this is <em>highly recommended</em>. It provides
flexibility and, if your proxy is properly configured for SSL, encryption.</p>
<p>Proxying isolates privileged operations within native applications that can
safely drop those privileges when no longer needed, using Java only for
unprivileged tasks. On Linux and UNIX systems, a process must be running with
root privileges to listen on any port under 1024, including the standard HTTP
and HTTPS ports (80 and 443 respectively). If the servlet container instead
listens on a higher port, such as the default port 8080, it can run as a
reduced-privilege user, allowing the reverse proxy to bear the burden of root
privileges. As a native application, the reverse proxy can make system calls to
safely drop root privileges once the port is open; a Java application like
Tomcat cannot do this.</p>
<section id="preparing-your-servlet-container">
<span id="preparing-servlet-container"></span><h2>Preparing your servlet container<a class="headerlink" href="#preparing-your-servlet-container" title="Link to this heading"></a></h2>
<p>Your servlet container is most likely already configured to listen for HTTP
connections on port 8080 as this is the default. If this is the case, and you
can already access Guacamole over port 8080 from a web browser, you need not
make any further changes to its configuration.</p>
<p>If you <em>have</em> changed this, perhaps with the intent of proxying Guacamole over
AJP, <em>change it back</em>. Using Guacamole over AJP is unsupported as it is known
to cause problems, namely:</p>
<ol class="arabic simple">
<li><p>WebSocket will not work over AJP, forcing Guacamole to fallback to HTTP,
possibly resulting in reduced performance.</p></li>
<li><p>Apache 2.4.3 and older does not support the HTTP PATCH method over AJP,
preventing the Guacamole management interface from functioning properly.</p></li>
</ol>
<p>The connector entry within <code class="docutils literal notranslate"><span class="pre">conf/server.xml</span></code> should look like this:</p>
<div class="highlight-xml notranslate"><div class="highlight"><pre><span></span><span class="nt"><Connector</span><span class="w"> </span><span class="na">port=</span><span class="s">"8080"</span><span class="w"> </span><span class="na">protocol=</span><span class="s">"HTTP/1.1"</span><span class="w"> </span>
<span class="w"> </span><span class="na">connectionTimeout=</span><span class="s">"20000"</span>
<span class="w"> </span><span class="na">URIEncoding=</span><span class="s">"UTF-8"</span>
<span class="w"> </span><span class="na">redirectPort=</span><span class="s">"8443"</span><span class="w"> </span><span class="nt">/></span>
</pre></div>
</div>
<p>Be sure to specify the <code class="docutils literal notranslate"><span class="pre">URIEncoding="UTF-8"</span></code> attribute as above to ensure that
connection names, user names, etc. are properly received by the web
application. If you will be creating connections that have Cyrillic, Chinese,
Japanese, or other non-Latin characters in their names or parameter values,
this attribute is required.</p>
<section id="setting-up-the-remote-ip-valve">
<span id="tomcat-remote-ip"></span><h3>Setting up the Remote IP Valve<a class="headerlink" href="#setting-up-the-remote-ip-valve" title="Link to this heading"></a></h3>
<p>By default, when Tomcat is behind a reverse proxy, the remote IP address of the
client that it sees is that of the proxy rather than the original client. In
order to allow applications hosted within Tomcat, like Guacamole, to see the
actual IP address of the client, you have to configure both the reverse proxy
and Tomcat.</p>
<p>Because the remote IP address in Guacamole is used for auditing of user logins
and connections and could potentially be used for authentication, it is
important that you are either in direct control of the proxy server or you
explicitly trust it. Passing the remote IP address is done using the
<code class="docutils literal notranslate"><span class="pre">X-Forwarded-For</span></code> header, and, as with most HTTP headers, attackers can attempt
to spoof this header in order to manipulate the behavior of the web server,
gain unauthorized access to the system, or attempt to disguise the host or IP
address they are coming from.</p>
<p>One final caveat: This may not work as expected if there are other upstream
proxy servers between your reverse proxy and the clients access Guacamole.
Other proxies or firewalls can mask the IP address of the client, and if the
configuration of those is not within your control you may end up with multiple
clients appearing to come from the same IP address or host. Make sure you take
this into account when configuring the system and looking at the data provided.</p>
<p>Configuring Tomcat to pass through the remote IP address provided by the
reverse proxy in the <code class="docutils literal notranslate"><span class="pre">X-Forwarded-For</span></code> header requires the configuration of
what Tomcat calls a Valve. In this case, it is the
<a class="reference external" href="https://tomcat.apache.org/tomcat-8.5-doc/config/valve.html#Remote_IP_Valve"><code class="docutils literal notranslate"><span class="pre">RemoteIpValve</span></code></a>
and is configured in the <code class="docutils literal notranslate"><span class="pre">conf/server.xml</span></code> file, in the <code class="docutils literal notranslate"><span class="pre"><Host></span></code> section:</p>
<div class="highlight-xml notranslate"><div class="highlight"><pre><span></span><span class="nt"><Valve</span><span class="w"> </span><span class="na">className=</span><span class="s">"org.apache.catalina.valves.RemoteIpValve"</span>
<span class="w"> </span><span class="na">internalProxies=</span><span class="s">"127.0.0.1"</span>
<span class="w"> </span><span class="na">remoteIpHeader=</span><span class="s">"x-forwarded-for"</span>
<span class="w"> </span><span class="na">remoteIpProxiesHeader=</span><span class="s">"x-forwarded-by"</span>
<span class="w"> </span><span class="na">protocolHeader=</span><span class="s">"x-forwarded-proto"</span><span class="w"> </span><span class="nt">/></span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">internalProxies</span></code> value should be set to the IP address or addresses of any
and all reverse proxy servers that will be accessing this Tomcat instance
directly. Often it is run on the same system that runs Tomcat, but in other
cases (for example, when running Docker), it may be on a different
system/container and may need to be set to the actual IP address of the reverse
proxy system. Only proxy servers listed in the <code class="docutils literal notranslate"><span class="pre">internalProxies</span></code> or
<code class="docutils literal notranslate"><span class="pre">trustedProxies</span></code> parameters will be allowed to manipulate the remote IP address
information. The other parameters in this configuration line allow you to
control which headers coming from the proxy server(s) are used for various
remote host information. They are as follows:</p>
<dl class="simple myst">
<dt><code class="docutils literal notranslate"><span class="pre">remoteIpHeader</span></code></dt><dd><p>The header that is queried to learn the client IP address of the client
that originated the request. The standard value is <code class="docutils literal notranslate"><span class="pre">X-Forwarded-For</span></code>, but
can be configured to any header you like. The IP address in this header
will be available to Java applications in the <code class="docutils literal notranslate"><span class="pre">request.getRemoteAddr()</span></code>
method.</p>
</dd>
<dt><code class="docutils literal notranslate"><span class="pre">remoteIpProxiesHeader</span></code></dt><dd><p>The header that is queried to learn the IP address of the proxy server
that forwarded the request. The default value is <code class="docutils literal notranslate"><span class="pre">X-Forwarded-By</span></code>, but can
be configured to any header that fits your environment. This value will
only be allowed by the valve if the proxy used is listed in the
<code class="docutils literal notranslate"><span class="pre">trustedProxies</span></code> parameter. Otherwise this header will not be available.</p>
</dd>
<dt><code class="docutils literal notranslate"><span class="pre">protocolHeader</span></code></dt><dd><p>The header that is queried to determine the protocol that the client used
to connect to the service. The default value is <code class="docutils literal notranslate"><span class="pre">X-Forwarded-Proto</span></code>, but
can be configured to fit your environment.</p>
</dd>
</dl>
<p>In addition to configuring Tomcat to properly handle these headers, you also
may need to configure your reverse proxy appropriately to send the headers. You
can find instructions for this in <a class="reference internal" href="#nginx"><span class="std std-ref">Nginx</span></a> - the Apache web server passes it
through by default.</p>
</section>
</section>
<section id="nginx">
<span id="id1"></span><h2>Nginx<a class="headerlink" href="#nginx" title="Link to this heading"></a></h2>
<p>Nginx can be used as a reverse proxy, and supports WebSocket out-of-the-box
<a class="reference external" href="http://nginx.com/blog/websocket-nginx/">since version 1.3</a>. Both Apache and
Nginx require some additional configuration for proxying of WebSocket to work
properly.</p>
<section id="proxying-with-nginx">
<span id="id2"></span><h3>Proxying Guacamole<a class="headerlink" href="#proxying-with-nginx" title="Link to this heading"></a></h3>
<p>Nginx does support WebSocket for proxying, but requires that the “Connection”
and “Upgrade” HTTP headers are set explicitly due to the nature of the
WebSocket protocol. From the Nginx documentation:</p>
<blockquote>
<div><p>NGINX supports WebSocket by allowing a tunnel to be set up between a client
and a back-end server. For NGINX to send the Upgrade request from the client
to the back-end server, Upgrade and Connection headers must be set
explicitly. …</p>
</div></blockquote>
<p>The proxy configuration belongs within a dedicated
<a class="reference external" href="http://nginx.org/en/docs/http/ngx_http_core_module.html#location%3E"><code class="docutils literal notranslate"><span class="pre">location</span></code></a>
block, declaring the backend hosting Guacamole and explicitly specifying the
“<code class="docutils literal notranslate"><span class="pre">Connection</span></code>” and “<code class="docutils literal notranslate"><span class="pre">Upgrade</span></code>” headers mentioned earlier:</p>
<div class="highlight-nginx notranslate"><div class="highlight"><pre><span></span><span class="k">location</span><span class="w"> </span><span class="s">/guacamole/</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kn">proxy_pass</span><span class="w"> </span><span class="s">http://HOSTNAME:8080</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_buffering</span><span class="w"> </span><span class="no">off</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_http_version</span><span class="w"> </span><span class="mi">1</span><span class="s">.1</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">X-Forwarded-For</span><span class="w"> </span><span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Upgrade</span><span class="w"> </span><span class="nv">$http_upgrade</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Connection</span><span class="w"> </span><span class="nv">$http_connection</span><span class="p">;</span>
<span class="w"> </span><span class="kn">access_log</span><span class="w"> </span><span class="no">off</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Here, <code class="docutils literal notranslate"><span class="pre">HOSTNAME</span></code> is the hostname or IP address of the machine hosting your
servlet container, and 8080 is the port that servlet container is configured to
use. You will need to replace these values with the correct values for your
server.</p>
<p>Related to the <code class="docutils literal notranslate"><span class="pre">RemoteIpValve</span></code> configuration for tomcat, documented in
<a class="reference internal" href="#tomcat-remote-ip"><span class="std std-ref">Setting up the Remote IP Valve</span></a>, the <code class="docutils literal notranslate"><span class="pre">proxy_set_header</span> <span class="pre">X-Forwarded-For</span> <span class="pre">$proxy_add_x_forwarded_for;</span></code> line is important if you want the
<code class="docutils literal notranslate"><span class="pre">X-Forwarded-For</span></code> header to be passed through to the web application server and
available to applications running inside it.</p>
<div class="admonition important">
<p class="admonition-title">Important</p>
<p><em>Do not forget to specify “<code class="docutils literal notranslate"><span class="pre">proxy_buffering</span> <span class="pre">off</span></code>”.</em></p>
<p>Most proxies, including Nginx, will buffer all data sent over the connection,
waiting until the connection is closed before sending that data to the client.
As Guacamole’s HTTP tunnel relies on streaming data to the client over an open
connection, excessive buffering will effectively block Guacamole connections,
rendering Guacamole useless.</p>
<p><em>If the option “<code class="docutils literal notranslate"><span class="pre">proxy_buffering</span> <span class="pre">off</span></code>” is not specified, Guacamole may not
work</em>.</p>
</div>
</section>
<section id="changing-the-path">
<span id="changing-path-with-nginx"></span><h3>Changing the path<a class="headerlink" href="#changing-the-path" title="Link to this heading"></a></h3>
<p>If you wish to serve Guacamole through Nginx under a path other than
<code class="docutils literal notranslate"><span class="pre">/guacamole/</span></code>, the easiest method is to simply rename the <code class="docutils literal notranslate"><span class="pre">.war</span></code> file. For
example, if intending to server Guacamole at <code class="docutils literal notranslate"><span class="pre">/new-path/</span></code>, you would:</p>
<ol class="arabic">
<li><p>Rename <code class="docutils literal notranslate"><span class="pre">guacamole.war</span></code> to <code class="docutils literal notranslate"><span class="pre">new-path.war</span></code>.</p></li>
<li><p>Update the path within the Nginx configuration to reflect the new
path:</p>
<div class="highlight-nginx notranslate"><div class="highlight"><pre><span></span><span class="hll"><span class="k">location</span><span class="w"> </span><span class="s">/new-path/</span><span class="w"> </span><span class="p">{</span>
</span><span class="w"> </span><span class="kn">proxy_pass</span><span class="w"> </span><span class="s">http://HOSTNAME:8080</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_buffering</span><span class="w"> </span><span class="no">off</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_http_version</span><span class="w"> </span><span class="mi">1</span><span class="s">.1</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">X-Forwarded-For</span><span class="w"> </span><span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Upgrade</span><span class="w"> </span><span class="nv">$http_upgrade</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Connection</span><span class="w"> </span><span class="nv">$http_connection</span><span class="p">;</span>
<span class="w"> </span><span class="kn">access_log</span><span class="w"> </span><span class="no">off</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</div>
</li>
</ol>
<p>Alternatively, the configuration can be altered slightly to handle requests at
a different location externally while still serving internal requests at
<code class="docutils literal notranslate"><span class="pre">/guacamole/</span></code>:</p>
<div class="highlight-nginx notranslate"><div class="highlight"><pre><span></span><span class="hll"><span class="k">location</span><span class="w"> </span><span class="s">/new-path/</span><span class="w"> </span><span class="p">{</span>
</span><span class="hll"><span class="w"> </span><span class="kn">proxy_pass</span><span class="w"> </span><span class="s">http://HOSTNAME:8080/guacamole/</span><span class="p">;</span>
</span><span class="w"> </span><span class="kn">proxy_buffering</span><span class="w"> </span><span class="no">off</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_http_version</span><span class="w"> </span><span class="mi">1</span><span class="s">.1</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">X-Forwarded-For</span><span class="w"> </span><span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Upgrade</span><span class="w"> </span><span class="nv">$http_upgrade</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Connection</span><span class="w"> </span><span class="nv">$http_connection</span><span class="p">;</span>
<span class="w"> </span><span class="kn">access_log</span><span class="w"> </span><span class="no">off</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</div>
</section>
<section id="adjusting-file-upload-limits">
<span id="nginx-file-upload-size"></span><h3>Adjusting file upload limits<a class="headerlink" href="#adjusting-file-upload-limits" title="Link to this heading"></a></h3>
<p>When proxying Guacamole through Nginx, you may run into issues with the default
limitations that Nginx places on file uploads (1MB). The errors you receive can
be non-intuitive (permission denied, for example), but may be indicative of
these limits. The <code class="docutils literal notranslate"><span class="pre">client_max_body_size</span></code> parameter can be set within the
<code class="docutils literal notranslate"><span class="pre">location</span></code> block to configure the maximum file upload size:</p>
<div class="highlight-nginx notranslate"><div class="highlight"><pre><span></span><span class="k">location</span><span class="w"> </span><span class="s">/guacamole/</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kn">proxy_pass</span><span class="w"> </span><span class="s">http://HOSTNAME:8080</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_buffering</span><span class="w"> </span><span class="no">off</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_http_version</span><span class="w"> </span><span class="mi">1</span><span class="s">.1</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">X-Forwarded-For</span><span class="w"> </span><span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Upgrade</span><span class="w"> </span><span class="nv">$http_upgrade</span><span class="p">;</span>
<span class="w"> </span><span class="kn">proxy_set_header</span><span class="w"> </span><span class="s">Connection</span><span class="w"> </span><span class="nv">$http_connection</span><span class="p">;</span>
<span class="hll"><span class="w"> </span><span class="kn">client_max_body_size</span><span class="w"> </span><span class="s">1g</span><span class="p">;</span>
</span><span class="w"> </span><span class="kn">access_log</span><span class="w"> </span><span class="no">off</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</div>
</section>
</section>
<section id="apache-and-mod-proxy">
<span id="apache"></span><h2>Apache and mod_proxy<a class="headerlink" href="#apache-and-mod-proxy" title="Link to this heading"></a></h2>
<p>Apache supports reverse proxy configurations through
<a class="reference external" href="http://httpd.apache.org/docs/2.4/mod/mod_proxy.html">mod_proxy</a>. Apache 2.4.5
and later also support proxying of WebSocket through a sub-module called
<a class="reference external" href="http://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html">mod_proxy_wstunnel</a>.
Both of these modules will need to be enabled for proxying of Guacamole to work
properly.</p>
<p>Lacking mod_proxy_wstunnel, it is still possible to proxy Guacamole, but
Guacamole will be unable to use WebSocket. It will instead fallback to using
the HTTP tunnel, resulting in reduced performance.</p>
<section id="proxying-with-apache">
<span id="id3"></span><h3>Proxying Guacamole<a class="headerlink" href="#proxying-with-apache" title="Link to this heading"></a></h3>
<p>Configuring Apache to proxy HTTP requests requires using the <code class="docutils literal notranslate"><span class="pre">ProxyPass</span></code> and
<code class="docutils literal notranslate"><span class="pre">ProxyPassReverse</span></code> directives, which are provided by the mod_proxy module.
These directives describe how HTTP traffic should be routed to the web server
behind the proxy:</p>
<div class="highlight-apache notranslate"><div class="highlight"><pre><span></span><span class="nt"><Location</span><span class="w"> </span><span class="s">/guacamole/</span><span class="nt">></span>
<span class="w"> </span><span class="nb">Order</span><span class="w"> </span>allow,deny
<span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
<span class="w"> </span><span class="nb">ProxyPass</span><span class="w"> </span>http://HOSTNAME:8080/guacamole/<span class="w"> </span>flushpackets=on
<span class="w"> </span><span class="nb">ProxyPassReverse</span><span class="w"> </span>http://HOSTNAME:8080/guacamole/
<span class="nt"></Location></span>
</pre></div>
</div>
<p>Here, <code class="docutils literal notranslate"><span class="pre">HOSTNAME</span></code> is the hostname or IP address of the machine hosting your
servlet container, and <code class="docutils literal notranslate"><span class="pre">8080</span></code> is the port that servlet container is configured
to use. You will need to replace these values with the correct values for your
server.</p>
<div class="admonition important">
<p class="admonition-title">Important</p>
<p><em>Do not forget the <code class="docutils literal notranslate"><span class="pre">flushpackets=on</span></code> option.</em></p>
<p>Most proxies, including mod_proxy, will buffer all data sent over the
connection, waiting until the connection is closed before sending that data to
the client. As Guacamole’s HTTP tunnel relies on streaming data to the client
over an open connection, excessive buffering will effectively block Guacamole
connections, rendering Guacamole useless.</p>
<p><em>If the option <code class="docutils literal notranslate"><span class="pre">flushpackets=on</span></code> is not specified, Guacamole may not work</em>.</p>
</div>
</section>
<section id="proxying-the-websocket-tunnel">
<span id="websocket-and-apache"></span><h3>Proxying the WebSocket tunnel<a class="headerlink" href="#proxying-the-websocket-tunnel" title="Link to this heading"></a></h3>
<p>Apache will not automatically proxy WebSocket connections, but you can proxy
them separately with Apache 2.4.5 and later using mod_proxy_wstunnel. After
enabling mod_proxy_wstunnel a secondary <code class="docutils literal notranslate"><span class="pre">Location</span></code> section can be added which
explicitly proxies the Guacamole WebSocket tunnel, located at
<code class="docutils literal notranslate"><span class="pre">/guacamole/websocket-tunnel</span></code>:</p>
<div class="highlight-apache notranslate"><div class="highlight"><pre><span></span><span class="nt"><Location</span><span class="w"> </span><span class="s">/guacamole/</span><span class="nt">></span>
<span class="w"> </span><span class="nb">Order</span><span class="w"> </span>allow,deny
<span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
<span class="w"> </span><span class="nb">ProxyPass</span><span class="w"> </span>http://HOSTNAME:8080/guacamole/<span class="w"> </span>flushpackets=on
<span class="w"> </span><span class="nb">ProxyPassReverse</span><span class="w"> </span>http://HOSTNAME:8080/guacamole/
<span class="nt"></Location></span>
<span class="hll"><span class="nt"><Location</span><span class="w"> </span><span class="s">/guacamole/websocket-tunnel</span><span class="nt">></span>
</span><span class="hll"><span class="w"> </span><span class="nb">Order</span><span class="w"> </span>allow,deny
</span><span class="hll"><span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
</span><span class="hll"><span class="w"> </span><span class="nb">ProxyPass</span><span class="w"> </span>ws://HOSTNAME:8080/guacamole/websocket-tunnel
</span><span class="hll"><span class="w"> </span><span class="nb">ProxyPassReverse</span><span class="w"> </span>ws://HOSTNAME:8080/guacamole/websocket-tunnel
</span><span class="hll"><span class="nt"></Location></span>
</span></pre></div>
</div>
<p>Lacking this, Guacamole will still work by using normal HTTP, but network
latency will be more pronounced with respect to user input, and performance may
be lower.</p>
<div class="admonition important">
<p class="admonition-title">Important</p>
<p><strong>The <code class="docutils literal notranslate"><span class="pre">Location</span></code> section for <code class="docutils literal notranslate"><span class="pre">/guacamole/websocket-tunnel</span></code> must be placed after
the <code class="docutils literal notranslate"><span class="pre">Location</span></code> section for the rest of Guacamole.</strong></p>
<p>Apache evaluates all Location sections, giving priority to the last section
that matches. If the <code class="docutils literal notranslate"><span class="pre">/guacamole/websocket-tunnel</span></code> section comes first, the
section for <code class="docutils literal notranslate"><span class="pre">/guacamole/</span></code> will match instead, and WebSocket will not be proxied
correctly.</p>
</div>
</section>
<section id="changing-path-with-apache">
<span id="id4"></span><h3>Changing the path<a class="headerlink" href="#changing-path-with-apache" title="Link to this heading"></a></h3>
<p>If you wish to serve Guacamole through Apache under a path other than
<code class="docutils literal notranslate"><span class="pre">/guacamole/</span></code>, the easiest method is to simply rename the <code class="docutils literal notranslate"><span class="pre">.war</span></code> file. For
example, if intending to server Guacamole at <code class="docutils literal notranslate"><span class="pre">/new-path/</span></code>, you would:</p>
<ol class="arabic">
<li><p>Rename <code class="docutils literal notranslate"><span class="pre">guacamole.war</span></code> to <code class="docutils literal notranslate"><span class="pre">new-path.war</span></code>.</p></li>
<li><p>Update the paths within the Apache configuration to reflect the new path:</p>
<div class="highlight-apache notranslate"><div class="highlight"><pre><span></span><span class="hll"><span class="nt"><Location</span><span class="w"> </span><span class="s">/new-path/</span><span class="nt">></span>
</span><span class="w"> </span><span class="nb">Order</span><span class="w"> </span>allow,deny
<span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
<span class="hll"><span class="w"> </span><span class="nb">ProxyPass</span><span class="w"> </span>http://HOSTNAME:8080/new-path/<span class="w"> </span>flushpackets=on
</span><span class="hll"><span class="w"> </span><span class="nb">ProxyPassReverse</span><span class="w"> </span>http://HOSTNAME:8080/new-path/
</span><span class="nt"></Location></span>
<span class="hll"><span class="nt"><Location</span><span class="w"> </span><span class="s">/new-path/websocket-tunnel</span><span class="nt">></span>
</span><span class="w"> </span><span class="nb">Order</span><span class="w"> </span>allow,deny
<span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
<span class="hll"><span class="w"> </span><span class="nb">ProxyPass</span><span class="w"> </span>ws://HOSTNAME:8080/new-path/websocket-tunnel
</span><span class="hll"><span class="w"> </span><span class="nb">ProxyPassReverse</span><span class="w"> </span>ws://HOSTNAME:8080/new-path/websocket-tunnel
</span><span class="nt"></Location></span>
</pre></div>
</div>
</li>
</ol>
<p>Alternatively, the configuration can be altered slightly to handle requests at
a different location externally while still serving internal requests at
<code class="docutils literal notranslate"><span class="pre">/guacamole/</span></code>:</p>
<div class="highlight-apache notranslate"><div class="highlight"><pre><span></span><span class="hll"><span class="nt"><Location</span><span class="w"> </span><span class="s">/new-path/</span><span class="nt">></span>
</span><span class="w"> </span><span class="nb">Order</span><span class="w"> </span>allow,deny
<span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
<span class="w"> </span><span class="nb">ProxyPass</span><span class="w"> </span>http://HOSTNAME:8080/guacamole/<span class="w"> </span>flushpackets=on
<span class="w"> </span><span class="nb">ProxyPassReverse</span><span class="w"> </span>http://HOSTNAME:8080/guacamole/
<span class="nt"></Location></span>
<span class="hll"><span class="nt"><Location</span><span class="w"> </span><span class="s">/new-path/websocket-tunnel</span><span class="nt">></span>
</span><span class="w"> </span><span class="nb">Order</span><span class="w"> </span>allow,deny
<span class="w"> </span><span class="nb">Allow</span><span class="w"> </span>from<span class="w"> </span><span class="k">all</span>
<span class="w"> </span><span class="nb">ProxyPass</span><span class="w"> </span>ws://HOSTNAME:8080/guacamole/websocket-tunnel
<span class="w"> </span><span class="nb">ProxyPassReverse</span><span class="w"> </span>ws://HOSTNAME:8080/guacamole/websocket-tunnel
<span class="nt"></Location></span>
</pre></div>
</div>
</section>
<section id="disabling-logging-of-tunnel-requests">
<span id="disable-tunnel-logging"></span><h3>Disabling logging of tunnel requests<a class="headerlink" href="#disabling-logging-of-tunnel-requests" title="Link to this heading"></a></h3>
<p>If WebSocket is unavailable, Guacamole will fallback to using an HTTP-based
tunnel. The Guacamole HTTP tunnel works by transferring a continuous stream of
data over multiple short-lived streams, each associated with a separate HTTP
request. By default, Apache will log each of these requests, resulting in a
rather bloated access log.</p>
<p>There is little value in a log file filled with identical tunnel requests, so
it is recommended to explicitly disable logging of those requests. Apache does
provide a means of matching URL patterns and setting environment variables
based on whether the URL matches. Logging can then be restricted to requests
which lack this environment variable:</p>
<div class="highlight-apache notranslate"><div class="highlight"><pre><span></span><span class="nb">SetEnvIf</span><span class="w"> </span>Request_URI<span class="w"> </span><span class="s2">"^/guacamole/tunnel"</span><span class="w"> </span>dontlog
<span class="nb">CustomLog</span><span class="w"> </span><span class="sx">/var/log/apache2/guac.log</span><span class="w"> </span>common<span class="w"> </span>env=!dontlog
</pre></div>
</div>
<p>Note that if you are serving Guacamole under a path different from
<code class="docutils literal notranslate"><span class="pre">/guacamole/</span></code>, you will need to change the value of <code class="docutils literal notranslate"><span class="pre">Request_URI</span></code> above
accordingly.</p>
</section>
</section>
</section>
</div>
</div>
<footer><div class="rst-footer-buttons" role="navigation" aria-label="Footer">
<a href="guacamole-docker.html" class="btn btn-neutral float-left" title="Installing Guacamole with Docker" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left" aria-hidden="true"></span> Previous</a>
<a href="configuring-guacamole.html" class="btn btn-neutral float-right" title="Configuring Guacamole" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right" aria-hidden="true"></span></a>
</div>
<hr/>
<div role="contentinfo">
<p>Copyright © 2024 <a href="http://www.apache.org/">The Apache Software Foundation</a>,
Licensed under the <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>.
Apache Guacamole, Guacamole, Apache, the Apache feather logo, and the Apache Guacamole project logo are
trademarks of The Apache Software Foundation.</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>