Salt GitFS 文件服务后端指南:仓库映射、环境晋升、认证与刷新

原作:Salt Project 贡献者(页面无个人署名);来源:Git Fileserver Backend Walkthrough。本文依据 2026 年 10 月 5 日读取的官方 latest 文档完整翻译整理。该页面会随版本变化,使用前应以自己的 Salt 版本核对。

GitFS 后端使 Salt 能从 Git 仓库提供文件。在 fileserver_backend 中启用 Git 后端,再通过 gitfs_remotes 配置一个或多个仓库即可。仓库的分支和标签会映射成 Salt 文件服务器环境。由于不同分支、标签可能各自带有 top.sls,从而产生互相冲突的 top 文件,使用 GitFS 管理 top 文件时,可以在 minion 配置里把 top_file_merging_strategy 设为 same;后文会解释为什么这还必须配合明确的 saltenv。

GitFS 将 Git 的 dev、qa 和 master 分支映射成 dev、qa 和 base 环境,master 缓存仓库并向明确选择环境的 minion 提供状态;代码依次从 dev 合并到 qa 再到 master。
原创技术示意:未完纪编辑整理。它表达环境映射与晋升关系,不是 Salt 运行截图。

一、安装依赖并选择 provider

Python 与 Git 的接口支持三种 provider:pygit2、GitPython,以及从 Salt 3008.0 开始加入的 gitcli。未显式设置 gitfs_provider 时,Salt 按 pygit2 → gitpython → gitcli 的顺序选择第一个可用实现。设置该参数可以覆盖自动选择。

3008.0 以前,如果 master 没有安装 pygit2 或 GitPython,GitFS 会报 No suitable gitfs provider module is installed。3008.0 开始可以退回仅依赖系统 Git 程序的 gitcli。

所读取文档列出的 CI 测试及 onedir 包组合是:pygit2 >= 1.13.1;Python 3.11 及以上使用 pygit2 >= 1.19.2;配套 libgit2 >= 1.5。GitPython 推荐 >= 3.1.50,并需要系统 Git。这些约束位于 requirements/base.txt 和 requirements/static/ci/。

Salt 导入阶段仍接受较老的 pygit2 0.20.3 和 GitPython 0.3 下限,可见 salt/utils/gitfs.py 中的 GITPYTHON_MINVER、PYGIT2_MINVER;但文档说明仅上述新组合进入现有测试。老版本缺少 SSH 认证、refspec、credential helper 相关修复,不应因为“能导入”就用于生产。应选择最新的兼容版本。

pygit2 与 libgit2

Salt onedir 包已经附带配套的 pygit2/libgit2,通常无须额外安装。源码安装可以优先使用发行版包:

# RHEL / Fedora / Alma / Rocky 8+:文档说明 EPEL 提供相关包
dnf install python3-pygit2

# Debian 11+ / Ubuntu 22.04+
apt-get install python3-pygit2

发行版包太旧时可从 PyPI 安装 pygit2。pygit2 与 libgit2 的 ABI 紧密耦合;应检查 pygit2 发布说明所对应的确切 ABI,不匹配会导致 salt-master 启动时导入失败。原文给出的 onedir 安装示例是:

apt-get install libgit2-1.5
salt-pip install 'pygit2>=1.13.1,<1.18' --no-deps

libgit2-1.5 必须按发行版实际提供的包调整;--no-deps 用来避免 salt-pip 升级自带的 cffi。编者核对:这个保留的旧示例把 pygit2 限在 1.18 以下,与同页 Python 3.11+ 的 1.19.2 下限不一致,不能组合照抄。应以自己 Salt/onedir/Python 的锁定依赖和 ABI 为准,本文未验证安装组合。

pygit2 的 SSH 认证需要 libgit2 链接 libssh2,不是 libssh。发行版 libgit2 通常已经包含支持。如果自行编译后对 ssh:// 仓库出现 Unsupported URL Protocol,应检查编译时是否缺少 libssh2 头文件。pygit2 即使在小版本中也可能调整不兼容 API,生产应锁定版本、升级时看 changelog,并将兼容问题报告给 Salt 项目。

GitPython

