Select any two versions of this STIG to compare the individual requirements
Select any old version/release of this STIG to view the previous requirements
Determine path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. nginx -c <path to nginx config> -qT | grep worker_connections worker_connections 512; Verify "worker_connections" is set in the configuration to an organization-defined number. If the "worker_connections" is not set to an organization-defined number, this is a finding.
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Add or modify the number of "worker_connections" based on the needs of the application. Note: By default, NGINX limits the number of concurrent connections to 512 per worker process, and the number of worker processes is equal the number of CPUs present. This results in a maximum number of connections equal to "worker_connections" multiplied by the number of "worker_processes". In a proxy configuration, NGINX will use a connection on both the client side and the server side. This results in two connections for each client side connection. Restart NGINX: systemctl restart nginx
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Verify "ssl_protocols" are set to TLSv1.2 or higher: nginx -c <path to nginx config> -qT | grep ssl_protocols ssl_protocols TLSv1.2 TLSv1.3; If "ssl_protocols" does not exist or does not specify TLSv1.2 or greater, this is a finding
Specify the allowed TLS protocols by adding the following line to the server {} block: ssl_protocols TLSv1.2 TLSv1.3; Restart NGINX with saved configuration: nginx -s reload
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Identify the NGINX runtime user: grep -E '^\s*user\s+' /etc/nginx/nginx.conf Expected output (example): user nginx; Verify the user has no login shell: getent passwd nginx Expected output: nginx:x:998:998:Nginx user:/nonexistent:/sbin/nologin Ensure the shell is "/sbin/nologin", "/usr/sbin/nologin", or "/bin/false". If the NGINX runtime user has shell access, this is a finding.
Set a nonlogin shell for the nginx user: sudo usermod -s /sbin/nologin nginx
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Identify the NGINX runtime user: grep -E '^\s*user\s+' /etc/nginx/nginx.conf Expected output (example): user nginx; Ensure the user is not in a privileged group: id nginx Expected output: uid=980(nginx) gid=979(nginx) groups=979(nginx) The user should not be a member of sudo, wheel, admin, or similar elevated groups. If the NGINX runtime user is a member of an elevated group, this is a finding.
Remove the user from privileged groups: sudo gpasswd -d nginx sudo sudo gpasswd -d nginx wheel sudo gpasswd -d nginx admin
Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Check nginx.conf for a section that verifies that user consent has been given. Setting a cookie upon user acceptance after reading the banner and checking for the presence of that cookie is one way to accomplish this. Check for this block in nginx.conf under the http block: # Define whether consent has been given based on the cookie map $http_cookie $consent_given { "~*user_consent=1" 1; default 0; } Check nginx.conf for a section that handles user consent and setting the cookie under the server block: # Serve the consent banner page if consent is not given location /consent { default_type text/html; return 200 "<html><body> <h1>Consent Required</h1> <p>You are accessing a U.S. Government (USG) Information System (IS) that is provided for USG-authorized use only. By using this IS (which includes any device attached to this IS), you consent to the following conditions: -The USG routinely intercepts and monitors communications on this IS for purposes including, but not limited to, penetration testing, COMSEC monitoring, network operations and defense, personnel misconduct (PM), law enforcement (LE), and counterintelligence (CI) investigations. -At any time, the USG may inspect and seize data stored on this IS. -Communications using, or data stored on, this IS are not private, are subject to routine monitoring, interception, and search, and may be disclosed or used for any USG-authorized purpose. -This IS includes security measures (e.g., authentication and access controls) to protect USG interests--not for your personal benefit or privacy. -Notwithstanding the above, using this IS does not constitute consent to PM, LE or CI investigative searching or monitoring of the content of privileged communications, or work product, related to personal representation or services by attorneys, psychotherapists, or clergy, and their assistants. Such communications and work product are private and confidential. See User Agreement for details.</p> <a href='/set-consent'>I Accept</a> </body></html>"; } # Handle consent acceptance and set the cookie location /set-consent { add_header Set-Cookie "user_consent=1; Path=/; Max-Age=31536000; HttpOnly"; return 302 /; } location / { # Redirect users to the consent page if they haven't given consent if ($consent_given = 0) { return 302 /consent; } If NGINX is not configured to display the Standard Mandatory DOD Notice and Consent Banner before granting access to the application, this is a finding.
Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Configure nginx.conf for a section that verifies that user consent has been given. Setting a cookie upon user acceptance after reading the banner and checking for the presence of that cookie is one way to accomplish this. Add this block in nginx.conf under the http block: # Define whether consent has been given based on the cookie map $http_cookie $consent_given { "~*user_consent=1" 1; default 0; } Configure nginx.conf for a section that handles user consent and setting the cookie under the server block: # Serve the consent banner page if consent is not given location /consent { default_type text/html; return 200 "<html><body> <h1>Consent Required</h1> <p>You are accessing a U.S. Government (USG) Information System (IS) that is provided for USG-authorized use only. By using this IS (which includes any device attached to this IS), you consent to the following conditions: -The USG routinely intercepts and monitors communications on this IS for purposes including, but not limited to, penetration testing, COMSEC monitoring, network operations and defense, personnel misconduct (PM), law enforcement (LE), and counterintelligence (CI) investigations. -At any time, the USG may inspect and seize data stored on this IS. -Communications using, or data stored on, this IS are not private, are subject to routine monitoring, interception, and search, and may be disclosed or used for any USG-authorized purpose. -This IS includes security measures (e.g., authentication and access controls) to protect USG interests--not for your personal benefit or privacy. -Notwithstanding the above, using this IS does not constitute consent to PM, LE or CI investigative searching or monitoring of the content of privileged communications, or work product, related to personal representation or services by attorneys, psychotherapists, or clergy, and their assistants. Such communications and work product are private and confidential. See User Agreement for details.</p> <a href='/set-consent'>I Accept</a> </body></html>"; } # Handle consent acceptance and set the cookie location /set-consent { add_header Set-Cookie "user_consent=1; Path=/; Max-Age=31536000; HttpOnly"; return 302 /; } location / { # Redirect users to the consent page if they haven't given consent if ($consent_given = 0) { return 302 /consent; } Save nginx.conf, test the config, and reload NGINX: nginx -t && nginx -s reload
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. cat <path to config> Verify that $server_name, $server_addr, $remote_addr, $remote_user, $time_local, $status, $request, $request_id, $http_user_agent, $http_x_forwarded_for, and/or any organization defined variables are included in any custom log_format directive. If a custom log_format is defined and does not include a minimum of $server_name, $server_addr, $remote_addr, $remote_user, $time_local, $status, $request, $request_id, $http_user_agent, and $http_x_forwarded_for, this is a finding.
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Include the $server_name, $server_addr, $remote_addr, $remote_user, $time_local, $status, $request, $request_id, $http_user_agent, $http_x_forwarded_for, or any organization defined variable in any custom log_format directives. For example: log_format custom '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; Restart NGINX: nginx -s reload
Check the current permissions of nginx.conf: ls -l /etc/nginx/nginx.conf -rw-r--r-- 1 root root 0 May 23 15:04 /etc/nginx/nginx.conf If file has write permissions for anyone other than the owner, this is a finding.
By default, nginx.conf has file permissions set to admins only. Performing the chmod command will set file permissions on nginx.conf. sudo chmod 600 /etc/nginx/nginx.conf This example command will set read/write permissions for the owner only.
Check nginx.conf for external modules being loaded (grep load_module). If additional modules are being loaded, confirm the directory does not include write or execute for other users. Determine the path to nginx config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. # grep load_module /etc/nginx/nginx.conf load_module modules/ngx_http_app_protect_module.so; # ls -la /etc/nginx/modules lrwxrwxrwx 1 root root 22 Oct 10 2023 modules -> /usr/lib/nginx/modules # ls -la /usr/lib/nginx drwxr-xr-x root root 4096 Jan 30 2024 modules If directory where modules are loaded is writeable by other, this is a finding.
Set permissions on directory containing external modules to read only for "Other" only. The directory may be organizationally defined. The default path is /usr/lib/nginx/modules. # chmod o-wx /usr/lib/nginx/modules
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Determine the location of the access and error logs: grep "_log" <path to config> Determine the permissions for the log files: ls -la <path to error.log> -rw-r--r-- 1 root root 0 May 23 15:04 /var/log/nginx/error.log ls -la <path to access.log> -rw-r--r-- 1 root root 0 May 23 15:04 /var/log/nginx/access.log If files have write permissions for anyone other than the owner, this is a finding.
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Determine the location of the access and error logs: grep "_log" <path to config> Set appropriate permissions for the log files: chmod 644 <path to error.log> chmod 644 <path to access.log>
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Review the configurations looking for any listen directives. If listen directives are enabled but unnecessary, this is a finding. Verify that listeners are using SSL and redirects to SSL-enabled listeners. # nginx -c <path to nginx config> -qT | grep -A5 listen listen 192.168.0.254:80; return 301 https://$host/$request_uri; } ` listen 192.168.0.254:443 ssl default_server; ... If the listen directive does not include SSL and there is not a redirect to an SSL listener, this is a finding.
Edit the NGINX configuration file(s) and remove any unnecessary listen directives. On listen directives that are organizationally defined to need TLS, ensure that the listen directive includes SSL and SSL redirection. After saving the configuration, reload NGINX: # nginx -s reload
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Identify authentication mechanism in use by checking whether NGINX is configured to protect access to administrative interfaces or APIs: grep -Ri 'auth_' /etc/nginx/nginx.conf grep -Ri 'proxy_pass' /etc/nginx/nginx.conf grep -Ri 'ssl_verify_client' /etc/nginx/nginx.conf Also inspect references to: auth_jwt auth_request ssl_client_certificate If JWT is in use, validate the config: auth_jwt_key_file /etc/nginx/jwt.pub; auth_jwt_require exp iat; Ensure the token includes expiration (exp) and ideally issued-at (iat) fields. If using mutual TLS: ssl_verify_client on; ssl_client_certificate /etc/nginx/certs/ca.pem; Ensure client-side certificate verification is required and the certificate authority (CA) trust is defined. If using auth_request: Ensure the upstream authentication server is enforcing replay resistance (such as nonce or short-lived tokens). Validate token behavior and session timeout logic. If no replay-resistant mechanism is found for network-based access, this is a finding.
Implement a replay-resistant authentication method for any NGINX Plus resource that allows network access. Option A: JWT with expiration: Configure NGINX to use JWT authentication: auth_jwt "Protected"; auth_jwt_key_file /etc/nginx/keys/jwt_key.pub; auth_jwt_require exp iat; Ensure the token issuer includes a short-lived exp (e.g., less than five minutes) and iat claims. Option B: Mutual TLS: Enable client certificate authentication: ssl_verify_client on; ssl_client_certificate /etc/nginx/certs/client_ca.pem;
If using OCSP for certificate revocation, this requirement is Not Applicable. Determine the path to NGINX config file: # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. # cat <path to config> Check the http { blocks for the following example: http { server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; ssl_client_certificate /etc/nginx/ssl/ca.crt; ssl_verify_client on; ssl_crl /etc/nginx/ssl/crl.pem; ssl_ocsp on; ssl_ocsp_responder http://ocsp.disa.mil; ssl_stapling on; ssl_stapling_verify on; ssl_stapling_file /etc/nginx/ssl/ocsp_cache.pem; ssl_stapling_responder_timeout 3s; # Timeout for OCSP responder queries ssl_stapling_responder_error_cache_time 300s; # Cache time for responder errors location / { proxy_pass http://backend; } } } Check for certificate path validation. If "ssl_verify_client on" is not present in the configuration, this is a finding. Check if a CRL file is configured. If "ssl_crl <file>" is not present in the configuration, this is a finding.
If using OCSP for certificate revocation, this requirement is Not Applicable. Determine path to NGINX config file: # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. # cat <path to config> Configure the following lines in the http { blocks using the example below. Set ssl_verify_client on. Set ssl_crl /etc/nginx/ssl/crl.pem to match the CRL file name. http { server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; ssl_client_certificate /etc/nginx/ssl/ca.crt; ssl_verify_client on; ssl_crl /etc/nginx/ssl/crl.pem; ssl_ocsp on; ssl_ocsp_responder http://ocsp.disa.mil; ssl_stapling on; ssl_stapling_verify on; ssl_stapling_file /etc/nginx/ssl/ocsp_cache.pem; ssl_stapling_responder_timeout 3s; # Timeout for OCSP responder queries ssl_stapling_responder_error_cache_time 300s; # Cache time for responder errors location / { proxy_pass http://backend; } } }
Determine the path to NGINX config file: nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. cat <path to config> Verify that private key(s) are only readable by the owner. Example: root@e4a935153ece:/etc/ssl/nginx# nginx -qT | grep certificate_key ssl_certificate_key /etc/ssl/nginx/server.key; root@e4a935153ece:/etc/ssl/nginx# ls -la /etc/ssl/nginx/server.key -rw------- 1 root root 1704 Dec 4 18:31 /etc/ssl/nginx/server.key If the private key(s) are readable anyone other than owner, this is a finding.
Change permissions on any TLS keys used in NGINX configuration: nginx -qT | grep certificate_key chmod 600 <path to TLS key> Example: root@e4a935153ece:/etc/ssl/nginx# nginx -qT | grep certificate_key ssl_certificate_key /etc/ssl/nginx/server.key; root@e4a935153ece:/etc/ssl/nginx# chmod 600 /etc/ssl/nginx/server.key Restart NGINX: nginx -s reload
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. # grep load_module /etc/nginx/nginx.conf load_module modules/ngx_http_app_protect_module.so; If modules are loaded that are not required or known, this is a finding.
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Remove any unnecessary or unpermitted modules from the configuration. After saving the configuration, reload NGINX: # nginx -s reload
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Verify timeouts and send timeouts exist in config file and the timeout value of 10 seconds (or less) has been configured for client headers and body. nginx -c <path to nginx config> -qT | grep timeout client_body_timeout 10s; client_header_timeout 10s; send_timeout 10s; If the client_header_timeout, client_body_timeout and send_timeout are unset or have values greater than 10, this is a finding.
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Add or modify the client_body_timeout, client_header_timeout and send_timeout directives to have a value of 10 seconds or lower. Setting this value in the http context will cover everything unless overridden in subsequent server or location contexts. client_body_timeout 10s; client_header_timeout 10s; send_timeout 10s; After saving the configuration, reload NGINX: # nginx -s reload
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Verify that the "server_tokens" directive is present, is not set to "on", and is not set to a custom string that identifies version information. nginx -c <path to nginx config> -qT | grep server_tokens server_tokens off; If the "server_tokens" directive is missing, this is a finding. If the "server_tokens" directive is set to "on", this is a finding. If the "server_tokens" directive includes the version number, this is a finding.
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Add or modify the "server_tokens" directive to set to "off" or set to a custom string without the version information. http { server_tokens off; ... } Restart nginx after modifying the configuration: # nginx -s reload
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Verify that "syslog:server= prefix" is included in any log directive: # cat <path to config> Find the "error_log: or "access_log" directives and verify the syslog:server= prefix is included. If "error_log" or "access_log" exists and does not include "syslog:server=", this is a finding.
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Include the "syslog:server= prefix" (which can be a domain name, an IP address, or a UNIX-domain socket path. A domain name or IP address can be specified with a port to override the default port, 514. A UNIX-domain socket path can be specified after the unix: prefix:) in any log directives and configure the optional parameters (facility, tag, severity). After saving the configuration, reload NGINX: # nginx -s reload
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Check the permissions on the directory: # ls -la /etc drwxr-x-r-x 3 root root 4096 Sep 16 18:28 nginx If permissions to write are allowed for "Other", this is a finding.
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Ensure permissions on the configuration directory do not allow write permissions for "Other": # chmod o-w /etc/nginx
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Verify allow/deny is set according to organizational policy: location / { allow 192.168.0.0; allow 10.0.0.0/16; deny all; } If allow or deny is not set to organizational policy, this is a finding.
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Set allow/deny according to organizational policy. Restart NGINX after modifying the configuration. # nginx -s reload
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Examine the SSL configuration settings: grep -R 'ssl_' /etc/nginx/nginx.conf Verify that "ssl_session_timeout" is not set to greater than 15. If "ssl_session_timeout" directive is missing or set to greater than 15, this is a finding.
Set the ssl_session_timeout directive to 15 or less. Note: The default setting is five minutes and meets the control, but this STIG explicitly sets the variable to not rely on a default which may change in future versions.
Determine path to NGINX config file: # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Check that the nginx.conf file has the SSL Certificate/Key installed, the SSL Client Certificate is present, and SSL Verify is configured. server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/server_cert.pem; ssl_certificate_key /etc/nginx/ssl/server_key.pem; # Enable client certificate verification ssl_client_certificate /etc/nginx/ca_cert.pem; ssl_verify_client on; # Optional: Set verification depth for client certificates ssl_verify_depth 2; location / { proxy_pass http://backend_service; # Restrict access to valid PIV credentials if ($ssl_client_verify != SUCCESS) { return 403; } } } If the certificates are not configured and ssl_verify is not enabled, this is a finding.
NGINX installs OpenSSL by default. If not installed, follow the OS documentation. Include the following lines in the server {} block of nginx.conf: ssl_certificate /etc/nginx/ssl/server_cert.pem; ssl_certificate_key /etc/nginx/ssl/server_key.pem; # Enable client certificate verification ssl_client_certificate /etc/nginx/ca_cert.pem; ssl_verify_client on; # Optional: Set verification depth for client certificates ssl_verify_depth 2; location / { proxy_pass http://backend_service; # Restrict access to valid PIV credentials if ($ssl_client_verify != SUCCESS) { return 403; } } Save and exit. Restart NGINX after modifying the configuration: # nginx -s reload
If a keyval store is not used to store tokens, this is not applicable. Determine path to NGINX config file: # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Determine if a keyval store is used and no timeout is specified: grep keyval <location of config> Example: keyval_zone zone=oidc_access_tokens:1M state=/var/lib/nginx/state/oidc_access_tokens.json timeout=1h; If a timeout is not specified to an organization defined timeout value, this is a finding.
Determine path to NGINX config file: # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Edit the config and set a timeout on any keyval storing credentials: keyval_zone zone=oidc_access_tokens:1M state=/var/lib/nginx/state/oidc_access_tokens.json timeout=1h; Restart NGINX: nginx -s reload
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Verify the embedded security attributes are present as HTTP Headers: server { listen 443 ssl; server_name secure-api.example.com; location /data { proxy_pass http://backend_service; proxy_set_header X-Security-Classification "Confidential"; proxy_set_header X-Data-Origin "Internal-System"; proxy_set_header X-Access-Permissions "Read,Write"; } } If the "proxy_pass" variable is not set nor the "proxy_set_header" is not set for the required headers, this is a finding.
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Include the "proxy_pass" service as well as the "proxy_set_header" values as required: proxy_pass http://backend_service; proxy_set_header X-Security-Classification "Confidential"; proxy_set_header X-Data-Origin "Internal-System"; proxy_set_header X-Access-Permissions "Read,Write"; After saving the configuration, reload NGINX: # nginx -s reload
Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Find any "ssl_certificate" ssl_client_certificate" directives and verify they are DOD approved. If the certificates are not DOD approved, this is a finding.
Replace any non-DOD issued certificates with DOD-issued certificates. After replacing the certificates, reload NGINX: # nginx -s reload
Check for the "limit_req" or "limit_conn" directives in the NGINX configuration files: grep -R "limit_req\|limit_conn" /etc/nginx/ Determine if NGINX App Protect is enabled: grep -R "app_protect_enable on" /etc/nginx/ If the "lmit_req" or "limit connections" are not present, this is a finding.
Define a connection limiting zone. Open the NGINX configuration file in a text editor: sudo nano /etc/nginx/nginx.conf Establish a shared memory zone to track and limit connections from each client. Add this directive above the server block in the nginx.conf: limit_conn_zone $binary_remote_addr zone=conn_limit_zone:10m; $binary_remote_addr: Uses the client's IP address for limiting. zone=conn_limit_zone:10m: Allocates 10MB of memory for tracking connections. Adjust size based on your needs. Apply connection limiting in the server block. Now, apply connection limiting to the desired location block. Inside the location block, add the connection limiting directive: nginx Copy code server { location / { limit_req zone=one burst=20 nodelay; limit_conn conn_limit_zone 10; proxy_pass http://backend; } } limit_conn conn_limit_zone 10: Limits the client to 10 concurrent connections. Save and exit the file. Restart NGINX: # nginx -s reload
Determine the path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Examine the SSL configuration settings: grep -R 'ssl_' /etc/nginx/nginx.conf Verify TLS versions: ssl_protocols TLSv1.2 TLSv1.3; Verify cipher suites: ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'; ssl_prefer_server_ciphers on; Note: The cipher list can be more restrictive if defined by the organization. If non-FIPS ciphers or weak protocols (e.g., TLSv1.0/1.1, RC4, MD5, 3DES) are present, this is a finding.
Restrict TLS versions to FIPS-approved protocols: ssl_protocols TLSv1.2 TLSv1.3; Configure only FIPS compliant ciphers: ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'; Do not allow clients to select the ciphers: ssl_prefer_server_ciphers on; Restart NGINX to apply changes: sudo nginx -t && sudo systemctl reload nginx
If using CRL for certificate revocation, this requirement is Not Applicable. Determine the path to NGINX config file(s): # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. # cat <path to config> Check the http { blocks for the following example: http { server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; ssl_client_certificate /etc/nginx/ssl/ca.crt; ssl_verify_client on; ssl_crl /etc/nginx/ssl/crl.pem; ssl_ocsp on; ssl_ocsp_responder http://ocsp.disa.mil; ssl_stapling on; ssl_stapling_verify on; ssl_stapling_file /etc/nginx/ssl/ocsp_cache.pem; ssl_stapling_responder_timeout 3s; # Timeout for OCSP responder queries ssl_stapling_responder_error_cache_time 300s; # Cache time for responder errors location / { proxy_pass http://backend; } } } Check for certificate path validation. If "ssl_verify_client on" is not in the configuration, this is a finding. Check if OCSP is enabled. If "ssl_ocsp on" is not in the configuration, this is a finding. Check if OCSP Stapling is configured. If "ssl_stapling on" or "ssl_stapling_verify on" is not in the configuration, this is a finding. If "ssl_stapling_file <file>" is not present in the configuration, this is a finding.
Edit the NGINX configuration file. Set "ssl_verify_client on", "ssl_ocsp on", ssl_stapling_verify on", and "ssl_stapling on" as shown in the example below. Create a local cache for OCSP responses: touch /etc/nginx/ssl/ocsp_cache.pem chmod 600 /etc/nginx/ssl/ocsp_cache.pem Set the "ssl_stapling_file" directive with the file created as shown in the example below. http { server { listen 443 ssl; server_name example.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; ssl_client_certificate /etc/nginx/ssl/ca.crt; ssl_verify_client on; ssl_crl /etc/nginx/ssl/crl.pem; ssl_ocsp on; ssl_ocsp_responder https://ocsp.disa.mil; ssl_stapling on; ssl_stapling_verify on; ssl_stapling_file /etc/nginx/ssl/ocsp_cache.pem; ssl_stapling_responder_timeout 3s; # Timeout for OCSP responder queries ssl_stapling_responder_error_cache_time 300s; # Cache time for responder errors location / { proxy_pass http://backend; } } }
Verify NGINX is using OpenSSL with FIPS enabled. For version 1.x: # nginx -V nginx version: nginx/1.15.2 (nginx-plus-r16) built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC) built with OpenSSL 1.0.2k-fips 26 Jan 2017" If the response does not include "fips" in the OpenSSL version, this is a finding. For version 3.x: # openssl list -providers Providers: base name: OpenSSL Base Provider version: 3.2.2 status: active default name: OpenSSL Default Provider version: 3.2.2 status: active fips name: Red Hat Enterprise Linux 9 - OpenSSL FIPS Provider version: 3.2.2-622cc79c634cbbef status: active If the response does not list a FIPS provider with a status of "active", this is a finding.
FIPS must be enabled on the operating system. Follow the OS guidelines for installing FIPS mode. After installation, confirm that FIPS is enabled: # sudo sysctl –a | grep fips crypto.fips_enabled = 1 Install the FIPS-validated version of OpenSSL to the operating system.
Determine path to NGINX config file(s): nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Identify the NGINX runtime user: grep -E '^\s*user\s+' /etc/nginx/nginx.conf Expected output (example): user nginx; Confirm the password is locked: passwd -S nginx Expected output (example): nginx LK 2025-05-21 -1 -1 -1 -1 (Password locked.) If the NGINX runtime user account is not locked for password changes, this is a finding.
Lock the password for the NGINX user (if not already locked): sudo passwd -l nginx
If not using the NGINX API, this is Not Applicable. Determine path to NGINX config file: # nginx -qT | grep "# configuration" # configuration file /etc/nginx/nginx.conf: Note: The default NGINX configuration is "/etc/nginx/nginx.conf", though various files may also be included. Check that the nginx.conf file contains the API directive and a separate listen address: http { server { listen 192.168.0.1:80; location / { proxy_pass http://backend; } location /api { api write=on; } } } If the API is running on the same network as production traffic, this is a finding.
Configure the API directive to use a separate listen address from production traffic: http { server { listen 192.168.0.1:80; location / { proxy_pass http://backend; } } server { listen 10.0.0.1:80; location /api { api write=on; } } } After saving the updated config, restart NGINX: nginx -s reload.
Check SSL/TLS certificate and private key file permissions: # ls -la /home/ubuntu/nginx.com.crt # ls -la /home/ubuntu/nginx.com.key Verify: - Certificate file permissions are 644 or more restrictive. - Private key file permissions are 600 or more restrictive. - Files are owned by nginx user or root. - Files are not world-readable or group-writable. If these permissions are not set, this is a finding. Verify certificate validity and strength: # openssl x509 -in /home/ubuntu/nginx.com.crt -text -noout Verify: - Certificate is not expired. - Uses RSA key length of 2048 bits minimum or ECDSA P-256 minimum. - Signature algorithm is SHA-256 or stronger (not SHA-1 or MD5). - Certificate chain is complete and valid. If these values are not met, this is a finding. Verify private key strength and protection: # openssl rsa -in /home/ubuntu/nginx.com.key -text -noout -check Verify: - Key length is 2048 bits minimum. - Key is not encrypted with weak algorithms. - Key passes integrity check. If these key values are not set, this is a finding.
Set proper file permissions for SSL certificate and private key: # chmod 644 /home/ubuntu/nginx.com.crt # chmod 600 /home/ubuntu/nginx.com.key # chown nginx:nginx /home/ubuntu/nginx.com.crt # chown nginx:nginx /home/ubuntu/nginx.com.key Move certificates to secure location: # mkdir -p /etc/nginx/ssl # mv /home/ubuntu/dev.sports.com.* /etc/nginx/ssl/ # chmod 700 /etc/nginx/ssl Update NGINX configuration to use secure certificate location: server { listen 443 ssl; ssl_certificate /etc/nginx/ssl/nginx.com.crt; ssl_certificate_key /etc/nginx/ssl/nginx.com.key; ssl_session_cache shared:SSL:10m; ssl_dhparam /etc/nginx/ssl/dhparam.pem; } Generate strong DH parameters if not present: # openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048 # chmod 644 /etc/nginx/ssl/dhparam.pem
Verify NGINX Plus revokes or invalidates access tokens: Check support for token revocation enforcement: # grep -i "introspect\|revok\|blacklist\|logout" /etc/nginx/conf.d/*.conf Confirm token validation includes checking revocation status using: - OAuth2 introspection endpoint. - Token revocation list. - Allowlist or deny-list implementation. - Explicit session logout propagation. Inspect logout and session termination handling: # grep -i "session_timeout\|logout\|revoke" /etc/nginx/conf.d/*.conf Ensure sessions are invalidated upon: - User logout. - Token expiration. - Authentication source change (e.g., password reset). Token revocation events (manual or automatic) must be logged with user ID, reason, and timestamp. If NGINX Plus does not support access token revocation or fails to enforce revocation upon compromise or logout, this is a finding.
Configure NGINX Plus to enforce access token revocation mechanisms. Enable token revocation via introspection endpoint (OIDC/OAuth2): location = /token/introspect { internal; proxy_pass https://auth.example.mil/oauth2/introspect; proxy_set_header Content-Type "application/x-www-form-urlencoded"; proxy_pass_request_body on; proxy_set_body "token=$http_authorization"; } Fail-closed on invalid or revoked token: location /secure/ { auth_request /token/introspect; error_page 401 = @error_revoke; } location @error_revoke { return 401 "Access token revoked or invalid."; } Revoke tokens during logout flows: Redirect logout requests to IdP to invalidate tokens: location = /logout { return 302 https://auth.example.mil/logout?token=$http_authorization; } Log all token revocation events: log_format token_revocation '$remote_addr - $remote_user [$time_local] ' '"$request" status=$status ' 'token_id="$jwt_jti" revoked="true" '; access_log /var/log/nginx/token_revocation.log token_revocation;