[nginx] setup nginx

Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
This commit is contained in:
Yohann D'ANELLO 2021-05-25 09:48:58 +02:00
parent f9491c6553
commit 8c4684a450
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
20 changed files with 712 additions and 2 deletions

View File

@ -5,5 +5,4 @@ glob_certbot:
dns_rfc2136_secret: "{{ vault.certbot_dns_secret }}"
mail: ynerant@crans.org
certname: ynerant.fr
# domains: "*.ynerant.fr"
domains: "ynerant.fr, *.ynerant.fr, ens.kitchen, *.ens.kitchen, ananas.paris, *.ananas.paris, saperlistpopette.fr, *.saperlistpopette.fr"
domains: "*.ynerant.fr"

32
group_vars/nginx.yml Normal file
View File

@ -0,0 +1,32 @@
---
glob_nginx:
contact: ynerant@crans.org
who: "Ÿnérant"
service_name: service
ssl:
# Add adm.ynerant.fr if necessary
- name: ynerant.fr
cert: /etc/letsencrypt/live/ynerant.fr/fullchain.pem
cert_key: /etc/letsencrypt/live/ynerant.fr/privkey.pem
trusted_cert: /etc/letsencrypt/live/ynerant.fr/chain.pem
servers:
- ssl: false # Replace by crans.org or adm.crans.org
default: true
server_name:
- "default"
- "_"
root: "/var/www/html"
locations:
- filter: "/"
params: []
additional_params: []
upstreams: []
auth_passwd: []
default_server:
default_ssl_server:
default_ssl_domain: ynerant.fr
real_ip_from:
- "172.16.0.0/16"
- "fd00:0:0:42::/64"
deploy_robots_file: false

View File

@ -0,0 +1,46 @@
loc_certbot:
- dns_rfc2136_server: '172.16.42.103'
dns_rfc2136_name: certbot_challenge.
dns_rfc2136_secret: "{{ vault.certbot_dns_secret }}"
mail: ynerant@crans.org
certname: ynerant.fr
domains: "ynerant.fr, *.ynerant.fr, ens.kitchen, *.ens.kitchen, ananas.paris, *.ananas.paris, saperlistpopette.fr, *.saperlistpopette.fr"
loc_nginx:
servers: []
ssl:
- name: ynerant.fr
cert: /etc/letsencrypt/live/ynerant.fr/fullchain.pem
cert_key: /etc/letsencrypt/live/ynerant.fr/privkey.pem
trusted_cert: /etc/letsencrypt/live/ynerant.fr/chain.pem
glob_reverseproxy:
redirect_dnames: []
reverseproxy_sites:
- {from: mailu.ynerant.fr, to: 172.16.42.104}
# - {from: mirror.adm.ynerant.fr, to: "https://ftps.crans.org"}
- {from: element.ynerant.fr, to: "172.16.42.199:8002"}
- {from: hydrogen.ynerant.fr, to: "172.16.42.199:8003"}
- {from: git.ynerant.fr, to: "172.16.42.199:8007"}
- {from: cloud.ynerant.fr, to: "172.16.42.199:8007"}
# - {from: notls.adh.crans.org, to: "172.16.42.199:8011"}
- {from: thelounge.ynerant.fr, to: "172.16.42.199:8012"}
- {from: bibliogram.ynerant.fr, to: "172.16.42.199:8014"}
- {from: reddit.ynerant.fr, to: "172.16.42.199:8015"}
- {from: teddit.ynerant.fr, to: "172.16.42.199:8015"}
- {from: whoami.ynerant.fr, to: "172.16.42.199:8016"}
- {from: saperlistpopette.fr, to: "172.16.42.199:8010"}
- {from: kfet.saperlistpopette.fr, to: "172.16.42.199:8010"}
- {from: ens.kitchen, to: "https://perso.crans.org/club-kitchens/"}
redirect_sites: []
# - {from: machin.ynerant.fr, to: truc.ynerant.fr}
static_sites:
- ynerant.fr
- thelounge.ynerant.fr

6
hosts
View File

@ -7,6 +7,9 @@ proxy.adm.ynerant.fr
[debian:children]
server
[nginx:children]
reverseproxy
[ntp_server]
routeur-templier.adm.ynerant.fr
@ -15,6 +18,9 @@ ynerant-pc.fil.sand.auro.re
ynerant-thinkpad.wifi.sand.auro.re
localhost
[reverseproxy]
proxy.adm.ynerant.fr
[server:children]
virtu
vm

