diff --git a/docs/deploy/hardening-guide.md b/docs/deploy/hardening-guide.md new file mode 100644 index 000000000..251446374 --- /dev/null +++ b/docs/deploy/hardening-guide.md @@ -0,0 +1,112 @@ +# Hardening Guide + +## Overview +There are several ways to do hardening and securing of nginx. In this documentation two guides are used, the guides are +overlapping in some points: + +- [nginx CIS Benchmark](https://www.cisecurity.org/benchmark/nginx/) +- [cipherlist.eu](https://cipherlist.eu/) (one of many forks of the now dead project cipherli.st) + +This guide describes, what of the different configurations described in those guides is already implemented as default +in the nginx implementation of kubernetes ingress, what needs to be configured, what is obsolete due to the fact that +the nginx is running as container (the CIS benchmark relates to a non-containerized installation) and what is difficult +or not possible. + +Be aware that this is only a guide and you are responsible for your own implementation. Some of the configurations may +lead to have specific clients unable to reach your site or similar consequences. + +This guide refers to chapters in the CIS Benchmark. For full explanation you should refer to the benchmark document itself + +## Configuration Guide +| Chapter in CIS benchmark | Status | Default | Action to do if not default| +|:-------------------------|:-------|:--------|:---------------------------| +| __1 Initial Setup__ ||| | +| ||| | +| __1.1 Installation__||| | +| 1.1.1 Ensure NGINX is installed (Scored)| OK | done through helm charts / following documentation to deploy nginx ingress | | +| 1.1.2 Ensure NGINX is installed from source (Not Scored)| OK | done through helm charts / following documentation to deploy nginx ingress | | +| ||| | +| __1.2 Configure Software Updates__||| | +| 1.2.1 Ensure package manager repositories are properly configured (Not Scored) | OK | done via helm, nginx version could be overwritten, however compability is not ensured then| | +| 1.2.2 Ensure the latest software package is installed (Not Scored)| ACTION NEEDED | done via helm, nginx version could be overwritten, however compability is not ensured then| Plan for periodic updates | +| ||| | +| __2 Basic Configuration__ ||| | +| ||| | +| __2.1 Minimize NGINX Modules__||| | +| 2.1.1 Ensure only required modules are installed (Not Scored) | ??? | ??? | | +| 2.1.2 Ensure HTTP WebDAV module is not installed (Scored) | RISK TO BE ACCEPTED | It is installed, see compile options [here](https://github.com/kubernetes/ingress-nginx/blob/master/images/nginx/rootfs/build.sh#L445). Disabling that would require building own image for nginx ingress controller. The effort is too high in comparison to the achieved effect | | +| 2.1.3 Ensure modules with gzip functionality are disabled (Scored)| RISK TO BE ACCEPTED | See previous answer | | +| 2.1.4 Ensure the autoindex module is disabled (Scored)| OK | No autoindex configs so far in ingress defaults| | +| ||| | +| __2.2 Account Security__||| | +| 2.2.1 Ensure that NGINX is run using a non-privileged, dedicated service account (Not Scored) | OK | Pod configured as user www-data: [See this line in helm chart values](https://github.com/kubernetes/ingress-nginx/blob/0cbe783f43a9313c9c26136e888324b1ee91a72f/charts/ingress-nginx/values.yaml#L10). Compiled with user www-data: [See this line in build script](https://github.com/kubernetes/ingress-nginx/blob/5d67794f4fbf38ec6575476de46201b068eabf87/images/nginx/rootfs/build.sh#L529) | | +| 2.2.2 Ensure the NGINX service account is locked (Scored) | OK | Docker design ensures this | | +| 2.2.3 Ensure the NGINX service account has an invalid shell (Scored)| OK | Shell is nologin: [see this line in build script](https://github.com/kubernetes/ingress-nginx/blob/5d67794f4fbf38ec6575476de46201b068eabf87/images/nginx/rootfs/build.sh#L613)| | +| ||| | +| __2.3 Permissions and Ownership__ ||| | +| 2.3.1 Ensure NGINX directories and files are owned by root (Scored) | OK | Obsolete through docker-design and ingress controller needs to update the configs dynamically| | +| 2.3.2 Ensure access to NGINX directories and files is restricted (Scored) | OK | See previous answer| | +| 2.3.3 Ensure the NGINX process ID (PID) file is secured (Scored)| OK | No PID-File due to docker design | | +| 2.3.4 Ensure the core dump directory is secured (Not Scored)| OK | No working_directory configured by default | | +| ||| | +| __2.4 Network Configuration__ ||| | +| 2.4.1 Ensure NGINX only listens for network connections on authorized ports (Not Scored)| OK | Ensured by automatic nginx.conf configuration| | +| 2.4.2 Ensure requests for unknown host names are rejected (Not Scored)| OK | They are not rejected but send to the "default backend" delivering approriate errors (mostly 404)| | +| 2.4.3 Ensure keepalive_timeout is 10 seconds or less, but not 0 (Scored)| ACTION NEEDED| Default is 75s | configure keep-alive to 10 seconds [according to this documentation](https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/configmap.md#keep-alive) | +| 2.4.4 Ensure send_timeout is set to 10 seconds or less, but not 0 (Scored)| ???| ???| | +| ||| | +| __2.5 Information Disclosure__||| | +| 2.5.1 Ensure server_tokens directive is set to `off` (Scored) | OK | server_tokens is configured to off by defaukt| | +| 2.5.2 Ensure default error and index.html pages do not reference NGINX (Scored) | ACTION NEEDED| 404 shows no version at all, 503 and 403 show "nginx", which is hardcoded [see this line in nginx source code](https://github.com/nginx/nginx/blob/master/src/http/ngx_http_special_response.c#L36) | configure custom error pages at least for 403, 404 and 503 and 500| +| 2.5.3 Ensure hidden file serving is disabled (Not Scored) | ACTION NEEDED | config not set | configure a config.server-snippet Snippet, but beware of .well-known challenges or similar. Refer to the benchmark here please | +| 2.5.4 Ensure the NGINX reverse proxy does not enable information disclosure (Scored)| ACTION NEEDED| hide not configured| configure hide-headers with array of "X-Powered-By" and "Server": [according to this documentation](https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/configmap.md#hide-headers) | +| ||| | +| __3 Logging__ ||| | +| ||| | +| 3.1 Ensure detailed logging is enabled (Not Scored) | OK | Ningx ingress has a very detailled log format by default | | +| 3.2 Ensure access logging is enabled (Scored) | OK | Access log is enabled by default | | +| 3.3 Ensure error logging is enabled and set to the info logging level (Scored)| OK | Error log is configured by default. The log level does not matter, because it is all sent to STDOUT anyway | | +| 3.4 Ensure log files are rotated (Scored) | OBSOLETE | Log file handling is not part of the nginx ingress and should be handled separatly | | +| 3.5 Ensure error logs are sent to a remote syslog server (Not Scored) | OBSOLETE | See previous answer| | +| 3.6 Ensure access logs are sent to a remote syslog server (Not Scored)| OBSOLETE | See previous answer| | +| 3.7 Ensure proxies pass source IP information (Scored)| OK | Headers are set by default | | +| ||| | +| __4 Encryption__ ||| | +| ||| | +| __4.1 TLS / SSL Configuration__ ||| | +| 4.1.1 Ensure HTTP is redirected to HTTPS (Scored) | OK | Redirect to TLS is default | | +| 4.1.2 Ensure a trusted certificate and trust chain is installed (Not Scored)| ACTION NEEDED| For installing certs there are enough manuals in the web. A good way is to use lets encrypt through cert-manager | Install proper certificates or use lets encrypt with cert-manager | +| 4.1.3 Ensure private key permissions are restricted (Scored)| ACTION NEEDED| See previous answer| | +| 4.1.4 Ensure only modern TLS protocols are used (Scored)| OK/ACTION NEEDED | Default is TLS 1.2 + 1.3, while this is okay for CIS Benchmark, cipherlist.eu only recommends 1.3. This may cut off old OS's | Set controller.config.ssl-protocols to "TLSv1.3"| +| 4.1.5 Disable weak ciphers (Scored) | ACTION NEEDED| Default ciphers are already good, but cipherlist.eu recommends even stronger ciphers | Set controller.config.ssl-ciphers to "EECDH+AESGCM:EDH+AESGCM"| +| 4.1.6 Ensure custom Diffie-Hellman parameters are used (Scored) | ACTION NEEDED| No custom DH parameters are generated| Generate dh parameters for each ingress deployment you use - [see here for a how to](https://kubernetes.github.io/ingress-nginx/examples/customization/ssl-dh-param/) | +| 4.1.7 Ensure Online Certificate Status Protocol (OCSP) stapling is enabled (Scored) | ??? | [The](https://github.com/kubernetes/ingress-nginx/issues/4651), [situation](https://github.com/kubernetes/ingress-nginx/pull/5133), [is](https://github.com/kubernetes/ingress-nginx/issues/4864), [really](https://github.com/kubernetes/ingress-nginx/issues/4758), [very](https://github.com/kubernetes/ingress-nginx/pull/4868), [compicated](https://github.com/kubernetes/ingress-nginx/issues/416)| | +| 4.1.8 Ensure HTTP Strict Transport Security (HSTS) is enabled (Scored)| OK | HSTS is enabled by default | | +| 4.1.9 Ensure HTTP Public Key Pinning is enabled (Not Scored)| ACTION NEEDED / RISK TO BE ACCEPTED | HKPK not enabled by default | If lets encrypt is not used, set correct HPKP header. There are several ways to implement this - with the helm charts it works via controller.add-headers. If lets encrypt is used, this is complicated, a solution here is yet unknown | +| 4.1.10 Ensure upstream server traffic is authenticated with a client certificate (Scored) | DEPENDS ON BACKEND | Highly dependend on backends, not every backend allows configuring this, can also be mitigated via a service mesh| If backend allows it, [manual is here](https://kubernetes.github.io/ingress-nginx/examples/auth/client-certs/)| +| 4.1.11 Ensure the upstream traffic server certificate is trusted (Not Scored) | DEPENDS ON BACKEND | Highly dependend on backends, not every backend allows configuring this, can also be mitigated via a service mesh| If backend allows it, [see configuration here](https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md#backend-certificate-authentication) | +| 4.1.12 Ensure your domain is preloaded (Not Scored) | ACTION NEEDED| Preload is not active by default | Set controller.config.hsts-preload to true| +| 4.1.13 Ensure session resumption is disabled to enable perfect forward security (Scored)| ACTION NEEDED| Session tickets are enabled by default | Set controller.config.ssl-session-tickets to false| +| 4.1.14 Ensure HTTP/2.0 is used (Not Scored) | OK | http2 is set by default| | +| ||| | +| __5 Request Filtering and Restrictions__||| | +| ||| | +| __5.1 Access Control__||| | +| 5.1.1 Ensure allow and deny filters limit access to specific IP addresses (Not Scored)| OK/ACTION NEEDED | Depends on use case, geo ip module is compiled into nginx ingress controller, there are several ways to use it | If needed set IP restrictions via annotations or work with config snippets (be careful with lets-encrypt-http-challenge!) | +| 5.1.2 Ensure only whitelisted HTTP methods are allowed (Not Scored) | OK/ACTION NEEDED | Depends on use case| If required it can be set via config snippet| +| ||| | +| __5.2 Request Limits__||| | +| 5.2.1 Ensure timeout values for reading the client header and body are set correctly (Scored) | ACTION NEEDED| Default timeout is 60s | Set via [this configuration parameter](https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/configmap.md#client-header-timeout) and respective body aequivalent| +| 5.2.2 Ensure the maximum request body size is set correctly (Scored)| ACTION NEEDED| Default is 1m| set via [this configuration parameter](https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/configmap.md#proxy-body-size)| +| 5.2.3 Ensure the maximum buffer size for URIs is defined (Scored) | ACTION NEEDED| Default is 4 8k| Set via [this configuration parameter](https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/configmap.md#large-client-header-buffers)| +| 5.2.4 Ensure the number of connections per IP address is limited (Not Scored) | ???| ???| | +| 5.2.5 Ensure rate limits by IP address are set (Not Scored) | ???| ???| | +| ||| | +| __5.3 Browser Security__||| | +| 5.3.1 Ensure X-Frame-Options header is configured and enabled (Scored)| ACTION NEEDED| Header not set by default| Several ways to implement this - with the helm charts it works via controller.add-headers | +| 5.3.2 Ensure X-Content-Type-Options header is configured and enabled (Scored) | ACTION NEEDED| See previous answer| See previous answer | +| 5.3.3 Ensure the X-XSS-Protection Header is enabled and configured properly (Scored)| ACTION NEEDED| See previous answer| See previous answer | +| 5.3.4 Ensure that Content Security Policy (CSP) is enabled and configured properly (Not Scored) | ACTION NEEDED| See previous answer| See previous answer | +| 5.3.5 Ensure the Referrer Policy is enabled and configured properly (Not Scored)| ACTION NEEDED | Depends on application. It should be handled in the applications webserver itself, not in the load balancing ingress | check backend webserver | +| ||| | +| __6 Mandatory Access Control__| n/a| too high level, depends on backends | | \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index dc35a6838..23b9e6c3a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -46,6 +46,7 @@ nav: - Bare-metal considerations: "deploy/baremetal.md" - Role Based Access Control (RBAC): "deploy/rbac.md" - Upgrade: "deploy/upgrade.md" + - Hardening guide: "deploy/hardening-guide.md" - User guide: - NGINX Configuration: - Introduction: "user-guide/nginx-configuration/index.md"