文档建议使用与基础依赖和 CI 锁文件一致的 GitPython >= 3.1.50。发行版和 onedir 安装示例分别是:

# RHEL / Fedora
dnf install python3-GitPython
# Debian / Ubuntu
apt-get install python3-git
# Onedir
salt-pip install 'GitPython>=3.1.50'

GitPython 会调用系统 git,所以还要安装该程序。macOS 可安装 Xcode command-line tools 或使用 Homebrew。GitPython 自身提醒不宜在长时间运行的进程中使用它,因为可能泄漏系统资源;Salt 通过按可配置间隔重启 fileserver worker 缓解,相关配置为 fileserver_interval。

gitcli(Salt 3008.0 起)

gitcli 直接调用系统 Git,不需要 Python Git 库。master 的 Git 版本至少为 2.3.0,可通过 git --version 查看。原文安装示例:

yum install git
# 或 Debian / Ubuntu
apt-get install git

默认以 bare 仓库并带 --depth 1 克隆,减少大量仓库的磁盘占用;可分别通过 gitfs_depth、git_pillar_depth、winrepo_depth 配置深度。gitcli 不支持 submodule,如果仓库依赖子模块,应使用 pygit2 或 GitPython。它的认证依赖环境变量,而且与另两个 provider 存在刻意保留的能力差异,见后文。

二、最小配置与多个远端的查找顺序

在 master 配置中加入以下两个设置,再重启 master 加载配置:

fileserver_backend:
  - gitfs

gitfs_remotes:
  - https://github.com/saltstack-formulas/salt-formula.git

git 也是有效的后端名称;在 2018.3.0 之前只能写 git。远端支持 git://、https://、file://、ssh://,SSH 还可以使用 scp 风格语法:

gitfs_remotes:
  - git@github.com:user/repo.git
  - ssh://user@domain.tld/path/to/repo.git

在 master/minion 模式中,master 为远端维护缓存,minion 不需要直接访问 Git 仓库。编者补充:优先使用带认证和完整性保护的 HTTPS/SSH;原文支持 git:// 不代表该明文传输适合承载受信任状态。

gitfs_remotes 是有顺序的列表。以下例子专门说明逐级查找行为,不是建议的仓库布局:

gitfs_remotes:
  - git://github.com/example/first.git
  - https://github.com/example/second.git
  - file:///root/third

# first.git:
#   top.sls
#   edit/vim.sls
#   edit/vimrc
#   nginx/init.sls
#   shell/init.sls
# second.git:
#   edit/dev_vimrc
#   haproxy/init.sls
#   shell.sls
# third:
#   haproxy/haproxy.conf
#   edit/dev_vimrc

请求的文件按列表顺序逐个仓库查找,找到后立即返回,不再继续查找。例如 salt://haproxy/init.sls 来自第二个仓库,salt://haproxy/haproxy.conf 来自第三个。对状态的查找还需注意:同名 shell.sls 优先于目录中的 shell/init.sls,因此 state.apply shell 会使用第二个仓库的文件。

file:// 表示本地目录里的 Git 仓库,但仍作为 remote 使用,不是直接把工作目录复制成 Salt 缓存。希望暴露的引用必须在该仓库中存在为本地 ref。Salt 2014.1.0 以前不耐受远端顺序或 URI 变化,原文建议那些旧版本在更改远端后、重启 master 前清理 /var/cache/salt/master/gitfs。这是历史兼容处理;清缓存会产生实际影响,不能作为所有新版本故障的第一步。

三、按远端设置参数

从 2014.7.0 起,许多全局设置可以被单个远端覆盖。原文列出的全局项包括 gitfs_base、gitfs_root、gitfs_ssl_verify、gitfs_mountpoint;认证项 gitfs_user、gitfs_password、gitfs_insecure_auth、gitfs_pubkey、gitfs_privkey、gitfs_passphrase 在该列表中标为 pygit2 使用;还有 2017.7.0 加入的 gitfs_refspecs,2018.3.0 加入的 gitfs_disable_saltenv_mapping、gitfs_ref_types、gitfs_update_interval,以及 3008.0 加入的 gitfs_proxy。pygit2 从 0.23.2 起支持关闭 SSL 校验。

