--- myst: html_meta: "description lang=en": "Kasm Workspaces - Reverse Proxy Security" "keywords": "Kasm, Server, Configuration, STIGs, Security, CIS, DISA, Workspaces, hardening" "property=og:locale": "en_US" --- ```{title} Reverse Proxy Security ``` # Reverse Proxy Security Kasm WebApp roles servers come with an NGINX container that sits in front of service containers and other components within the system. The default configuration of our NGINX service is fairly secure, however, there are certain configurations that we cannot harden out of the box because the hardening may break certain use cases and/or degrade compatibility. Organizations may wish to either harden Kasm's NGINX configuration even further or place Kasm WebApp servers behind an Enterprise grade reverse proxy or load balancer. The following recommendations assume an NGINX reverse proxy, see your vendor's documentation for details on how to configure these features on your specific device. ## X-Frame-Options Header Including the [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) header will allow you to block other sites embedding your deployment of Kasm in an iframe within another site. This is not configured by default in Kasm, because many clients wish to embed Kasm in their own website. The following example will configure NGINX to add the X-Frame-Options header to same origin, which will require the parent page to be on the same domain. See the link above for more details on other configurations, such specifying explicit domain names to be allowed for the parent site. Add this line to each NGINX configuration file that currently defines `add_header` directives in this directory and subdirectories within /opt/kasm/current/conf/nginx. ``` add_header X-Frame-Options SAMEORIGIN always; ``` ## Content Security Policy Kasm has a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) that uses a default of self, meaning the site and any sub domains are allowed for all content. More specific settings are defined for images and media, which allows loading these content types from any source. Organizations may want to specify a more restrictive policy that only allows the page to load all content from `self`. The following policy will require all content, to include images, to be served from the same domain or sub-domain as your Kasm deployment. Add this line to each NGINX configuration file that currently defines `add_header` directives in this directory and subdirectories within /opt/kasm/current/conf/nginx. ``` add_header 'Content-Security-Policy' "default-src 'unsafe-inline' 'unsafe-eval' 'self';"; ``` ## TLS Settings Kasm ships with a default cipher suite and SSL settings that meet a high degree of security compliance out of the box, however, organizations may wish to define more restrictive SSL cipher suites. The cipher suites are defined on each WebApp server in the file `/opt/kasm/current/conf/nginx/orchestration.conf`. Kasm's default SSL Configuration: ``` ssl_certificate /etc/ssl/certs/kasm_nginx.crt; ssl_certificate_key /etc/ssl/private/kasm_nginx.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"; ssl_stapling on; ssl_stapling_verify on; ``` Here is an example of a more secure configuration. **Warning**: More restrictive cipher suites may break compatibility with older clients. ``` ssl_protocols TLSv1.3 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ecdh_curve secp521r1:secp384r1; ssl_ciphers EECDH+AESGCM:EECDH+AES256; ssl_dhparam /etc/ssl/certs/dhparam.pem; ssl_session_cache shared:TLS:2m; ssl_buffer_size 4k; ssl_stapling on; ssl_stapling_verify on; ``` In order to configure `ssl_dhparam` you will need to generate the dhparam.pem file referenced in the above configuration. ``` sudo openssl dhparam -out /opt/kasm/current/certs/dhparam.pem 4096 sudo chown kasm:kasm /opt/kasm/current/certs/dhparam.pem ``` Next, edit the `/opt/kasm/current/docker/docker-compose.yaml` file to map in the dhparam.pem file into the nginx configuration. The following snippet shows only the relevant portions of the yaml configuration. Add the volume mapping shown on the last line of the below snippet to the proxy definition in the yaml file. ```yaml proxy: container_name: kasm_proxy image: "kasmweb/nginx:1.25.1" volumes: - /opt/kasm/current/certs/dhparam.pem:/etc/ssl/certs/dhparam.pem ``` Next remove the kasm_proxy container and start kasm services back up. ``` sudo docker rm -f kasm_proxy sudo /opt/kasm/bin/start ``` ## SSL Certificates Kasm uses self-signed certificates that are generated during the installation process. The cert and key are stored on each host at `/opt/kasm/current/certs/kasm_nginx.crt` and `/opt/kasm/current/certs/kasm_nginx.key` respectively. Clients may with to replace these certs with properly signed public SSL certs or certs generated by their organizations certificate authority. You can replace the cert and key mentioned above with files named the same as those above, in the PEM format. Ensure both files are owned by the `kasm` user. ``` cd /opt/kasm/current/certs mv kasm_nginx.crt kasm_nginx.crt.bak mv kasm_nginx.key kasm_nginx.key.bak cp /my/cert/location/mycert.pem ./kasm_nginx.crt cp /my/key/location/mykey.pem ./kasm_nginx.key chown kasm:kasm /opt/kasm/current/certs/kasm_nginx.crt chown kasm:kasm /opt/kasm/current/certs/kasm_nginx.key ``` Next restart the services. ``` sudo /opt/kasm/bin/stop sudo /opt/kasm/bin/start ``` ## Referrer Policy You may wish to define a [referrer policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) which will keep client browsers from passing the referrer header to external sites. Add this line to each NGINX configuration file that currently defines `add_header` directives in /opt/kasm/current/conf/nginx/services.d/. ``` add_header Referrer-Policy 'same-origin'; ``` ## Rate Limiting Many organizations will require rate limiting. NGINX supports rate limiting of requests, see the [NGINX documentation](https://www.nginx.com/blog/rate-limiting-nginx/) for full details. Kasm Workspaces does not ship with rate limiting enabled, mostly due to the fact that some context of the deployment is needed to configure it properly. First, you need to know if Kasm's WebApp servers are directly accessible via clients, or if there is a load balancer of some other device between the clients and the web app servers. If there is a device between the client and the Kasm WebApp servers, NGINX may not see the original source IP address of the client, this must be considered when considering which rate limiting approach to take. If clients access the Kasm WebApp servers directly, then you can create a simple rate limiting policy that imposes a request rate limit per client based on the source IP address of the client. The following configuration would be placed above the `server` directive within the file `/opt/kasm/current/conf/nginx/orchestration.conf`. ``` limit_req_zone $binary_remote_addr zone=mylimit:10m rate=100r/s; server { } ``` If there is a reverse proxy or load balancer in front of the Kasm WebApp servers, you may want to place the rate limiting on the front facing load balancer instead. NGINX can, however, use the X-Forwarded-For header for rate limiting, assuming the front facing reverse proxy/load-balancer injects this header on proxied requests. ``` limit_req_zone $http_x_forwarded_for zone=zone:10m rate=100r/s; ``` After implementing one of the two lines above, you must apply it to each proxied location. Within each `.conf` file located at `/opt/kasm/current/conf/nginx/services.d/`, place the following line under each location stanza. ``` location / { limit_req zone=mylimit burst=20 nodelay; } ``` ## CORS Headers Kasm implements proper CORS headers, however, we must use a dynamic origin value. This is considered a low severity security finding. The following is the offending line, which can be found in multiple files under `/opt/kasm/current/conf/nginx/services.d/` ``` add_header 'Access-Control-Allow-Origin' $http_origin always; ``` Replace the above line with the following, replace `example.com` with your domain name. ``` add_header 'Access-Control-Allow-Origin' 'https://example.com' always; ``` This may affect deployments that use sub domains for different [Zones](../guide/zones/deployment_zones.md). The following more complex example may be used if you must clear this vulnerability and also support multiple domain names with CORs. This uses a dynamic Access-Control-Allow-Origin header value, but whitelists it to a list of regular expressions. ``` map $http_origin $allow_origin { ~^https?://(.*\.)?example.com(:\d+)?$ $http_origin; ~^https?://(.*\.)?example2.com(:\d+)?$ $http_origin; default ""; } location / { add_header 'Access-Control-Allow-Origin' $allow_origin always; } ```