From 8df1cba71c4984f42a4657c31558090b6a92e2a7 Mon Sep 17 00:00:00 2001 From: Mark Janssen -- Sig-I/O Automatisering Date: Sun, 14 Jul 2024 21:43:53 +0200 Subject: [PATCH] WIP: generiek nginx role --- authorized_keys/foobar.keys | 4 +- group_vars/all.yaml | 21 ++++--- group_vars/monitoring.yaml | 6 ++ monitoring.yaml | 7 ++- roles/acme/tasks/main.yaml | 2 +- roles/common/tasks/main.yaml | 3 + roles/nginx/defaults/main.yaml | 16 +++++ roles/nginx/handlers/main.yaml | 11 ++++ roles/nginx/tasks/main.yaml | 80 +++++++++++++++++++++++++ roles/nginx/templates/default.j2 | 37 ++++++++++++ roles/nginx/templates/etc-nginx.conf.j2 | 35 +++++++++++ roles/nginx/templates/site.conf.j2 | 36 +++++++++++ roles/nginx/templates/tls_params.j2 | 22 +++++++ snippets/prometheus-nginx.j2 | 13 ++++ 14 files changed, 278 insertions(+), 15 deletions(-) create mode 100644 roles/nginx/defaults/main.yaml create mode 100644 roles/nginx/handlers/main.yaml create mode 100644 roles/nginx/tasks/main.yaml create mode 100644 roles/nginx/templates/default.j2 create mode 100644 roles/nginx/templates/etc-nginx.conf.j2 create mode 100644 roles/nginx/templates/site.conf.j2 create mode 100644 roles/nginx/templates/tls_params.j2 create mode 100644 snippets/prometheus-nginx.j2 diff --git a/authorized_keys/foobar.keys b/authorized_keys/foobar.keys index 6493dc3..f7fac20 100644 --- a/authorized_keys/foobar.keys +++ b/authorized_keys/foobar.keys @@ -1,2 +1,2 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUIAkaRsvb6cD1XIGF80JpMH1mYE9XhCgptOkt9AfloZQlO7Ds5XeCwJk5/TsoidTcb/0yFUov8SMwaIVtrFfkNUqqeAsfm3luJ4JwOXeCwrXD6W7c5Wqg/FGNH0eZr0kEnxpNS10L72+oNBQgnlSNjqWS29lEmXApKQ3IKy6aP9cMwEh25fsH/2G7mHsZX2UMPK0tZPC6MPxY5P9PWLIulUpsX96c6OcAvGYIvsCnecsVsTdhK36w4Z/t7XoLFz5X6k3eXT7gG4SMGuBixjroTUhumWzgJJ6T1Nn/eESe7Im8krlzO/0hG/F8uBy3s04TAJuXFmygvtC4YLyq91U5 -ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICyKprIcR81+RFSBxU3iyW4vd0ctr0q1Pqifzxbro+0C +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUIAkaRsvb6cD1XIGF80JpMH1mYE9XhCgptOkt9AfloZQlO7Ds5XeCwJk5/TsoidTcb/0yFUov8SMwaIVtrFfkNUqqeAsfm3luJ4JwOXeCwrXD6W7c5Wqg/FGNH0eZr0kEnxpNS10L72+oNBQgnlSNjqWS29lEmXApKQ3IKy6aP9cMwEh25fsH/2G7mHsZX2UMPK0tZPC6MPxY5P9PWLIulUpsX96c6OcAvGYIvsCnecsVsTdhK36w4Z/t7XoLFz5X6k3eXT7gG4SMGuBixjroTUhumWzgJJ6T1Nn/eESe7Im8krlzO/0hG/F8uBy3s04TAJuXFmygvtC4YLyq91U5 Sig-I/O Beheer key +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICyKprIcR81+RFSBxU3iyW4vd0ctr0q1Pqifzxbro+0C mark@x240-ed25519 diff --git a/group_vars/all.yaml b/group_vars/all.yaml index bdafa45..fd209d8 100644 --- a/group_vars/all.yaml +++ b/group_vars/all.yaml @@ -6,22 +6,25 @@ notify_email: bestuur@bitlair.nl acme_bootstrap_certs: no trusted_ranges: # localhost - - { v: ipv4, cidr: 127.0.0.1/8 } + - { v: ipv4, cidr: "127.0.0.1/8" } - { v: ipv6, cidr: "::1" } # rf1928 - - { v: ipv4, cidr: 10.0.0.0/8 } - - { v: ipv4, cidr: 172.16.0.0/12 } - - { v: ipv4, cidr: 192.168.0.0/16 } + - { v: ipv4, cidr: "10.0.0.0/8" } + - { v: ipv4, cidr: "172.16.0.0/12" } + - { v: ipv4, cidr: "192.168.0.0/16" } # v6 local - { v: ipv6, cidr: "fe80::/10" } # vihamij - - { v: ipv4, cidr: 45.88.49.140 } + - { v: ipv4, cidr: "45.88.49.140" } # eventinfra - - { v: ipv4, cidr: 204.2.64.0/20 } - - - { v: ipv4, cidr: 100.64.0.0/10 } - - { v: ipv4, cidr: 185.205.52.194/32 } + - { v: ipv4, cidr: "204.2.64.0/20" } + # bitlair + - { v: ipv4, cidr: "100.64.0.0/10" } + - { v: ipv4, cidr: "185.205.52.194/32" } - { v: ipv6, cidr: "2a02:166b:92::/48" } + # foobar + - { v: ipv4, cidr: "31.187.251.213/32" } + - { v: ipv6, cidr: "2a0e:5700:4:2::/64" } root_access: - ak diff --git a/group_vars/monitoring.yaml b/group_vars/monitoring.yaml index b692290..51d9b97 100644 --- a/group_vars/monitoring.yaml +++ b/group_vars/monitoring.yaml @@ -40,3 +40,9 @@ prometheus_scrape_configs: target_label: instance - target_label: __address__ replacement: "{{ blackbox_exporter_web_listen_address }}" + +nginx_sites: + - server_name: "dashboard.bitlair.nl" + localproxy: "9000" + snippets: + - "prometheus-nginx.j2" diff --git a/monitoring.yaml b/monitoring.yaml index 9ad8623..9e05df0 100644 --- a/monitoring.yaml +++ b/monitoring.yaml @@ -2,6 +2,7 @@ - hosts: monitoring roles: - - common - - acme - - monitoring + - { role: "common", tags: [ "common" ] } + - { role: "acme", tags: [ "acme" ] } + - { role: "nginx", tags: [ "nginx" ] } + - { role: "monitoring", tags: [ "monitoring" ] } diff --git a/roles/acme/tasks/main.yaml b/roles/acme/tasks/main.yaml index 229f566..0be3133 100644 --- a/roles/acme/tasks/main.yaml +++ b/roles/acme/tasks/main.yaml @@ -23,7 +23,7 @@ owner: "{{ item.owner | default('root') }}" group: "{{ item.group | default('root') }}" mode: "{{ item.mode | default('0640') }}" - notify: "{{ item.notify | default([]) }}" + notify: "{{ item.notify | default([]) }}" with_items: - { src: "config.sh", dest: "/etc/dehydrated/conf.d/ansible.sh", mode: '0755' } - { src: "deploy.sh", dest: "/etc/dehydrated/conf.d/deploy.sh", mode: '0755' } diff --git a/roles/common/tasks/main.yaml b/roles/common/tasks/main.yaml index 10ce3a1..d20da44 100644 --- a/roles/common/tasks/main.yaml +++ b/roles/common/tasks/main.yaml @@ -18,6 +18,7 @@ - { src: "sources.list.j2", dest: "/etc/apt/sources.list" } - { src: "apt-auto-upgrades.j2", dest: "/etc/apt/apt.conf.d/20auto-upgrades" } - { src: "apt-unattended-upgrades.j2", dest: "/etc/apt/apt.conf.d/50unattended-upgrades" } + register: aptconfig when: - ansible_os_family == "Debian" tags: @@ -56,6 +57,8 @@ - name: Install standard packages ansible.builtin.apt: + cache_valid_time: 3600 + update_cache: "{{ aptconfig.changed | bool | default(false) }}" pkg: - curl - fzf diff --git a/roles/nginx/defaults/main.yaml b/roles/nginx/defaults/main.yaml new file mode 100644 index 0000000..b9e4710 --- /dev/null +++ b/roles/nginx/defaults/main.yaml @@ -0,0 +1,16 @@ +--- + +nginx_package: "nginx-light" +nginx_user: "www-data" +nginx_modules_dir: "/etc/nginx/modules-enabled" + + +nginx_tls_version: "TLSv1.2 TLSv1.3" +nginx_tls_cipherlist: "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:!SHA:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS" +nginx_tls_curve: "prime256v1:secp384r1" +nginx_tls_cache_size: "10m" +nginx_tls_session_timeout: "1h" +nginx_ssl_stapling: "on" +nginx_ssl_stapling_verify: "on" +nginx_wk_acme: "/var/lib/dehydrated/acme-challenges" + diff --git a/roles/nginx/handlers/main.yaml b/roles/nginx/handlers/main.yaml new file mode 100644 index 0000000..e9738d0 --- /dev/null +++ b/roles/nginx/handlers/main.yaml @@ -0,0 +1,11 @@ +--- + +- name: Reload nginx + ansible.builtin.systemd: + name: nginx + state: reloaded + enabled: true + listen: "Reload app-services" + when: + - nginx_sites is defined + diff --git a/roles/nginx/tasks/main.yaml b/roles/nginx/tasks/main.yaml new file mode 100644 index 0000000..78f6f9b --- /dev/null +++ b/roles/nginx/tasks/main.yaml @@ -0,0 +1,80 @@ +--- + +- name: Install nginx base package + ansible.builtin.apt: + name: "{{ nginx_package }}" + state: present + when: + - nginx_sites is defined + +- name: Create sites-available / sites-enabled directories + ansible.builtin.file: + state: directory + path: "{{ item.path }}" + owner: "{{ item.owner | default('root') }}" + group: "{{ item.group | default('root') }}" + mode: "{{ item.mode | default('0755') }}" + with_items: + - { path: "/etc/nginx/sites-available" } + - { path: "/etc/nginx/sites-enabled" } + notify: Reload nginx + when: + - nginx_sites is defined + +- name: Template default nginx config files + ansible.builtin.template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: "{{ item.owner | default('root') }}" + group: "{{ item.group | default('root') }}" + mode: "{{ item.mode | default('0644') }}" + force: "{{ item.force | default('yes') }}" + backup: true + loop_control: + label: "{{ item.dest }}" + with_items: + - { src: "etc-nginx.conf.j2", dest: "/etc/nginx/nginx.conf", notify: "Reload nginx" } + - { src: "tls_params.j2", dest: "/etc/nginx/tls_params", notify: "Reload nginx" } + - { src: "default.j2", dest: "/etc/nginx/sites-available/default", notify: "Reload nginx" } +# - { src: "dhparam.pem.j2", dest: "{{ nginx_dhparams_file }}", notify: "Reload nginx" } +# - { src: "check_nginx.j2", dest: "{{ nagios_plugin_location }}/check_nginx", mode: '755' } +# - { src: "nrpe-check_nginx.j2", dest: "/etc/nagios/nrpe.d/10-nginx.cfg", notify: "Restart nrpe" } + notify: "{{ item.notify | default(omit) }}" + when: + - nginx_sites is defined + +- name: Template site-specific configs + ansible.builtin.template: + src: "site.conf.j2" + dest: "/etc/nginx/sites-available/{{ site.server_name }}.conf" + owner: "{{ site.owner | default('root') }}" + group: "{{ site.group | default('root') }}" + mode: "{{ site.mode | default('0644') }}" + force: "{{ site.force | default('yes') }}" + backup: true + loop: "{{ nginx_sites }}" + loop_control: + loop_var: site + label: "{{ site.server_name }}" + notify: Reload nginx + when: + - nginx_sites is defined + tags: + - nginxextra + - nginx_site + +- name: Enable nginx sites + ansible.builtin.file: + src: "/etc/nginx/sites-available/{{ site.server_name }}.conf" + path: "/etc/nginx/sites-enabled/{{ site.server_name }}.conf" + state: "{% if site.disabled | default(false) %}absent{% else %}link{% endif %}" + mode: "0644" + loop: "{{ nginx_sites }}" + loop_control: + loop_var: site + label: "{{ site.server_name }}" + notify: Reload nginx + when: + - nginx_sites is defined + ignore_errors: "{{ ansible_check_mode }}" + diff --git a/roles/nginx/templates/default.j2 b/roles/nginx/templates/default.j2 new file mode 100644 index 0000000..b417134 --- /dev/null +++ b/roles/nginx/templates/default.j2 @@ -0,0 +1,37 @@ +# {{ ansible_managed }} + +server { + listen 80 default_server; + listen [::]:80 + + server_name {{ inventory_hostname }}; + + # Accept ACME-Challenges over http + location ^~ /.well-known/acme-challenge/ { + alias {{ nginx_wk_acme }}/; + } + + # Block .ht files + location ~ /\.ht { + deny all; + } + + # Redirect everything to https by default + location / { + return 301 https://$host$request_uri; + } + + location /server_status { + # Enable Nginx stats + stub_status on; + # Only allow access from localhost + allow 127.0.0.1; + # Other request should be denied + deny all; + } +} + +{% for line in nginx_default_extra | default([]) %} +{{ line }} +{% endfor %} + diff --git a/roles/nginx/templates/etc-nginx.conf.j2 b/roles/nginx/templates/etc-nginx.conf.j2 new file mode 100644 index 0000000..b4d4d7a --- /dev/null +++ b/roles/nginx/templates/etc-nginx.conf.j2 @@ -0,0 +1,35 @@ +# {{ ansible_managed }} + +user {{ nginx_user }}; +worker_processes auto; +pid /run/nginx.pid; +worker_rlimit_nofile 16384; +include {{ nginx_modules_dir }}/*.conf; + +http { + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + server_tokens off; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Default nginx log format with $request time added + log_format bitlair '$remote_addr - $remote_user [$time_local] ' + '"$request" $status $body_bytes_sent ' + '"$http_referer" "$http_user_agent" $request_time'; + access_log /var/log/nginx/access.log bitlair; + + gzip on; + gzip_disable "msie6"; + +{% for line in nginx_http_extra | default([]) %} + {{ line }} +{% endfor %} + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; +} diff --git a/roles/nginx/templates/site.conf.j2 b/roles/nginx/templates/site.conf.j2 new file mode 100644 index 0000000..09e4e0c --- /dev/null +++ b/roles/nginx/templates/site.conf.j2 @@ -0,0 +1,36 @@ +# {{ ansible_managed }} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + + server_name {{ site.server_name|default(inventory_hostname) }}{% if site.server_alias is defined %} {{ site.server_alias }}{% endif %}; + + include /etc/nginx/tls_params; + ssl_certificate /var/lib/dehydrated/certs/{{ site.server_name }}/fullchain.pem; + ssl_certificate_key /var/lib/dehydrated/certs/{{ site.server_name }}/fullkey.pem; + + location ~ /\.ht { + deny all; + } + + access_log /var/log/nginx/{{ site.server_name }}.access.log bitlair; + error_log /var/log/nginx/{{ site.server_name }}.error.log; + +{% if site.localproxy is defined %} + location / { + proxy_pass http://localhost:{{ site.localproxy }}/; + include proxy_params; + } +{% endif %} + + # Include snippets +{% for file in site.snippets | default([]) %} +{% include "../../../snippets/" . file %} +{% endif %} + + # Per site configuration +{% for line in site.config | default([]) %} + {{ line }} +{% endfor %} +} diff --git a/roles/nginx/templates/tls_params.j2 b/roles/nginx/templates/tls_params.j2 new file mode 100644 index 0000000..7abe3b6 --- /dev/null +++ b/roles/nginx/templates/tls_params.j2 @@ -0,0 +1,22 @@ +# {{ ansible_managed }} + +ssl_session_timeout {{ nginx_tls_session_timeout }}; +ssl_session_tickets off; + +ssl_prefer_server_ciphers on; +ssl_session_cache shared:SSL:{{ nginx_tls_cache_size }}; + +ssl_protocols {{ nginx_tls_version }}; +ssl_ciphers {{ nginx_tls_cipherlist }}; +ssl_ecdh_curve {{ nginx_tls_curve }}; + +# HSTS (ngx_http_headers_module is required) (63072000 seconds) +add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; +add_header X-Frame-Options "sameorigin"; +add_header X-Content-Type-Options "nosniff"; +add_header X-Robots-Tag noindex; + +# OCSP stapling +ssl_stapling {{ nginx_ssl_stapling }}; +ssl_stapling_verify {{ nginx_ssl_stapling_verify }}; + diff --git a/snippets/prometheus-nginx.j2 b/snippets/prometheus-nginx.j2 new file mode 100644 index 0000000..a38e527 --- /dev/null +++ b/snippets/prometheus-nginx.j2 @@ -0,0 +1,13 @@ +# dashboard nginx config snippet + +location /prometheus/ { + proxy_pass http://localhost:9090/prometheus/; + include proxy_params; + +{% for host in bitlair_ip_whitelist %} + allow {{ host }}; +{% endif %} + allow "127.0.0.0/8" + allow "::1"; + deny all; +}