以下保留文档用于展示参数效果的综合例子。其中 ssl_verify: False、HTTP 上的密码与 insecure_auth: True 是不应照搬到生产的条件;示例密码只是原文占位文字,不是真实凭据。

gitfs_provider: pygit2
gitfs_base: develop

gitfs_remotes:
  - https://foo.com/foo.git
  - https://foo.com/bar.git:
    - root: salt
    - mountpoint: salt://bar
    - base: salt-base
    - ssl_verify: False
    - update_interval: 120
  - https://foo.com/bar.git:
    - name: second_bar_repo
    - root: other/salt
    - mountpoint: salt://other/bar
    - base: salt-base
    - ref_types:
      - branch
  - http://foo.com/baz.git:
    - root: salt/states
    - user: joe
    - password: mysupersecretpassword
    - insecure_auth: True
    - disable_saltenv_mapping: True
    - saltenv:
      - foo:
        - ref: foo
  - http://foo.com/quux.git:
    - all_saltenvs: master

带有单远端配置的 URL 结尾必须加冒号。单远端参数通常去掉全局名称前面的 gitfs_;name、saltenv、all_saltenvs 只存在于单远端配置中。all_saltenvs 从 2018.3.0 起提供。

上述配置的效果逐项如下:

  1. 第一和第四个远端把 develop 分支或标签映射为 base;第二和第三个使用 salt-base。
  2. 第一个提供整个仓库;第二个只提供 salt 及其子目录;第三个只提供 other/salt;第四个只提供 salt/states。
  3. 第三个只从分支提供文件,不使用标签或 SHA。
  4. 第四个仅有 base(指向 develop)和 foo(指向 foo)两个 saltenv。
  5. 第一、第四个从 salt:// 根命名空间提供文件;第二个挂在 salt://bar,第三个挂在 salt://other/bar。
  6. 第二、第三个引用同一仓库,需要用唯一名称区分重复 remote。
  7. 第四个绕过默认禁止向非 HTTPS 远端发送认证信息的保护。
  8. 第五个配置 all_saltenvs,因此 master 分支或标签的内容出现在每个 GitFS 环境中。
  9. 设置 update_interval: 120 的是第二个远端,它每 120 秒刷新;原文解释误写为“第一个”,本文按代码纠正。

原文解释,未使用认证时允许 HTTP;向 HTTP 发送凭据则要显式启用 insecure_auth。编者补充:“允许”只是配置行为,不意味着传输有完整性或保密性保障;状态内容被篡改也会影响执行,应使用可信加密传输,不使用文档中的明文凭据演示。

四、按 saltenv 设置 root、mountpoint 和 ref

从 2016.11.0 起,同一仓库的不同环境还可以分别覆盖挂载点、根目录,以及该环境对应的分支或标签:

gitfs_root: salt

gitfs_saltenv:
  - dev:
    - mountpoint: salt://gitfs-dev
    - ref: develop

gitfs_remotes:
  - https://foo.com/bar.git:
    - saltenv:
      - staging:
        - ref: qa
        - mountpoint: salt://bar-staging
      - dev:
        - ref: development
  - https://foo.com/baz.git:
    - saltenv:
      - staging:
        - mountpoint: salt://baz-staging

由此,所有远端的 dev 环境都挂载在 salt://gitfs-dev;第一个远端的 dev 取自 development 分支,第二个取自 develop 分支。staging 环境分别挂载在 salt://bar-staging 与 salt://baz-staging。所有远端、所有环境仍从仓库的 salt 目录及其子目录提供文件。

五、自定义 refspec、共享分支与刷新间隔

GitFS 默认抓取远端分支和标签。2017.7.0 起可以通过 gitfs_refspecs 增加其他 ref,例如 GitHub pull request:

gitfs_refspecs:
  - '+refs/heads/*:refs/remotes/origin/*'
  - '+refs/tags/*:refs/tags/*'
  - '+refs/pull/*/head:refs/remotes/origin/pr/*'
  - '+refs/pull/*/merge:refs/remotes/origin/merge/*'

