代理Guacamole
像大多数web应用程序一样,Guacamole可以放在反向代理后面。对于Guacamole的生产部署,强烈建议这样做。它提供了灵活性,且如果代理针对SSL进行了正确配置,还可以提供加密。
代理隔离了本机应用程序中的特权操作,这些操作可以在不再需要时安全地丢弃这些特权,仅将Java用于非特权任务。在Linux和UNIX系统上,进程必须以root权限运行,才能侦听1024以下的任何端口,包括标准HTTP和HTTPS端口(分别为80和443)。如果servlet容器改为侦听更高的端口,例如默认端口8080,那么它可以作为降低权限的用户运行,从而允许反向代理承担root特权的负担。作为本机应用程序,反向代理可以在端口打开后进行系统调用,以安全地丢弃root权限;像Tomcat这样的Java应用程序则无法做到这一点。
准备你的servlet容器
你的servlet容器很可能已经默认配置为侦听端口8080上的HTTP连接。如果是这种情况,且你已经可以从web浏览器通过端口8080访问Guacamole,则无需对其配置进行任何进一步更改。
如果你改变了这一点,或许是为了在AJP上代理Guacamole,请把它改回来。不支持在AJP上使用Guacamole,众所周知它会产生问题,即:
- WebSocket不能在AJP上运转,这将迫使Guacamole退回到HTTP,可能会导致性能降低。
- 在AJP上,Apache 2.4.3及较老的版本不支持HTTP PATCH方法,这将导致Guacamole管理界面无法正常运行。
conf/server.xml
中的Connector
条目应如下所示:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
URIEncoding="UTF-8"
redirectPort="8443" />
确保如上指定URIEncoding="UTF-8"
属性,以确保web应用程序正确地接收连接名、用户名等。如果要创建的连接的名称或参数值中包含西里尔文、中文、日文或其他非拉丁字符,则该属性是必需的。
设置远程IP值
默认情况下,当Tomcat位于反向代理后面时,它看到的客户端的远程IP地址是代理的IP地址,而不是原始客户端的远程IP地址。为了让托管在Tomcat中的应用程序(如Guacamole)能够看到客户端的真实IP地址,必须同时配置反向代理和Tomcat。
因为Guacamole中的远程IP地址用于审核用户登录和连接,并且可能用于身份验证,所以直接控制代理服务器或者明确信任它非常重要。通过X-Forwarded-For
报头传递远程IP地址,与大多数HTTP报头一样,攻击者可以尝试欺骗该报头,以操纵web服务器的行为,获得对系统的未经授权访问,或试图伪装攻击源的主机或IP地址。
最后一个警告:如果在你的反向代理和访问Guacamole的客户端之间存在其他上游代理服务器,那么可能无法按预期的想法工作。其他代理或防火墙可能会屏蔽客户端的IP地址,如果这些代理或防火墙的配置不在你的控制范围内,你可能最终会看到多个客户端似乎来自同一IP地址或主机。确保在配置系统和查看提供的数据时考虑到这一点。
将Tomcat配置为通过X-Forwarded-For
报头中的反向代理提供的远程IP地址,需要Tomcat称之为Value
的配置项。在本例中,它是RemoteIpValve
,在conf/server.xml
文件的<Host>
部分进行配置:
<Valve className="org.apache.catalina.valves.RemoteIpValve"
internalProxies="127.0.0.1"
remoteIpHeader="x-forwarded-for"
remoteIpProxiesHeader="x-forwarded-by"
protocolHeader="x-forwarded-proto" />
internalProxies
的值应该设置为任一且所有反向代理都可以直接访问该Tomcat实例的代理的IP地址或地址。通常与Tomcat同在一个系统上运行,但在其他情况下(如,运行在Docker容器中时),它可能在不同的系统或容器上运行,且可能需要设置为反向代理系统的真实IP地址。仅允许internalProxies
或trustedProxies
参数中列出的代理服务器控制远程IP地址信息。
该配置中的其他参数允许你控制哪些来自代理服务器的用于各种远程主机信息的报头,如下所示:
-
remoteIpHeader
用于查询发起请求的客户端的客户端IP地址的报头。标准值为
X-Forwarded-For
,也可以配置为任何你喜欢的报头。该报头中的IP地址将可用于request.getRemoteAddr()
方法中的Java应用程序。 -
remoteIpProxiesHeader
用以查询转发请求的代理服务器的IP地址的报头。默认值为
X-Forwarded-By
,但可以配置为适合你环境的任意报头。仅当trustedProxies
参数中列出了使用的代理时,Value
才允许该值。否则,此报头将不可用。 -
protocolHeader
用以查询客户端用于连接服务的协议的报头。默认值为
X-Forwarded-Proto
,但可以配置为符合你环境的任意报头。
除了配置Tomcat来正确处理这些报头之外,还可能需要适当配置你的反向代理来发送报头。你可以在《Nginx》章节找到这方面的说明,Apache web服务默认会传递它。
Nginx
Nginx可以作为反向代理,从1.3版开始就支持WebSocket的开箱即用。Apache和Nginx都需要一些额外的配置以便WebSocket代理能正常工作。
代理Guacamole
Nginx确实支持WebSocket代理,但由于WebSocket协议的性质,需要显式设置Connection
和Upgrade
这两个HTTP报头。从Nginx文档中:
NGINX通过允许在客户端和后端服务器之间建立隧道来支持WebSocket。为了让NGINX将Upgrade请求从客户端发送到后端服务器,必须显式设置Upgrade和Connection报头......
代理配置应在一个专用的location
块下,声明托管Guacamole的后端,并明确指定前面提到的Connection
和Upgrade
报头:
location /guacamole/ {
proxy_pass http://HOSTNAME:8080;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
access_log off;
}
此处,HOSTNAME
是托管你的servlet容器的机器的主机名或IP地址,8080是servlet容器配置使用的端口。你需要将这些值正确的配置为与你的服务匹配的内容。
Tomcat与RemoteIpValve
相关的配置,记录在《设置远程IP值》章节, 如果希望将X-Forwarded-For
报头传递给web应用程序服务器,并可供其中运行的应用程序使用, proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
这一行非常重要。
❗重点:
别忘了指定“proxy_buffering off”。
包括Nginx在内的大多数代理都会缓存通过连接发送的所有数据,直到连接关闭后再将数据发送到客户端。由于Guacamole的HTTP隧道依赖于通过开放连接将数据流传输到客户端,过度的缓存会有效地阻止Guacamole连接,使Guacamole变得无用。
如果没有指定“proxy_buffering off”选项,Guacamole可能无法运转。
变更路径
如果你希望在Nginx下通过不同于/guacamole/
的路径来提供Guacamole服务,最简单的方法是简单地重命名.war
文件。例如,如果打算在/new-path/
提供Guacamole服务,你需要:
-
将
guacamole.war
重命名为new-path.war
。 -
更新Nginx配置中的路径为新的路径:
location /new-path/ { proxy_pass http://HOSTNAME:8080; proxy_buffering off; proxy_http_version 1.1; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $http_connection; access_log off; }
或者,可以稍微更改下配置,变为在外部处理不同位置的请求,同时仍在/guacamole/
上处理内部请求:
location /new-path/ {
proxy_pass http://HOSTNAME:8080/guacamole/;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
access_log off;
}
调整文件上传限制
通过Nginx代理Guacamole时,可能会遇到Nginx对文件上传设置的默认限制问题(1MB)。收到的错误可能是非直观的(例如,权限被拒绝),但可能表明存在这些限制。可以在location
块中设置client_max_body_size
参数,以配置最大文件上传大小:
location /guacamole/ {
proxy_pass http://HOSTNAME:8080;
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
client_max_body_size 1g;
access_log off;
}
Apache和mod_proxy
Apache支持通过mod_proxy
进行反向代理配置。2.4.5或更高版本的Apache同样支持通过名为mod_proxy_wstunnel
的子模块代理WebSocket。这两个模块都需要启用,Guacamole的代理才能正常运行。
缺少mod_proxy_wstuunel
,仍然可以代理Guacamole,但Guacamole将不能使用WebSocket。它将转而使用HTTP隧道,从而导致性能下降。
代理Guacamole
配置Apache代理HTTP请求需要使用mod_proxy
模块提供的ProxyPass
和ProxyPassReverse
指令。这些指令描述了HTTP通信应该如何路由到代理后面的web服务器:
<Location /guacamole/>
Order allow,deny
Allow from all
ProxyPass http://HOSTNAME:8080/guacamole/ flushpackets=on
ProxyPassReverse http://HOSTNAME:8080/guacamole/
</Location>
此处,HOSTNAME
是托管你的servlet容器的机器的主机名或IP地址,8080是servlet容器配置使用的端口。你需要将这些值正确的配置为与你的服务匹配的内容。
❗重点:
别忘了“flushpackets=on”选项。
包括mod_proxy在内的多数代理都会缓存通过连接发送的所有数据,直到连接关闭后再将数据发送到客户端。由于Guacamole的HTTP隧道依赖于通过开放连接将数据流传输到客户端,过度的缓存会有效地阻止Guacamole连接,使Guacamole变得无用。
如果没有指定“flushpackets=on”选项,Guacamole可能无法运转。
代理WebSocket隧道
Apache不会自动代理WebSocket连接,但你可以使用Apache 2.4.5和更高版本利用mod_proxy_wstunnel
单独代理它们。启用mod_proxy_wstunnel
后,可以添加一个次要的location
部件,该部件明确地代理位于/Guacamole/WebSocket-tunnel
的Guacamole WebSocket隧道:
<Location /guacamole/>
Order allow,deny
Allow from all
ProxyPass http://HOSTNAME:8080/guacamole/ flushpackets=on
ProxyPassReverse http://HOSTNAME:8080/guacamole/
</Location>
<Location /guacamole/websocket-tunnel>
Order allow,deny
Allow from all
ProxyPass ws://HOSTNAME:8080/guacamole/websocket-tunnel
ProxyPassReverse ws://HOSTNAME:8080/guacamole/websocket-tunnel
</Location>
如果没有这一点,Guacamole仍然可以通过使用正常的HTTP来工作,但网络延迟将在用户输入方面更加明显,性能可能更低。
❗重点:
“/guacamole/websocket”隧道的Location部件必须放置在其余Guacamole的Location部件之后。
Apache评估所有Location部件,将优先考虑匹配的最后一个部分。如果首先是"/guacamole/websocket-tuunel"部件,"/guacamole/"部件反而能匹配,WebSocket却无法正确代理。
变更路径
如果你希望在Apache下通过不同于/guacamole/
的路径来提供Guacamole服务,最简单的方法是简单地重命名.war
文件。例如,如果打算在/new-path/
提供Guacamole服务,你需要:
-
将
guacamole.war
重命名为new-path.war
。 -
更新Apache配置中的路径为新的路径:
<Location /new-path/> Order allow,deny Allow from all ProxyPass http://HOSTNAME:8080/new-path/ flushpackets=on ProxyPassReverse http://HOSTNAME:8080/new-path/ </Location> <Location /new-path/websocket-tunnel> Order allow,deny Allow from all ProxyPass ws://HOSTNAME:8080/new-path/websocket-tunnel ProxyPassReverse ws://HOSTNAME:8080/new-path/websocket-tunnel </Location>
或者,可以稍微更改下配置,变为在外部处理不同位置的请求,同时仍在/guacamole/
上处理内部请求:
<Location /new-path/>
Order allow,deny
Allow from all
ProxyPass http://HOSTNAME:8080/guacamole/ flushpackets=on
ProxyPassReverse http://HOSTNAME:8080/guacamole/
</Location>
<Location /new-path/websocket-tunnel>
Order allow,deny
Allow from all
ProxyPass ws://HOSTNAME:8080/guacamole/websocket-tunnel
ProxyPassReverse ws://HOSTNAME:8080/guacamole/websocket-tunnel
</Location>
禁用隧道请求的日志记录
如果WebSocket不可用,Guacamole将转而使用基于HTTP的隧道。Guacamole HTTP隧道的工作原理是通过多个短期流传输连续的数据流,每个流都与一个单独的HTTP请求相关联。默认情况下,Apache将记录这些请求中的每一个,从而导致访问日志相当臃肿。
充满相同隧道请求的日志文件几乎没有价值,因此建议显式禁用这些请求的日志记录。Apache确实提供了一种匹配URL模式的方法,并根据URL是否匹配来设置环境变量。然后,可以将日志记录限制为缺少此环境变量的请求:
SetEnvIf Request_URI "^/guacamole/tunnel" dontlog
CustomLog /var/log/apache2/guac.log common env=!dontlog
注意,如果你在不同于/guacamole/
的路径下提供Guacamole,则需要相应地更改上述Request_URI
的值。