From ce77935eeeabcbb8d3510d09d12e0d79e74ad045 Mon Sep 17 00:00:00 2001
From: zsdc <taras@vyos.io>
Date: Thu, 30 Dec 2021 00:41:31 +0200
Subject: dhclient: T4121: Fixed resolv.conf generation at early boot stage

In case if a CLI configuration is not available, dhclient cannot add
nameservers to a `resolv.conf` file, because `vyos-hostsd` requires that
an interface be listed in the `set system name-server` option.
This commit introduces two changes:

* `vyos-hostsd` service will not be started before Cloud-Init fetch all
remote data. This is required because all meta-data should be available
for Cloud-Init before any of VyOS-related services start since it is
used for configuration generation.

* the `vyos-hostsd-client` in the `dhclient-script` will be used only if
the `vyos-hostsd` is running. In other words - if VyOS services already
started, dhclient changes `resolv.conf` using `vyos-hostsd`; in other
cases - does this directly.

These changes should protect us from problems with DHCP during system
boot if DHCP is required by third-party utils.
---
 .../dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf | 82 ++++++++++++----------
 src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup | 31 ++++----
 src/systemd/vyos-hostsd.service                    |  2 +-
 3 files changed, 63 insertions(+), 52 deletions(-)

diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf b/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf
index 24090e2a8..b1902b585 100644
--- a/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf
+++ b/src/etc/dhcp/dhclient-enter-hooks.d/04-vyos-resolvconf
@@ -1,44 +1,48 @@
-# modified make_resolv_conf () for VyOS
-make_resolv_conf() {
-    hostsd_client="/usr/bin/vyos-hostsd-client"
-    hostsd_changes=
+# modified make_resolv_conf() for VyOS
+# should be used only if vyos-hostsd is running
 
-    if [ -n "$new_domain_name" ]; then
-        logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client"
-        $hostsd_client --delete-search-domains --tag "dhcp-$interface"
-        logmsg info "Adding domain name \"$new_domain_name\" as search domain with tag \"dhcp-$interface\" via vyos-hostsd-client"
-        $hostsd_client --add-search-domains "$new_domain_name" --tag "dhcp-$interface"
-        hostsd_changes=y
-    fi
+if /usr/bin/systemctl -q is-active vyos-hostsd; then
+    make_resolv_conf() {
+        hostsd_client="/usr/bin/vyos-hostsd-client"
+        hostsd_changes=
 
-    if [ -n "$new_dhcp6_domain_search" ]; then
-        logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
-        $hostsd_client --delete-search-domains --tag "dhcpv6-$interface"
-        logmsg info "Adding search domain \"$new_dhcp6_domain_search\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
-        $hostsd_client --add-search-domains "$new_dhcp6_domain_search" --tag "dhcpv6-$interface"
-        hostsd_changes=y
-    fi
+        if [ -n "$new_domain_name" ]; then
+            logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client"
+            $hostsd_client --delete-search-domains --tag "dhcp-$interface"
+            logmsg info "Adding domain name \"$new_domain_name\" as search domain with tag \"dhcp-$interface\" via vyos-hostsd-client"
+            $hostsd_client --add-search-domains "$new_domain_name" --tag "dhcp-$interface"
+            hostsd_changes=y
+        fi
 
-    if [ -n "$new_domain_name_servers" ]; then
-        logmsg info "Deleting nameservers with tag \"dhcp-$interface\" via vyos-hostsd-client"
-        $hostsd_client --delete-name-servers --tag "dhcp-$interface"
-        logmsg info "Adding nameservers \"$new_domain_name_servers\" with tag \"dhcp-$interface\" via vyos-hostsd-client"
-        $hostsd_client --add-name-servers $new_domain_name_servers --tag "dhcp-$interface"
-        hostsd_changes=y
-    fi
+        if [ -n "$new_dhcp6_domain_search" ]; then
+            logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+            $hostsd_client --delete-search-domains --tag "dhcpv6-$interface"
+            logmsg info "Adding search domain \"$new_dhcp6_domain_search\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+            $hostsd_client --add-search-domains "$new_dhcp6_domain_search" --tag "dhcpv6-$interface"
+            hostsd_changes=y
+        fi
 
-    if [ -n "$new_dhcp6_name_servers" ]; then
-        logmsg info "Deleting nameservers with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
-        $hostsd_client --delete-name-servers --tag "dhcpv6-$interface"
-        logmsg info "Adding nameservers \"$new_dhcpv6_name_servers\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
-        $hostsd_client --add-name-servers $new_dhcpv6_name_servers --tag "dhcpv6-$interface"
-        hostsd_changes=y
-    fi
+        if [ -n "$new_domain_name_servers" ]; then
+            logmsg info "Deleting nameservers with tag \"dhcp-$interface\" via vyos-hostsd-client"
+            $hostsd_client --delete-name-servers --tag "dhcp-$interface"
+            logmsg info "Adding nameservers \"$new_domain_name_servers\" with tag \"dhcp-$interface\" via vyos-hostsd-client"
+            $hostsd_client --add-name-servers $new_domain_name_servers --tag "dhcp-$interface"
+            hostsd_changes=y
+        fi
 
-    if [ $hostsd_changes ]; then
-        logmsg info "Applying changes via vyos-hostsd-client"
-        $hostsd_client --apply
-    else
-        logmsg info "No changes to apply via vyos-hostsd-client"
-    fi
-}
+        if [ -n "$new_dhcp6_name_servers" ]; then
+            logmsg info "Deleting nameservers with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+            $hostsd_client --delete-name-servers --tag "dhcpv6-$interface"
+            logmsg info "Adding nameservers \"$new_dhcpv6_name_servers\" with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+            $hostsd_client --add-name-servers $new_dhcpv6_name_servers --tag "dhcpv6-$interface"
+            hostsd_changes=y
+        fi
+
+        if [ $hostsd_changes ]; then
+            logmsg info "Applying changes via vyos-hostsd-client"
+            $hostsd_client --apply
+        else
+            logmsg info "No changes to apply via vyos-hostsd-client"
+        fi
+    }
+fi
diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup
index fec792b64..ad6a1d5eb 100644
--- a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup
+++ b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup
@@ -4,14 +4,19 @@
 # NOTE: here we use 'ip' wrapper, therefore a route will be actually deleted via /usr/sbin/ip or vtysh, according to the system state
 hostsd_client="/usr/bin/vyos-hostsd-client"
 hostsd_changes=
