]> git.hoellein.online Git - zenbook/commitdiff
committing changes in /etc made by "apt install openvpn-systemd-resolved"
authorroot <root@localhost>
Thu, 14 Nov 2019 08:25:19 +0000 (09:25 +0100)
committerroot <root@localhost>
Thu, 14 Nov 2019 08:25:19 +0000 (09:25 +0100)
Package changes:
+libnss-resolve 240-6ubuntu5.7 amd64
+openvpn-systemd-resolved 1.2.7-1 amd64

.etckeeper
nsswitch.conf
openvpn/update-systemd-resolved [new file with mode: 0755]

index 9599de2b18d6e56d4338d0bc952bd19b56535e9a..efc16720750729d0c242aba3b4d3efee5ca72e5e 100755 (executable)
@@ -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'
index b79473449d602c868e0b36988ff4a865b2345645..acb284384d907971be938fa442b09197266e5625 100644 (file)
@@ -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 (executable)
index 0000000..04c244c
--- /dev/null
@@ -0,0 +1,421 @@
+#!/usr/bin/env bash
+#
+# OpenVPN helper to add DHCP information into systemd-resolved via DBus.
+# Copyright (C) 2016, Jonathan Wright <jon@than.io>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+# 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