head 指待合并分支头;merge 指合并结果的引用。自定义 ref 的目的路径必须在 refs/remotes/origin/ 下,最好像示例一样使用子目录。环境名由其相对于这个前缀的路径形成,例如 PR 12345 的头映射成 pr/12345,斜线保留。也可以只针对第二个仓库扩展:

gitfs_remotes:
  - https://domain.tld/foo.git
  - https://domain.tld/bar.git:
    - refspecs:
      - '+refs/heads/*:refs/remotes/origin/*'
      - '+refs/tags/*:refs/tags/*'
      - '+refs/pull/*/head:refs/remotes/origin/pr/*'
      - '+refs/pull/*/merge:refs/remotes/origin/merge/*'

编者补充:拉取未审查 PR 的状态内容与执行它是不同权限边界,不应让生产 minion 自动应用不受信任的 PR 环境。

all_saltenvs(2018.3.0 起)让单个分支或标签出现在全部 GitFS saltenv 中,很适合公共 formula。此前 formula 若用于多个环境,就必须在远端为各环境建分支或标签,并同步每次更新。现在只需维护一个分支:

gitfs_remotes:
  - http://foo.com/quux.git:
    - all_saltenvs: anything

这个效果只作用于 GitFS,不会让文件自动出现在由 file_roots 等其他后端定义的环境里。若还需要测试 formula 仓库自己的工作分支,可以改用从 Salt 3001 起提供的全局或单远端 fallback:

gitfs_remotes:
  - http://foo.com/quux.git:
    - fallback: anything

这里的 HTTP 保留原文示例;实际受信任仓库建议改用 HTTPS/SSH。两者的区别是 all_saltenvs 强制把一个 ref 映射到所有环境,而 fallback 只在需要回退时提供该 ref。

2018.3.0 以前,GitFS 在专门的 maintenance 进程中跟随 loop_interval 进行更新,各文件服务后端共享间隔。现在可用 gitfs_update_interval 独立配置,并按 remote 覆盖:

gitfs_update_interval: 180

gitfs_remotes:
  - https://foo.com/foo.git
  - https://foo.com/bar.git:
    - update_interval: 120

第一个远端每三分钟更新,第二个每两分钟更新。

六、配置优先级、子目录和挂载点

正常情况下,从高到低的覆盖顺序为:

  1. 单远端下的 saltenv 设置。
  2. 全局 gitfs_saltenv 中的环境设置。
  3. 单远端参数。
  4. 全局参数。

以 mountpoint 为例,四层写法分别如下,较高层覆盖较低层:

# 1. 单远端、单环境
gitfs_remotes:
  - https://foo.com/bar.git:
    - saltenv:
      - dev:
        - mountpoint: salt://bar

# 2. 全局、单环境
gitfs_saltenv:
  - dev:
    - mountpoint: salt://bar

# 3. 单远端
gitfs_remotes:
  - https://foo.com/bar.git:
    - mountpoint: salt://bar

# 4. 全局
gitfs_mountpoint: salt://bar

上面的四段是彼此独立的示例,不是应拼成一个重复键的 YAML 文件。例外是 all_saltenvs:它覆盖分支或标签到环境的映射逻辑,即使全局 gitfs_saltenv 指定了映射,也由该 remote 的 all_saltenvs 优先决定。不过 root 和 mountpoint 的配置不受这个例外影响。

gitfs_root 将仓库中的某个子目录作为暴露边界。假设仓库如下:

.gitignore
README.txt
foo/
foo/bar/
foo/bar/one.txt
foo/bar/two.txt
foo/bar/three.txt
foo/baz/
foo/baz/top.sls
foo/baz/edit/vim.sls
foo/baz/edit/vimrc
foo/baz/nginx/init.sls

如下配置只提供 foo/baz 及其下的文件,忽略其他文件:

gitfs_remotes:
  - git://mydomain.com/stuff.git
gitfs_root: foo/baz

root 也能按远端配置。gitfs_mountpoint(2014.7.0 起)则在文件服务路径前增加前缀,使现有仓库不必为 Salt 的目录布局重组。比如希望仓库根目录的 foo.conf 从 salt://webapps/foo/files/foo.conf 提供:

gitfs_remotes:
  - https://mydomain.com/stuff.git
