原作: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。

一、安装依赖并选择 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 起提供。
上述配置的效果逐项如下:
- 第一和第四个远端把
develop分支或标签映射为 base;第二和第三个使用salt-base。 - 第一个提供整个仓库;第二个只提供
salt及其子目录;第三个只提供other/salt;第四个只提供salt/states。 - 第三个只从分支提供文件,不使用标签或 SHA。
- 第四个仅有 base(指向 develop)和 foo(指向 foo)两个 saltenv。
- 第一、第四个从
salt://根命名空间提供文件;第二个挂在salt://bar,第三个挂在salt://other/bar。 - 第二、第三个引用同一仓库,需要用唯一名称区分重复 remote。
- 第四个绕过默认禁止向非 HTTPS 远端发送认证信息的保护。
- 第五个配置 all_saltenvs,因此 master 分支或标签的内容出现在每个 GitFS 环境中。
- 设置
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
第一个远端每三分钟更新,第二个每两分钟更新。
六、配置优先级、子目录和挂载点
正常情况下,从高到低的覆盖顺序为:
- 单远端下的 saltenv 设置。
- 全局
gitfs_saltenv中的环境设置。 - 单远端参数。
- 全局参数。
以 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,状态编译器拒绝继续。这与先开发、再测试、最后晋升生产的工作流冲突。要实现这种晋升,原文要求同时做到三件事:
- top.sls 的环境键写成
{{ saltenv }},使相同 top 文件可以存在于各分支,渲染时替换成正在处理的环境。 - 在 minion 中设置
top_file_merging_strategy: same,让各环境仅查看自己的 top.sls。 - 明确指定
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 是服务身份,原文按以下次序推荐凭据:
- Deploy token:项目或组范围、只读,适合 gitfs 和 git_pillar。在 Settings → Repository → Deploy tokens 创建,授予
read_repository。用户名使用 GitLab 创建时显示的值,token 作为密码。 - Project access token:项目范围、角色可控,适用于 master 确实需要推送的场合,例如 winrepo runner;原文示例以 token 名称为用户名、token 为密码。
- Personal access token:可以工作,但把 master 访问绑定到真人账户,按 token 所有者认证。
- 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 代码、文档的其他许可要求以对应项目许可文件为准,本文不凭网页版权行推断额外许可。原创示意图与标明的勘误、审核说明由未完纪编辑整理。












暂无评论内容