Go安全版图再添利器:OpenPubkey SSH开源,用SSO彻底改变SSH认证

本文永久链接 – https://tonybai.com/2025/03/31/openpubkey-ssh-open-source

对于许多开发者和运维工程师而言,管理SSH密钥是一项繁琐且易出错的任务。正如SSH发明者、芬兰计算机科学家Tatu Ylonen所指出的,许多组织中过时授权密钥的数量甚至远超员工人数,这带来了巨大的安全隐患。现在,一个基于Go语言生态的创新项目——OpenPubkey SSH (OPKSSH),旨在彻底改变这一现状。近日,随着Cloudflare将OPKSSH代码捐赠给Linux基金会下的OpenPubkey项目并将其开源,开发者们终于可以拥抱一种更便捷、更安全的SSH认证方式:使用熟悉的单点登录(SSO)系统。本文将简要介绍OPKSSH项目及其技术基石OpenPubkey技术。

1. 核心看点:OPKSSH 开源与价值解读

OPKSSH (OpenPubkey SSH) 是一个巧妙的工具,它将OpenID Connect (OIDC) 等现代SSO技术与SSH协议集成起来,其核心目标是消除手动管理和配置SSH公私钥的需求,同时不引入除身份提供商(IdP)之外的任何新的可信第三方

此前,虽然底层的OpenPubkey协议已于2023年成为Linux基金会的开源项目,但OPKSSH作为BastionZero(现已被Cloudflare收购)的产品,一直是闭源的。Cloudflare的此次捐赠,使得整个OpenPubkey技术栈的关键应用层实现也完全开放,这对于Go社区和整个基础设施安全领域都是一个重要进展。

2. OPKSSH解决了什么痛点?

通常,我们在进行远程服务器管理和运维操作时会使用SSH免密登录,即通过生成SSH密钥对并将公钥复制到远程服务器来实现。但这种传统方式的SSH密钥管理存在诸多问题:

  • 密钥分发与轮换困难:需要手动将公钥部署到目标服务器,密钥泄露或员工离职后的吊销流程复杂。
  • 长期密钥风险:长期存在的私钥增加了泄露风险,一旦泄露,影响范围广。
  • 可见性差:难以清晰追踪谁拥有对哪些服务器的访问权限,公钥本身缺乏身份信息。

这些问题常常困扰企业的IT运维团队和安全管理人员,他们需要确保访问控制的安全性和可管理性,同时降低操作复杂性和人力成本。

那如何解决这些问题呢?OPKSSH带来了新的解决方案。

3. OPKSSH如何解决这些问题?

OPKSSH基于OpenPubkey协议,带来了革命性的改进:

  • 使用临时性密钥(Ephemeral Keys)提升安全性

OPKSSH使用按需生成的临时SSH密钥对取代长期密钥。用户通过SSO登录后,OPKSSH自动生成有效期较短(默认为24小时,可配置)的密钥。这大大缩短了密钥泄露的风险窗口。

  • 通过单点登录(SSO Login)增强易用性

用户只需运行opkssh login,通过熟悉的IdP (如Google, Azure AD等) 进行SSO认证,即可自动获取所需的SSH密钥。无需手动生成、复制或管理私钥文件,即可在任何安装了opkssh的机器上进行SSH连接。

  • 通过Identity-based Auth提升可见性与简化管理

授权不再基于难以管理的公钥列表(比如~/.ssh/known_hosts),而是基于易于理解和审计的用户身份(如Email地址)。管理员只需在服务器配置中指定允许访问的电子邮件地址列表即可。

到这里你可能会问:这么好用的OPKSSH是如何工作的呢?别急,我们下面就来介绍一下OPKSSH的工作原理。

4. OPKSSH的工作原理

Cloudflare的文章中有一个很好的介绍Opkssh工作原理的例子和示意图,这里也借用过来:

