feat(chv-guest): Gitea 同步变量经 seed 进 guest-env(执行器集成)

- 新增 cloud/chv/gitea_sync.py:6 键白名单,三源合并
  (宿主 CHV_GITEA_* < 任务 gitea_sync < extra_env),
  必填 4 键缺失即 fail-closed,空即不同步
- ssh_transport:GITEA_TOKEN 进密钥集(argv 拒绝),其余
  5 键进 guest-env 集(split_guest_env)
- hostkeys.write_seed:guest_env 与 secret_env 合并写同一
  条目,仍无 runcmd、0600
- config:_gitea_fragment 产 gitea_sync_env(函数内 import,
  叶子模块顶层只许标准库)
- executor.wake_vm:起 VMM 前合并+校验(gitea_sync_incomplete),
  guest-env 随 seed 投递;provision_host_key 透传 guest_env
- worker:_run_attempt 仅对 CHV 执行器把同步变量并进 gateway_env
- 测试:cloud/tests/test_chv_gitea_sync.py 20 项
- 边界:tasks 表无 JSON 列,DB 往返丢任务 gitea_sync;
  生产主来源是宿主 CHV_GITEA_* 环境变量
This commit is contained in:
host committed 2026-11-06 16:55:32 +00:00
1 parent 67ca0b86cd
commit e7f699c62b
7 files changed
+785 -37

No files matched your search

+36 -29
View File
@@ -168,20 +168,37 @@ def build_seed_user_data(private_key_text: str, public_key_text: str) -> str:
return "\n".join(lines) + "\n"
def _guest_env_entry(merged_env: dict) -> list:
"""guest-env 的 write_files 条目行(0600 root,值经
shlex.quote 引用):只能并进已有的 write_files 列表,
绝不能再写一个 write_files: 键(YAML 重复键会覆盖掉
host key 的两条写文件条目,预置即失效)。"""
lines = [
" - path: %s" % GUEST_ENV_PATH,
" permissions: '0600'",
" owner: root:root",
" content: |",
]
for key in sorted(merged_env):
lines.append(" %s=%s"
% (key, shlex.quote(str(merged_env[key]))))
return lines
def write_seed(seed_dir: str, vm_name: str, private_key_text: str,
public_key_text: str,
secret_env: dict | None = None) -> str:
"""写 seed 目录:meta-data + user-data(均为 0600)。
secret_env: dict | None = None,
guest_env: dict | None = None) -> str:
"""写 seed 目录:meta-data + user-data(均为 0600),
返回 seed 目录路径。调用方负责随 VM 销毁删掉它。
返回 seed 目录路径。seed 目录本身不含已知主机之外的
共享数据,调用方(执行器关闭/回收)负责删除它。
feat-chv-secfix(H2):secret_env 给定时,user-data 再
写一个 write_files 条目,把密钥环境变量写进客内
GUEST_ENV_PATH(0600 root),值经 shlex.quote 引用后
才进文件内容——远程命令 source 该文件即可得到密钥
环境,密钥值从不出现在宿主 ssh argv、宿主进程环境与
审计明文里。secret_env 为空时不写该条目(客内文件不
存在时远程命令也不 source 它,见执行器)。"""
secret_env 给定时写进客内 GUEST_ENV_PATH(0600 root,
远程命令 source 它得密钥环境,值不进宿主 argv/环境/
审计明文)。guest_env 给定时与 secret_env 合并写同一
个条目(secret 优先于同名键,防两行同名);guest_env
装 boot 时要用的非密钥同步变量(GITEA_URL 等,见
gitea_sync):只走 SSH 前缀到不了 cloud-init 时运行
的 chv-sync boot。两者皆空时不写该条目。"""
os.makedirs(seed_dir, exist_ok=True)
meta_path = os.path.join(seed_dir, "meta-data")
user_path = os.path.join(seed_dir, "user-data")
@@ -190,24 +207,14 @@ def write_seed(seed_dir: str, vm_name: str, private_key_text: str,
% (vm_name, vm_name))
os.chmod(meta_path, 0o600)
user_data = build_seed_user_data(private_key_text, public_key_text)
if secret_env:
# 注意:只能把条目并进已有的 write_files 列表,绝不能
# 再写一个 write_files: 键(YAML 重复键会覆盖掉 host
# key 的两条写文件条目,预置即失效)。seed 自 2026-11-07
# 起不再带 runcmd 段(见 build_seed_user_data 的根因
# 禁令),所以正常走 else 分支追加到 write_files 列表
# 末尾;marker 分支只作防御(将来模板若恢复 runcmd,
# 条目仍插在其前、仍属同一列表)。
lines = [
" - path: %s" % GUEST_ENV_PATH,
" permissions: '0600'",
" owner: root:root",
" content: |",
]
for key in sorted(secret_env):
lines.append(" %s=%s"
% (key, shlex.quote(str(secret_env[key]))))
# 条目插在 runcmd 段之前、仍属同一 write_files 列表。
merged_env = dict(guest_env or {})
merged_env.update(secret_env or {})
if merged_env:
lines = _guest_env_entry(merged_env)
# 条目插在 runcmd 段之前、仍属同一 write_files 列表
#(seed 自 2026-11-07 起不再带 runcmd,见
# build_seed_user_data 的根因禁令;marker 分支只作
# 防御,将来模板若恢复 runcmd 仍插在其前)。
marker = "runcmd:\n"
if marker in user_data:
head, tail = user_data.split(marker, 1)