14
plays/nginx.yml Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env ansible-playbook
---
- hosts: nginx,!reverseproxy
vars:
nginx: "{{ glob_nginx | default({}) | combine(service_nginx | default({})) | combine(loc_nginx | default({})) }}"
roles:
- nginx
- hosts: reverseproxy
vars:
nginx: "{{ glob_nginx | default({}) | combine(service_nginx | default({})) | combine(loc_nginx | default({})) }}"
reverseproxy: "{{ glob_reverseproxy | default({}) | combine(service_reverseproxy | default({})) | combine(loc_reverseproxy | default({})) }}"
roles:
- nginx

View File

@ -0,0 +1,5 @@
---
- name: Reload nginx
systemd:
name: nginx
state: reloaded

128
roles/nginx/tasks/main.yml Normal file
View File

@ -0,0 +1,128 @@
---
- name: Install NGINX
apt:
update_cache: true
name: nginx
register: apt_result
retries: 3
until: apt_result is succeeded
- name: Copy proxypass snippets
template:
src: "nginx/snippets/options-proxypass.conf.j2"
dest: "/etc/nginx/snippets/options-proxypass.conf"
owner: root
group: root
mode: 0644
- name: Copy SSL snippets
template:
src: "nginx/snippets/options-ssl.conf.j2"
dest: "/etc/nginx/snippets/options-ssl.{{ item.name }}.conf"
owner: root
group: root
mode: 0644
loop: "{{ nginx.ssl }}"
- name: Disable default site
file:
dest: "/etc/nginx/sites-enabled/default"
state: absent
- name: Copy reverse proxy sites
when: reverseproxy is defined
template:
src: "nginx/sites-available/{{ item }}.j2"
dest: "/etc/nginx/sites-available/{{ item }}"
owner: root
group: root
mode: 0644
loop:
- reverseproxy
- reverseproxy_redirect_dname
- redirect
notify: Reload nginx
- name: Activate reverse proxy sites
when: reverseproxy is defined
file:
src: "/etc/nginx/sites-available/{{ item }}"
dest: "/etc/nginx/sites-enabled/{{ item }}"
owner: root
group: root
state: link
loop:
- reverseproxy
- reverseproxy_redirect_dname
- redirect
notify: Reload nginx
ignore_errors: "{{ ansible_check_mode }}"
- name: Copy service nginx configuration
when: nginx.servers is defined and nginx.servers|length > 0
template:
src: "nginx/sites-available/service.j2"
dest: "/etc/nginx/sites-available/{{ nginx.service_name }}"
owner: root
group: root
mode: 0644
notify: Reload nginx
- name: Activate local nginx service site
when: nginx.servers is defined and nginx.servers|length > 0
file:
src: "/etc/nginx/sites-available/{{ nginx.service_name }}"
dest: "/etc/nginx/sites-enabled/{{ nginx.service_name }}"
owner: root
group: root
state: link
notify: Reload nginx
ignore_errors: "{{ ansible_check_mode }}"
- name: Copy 50x error page
template:
src: www/html/50x.html.j2
dest: /var/www/html/50x.html
owner: www-data
group: www-data
mode: 0644
- name: Copy robots.txt file
when: nginx.deploy_robots_file
template:
src: www/html/robots.txt.j2
dest: /var/www/html/robots.txt
owner: www-data
group: www-data
mode: 0644
- name: Install passwords
when: nginx.auth_passwd|length > 0
template:
src: nginx/passwd.j2
dest: /etc/nginx/passwd
mode: 0644
- name: Copy 401 error page
when: nginx.auth_passwd|length > 0
template:
src: www/html/401.html.j2
dest: /var/www/html/401.html
owner: www-data
group: www-data
mode: 0644
- name: Indicate role in motd
template:
src: update-motd.d/05-service.j2
dest: /etc/update-motd.d/05-nginx
mode: 0755
- name: Clean old files
file:
path: "{{ item }}"
state: absent
loop:
- "/etc/nginx/snippets/options-ssl.conf"
- "/var/www/custom_401.html"
- "/var/www/robots.txt"

View File

@ -0,0 +1,8 @@
-----BEGIN DH PARAMETERS-----
MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz
+8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a
87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7
YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi
7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD
ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg==
-----END DH PARAMETERS-----

View File

@ -0,0 +1,4 @@
{{ ansible_header | comment }}
{% for user, hash in nginx.auth_passwd.items() -%}
{{ user }}:{{ hash }}
{% endfor -%}