如图所示,当用户alice@example.com使用OPKSSH登录服务器,这个过程大致如下:

  • 用户本地执行命令opkssh login触发OIDC流程,用户向IdP认证。
  • OpenPubkey协议介入,在OIDC流程中巧妙地将用户临时生成的公钥与用户的身份信息绑定,生成一个PK Token(本质上是一个增强的ID Token,包含了公钥信息并由IdP签名)。
  • OPKSSH将此PK Token打包进一个临时的SSH 公钥文件(利用SSH证书的扩展字段)。
  • 当用户发起SSH连接时,这个特殊的公钥文件被发送到服务器。
  • 服务器配置了AuthorizedKeysCommand指令,调用opkssh verify(OpenPubkey验证器)。
  • 验证器检查PK Token的有效性(签名、有效期、颁发者),提取公钥和用户身份(Email),并根据服务器配置判断该用户是否有权访问。

关键在于,这一切无需修改现有的SSH客户端或服务器软件本身,仅需在服务器端sshd_config中添加两行配置即可启用,这个我们在本文后面会详细说明。

OPKSSH的魔力源于其底层的OpenPubkey协议。OpenPubkey本身是一个基于Go语言实现的Linux基金会项目 (github.com/openpubkey/openpubkey)。

OpenPubkey的核心创新在于,它通过一种客户端修改的方式,将用户持有的公钥(PKu)与OIDC的ID Token进行了加密绑定,而无需 OIDC 提供商(OP)作任何修改。这是通过巧妙利用OIDC流程中的nonce参数实现的。客户端不再生成完全随机的nonce,而是生成一个包含其公钥等信息的客户端实例声明(cic),并将cic的哈希值作为nonce发送给OP。OP在签发ID Token时会包含这个nonce。这样,最终得到的PK Token就同时承载了OP 对用户身份的认证以及用户对其公钥的所有权声明(通过客户端的额外签名防止身份误绑定攻击)。

这一机制将OIDC的认证模型从持有者认证(Bearer Authentication) 升级到了持有证明(Proof-of-Possession, PoP)。在Bearer模型下,任何窃取到ID Token的人都可以冒充用户;而在PoP模型下,用户需要证明自己持有与PK Token中公钥对应的私钥,从而有效抵御令牌重放(Token Replay)令牌泄露(Token Export) 攻击,安全性显著提高。

OpenPubkey的设计还考虑了可扩展性,例如引入MFA-Cosigner概念,可以进一步增强安全性,甚至在OP本身被攻陷的情况下也能提供保护。关于OpenPubkey协议设计的详细内容,可以参见参考资料中OpenPubkey的论文,这里就不赘述了。

了解了原理之后,下面我们来实际验证一下opkssh通过IdP实现SSO一键登录服务器的效果。

5. 使用opkssh实现免密登录服务器

这次验证的环境是这样的:

  • 客户端:macOS
  • 服务端:Ubuntu 22.04.1 LTS
  • IdP:microsoft (注:国内访问microsoft的服务器成功率高)

我们先来看看客户端的操作步骤:

5.1 opkssh在客户端的操作

首先在客户端安装opkssh,你可以选择直接下载编译好的opkssh二进制文件:

$curl -L https://github.com/openpubkey/opkssh/releases/latest/download/opkssh-osx-amd64 -o opkssh; chmod +x opkssh

由于opkssh是纯Go实现的,如果你本地有Go工具链,也可以选择通过源码安装(在国内,可能选择源码安装的速度更快):

$go install github.com/openpubkey/opkssh@latest

安装完成后,我们就来进行客户端的IdP认证。输入下面命令:

$opkssh login
INFO[0000] Opening browser to http://127.0.0.1:59638/chooser

该命令会打开本地浏览器,并展示下面页面:

截止到目前,opkssh支持选择Google、Microsoft或Gitlab作为IdP,这里我们选择Sign in with Microsoft

之后浏览器将跳转到下面页面:

这里使用我的Microsoft账号进行身份认证,点击“接受”,即完成认证,之后你可以关闭页面!

而命令行也会提示下面信息:

INFO[0002] listening on http://127.0.0.1:3000/
INFO[0002] press ctrl+c to stop
Writing opk ssh public key to /Users/tonybai/.ssh/id_ed25519.pub and corresponding secret key to /Users/tonybai/.ssh/id_ed25519Keys generated for identity
Email, sub, issuer, audience:
bigwhite.cn@hotmail.com AAAAAAAAAAAAAAAAAAAAAP5YMhbf2Ufl_eI1PdK12VE https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0 096ce0a3-5e72-4da8-9c86-12924b294a01

接下来,我们再来看看服务端要进行的操作与配置。

5.2 opkssh在服务端的操作

在要登录的服务器端安装opkssh,由于安装后还要进行一些设置,我建议直接采用opkssh项目提供的安装脚本进行安装:

$ wget -qO- "https://raw.githubusercontent.com/openpubkey/opkssh/main/scripts/install-linux.sh" | sudo bash
Detected OS is debian
Created group: opksshuser
Created user: opksshuser with group: opksshuser
Downloading version latest of opkssh from https://github.com/openpubkey/opkssh/releases/latest/download/opkssh-linux-amd64...
opkssh                           100%[========================================================>]  16.01M  83.4MB/s    in 0.2s
Installed opkssh to /usr/local/bin/opkssh
Configuring opkssh:
  Creating sudoers file at /etc/sudoers.d/opkssh...
  Adding sudoers rule for opksshuser...
Installation successful! Run 'opkssh' to use it.

之后我们需要修改一下服务端的sshd server的配置。SSH服务器支持一个名为AuthorizedKeysCommand的配置参数,该参数允许我们使用自定义程序来确定SSH公钥是否被授权。因此,我们通过对/etc/ssh/sshd_config文件进行以下两行更改,将SSH服务器的配置文件更改为使用OpenPubkey验证程序而不是SSH默认的验证程序:

AuthorizedKeysCommand /usr/local/bin/opkssh verify %u %k %t
AuthorizedKeysCommandUser opksshuser

然后通过opkssh添加授权的用户,这些用户登录后将具备root用户权限:

$opkssh add root bigwhite.cn@hotmail.com microsoft
Successfully added new policy to /etc/opk/auth_id

最后重启一下sshd服务:

$systemctl daemon-reload
$systemctl status sshd

5.3 ssh登录验证

注:为了避免使用之前的ssh免密登录,可以在服务端将.ssh/authorized_keys中的公钥删除!

服务端的opkssh命令行被sshd服务调用进行客户端验证时,会在/var/log/opkssh.log中打印相关日志,这也是opkssh起到作用的一个间接证明。

我在客户端依然以原先的ssh登录命令尝试登录服务器:

$ssh root@<your_server_ip>

我们在服务端opkssh.log中可以看到下面一些输出:

2025/03/29 02:57:43 /usr/local/bin/opkssh verify root AAAAKGVjZHNhLXNoYTItbDUQAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAaAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSXO9YZhMPnGkYfnwpFu/HeX29s7q0l4lK5qCgvaeaWh3zBSidDh49Nirsu5Iwh7YVRkKMa5q+hhnJEFAh7FL5LAAAAZAAAABNlY2RzYS1zaGEyLW5pc3RwMjU2AAAASQAAACEAqD5msj3BsQhlpszOJHBoIcmK3Ex/BwyNWKHgp6labScAAAAgULO5naYi9xOmzrShcGiVIprRbdSvdWltioSVKu63h6Y= ecdsa-sha2-nistp256-cert-v01@openssh.com
2025/03/29 02:57:43 Providers loaded:  https://accounts.google.com 206584157355-7cbe4s640tvm7naoludob4ut1emii7sf.apps.googleusercontent.com 24h
https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0 096ce0a3-5e72-4da8-9c86-12924b294a01 24h
https://gitlab.com 8d8b7024572c7fd501f64374dec6bba37096783dfcd792b3988104be08cb6923 24h

2025/03/29 02:57:44 warning: failed to load user policy: failed to read user policy file /root/.opk/auth_id: error reading root home policy using command /usr/bin/sudo -n /usr/local/bin/opkssh readhome root got output Failed to read user's home policy file: failed to open /root/.opk/auth_id, open /root/.opk/auth_id: no such file or directory
 and err exit status 1
