From: root Date: Thu, 14 Nov 2019 08:25:19 +0000 (+0100) Subject: committing changes in /etc made by "apt install openvpn-systemd-resolved" X-Git-Url: https://git.hoellein.online/?a=commitdiff_plain;h=7995139aff8fd453342c40de6ef6d8c575dfe72e;p=zenbook committing changes in /etc made by "apt install openvpn-systemd-resolved" Package changes: +libnss-resolve 240-6ubuntu5.7 amd64 +openvpn-systemd-resolved 1.2.7-1 amd64 --- diff --git a/.etckeeper b/.etckeeper index 9599de2..efc1672 100755 --- a/.etckeeper +++ b/.etckeeper @@ -2421,6 +2421,7 @@ maybe chmod 0755 'openvpn' maybe chmod 0755 'openvpn/client' maybe chmod 0755 'openvpn/server' maybe chmod 0755 'openvpn/update-resolv-conf' +maybe chmod 0755 'openvpn/update-systemd-resolved' maybe chmod 0755 'opt' maybe chown '1002' 'opt/Adobe' maybe chgrp '1002' 'opt/Adobe' diff --git a/nsswitch.conf b/nsswitch.conf index b794734..acb2843 100644 --- a/nsswitch.conf +++ b/nsswitch.conf @@ -9,7 +9,7 @@ group: compat systemd shadow: compat gshadow: files -hosts: files mdns4_minimal [NOTFOUND=return] dns myhostname +hosts: files mdns4_minimal [NOTFOUND=return] resolve [!UNAVAIL=return] dns myhostname networks: files protocols: db files diff --git a/openvpn/update-systemd-resolved b/openvpn/update-systemd-resolved new file mode 100755 index 0000000..04c244c --- /dev/null +++ b/openvpn/update-systemd-resolved @@ -0,0 +1,421 @@ +#!/usr/bin/env bash +# +# OpenVPN helper to add DHCP information into systemd-resolved via DBus. +# Copyright (C) 2016, Jonathan Wright +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This script will parse DHCP options set via OpenVPN (dhcp-option) to update +# systemd-resolved directly via DBus, instead of updating /etc/resolv.conf. To +# install, set as the 'up' and 'down' script in your OpenVPN configuration file +# or via the command-line arguments, alongside setting the 'down-pre' option to +# run the 'down' script before the device is closed. For example: +# up /etc/openvpn/scripts/update-systemd-resolved +# down /etc/openvpn/scripts/update-systemd-resolved +# down-pre + +# Define what needs to be called via DBus +DBUS_DEST="org.freedesktop.resolve1" +DBUS_NODE="/org/freedesktop/resolve1" + +SCRIPT_NAME="${BASH_SOURCE[0]##*/}" + +log() { + logger -s -t "$SCRIPT_NAME" "$@" +} + +for level in emerg err warning info debug; do + printf -v functext -- '%s() { log -p user.%s -- "$@" ; }' "$level" "$level" + eval "$functext" +done + +usage() { + err "${1:?${1}. }. Usage: ${SCRIPT_NAME} up|down device_name." +} + +busctl_call() { + # Preserve busctl's exit status + busctl call "$DBUS_DEST" "$DBUS_NODE" "${DBUS_DEST}.Manager" "$@" || { + local -i status=$? + emerg "'busctl' exited with status $status" + return $status + } +} + +get_link_info() { + dev="$1" + shift + + link='' + link="$(ip link show dev "$dev")" || return $? + + echo "$dev" "${link%%:*}" +} + +dhcp_settings() { + for foreign_option in "${!foreign_option_@}"; do + foreign_option_value="${!foreign_option}" + + [[ "$foreign_option_value" == *dhcp-option* ]] \ + && echo "${foreign_option_value#dhcp-option }" + done +} + +up() { + local link="$1" + shift + local if_index="$1" + shift + + info "Link '$link' coming up" + + # Preset values for processing -- will be altered in the various process_* + # functions. + local -a dns_servers=() dns_domain=() dns_search=() dns_routed=() + local -i dns_server_count=0 dns_domain_count=0 dns_search_count=0 dns_routed_count=0 + local dns_sec="" + + while read -r setting; do + setting_type="${setting%% *}" + setting_value="${setting#* }" + + process_setting_function="${setting_type,,}" + process_setting_function="process_${process_setting_function//-/_}" + + if declare -f "$process_setting_function" &>/dev/null; then + "$process_setting_function" "$setting_value" || return $? + else + warning "Not a recognized DHCP setting: '${setting}'" + fi + done < <(dhcp_settings) + + if [[ "${#dns_servers[*]}" -gt 0 ]]; then + busctl_params=("$if_index" "$dns_server_count" "${dns_servers[@]}") + info "SetLinkDNS(${busctl_params[*]})" + busctl_call SetLinkDNS 'ia(iay)' "${busctl_params[@]}" || return $? + fi + + if [[ "${#dns_domain[*]}" -gt 0 \ + || "${#dns_search[*]}" -gt 0 \ + || "${#dns_routed[*]}" -gt 0 ]]; then + dns_count=$((dns_domain_count+dns_search_count+dns_routed_count)) + busctl_params=("$if_index" "$dns_count") + if [[ "${#dns_domain[*]}" -gt 0 ]]; then + busctl_params+=("${dns_domain[@]}") + fi + if [[ "${#dns_search[*]}" -gt 0 ]]; then + busctl_params+=("${dns_search[@]}") + fi + if [[ "${#dns_routed[*]}" -gt 0 ]]; then + busctl_params+=("${dns_routed[@]}") + fi + info "SetLinkDomains(${busctl_params[*]})" + busctl_call SetLinkDomains 'ia(sb)' "${busctl_params[@]}" || return $? + fi + + if [[ -n "${dns_sec}" ]]; then + if [[ "${dns_sec}" == "default" ]]; then + # We need to provide an empty string to use the default settings + info "SetLinkDNSSEC($if_index '')" + busctl_call SetLinkDNSSEC 'is' "$if_index" "" || return $? + else + info "SetLinkDNSSEC($if_index ${dns_sec})" + busctl_call SetLinkDNSSEC 'is' "$if_index" "${dns_sec}" || return $? + fi + fi +} + +down() { + local link="$1" + shift + local if_index="$1" + shift + + info "Link '$link' going down" + if [[ "$(whoami 2>/dev/null)" != "root" ]]; then + # Cleanly handle the priviledge dropped case by not calling RevertLink + info "Priviledges dropped in the client: Cannot call RevertLink." + else + busctl_call RevertLink i "$if_index" + fi +} + +process_dns() { + address="$1" + shift + + if looks_like_ipv6 "$address"; then + process_dns_ipv6 "$address" || return $? + elif looks_like_ipv4 "$address"; then + process_dns_ipv4 "$address" || return $? + else + err "Not a valid IPv6 or IPv4 address: '$address'" + return 1 + fi +} + +looks_like_ipv4() { + [[ -n "$1" ]] && { + local dots="${1//[^.]}" + (( ${#dots} == 3 )) + } +} + +looks_like_ipv6() { + [[ -n "$1" ]] && { + local colons="${1//[^:]}" + (( ${#colons} >= 2 )) + } +} + +process_dns_ipv4() { + local address="$1" + shift + + info "Adding IPv4 DNS Server ${address}" + (( dns_server_count += 1 )) + dns_servers+=(2 4 ${address//./ }) +} + +# Enforces RFC 5952: +# 1. Don't shorten a single 0 field to '::' +# 2. Only longest run of zeros should be compressed +# 3. If there are multiple longest runs, the leftmost should be compressed +# 4. Address must be maximally compressed, so no all-zero runs next to '::' +# +# ... +# +# Thank goodness we don't have to handle port numbers, though :) +parse_ipv6() { + local raw_address="$1" + + log_invalid_ipv6() { + local message="'$raw_address' is not a valid IPv6 address" + emerg "${message}: $*" + } + + trap -- 'unset -f log_invalid_ipv6' RETURN + + if [[ "$raw_address" == *::*::* ]]; then + log_invalid_ipv6 "address cannot contain more than one '::'" + return 1 + elif [[ "$raw_address" =~ :0+:: ]] || [[ "$raw_address" =~ ::0+: ]]; then + log_invalid_ipv6 "address contains a 0-group adjacent to '::' and is not maximally shortened" + return 1 + fi + + local -i length=8 + local -a raw_segments=() + + IFS=$':' read -r -a raw_segments <<<"$raw_address" + + local -i raw_length="${#raw_segments[@]}" + + if (( raw_length > length )); then + log_invalid_ipv6 "expected ${length} segments, got ${raw_length}" + return 1 + fi + + # Store zero-runs keyed to their sizes, storing all non-zero segments prefixed + # with a token marking them as such. + local nonzero_prefix=$'!' + local -i zero_run_i=0 compressed_i=0 + local -a tokenized_segments=() + local decimal_segment='' next_decimal_segment='' + + for (( i = 0 ; i < raw_length ; i++ )); do + raw_segment="${raw_segments[i]}" + + printf -v decimal_segment -- '%d' "0x${raw_segment:-0}" + + # We're in the compressed group. The length of this run should be + # enough to bring the total number of segments to 8. + if [[ -z "$raw_segment" ]]; then + (( compressed_i = zero_run_i )) + + # `+ 1' because the length of the current segment is counted in + # `raw_length'. + (( tokenized_segments[zero_run_i] = ((length - raw_length) + 1) )) + + # If we have an address like `::1', skip processing the next group to + # avoid double-counting the zero-run, and increment the number of + # 0-groups to add since the second empty group is counted in + # `raw_length'. + if [[ -z "${raw_segments[i + 1]}" ]]; then + (( i++ )) + (( tokenized_segments[zero_run_i]++ )) + fi + + (( zero_run_i++ )) + elif (( decimal_segment == 0 )); then + (( tokenized_segments[zero_run_i]++ )) + + # The run is over if the next segment is not 0, so increment the + # tracking index. + printf -v next_decimal_segment -- '%d' "0x${raw_segments[i + 1]}" + + (( next_decimal_segment != 0 )) && (( zero_run_i++ )) + else + # Prefix the raw segment with `nonzero_prefix' to mark this as a + # non-zero field. + tokenized_segments[zero_run_i]="${nonzero_prefix}${decimal_segment}" + (( zero_run_i++ )) + fi + done + + if [[ "$raw_address" == *::* ]]; then + if (( ${#tokenized_segments[*]} == length )); then + log_invalid_ipv6 "single '0' fields should not be compressed" + return 1 + else + local -i largest_run_i=0 largest_run=0 + + for (( i = 0 ; i < ${#tokenized_segments[@]}; i ++ )); do + # Skip groups that aren't zero-runs + [[ "${tokenized_segments[i]:0:1}" == "$nonzero_prefix" ]] && continue + + if (( tokenized_segments[i] > largest_run )); then + (( largest_run_i = i )) + largest_run="${tokenized_segments[i]}" + fi + done + + local -i compressed_run="${tokenized_segments[compressed_i]}" + + if (( largest_run > compressed_run )); then + log_invalid_ipv6 "the compressed run of all-zero fields is smaller than the largest such run" + return 1 + elif (( largest_run == compressed_run )) && (( largest_run_i < compressed_i )); then + log_invalid_ipv6 "only the leftmost largest run of all-zero fields should be compressed" + return 1 + fi + fi + fi + + for segment in "${tokenized_segments[@]}"; do + if [[ "${segment:0:1}" == "$nonzero_prefix" ]]; then + printf -- '%04x\n' "${segment#${nonzero_prefix}}" + else + for (( n = 0 ; n < segment ; n++ )); do + echo 0000 + done + fi + done +} + +process_dns_ipv6() { + local address="$1" + shift + + info "Adding IPv6 DNS Server ${address}" + + local -a segments=() + segments=($(parse_ipv6 "$address")) || return $? + + # Add AF_INET6 and byte count + dns_servers+=(10 16) + for segment in "${segments[@]}"; do + dns_servers+=("$((16#${segment:0:2}))" "$((16#${segment:2:2}))") + done + + (( dns_server_count += 1 )) +} + +process_domain() { + local domain="$1" + shift + + info "Setting DNS Domain ${domain}" + (( dns_domain_count = 1 )) + dns_domain=("${domain}" false) +} + +process_adapter_domain_suffix() { + # This enables support for ADAPTER_DOMAIN_SUFFIX which is a Microsoft standard + # which works in the same way as DOMAIN to set the primary search domain on + # this specific link. + process_domain "$@" +} + +process_domain_search() { + local domain="$1" + shift + + info "Adding DNS Search Domain ${domain}" + (( dns_search_count += 1 )) + dns_search+=("${domain}" false) +} + +process_domain_route() { + local domain="$1" + shift + + info "Adding DNS Routed Domain ${domain}" + (( dns_routed_count += 1 )) + dns_routed+=("${domain}" true) +} + +process_dnssec() { + local option="$1" setting="" + shift + + case "${option,,}" in + yes|true) + setting="yes" ;; + no|false) + setting="no" ;; + default) + setting="default" ;; + allow-downgrade) + setting="allow-downgrade" ;; + *) + local message="'$option' is not a valid DNSSEC option" + emerg "${message}" + return 1 ;; + esac + + info "Setting DNSSEC to ${setting}" + dns_sec="${setting}" +} + +main() { + local script_type="$1" + shift + local dev="$1" + shift + + if [[ -z "$script_type" ]]; then + usage 'No script type specified' + return 1 + elif [[ -z "$dev" ]]; then + usage 'No device name specified' + return 1 + elif ! declare -f "${script_type}" &>/dev/null; then + usage "Invalid script type: '${script_type}'" + return 1 + else + if ! read -r link if_index _ < <(get_link_info "$dev"); then + usage "Invalid device name: '$dev'" + return 1 + fi + + "$script_type" "$link" "$if_index" "$@" + fi +} + +if [[ "${BASH_SOURCE[0]}" == "$0" ]] || [[ "$AUTOMATED_TESTING" == 1 ]]; then + set -o nounset + + main "${script_type:-}" "${dev:-}" "$@" +fi