diff options
author | clarkzjw <[email protected]> | 2023-02-08 00:40:09 -0800 |
---|---|---|
committer | clarkzjw <[email protected]> | 2023-02-08 00:40:09 -0800 |
commit | 1204730924436ef9e1c7c49c9557837f9a5ed0e8 (patch) | |
tree | 129d79dfd11245751cee6d4082ff5d2f6e941610 /ansible/roles/nginx | |
parent | 9635ac4dedf69de5bff65785bcc16bef80b52d75 (diff) | |
download | mail-master.tar.gz |
Diffstat (limited to 'ansible/roles/nginx')
-rw-r--r-- | ansible/roles/nginx/defaults/main.yml | 6 | ||||
-rw-r--r-- | ansible/roles/nginx/files/conf.d/http.custom.conf | 84 | ||||
-rw-r--r-- | ansible/roles/nginx/files/tls/ssl_ciphers_intermediate | 10 | ||||
-rw-r--r-- | ansible/roles/nginx/files/tls/ssl_ciphers_modern | 7 | ||||
-rw-r--r-- | ansible/roles/nginx/files/tls/ssl_ciphers_tls13 | 7 | ||||
-rw-r--r-- | ansible/roles/nginx/files/tls/ssl_params | 55 | ||||
-rw-r--r-- | ansible/roles/nginx/handlers/main.yml | 3 | ||||
-rw-r--r-- | ansible/roles/nginx/tasks/main.yml | 118 | ||||
-rw-r--r-- | ansible/roles/nginx/templates/basic-site.conf.j2 | 68 |
9 files changed, 358 insertions, 0 deletions
diff --git a/ansible/roles/nginx/defaults/main.yml b/ansible/roles/nginx/defaults/main.yml new file mode 100644 index 0000000..34ac696 --- /dev/null +++ b/ansible/roles/nginx/defaults/main.yml | |||
@@ -0,0 +1,6 @@ | |||
1 | --- | ||
2 | nginx: | ||
3 | # Don't disable anything by default, but provide it here | ||
4 | # so you don't need to include empty 'disabled' in your own | ||
5 | # host configs. | ||
6 | disabled: [] | ||
diff --git a/ansible/roles/nginx/files/conf.d/http.custom.conf b/ansible/roles/nginx/files/conf.d/http.custom.conf new file mode 100644 index 0000000..5f70166 --- /dev/null +++ b/ansible/roles/nginx/files/conf.d/http.custom.conf | |||
@@ -0,0 +1,84 @@ | |||
1 | |||
2 | ## Proxy options | ||
3 | proxy_buffering on; | ||
4 | # proxy_cache_min_uses 3; | ||
5 | proxy_cache_path /var/nginx/proxy-cache/ levels=1:2 keys_zone=cache:10m inactive=10m max_size=1000M; | ||
6 | proxy_cache_valid any 10m; | ||
7 | proxy_ignore_client_abort off; | ||
8 | proxy_intercept_errors on; | ||
9 | proxy_next_upstream error timeout invalid_header; | ||
10 | proxy_redirect off; | ||
11 | proxy_set_header Host $host; | ||
12 | proxy_set_header X-Forwarded-For $remote_addr; | ||
13 | proxy_connect_timeout 60; | ||
14 | proxy_send_timeout 60; | ||
15 | proxy_read_timeout 60; | ||
16 | |||
17 | # We used to use this header when we ran dual http/https stacks to verify | ||
18 | # user login pages were being only requested over https, but now we forward | ||
19 | # every site to https, so we can assume our schemes are aligned to our interests | ||
20 | # (as long as all our backend code stopped checking for X-Forwarded-Proto too). | ||
21 | #proxy_set_header X-Forwarded-Proto $scheme; | ||
22 | |||
23 | ## Size Limits | ||
24 | # May need to override these (server or location blocks) if doing large uploads. | ||
25 | # Setting to zero disables any size checking. | ||
26 | client_body_buffer_size 16k; | ||
27 | client_max_body_size 15m; | ||
28 | |||
29 | # If clients send headers larger than 1k, | ||
30 | # they get upgraded to large_client_header_buffers. | ||
31 | client_header_buffer_size 1k; | ||
32 | large_client_header_buffers 32 64k; | ||
33 | |||
34 | ## Timeouts | ||
35 | client_body_timeout 5s; | ||
36 | client_header_timeout 5s; | ||
37 | keepalive_timeout 5s 5s; | ||
38 | #keepalive_timeout 0; | ||
39 | send_timeout 5s; | ||
40 | |||
41 | ## General Options | ||
42 | ignore_invalid_headers on; | ||
43 | recursive_error_pages on; | ||
44 | #sendfile on; # enabled by top level config | ||
45 | server_name_in_redirect off; | ||
46 | server_tokens off; | ||
47 | |||
48 | # For per-client rate limiting, see config options at: | ||
49 | # https://nginx.org/en/docs/http/ngx_http_limit_conn_module.html | ||
50 | |||
51 | ## Compression | ||
52 | #gzip on; # enabled by top level config | ||
53 | gzip_static on; | ||
54 | gzip_buffers 16 32k; | ||
55 | gzip_comp_level 6; | ||
56 | gzip_http_version 1.0; | ||
57 | gzip_min_length 500; | ||
58 | gzip_types text/plain application/x-javascript text/xml text/css image/x-icon application/xml application/xml+rss text/javascript application/javascript application/json image/svg+xml font/truetype font/opentype application/vnd.ms-fontobject; | ||
59 | gzip_vary on; | ||
60 | gzip_proxied any; # required for cloudfront to receive a gzip'd response | ||
61 | |||
62 | ## Filesystem Operation Cache (caches fds, sizes, times, errors, etc) | ||
63 | open_file_cache max=6000 inactive=5m; | ||
64 | open_file_cache_valid 2m; | ||
65 | open_file_cache_min_uses 1; | ||
66 | open_file_cache_errors on; | ||
67 | |||
68 | # For reading a response from disk | ||
69 | output_buffers 32 32k; | ||
70 | |||
71 | ## Optimize Large File Transfers (can be overriden in hosts and locations) | ||
72 | aio threads; # use default thread pool, create thread pools: threads=NAME; | ||
73 | aio_write on; # use threaded writes for temporary files and proxied data | ||
74 | |||
75 | # For files larger than 8 MB, use O_DIRECT instead of sendfile() | ||
76 | directio 8m; | ||
77 | directio_alignment 512; # if using XFS, set as 4096 | ||
78 | |||
79 | ## Access Log Caches | ||
80 | open_log_file_cache max=64 inactive=20s min_uses=1 valid=1m; | ||
81 | |||
82 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' | ||
83 | '$status $body_bytes_sent "$http_referer" ' | ||
84 | '"$http_user_agent" "$http_x_forwarded_for"'; | ||
diff --git a/ansible/roles/nginx/files/tls/ssl_ciphers_intermediate b/ansible/roles/nginx/files/tls/ssl_ciphers_intermediate new file mode 100644 index 0000000..bc79954 --- /dev/null +++ b/ansible/roles/nginx/files/tls/ssl_ciphers_intermediate | |||
@@ -0,0 +1,10 @@ | |||
1 | # From https://mozilla.github.io/server-side-tls/ssl-config-generator/ | ||
2 | # as of 2018-07-12 | ||
3 | |||
4 | # No TLSv1.3 support yet! | ||
5 | |||
6 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | ||
7 | ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305: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-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; | ||
8 | |||
9 | # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits | ||
10 | ssl_dhparam /etc/ssl/ffdhe2048.pem; | ||
diff --git a/ansible/roles/nginx/files/tls/ssl_ciphers_modern b/ansible/roles/nginx/files/tls/ssl_ciphers_modern new file mode 100644 index 0000000..ab93ffc --- /dev/null +++ b/ansible/roles/nginx/files/tls/ssl_ciphers_modern | |||
@@ -0,0 +1,7 @@ | |||
1 | # From https://mozilla.github.io/server-side-tls/ssl-config-generator/ | ||
2 | # as of 2018-07-12 | ||
3 | |||
4 | # No TLSv1.3 support yet! | ||
5 | |||
6 | ssl_protocols TLSv1.2; | ||
7 | ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; | ||
diff --git a/ansible/roles/nginx/files/tls/ssl_ciphers_tls13 b/ansible/roles/nginx/files/tls/ssl_ciphers_tls13 new file mode 100644 index 0000000..db04c36 --- /dev/null +++ b/ansible/roles/nginx/files/tls/ssl_ciphers_tls13 | |||
@@ -0,0 +1,7 @@ | |||
1 | # From https://github.com/cloudflare/sslconfig/blob/796bc5ac7224f1e540394d792323ccafa86aaeea/conf | ||
2 | |||
3 | # nginx >= 1.11.0 (2016-05-24) created the 'ssl_ecdh_curve' parameter | ||
4 | |||
5 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; | ||
6 | ssl_ecdh_curve X25519:P-256:P-384:P-224:P-521; | ||
7 | ssl_ciphers '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]:ECDHE+AES128:RSA+AES128:ECDHE+AES256:RSA+AES256:ECDHE+3DES:RSA+3DES'; | ||
diff --git a/ansible/roles/nginx/files/tls/ssl_params b/ansible/roles/nginx/files/tls/ssl_params new file mode 100644 index 0000000..37798fc --- /dev/null +++ b/ansible/roles/nginx/files/tls/ssl_params | |||
@@ -0,0 +1,55 @@ | |||
1 | # Test OCSP with: | ||
2 | # openssl s_client -connect $site:443 -tls1 -tlsextdebug -status | ||
3 | # | ||
4 | # also test with: | ||
5 | # openssl s_client -connect $site:443 -CAfile /etc/ssl/certs/ca-certificates.crt -showcerts -status -tlsextdebug -cipher RSA </dev/null | ||
6 | # | ||
7 | # openssl s_client -connect $site:443 -CAfile /etc/ssl/certs/ca-certificates.crt -showcerts -status -tlsextdebug -cipher ECDSA </dev/null | ||
8 | |||
9 | |||
10 | # Duration client SSL session tickets are valid for: | ||
11 | ssl_session_timeout 1d; | ||
12 | # NOTE NOTE NOTE NOTE NOTE | ||
13 | # nginx only regenerates its ssl_session_ticket_key on reload or restart. | ||
14 | # the ticket key is basically a symmetric key that effectively breaks | ||
15 | # forward secrecy if leaked. | ||
16 | # With ssl_session_tickets enabled, you should reload nginx daily to reset | ||
17 | # the internal cached ticket key. | ||
18 | # If you are using external ticket keys, those should also be rotated daily. | ||
19 | # END NOTE END NOTE END NOTE | ||
20 | |||
21 | # Internal cache of SSL sessions | ||
22 | ssl_session_cache shared:SSL:500m; # 500MB = 2M cached sessions (4k sessions/MB) | ||
23 | |||
24 | # session tickets are reused for the life of the server. | ||
25 | # For multiple servers serving the same host, | ||
26 | # have them all share the same key and rotate as necessary: | ||
27 | # ssl_session_ticket_key [keyfile]; | ||
28 | # Without a ticket key file defined, a reload of nginx resets the key. | ||
29 | ssl_session_tickets on; | ||
30 | |||
31 | # Individual cipher files are included externally | ||
32 | # (one of ssl_ciphers_{intermediate,modern}) | ||
33 | ssl_prefer_server_ciphers on; | ||
34 | |||
35 | # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) | ||
36 | add_header Strict-Transport-Security "max-age=15768000; includeSubdomains"; | ||
37 | |||
38 | # OCSP Stapling --- | ||
39 | # fetch OCSP records from URL in ssl_certificate and cache them | ||
40 | ssl_stapling on; | ||
41 | ssl_stapling_verify on; | ||
42 | |||
43 | # See: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_trusted_certificate | ||
44 | ssl_trusted_certificate /etc/ssl/lets-encrypt-x3-cross-signed.pem; | ||
45 | |||
46 | # Instead of using resolver, take response from file: | ||
47 | # ssl_stapling_file <-- must be set PER domain, but nginx so far has refused | ||
48 | # to add the ability to have one stapling file per certificate now that nginx | ||
49 | # supports both RSA and EC per domain. So, this is useless if you have multiple | ||
50 | # certificates per domain. | ||
51 | |||
52 | # 'valid' ignores DNS TTL and caches lookups for specified duration | ||
53 | # This should be replaced with a local dnsmasq resolver at 127.0.0.1 | ||
54 | resolver 127.0.0.53 4.2.2.2 8.8.8.8 1.1.1.1 valid=600s ipv6=off; | ||
55 | resolver_timeout 4s; | ||
diff --git a/ansible/roles/nginx/handlers/main.yml b/ansible/roles/nginx/handlers/main.yml new file mode 100644 index 0000000..31f8ade --- /dev/null +++ b/ansible/roles/nginx/handlers/main.yml | |||
@@ -0,0 +1,3 @@ | |||
1 | --- | ||
2 | - name: reload nginx | ||
3 | command: nginx -s reload | ||
diff --git a/ansible/roles/nginx/tasks/main.yml b/ansible/roles/nginx/tasks/main.yml new file mode 100644 index 0000000..73469a1 --- /dev/null +++ b/ansible/roles/nginx/tasks/main.yml | |||
@@ -0,0 +1,118 @@ | |||
1 | --- | ||
2 | - name: emerge, nginx with extra modules! | ||
3 | apt: | ||
4 | pkg: nginx-extras | ||
5 | state: latest | ||
6 | |||
7 | # Keep 32 logs | ||
8 | - name: adjust nginx logrotate keep files | ||
9 | lineinfile: | ||
10 | state: present | ||
11 | path: /etc/logrotate.d/nginx | ||
12 | regexp: "^(\\s+)rotate " | ||
13 | line: "\\1rotate 32" | ||
14 | backrefs: yes | ||
15 | |||
16 | # And only rotate when they grow larger than 1 GB | ||
17 | - name: adjust nginx logrotate trigger rolls | ||
18 | lineinfile: | ||
19 | state: present | ||
20 | path: /etc/logrotate.d/nginx | ||
21 | regexp: "minsize" | ||
22 | line: "minsize 1G" | ||
23 | insertafter: "rotate \\d+" | ||
24 | |||
25 | - name: verify nginx isn't serving default pages | ||
26 | file: | ||
27 | path: /etc/nginx/sites-enabled/default | ||
28 | state: absent | ||
29 | notify: | ||
30 | - reload nginx | ||
31 | |||
32 | - name: verify nginx proxy cache dir exists | ||
33 | file: | ||
34 | path: /var/nginx/proxy-cache | ||
35 | owner: www-data | ||
36 | state: directory | ||
37 | |||
38 | - name: verify nginx cpu affinity | ||
39 | lineinfile: | ||
40 | state: present | ||
41 | path: /etc/nginx/nginx.conf | ||
42 | regexp: "^worker_cpu_affinity " | ||
43 | line: "worker_cpu_affinity auto;" | ||
44 | insertafter: '^worker_processes ' | ||
45 | notify: | ||
46 | - reload nginx | ||
47 | |||
48 | - name: drop keepalive from nginx conf because we set it custom | ||
49 | lineinfile: | ||
50 | state: absent | ||
51 | path: /etc/nginx/nginx.conf | ||
52 | regexp: "^\\s+keepalive_timeout" | ||
53 | notify: | ||
54 | - reload nginx | ||
55 | |||
56 | - name: copy config extensions | ||
57 | copy: | ||
58 | src: conf.d | ||
59 | dest: /etc/nginx/ | ||
60 | notify: | ||
61 | - reload nginx | ||
62 | |||
63 | - name: copy shared tls settings | ||
64 | copy: | ||
65 | src: tls/ | ||
66 | dest: /etc/nginx/ | ||
67 | notify: | ||
68 | - reload nginx | ||
69 | |||
70 | - name: generate our templated basic sites | ||
71 | template: | ||
72 | src: basic-site.conf.j2 | ||
73 | dest: "/etc/nginx/sites-available/{{ item.domain }}" | ||
74 | loop: "{{ nginx.basic }}" | ||
75 | notify: | ||
76 | - reload nginx | ||
77 | |||
78 | - name: copy our more complex sites we don't want templated | ||
79 | copy: | ||
80 | src: "servers/{{ item }}" | ||
81 | dest: /etc/nginx/sites-available/ | ||
82 | loop: "{{ nginx.complex }}" | ||
83 | notify: | ||
84 | - reload nginx | ||
85 | |||
86 | - name: activate our nginx site configs | ||
87 | file: | ||
88 | src: "/etc/nginx/sites-available/{{ item }}" | ||
89 | dest: "/etc/nginx/sites-enabled/{{ item }}" | ||
90 | state: link | ||
91 | loop: "{{ nginx.complex }}" | ||
92 | notify: | ||
93 | - reload nginx | ||
94 | |||
95 | - name: activate our nginx site templates | ||
96 | file: | ||
97 | src: "/etc/nginx/sites-available/{{ item.domain }}" | ||
98 | dest: "/etc/nginx/sites-enabled/{{ item.domain }}" | ||
99 | state: link | ||
100 | loop: "{{ nginx.basic }}" | ||
101 | notify: | ||
102 | - reload nginx | ||
103 | |||
104 | - name: remove disabled sites | ||
105 | file: | ||
106 | src: "/etc/nginx/sites-enabled/{{ item }}" | ||
107 | state: absent | ||
108 | loop: "{{ nginx.disabled | default([]) }}" | ||
109 | notify: | ||
110 | - reload nginx | ||
111 | |||
112 | - name: reload if certs newish | ||
113 | include_role: | ||
114 | name: certreload | ||
115 | vars: | ||
116 | certreload: | ||
117 | notifiers: | ||
118 | - reload nginx | ||
diff --git a/ansible/roles/nginx/templates/basic-site.conf.j2 b/ansible/roles/nginx/templates/basic-site.conf.j2 new file mode 100644 index 0000000..454b2bd --- /dev/null +++ b/ansible/roles/nginx/templates/basic-site.conf.j2 | |||
@@ -0,0 +1,68 @@ | |||
1 | server { | ||
2 | listen {{ item.domain }}:443 ssl http2 fastopen=4096 reuseport; | ||
3 | server_name {{ item.domain }}; | ||
4 | |||
5 | access_log /var/log/nginx/{{ item.domain }}.access.log main buffer=32k; | ||
6 | error_log /var/log/nginx/{{ item.domain }}.error.log error; | ||
7 | |||
8 | ssl on; | ||
9 | |||
10 | include /etc/nginx/ssl_params; | ||
11 | |||
12 | {% if nginx.ssl == "modern" %} | ||
13 | include /etc/nginx/ssl_ciphers_modern; | ||
14 | {% elif nginx.ssl == "tls13" %} | ||
15 | include /etc/nginx/ssl_ciphers_tls13; | ||
16 | {% else %} | ||
17 | # Default, just use commonly accepted options: | ||
18 | include /etc/nginx/ssl_ciphers_intermediate; | ||
19 | {% endif %} | ||
20 | |||
21 | ssl_certificate /etc/ssl/{{ item.domain }}-cert-combined.rsa2048.pem; | ||
22 | ssl_certificate_key /etc/ssl/private/{{ item.domain }}-key.rsa2048.pem; | ||
23 | |||
24 | # nginx >= 1.11.0 (2016-05-24) allows loading redundant certs and keys so you | ||
25 | # can serve modern EC clients and less modern RSA clients at the same time. | ||
26 | ssl_certificate /etc/ssl/{{ item.domain }}-cert-combined.prime256v1.pem; | ||
27 | ssl_certificate_key /etc/ssl/private/{{ item.domain }}-key.prime256v1.pem; | ||
28 | |||
29 | root /srv/web/{{ item.domain }}; | ||
30 | |||
31 | {% if nginx.google is defined %} | ||
32 | location /{{ nginx.google.siteKey }}.html { | ||
33 | root {{ nginx.google.siteKeyServeDir }}}; | ||
34 | } | ||
35 | {% endif %} | ||
36 | |||
37 | {% if item.customConfig is defined %} | ||
38 | {{ item.customConfig }} | ||
39 | {% endif %} | ||
40 | |||
41 | {% for location in item.uri %} | ||
42 | location {{ location.path }} { | ||
43 | {% if location.appServer is defined %} | ||
44 | proxy_pass {{ location.appServer }}/$request_uri; | ||
45 | proxy_set_header Host $host; | ||
46 | {% else %} | ||
47 | root /srv/web/{{ item.domain }}; | ||
48 | {% endif %} | ||
49 | } | ||
50 | {% endfor %} | ||
51 | } | ||
52 | |||
53 | server { | ||
54 | listen {{ item.domain }} fastopen=4096 reuseport; | ||
55 | server_name www.{{ item.domain }} {{ item.domain }}; | ||
56 | |||
57 | access_log /var/log/nginx/{{ item.domain }}.access.log main buffer=32k; | ||
58 | error_log /var/log/nginx/{{ item.domain }}.error.log error; | ||
59 | |||
60 | location /.well-known/acme-challenge/ { | ||
61 | alias /srv/web/challenges/; | ||
62 | try_files $uri =404; | ||
63 | } | ||
64 | |||
65 | location / { | ||
66 | return 301 https://{{ item.domain }}$request_uri; | ||
67 | } | ||
68 | } | ||