From db3d24a835da4e01ce06eab006efdc494b590b77 Mon Sep 17 00:00:00 2001 From: Root <175176948+RKNF404@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:39:54 -0500 Subject: [PATCH] feat: implement just dns-selector and add to post install (#571) --- docs/POSTINSTALL-README.md | 9 + files/justfiles/secureblue.just | 332 ++++++++++++++++++ .../{securedns.conf => 10-securedns.conf} | 0 3 files changed, 341 insertions(+) rename files/system/etc/systemd/resolved.conf.d/{securedns.conf => 10-securedns.conf} (100%) diff --git a/docs/POSTINSTALL-README.md b/docs/POSTINSTALL-README.md index 52ff081..8509d70 100644 --- a/docs/POSTINSTALL-README.md +++ b/docs/POSTINSTALL-README.md @@ -108,6 +108,15 @@ When using a non-wheel user, you can add the user to other groups if you want. F - use `adb` and `fastboot`: `plugdev` - use systemwide flatpaks: `flatpak` +## Setup system DNS + +Interactively setup system DNS resolution for systemd-resolved (optionally also set the resolver for hardened-chromium via management policy): + +``` +ujust dns-selector +``` + +NOTE: If you intend to use a VPN, use the system default state (network provided resolver). This will ensure your system uses the VPN provided DNS resolver to prevent DNS leaks. ESPECIALLY avoid setting the browser DNS policy in this case. ## Bash environment lockdown diff --git a/files/justfiles/secureblue.just b/files/justfiles/secureblue.just index 1342dec..32ee0e7 100644 --- a/files/justfiles/secureblue.just +++ b/files/justfiles/secureblue.just @@ -500,6 +500,19 @@ audit-secureblue: print_status "$CHRONYD_TEST_STRING" "$STATUS_FAILURE" fi + SECUREDNS_TEST_STRING="Ensuring system DNS resolution is secure" + if systemctl is-active --quiet systemd-resolved; then + DNSSEC_STATUS="$(cat /etc/systemd/resolved.conf.d/10-securedns.conf | grep "DNSSEC")" + DOT_STATUS="$(cat /etc/systemd/resolved.conf.d/10-securedns.conf | grep "DNSOverTLS")" + if [[ "$DNSSEC_STATUS" == "DNSSEC=true" && "$DOT_STATUS" == "DNSOverTLS=true" ]]; then + print_status "$SECUREDNS_TEST_STRING" "$STATUS_SUCCESS" + else + print_status "$SECUREDNS_TEST_STRING" "$STATUS_FAILURE" + fi + else + print_status "$SECUREDNS_TEST_STRING" "$STATUS_FAILURE" + fi + BASH_TEST_STRING="Ensuring bash environment lockdown" BASH_ENV_FILES=(~/.bashrc ~/.bash_profile) all_locked=1 @@ -681,3 +694,322 @@ debug-info: failed_services=$(echo -e "\n=== Failed Services ===\n"; systemctl list-units --state=failed) content="$rpm_ostree_status$sysinfo$flatpaks$audit_results$local_overrides$recent_events$failed_services" echo "$content" | fpaste --confirm --private=1 + +# setup system DNS resolution +dns-selector: + #! /bin/run0 /bin/bash + # constants + readonly resolved_conf="/etc/systemd/resolved.conf.d/10-securedns.conf" + readonly policy_file="/etc/chromium/policies/managed/10-securedns-browser.json" + mkdir -p /etc/systemd/resolved.conf.d/ + mkdir -p /etc/chromium/policies/managed/ + # variables + valid_input="0" + resolver_selection="" + resolver_subselection="" + resolver_has_second_ip="" + resolver_supports_ipv6="" + resolver_ipv4_address="" + resolver_ipv4_address_2="" + resolver_ipv6_address="" + resolver_ipv6_address_2="" + resolver_hostname="" + resolver_https_address="" + set_browser_policy="" + + echo "Below will be some options to set the DNS resolver for systemd-resolved." + echo "All resolvers support DNS-over-TLS (DoT) or DNS-over-QUIC (DoQ), DNS-over-HTTPS (DoH), and DNSSEC." + echo "Please select which DNS resolver you would like to set:" + echo " 0) Network Resolver - security may vary (system default state)" + echo " 1) Control D - has content filtering, anycast" + echo " 2) Mullvad - has content filtering, anycast" + echo " 3) Cloudflare - very fast with some data collection, anycast" + echo " 4) DNSForge - powerful filtering but can be very slow" + echo " 5) Custom Resolver - use a custom resolver (must support DoT/DoQ and DNSSEC, DoH support is also required to set a browser policy should that be desired)" + while [[ "$valid_input" == "0" ]]; do + read -p "Selection [0-5]: " resolver_selection + if [[ "$resolver_selection" == [012345]* ]]; then + valid_input="1" + else + echo "That is not a valid selection." + fi + done + valid_input="0" + + echo "" # blank space + case "$resolver_selection" in + 0) + echo "Resetting resolved to default state." + if [[ -f "$policy_file" ]]; then + rm $policy_file + echo "Removed browser policy." + fi + cp /usr$resolved_conf $resolved_conf + systemctl restart systemd-resolved + echo "Configuration file for resolved reset and service restarted." + exit 0 + ;; + 1) + resolver_has_second_ip="y" + resolver_supports_ipv6="y" + echo "Setting resolver Control D." + echo "What content would you like to filter:" + echo " 0) No filtering" + echo " 1) Malware: Malware filtering" + echo " 2) Standard: Malware + ad and tracker filtering" + echo " 3) Social: Standard + social media filtering" + echo " 4) Family: Social + adult content filtering (also enables safe search in major search engines)" + while [[ "$valid_input" == "0" ]]; do + read -p "Selection [0-4]: " resolver_subselection + if [[ "$resolver_subselection" == [01234]* ]]; then + valid_input="1" + else + echo "That is not a valid selection." + fi + done + valid_input="0" + case "$resolver_subselection" in + 0) + resolver_ipv4_address="76.76.2.0" + resolver_ipv4_address_2="76.76.10.0" + resolver_ipv6_address="2606:1a40::" + resolver_ipv6_address_2="2606:1a40:1::" + resolver_hostname="p0.freedns.controld.com" + resolver_https_address="https://freedns.controld.com/p0" + ;; + 1) + resolver_ipv4_address="76.76.2.1" + resolver_ipv4_address_2="76.76.10.1" + resolver_ipv6_address="2606:1a40::1" + resolver_ipv6_address_2="2606:1a40:1::1" + resolver_hostname="p1.freedns.controld.com" + resolver_https_address="https://freedns.controld.com/p1" + ;; + 2) + resolver_ipv4_address="76.76.2.2" + resolver_ipv4_address_2="76.76.10.2" + resolver_ipv6_address="2606:1a40::2" + resolver_ipv6_address_2="2606:1a40:1::2" + resolver_hostname="p2.freedns.controld.com" + resolver_https_address="https://freedns.controld.com/p2" + ;; + 3) + resolver_ipv4_address="76.76.2.3" + resolver_ipv4_address_2="76.76.10.3" + resolver_ipv6_address="2606:1a40::3" + resolver_ipv6_address_2="2606:1a40:1::3" + resolver_hostname="p3.freedns.controld.com" + resolver_https_address="https://freedns.controld.com/p3" + ;; + 4) + resolver_ipv4_address="76.76.2.4" + resolver_ipv4_address_2="76.76.10.4" + resolver_ipv6_address="2606:1a40::4" + resolver_ipv6_address_2="2606:1a40:1::4" + resolver_hostname="family.freedns.controld.com" + resolver_https_address="https://freedns.controld.com/family" + ;; + esac + ;; + 2) + resolver_has_second_ip="n" + resolver_supports_ipv6="y" + echo "Setting resolver Mullvad." + echo "What content would you like to filter:" + echo " 0) No filtering" + echo " 1) Standard: Ad and tracker filtering" + echo " 2) Base: Standard + malware filtering" + echo " 3) Extended: Base + social media filtering" + echo " 4) Family: Base + gambling and adult content filtering" + echo " 5) All: Family + social media filtering" + while [[ "$valid_input" == "0" ]]; do + read -p "Selection [0-5]: " resolver_subselection + if [[ "$resolver_subselection" == [012345]* ]]; then + valid_input="1" + else + echo "That is not a valid selection." + fi + done + valid_input="0" + case "$resolver_subselection" in + 0) + resolver_ipv4_address="194.242.2.2" + resolver_ipv6_address="2a07:e340::2" + resolver_hostname="dns.mullvad.net" + ;; + 1) + resolver_ipv4_address="194.242.2.3" + resolver_ipv6_address="2a07:e340::3" + resolver_hostname="adblock.dns.mullvad.net" + ;; + 2) + resolver_ipv4_address="194.242.2.4" + resolver_ipv6_address="2a07:e340::4" + resolver_hostname="base.dns.mullvad.net" + ;; + 3) + resolver_ipv4_address="194.242.2.5" + resolver_ipv6_address="2a07:e340::5" + resolver_hostname="extended.dns.mullvad.net" + ;; + 4) + resolver_ipv4_address="194.242.2.6" + resolver_ipv6_address="2a07:e340::6" + resolver_hostname="family.dns.mullvad.net" + ;; + 5) + resolver_ipv4_address="194.242.2.9" + resolver_ipv6_address="2a07:e340::9" + resolver_hostname="all.dns.mullvad.net" + ;; + esac + resolver_https_address="https://$resolver_hostname/dns-query" + ;; + 3) + resolver_has_second_ip="y" + resolver_supports_ipv6="y" + echo "Setting resolver Cloudflare. (glory to the cloud)" + echo "What content would you like to filter:" + echo " 0) No filtering" + echo " 1) Security: Malware filtering" + echo " 2) Family: Security + adult content filtering" + while [[ "$valid_input" == "0" ]]; do + read -p "Selection [0-2]: " resolver_subselection + if [[ "$resolver_subselection" == [012]* ]]; then + valid_input="1" + else + echo "That is not a valid selection." + fi + done + valid_input="0" + case "$resolver_subselection" in + 0) + resolver_ipv4_address="1.1.1.1" + resolver_ipv4_address_2="1.0.0.1" + resolver_ipv6_address="2606:4700:4700::1111" + resolver_ipv6_address_2="2606:4700:4700::1001" + resolver_hostname="cloudflare-dns.com" + ;; + 1) + resolver_ipv4_address="1.1.1.2" + resolver_ipv4_address_2="1.0.0.2" + resolver_ipv6_address="2606:4700:4700::1112" + resolver_ipv6_address_2="2606:4700:4700::1002" + resolver_hostname="security.cloudflare-dns.com" + ;; + 2) + resolver_ipv4_address="1.1.1.3" + resolver_ipv4_address_2="1.0.0.3" + resolver_ipv6_address="2606:4700:4700::1113" + resolver_ipv6_address_2="2606:4700:4700::1003" + resolver_hostname="family.cloudflare-dns.com" + ;; + esac + resolver_https_address="https://$resolver_hostname/dns-query" + ;; + 4) + resolver_has_second_ip="y" + resolver_supports_ipv6="y" + echo "Setting resolver DNSForge." + echo "What content would you like to filter:" + echo " 0) Standard: Ad, tracker, and malware filtering" + echo " 1) Clean: Standard + adult content filtering" + echo " 2) Hard: Clean + stricter ad, tracker, and malware filtering" + while [[ "$valid_input" == "0" ]]; do + read -p "Selection [0-2]: " resolver_subselection + if [[ "$resolver_subselection" == [012]* ]]; then + valid_input="1" + else + echo "That is not a valid selection." + fi + done + valid_input="0" + case "$resolver_subselection" in + 0) + resolver_ipv4_address="176.9.93.198" + resolver_ipv4_address_2="176.9.1.117" + resolver_ipv6_address="2a01:4f8:151:34aa::198" + resolver_ipv6_address_2="2a01:4f8:141:316d::117" + resolver_hostname="dnsforge.de" + ;; + 1) + resolver_ipv4_address="49.12.223.2" + resolver_ipv4_address_2="49.12.43.208" + resolver_ipv6_address="2a01:4f8:c17:4fbc::2" + resolver_ipv6_address_2="2a01:4f8:c012:ed89::208" + resolver_hostname="clean.dnsforge.de" + ;; + 2) + resolver_ipv4_address="49.12.222.213" + resolver_ipv4_address_2="88.198.122.154" + resolver_ipv6_address="2a01:4f8:c17:2c61::213" + resolver_ipv6_address_2="2a01:4f8:c013:5ec0::154" + resolver_hostname="hard.dnsforge.de" + ;; + esac + resolver_https_address="https://$resolver_hostname/dns-query" + ;; + 5) + echo "Setting custom resolver." + echo "NOTE: If the resolver does not support DoT/DoQ or DNSSEC, this process will not work." + echo "" + echo "Please provide the technical information." + read -p "Please enter the resolver's IP address (e.g. '1.1.1.2'): " resolver_ipv4_address + read -p "Does the resolver provide two distinct IP addresses (e.g. '1.1.1.2' and '1.0.0.2')? [Y/n] " resolver_has_second_ip + if [[ "$resolver_has_second_ip" == [Yy]* ]]; then + read -p "Please enter the resolver's second IP address: " resolver_ipv4_address_2 + fi + read -p "Does the resolver support IPv6 (e.g. '2606:4700:4700::1112')? [Y/n] " resolver_supports_ipv6 + if [[ "$resolver_supports_ipv6" == [Yy]* ]]; then + read -p "Please enter the resolver's IPv6 address: " resolver_ipv6_address + if [[ "$resolver_has_second_ip" == [Yy]* ]]; then + read -p "Please enter the resolver's second IPv6 address: " resolver_ipv6_address_2 + fi + fi + read -p "Please enter the second resolver's hostname (e.g. 'security.cloudflare-dns.com'): " resolver_hostname + ;; + esac + + read -p "Would you like the resolver to be set in the default browser (hardened-chromium) via management policy? [Y/n] " set_browser_policy + if [[ "$set_browser_policy" == [Yy]* && "$resolver_selection" == 5 ]]; then + read -p "Please enter the second resolver's HTTPS address (e.g. 'https://security.cloudflare-dns.com/dns-query'): " resolver_https_address + fi + + resolved_conf_dns_string="DNS=" + resolved_conf_dns_string+=" $resolver_ipv4_address" + resolved_conf_dns_string+="#$resolver_hostname" + if [[ "$resolver_has_second_ip" == [Yy]* ]]; then + resolved_conf_dns_string+=" $resolver_ipv4_address_2" + resolved_conf_dns_string+="#$resolver_hostname" + fi + if [[ "$resolver_supports_ipv6" == [Yy]* ]]; then + resolved_conf_dns_string+=" $resolver_ipv6_address" + resolved_conf_dns_string+="#$resolver_hostname" + if [[ "$resolver_has_second_ip" == [Yy]* ]]; then + resolved_conf_dns_string+=" $resolver_ipv6_address_2" + resolved_conf_dns_string+="#$resolver_hostname" + fi + fi + + cat << EOF > "$resolved_conf" + [Resolve] + DNSSEC=true + DNSOverTLS=true + $resolved_conf_dns_string + EOF + + systemctl restart systemd-resolved + + echo "Config file for resolved configured with selected resolver and service restarted." + + if [[ "$set_browser_policy" != [Yy]* ]]; then + exit 0 + fi + cat << EOF > "$policy_file" + { + "DnsOverHttpsMode": "secure", + "DnsOverHttpsTemplates": "$resolver_https_address" + } + EOF + + echo "Browser policy set with selected resolver." diff --git a/files/system/etc/systemd/resolved.conf.d/securedns.conf b/files/system/etc/systemd/resolved.conf.d/10-securedns.conf similarity index 100% rename from files/system/etc/systemd/resolved.conf.d/securedns.conf rename to files/system/etc/systemd/resolved.conf.d/10-securedns.conf