Enhance UVM installation and configuration for Ubuntu 22.04 root filesystem
- Update README and USAGE documentation to reflect changes in rootfs handling, including the default local build from Ubuntu 22.04 packages. - Modify the installer to support building the rootfs locally with authenticated packages and to require SHA-256 checksums for prebuilt images. - Implement checks for setuid permissions on sudo and update the provisioning logic for guest users to ensure proper sudo access. - Adjust tests to validate the new rootfs building process and ensure that existing functionality is preserved. - Refactor code to improve clarity and maintainability, including the introduction of constants for Ubuntu release and guest packages.
This commit is contained in:
+37
-3
@@ -226,6 +226,8 @@ class ImageStore:
|
||||
encoding="utf-8",
|
||||
)
|
||||
shadow.path.chmod(shadow.mode)
|
||||
if username != "root":
|
||||
self._provision_guest_sudo(disk, username, temporary)
|
||||
|
||||
sshd_config.path.write_text(
|
||||
_enable_ssh_password_authentication(
|
||||
@@ -244,6 +246,31 @@ class ImageStore:
|
||||
except (OSError, UnicodeError) as error:
|
||||
raise UvmError(f"could not provision guest credentials: {error}") from error
|
||||
|
||||
def _provision_guest_sudo(self, disk: Path, username: str, temporary: Path) -> None:
|
||||
sudoers = self._read_guest_file(disk, "/etc/sudoers", temporary / "sudoers", required=False)
|
||||
sudo = self._runner.run(("debugfs", "-R", "stat /usr/bin/sudo", disk), capture=True)
|
||||
mode = _MODE_PATTERN.search(sudo.stdout)
|
||||
owner = _OWNER_PATTERN.search(sudo.stdout)
|
||||
if (
|
||||
sudoers is None
|
||||
or re.search(r"Type:\s+regular\b", sudo.stdout) is None
|
||||
or mode is None or not int(mode.group(1), 8) & 0o4000
|
||||
or owner is None or int(owner.group(1)) != 0
|
||||
):
|
||||
raise UvmError(
|
||||
"guest image needs setuid-root sudo and /etc/sudoers for a custom administrator; "
|
||||
"run uvm install --force to build the complete Ubuntu image"
|
||||
)
|
||||
contents = sudoers.path.read_text(encoding="utf-8").rstrip()
|
||||
sudoers.path.write_text(
|
||||
contents + "\n\n# Managed by uvm: guest administrator\n"
|
||||
+ f"Defaults:{username} !rootpw, !runaspw, !targetpw\n"
|
||||
+ f"{username} ALL=(ALL:ALL) PASSWD: ALL\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
sudoers.path.chmod(sudoers.mode)
|
||||
self._write_guest_file(disk, "/etc/sudoers", sudoers)
|
||||
|
||||
def _create_guest_user(
|
||||
self,
|
||||
disk: Path,
|
||||
@@ -562,12 +589,16 @@ def _enable_ssh_password_authentication(contents: str, username: str) -> str:
|
||||
}
|
||||
if username == "root":
|
||||
managed_directives.add("permitrootlogin")
|
||||
unmanaged_lines = []
|
||||
global_lines = []
|
||||
match_lines = []
|
||||
in_match = False
|
||||
for line in contents.splitlines():
|
||||
stripped = line.lstrip()
|
||||
directive = stripped.split(None, 1)[0].lower() if stripped else ""
|
||||
if directive == "match":
|
||||
in_match = True
|
||||
if stripped.startswith("#") or directive not in managed_directives:
|
||||
unmanaged_lines.append(line)
|
||||
(match_lines if in_match else global_lines).append(line)
|
||||
|
||||
directives = [
|
||||
"HostKey /etc/ssh/ssh_host_rsa_key",
|
||||
@@ -579,13 +610,16 @@ def _enable_ssh_password_authentication(contents: str, username: str) -> str:
|
||||
]
|
||||
if username == "root":
|
||||
directives.append("PermitRootLogin yes")
|
||||
# Global-only directives (e.g. UsePAM and Subsystem) must precede every Match block.
|
||||
globals_text = "\n".join(global_lines).strip("\n")
|
||||
return (
|
||||
"# Managed by uvm\n"
|
||||
+ "\n".join(directives)
|
||||
+ (f"\n{globals_text}" if globals_text else "")
|
||||
+ f"\nMatch User {username}\n"
|
||||
+ " PasswordAuthentication yes\n"
|
||||
+ "Match all\n"
|
||||
+ "\n".join(unmanaged_lines).rstrip()
|
||||
+ "\n".join(match_lines).rstrip()
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user