2025/03/29 02:57:44 successfully verified

之后,我就成功登录到服务器上了!

6.小结

OPKSSH 的开源是 OpenPubkey 项目和 Go 安全生态的重要里程碑。它不仅提供了一个解决 SSH 密钥管理难题的实用方案,也展示了 Go 语言在构建安全、可靠的基础设施工具方面的强大能力。

我们鼓励对安全、身份认证和 Go 开发感兴趣的开发者们:

  • 试用 OPKSSH: 在你的开发或测试环境中体验 SSO 登录 SSH 的便捷。
  • 关注 OpenPubkey 项目: Star GitHub 仓库,了解最新动态。
  • 参与社区贡献: 通过 Pull Request、Issue 反馈、参与讨论等方式为项目贡献力量。可以在 OpenSSF Slack 的 #openpubkey 频道找到社区成员,或参加每月一次的社区会议。

随着 OPKSSH 的加入和持续发展,我们期待 OpenPubkey 能够在更多场景下发挥价值,例如代码签名 (Sigstore 集成)、端到端加密通信等,进一步丰富和巩固 Go 语言在云原生和安全领域的基础设施地位。

7. 参考资料


Gopher部落知识星球在2025年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。并且,2025年将在星球首发“Gopher的AI原生应用开发第一课”、“Go陷阱与缺陷”和“Go原理课”专栏!此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格6$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily
  • Gopher Daily Feed订阅 – https://gopherdaily.tonybai.com/feed

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

Go模块发布流程再加固:go mod verify -tag提案详解

本文永久链接 – https://tonybai.com/2025/03/28/go-mod-verify-tag

Go模块(module)在Go 1.11版本中引入,显著简化了依赖管理,使开发者能够通过go.mod文件明确声明和管理库依赖,支持语义版本控制,并提高了构建速度和可移植性。使得Go语言的依赖管理更加现代化和高效,提升了开发者的体验。

