这期周报分享我的 1Password 密钥管理实践,主要内容包括常见的密码管理,SSH/Shell 集成和 CI/CD 应用。
这些实践应当同样适用于 bitwarden 等服务。
常见的 SaaS 服务密码管理直接使用 1Password 跨平台客户端创建即可,我个人会遵循如下原则:
每个网站都使用随机生成的唯一密码
1Password 默认使用 Smart Password
来根据网站的要求来生成,不满意的话可以自行微调,比如生成 32 位带大小写字母,数字和符号的密码。
两步验证应开尽开
两步验证是保护账号安全十分有效的手段,如果网站本身支持,应当尽可能开启。
我个人更喜欢 TOTP (Time-based one-time password) 而不是基于短信的两步验证方案:
1Password 的浏览器插件集成了 Scan QR Code 功能,在开启两步验证时直接扫码即可。
密保问题和答案也是密码
现在还需要设置密保问题和答案的服务已经不多了,但是如果需要设置的话,请将他们视作密码的一部分:不要使用真实的回答,将问题原文记录下来,并使用密码生成器生成随机字符串作为答案。
1Password 在上一轮融资之后大刀阔斧地开发了一系列开发者工具,SSH 集成就是其中之一。
对我来说,它能解决如下问题:
它的思路非常简单:开发一个 SSH Agent,对外暴露为 SSH_AUTH_SOCK
,这样所有 SSH Key 请求都由本地 1Password 客户端来处理。
配置完成后,所有应用首次访问指定的 SSH Key 都会弹出如下窗口:
该窗口会展示应用 X 尝试访问 SSH Key Y,只有在认证通过后才能正常获取到 key 的内容。根据各自系统的配置不同,支持 Apple Touch ID,Windows Hello 等生物验证,在 Linux 平台上还提供了完整的 polkit
和 PAM
支持,会调用系统内置的用户认证机制提供原生体验。
使用这个功能首先需要正确配置 1Password SSH Agent。
在 1Password 客户端的配置中勾选并开启 SSH Agent:
在 ssh config 增加如下配置以指定 IdentityAgent
:
Host *
IdentityAgent ~/.1password/agent.sock
根据实际的情况,可以对不同的 Host 增加一些额外配置,比如指定 Host 不使用 1Password:
Host ec2-server
HostName 1.2.3.4
User ec2-user
IdentityFile ~/.ssh/ssh-key-not-on-1password.pem
IdentityAgent none
或者为其指定一个 key:
Host github.com
User git
IdentitiesOnly yes
IdentityFile ~/.ssh/github.pub
最后设置 SSH_AUTH_SOCK
即可:
export SSH_AUTH_SOCK=~/.1password/agent.sock
完成 SSH 配置之后,我们就可以开始迁移自己的 SSH Key 了。目前 1Password 支持 Ed25519
和 RSA
(2048-bit, 3072-bit, and 4096-bit) 两种 key,点击 New Item
,选择 SSH Key
,然后将 Private Key 添加进去就行。Private Key 添加完毕后 1Password 会自动生成一系列信息以供区分(体验满分!):
配置完毕后可以使用 git fetch
等命令来验证~
除了 SSH 集成之外,1Password 还基于他们的 CLI 开发了 shell-plugins
。它解决的问题与 SSH 集成是类似的:很多开发者本地电脑上都以静态方式存储着 AWS/GCP/Github 等服务的密钥,万一泄漏的话可能就会导致测试环境甚至生产环境受到影响。思路同样非常简单,1Password 负责存储实际的密钥,而 1Password Plugin 是一层简单的 wrapper,将 GITHUB_TOKEN 等环境变量替换为实际的密钥,然后再调用对应的命令。
以 Github Plugin 为例,本地的 op/plugins.sh
内容如下:
export OP_PLUGIN_ALIASES_SOURCED=1
alias gh="op plugin run -- gh"
而对应的具体实现为:
func GitHubCLI() schema.Executable {
return schema.Executable{
Name: "GitHub CLI",
Runs: []string{"gh"},
DocsURL: sdk.URL("https://cli.github.com"),
NeedsAuth: needsauth.IfAll(
needsauth.NotForHelpOrVersion(),
needsauth.NotWithoutArgs(),
),
Uses: []schema.CredentialUsage{
{
Name: credname.PersonalAccessToken,
},
},
}
}
配置 Shell 集成同样十分简单:
# Signin op to make sure op itself works
op signin
# Setup plugin gh
op plugin init gh
所有的 shell plugin 都开源在 shell-plugins,使用 Golang 开发,如果有想支持的命令行工具欢迎提交 PR!
最后想分享的是 1Password 在 CI/CD 中的应用。Github Actions 提供了内置的 Secrets 功能,但是有如下问题:
OpenDAL 项目就遇到了这些问题:
为此,OpenDAL 团队采用了基于 1Password 的密钥管理方案。
感谢 1Password 对开源项目的大力支持,只需要向 1password-teams-open-source 提交 PR 即可获取免费的 1Password Teams membership。OpenDAL 提交的 PR 参见:Add Apache OpenDAL (incubating)。OpenDAL 创建了一个独立的 Services
Vault 并将权限赋予给所有的 PPMC 和 Committers,这样确保了团队内部对所有 Secrets 访问的一致性,消除了对 @Xuanwo 的单点依赖。
在最近的更新中,1Password 增加了 Service Account 功能,支持授权该账户访问特定的 Vault。我们向 Infra team 提交了开通 1Password/load-secrets-action 并设置 OP_SERVICE_ACCOUNT_TOKEN
的申请。到这里,我们的 Github 环境已经可以正常从 CI 中获取 1Password 存储的密钥了。
接下来我们只需要使用 load-secrets-action
来加载密钥即可,以 OpenDAL 配置 COS 服务为例:
- name: Load secret
id: op-load-secret
uses: 1password/load-secrets-action@v1
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: $
OPENDAL_COS_TEST: op://services/cos/test
OPENDAL_COS_BUCKET: op://services/cos/bucket
OPENDAL_COS_ENDPOINT: op://services/cos/endpoint
OPENDAL_COS_SECRET_ID: op://services/cos/secret_id
OPENDAL_COS_SECRET_KEY: op://services/cos/secret_key
- name: Test
shell: bash
working-directory: core
run: cargo test cos -- --show-output
env:
RUST_BACKTRACE: full
RUST_LOG: debug
export-env
表示将这些密钥导出为环境变量OP_SERVICE_ACCOUNT_TOKEN
是我们之前配置好的 service account tokenop://services/cos/test
表示使用 services
vault 的 cos
条目中的 test
字段值来替换需要注意的是,该方案同样依赖静态的 OP_SERVICE_ACCOUNT_TOKEN
密钥,如果泄漏可能导致整个 vault 中的密钥泄漏,所以:
本文分享了我在日常工作和生活中使用 1Password 的实践经验,希望能对大家设计自己的密钥管理方案提供一些借鉴!