Nginx installation guide
Prerequisite
This guide was tested on Debian Jessie (stable), and Stretch (testing). For other distributions you might have some adjustments to do.
While not mandatory, the guide makes use of the following programs to enhance the security of the installation
Install
Nginx
Jessie
The version of nginx in Debian Jessie support the deprecated SPDY protocol. Using the version from jessie-backports allows to get support for HTTP/2.
$ sudo apt install nginx-light/jessie-backports libnginx-mod-http-headers-more-filter
Stretch
$ sudo apt install nginx-light libnginx-mod-http-headers-more-filter
nginx_modsite
nginx_modsite is a script that allows to activate or deactivate a site simply, without having to handle symlinks manually. In Debian, it is distributed in source form as part of the nginx-doc
package. The easiest is to download it directly from the source repository:
$ sudo curl -o /usr/local/sbin/nginx_modsite "https://anonscm.debian.org/cgit/collab-maint/nginx.git/plain/debian/help/examples/nginx_modsite"
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 4625 100 4625 0 0 12836 0 --:--:-- --:--:-- --:--:-- 12847
$ sudo chmod +x /usr/local/sbin/nginx_modsite
Configure
conf.d
The conf.d folder stores shared configuration shared between all the sites hosted on your server.
Create the following files:
/etc/nginx/conf.d/dns.conf
# DNS resolver # It is required for OCSP Stapling. It might also be used if you use a hostname for upstream servers resolver 127.0.0.1; # If you don't have a DNS resolver on your machine you can use google public ones instead #resolver 8.8.8.8 8.8.4.4;
/etc/nginx/conf.d/gzip.conf
# Insert header "Vary: Accept-Encoding" in responses # https://www.maxcdn.com/blog/accept-encoding-its-vary-important/ gzip_vary on; gzip_comp_level 6; gzip_proxied any; gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
/etc/nginx/conf.d/php*.conf
See documentation to install PHP./etc/nginx/conf.d/server_tokens.conf
# Hide nginx version # This doesn't provides any real security but makes hackers life a bit more difficult server_tokens off; more_clear_headers Server;
/etc/nginx/conf.d/ssl.conf
Generate file# These two settings are now included by default in nginx.conf #ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #ssl_prefer_server_ciphers on; ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!CAMELLIA:!SEED"; # Parameters for Diffie-Hellman handshake # Generate the file with the command: # openssl dhparam 2048 -out /etc/nginx/dh2048.pem ssl_dhparam /etc/nginx/dh2048.pem; # Support OSCP Stapling. Check that resolver from in dns.conf is working ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt; # Support SSL session cache ssl_session_cache shared:NginxCache:50m; ssl_session_tickets off; # https://timtaubert.de/blog/2014/11/the-sad-state-of-server-side-tls-session-resumption-implementations/
/etc/nginx/dh2048.pem
with$ sudo openssl dhparam 2048 -out /etc/nginx/dh2048.pem
snippets
The snippets folder allows you to store bits of configuration that you can later include in virtual hosts configuration.This saves a lot of typing and errors when creating a new site.
/etc/nging/conf.d/acme-challenge.conf
See Let’s Encrypt/etc/nging/conf.d/hsts.conf
# Activate HTTP Strict Transport Security # max-age value is in seconds. 31536000 is 1 year add_header Strict-Transport-Security max-age=31536000 always;
/etc/nginx/snippets/security-headers.conf
# Some safe security headers that can almost be used for any site # https://stackoverflow.com/a/24998106/1631174 add_header X-XSS-Protection "1; mode=block" always; # https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#X-Content-Type-Options add_header X-Content-Type-Options nosniff always; # Prevent access from flash and PDF add_header X-Permitted-Cross-Domain-Policies none always;
/etc/nginx/snippets/x-frame-options-deny.conf
/etc/nginx/snippets/x-frame-options-sameorigin.conf
# Prevent all usages of the website in an iframe. # Warning: This might break the site if it uses iframes for internal # functionalities. You might want to use the less strict # x-frame-options-sameorigin.conf in that case. add_header X-Frame-Options DENY always;
# Prevent usage of the website in an iframe from other domains. # Warning: This might break the site if it uses iframes for internal # functionalities. You might want to use the less strict # x-frame-options-sameorigin.conf in that case. add_header X-Frame-Options SAMEORIGIN always;
/etc/nginx/snippets/https-permanent-redirect.conf
# Reply to the browser with a permanent redirect to the secure version of the page # Wrapped in a location block so that other snippets (acme-challenge.conf) can override that. location / { return 301 https://$host$request_uri; }
/etc/nginx/snippets/listen-http.conf
/etc/nginx/snippets/listen-https.conf
Obviously, you need to replace the example IP addresses by the one of your server. You can get the IP of your server with the commandscurl https://ipv6.meurisse.org
andcurl https://ipv4.meurisse.org
.listen [2001:db8:3:47d0::2e:7]:80; listen 203.0.113.23:80;
listen [2001:db8:3:47d0::2e:7]:443 ssl http2; listen 203.0.113.23:443 ssl http2;
/etc/nginx/snippets/ssl.conf
ssl on; ssl_stapling on;
HTTP Auth
Install
$ sudo apt install apache2-utils
Create Password File
If the folder doesn't exist, you need to create it using
$ sudo mkdir /etc/nginx/htpasswd
$ sudo chmod 710 /etc/nginx/htpasswd
$ sudo chown root:www-data /etc/nginx/htpasswd
The create the user file
$ sudo touch /etc/nginx/htpasswd/generic.htpasswd
If you want different website to have different users, you can create as many password files as you want.
Add User
$ sudo htpasswd /etc/nginx/htpasswd/generic.htpasswd jdoe
New password:
Re-type new password:
Adding password for user jdoe
To update a password user, just run the same command.
Nginx will pick the modified file automatically. There is nothing to restart.
Use
To restrict access to a site or part of it, add the following lines to a server
or location
config
auth_basic "You shall not pass!";
auth_basic_user_file /etc/nginx/htpasswd/generic.htpasswd;
Firewall
You need to open TCP ports 80 and 443 in your firewall. Assuming that you configured nftables as described, you can edit file /etc/nftables/main_config.conf
and add
# Web
add element inet main tcp_port_in { 80, 443 }
and activate it using
$ sudo /etc/nftables/reload_main.conf
httpoxy
The httpoxy security flow is a flow targeting CGI scripts using the Proxy HTTP header. It is possible to mitigate it by filtering out this header in fastcgi and proxy calls in Nginx.
Edit files /etc/nginx/fastcgi.conf
and /etc/nginx/fastcgi_params
and add these lines
# httpoxy.org
fastcgi_param HTTP_PROXY "";
Also edit file /etc/nginx/proxy_params
add add these lines
# httpoxy.org
proxy_set_header Proxy "";
/var/www/ permissions
Setting the setgid bit on the /var/www/
allows to make sure that new files are readable by Nginx.
$ sudo chmod 2750 /var/www/
$ sudo chown root:www-data /var/www/
This also revoke the default read permission to user outside the www-data
group. They don't need it and some data might not be public here.
New Site
This section shows how to create a new website in your Nginx server. Instructions here a very generic and will need to be adapted for your specific case.
In the following sections, we are showing the conf for a site called mysite.example.org. You need to replace all occurrences of mysite.example.org by the name of the site you want to create.
- Create the config file
/etc/nginx/sites-available/mysite.example.org
server { include snippets/listen-http.conf; server_name mysite.example.org; access_log /var/log/nginx/mysite.example.org.access.log; error_log /var/log/nginx/mysite.example.org.error.log info; include snippets/acme-challenge.conf; include snippets/https-permanent-redirect.conf; } server { include snippets/listen-https.conf; server_name mysite.example.org; access_log /var/log/nginx/mysite.example.org.access.log; error_log /var/log/nginx/mysite.example.org.error.log info; include snippets/acme-challenge.conf; #include snippets/ssl.conf; #ssl_certificate /etc/letsencrypt/live/mysite.example.org/fullchain.pem; #ssl_certificate_key /etc/letsencrypt/live/mysite.example.org/privkey.pem; #include snippets/hsts.conf; include snippets/security-headers.conf; include snippets/x-frame-options-deny.conf; root /var/www/mysite.example.org; }
- Activate the configuration with
$ sudo nginx_modsite -e mysite.example.org Would you like to reload the Nginx configuration now? (Y/n) Y
- Edit file
/usr/local/etc/certmanage/main.json
and add the following to the list{ "domains": ["mysite.example.org"], "reload": [["/bin/systemctl", "reload", "nginx.service"]] }
- Get your certificate
$ sudo /usr/local/sbin/certmanage Renewing certificate for mysite.example.org that will expire on 0001-01-01 Saving debug log to /var/log/letsencrypt/letsencrypt.log Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org Obtaining a new certificate Performing the following challenges: http-01 challenge for mysite.example.org Using the webroot path /var/www/acme-challenge for all unmatched domains. Waiting for verification... Cleaning up challenges Generating key (2048 bits): /etc/letsencrypt/keys/1764_key-certbot.pem Creating CSR: /etc/letsencrypt/csr/1764_csr-certbot.pem IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/mysite.example.org/fullchain.pem. Your cert will expire on 2025-03-05. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le Restarting services: systemctl reload nginx.service
- Uncomment the ssl related lines in
/etc/nginx/sites-available/mysite.example.org
and run$ sudo systemctl reload nginx.service
Fail2Ban
Webservers are usually a good target for hackers. A lot of them contain outdated, insecure and misconfigured software and if your server run languages like PHP, the attacker would be able to execute pretty much any action once he cracked your server.
Warning: The rules described here protect against generic attacks on your webserver. If you install some specific software that has it's own authentication (owncoud, roundcube...) you need to create rules for it.
nginx-http-auth
First rule is pretty simple simple. It protect against http authentication (the ugly popups asking your password before you enter the site).
Create file /etc/fail2ban/jail.d/nginx-http-auth.conf
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/*error.log
nginx-botsearch
This rule match 404 errors when bots try to find unsecure software on your server. While it should generally work fine, you should check ban report to make sure you don't lock out legitimate users.
Create file /etc/fail2ban/jail.d/nginx-botsearch.conf
[nginx-botsearch]
enabled = true
port = http,https
logpath = /var/log/nginx/*error.log
maxretry = 2