同时引入的校验和数据库 (sumdb) 也极大地增强了Go生态的依赖管理的确定性和安全性。然而,在模块作者发布新版本时,从本地代码库打上标签推送到代码托管平台,再到被Go Proxy和sumdb收录,这个过程中仍然存在一个微妙但关键的信任验证环节缺失。近期,Go团队接受了一项备受关注的提案(Issue #68669,旨在通过扩展go mod verify命令来弥补这一空白,为模块作者提供一种官方途径来验证他们本地的代码和标签确实与Go生态系统将收录的版本一致。在这一篇文章中,我就根据issue中的内容,来简单介绍一下这一新增安全机制的背景和运作原理。

注:该机制的提案刚刚被Accept,尚未确定在哪个版本落地,不过大概率是在Go 1.25版本中。

1. 问题背景:发布过程中的信任鸿沟

当前,Go开发者在发布一个新的模块版本时,通常的流程是:

  • 在本地代码库完成开发和测试。
  • 使用git tag (例如git tag v1.2.3) 创建版本标签。
  • 使用git push –tags 将代码和标签推送到代码托管平台 (如 GitHub)。
  • 等待Go Proxy (如proxy.golang.org) 拉取新版本,并将其信息提交给官方sumdb。

虽然sumdb保证了下游用户下载的模块代码未被篡改 (相对于sumdb中的记录),但它无法保证sumdb中记录的版本就精确地是模块作者在本地打标签时所期望的版本。潜在的风险点包括:

  • 代码托管平台被篡改: 拥有强制推送权限的攻击者可能在标签推送后修改了标签指向的提交。
  • 代码托管平台自身问题: 平台自身可能存在Bug或被攻击,导致返回给Go Proxy的代码与原始标签不符。
  • Go Proxy或sumdb问题: 尽管概率较低,但中间环节也可能存在问题。

正如提案贡献者和Go核心团队成员在讨论中指出的,目前缺少一个简单直接的方式让模块作者确认:“我本地标记为v1.2.3的代码,是否就是全世界通过Go工具链获取到的那个v1.2.3?”。

2. 提案核心:go mod verify -tag

为了解决这个问题,提案#68669建议为现有的go mod verify命令增加一个新的-tag标志。go mod verify命令目前用于检查本地缓存的依赖项是否被修改,而新的-tag标志则将关注点转向了当前模块本身

2.1 拟议的功能

$go mod verify -tag=<value>

其中 可以是:

  • : 一个具体的 Git 标签,例如v1.2.3。命令将检查本地仓库中该标签对应的代码树,计算其哈希,并与sumdb中记录的该版本的哈希进行比对。
  • latest: 检查本地仓库中最新的Git标签。
  • all: 检查本地仓库中所有的Git标签。

2.2 核心价值与使用场景

  1. 发布后验证 (主要场景):这是该提案最核心的预期用途。模块作者在推送标签后,可以立即运行此命令来确认他们的代码已经“安全”地进入了Go的模块分发体系,且内容无误。
# 假设已完成开发
$git tag v1.2.3
$git push origin v1.2.3 # 或 git push --tags

# 关键一步:验证刚推送的标签
$go mod verify -tag=v1.2.3

这个操作还有一个重要的副作用:如果v1.2.3 尚未被Go Proxy和sumdb收录,运行go mod verify -tag=v1.2.3 会触发Go工具链去查询这个版本,从而加速它被Go生态系统发现和记录的过程,同时完成验证

  1. 安全审计与代码审查: 当需要对某个模块的特定版本进行安全审计或深入的代码审查时,可以使用此命令验证本地检出的代码副本确实是sumdb中记录的那个“官方”版本,而不是可能已被篡改的某个代码托管平台上的版本。

3 社区讨论与设计考量

在提案的讨论过程中,社区也探讨了该功能是否应该放在go mod verify命令下,因为它与验证依赖项的现有功能有所不同。一些替代方案被提出,例如创建一个新的子命令go mod verify-tags或go mod proxy -check=TAG等。

最终,提案审查小组倾向于并接受了将此功能作为go mod verify的扩展,主要是考虑到:

  • 概念一致性: 虽然对象不同(当前模块 vs 依赖项),但核心都是进行某种形式的“验证” (verify)。
  • 避免命令扩散: 增加标志比增加新子命令更轻量。
  • 文档可更新: 可以通过更新go mod verify 的文档来清晰地说明其扩展后的功能范围。

需要注意的是,该提案主要解决的是模块作者验证自身发布的问题,与验证项目依赖项是否在源头(如GitHub)被篡改(例如Issue #66653讨论的情况)是不同的问题,尽管它们都属于Go模块供应链安全的一部分。

4. 小结

go mod verify -tag提案的接受是Go模块生态系统在安全性方面迈出的又一重要步伐。它为模块作者提供了一个简单、官方的工具来关闭发布流程中的一个关键信任缺口,增强了从代码编写到模块分发的端到端完整性保证。

虽然具体的实现细节仍在进行中 (由 Issue #68669 跟踪),但Go开发者可以期待在未来的Go版本中获得这一实用功能。这不仅有助于提升个别模块的安全性,也将进一步巩固整个Go生态系统的供应链安全基础。

5. 参考资料


Gopher部落知识星球在2025年将继续致力于打造一个高品质的Go语言学习和交流平台。我们将继续提供优质的Go技术文章首发和阅读体验。并且,2025年将在星球首发“Go陷阱与缺陷”和“Go原理课”专栏!此外,我们还会加强星友之间的交流和互动。欢迎大家踊跃提问,分享心得,讨论技术。我会在第一时间进行解答和交流。我衷心希望Gopher部落可以成为大家学习、进步、交流的港湾。让我相聚在Gopher部落,享受coding的快乐! 欢迎大家踊跃加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格6$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻) – https://gopherdaily.tonybai.com

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite
  • Gopher Daily归档 – https://github.com/bigwhite/gopherdaily
  • Gopher Daily Feed订阅 – https://gopherdaily.tonybai.com/feed

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



Statcounter View My Stats