mirror of
https://github.com/vhaudiquet/ecrou.git
synced 2026-11-12 22:22:44 +00:00
fix(rootfs): prefer the uplink resolver, drop loopback nameservers
Copying /etc/resolv.conf verbatim gives the guest systemd-resolved's stub at 127.0.0.53, and loopback never crosses the engine's socket proxy: the name lookup dies inside the guest. Source the uplink config from /run/systemd/resolve first and strip 127.x nameserver lines from whatever is copied.
This commit is contained in:
1 parent
7d9dc6650e
commit
7adbf41f59
1 file changed
+54
-5
@@ -303,20 +303,33 @@ fn extract_prepared(
|
|||||||
/// guest has whatever the image shipped, which is usually nothing.
|
/// guest has whatever the image shipped, which is usually nothing.
|
||||||
/// Best effort: a rootfs with its own resolver config keeps it.
|
/// Best effort: a rootfs with its own resolver config keeps it.
|
||||||
fn provision_resolver(rootfs: &Path) {
|
fn provision_resolver(rootfs: &Path) {
|
||||||
let host = match std::fs::read_to_string("/etc/resolv.conf") {
|
// The engine proxies guest sockets to the host's stack, so any
|
||||||
Ok(s) if !s.trim().is_empty() => s,
|
// host-reachable nameserver works in the machine - but loopback
|
||||||
|
// addresses do not cross: 127.0.0.53 stays inside the guest, where
|
||||||
|
// nothing listens. So the source of truth is systemd-resolved's
|
||||||
|
// uplink config when present, and loopback nameservers are dropped
|
||||||
|
// from whatever is copied. Best effort: failures leave the
|
||||||
|
// image's file.
|
||||||
|
let host = match std::fs::read_to_string("/run/systemd/resolve/resolv.conf")
|
||||||
|
.or_else(|_| std::fs::read_to_string("/etc/resolv.conf"))
|
||||||
|
{
|
||||||
|
Ok(s) if !s.trim().is_empty() => drop_loopback_nameservers(&s),
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
log::debug!("host /etc/resolv.conf is empty; leaving the image's in place");
|
log::debug!("host resolver config is empty; leaving the image's in place");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"no host /etc/resolv.conf ({}); leaving the image's in place",
|
"no host resolver config ({}); leaving the image's in place",
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if !host.contains("nameserver ") {
|
||||||
|
log::debug!("host resolver config has no usable nameserver; leaving the image's");
|
||||||
|
return;
|
||||||
|
}
|
||||||
let target = rootfs.join("etc/resolv.conf");
|
let target = rootfs.join("etc/resolv.conf");
|
||||||
if let Err(e) = std::fs::remove_file(&target) {
|
if let Err(e) = std::fs::remove_file(&target) {
|
||||||
if e.kind() != std::io::ErrorKind::NotFound {
|
if e.kind() != std::io::ErrorKind::NotFound {
|
||||||
@@ -328,11 +341,30 @@ fn provision_resolver(rootfs: &Path) {
|
|||||||
let _ = std::fs::create_dir_all(parent);
|
let _ = std::fs::create_dir_all(parent);
|
||||||
}
|
}
|
||||||
match std::fs::write(&target, host) {
|
match std::fs::write(&target, host) {
|
||||||
Ok(()) => log::debug!("provisioned the host resolver into {}", target.display()),
|
Ok(()) => log::debug!("provisioned a resolver into {}", target.display()),
|
||||||
Err(e) => log::debug!("could not write {}: {}", target.display(), e),
|
Err(e) => log::debug!("could not write {}: {}", target.display(), e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lines naming a loopback nameserver go: the guest's loopback is its
|
||||||
|
/// own, and the stub resolvers live on the host's.
|
||||||
|
fn drop_loopback_nameservers(config: &str) -> String {
|
||||||
|
config
|
||||||
|
.lines()
|
||||||
|
.filter(|line| {
|
||||||
|
let rest = match line.trim_start().strip_prefix("nameserver ") {
|
||||||
|
Some(r) => r.trim(),
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
!rest.starts_with("127.")
|
||||||
|
})
|
||||||
|
.fold(String::new(), |mut acc, line| {
|
||||||
|
acc.push_str(line);
|
||||||
|
acc.push('\n');
|
||||||
|
acc
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Write a tarball of `rootfs` at `dest`, compressed according to dest's
|
/// Write a tarball of `rootfs` at `dest`, compressed according to dest's
|
||||||
/// extension (.tar.gz, .tar.xz, .tar.zst, plain .tar), atomically via a
|
/// extension (.tar.gz, .tar.xz, .tar.zst, plain .tar), atomically via a
|
||||||
/// `.partial` file.
|
/// `.partial` file.
|
||||||
@@ -481,6 +513,23 @@ fn get_tarball_extension(url: &str) -> &str {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::drop_loopback_nameservers;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn loopback_nameservers_dropped() {
|
||||||
|
let config = "# comment\nnameserver 127.0.0.53\noptions edns0\nnameserver 192.168.1.1\nnameserver 127.0.0.1\n";
|
||||||
|
assert_eq!(
|
||||||
|
drop_loopback_nameservers(config),
|
||||||
|
"# comment\noptions edns0\nnameserver 192.168.1.1\n"
|
||||||
|
);
|
||||||
|
// A search line whose address-ish token is untouched.
|
||||||
|
let config = "search 127.example\nnameserver 10.0.0.1\n";
|
||||||
|
assert_eq!(
|
||||||
|
drop_loopback_nameservers(config),
|
||||||
|
"search 127.example\nnameserver 10.0.0.1\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::progress::NoProgress;
|
use crate::progress::NoProgress;
|
||||||
use std::os::unix::fs::PermissionsExt;
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
|||||||
Reference in new issue
Block a user