gitfs_mountpoint: salt://webapps/foo/files

在有 mountpoint 之前,仓库中必须真的存在 webapps/foo/files/ 这些父目录。mountpoint 同样可以按 remote 设置。

七、masterless 与多个文件服务后端

从 2014.7.0 起,GitFS 可用于 masterless 模式。将 GitFS 参数和 fileserver_backend 写到 minion 配置里,而不是 master 配置里即可。

有时 SLS 放在 Git,大文件直接放在 master,会更适合组合多个后端。查找同样遵循列表顺序:

fileserver_backend:
  - roots
  - git

先查 roots 默认的 /srv/salt,找不到再依次查 Git remote。2018.3.5 和 2019.2.1 起,还可以结合 file_roots 的 __env__ 通配环境:

file_roots:
  base:
    - /srv/salt
  __env__:
    - /srv/salt

八、分支、环境与 top 文件:从 dev 晋升到 qa、base

通常,分支和标签直接按名称映射为环境。例外是 master 默认映射为 base,所以常见组合是 Git 的 master、qa、dev 对应 Salt 的 base、qa、dev。如果希望其他分支作为 base,可全局或按远端设置:

gitfs_base: salt-base

执行 highstate 时,不同分支与标签的 top.sls 可能被合并。如果同一 SLS 的多个环境版本被拉进一次 highstate,就会产生重复状态 ID,状态编译器拒绝继续。这与先开发、再测试、最后晋升生产的工作流冲突。要实现这种晋升,原文要求同时做到三件事:

  1. top.sls 的环境键写成 {{ saltenv }},使相同 top 文件可以存在于各分支,渲染时替换成正在处理的环境。
  2. 在 minion 中设置 top_file_merging_strategy: same,让各环境仅查看自己的 top.sls。
  3. 明确指定 saltenv,限制本次 highstate 处理的环境。

示例 top.sls:

{{ saltenv }}:
  '*':
    - mystuff

对应 mystuff.sls:

manage_mystuff:
  pkg.installed:
    - name: mystuff
  file.managed:
    - name: /etc/mystuff.conf
    - source: salt://mystuff/files/mystuff.conf
  service.running:
    - name: mystuffd
    - enable: True
    - watch:
      - file: /etc/mystuff.conf

如果在 dev 修改 mystuff/files/mystuff.conf,提交并推送,而只完成前两项设置,那么 highstate 仍可能显示:

myminion:
    Data failed to compile:
----------
    Detected conflicting IDs, SLS IDs need to be globally unique.
    The conflicting ID is 'manage_mystuff' and is found in SLS 'base:mystuff' and SLS 'dev:mystuff'
----------
    Detected conflicting IDs, SLS IDs need to be globally unique.
    The conflicting ID is 'manage_mystuff' and is found in SLS 'dev:mystuff' and SLS 'qa:mystuff'

原因是没有明确 saltenv 时,仍会考虑所有环境的 top 文件。虽然每个环境只读自己的 top.sls,但每个分支又都有 mystuff.sls,于是同一个 manage_mystuff 被加入多次。same 并不等于只运行一个环境。

指定 saltenv 有两种方式:在 minion 配置中固定,例如开发机器为 dev、测试机器为 qa、生产机器为 base;或者在运行时给出:

salt myminion state.apply saltenv=dev

运行时参数优先于 minion 配置。即使机器平时配置为 base,也可以用该命令有意识地应用 dev;如果 minion 没有固定 saltenv,就必须在运行时明确指定,避免重复 ID。若 qa 从 master 分出、dev 从 qa 分出,便可以依次将 dev 合并到 qa,再将 qa 合并到 master,完成 dev → qa → prod 的晋升。

编者补充:state.apply 会安装包、改写配置、启停服务等,不是只查看差异。应先在正确目标和环境中审阅渲染结果、执行受控演练,并限制仓库和生产分支的写权限。共享 formula 或 all_saltenvs 的变更也可能同时影响多个环境。

九、控制哪些分支和标签暴露为环境

2014.7.0 起,gitfs_saltenv_whitelist 与 gitfs_saltenv_blacklist 可过滤环境。匹配按精确值、glob、正则表达式顺序进行。正则不能带 ^ 和 $,并且必须匹配整个分支或标签名:

gitfs_saltenv_whitelist:
  - base
  - v1.*
  - 'mybranch\d+'

v1.* 既像 glob 又像正则,但会先作为 glob 匹配。只配 whitelist 时,仅匹配到的环境可用;只配 blacklist 时,匹配到的环境被排除;两者都配时,必须先进入 whitelist 且不在 blacklist 中,才会暴露。

十、认证:不同 provider 不可混用配置

pygit2 的 HTTPS 与 SSH

pygit2 从 0.20.3 起支持 HTTPS 和 SSH,以下使用 2014.7.0 起提供的单远端设置。HTTPS 可写用户和密码:

gitfs_remotes:
  - https://domain.tld/myrepo.git:
    - user: git
    - password: mypassword

原文还展示 HTTP 的覆盖保护写法:

gitfs_remotes:
  - http://domain.tld/insecure_repo.git:
    - user: git
    - password: mypassword
    - insecure_auth: True

不要采用该 HTTP 示例传输真实密码。Salt 默认拒绝这种认证是有意的保护。配置里的 mypassword 是演示文字,真实凭据应使用只读服务身份并控制配置文件读权限,不要写入状态仓库。

SSH 的 ssh://git@github.com/user/repo.git 与 git@github.com:user/repo.git 等价。pygit2 需要同时指定公钥与私钥;若私钥有口令,还可指定 passphrase:

gitfs_remotes:
  - git@github.com:user/repo.git:
    - pubkey: /root/.ssh/id_rsa.pub
    - privkey: /root/.ssh/id_rsa
    - passphrase: myawesomepassphrase

还必须把远端主机密钥加入 known_hosts。原文记录过 pygit2 向 Microsoft Visual Studio/VSTS 认证的历史问题:libssh2 1.7.0 及之后已知可用,1.5.0 及之前已知不兼容,当时未测试 1.6.0。升级 libssh2 可能牵动 curl、libgit2 和 pygit2 重建,原文给出的旧系统权宜方案是 GitPython 配合无口令密钥。这里只保留历史条件,不建议为迁就旧依赖放弃当前安全维护。

GitPython 的 HTTPS 与 SSH

HTTPS 认证可以写在 URL 中,或者放入 /var/lib/salt/.netrc:

# 原文 URL 示例,真实密码不应提交或输出到日志:
gitfs_remotes:
  - https://git:mypassword@domain.tld/myrepo.git

# /var/lib/salt/.netrc 内容:
machine domain.tld
login git
password mypassword

这是不同文件的两个示例,不应合并为同一 YAML。文档也展示过 HTTP 加 insecure_auth: True:

gitfs_remotes:
  - http://git:mypassword@domain.tld/insecure_repo.git:
    - insecure_auth: True

同样仅作反面条件说明。URL 凭据可能进入配置、日志和诊断信息;优先使用受控凭据机制和 HTTPS。

文档说明 GitPython 只支持无口令 SSH 密钥;pygit2 的 pubkey、privkey 等认证参数对它不生效。远端可以这样写:

gitfs_remotes:
  - ssh://git@github.com/example/salt-states.git

GitPython 调用 Git CLI,所以私钥通常放在运行 master 的用户的 ~/.ssh/id_rsa,权限为 0600。URL 没指定用户名时,会使用当前 master 用户,通常是 root。要换密钥,应在该用户的 ~/.ssh/config 指定:

Host github.com
    IdentityFile /root/.ssh/id_rsa_gitfs

Host 应匹配仓库主机名或主机通配模式,并需要维护 known_hosts。原文指出可以增加下面的设置绕过严格检查,但也明确认为不安全、不推荐:

Host github.com
    IdentityFile /root/.ssh/id_rsa_gitfs
    StrictHostKeyChecking no

不要把绕过主机密钥校验当成解决认证错误的方法。

gitcli 的环境变量认证

从 3008.0 起,gitcli 通过环境变量把认证交给 Git 程序:

配置 实际行为
ssl_verify: False 设置 GIT_SSL_NO_VERIFY=true。
proxy 导出为 http_proxy 与 https_proxy。
privkey 形成 GIT_SSH_COMMAND=ssh -o StrictHostKeyChecking=no -i <privkey>。

