Skip to content

Instantly share code, notes, and snippets.

@dbouras
Last active June 7, 2024 10:19
Show Gist options
  • Save dbouras/d390bc3511280aaf717fbb976e20447e to your computer and use it in GitHub Desktop.
Save dbouras/d390bc3511280aaf717fbb976e20447e to your computer and use it in GitHub Desktop.
Add support to AWS VPN for distributions (such as OpenSUSE) that use NetworkManager/netconfig for configuring DNS resolution

After installing AWS VPN client from: client-vpn-connect-linux-install, replace:

   /opt/awsvpnclient/Service/Resources/openvpn/configure-dns

with the modified configure-dns script below. If you would like to to review the modifications, see file configure-dns.patch.

If you are running OpenSUSE Leap or Tumbleweed, you will need to have dpkg installed; after you install awsvpnclient_amd64.deb using dpkg -i, you will also need to edit:

   /usr/share/applications/awsvpnclient.desktop

and replace:

   Exec=/opt/awsvpnclient/AWS\ VPN\ Client %u

with:

   Exec=/opt/awsvpnclient/AWS\\ VPN\\ Client %u

for the desktop entry to work properly.

#!/usr/bin/env bash
#
# Modified to support distributions that rely either on systemd-resolve
# (such as Ubuntu 18.04 or 20.04) or on NetworkManager/netconfig (such
# as OpenSUSE Leap/Tumbleweed) for DNS resolver setup
configure_logging() {
log_folder="/var/log/aws-vpn-client"
mkdir -p $log_folder
# $script_type is set by OpenVPN
if [[ $script_type == *up* ]]; then
log_file=$log_folder/configure-dns-up.log
elif [[ $script_type == *down* ]]; then
log_file=$log_folder/configure-dns-down.log
fi
if [[ -f "$log_file" ]]; then
rm -f "$log_file"
fi
}
log() {
echo "$(date) $1" >> "$log_file"
}
# Get distribution via lsb-release in order to support more
# than just Ubuntu 18.04 LTS (bionic) and 20.04 LTS (focal)
get_distribution() {
# if available, use LSB release utility
LSBREL=$(type -p lsb-release)
test -n "${LSBREL}" && dist=$("${LSBREL}" -s -i) || dist=""
}
check_systemd_resolve_status() {
output=$(systemd-resolve --status 2>&1)
exit_code=$?
log "'systemd-resolve --status' exit code: $exit_code output:"
log "$output"
}
call_systemd_resolved() {
log "Calling busctl with parameters '$*'"
output=$(busctl call "org.freedesktop.resolve1" "/org/freedesktop/resolve1" "org.freedesktop.resolve1.Manager" "$@" 2>&1)
exit_code=$?
log "busctl command exit code: $exit_code, output: $output"
if [[ $exit_code -ne 0 ]]; then
exit $exit_code
fi
}
# Remove DNS name servers from the resolver config
# file and add the ones passed on the argument list
edit_resolv_conf_servers() {
cp ${rslvcnf} ${rslvcnf}.ovpnsave
grep -v -e nameserver ${rslvcnf}.ovpnsave > ${rslvcnf}
for dsrv in "$@"; do
log "Adding DNS server: ${dsrv}"
echo "nameserver ${dsrv}" >> ${rslvcnf}
done
rm -f ${rslvcnf}.ovpnsave
}
# Remove DNS domain search list from the resolver
# config file and add new one from the argument list
edit_resolv_conf_search() {
cp ${rslvcnf} ${rslvcnf}.ovpnsave
grep -v -e search ${rslvcnf}.ovpnsave > ${rslvcnf}
log "Adding domain search list: ${*}"
echo "search ${@}" >> ${rslvcnf}
rm -f ${rslvcnf}.ovpnsave
}
# Refresh the Name Service Cache by restarting the nscd daemon
refresh_nscd() {
log "Refreshing the name service cache"
output=$(systemctl restart nscd 2>&1)
exit_code=$?
log "systemctl restart nscd exit code: $exit_code, output: $output"
}
# $foreign_option_<n> is set by OpenVPN.
# Currently we support DNS and DOMAIN types.
# Search domains are used in the order they appear in the OpenVPN config
# Example:
# "dhcp-option DNS 172.168.0.1"
# "dhcp-option DOMAIN amazon.com"
# "dhcp-option DOMAIN amazonaws.com"
# Domains will be set in the order 1) amazon.com, 2) amazonaws.com
# Client VPN service doesn't support IPv6.
get_dns_from_server() {
log "Getting DNS servers from OpenVPN"
for fopt in "${!foreign_option_@}"; do
fopt_val="${!fopt}"
log "$fopt from OpenVPN: $fopt_val"
if [[ "${fopt_val}" == *dhcp-option\ DNS* ]]; then
dns_server_count=$((dns_server_count + 1))
# dhcp-option DNS 172.168.0.1 -> 172.168.0.1
dns_server_ip="${fopt_val#dhcp-option DNS }"
#
# Append: 2 4 172 168 0 1 for the systemd-resolve case
# or simply the DNS server IP address for netconfig
#
test "${namesrv}" = "netconfig" && \
dns_servers+=(${dns_server_ip}) || \
dns_servers+=(2 4 ${dns_server_ip//./ })
elif [[ "${fopt_val}" == *dhcp-option\ DOMAIN\ * ]]; then
dns_domain_count=$((dns_domain_count + 1))
# dhcp-option DOMAIN example.com -> example.com
dns_domain="${fopt_val#dhcp-option DOMAIN }"
#
# For the netconfig case simply add the domain to the list
#
# For the systemd-resolve case [1]:
# "It takes a network interface index and an array of domains, each with
# a boolean parameter indicating whether the specified domain shall be
# used as a search domain (false), or just as a routing domain (true)"
# [1] https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html
#
test "${namesrv}" = "netconfig" && \
dns_domains+=(${dns_domain}) || \
dns_domains+=("${dns_domain}" false)
elif [[ "${fopt_val}" == *dhcp-option\ DOMAIN-ROUTE* ]]; then
dns_domain_count=$((dns_domain_count + 1))
# dhcp-option DOMAIN-ROUTE . -> .
dns_domain="${fopt_val#dhcp-option DOMAIN-ROUTE }"
#
# Domain route '~.' needs to be added to prevent DNS leakage
# https://github.com/systemd/systemd/issues/6076#issuecomment-387332572
#
test "${namesrv}" = "netconfig" && \
dns_domains+=(${dns_domain}) || \
dns_domains+=("${dns_domain}" true)
fi
done
}
# $dev is set by OpenVPN
# "The actual name of the TUN/TAP device, including a unit number if it exists."
get_device_index() {
log "Getting device index for $dev"
device_info="$(ip link show dev "$dev")"
exit_code=$?
log "'ip link show dev "$dev"' exit code: $exit_code, output: $device_info"
if [[ $exit_code -ne 0 ]]; then
exit $exit_code
fi
device_index="${device_info%%:*}"
log "Device index for $dev: $device_index"
}
configure_dns() {
local -a dns_servers=() dns_domains=()
local -i dns_server_count=0 dns_domain_count=0
local device_index
log "Configuring to use DNS servers from OpenVPN"
get_dns_from_server
if [[ $dns_server_count -eq 0 && $dns_domain_count -eq 0 ]]; then
log "No DNS configured for the endpoint"
return 0
fi
# The device index is not needed for the netconfig case
test "${namesrv}" != "netconfig" && get_device_index
if [[ $dns_server_count -gt 0 ]]; then
case "${namesrv}" in
"netconfig")
log "Updating ${rslvcnf} DNS servers with: '${dns_servers[*]}'"
edit_resolv_conf_servers "${dns_servers[@]}"
;;
"systemd-resolve")
params=("$device_index" "$dns_server_count" "${dns_servers[@]}")
log "Calling SetLinkDNS with parameters: '${params[*]}'"
# Example: SetLinkDNS 'ia(iay)' 125 2 2 4 172 31 29 109 2 4 172 31 4 164
call_systemd_resolved SetLinkDNS 'ia(iay)' "${params[@]}"
;;
*)
;;
esac
fi
if [[ $dns_domain_count -gt 0 ]]; then
case "${namesrv}" in
"netconfig")
log "Updating ${rslvcnf} domain search list with: '${dns_domains[*]}'"
edit_resolv_conf_search "${dns_domains[@]}"
;;
"systemd-resolve")
params=("$device_index" "$dns_domain_count" "${dns_domains[@]}")
log "Calling SetLinkDomains with parameters: '${params[*]}'"
# Example: SetLinkDomains 'ia(sb)' amazon.com false example.com false
call_systemd_resolved SetLinkDomains 'ia(sb)' "${params[@]}"
;;
*)
;;
esac
fi
test "${namesrv}" = "netconfig" && refresh_nscd || check_systemd_resolve_status
}
reset_dns() {
log "Restoring DNS setting to the state before VPN connection"
case "${namesrv}" in
"netconfig")
${netconf} update -f
;;
"systemd-resolve")
local device_index
get_device_index
call_systemd_resolved RevertLink i "$device_index"
check_systemd_resolve_status
;;
*)
;;
esac
}
# Script args example: tun0 1500 1552 10.0.1.2 255.255.255.224 init
main() {
local dist
declare namesrv
declare rslvcnf
declare netconf
configure_logging
get_distribution
case "${dist}" in
"openSUSE")
namesrv="netconfig"
rslvcnf="/etc/resolv.conf"
netconf="/sbin/netconfig"
;;
"Ubuntu")
namesrv="systemd-resolve"
;;
*)
log "Unsupported distribution: ${dist}"
exit 1
;;
esac
# $script_type is set by OpenVPN
log "Executing $script_type script with parameters '$*' under $dist"
if [[ $script_type == *up* ]]; then
configure_dns
elif [[ $script_type == *down* ]]; then
reset_dns
fi
}
main "$@"
--- configure-dns.orig 2023-02-13 11:40:47.000000000 -0800
+++ configure-dns 2023-04-14 22:08:29.517441148 -0700
@@ -1,4 +1,8 @@
#!/usr/bin/env bash
+#
+# Modified to support distributions that rely either on systemd-resolve
+# (such as Ubuntu 18.04 or 20.04) or on NetworkManager/netconfig (such
+# as OpenSUSE Leap/Tumbleweed) for DNS resolver setup
configure_logging() {
log_folder="/var/log/aws-vpn-client"
@@ -20,6 +24,14 @@
echo "$(date) $1" >> "$log_file"
}
+# Get distribution via lsb-release in order to support more
+# than just Ubuntu 18.04 LTS (bionic) and 20.04 LTS (focal)
+get_distribution() {
+ # if available, use LSB release utility
+ LSBREL=$(type -p lsb-release)
+ test -n "${LSBREL}" && dist=$("${LSBREL}" -s -i) || dist=""
+}
+
check_systemd_resolve_status() {
output=$(systemd-resolve --status 2>&1)
exit_code=$?
@@ -38,6 +50,36 @@
fi
}
+# Remove DNS name servers from the resolver config
+# file and add the ones passed on the argument list
+edit_resolv_conf_servers() {
+ cp ${rslvcnf} ${rslvcnf}.ovpnsave
+ grep -v -e nameserver ${rslvcnf}.ovpnsave > ${rslvcnf}
+ for dsrv in "$@"; do
+ log "Adding DNS server: ${dsrv}"
+ echo "nameserver ${dsrv}" >> ${rslvcnf}
+ done
+ rm -f ${rslvcnf}.ovpnsave
+}
+
+# Remove DNS domain search list from the resolver
+# config file and add new one from the argument list
+edit_resolv_conf_search() {
+ cp ${rslvcnf} ${rslvcnf}.ovpnsave
+ grep -v -e search ${rslvcnf}.ovpnsave > ${rslvcnf}
+ log "Adding domain search list: ${*}"
+ echo "search ${@}" >> ${rslvcnf}
+ rm -f ${rslvcnf}.ovpnsave
+}
+
+# Refresh the Name Service Cache by restarting the nscd daemon
+refresh_nscd() {
+ log "Refreshing the name service cache"
+ output=$(systemctl restart nscd 2>&1)
+ exit_code=$?
+ log "systemctl restart nscd exit code: $exit_code, output: $output"
+}
+
# $foreign_option_<n> is set by OpenVPN.
# Currently we support DNS and DOMAIN types.
# Search domains are used in the order they appear in the OpenVPN config
@@ -57,23 +99,40 @@
dns_server_count=$((dns_server_count + 1))
# dhcp-option DNS 172.168.0.1 -> 172.168.0.1
dns_server_ip="${fopt_val#dhcp-option DNS }"
- # Append: 2 4 172 168 0 1
- dns_servers+=(2 4 ${dns_server_ip//./ })
+ #
+ # Append: 2 4 172 168 0 1 for the systemd-resolve case
+ # or simply the DNS server IP address for netconfig
+ #
+ test "${namesrv}" = "netconfig" && \
+ dns_servers+=(${dns_server_ip}) || \
+ dns_servers+=(2 4 ${dns_server_ip//./ })
elif [[ "${fopt_val}" == *dhcp-option\ DOMAIN\ * ]]; then
dns_domain_count=$((dns_domain_count + 1))
# dhcp-option DOMAIN example.com -> example.com
dns_domain="${fopt_val#dhcp-option DOMAIN }"
- # https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html
- # "It takes a network interface index and an array of domains, each with a boolean parameter indicating
- # whether the specified domain shall be used as a search domain (false), or just as a routing domain (true)"
- dns_domains+=("${dns_domain}" false)
+ #
+ # For the netconfig case simply add the domain to the list
+ #
+ # For the systemd-resolve case [1]:
+ # "It takes a network interface index and an array of domains, each with
+ # a boolean parameter indicating whether the specified domain shall be
+ # used as a search domain (false), or just as a routing domain (true)"
+ # [1] https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html
+ #
+ test "${namesrv}" = "netconfig" && \
+ dns_domains+=(${dns_domain}) || \
+ dns_domains+=("${dns_domain}" false)
elif [[ "${fopt_val}" == *dhcp-option\ DOMAIN-ROUTE* ]]; then
dns_domain_count=$((dns_domain_count + 1))
# dhcp-option DOMAIN-ROUTE . -> .
dns_domain="${fopt_val#dhcp-option DOMAIN-ROUTE }"
+ #
# Domain route '~.' needs to be added to prevent DNS leakage
# https://github.com/systemd/systemd/issues/6076#issuecomment-387332572
- dns_domains+=("${dns_domain}" true)
+ #
+ test "${namesrv}" = "netconfig" && \
+ dns_domains+=(${dns_domain}) || \
+ dns_domains+=("${dns_domain}" true)
fi
done
}
@@ -107,36 +166,88 @@
return 0
fi
- get_device_index
+ # The device index is not needed for the netconfig case
+ test "${namesrv}" != "netconfig" && get_device_index
+
if [[ $dns_server_count -gt 0 ]]; then
- params=("$device_index" "$dns_server_count" "${dns_servers[@]}")
- log "Calling SetLinkDNS with parameters: '${params[*]}'"
- # Example: SetLinkDNS 'ia(iay)' 125 2 2 4 172 31 29 109 2 4 172 31 4 164
- call_systemd_resolved SetLinkDNS 'ia(iay)' "${params[@]}"
+ case "${namesrv}" in
+ "netconfig")
+ log "Updating ${rslvcnf} DNS servers with: '${dns_servers[*]}'"
+ edit_resolv_conf_servers "${dns_servers[@]}"
+ ;;
+ "systemd-resolve")
+ params=("$device_index" "$dns_server_count" "${dns_servers[@]}")
+ log "Calling SetLinkDNS with parameters: '${params[*]}'"
+ # Example: SetLinkDNS 'ia(iay)' 125 2 2 4 172 31 29 109 2 4 172 31 4 164
+ call_systemd_resolved SetLinkDNS 'ia(iay)' "${params[@]}"
+ ;;
+ *)
+ ;;
+ esac
fi
if [[ $dns_domain_count -gt 0 ]]; then
- params=("$device_index" "$dns_domain_count" "${dns_domains[@]}")
- log "Calling SetLinkDomains with parameters: '${params[*]}'"
- # Example: SetLinkDomains 'ia(sb)' amazon.com false example.com false
- call_systemd_resolved SetLinkDomains 'ia(sb)' "${params[@]}"
+ case "${namesrv}" in
+ "netconfig")
+ log "Updating ${rslvcnf} domain search list with: '${dns_domains[*]}'"
+ edit_resolv_conf_search "${dns_domains[@]}"
+ ;;
+ "systemd-resolve")
+ params=("$device_index" "$dns_domain_count" "${dns_domains[@]}")
+ log "Calling SetLinkDomains with parameters: '${params[*]}'"
+ # Example: SetLinkDomains 'ia(sb)' amazon.com false example.com false
+ call_systemd_resolved SetLinkDomains 'ia(sb)' "${params[@]}"
+ ;;
+ *)
+ ;;
+ esac
fi
- check_systemd_resolve_status
+ test "${namesrv}" = "netconfig" && refresh_nscd || check_systemd_resolve_status
}
reset_dns() {
log "Restoring DNS setting to the state before VPN connection"
- local device_index
- get_device_index
- call_systemd_resolved RevertLink i "$device_index"
- check_systemd_resolve_status
+ case "${namesrv}" in
+ "netconfig")
+ ${netconf} update -f
+ ;;
+ "systemd-resolve")
+ local device_index
+ get_device_index
+ call_systemd_resolved RevertLink i "$device_index"
+ check_systemd_resolve_status
+ ;;
+ *)
+ ;;
+ esac
}
# Script args example: tun0 1500 1552 10.0.1.2 255.255.255.224 init
main() {
+ local dist
+ declare namesrv
+ declare rslvcnf
+ declare netconf
+
configure_logging
-
+ get_distribution
+
+ case "${dist}" in
+ "openSUSE")
+ namesrv="netconfig"
+ rslvcnf="/etc/resolv.conf"
+ netconf="/sbin/netconfig"
+ ;;
+ "Ubuntu")
+ namesrv="systemd-resolve"
+ ;;
+ *)
+ log "Unsupported distribution: ${dist}"
+ exit 1
+ ;;
+ esac
+
# $script_type is set by OpenVPN
- log "Executing $script_type script with parameters '$*'"
+ log "Executing $script_type script with parameters '$*' under $dist"
if [[ $script_type == *up* ]]; then
configure_dns
@@ -146,3 +257,4 @@
}
main "$@"
+
@pvanek
Copy link

pvanek commented Jun 7, 2024

awesome! The only thing is that it keeps AWS DNS IP after the disconnect. sudo rcnetwork restart fixes it though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment