- 网络安全
- 认证鉴权
- 运维
- 后端
【免费下载链接】teleport
The easiest, and most secure way to access and protect all of your infrastructure.
导读
当安全团队需要在维护窗口期锁定整个集群、立即终止已持有效证书用户的访问、或满足 FedRAMP/NIST 合规要求时,Teleport 的 Locking 机制(源自 RFD 9 - Locking)提供了标准化的解决手段。本指南以该设计文档为核心,结合当前开源仓库中的类型定义、CLI 实现与核心校验逻辑,系统讲解Lock资源的模型、tctl lock的完整操作方式、集群内传播与"严格/尽力"两种回退模式,帮助你掌握从创建锁到终止活跃会话的全链路能力。
一、设计动机:为什么需要 Locking
RFD 9 将 Locking 定位为一种面向**交互(interactions)**的授权控制机制。所谓交互包括三类对象:会话(SSH/k8s/App/DB)、连接以及证书的签发。安全团队由此获得三项核心能力:
- 维护窗口锁定:在计划维护期间整体锁出团队,防止变更期间产生并发访问;
- 立即撤销访问:即使攻击者或离职员工已持有有效的 Teleport 证书,也能终止其会话并禁止后续建立新连接;
- 合规落地:为 FedRAMP/NIST 等审计要求提供可记录、可解释的强制访问控制手段。
需要特别强调的是,Locking 与User资源中已有的Status/LoginStatus字段是两个层次的机制。后者服务于 Web UI 登录失败时的认证(authn)限制;而 Locking 是作用于已认证用户的授权(authz)限制——它会连带影响已建立的会话,因此不应被用于响应登录失败的场景。二者分别作用于 authn 与 authz 两个层面,相辅相成,需要同时保留。
二、Lock资源模型
RFD 9 引入了一个名为Lock的 v2 资源,其核心数据结构由LockSpecV2和LockTarget组成:
message LockSpecV2 { // Target describes the set of interactions that the lock applies to. LockTarget Target; // Message is the message displayed to locked-out users. string Message; // Expires if set specifies TTL for the lock. google.protobuf.Timestamp Expires; } message LockTarget { string User; // Teleport 用户名 string Role; // 根集群中已知的 RBAC 角色名;在远程集群中同时评估原始角色与映射后角色 string Login; // 本地 UNIX 用户名 string Node; // 节点名称或 UUID(已弃用,请用 ServerID) string MFADevice; // 用户 MFA 设备的 UUID string WindowsDesktop; // Windows 桌面名称 string AccessRequest; // Access Request 的 UUID string Device; // 可信设备(Trusted Device)ID,需 Teleport Enterprise string ServerID; // Teleport server 的 UUID }在 api/types/lock.go 中,Lock被定义为一组接口契约:Target()/SetTarget()负责目标读写,Message()/SetMessage()负责向被锁用户展示的消息,LockExpiry()/SetLockExpiry()与CreatedAt、CreatedBy记录生命周期信息。其中关键方法IsInForce(t time.Time) bool实现了时效判断:若Expires为空则永续生效,否则返回t.Before(*c.Spec.Expires)。
从当前实现看,LockTarget在原始设计基础上又扩展了LinuxDesktop、BotInstanceID(Bot 实例 UUID)与JoinToken(Bot 接入令牌名)等字段(见 api/types/lock.go 中的Match方法),体现了锁定机制随机器身份(Machine ID)与桌面访问功能演进的趋势。
2.1 匹配语义:简单名匹配,无通配符
RFD 9 明确规定了LockTarget的匹配规则:按简单名称精确匹配,不支持通配符或正则表达式。多个锁可以并存且非单例资源——只要交互被任意一个生效中的锁命中,即被终止或阻止。这在实际运维中意味着:可以通过多个细粒度锁叠加实现复杂的访问控制矩阵。
源码 api/types/lock.go 中的Match方法验证了这一语义:每个字段只要非空即参与精确等值比较,所有字段同时满足才算命中;空LockTarget{}永远不匹配。而CheckAndSetDefaults则强制要求至少设置一个目标字段,防止创建"锁所有交互"的空洞锁。
三、管理锁:tctl lock完整命令参考
RFD 9 规定Lock资源可通过标准的tctl [get|create|rm]管理,并提供了专用的tctl lock辅助命令。当前仓库的 tool/tctl/common/lock_command.go 将辅助命令实现为一组 flag,完整对应关系如下:
| Flag | 作用 | 对应 LockTarget 字段 |
|---|---|---|
--user | 锁定指定 Teleport 用户 | User |
--role | 锁定指定角色 | Role |
--login | 锁定指定本地 UNIX 用户 | Login |
--mfa-device | 锁定指定 MFA 设备 UUID | MFADevice |
--windows-desktop | 锁定指定 Windows 桌面 | WindowsDesktop |
--linux-desktop | 锁定指定 Linux 桌面 | LinuxDesktop |
--access-request | 锁定指定 Access Request UUID | AccessRequest |
--device | 锁定指定可信设备 UUID(Enterprise) | Device |
--server-id | 锁定指定 Teleport server/agent 的 UUID | ServerID |
--bot-instance-id | 锁定指定 Bot 实例 UUID | BotInstanceID |
--join-token | 锁定指定 Bot 接入令牌 | JoinToken |
--message | 向被锁用户展示的提示消息 | Message |
--expires | 过期时间点(RFC3339),如2021-06-14T22:27:00Z | Expires |
--ttl | 从当前时刻起的持续时长,如10h | Expires |
--format | 输出格式:text(默认)/json/yaml | — |
源码细节值得注意:computeLockExpiry(tool/tctl/common/lock_command.go)强制--expires与--ttl只能二选一,二者同时给出会返回BadParameter错误;--ttl内部转换为time.Now().UTC().Add(ttl),即最终仍以 RFC3339 时间点存储。创建后的输出支持--format=json/yaml,可直接序列化锁资源,便于脚本与 IaC 工具消费。
3.1 场景一:创建永久锁
$ tctl lock --user=foo@example.com --message="Suspicious activity." Created a lock with name "dc7cee9d-fe5e-4534-a90d-db770f0234a1".此命令锁定foo@example.com且永不过期。解除方式为tctl rm lock/dc7cee9d-fe5e-4534-a90d-db770f0234a1。上述命令等价于:
tctl create <<EOF kind: lock metadata: name: dc7cee9d-fe5e-4534-a90d-db770f0234a1 spec: message: "Suspicious activity." target: user: foo@example.com version: v2 EOF该 YAML 同样对应tctl get lock/dc7cee9d-fe5e-4534-a90d-db770f0234a1的输出。
3.2 场景二:创建带过期时间的锁
$ tctl lock --role=developers --message="Cluster maintenance." --ttl=10h Created a lock with name "dc7cee9d-fe5e-4534-a90d-db770f0234a1".该命令在接下来 10 小时内锁出所有developers角色的用户。若命令在 2021-06-14 12:27 UTC 执行,则等价于:
tctl create <<EOF kind: lock metadata: name: dc7cee9d-fe5e-4534-a90d-db770f0234a1 spec: target: role: developers message: "Cluster maintenance." expires: "2021-06-14T22:27:00Z" # RFC3339 version: v2 EOF也等价于tctl lock --role=developers --message="Cluster maintenance." --expires="2021-06-14T22:27:00Z"。
3.3 场景三:锁定节点与各类 Agent
--node已在 Teleport 13.x.x、12.4.x 与 11.3.x 中被--server-id取代,前者保留仅为向后兼容:
# 已弃用:仅能锁定 SSH 节点 $ tctl lock --node=node-uuid # 推荐:锁定任意 Agent(SSH、Kubernetes、Database 等) $ tctl lock --server-id=agent-uuid--server-id的引入意义在于:一个 agent 进程可能同时运行多种服务(例如 SSH + Database + k8s),按 UUID 锁定即可一次性覆盖其全部服务,且文档明确说明命中锁的节点/agent 还会被阻止向 auth server 心跳上报。
3.4 场景四:锁定 MFA 设备与可信设备
$ tctl lock --mfa-device=device-uuid # 锁定 MFA 设备 $ tctl lock --device=device-uuid # 锁定可信设备(需 Enterprise) $ tctl lock --windows-desktop=windows-desktop-name按 MFA 设备锁定意味着即使攻击者窃取了某用户的证书与 TOTP 设备,也可通过设备 UUID 精准隔离而无需波及整张用户账号。
四、锁定模式的推导与回退(Fallback)机制
Lock资源在集群内通过独立的LockWatcher传播,而非复用主缓存系统。每个 Teleport 进程都持有自己的本地 lock watcher,它维护一条到 auth server 的专用连接以监听Lock资源变化,并支持按LockTarget列表配置的派生订阅;在后台会自动记录并重试连接/数据陈旧问题。LockWatcher由一个时长参数控制"最大失效期",默认 5 分钟(见 lib/services/watcher.go 中的NewLockWatcher与LockWatcherConfig)。
当本地锁视图超过容忍间隔变为陈旧(stale)时,系统需要决定是信任最后一次已知的锁集合,还是采取预防性封锁。这个决策策略被编码为锁定模式(locking mode):
spec: options: # strict: 锁数据不新鲜时,终止所有匹配交互(预防性封锁) # best_effort: 继续使用最近一次已知的锁视图 lock: [strict|best_effort]模式的最终取值遵循三条规则:
- 涉及用户的事务,从该用户任一角色的 options推导;
- 若用户所有角色均未指定,或事务不涉及用户,则取集群级默认值
AuthPreference.LockingMode,默认best_effort(以保持与 HA 部署的向后兼容); - 只要输入中有一个
strict,最终结果即为strict——单个 strict 会覆盖其他所有 best_effort。
best_effort意味着锁数据暂时不可用时仍沿用最近视图继续放行,优先可用性;strict则宁可错杀,优先安全性。在 lib/services/lock.go 中可见与之配套的StrictLockingModeAccessDenied错误:"preventive lock-out due to local lock view becoming unreliable"(因本地锁视图不可靠而进行的预防性封锁)。
五、锁的强制点:签发、建连与断连
RFD 9 将强制逻辑部署在三个生命周期阶段,当前实现均可从源码中得到印证。
5.1 禁止签发新证书
锁定生效期间,任何命中锁目标的用户证书与主机证书都不得再签发,同时应产生一条审计事件。RFD 9 指明在lib/auth/auth.go的generateUserCert与GenerateHostCert中增加锁检查。当前实现中,auth server 通过CheckLockInForce(mode, targets)(lib/auth/auth.go)在证书签发路径上执行校验,命中后返回AccessDenied。实际报错形如:
ERROR: lock targeting Role:"developers" is in force: Cluster maintenance.5.2 禁止发起新连接
即便被锁用户已持有有效证书,也应被阻止发起新会话。RFD 9 要求在auth.Authorize中加入检查,且该限制覆盖 Teleport 支持的全部代理:SSH、k8s、App、DB,同时阻断 Proxy Web UI 会话发起以及 Auth API(gRPC 与 HTTP/JSON)请求。证书签发路径上,lib/auth/auth.go 会综合CertParams.LockingMode(defaultMode)与由用户名、角色集合、激活的 Access Request、Bot 实例、Join Token 等组装出的lockTargets列表执行检查,与设计文档的覆盖面一致。
5.3 终止已建立的会话
对于 SSH、k8s、DB 等具备"实时会话"语义的协议,新建的锁应像证书过期一样强制踢出既有会话。实现方式是把与 Teleport 实例关联的LockWatcher引用传入srv.Monitor。文档同时指出,由于srv.Monitor是共享会话监视组件,这套终止逻辑可天然复用于所有使用它的协议。被终止前会先向用户打印信息:
Lock targeting Role:"developers" is in force: Cluster maintenance. the connection was closed on the remote side on 15 Jun 21 10:43 CEST六、叶子集群复制
Lock资源从根集群向叶子集群(leaf clusters)复制,方式与可信集群间共享 CA 的periodicUpdateCertAuthorities例程类似:周期性调用叶子集群 API,将根集群当前生效的锁集合整体替换到叶子集群。来自根集群的锁存储于独立的 backend key 命名空间:
/locks/<clusterName>/<lockName>这一设计确保了即使叶子集群的本地管理员不了解根集群策略,根集群的安全决定依然能强制执行到叶子侧。需要补充的是,RFD 9 特别指出Role目标在远程集群中的评估会同时考虑原始角色与映射后角色(root cluster 到 leaf cluster 的角色映射),避免因角色名映射而绕过锁定。
七、在仓库中继续深挖
- rfd/0009-locking.md:本文的设计源头文档,状态为
implemented; - api/types/lock.go:
Lock接口、LockTarget.Match匹配逻辑与IsInForce时效判断; - api/types/lock_test.go:锁类型单元测试,可观察边界行为;
- lib/services/lock.go:
LockInForceAccessDenied错误构造与SSHAccessLockTargets/ProxyingLockTargets等目标组装逻辑; - lib/services/watcher.go:
LockWatcher与LockWatcherConfig的实现; - lib/auth/auth.go:证书签发与授权路径上的
CheckLockInForce检查点; - tool/tctl/common/lock_command.go:
tctl lock全部 flag、computeLockExpiry与多格式输出实现。
结语
Teleport 的 Locking 机制是一个层次清晰的授权强制框架:Lock资源以精确简单名匹配描述"锁什么",LockWatcher负责把决策实时同步到每个进程,strict/best_effort模式定义锁数据不可用时的安全姿态,而签发、建连、断连三个强制点保证锁在会话全生命周期内都不可绕过。对于需要维护窗口管控、应急隔离与合规审计的团队,掌握tctl lock的命令语义与模式推导规则,即可在数秒内完成一次覆盖全集群、全协议族的访问封禁。
- 网络安全
- 认证鉴权
- 运维
- 后端
【免费下载链接】teleport
The easiest, and most secure way to access and protect all of your infrastructure.
相关推荐
Teleport 基于 Datalog 的 RBAC 访问测试器设计解析(RFD 32)
Teleport 基于 Datalog 的 RBAC 访问测试器设计解析(RFD 32) 本篇技术指南以 Teleport 开源仓库中的设计文档 RFD 32
网络安全认证鉴权运维后端Teleport 会话录制细粒度访问控制:RBAC `where` 条件与 `session` 标识符实战解析(RFD 44)
Teleport 会话录制细粒度访问控制:RBAC where 条件与 session 标识符实战解析(RFD 44) 导读 本篇文章以 Teleport 仓库
网络安全认证鉴权运维后端Teleport 活动 SSH 会话访问控制:RFD 45 `where` 条件详解与实战
Teleport 活动 SSH 会话访问控制:RFD 45 where 条件详解与实战 导读 本文围绕 Teleport 开源仓库中的 RFD 45(ssh_s
网络安全认证鉴权运维后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考