For host-managed container network interfaces using DHCP, dhclient may overwrite /etc/resolv.conf with DNS information supplied by the DHCP server, causing any explicitly configured nameserver/searchdomain to be silently ignored.
Fix this with a custom dhclient.conf that ensures the container config takes precedence over any DNS settings offered by the DHCP server. Signed-off-by: Filip Schauer <[email protected]> --- src/PVE/LXC.pm | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm index 9633451..c4d4a11 100644 --- a/src/PVE/LXC.pm +++ b/src/PVE/LXC.pm @@ -5,7 +5,7 @@ use warnings; use Cwd qw(); use Errno qw(ELOOP ENOTDIR EROFS ECONNREFUSED EEXIST); -use Fcntl qw(O_RDONLY O_WRONLY O_NOFOLLOW O_DIRECTORY O_CREAT :mode); +use Fcntl qw(O_RDONLY O_WRONLY O_NOFOLLOW O_DIRECTORY O_CREAT SEEK_SET :mode); use File::Basename; use File::Path; use File::Spec; @@ -1324,12 +1324,42 @@ sub get_interfaces { } sub manage_dhclient { - my ($action, $vmid, $ipversion, $eth, $rootdir) = @_; + my ($action, $vmid, $conf, $ipversion, $eth, $rootdir) = @_; File::Path::make_path("/var/lib/lxc/$vmid/hook") if $action eq 'start'; my $pidfile = "/var/lib/lxc/$vmid/hook/dhclient$ipversion-$eth.pid"; my $leasefile = "/var/lib/lxc/$vmid/hook/dhclient$ipversion-$eth.leases"; my $scriptfile = '/usr/share/lxc/hooks/dhclient-script'; + + # Copied from /etc/dhcp/dhclient.conf in isc-dhcp-client Debian package + my $config = + "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n" + . "send host-name = gethostname();\n" + . "request subnet-mask, broadcast-address, time-offset, routers, " + . "domain-name, domain-name-servers, domain-search, host-name, " + . "dhcp6.name-servers, dhcp6.domain-search, dhcp6.fqdn, dhcp6.sntp-servers, " + . "netbios-name-servers, netbios-scope, interface-mtu, " + . "rfc3442-classless-static-routes, ntp-servers;\n"; + + if ($ipversion == 6) { + if (my @nameservers = grep { /$IPV6RE/ } PVE::Tools::split_list($conf->{nameserver})) { + $config .= "supersede dhcp6.name-servers " . join(", ", @nameservers) . ";\n"; + } + } elsif (my @nameservers = grep { /$IPV4RE/ } PVE::Tools::split_list($conf->{nameserver})) { + $config .= "supersede domain-name-servers " . join(", ", @nameservers) . ";\n"; + } + + if (my @searchdomains = PVE::Tools::split_list($conf->{searchdomain})) { + $config .= "supersede " . (($ipversion == 6) ? "dhcp6." : "") . "domain-search "; + $config .= join(", ", map { "\"$_\"" } @searchdomains) . ";\n"; + } + + open(my $configfh, '+>', undef) or die "failed to create dhclient config: $!"; + print $configfh $config; + $configfh->flush(); + sysseek($configfh, 0, SEEK_SET); + my $configfile = "/proc/$$/fd/" . fileno($configfh); + PVE::Tools::run_command([ 'lxc-attach', '-n', @@ -1351,6 +1381,8 @@ sub manage_dhclient { "ROOTFS=$rootdir", '-sf', $scriptfile, + '-cf', + $configfile, $eth, ]); } @@ -1411,10 +1443,10 @@ sub update_ipconfig { my $is_real_ip = ($newip && $newip !~ /^(?:auto|dhcp|manual)$/); if ($change_ip) { if ($newnet->{'host-managed'} && $newip && $newip eq 'dhcp') { - manage_dhclient('start', $vmid, $ipversion, $eth, $rootdir); + manage_dhclient('start', $vmid, $conf, $ipversion, $eth, $rootdir); } if ($optdata->{'host-managed'} && $oldip && $oldip eq 'dhcp') { - manage_dhclient('stop', $vmid, $ipversion, $eth, $rootdir); + manage_dhclient('stop', $vmid, $conf, $ipversion, $eth, $rootdir); } if ($is_real_ip) { @@ -1425,7 +1457,7 @@ sub update_ipconfig { } } } elsif ($optdata->{'host-managed'} && !$newnet->{'host-managed'}) { - manage_dhclient('stop', $vmid, $ipversion, $eth, $rootdir); + manage_dhclient('stop', $vmid, $conf, $ipversion, $eth, $rootdir); } # step 2: replace gateway @@ -3227,12 +3259,12 @@ sub vm_start { my $rootdir = "/proc/$pid/root"; for my $eth (@managed_dhcpv4_interfaces) { - eval { manage_dhclient('start', $vmid, 4, $eth, $rootdir) }; + eval { manage_dhclient('start', $vmid, $conf, 4, $eth, $rootdir) }; PVE::RESTEnvironment::log_warn("DHCP failed - $@") if $@; } for my $eth (@managed_dhcpv6_interfaces) { - eval { manage_dhclient('start', $vmid, 6, $eth, $rootdir) }; + eval { manage_dhclient('start', $vmid, $conf, 6, $eth, $rootdir) }; PVE::RESTEnvironment::log_warn("DHCP failed - $@") if $@; } -- 2.47.3
