Organisation des fichiers

  • /etc/nginx/
    • certs/
      • ca.crt
      • dhparam.pem
    • config/
      • client_ssl_loc.conf
      • client_ssl_srv.conf
      • compression.conf
      • errors.conf
      • https.conf
      • proxy.conf
      • security.conf
      • security_csp.conf
      • security_fp.conf
    • html/
      • error.html
    • mime.types
    • nginx.conf
    • sites/
      • <vhost 1>.ticloud.fr.conf
      • <vhost 2>.ticloud.fr.conf

Configuration principale

# nginx.conf
user nginx;
pid /run/nginx/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;
pcre_jit on;
load_module modules/ngx_http_headers_more_filter_module.so;

events {
}

http {
	charset utf-8;

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;

	server_tokens off;

	client_body_buffer_size       16m;  
	client_header_buffer_size     1k;     
	client_max_body_size          16m;   
	large_client_header_buffers   2 1k;

	# MIME
	include mime.types;
	default_type application/octet-stream;

	# logging (option vhost)
        log_format main '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';
	access_log /var/log/nginx/access.log main;
	error_log /var/log/nginx/error.log warn;


	# limits
        limit_req_zone $binary_remote_addr zone###### flood:10m rate50r/s;
        limit_req zone###### flood burst200 nodelay;
        limit_conn_zone $binary_remote_addr zone=limit:10m;
        limit_conn limit 150;

	# SSL
	ssl_session_timeout 12h;
	ssl_session_cache shared:SSL:10m;
	ssl_session_tickets off;
 	ssl_prefer_server_ciphers on;
	ssl_dhparam certs/dhparam.pem;
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384;
 	ssl_ecdh_curve secp521r1:secp384r1;
 	ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;

	# OCSP Stapling
	ssl_stapling on;
	ssl_stapling_verify on;
	resolver 80.67.169.12 80.67.169.40 valid=60s;
	resolver_timeout 2s;

	map $status $status_text {
	  400 'Bad Request';
	  401 'Unauthorized';
	  402 'Payment Required';
	  403 'Forbidden';
	  404 'Not Found';
	  405 'Method Not Allowed';
	  406 'Not Acceptable';
	  407 'Proxy Authentication Required';
	  408 'Request Timeout';
	  409 'Conflict';
	  410 'Gone';
	  411 'Length Required';
	  412 'Precondition Failed';
	  413 'Payload Too Large';
	  414 'URI Too Long';
	  415 'Unsupported Media Type';
	  416 'Range Not Satisfiable';
	  417 'Expectation Failed';
	  418 'I<br/>'m a teapot';
	  421 'Misdirected Request';
	  422 'Unprocessable Entity';
	  423 'Locked';
	  424 'Failed Dependency';
	  425 'Too Early';
	  426 'Upgrade Required';
	  428 'Precondition Required';
	  429 'Too Many Requests';
	  431 'Request Header Fields Too Large';
	  451 'Unavailable For Legal Reasons';
	  500 'Internal Server Error';
	  501 'Not Implemented';
	  502 'Bad Gateway';
	  503 'Service Unavailable';
	  504 'Gateway Timeout';
	  505 'HTTP Version Not Supported';
	  506 'Variant Also Negotiates';
	  507 'Insufficient Storage';
	  508 'Loop Detected';
	  510 'Not Extended';
	  511 'Network Authentication Required';
	  default 'Something is wrong';
	}

	# load configs
	include sites/*;
}

Déclaration des vhost

# vhost.ticloud.fr.conf
server {
  include config/https.conf;

  # security
  include config/security*.conf;

  # additional config
  include config/compression.conf;

  # errors                     
  include config/errors.conf;

  server_name <vhost>.ticloud.fr;

  #SSL client security
  include config/client_ssl_srv.conf;

  location / {
    #SSL client security
    include config/client_ssl_loc.conf;
  
    proxy_pass http://<ip>:<port>;
    include config/proxy.conf;
  }
}

Authentification client SSL

Cela permet d’authentifier un client avec un certificat SSL. Plus d’infos ici : Authentification par certificat client

# client_ssl_loc.conf
# if the client-side certificate failed to authenticate, show a 403
# message to the client
if ($ssl_client_verify != SUCCESS) {
    return 403;
}
# client_ssl_srv.conf
ssl_client_certificate certs/ca.crt;
ssl_verify_client optional;

Gestion de la compression

⚠️ Il n’est pas recommandé d’activer la compression sur HTTPS pour des données sensibles, des failles existent (exploit CRIME ou BREACH par exemple. Cependant, il est acceptable d’utiliser la compression seulement pour certains type de données (images, polices, css,…) ⚠️

# compression.conf
# gzip
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types text/plain 
	text/css 
	text/xml 
	application/json 
	application/javascript 
	application/rss+xml 
	application/atom+xml 
        font/woff2
	image/bmp
	image/png
	image/jpeg
        image/x-icon
	image/svg+xml;

Gestion des erreurs

⚠️ Dans le cadre d’un reverse proxy, par défaut, NGINX n’intercepte pas les codes erreurs venant du backend. C’est alors la page d’erreurs du site en backend qui est renvoyée au client !

2 solutions à ça :

  1. Insérer la directive suivante : nginxproxy_intercept_errors Ainsi peu importe d’où vient les codes d’erreurs, c’est toujours les pages d’erreurs NGINX qui seront affichées.
  2. Paramétrer des pages d’erreurs custom sur le backend.

La deuxième solution est préférable pour éviter d’altérer le comportement de certains sites qui ont leur propre gestion des erreurs.

Ce code est à inclure dans le bloc server de chaque vhost.

# errors.conf
error_page 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 421 422 423 424 425 426 428 429 431 451 500 501 502 503 504 505 506 507 508 510 511 /error.html;

location = /error.html {
  ssi on;
  internal;
  root /etc/nginx/html;
}

Listener https

# https.conf
listen 443 ssl http2;

# SSL
ssl_certificate <path>/ticloud.fr/fullchain.cer;
ssl_certificate_key <path>/ticloud.fr.key;

Gestion des reverse proxy

# proxy.conf
proxy_http_version	1.1;
proxy_cache_bypass	$http_upgrade;

proxy_set_header Upgrade		$http_upgrade;
proxy_set_header Connection		"upgrade";
proxy_set_header Host			$host;

proxy_set_header X-Real-IP		$remote_addr;
proxy_set_header X-Forwarded-For	$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto	$scheme;
proxy_set_header X-Forwarded-Host	$host:$server_port;
proxy_set_header X-Forwarded-Port	$server_port;
proxy_set_header X-Forwarded-Server 	$host;

# RAZ des header venant du backend
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
proxy_hide_header X-XSS-Protection;
proxy_hide_header Referrer-Policy;
proxy_hide_header X-Powered-By;

proxy_intercept_errors on;

Hardening

Ces différentes configuration servent à renforcer la sécurité du serveur.

more_set_headers permet de cacher le type de server (normalement nginx) en le remplaçant par ce qu’on veut. Cette fonction n’est pas standard, il faut charger le module Headers-mode Plus d’informations ici : Chargement de modules dynamiques NGINX

# security.conf
# security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
more_set_headers "Server: Unknown";

## Block Software download user agents + wget ##
if ($http_user_agent ~* LWP::Simple|BBBike|wget) {
  return 403;
}

# Block access to multiple extensions
location ~* ^.*(<br/>.(?:git|svn|bak|conf|dist|in[ci]|log|orig|sql|sw[op]|htaccess))$ {
  deny all;
  access_log /var/log/nginx/restricted-files.log main;
}
# security_csp.conf
add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; connect-src 'self'; img-src * data:; style-src * 'unsafe-inline'; font-src *; frame-src 'self'; frame-ancestors 'self'; media-src *;" always;
# security_fp.conf
add_header Feature-Policy "geolocation 'none'; midi 'none'; notifications 'none'; push 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; vibrate 'none'; fullscreen 'none'; payment 'none'; usb 'none';";

Sources

La bible nginx
SSLlabs (test conf SSL)
Test de conf SSL de Mozilla
Check des headers

La conf de l’ami Seb