302 lines
7.2 KiB
Bash
Executable File
302 lines
7.2 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# omarchy:summary=Show or configure the system DNS provider
|
|
# omarchy:args=[Cloudflare|Google|DHCP|Custom]
|
|
# omarchy:examples=omarchy dns | omarchy dns Cloudflare | omarchy dns Custom
|
|
|
|
set -euo pipefail
|
|
|
|
NM_DNS_CONF=/etc/NetworkManager/conf.d/20-omarchy-dns.conf
|
|
|
|
provider_from_arg() {
|
|
case "${1:-}" in
|
|
Cloudflare | cloudflare)
|
|
echo "Cloudflare"
|
|
;;
|
|
Google | google)
|
|
echo "Google"
|
|
;;
|
|
DHCP | dhcp)
|
|
echo "DHCP"
|
|
;;
|
|
Custom | custom)
|
|
echo "Custom"
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
self_path() {
|
|
local self="${BASH_SOURCE[0]}"
|
|
|
|
if [[ $self != /* ]]; then
|
|
self=$(command -v -- "$self")
|
|
fi
|
|
|
|
readlink -f "$self"
|
|
}
|
|
|
|
require_root() {
|
|
if (( EUID == 0 )); then
|
|
return
|
|
fi
|
|
|
|
if ! command -v pkexec >/dev/null; then
|
|
echo "Error: pkexec is required to change DNS settings." >&2
|
|
exit 1
|
|
fi
|
|
|
|
exec pkexec "$(self_path)" "$@"
|
|
}
|
|
|
|
networkmanager_global_dns() {
|
|
[[ -f $NM_DNS_CONF ]] || return 0
|
|
|
|
awk -F= '
|
|
/^[[:space:]]*#/ { next }
|
|
/^[[:space:]]*\[global-dns-domain-\*\][[:space:]]*$/ { in_default = 1; next }
|
|
/^[[:space:]]*\[/ { in_default = 0 }
|
|
in_default && /^[[:space:]]*servers[[:space:]]*=/ {
|
|
value = $0
|
|
sub(/^[^=]*=/, "", value)
|
|
print value
|
|
exit
|
|
}
|
|
' "$NM_DNS_CONF"
|
|
}
|
|
|
|
resolved_dns() {
|
|
awk -F= '
|
|
/^[[:space:]]*#/ { next }
|
|
/^[[:space:]]*DNS[[:space:]]*=/ {
|
|
value=$0
|
|
sub(/^[^=]*=/, "", value)
|
|
print value
|
|
exit
|
|
}
|
|
' /etc/systemd/resolved.conf 2>/dev/null || true
|
|
}
|
|
|
|
current_dns_provider() {
|
|
local dns=""
|
|
local compact=""
|
|
|
|
dns=$(networkmanager_global_dns)
|
|
if [[ -z $(printf '%s' "$dns" | tr -d '[:space:],') ]]; then
|
|
dns=$(resolved_dns)
|
|
fi
|
|
|
|
compact=$(printf '%s' "$dns" | tr -d '[:space:],')
|
|
|
|
if [[ -z $compact ]]; then
|
|
echo "DHCP"
|
|
elif [[ $dns == *"cloudflare-dns.com"* || $dns == *"1.1.1.1"* || $dns == *"2606:4700:4700::1111"* ]]; then
|
|
echo "Cloudflare"
|
|
elif [[ $dns == *"dns.google"* || $dns == *"8.8.8.8"* || $dns == *"2001:4860:4860::8888"* ]]; then
|
|
echo "Google"
|
|
else
|
|
echo "Custom"
|
|
fi
|
|
}
|
|
|
|
normalize_servers() {
|
|
printf '%s\n' "$*" | tr ',\t\n' ' ' | xargs | tr ' ' ','
|
|
}
|
|
|
|
split_dns_servers() {
|
|
local servers="$1"
|
|
local server clean
|
|
ipv4_dns=""
|
|
ipv6_dns=""
|
|
|
|
for server in ${servers//,/ }; do
|
|
clean=${server#dns+tls://}
|
|
clean=${clean#dns+udp://}
|
|
clean=${clean%%#*}
|
|
clean=${clean#[}
|
|
clean=${clean%]}
|
|
|
|
[[ -n $clean ]] || continue
|
|
if [[ $clean == *:* ]]; then
|
|
ipv6_dns+="${ipv6_dns:+ }$clean"
|
|
else
|
|
ipv4_dns+="${ipv4_dns:+ }$clean"
|
|
fi
|
|
done
|
|
}
|
|
|
|
write_networkmanager_dns() {
|
|
local servers="$1"
|
|
|
|
install -d -m 0755 "$(dirname "$NM_DNS_CONF")"
|
|
cat >"$NM_DNS_CONF" <<EOF
|
|
# Managed by omarchy-dns. Remove this file or run omarchy dns DHCP to use DHCP DNS again.
|
|
[global-dns]
|
|
|
|
[global-dns-domain-*]
|
|
servers=$servers
|
|
EOF
|
|
}
|
|
|
|
clear_networkmanager_dns() {
|
|
rm -f "$NM_DNS_CONF"
|
|
}
|
|
|
|
networkmanager_dns_connection() {
|
|
case "$1" in
|
|
802-11-wireless|802-3-ethernet) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
set_connection_dns() {
|
|
local uuid type
|
|
local ipv4_dns="${1:-}"
|
|
local ipv6_dns="${2:-}"
|
|
|
|
command -v nmcli >/dev/null 2>&1 || return 0
|
|
|
|
while IFS=: read -r uuid type; do
|
|
[[ -n $uuid ]] || continue
|
|
networkmanager_dns_connection "$type" || continue
|
|
|
|
nmcli connection modify "$uuid" \
|
|
ipv4.ignore-auto-dns yes \
|
|
ipv4.dns "$ipv4_dns" \
|
|
ipv6.ignore-auto-dns yes \
|
|
ipv6.dns "$ipv6_dns" \
|
|
>/dev/null
|
|
done < <(nmcli -t -f UUID,TYPE connection show)
|
|
}
|
|
|
|
clear_connection_dns() {
|
|
local uuid type
|
|
|
|
command -v nmcli >/dev/null 2>&1 || return 0
|
|
|
|
while IFS=: read -r uuid type; do
|
|
[[ -n $uuid ]] || continue
|
|
networkmanager_dns_connection "$type" || continue
|
|
|
|
nmcli connection modify "$uuid" \
|
|
ipv4.ignore-auto-dns no \
|
|
ipv4.dns "" \
|
|
ipv6.ignore-auto-dns no \
|
|
ipv6.dns "" \
|
|
>/dev/null
|
|
done < <(nmcli -t -f UUID,TYPE connection show)
|
|
}
|
|
|
|
reapply_active_dns_connections() {
|
|
local device type state
|
|
|
|
command -v nmcli >/dev/null 2>&1 || return 0
|
|
|
|
while IFS=: read -r device type state; do
|
|
[[ -n $device && $state == connected ]] || continue
|
|
case "$type" in
|
|
wifi|ethernet)
|
|
nmcli device reapply "$device" >/dev/null 2>&1 || true
|
|
;;
|
|
esac
|
|
done < <(nmcli -t -f DEVICE,TYPE,STATE device status)
|
|
}
|
|
|
|
reload_dns_stack() {
|
|
if systemctl is-active --quiet NetworkManager.service 2>/dev/null; then
|
|
# Load the updated NetworkManager config first, then reapply the active
|
|
# profiles. A single conf,dns-full reload here pushes the old active DNS
|
|
# settings, making the shell toggle appear one selection behind.
|
|
nmcli general reload conf >/dev/null 2>&1 || systemctl reload NetworkManager.service 2>/dev/null || true
|
|
reapply_active_dns_connections
|
|
fi
|
|
|
|
systemctl reload systemd-resolved.service 2>/dev/null || systemctl restart systemd-resolved.service
|
|
|
|
if systemctl is-active --quiet NetworkManager.service 2>/dev/null; then
|
|
# A resolved reload/restart can leave per-link DNS stale or empty; ask
|
|
# NetworkManager to publish DNS after resolved has reread its config.
|
|
nmcli general reload dns-full >/dev/null 2>&1 || true
|
|
fi
|
|
}
|
|
|
|
usage() {
|
|
echo "Usage: omarchy-dns [Cloudflare|Google|DHCP|Custom]" >&2
|
|
}
|
|
|
|
if (( $# == 0 )); then
|
|
current_dns_provider
|
|
exit 0
|
|
fi
|
|
|
|
if (( $# > 1 )); then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
if ! provider=$(provider_from_arg "$1"); then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
require_root "$provider"
|
|
|
|
case "$provider" in
|
|
Cloudflare)
|
|
write_networkmanager_dns "1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001"
|
|
set_connection_dns "1.1.1.1 1.0.0.1" "2606:4700:4700::1111 2606:4700:4700::1001"
|
|
tee /etc/systemd/resolved.conf >/dev/null <<'EOF'
|
|
[Resolve]
|
|
DNS=1.1.1.1#cloudflare-dns.com 1.0.0.1#cloudflare-dns.com 2606:4700:4700::1111#cloudflare-dns.com 2606:4700:4700::1001#cloudflare-dns.com
|
|
FallbackDNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net 2620:fe::fe#dns.quad9.net 2620:fe::9#dns.quad9.net
|
|
DNSOverTLS=opportunistic
|
|
EOF
|
|
;;
|
|
|
|
Google)
|
|
write_networkmanager_dns "8.8.8.8,8.8.4.4,2001:4860:4860::8888,2001:4860:4860::8844"
|
|
set_connection_dns "8.8.8.8 8.8.4.4" "2001:4860:4860::8888 2001:4860:4860::8844"
|
|
tee /etc/systemd/resolved.conf >/dev/null <<'EOF'
|
|
[Resolve]
|
|
DNS=8.8.8.8#dns.google 8.8.4.4#dns.google 2001:4860:4860::8888#dns.google 2001:4860:4860::8844#dns.google
|
|
FallbackDNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net 2620:fe::fe#dns.quad9.net 2620:fe::9#dns.quad9.net
|
|
DNSOverTLS=opportunistic
|
|
EOF
|
|
;;
|
|
|
|
DHCP)
|
|
clear_networkmanager_dns
|
|
clear_connection_dns
|
|
tee /etc/systemd/resolved.conf >/dev/null <<'EOF'
|
|
[Resolve]
|
|
DNSOverTLS=no
|
|
EOF
|
|
;;
|
|
|
|
Custom)
|
|
echo "Enter your DNS servers (space-separated, e.g. '192.168.1.1 1.1.1.1'):"
|
|
if ! read -r dns_servers; then
|
|
dns_servers=""
|
|
fi
|
|
|
|
dns_servers=$(normalize_servers "$dns_servers")
|
|
if [[ -z $dns_servers ]]; then
|
|
echo "Error: No DNS servers provided." >&2
|
|
exit 1
|
|
fi
|
|
|
|
split_dns_servers "$dns_servers"
|
|
write_networkmanager_dns "$dns_servers"
|
|
set_connection_dns "$ipv4_dns" "$ipv6_dns"
|
|
tee /etc/systemd/resolved.conf >/dev/null <<EOF
|
|
[Resolve]
|
|
DNS=${dns_servers//,/ }
|
|
FallbackDNS=9.9.9.9#dns.quad9.net 149.112.112.112#dns.quad9.net 2620:fe::fe#dns.quad9.net 2620:fe::9#dns.quad9.net
|
|
EOF
|
|
;;
|
|
esac
|
|
|
|
reload_dns_stack
|