part 4 缺陷 3:并发 cell 撞毁——唯一身份 + run_cell 接线
RETEST3 缺陷 3:固定 ve-chv-cell 名 + 固定 10.200.0.1/24, 第二个 cell 在 nspawn 建 veth 时即 File exists/Address already assigned。 - CellNet + allocate_cell_net():每次 run_cell 在 flock 锁下 (/run/chv/.cell-alloc.lock,30 秒超时)分配唯一身份: machine chv-cell-<xx>(11 字符)、宿主 ve-chv-cell-<xx> (14 字符,接口名 15 字符上限内)、10.200.<xx>.0/24; 扫描活接口 + 地址 + machinectl 在册名,避让注销滞后的下标。 - 配网命令表(host/cell/cleanup)与 build_nspawn_argv 接受 net/cell_net 参数;None 时仍用旧常量(argv 子命令与旧测试)。 - run_cell 重写:分配身份 -> 生成专属文件 -> 起步门 payload -> 等 veth(20 秒)-> 宿主配网 -> 轮询 init pid(10 秒) -> 写 pid 文件 -> 单元内配网 -> 开门 -> 等退出码; nspawn 已死返回其退出码;finally 清 NAT/pid/专属文件。 共享网络模式跳过门与身份(无配网可等)。 - broker:pid ns 对照改多 cell 版(peer_allowed_multi + _read_cell_pids:cell-<machine>.pid glob + 兼容旧 cell.pid; 对端 ns 与任一活单元同 ns 即放行;残留 pid 文件只跳过, 全部读不到才拒绝,不降级)。 新测试:CellAllocTest、CellPart4NetworkTest、 CellPart4ArgvTest(cell_net 两法)、BrokerMultiTest。
This commit is contained in:
1 parent
2be69af92c
commit
7a227323f2
3 files changed
+524
-47
No files matched your search
+55
-8
@@ -5,11 +5,12 @@
|
||||
SO_PEERCRED 的最小权限 IPC。broker 只认白名单操作,凭证与浏览器
|
||||
都在单元外——单元看不见 token,只能请 broker 代办。
|
||||
|
||||
校验(双重,缺一不可才放行;cell.pid 不存在时只做 uid 检查并打日志):
|
||||
校验(双重,缺一不可才放行;无 cell pid 文件时只做 uid 检查并打日志):
|
||||
1. SO_PEERCRED 的 uid 须在单元映射范围 [1000, 1000+65536) 内;
|
||||
2. 对端 pid 的 pid ns 须与 /run/chv/cell.pid 记的单元 init 同 ns
|
||||
(读不到 cell.pid 时跳过第 2 项并警告;cell.pid 存在但对端 ns
|
||||
读不到时拒绝,不降级)。
|
||||
2. 对端 pid 的 pid ns 须与任一活着的单元 init 同 ns(part 4:
|
||||
并发 cell 各有 cell-<machine>.pid,旧的单文件 cell.pid 只记
|
||||
最新单元;读不到任何 pid 文件时跳过第 2 项并警告;有 pid 文
|
||||
件但对端 ns 与任一可读的单元 ns 都对不上时拒绝,不降级)。
|
||||
|
||||
操作白名单:
|
||||
ping -> {"ok": true}
|
||||
@@ -22,6 +23,7 @@ SO_PEERCRED 的最小权限 IPC。broker 只认白名单操作,凭证与浏览
|
||||
只用标准库。单连接一请求一响应(JSON 行,≤64KB);异常只断本
|
||||
连接,绝不杀死 broker 循环。
|
||||
"""
|
||||
import glob
|
||||
import grp
|
||||
import json
|
||||
import os
|
||||
@@ -38,6 +40,7 @@ BROKER_SOCK = "/run/chv/broker.sock"
|
||||
# 的 unlink 能力,所以用独立目录(0755 chv-browser 所有)。
|
||||
BROWSER_SOCK = "/run/chv-browser/browser.sock"
|
||||
CELL_PID_FILE = "/run/chv/cell.pid"
|
||||
CELL_PID_GLOB = "/run/chv/cell-*.pid"
|
||||
UID_BASE = 1000
|
||||
UID_COUNT = 65536
|
||||
WORKSPACE = "/home/chv/workspace"
|
||||
@@ -76,6 +79,33 @@ def peer_allowed(peer_uid, peer_pid, cell_pid=None, proc_root="/proc"):
|
||||
return True, "ok"
|
||||
|
||||
|
||||
def peer_allowed_multi(peer_uid, peer_pid, cell_pids, proc_root="/proc"):
|
||||
"""多 cell 版:对端 pid ns 与任一活 cell 的 init 同 ns 即放行
|
||||
(part 4 并发 cell 用;单 cell 时与 peer_allowed 等价)。
|
||||
cell_pids 为空即只做 uid 检查;全部 pid 的 ns 都读不到时
|
||||
拒绝(不降级);个别 pid 的 ns 读不到(已退出的残留文件)
|
||||
只跳过该项,不因此拒绝整个请求。"""
|
||||
if not (UID_BASE <= peer_uid < UID_BASE + UID_COUNT):
|
||||
return False, "uid %d 不在单元映射范围 [%d, %d)" % (
|
||||
peer_uid, UID_BASE, UID_BASE + UID_COUNT)
|
||||
if not cell_pids:
|
||||
return True, "uid 在范围内(无 cell pid 文件,未做 pid ns 对照)"
|
||||
peer_ns = _read_ns_link(peer_pid, proc_root)
|
||||
if peer_ns is None:
|
||||
return False, "对端 pid ns 读不到,拒绝"
|
||||
readable = 0
|
||||
for cell_pid in cell_pids:
|
||||
cell_ns = _read_ns_link(cell_pid, proc_root)
|
||||
if cell_ns is None:
|
||||
continue
|
||||
readable += 1
|
||||
if peer_ns == cell_ns:
|
||||
return True, "ok"
|
||||
if readable == 0:
|
||||
return False, "cell pid 的 pid ns 均读不到,拒绝(不降级)"
|
||||
return False, "对端 pid ns 与任何单元不一致"
|
||||
|
||||
|
||||
def _read_cell_pid():
|
||||
try:
|
||||
with open(CELL_PID_FILE) as fh:
|
||||
@@ -84,6 +114,23 @@ def _read_cell_pid():
|
||||
return None
|
||||
|
||||
|
||||
def _read_cell_pids(cell_dir="/run/chv"):
|
||||
"""读全部 cell pid:每单元一份 cell-<machine>.pid + 兼容旧
|
||||
路径 cell.pid(最新单元)。去重、跳过坏内容,返回 int 列表。"""
|
||||
pids = []
|
||||
paths = sorted(glob.glob(os.path.join(cell_dir, "cell-*.pid")))
|
||||
paths.append(os.path.join(cell_dir, "cell.pid"))
|
||||
for path in paths:
|
||||
try:
|
||||
with open(path) as fh:
|
||||
pid = int(fh.read().strip())
|
||||
except (OSError, ValueError):
|
||||
continue
|
||||
if pid not in pids:
|
||||
pids.append(pid)
|
||||
return pids
|
||||
|
||||
|
||||
def _resolve_repo(repo):
|
||||
"""仓目录只允许 ~/workspace 下。repo 为空时返回 None(由
|
||||
chv-sync 自己取唯一仓)。返回解析后的绝对路径或抛 ValueError。"""
|
||||
@@ -212,15 +259,15 @@ def serve(sock_path=BROKER_SOCK, browser_sock=BROWSER_SOCK):
|
||||
continue
|
||||
try:
|
||||
pid, uid, _gid = _peer_creds(conn)
|
||||
cell_pid = _read_cell_pid()
|
||||
allowed, reason = peer_allowed(uid, pid, cell_pid)
|
||||
cell_pids = _read_cell_pids()
|
||||
allowed, reason = peer_allowed_multi(uid, pid, cell_pids)
|
||||
if not allowed:
|
||||
print("chv-broker: 拒绝连接 pid=%d uid=%d:%s"
|
||||
% (pid, uid, reason), file=sys.stderr, flush=True)
|
||||
conn.close()
|
||||
continue
|
||||
if cell_pid is None:
|
||||
print("chv-broker: 警告:cell.pid 缺失,只做了 uid 检查 "
|
||||
if not cell_pids:
|
||||
print("chv-broker: 警告:无 cell pid 文件,只做了 uid 检查 "
|
||||
"(pid=%d uid=%d)" % (pid, uid), file=sys.stderr,
|
||||
flush=True)
|
||||
conn.settimeout(310)
|
||||
|
||||
Reference in new issue
Block a user