From 804b2e3c6c9a9437fc83c7e5c22ceb28f879115a Mon Sep 17 00:00:00 2001 From: DarkFeather Date: Tue, 2 Dec 2025 14:43:49 -0600 Subject: [PATCH 1/3] Adding geoip module and instituting a deny variable for vhosts to consume --- examples/msn0.yml | 2 ++ precommit-hooks/find-passwords-in-files | 1 + roles/WebServer/files/nginx.conf | 3 +++ roles/WebServer/tasks/main.yml | 24 ++++++++++++++++- roles/WebServer/templates/conf/geoip.conf.j2 | 28 ++++++++++++++++++++ 5 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 roles/WebServer/templates/conf/geoip.conf.j2 diff --git a/examples/msn0.yml b/examples/msn0.yml index 8ef73a6..8cc8e8f 100644 --- a/examples/msn0.yml +++ b/examples/msn0.yml @@ -43,6 +43,8 @@ all: ciphersuite: "!NULL:!SSLv2:!SSLv3:!TLSv1:EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH" Aether_nodes: - DedSec.msn0.aninix.net + operational_countries: + - 'US' children: managed: diff --git a/precommit-hooks/find-passwords-in-files b/precommit-hooks/find-passwords-in-files index 197397d..a136147 100644 --- a/precommit-hooks/find-passwords-in-files +++ b/precommit-hooks/find-passwords-in-files @@ -30,6 +30,7 @@ if [ $? -ne 1 ]; then fi IFS=" " + for i in `ansible-vault decrypt --output - ${ANSIBLE_VAULT_FILE} | sed 's/\s\?-\?\s\?[A-Za-z0-9_]\+://' | grep -vE '\||password|^\s\?$|#|https://' | sed "s/^ \+['\"]\?//" | sed "s/[\"']\s\?//" | sort | uniq`; do grep -rlF "${i}" . if [ $? -ne 1 ]; then diff --git a/roles/WebServer/files/nginx.conf b/roles/WebServer/files/nginx.conf index 8408f35..2400b08 100644 --- a/roles/WebServer/files/nginx.conf +++ b/roles/WebServer/files/nginx.conf @@ -7,6 +7,7 @@ error_log logs/error.log notice; error_log logs/error.log info; load_module /usr/lib/nginx/modules/ngx_http_modsecurity_module.so; +load_module /usr/lib/nginx/modules/ngx_http_geoip2_module.so; events { worker_connections 1024; @@ -24,6 +25,8 @@ http { keepalive_timeout 65; gzip on; + include conf/geoip.conf; + # Redirect all HTTP to HTTPS server { diff --git a/roles/WebServer/tasks/main.yml b/roles/WebServer/tasks/main.yml index ba67acd..6cc61b4 100644 --- a/roles/WebServer/tasks/main.yml +++ b/roles/WebServer/tasks/main.yml @@ -9,6 +9,7 @@ - nginx - libmodsecurity - nginx-mod-modsecurity + - nginx-mod-geoip2 - php - php-fpm @@ -103,6 +104,27 @@ mode: 0660 register: secconf + - name: Populate GeoIP config + become: yes + template: + src: conf/geoip.conf.j2 + dest: /etc/nginx/conf/geoip.conf + owner: http + group: http + mode: 0660 + register: geoipconf + + - name: Ensure MaxMindDB is present + become: yes + file: + path: /etc/nginx/conf/maxmind-geoip2.mmdb + state: file + owner: http + group: http + mode: 0440 + # This requires a https://maxmind.com/ account, so the source will have to come from that site. + # This file should be the current country database. + - name: Clone OWASP-CRS ignore_errors: true become: yes @@ -148,7 +170,7 @@ - name: Ensure service is started become: yes - when: conf.changed or confd.changed or secconf.changed or baseconf.changed or modsecconf.changed + when: conf.changed or confd.changed or geoipconf.changed or secconf.changed or baseconf.changed or modsecconf.changed service: name: "{{ item }}" enabled: yes diff --git a/roles/WebServer/templates/conf/geoip.conf.j2 b/roles/WebServer/templates/conf/geoip.conf.j2 new file mode 100644 index 0000000..04ddff5 --- /dev/null +++ b/roles/WebServer/templates/conf/geoip.conf.j2 @@ -0,0 +1,28 @@ +# Load database and set variables from the database. +geoip2 /etc/nginx/conf/maxmind-geoip2.mmdb { + auto_reload 60m; + $geoip2_metadata_country_build metadata build_epoch; + $geoip2_data_country_code country iso_code; + $geoip2_data_country_name country names en; +} +fastcgi_param COUNTRY_CODE $geoip2_data_country_code; +fastcgi_param COUNTRY_NAME $geoip2_data_country_name; + +# Allow LAN and operational countries. +geo $lan { + default 0; + {{ main_subnet }}/{{ netmask }} 1; +} +map $geoip2_data_country_code $allowed_country { + default 0; +{% for country in operational_countries %} + {{ country }} 1; +{% endfor %} +} + +# Define the deny variable such that LAN & country requests are allowed. +# Thanks to https://stackoverflow.com/a/64071860 for the example +map $lan$allowed_country $deny { + default 0; + 00 1; +} From 49839d1333cd319f0966aeddb9a708ab36cb6569 Mon Sep 17 00:00:00 2001 From: DarkFeather Date: Tue, 2 Dec 2025 14:47:29 -0600 Subject: [PATCH 2/3] Adding geoip block to Superintendent for testing --- roles/WebServer/files/conf.d/Yggdrasil/superintendent.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/roles/WebServer/files/conf.d/Yggdrasil/superintendent.conf b/roles/WebServer/files/conf.d/Yggdrasil/superintendent.conf index ea2fcc6..8995c77 100644 --- a/roles/WebServer/files/conf.d/Yggdrasil/superintendent.conf +++ b/roles/WebServer/files/conf.d/Yggdrasil/superintendent.conf @@ -13,6 +13,12 @@ server { # include conf/local.conf; include conf/letsencrypt.conf; + # GeoIP block + if ($deny) { + return 503; + } + + # Handle the location location / { proxy_set_header Host $http_host; From 43f764e66465fb89d62b0baba84ec30d78a38079 Mon Sep 17 00:00:00 2001 From: DarkFeather Date: Thu, 18 Dec 2025 14:43:13 -0600 Subject: [PATCH 3/3] Adding domain monitoring for TLSA/SSHFP/CAA records --- roles/Sharingan/files/monit/checks/domain | 8 +++++ .../Sharingan/files/monit/hostdefs/Yggdrasil | 1 + .../files/monit/scripts/check-domain | 32 +++++++++++++++++++ roles/Sharingan/tasks/eval.yml | 2 +- 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 roles/Sharingan/files/monit/checks/domain create mode 100755 roles/Sharingan/files/monit/scripts/check-domain diff --git a/roles/Sharingan/files/monit/checks/domain b/roles/Sharingan/files/monit/checks/domain new file mode 100644 index 0000000..752e336 --- /dev/null +++ b/roles/Sharingan/files/monit/checks/domain @@ -0,0 +1,8 @@ +check program domain-tlsa with path "/etc/monit.d/scripts/check-domain aninix.net tlsa aninix.net-0002" + if status != 0 for 5 times within 5 cycles then exec "/etc/monit.d/scripts/critical TLSA records do not match -- regenerate and update" + +check program domain-sshfp with path "/etc/monit.d/scripts/check-domain aninix.net sshfp" + if status != 0 for 5 times within 5 cycles then exec "/etc/monit.d/scripts/critical SSHFP records do not match -- regenerate and update" + +check program domain-caa with path "/etc/monit.d/scripts/check-domain aninix.net caa" + if status != 0 for 5 times within 5 cycles then exec "/etc/monit.d/scripts/critical CAA record does not match -- regenerate and update" diff --git a/roles/Sharingan/files/monit/hostdefs/Yggdrasil b/roles/Sharingan/files/monit/hostdefs/Yggdrasil index 14f12ab..3c3b034 100644 --- a/roles/Sharingan/files/monit/hostdefs/Yggdrasil +++ b/roles/Sharingan/files/monit/hostdefs/Yggdrasil @@ -3,3 +3,4 @@ include "/etc/monit.d/checks/watcher-of-watchers" include "/etc/monit.d/checks/warrant-canary" include "/etc/monit.d/checks/grimoire" include "/etc/monit.d/checks/automated_response" +include "/etc/monit.d/checks/domain" diff --git a/roles/Sharingan/files/monit/scripts/check-domain b/roles/Sharingan/files/monit/scripts/check-domain new file mode 100755 index 0000000..365fe29 --- /dev/null +++ b/roles/Sharingan/files/monit/scripts/check-domain @@ -0,0 +1,32 @@ +#!/bin/bash + +source /opt/aninix/Uniglot/Bash/dns.bash + +domain="$1" + +function checkTLSA() { + ### Usage: $0 "${domain}" tlsa _443._tcp + identity="$1" + git diff --no-index <(GenerateTLSA "${identity}" | sed 's/\s\+//g' | tr '[[:upper:]]' '[[:lower:]]' | sort) <(dig _443._tcp."${domain}" TLSA +short | sed 's/\s\+//g' | tr '[[:upper:]]' '[[:lower:]]' | sort) + +} + +function checkSSHFP() { + git diff --no-index <(GenerateSSHFP | sed 's/\s\+//g' | tr '[[:upper:]]' '[[:lower:]]' | sort) <(dig "${domain}" SSHFP +short | sed 's/\s\+//g' | tr '[[:upper:]]' '[[:lower:]]' | sort) +} + +function checkCAA() { + ### Usage: $0 "${domain}" caa + caa="$(dig "${domain}" CAA +short)" + if [ "$caa" != '128 issue "letsencrypt.org"' ]; then + exit 1 + else + exit 0 + fi +} + +case "$2" in + "tlsa") checkTLSA "$3" ;; + "sshfp") checkSSHFP ;; + "caa") checkCAA ;; +esac diff --git a/roles/Sharingan/tasks/eval.yml b/roles/Sharingan/tasks/eval.yml index 72d2199..9b9e644 100644 --- a/roles/Sharingan/tasks/eval.yml +++ b/roles/Sharingan/tasks/eval.yml @@ -3,7 +3,7 @@ - name: Generate monitoring from inventory delegate_to: localhost run_once: true - command: "python3 ../bin/generate-monitoring.py {{ inventory_file }}" + command: "../bin/generate-monitoring.py {{ inventory_file }}" - name: Sharingan-Eval service copy become: yes