跳到主要内容

来自环境变量和文件的密钥

除了把密钥保存在数据库中,密钥库(Key Store)中的条目还可以在任务运行时从 Semaphore 服务器上的 文件,或从 Semaphore 服务器进程的环境变量中读取其值。 当凭据已经在 Semaphore 之外完成配置时,这非常有用,例如:

  • 以 Docker 或 Kubernetes secret 的形式挂载到 Semaphore 容器中的 SSH 密钥;
  • 由某个代理(HashiCorp Vault Agent、cert-manager 等)写入磁盘并定期轮换的令牌;
  • 由编排器注入到容器环境中的密码。

Semaphore 不会把该值复制到自己的数据库中。每当任务需要该密钥时,服务器都会重新读取文件或变量, 因此在磁盘上轮换凭据后,下一个任务就会生效。

信息

文件或变量由 Semaphore 服务器读取,而不是由运行器(Runner)读取。使用远程运行器时, 请把文件挂载到服务器主机上;服务器解析密钥后再交给运行器。

选择来源

创建或编辑密钥时(Key Store → New Key),表单顶部有来源选项卡:

选项卡值的来源需要填写的内容
LocalSemaphore 数据库(加密存储)在表单中填写登录名、密码或私钥
Storage Pro外部密钥存储,例如 HashiCorp Vault存储以及密钥路径
EnvSemaphore 服务器进程的环境变量变量名,例如 PROD_SSH_KEY
FileSemaphore 服务器上的文件文件的绝对路径,例如 /var/lib/semaphore/secrets/prod.json

选择 EnvFile 后,登录名、密码和私钥字段会消失。完整的凭据(包括 SSH 和 Login With Password 类型密钥的登录名)都必须放在文件或变量中。

1. 允许目录

出于安全考虑,Semaphore 只读取位于其密钥目录内的密钥文件。任务启动时,其他任何路径都会被拒绝:

Failed to install inventory: file path must be inside secrets path

默认的密钥目录是 /tmp/semaphore。请通过 config.json 中的 dirs.secretsSEMAPHORE_SECRETS_PATH 环境变量把它指向存放密钥文件的目录。 优先级规则请参阅密钥目录

下面的 Docker Compose 示例挂载了一个主机目录并允许读取它:

services:
semaphore:
image: semaphoreui/semaphore:latest
environment:
SEMAPHORE_SECRETS_PATH: /var/lib/semaphore/secrets
volumes:
- /srv/semaphore/secrets:/var/lib/semaphore/secrets:ro

等效的 config.json 片段:

{
"dirs": {
"secrets": "/var/lib/semaphore/secrets"
}
}

File 选项卡中填写的路径须遵守以下规则:

  • 必须是绝对路径(/var/lib/semaphore/secrets/prod.json,而不是 prod.json);
  • 不得包含 .. 片段;
  • 解析后必须位于密钥目录内(子目录也可以);
  • 文件必须可被运行 Semaphore 的用户读取(在官方 Docker 镜像中该用户是 semaphore,UID 1001)。

环境变量没有此类限制;服务器只是从自身环境中读取指定名称的变量。

2. 设置值的格式

文件内容(或变量的值)取决于密钥类型。文件末尾的单个换行符会被忽略;其余内容按原样使用。

SSH 密钥

Semaphore 期望的是一个 JSON 文档,而不是原始的 PEM 或 OpenSSH 私钥文件:

{
"login": "deploy",
"passphrase": "",
"private_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----\n"
}
  • login——SSH 用户名,以 --user 参数传给 Ansible。留空则由清单(Inventory)决定(ansible_user)。对于 Git 仓库,登录名为空时默认使用 git
  • passphrase——私钥的口令,或空字符串。
  • private_key——私钥内容,换行符编码为 \n

可以用 jq 从现有密钥生成该封装格式,它会自动处理转义:

jq -n --arg login deploy --rawfile key ~/.ssh/id_ed25519 \
'{login: $login, passphrase: "", private_key: $key}' \
> /srv/semaphore/secrets/prod_ssh.json
chmod 0400 /srv/semaphore/secrets/prod_ssh.json

然后创建一个类型为 SSH 的密钥,打开 File 选项卡,输入 /var/lib/semaphore/secrets/prod_ssh.json (即容器内部看到的路径)。

注意

File 选项卡指向原始私钥(例如 ~/.ssh/id_ed25519)是行不通的。 该文件会被按 JSON 解析,任务在加载清单时会失败。

Login With Password

同样是一个 JSON 文档:

{
"login": "svc-ansible",
"password": "s3cr3t"
}

login 留空即可把该密钥用作普通令牌或密码,例如用作 Ansible vault 密码。

环境变量示例

Env 选项卡使用相同的 JSON 格式。在 Docker Compose 中:

services:
semaphore:
image: semaphoreui/semaphore:latest
environment:
PROD_SSH_KEY: '{"login":"deploy","passphrase":"","private_key":"-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----\n"}'

创建一个 SSH 密钥,选择 Env 选项卡,并输入 PROD_SSH_KEY 作为变量名。

提示

环境变量对容器内的每个进程都可见,而且经常会出现在编排器的元数据和日志中。 在可能的情况下,优先使用 File 选项卡配合挂载的 secret。

故障排查

错误原因解决方法
file path must be absolute填写了相对路径填写以 / 开头的完整路径
file path must not contain traversal segments路径中包含 ..填写解析后的路径
file path must be inside secrets path文件位于 dirs.secrets 之外SEMAPHORE_SECRETS_PATH 设为文件所在目录,或移动文件
no such file or directory路径错误,或未挂载到容器中检查卷挂载,并使用容器内的路径
permission deniedSemaphore 进程无法读取该文件修正文件的所有者或权限
invalid character '-' looking for beginning of value提供的是原始私钥而不是 JSON 封装按上文所示封装密钥