其他 provider 中可用的部分设置会被静默忽略:user/password 不提供 HTTPS basic auth,需使用 Git 的凭据 helper 或 URL;passphrase 不支持交互输入,因此要求无口令密钥;pubkey 不使用,只有 privkey 有效;insecure_auth 没有效果,HTTP basic auth 由 URL 决定。

实际风险:只要设置 privkey,这一实现就会关闭 SSH 严格主机密钥校验。原文提醒只在可信端点条件下考虑使用。对于必须验证主机身份的环境,应选择和验证能保持校验的 provider/配置方案,而不是因为内网或“自己搭建”就认定没有中间人风险。

GitLab 凭据选择

GitLab 仓库沿用上述 HTTPS 和 SSH 机制,但 Salt master 是服务身份,原文按以下次序推荐凭据:

  1. Deploy token:项目或组范围、只读,适合 gitfs 和 git_pillar。在 Settings → Repository → Deploy tokens 创建,授予 read_repository。用户名使用 GitLab 创建时显示的值,token 作为密码。
  2. Project access token:项目范围、角色可控,适用于 master 确实需要推送的场合,例如 winrepo runner;原文示例以 token 名称为用户名、token 为密码。
  3. Personal access token:可以工作,但把 master 访问绑定到真人账户,按 token 所有者认证。
  4. SSH deploy key:创建无口令密钥对,在 Project → Settings → Repository → Deploy Keys 添加公钥。
# 分别为四种凭据的独立示例;XXXX 为占位符。
# pygit2 + deploy token
gitfs_remotes:
  - https://gitlab.example.com/group/states.git:
    - user: salt-deploy-states
    - password: gldt-XXXXXXXXXXXXXXXXXXXX

# project access token
gitfs_remotes:
  - https://gitlab.example.com/group/winrepo.git:
    - user: salt-winrepo
    - password: glpat-XXXXXXXXXXXXXXXXXXXX

# personal access token
gitfs_remotes:
  - https://gitlab.example.com/group/repo.git:
    - user: my-gitlab-user
    - password: glpat-XXXXXXXXXXXXXXXXXXXX

# pygit2 + deploy key
gitfs_remotes:
  - git@gitlab.example.com:group/repo.git:
    - pubkey: /etc/salt/gitlab_deploy.pub
    - privkey: /etc/salt/gitlab_deploy

原文说 deploy key 可以与 pygit2、GitPython 配合,但必须遵守前文 provider 差异:GitPython 不读取以上 pubkey/privkey 参数,应通过其运行用户的 SSH 配置设置路径,而且只用无口令密钥。先验证 GitLab 主机身份,再将主机密钥加入 known_hosts;原文命令是:

salt-call --local ssh.set_known_host hostname=gitlab.example.com

GitLab 的 deploy/project token 过期或缺少 read_repository 权限时可能只返回 401 Unauthorized。如果原先正常的 GitFS 突然出现 401,应先检查有效期和 scope,再改 Salt 配置。

十一、加入 known_hosts 并核验主机身份

SSH 认证需要运行 master 用户的 ~/.ssh/known_hosts 包含远端主机密钥。如果 master 自身也运行 minion,可使用:

salt mymaster ssh.set_known_host user=root hostname=github.com

原文历史输出中,返回数据包含 new 下的 enc: ssh-rsa、fingerprint、散列 hostname、完整公钥,old 为 None,status 为 updated。示例指纹为 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48。这个值只代表文档中的历史输出,不能作为今天 GitHub 的可信指纹。

如果 master 不是 minion,可切换到运行 salt-master 的用户(原文通常用 root)再发起 SSH 连接:

su -
ssh github.com

原文记录了“无法建立主机真实性”的首次连接提示;回答 yes 后,主机密钥写入 known_hosts,即使最终出现 Permission denied (publickey) 也不妨碍写入。不过,编者补充:只有先从独立可信渠道核对主机指纹,才能有依据地接受首次连接;不能把随网络收到的密钥直接当成已验证身份。

原文还给出查看主机密钥的 nmap 方法:

nmap -p 22 github.com --script ssh-hostkey

它的 2014 年输出包含 DSA、RSA 的历史指纹、端口和耗时信息,其中 RSA 为上面那个历史值。原文提醒 AWS 可能把 nmap 使用视为滥用,因此在 AWS 主机上更倾向使用 ssh-keygen 方法。另一条原命令是:

ssh-keygen -l -f /dev/stdin <<<`ssh-keyscan github.com 2>/dev/null` | awk '{print $2}'

这条命令使用 Bash here-string 和反引号替换;它显示的是 ssh-keyscan 刚取到的密钥指纹,原文把它描述成检查自己的 known_hosts 并不精确。无论扫描还是打印指纹,都没有自动建立信任,仍需和服务方通过可信渠道公布的当前指纹比较。本文保留命令并指出这一差别,没有扫描任何主机。

OpenSSH 6.8 起,默认指纹显示为主机密钥的 base64 SHA256 摘要,而不是上述冒号分隔形式。原文举例为 SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8;同样只作格式演示,不作当前可信值。

十二、推送后立即刷新 GitFS

默认远端文件服务后端每 60 秒更新一次。若希望推送后更快刷新,且 Git 服务器也是 Salt minion,可以通过 Reactor 向 master 发出刷新事件。

首先在 master 新建 /srv/reactor/update_fileserver.sls。原文写法如下:

update_fileserver:
  runner.fileserver.update

然后在 master 配置中关联事件:

reactor:
  - 'salt/fileserver/gitfs/update':
    - /srv/reactor/update_fileserver.sls

Git 服务器上增加 post-receive hook。如果执行 Git push 的用户与 minion 用户相同:

#!/usr/bin/env sh
salt-call event.fire_master update salt/fileserver/gitfs/update

要让其他 Git 用户触发,则原文使用 sudo:

#!/usr/bin/env sh
sudo -u root salt-call event.fire_master update salt/fileserver/gitfs/update

此时原文给出以下 sudoers 规则:

Cmnd_Alias SALT_GIT_HOOK = /bin/salt-call event.fire_master update salt/fileserver/gitfs/update
Defaults!SALT_GIT_HOOK !requiretty
ALL ALL=(root) NOPASSWD: SALT_GIT_HOOK

event.fire_master 后面的 update 是事件数据,本例 reactor 不读取其内容,因此可以换成其他值。事件标签 salt/fileserver/gitfs/update 也可以改名,但 producer 和 reactor 必须一致。脚本与 sudoers 里的 root 应改成实际运行 minion 的用户。

编者静态审核:这一 sudoers 示例授权所有本地用户无口令执行指定命令,范围比专门的 Git 服务用户更宽。实际使用应缩小到明确的服务身份、核对 salt-call 的绝对路径,并通过受控配置检查验证。hook 会执行服务器上的命令,事件可触发更新,不能信任任意可改 hook 的账户。原 reactor 片段只是文档写法,未在本文所处环境进行 Salt 版本兼容或事件联调;不要把它当作通过测试的部署文件。

十三、Git 外部 Pillar 与自定义模块同步问题

Git external pillar,也称 git_pillar,在 Salt 2015.8.0 重写,加入 pygit2 支持,因此可以访问需要认证的仓库,并提供更细的单远端配置。它有独立的配置结构,应查阅原文链接的 git_pillar 文档,不要把 GitFS 示例无条件套用。

最后,原文解释了一个很旧的同步问题:Salt 0.16.3 及更早版本中,部分 GitPython 版本抓取时可能产生 Salt 没捕获的错误。错误不一定中断 fetch,却会打断自定义类型同步之前的 fileserver 更新,连带中断模块或状态同步。旧版本可暂时关闭 master 的 Git 文件服务后端、重启 master 后重试同步;Salt 0.16.4 及之后已绕过这一问题。该说明只适用于对应历史条件,不能作为现代同步故障的通用诊断结论。

原页面保留 © Copyright 2026 声明;本页未列出特定个人作者。Salt Project 代码、文档的其他许可要求以对应项目许可文件为准,本文不凭网页版权行推断额外许可。原创示意图与标明的勘误、审核说明由未完纪编辑整理。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容