View File

@ -0,0 +1,87 @@
{{ ansible_header | comment }}
{% for site in reverseproxy.redirect_sites %}
# Redirect http://{{ site.from }} to http://{{ site.to }}
server {
listen 80;
listen [::]:80;
server_name {{ site.from }};
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 http://{{ site.to }}$request_uri;
}
}
# Redirect https://{{ site.from }} to https://{{ site.to }}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ site.from }};
# SSL common conf
include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 https://{{ site.to }}$request_uri;
}
}
{% endfor %}
{# Also redirect for DNAMEs #}
{% for dname in reverseproxy.redirect_dnames %}
{% for site in reverseproxy.redirect_sites %}
{% set from = site.from | regex_replace('crans.org', dname) %}
{% if from != site.from %}
# Redirect http://{{ from }} to http://{{ site.to }}
server {
listen 80;
listen [::]:80;
server_name {{ from }};
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 http://{{ site.to }}$request_uri;
}
}
# Redirect https://{{ from }} to https://{{ site.to }}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ from }};
# SSL common conf
include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 https://{{ site.to }}$request_uri;
}
}
{% endif %}
{% endfor %}
{% endfor %}

View File

@ -0,0 +1,62 @@
{{ ansible_header | comment }}
# Automatic Connection header for WebSocket support
# See http://nginx.org/en/docs/http/websocket.html
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
{% for site in reverseproxy.reverseproxy_sites %}
# Redirect http://{{ site.from }} to https://{{ site.from }}
server {
listen 80;
listen [::]:80;
server_name {{ site.from }};
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 https://$host$request_uri;
}
}
# Reverse proxify https://{{ site.from }} to {% if not site.to.startswith("http") %}http://{% endif %}{{ site.to }}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ site.from }};
# SSL common conf
include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
# Log into separate log files
access_log /var/log/nginx/{{ site.from }}.log;
error_log /var/log/nginx/{{ site.from }}_error.log;
# Keep the TCP connection open a bit for faster browsing
keepalive_timeout 70;
# Custom error page
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/html;
}
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
proxy_pass {% if not site.to.startswith("http") %}http://{% endif %}{{ site.to }};
include "/etc/nginx/snippets/options-proxypass.conf";
}
}
{% endfor %}

View File

@ -0,0 +1,47 @@
{{ ansible_header | comment }}
{% for dname in reverseproxy.redirect_dnames %}
{% for site in reverseproxy.reverseproxy_sites %}
{% set from = site.from | regex_replace('crans.org', dname) %}
{% set to = site.from %}
{% if from != site.from %}
# Redirect http://{{ from }} to http://{{ to }}
server {
listen 80;
listen [::]:80;
server_name {{ from }};
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 http://{{ to }}$request_uri;
}
}
# Redirect https://{{ from }} to https://{{ to }}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name {{ from }};
# SSL common conf
include "/etc/nginx/snippets/options-ssl.{{ site.ssl|default(nginx.default_ssl_domain) }}.conf";
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 https://{{ to }}$request_uri;
}
}
{% endif %}
{% endfor %}
{% endfor %}

View File

