chv-cell: ns 内 setuid 修复 + 单元内 DNS(上游 resolv.conf 绑定)

- 中间进程不再先 setuid(1000) 再 unshare:真机对照实测那样
  后续 unshare(CLONE_NEWNS) 得 EPERM;改为保持 root 外层身份
  unshare(creator caps 在),孙进程在 ns 内 setgid/setuid(0)
  切到内 0(映射到外层 chv 1000)。
- 单元内 DNS:客机 /etc/resolv.conf 是 127.0.0.53 stub,单元
  独立 netns 里自己的 lo 上没有 resolver;把上游
  /run/systemd/resolve/resolv.conf 绑到 /etc/resolv.conf
  (解析 symlink 后挂真实目标),只在单元私有 mount ns 里。
- 单测:CellResolvTest(bind 参数/symlink 解析/缺失回落)
  + 规约 dns_resolv_conf 字段;P2 + 阶段 2 共 73 项全过。
This commit is contained in:
host committed 2026-11-08 09:45:00 +00:00
1 parent afbb7772c5
commit 4b5f2f124f
2 files changed
+104 -13

No files matched your search

+46
View File
@@ -111,6 +111,52 @@ class CellArgvTest(unittest.TestCase):
self.assertNotIn("GITEA_TOKEN", real)
def test_dns_resolv_conf_upstream_bind(self):
spec = CELL.build_sandbox_spec(["true"])
self.assertEqual(spec["dns_resolv_conf"], "upstream-bind")
class CellResolvTest(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.mkdtemp(prefix="cell-resolv-")
def tearDown(self):
shutil.rmtree(self.tmp, ignore_errors=True)
def test_upstream_bind_args(self):
upstream = os.path.join(self.tmp, "resolv.conf")
with open(upstream, "w", encoding="utf-8") as f:
f.write("nameserver 8.8.8.8\nnameserver 1.1.1.1\n")
target = os.path.join(self.tmp, "target-resolv.conf")
with open(target, "w", encoding="utf-8") as f:
f.write("nameserver 127.0.0.53\n")
self.assertEqual(CELL._resolv_bind(upstream, target),
["--bind", upstream, target])
def test_symlink_target_resolved(self):
upstream = os.path.join(self.tmp, "resolv.conf")
with open(upstream, "w", encoding="utf-8") as f:
f.write("nameserver 8.8.8.8\n")
real = os.path.join(self.tmp, "stub-resolv.conf")
with open(real, "w", encoding="utf-8") as f:
f.write("nameserver 127.0.0.53\n")
link = os.path.join(self.tmp, "resolv-link")
os.symlink(real, link)
self.assertEqual(CELL._resolv_bind(upstream, link),
["--bind", upstream, real])
def test_missing_or_no_nameserver_returns_none(self):
target = os.path.join(self.tmp, "target-resolv.conf")
with open(target, "w", encoding="utf-8") as f:
f.write("nameserver 127.0.0.53\n")
self.assertIsNone(CELL._resolv_bind(
os.path.join(self.tmp, "no-such-file"), target))
empty = os.path.join(self.tmp, "empty-resolv.conf")
with open(empty, "w", encoding="utf-8") as f:
f.write("search .\n")
self.assertIsNone(CELL._resolv_bind(empty, target))
class CellSeccompTest(unittest.TestCase):
def test_filter_structure(self):
raw = CELL.build_seccomp_filter()