mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-10-08 14:36:38 +00:00
fix(oci): set HOME for native OCI runtime
This commit is contained in:
+33
-10
@@ -1011,6 +1011,37 @@ apply_runtime_user() {
|
||||
set_lxc_directive lxc.init.gid "$gid"
|
||||
}
|
||||
|
||||
set_runtime_environment() {
|
||||
local name=$1 value=$2 temporary
|
||||
[[ $name =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || die "$(translate "Invalid variable name:") $name"
|
||||
[[ $value != *$'\n'* && $value != *$'\r'* ]] || die "$(translate "The variable contains line breaks:") $name"
|
||||
if LC_ALL=C grep -q '[[:cntrl:]]' <<<"$value"; then
|
||||
die "$(translate "The variable contains control characters:") $name"
|
||||
fi
|
||||
temporary=$(mktemp)
|
||||
awk -v prefix="lxc.environment.runtime: ${name}=" 'index($0, prefix) != 1' "$CONF" >"$temporary"
|
||||
printf 'lxc.environment.runtime: %s=%s\n' "$name" "$value" >>"$temporary"
|
||||
cat "$temporary" >"$CONF"
|
||||
rm -f "$temporary"
|
||||
}
|
||||
|
||||
ensure_runtime_home() {
|
||||
local uid home rootfs passwd_file
|
||||
grep -q '^lxc.environment.runtime: HOME=' "$CONF" && return 0
|
||||
uid=$(awk -F': ' '$1 == "lxc.init.uid" {print $2; exit}' "$CONF" 2>/dev/null || true)
|
||||
uid=${uid:-0}
|
||||
[[ $uid =~ ^[0-9]+$ ]] || return 0
|
||||
rootfs="/var/lib/lxc/${VMID}/rootfs"
|
||||
passwd_file="${rootfs}/etc/passwd"
|
||||
mount_ct_rootfs
|
||||
if ! home=$(python3 "$OCI_RUNTIME_RESOLVER" --home "$passwd_file" "$uid" 2>>"${OCI_LOG:-/dev/stderr}"); then
|
||||
oci_quiet pct unmount "$VMID" || true
|
||||
return 0
|
||||
fi
|
||||
oci_quiet pct unmount "$VMID" || die "$(translate "Could not unmount the container filesystem")"
|
||||
set_runtime_environment HOME "$home"
|
||||
}
|
||||
|
||||
apply_runtime_groups() {
|
||||
local groups_csv=$1 rootfs="/var/lib/lxc/${VMID}/rootfs"
|
||||
local group_file="${rootfs}/etc/group" existing resolved group failed=0
|
||||
@@ -1883,21 +1914,13 @@ apply_native_device_permissions
|
||||
# PVE represents the public env property as repeated native LXC runtime lines.
|
||||
# Merge only Compose overrides while the newly-created CT is stopped.
|
||||
while IFS=$'\t' read -r NAME ENCODED; do
|
||||
[[ $NAME =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || die "$(translate "Invalid variable name:") $NAME"
|
||||
VALUE=$(printf '%s' "$ENCODED" | base64 -d)
|
||||
[[ $VALUE != *$'\n'* && $VALUE != *$'\r'* ]] || die "$(translate "The variable contains line breaks:") $NAME"
|
||||
if LC_ALL=C grep -q '[[:cntrl:]]' <<<"$VALUE"; then
|
||||
die "$(translate "The variable contains control characters:") $NAME"
|
||||
fi
|
||||
TEMP_CONF=$(mktemp)
|
||||
awk -v prefix="lxc.environment.runtime: ${NAME}=" 'index($0, prefix) != 1' "$CONF" >"$TEMP_CONF"
|
||||
printf 'lxc.environment.runtime: %s=%s\n' "$NAME" "$VALUE" >>"$TEMP_CONF"
|
||||
cat "$TEMP_CONF" >"$CONF"
|
||||
rm -f "$TEMP_CONF"
|
||||
set_runtime_environment "$NAME" "$VALUE"
|
||||
done < <(jq -r '.environment[] | [.name, (.value | @base64)] | @tsv' "$DEPLOYMENT_FILE")
|
||||
|
||||
apply_extra_hosts
|
||||
apply_installer_profile
|
||||
ensure_runtime_home
|
||||
apply_rlimits
|
||||
apply_host_monitor
|
||||
apply_kept_settings
|
||||
|
||||
@@ -89,7 +89,29 @@ def effective_entrypoint(
|
||||
return " ".join(shlex.quote(item) for item in process)
|
||||
|
||||
|
||||
def home_directory(passwd_path: Path, uid: int) -> str | None:
|
||||
"""Return the safe home directory recorded for a container UID."""
|
||||
for line in passwd_path.read_text(encoding="utf-8").splitlines():
|
||||
fields = line.split(":")
|
||||
if len(fields) < 7 or fields[2] != str(uid):
|
||||
continue
|
||||
home = fields[5]
|
||||
return home if home.startswith("/") else None
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) == 4 and sys.argv[1] == "--home":
|
||||
try:
|
||||
uid = int(sys.argv[3])
|
||||
home = home_directory(Path(sys.argv[2]), uid)
|
||||
except (OSError, ValueError) as exc:
|
||||
print(f"{translate('Cannot resolve the container home directory:')} {exc}", file=sys.stderr)
|
||||
return 1
|
||||
if home is None:
|
||||
return 1
|
||||
print(home)
|
||||
return 0
|
||||
if len(sys.argv) not in (3, 4):
|
||||
print(
|
||||
f"{translate('Usage:')} {sys.argv[0]} OCI_ARCHIVE COMMAND_JSON [ENTRYPOINT_JSON]",
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Regression tests for the native OCI PID 1 HOME fallback."""
|
||||
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "remote"))
|
||||
|
||||
import oci_runtime # noqa: E402
|
||||
|
||||
|
||||
class RuntimeHomeResolutionTests(unittest.TestCase):
|
||||
def passwd(self, content: str) -> Path:
|
||||
directory = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(directory.cleanup)
|
||||
path = Path(directory.name) / "passwd"
|
||||
path.write_text(content, encoding="utf-8")
|
||||
return path
|
||||
|
||||
def test_root_home_is_resolved_from_passwd(self):
|
||||
passwd = self.passwd("root:x:0:0:root:/root:/bin/sh\n")
|
||||
self.assertEqual(oci_runtime.home_directory(passwd, 0), "/root")
|
||||
|
||||
def test_non_root_home_is_resolved_from_passwd(self):
|
||||
passwd = self.passwd(
|
||||
"root:x:0:0:root:/root:/bin/sh\n"
|
||||
"app:x:1001:1001:App user:/srv/app:/sbin/nologin\n"
|
||||
)
|
||||
self.assertEqual(oci_runtime.home_directory(passwd, 1001), "/srv/app")
|
||||
|
||||
def test_unknown_or_unsafe_home_is_not_invented(self):
|
||||
passwd = self.passwd(
|
||||
"root:x:0:0:root:relative-home:/bin/sh\n"
|
||||
"app:x:1001:1001:App user::/sbin/nologin\n"
|
||||
)
|
||||
self.assertIsNone(oci_runtime.home_directory(passwd, 0))
|
||||
self.assertIsNone(oci_runtime.home_directory(passwd, 1001))
|
||||
self.assertIsNone(oci_runtime.home_directory(passwd, 1234))
|
||||
|
||||
|
||||
class RuntimeHomeInstallerContractTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.source = (ROOT / "remote" / "install_oci.sh").read_text(encoding="utf-8")
|
||||
|
||||
def test_explicit_home_is_preserved_and_fallback_uses_effective_uid(self):
|
||||
self.assertIn("ensure_runtime_home()", self.source)
|
||||
self.assertIn("lxc.environment.runtime: HOME=", self.source)
|
||||
self.assertIn('uid=${uid:-0}', self.source)
|
||||
self.assertIn('home=$(python3 "$OCI_RUNTIME_RESOLVER" --home', self.source)
|
||||
self.assertIn('set_runtime_environment HOME "$home"', self.source)
|
||||
|
||||
def test_home_fallback_runs_after_runtime_user_is_applied(self):
|
||||
self.assertIn(
|
||||
"apply_extra_hosts\napply_installer_profile\nensure_runtime_home\napply_rlimits",
|
||||
self.source,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user