@ -0,0 +1,132 @@
{{ ansible_header | comment }}
# Automatic Connection header for WebSocket support
# See http://nginx.org/en/docs/http/websocket.html
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
{% for upstream in nginx.upstreams -%}
upstream {{ upstream.name }} {
# Path of the server
server {{ upstream.server }};
}
{% endfor -%}
{% if nginx.default_ssl_server -%}
# Redirect all services to the main site
server {
listen 443 default_server ssl;
listen [::]:443 default_server ssl;
include "/etc/nginx/snippets/options-ssl.{{ nginx.default_ssl_domain }}.conf";
server_name _;
charset utf-8;
# Hide Nginx version
server_tokens off;
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 https://{{ nginx.default_ssl_server }}$request_uri;
}
}
{% endif -%}
{% if nginx.default_server -%}
# Redirect all services to the main site
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
charset utf-8;
# Hide Nginx version
server_tokens off;
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 http://{{ nginx.default_server }}$request_uri;
}
}
{% endif -%}
{% for server in nginx.servers %}
{% if server.ssl is defined and server.ssl -%}
# Redirect HTTP to HTTPS
server {
listen 80{% if server.default is defined and server.default %} default_server{% endif %};
listen [::]:80{% if server.default is defined and server.default %} default_server{% endif %};
server_name {{ server.server_name|join(" ") }};
charset utf-8;
# Hide Nginx version
server_tokens off;
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
location / {
return 302 https://$host$request_uri;
}
}
{% endif -%}
server {
{% if server.ssl is defined and server.ssl -%}
listen 443{% if server.default is defined and server.default %} default_server{% endif %} ssl;
listen [::]:443{% if server.default is defined and server.default %} default_server{% endif %} ssl;
include "/etc/nginx/snippets/options-ssl.{{ server.ssl }}.conf";
{% else -%}
listen 80{% if server.default is defined and server.default %} default_server{% endif %};
listen [::]:80{% if server.default is defined and server.default %} default_server{% endif %};
{% endif -%}
server_name {{ server.server_name|join(" ") }};
charset utf-8;
# Hide Nginx version
server_tokens off;
{% for realip in nginx.real_ip_from %}
set_real_ip_from {{ realip }};
{% endfor %}
real_ip_header X-Real-Ip;
{% if server.root is defined %}root {{ server.root }};{% endif %}
{% if server.index is defined %}index {{ server.index|join(" ") }};{% endif %}
{% if server.access_log is defined %}access_log {{ server.access_log }};{% endif %}
{% if server.error_log is defined %}error_log {{ server.error_log }};{% endif %}
{% if server.additional_params is defined %}
{% for param in server.additional_params %}
{{ param }};
{% endfor %}
{% endif %}
{% if server.locations is defined %}
{% for location in server.locations %}
location {{ location.filter }} {
{% for param in location.params %}
{{ param }};
{% endfor %}
}
{% endfor %}
{% endif %}
}
{% endfor %}

View File

@ -0,0 +1,18 @@
{{ ansible_header | comment }}
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info (^/[^/]*)(.*)$;
# check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
# Let NGINX handle errors
fastcgi_intercept_errors on;
include /etc/nginx/fastcgi.conf;
fastcgi_pass unix:/var/run/fcgiwrap.socket;

View File

@ -0,0 +1,19 @@
{{ ansible_header | comment }}
proxy_redirect off;
proxy_set_header Host $host;
# Pass the real client IP
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Tell proxified server that we are HTTPS, fix Wordpress
proxy_set_header X-Forwarded-Proto https;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# For Owncloud WebDav
client_max_body_size 10G;

View File

@ -0,0 +1,17 @@
{{ ansible_header | comment }}
ssl_certificate {{ item.cert }};
ssl_certificate_key {{ item.cert_key }};
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_dhparam /etc/letsencrypt/dhparam;
ssl_protocols TLSv1.2 TLSv1.3;
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_prefer_server_ciphers off;
# Enable OCSP Stapling, point to certificate chain
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate {{ item.trusted_cert }};

View File

@ -0,0 +1,3 @@
#!/usr/bin/tail +14
{{ ansible_header | comment }}
> NGINX a été déployé sur cette machine. Voir /etc/nginx/.

View File

@ -0,0 +1,18 @@
{{ ansible_header | comment('xml') }}
<html>
<head>
<title>Accès refusé</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>Accès refusé</h1>
<p>
Pour éviter le scan des adresses de diffusions par un robot, cette page demande un identifiant et mot de passe.
</p>
<ul>
<li>Identifiant : <em>Stop</em></li>
<li>Mot de passe : <em>Spam</em></li>
</ul>
</body>
</html>

View File

@ -0,0 +1,63 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>502</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
line-height: 1.2;
margin: 0;
}
html {
color: #888;
display: table;
font-family: sans-serif;
height: 100%;
text-align: center;
width: 100%;
}
body {
display: table-cell;
vertical-align: middle;
margin: 2em auto;
}
a {
color: #888;
text-decoration: underline dotted;
}
h1 {
color: #555;
font-size: 2em;
font-weight: 400;
}
p {
margin: 1em auto;
max-width: 480px;
}
@media only screen and (max-width: 280px) {
body, p {
width: 95%;
}
h1 {
font-size: 1.5em;
margin: 0 0 0.3em;
}
}
</style>
</head>
<body>
<h1>502</h1>
<p>Whoops, le service prend trop de temps à répondre…</p>
<p>Essayez de rafraîchir la page. Si le problème persiste, pensez
à contacter <a href="mailto:{{ nginx.contact }}">{{ nginx.who }}</a>.</p>
</body>
</html>

View File

@ -0,0 +1,2 @@
User-agent: *
Disallow: /