+# check vyos-hostsd status
+/usr/bin/systemctl -q is-active vyos-hostsd
+hostsd_status=$?
 
 if [[ $reason =~ (EXPIRE|FAIL|RELEASE|STOP) ]]; then
-    # delete search domains and nameservers via vyos-hostsd
-    logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client"
-    $hostsd_client --delete-search-domains --tag "dhcp-$interface"
-    logmsg info "Deleting nameservers with tag \"dhcp-${interface}\" via vyos-hostsd-client"
-    $hostsd_client --delete-name-servers --tag "dhcp-${interface}"
-    hostsd_changes=y
+    if [[ $hostsd_status -eq 0 ]]; then
+        # delete search domains and nameservers via vyos-hostsd
+        logmsg info "Deleting search domains with tag \"dhcp-$interface\" via vyos-hostsd-client"
+        $hostsd_client --delete-search-domains --tag "dhcp-$interface"
+        logmsg info "Deleting nameservers with tag \"dhcp-${interface}\" via vyos-hostsd-client"
+        $hostsd_client --delete-name-servers --tag "dhcp-${interface}"
+        hostsd_changes=y
+    fi
 
     if_metric="$IF_METRIC"
 
@@ -92,12 +97,14 @@ if [[ $reason =~ (EXPIRE|FAIL|RELEASE|STOP) ]]; then
 fi
 
 if [[ $reason =~ (EXPIRE6|RELEASE6|STOP6) ]]; then
-    # delete search domains and nameservers via vyos-hostsd
-    logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
-    $hostsd_client --delete-search-domains --tag "dhcpv6-$interface"
-    logmsg info "Deleting nameservers with tag \"dhcpv6-${interface}\" via vyos-hostsd-client"
-    $hostsd_client --delete-name-servers --tag "dhcpv6-${interface}"
-    hostsd_changes=y
+    if [[ $hostsd_status -eq 0 ]]; then
+        # delete search domains and nameservers via vyos-hostsd
+        logmsg info "Deleting search domains with tag \"dhcpv6-$interface\" via vyos-hostsd-client"
+        $hostsd_client --delete-search-domains --tag "dhcpv6-$interface"
+        logmsg info "Deleting nameservers with tag \"dhcpv6-${interface}\" via vyos-hostsd-client"
+        $hostsd_client --delete-name-servers --tag "dhcpv6-${interface}"
+        hostsd_changes=y
+    fi
 fi
 
 if [ $hostsd_changes ]; then
diff --git a/src/systemd/vyos-hostsd.service b/src/systemd/vyos-hostsd.service
index b77335778..4da55f518 100644
--- a/src/systemd/vyos-hostsd.service
+++ b/src/systemd/vyos-hostsd.service
@@ -7,7 +7,7 @@ DefaultDependencies=no
 
 # Seemingly sensible way to say "as early as the system is ready"
 # All vyos-hostsd needs is read/write mounted root
-After=systemd-remount-fs.service
+After=systemd-remount-fs.service cloud-init.service
 
 [Service]
 WorkingDirectory=/run/vyos-hostsd
-- 
cgit v1.2.3