- 网络安全
- 认证鉴权
- 运维
- 后端
【免费下载链接】teleport
The easiest, and most secure way to access and protect all of your infrastructure.
导读
Teleport 的 Discord 访问申请插件(Access Request Plugin)是构建在 Teleport Access API 之上的一个简单消息插件:当用户提交访问申请(Access Request)时,它会自动向指定的 Discord 频道发送告警消息,并在申请被批准或拒绝后实时更新消息内容,从而把人工审批流程直接嵌入团队日常使用的 Discord 会话中。阅读本文后,你将掌握该插件的完整配置文件结构、CLI 命令用法、Discord 消息与审核展示机制、底层实现原理,以及从构建到运行的完整部署路径。
该插件位于仓库 integrations/access/discord 目录,文档源头为 integrations/access/discord/README.md,官方将其定位为 "a simple Discord access request plugin that sends an alert to a Discord channel when an access request is created"(当访问申请创建时向 Discord 频道发送告警的简单插件)。
插件定位与整体工作流程
Teleport 的访问申请(Access Request)机制允许用户申请临时或长期的角色权限、资源访问权。默认情况下,审批发生在tsh命令行或 Web UI 中;而接入 Discord 插件后,审批信息会被推送到聊天工具,让审批人不必时刻盯着 Teleport 控制台。
从源码结构看,整个插件的核心职责可以归纳为三件事:
- 监听:通过 integrations/access/common/app.go 中的通用 BaseApp 框架连接 Teleport Auth Server(走 gRPC),订阅访问申请事件;
- 推送:通过 bot.go 中的
DiscordBot,把访问申请的标题、角色、登录名、资源、原因等信息格式化成 Discord 消息,POST到目标频道的消息接口; - 更新:当访问申请被审批(批准/拒绝/过期)后,用
PATCH请求原地更新已发送的消息,并通过 Discord Embed(嵌入卡片)展示每一条审核记录。
插件通过role_to_recipients映射把"角色"关联到"Discord 频道 ID",实现按角色路由通知:某个角色对应的访问申请只通知到为其配置的频道。
仓库模块速览
先浏览插件目录,便于后续对照阅读:
integrations/access/discord/ ├── cmd/teleport-discord/ │ ├── example_config.toml # 官方示例配置文件(TOML) │ ├── install # 安装脚本 │ └── main.go # CLI 入口:configure / version / start ├── testlib/ │ ├── fake_discord.go # 模拟 Discord API 的测试替身 │ ├── helpers.go │ ├── message.go │ ├── oss_integration_test.go # OSS 集成测试 │ └── suite.go ├── Makefile # 构建入口(ACCESS_PLUGIN = discord) ├── app.go # 应用组装,注册插件名 "discord" ├── bot.go # DiscordBot 消息机器人核心实现 ├── config.go # 配置解析与校验 └── types.go # Discord API 请求/响应结构体其中 app.go 的职责非常轻量——它定义插件名常量discordPluginName = "discord",该名字用于给 GenericPluginData 打标签,并作为 Audit Log 中的 Delegator(审计日志中标记"由谁处理"的字段);然后通过common.NewApp(conf, discordPluginName)复用所有消息类访问插件共用的应用框架。
配置文件详解(example_config.toml)
插件使用 TOML 格式配置,官方在 example_config.toml 中给出了完整模板,运行teleport-discord configure命令也可以直接打印出这份模板。完整内容如下:
# Example discord plugin configuration TOML file [teleport] # Teleport Auth/Proxy Server address. # addr = "example.com:3025" # # Should be port 3025 for Auth Server and 3080 or 443 for Proxy. # For Teleport Cloud, should be in the form "your-account.teleport.sh:443". # Credentials generated with `tctl auth sign`. # # When using --format=file: # identity = "/var/lib/teleport/plugins/discord/auth_id" # Identity file # refresh_identity = true # Refresh identity file on a periodic basis. # # When using --format=tls: # client_key = "/var/lib/teleport/plugins/discord/auth.key" # Teleport TLS secret key # client_crt = "/var/lib/teleport/plugins/discord/auth.crt" # Teleport TLS certificate # root_cas = "/var/lib/teleport/plugins/discord/auth.cas" # Teleport CA certs [discord] # Discord Bot OAuth token token = "secret-token" [role_to_recipients] # Map roles to recipients. # # Provide discord channel IDs recipients for access requests for specific roles. # "*" must be provided to match non-specified roles. # # "dev" = "0987654321" # "*" = ["1234567890", "0987654321"] [log] output = "stderr" # Logger output. Could be "stdout", "stderr" or "/var/lib/teleport/discord.log" severity = "INFO" # Logger severity. Could be "INFO", "ERROR", "DEBUG" or "WARN".各段配置说明
[teleport]——连接 Teleport 集群
addr:Teleport Auth/Proxy 服务器地址。Auth Server 用 3025 端口,Proxy 用 3080 或 443;Teleport Cloud 用户则填写形如your-account.teleport.sh:443的地址。- 凭据通过
tctl auth sign生成,两种格式二选一:--format=file:生成身份文件(identity),配合refresh_identity = true可让插件周期性自动刷新身份文件;--format=tls:生成 TLS 私钥(client_key)、证书(client_crt)和 CA 根证书(root_cas)三个文件。
[discord]——Discord Bot 凭据
token:Discord Bot 的 OAuth Token。注意:源码中构造请求头时会在 token 前加上"Bot "前缀(见 config.go 第 104 行token := "Bot " + c.Discord.Token),因此这里填写的是不带Bot前缀的原始 Token。- 除此之外,
GenericAPIConfig还支持APIURL字段(见 integrations/access/common/config.go 第 109-117 行),默认值为https://discord.com/api/(config.go 第 37 行的discordAPIUrl常量),一般无需修改,仅自建 Discord API 代理时才会用到。
[role_to_recipients]——角色到频道的路由表
- 键为 Teleport 角色名,值为 Discord 频道 ID 或 ID 列表。
- 通配符
"*"必须提供,用于匹配所有未显式列出的角色,保证任何访问申请都能找到通知目标。 - 例如
"dev" = "0987654321"表示 dev 角色的申请通知到频道 0987654321;"*" = ["1234567890", "0987654321"]表示其余角色同时通知两个频道。
[log]——日志配置
output:日志输出位置,可为stdout、stderr或某个日志文件路径(如/var/lib/teleport/discord.log)。severity:日志级别,可为INFO、ERROR、DEBUG或WARN。
配置校验逻辑
config.go 的CheckAndSetDefaults(第 56-80 行)定义了启动时的强校验规则:
discord.token缺失 → 直接报错missing required value discord.token;role_to_recipients为空 → 报错missing required value role_to_recipients.;role_to_recipients["*"]为空 → 报错missing required value role_to_recipients[*].;APIURL、Log.Output(默认stderr)、Log.Severity(默认info)缺失时自动填充默认值。
Token 文件化支持:LoadDiscordConfig(第 131-152 行)在加载 TOML 后还会检查 token 是否以/开头——如果是,则将其视为文件路径,通过lib.ReadPassword从文件中读取真实 Token(典型的做法是把 Token 放进权限受限的文件,避免出现在命令行或配置明文里)。
CLI 命令与启动方式
插件入口在 cmd/teleport-discord/main.go,基于 kingpin 框架提供三个子命令:
| 命令 | 作用 |
|---|---|
teleport-discord configure | 打印示例 TOML 配置(内嵌的 example_config.toml) |
teleport-discord version | 打印插件版本(teleport.Version 与 Gitref)并退出 |
teleport-discord start | 启动插件主进程 |
start子命令支持两个 flag:
-c/--config:指定 TOML 配置文件路径,默认/etc/teleport-discord.toml;-d/--debug:开启 verbose 日志输出到 stderr(等价于把日志级别强制设为 debug)。
启动流程(run函数,第 74-99 行)依次为:加载并校验配置 → 初始化日志 → 通过discord.NewApp(conf)组装应用 →lib.ServeSignals挂接信号处理(实现优雅关闭)→app.Run(ctx)进入主循环。日志中会输出版本信息:
Starting Teleport Access Discord Plugin version=... git_ref=...消息机器人实现原理(bot.go 深度解析)
bot.go 定义了DiscordBot,它持有三个关键字段:client(resty HTTP 客户端)、clusterName(集群名,用于消息展示)和webProxyURL(Web Proxy 地址,用于在消息中生成申请详情链接)。
HTTP 客户端与健康检查
NewBot(config.go 第 93-126 行)创建 resty 客户端时设置了几个值得注意的参数:
Timeout: 10 * time.Second(discordHTTPTimeout);- 每个 Host 最大连接数 100(
discordMaxConns); - 使用
http.ProxyFromEnvironment,即支持通过环境变量配置 HTTP 代理; - 自动附加请求头:
Content-Type: application/json、Accept: application/json、Authorization: Bot <token>。
插件的健康检查(CheckHealth,第 101-110 行)会调用 Discord API 的GET /users/@me——如果 Token 无效,该请求必然失败,因此错误信息直接提示 "health check failed, probably invalid token"。
申请消息的发送与更新
发送申请消息走BroadcastAccessRequestMessage(第 125-148 行):对recipients列表中的每个频道逐一POST /channels/{channelID}/messages,请求体是DiscordMsg{Msg: {Channel: ...}, Text: ...},并记录返回的message_id用于后续更新。单个频道失败不会中断整体,所有错误最终通过trace.NewAggregate聚合返回。
当访问申请状态变化时,UpdateMessages(第 161-178 行)对每个已发送消息执行PATCH /channels/{channelID}/messages/{messageID},用最新状态文本 + 审核 Embed 列表整体替换原消息。
消息文本组装
消息正文由三个函数拼接而成(discordMsgText,第 215-219 行),定义在共享模块 integrations/access/accessrequest/message.go:
MsgTitle:标题。根据申请是否包含资源,显示 "You have a new Role Request" 或 "You have a new Resource Request";长时访问(long-term access)会追加(long-term access)后缀;MsgFields:字段明细,按序输出 ID、Cluster、User、每个角色的 Role/Login(s)(角色与登录名均排序后输出)、Resource(s)、Request reason 等,所有用户输入都会经过lib.MarkdownEscape转义,防止 Discord Markdown 注入;MsgStatusText:状态行,带 emoji——⏳ PENDING(未处理)、✅ APPROVED(已批准)、❌ DENIED(已拒绝)、⌛ EXPIRED(已过期),并附带审批理由(Resolution reason)。
审核记录的 Embed 卡片
discordEmbeds(第 180-213 行)把每条AccessReview渲染成一张 Discord Embed 卡片:
- 批准(
RequestState_APPROVED)→ 标题 "Approved request at <时间>",颜色discordGreenColor(数值 2328611); - 拒绝(
RequestState_DENIED)→ 标题 "Denied request at <时间>",颜色discordRedColor(数值 13771309,注释中标注为 0xD2222D); - 卡片包含作者(审批人)、描述("Reason: ..."),审核理由同样会经过转义并受长度限制。
能力边界(源码中明确"未实现"的部分)
源码注释和实现明确标注了以下限制,部署前务必知悉:
FetchRecipient仅支持频道 ID(第 221-232 行):Discord Bot 权限无法解析邮箱地址,而按频道名解析需要缓存(该接口返回全量频道且受速率限制),因此当前只支持把"接收者"直接当作 Discord Channel ID 使用;PostReviewReply为空实现(第 150-153 行):Discord 没有线程化回复机制,审核信息统一通过更新原消息的 Embeds 展示;NotifyUser未实现:不会向申请用户发送私信通知;SendReviewReminders未实现:Access List 的定期审核提醒功能尚未支持;FetchOncallUsers未实现:不支持按注解拉取 on-call 用户。
从SupportedApps(第 113-117 行)可以看出,当前机器人只注册了accessrequest.NewApp()一个应用,即专注访问申请通知这一个场景。
运行状态上报与错误处理
插件通过common.StatusSink上报自身健康状态。onAfterResponseDiscord(bot.go 第 59-80 行)是 resty 的响应后置钩子:每次请求完成后,先把 HTTP 状态码换算成插件状态(common.StatusFromStatusCode)推送给 StatusSink——即使请求失败也会上报,这样外部监控能感知"插件目前处于故障"状态。
上报时特意使用context.Background()配合 10 秒超时(discordStatusUpdateTimeout),因为 resty 请求自带的 context 可能已被取消,无法用于上报故障状态。
错误处理方面:当 Discord API 返回非 2xx 时,插件尝试解析DiscordResponse({code, message},定义在 types.go),优先返回message (code: X, status: Y)形式的错误,否则回退为原始响应体 + 状态码。
构建与部署
构建
插件构建走共享 Makefile 体系。Makefile 内容极简:
ACCESS_PLUGIN = discord include ../common.mk即通过设置ACCESS_PLUGIN = discord复用 integrations/access/common.mk 中的通用构建规则,产物名称为teleport-discord。cmd/teleport-discord/install脚本则用于安装到目标系统(通常配合 systemd 作为服务运行)。
部署步骤概述
综合配置文件注释与入口代码,一套典型的部署流程为:
- 创建 Discord Bot:在 Discord 开发者后台创建应用并生成 Bot OAuth Token(配置中的
discord.token),将 Bot 邀请进目标服务器,取得目标频道的 Channel ID; - 生成 Teleport 凭据:在 Teleport 集群上用
tctl auth sign --format=file(或--format=tls)为插件生成专用身份,配置[teleport]段; - 编写配置文件:参照上文
example_config.toml,填写addr、凭据路径、discord.token与role_to_recipients路由表,保存到/etc/teleport-discord.toml(或自定义路径); - 启动:执行
teleport-discord start,通过-d开启 debug 日志排查问题; - 验证:提交一个测试访问申请,观察对应频道是否收到消息;审批后再确认原消息是否被更新为含审核 Embed 的新状态。
测试体系
插件自带一套基于 fake 后端的测试设施(testlib):
fake_discord.go:模拟 Discord API 的测试替身,让集成测试无需真实 Discord 账号即可验证消息发送与更新逻辑;oss_integration_test.go+suite.go:OSS 版本集成测试套件,覆盖从访问申请创建、消息广播到状态更新的完整链路。
这与插件主代码形成了闭环验证:BroadcastAccessRequestMessage/UpdateMessages的请求构造、discordMsgText的消息格式、Embed 颜色与标题规则,都有对应的自动化测试守护。
总结
Teleport Discord 访问申请插件是一个轻量但完整的"访问申请 → Discord 通知 → 聊天内审批展示"闭环实现:它复用integrations/access/common的通用插件框架,仅用少量业务代码(bot.go 约 240 行)就完成了 Discord 消息的格式化、发送、更新与健康上报。对于希望把 Teleport 审批流嵌入 Discord 的团队而言,只需准备一个 Bot Token、若干频道 ID 和一份 TOML 配置即可上线;同时也要注意其能力边界——接收者目前仅支持频道 ID,且不支持私信通知与 Access List 审核提醒。
若需在自托管 Teleport 环境中进一步查阅官方部署文档,可在 Access Requests with Discord 页面(该链接由原 README.md 提供)获取详细配置说明;本文所有实现细节均以当前仓库源码为准。
- 网络安全
- 认证鉴权
- 运维
- 后端
【免费下载链接】teleport
The easiest, and most secure way to access and protect all of your infrastructure.
相关推荐
Teleport Jira 访问请求插件:用 Jira 看板审批 Teleport 权限请求的完整指南
Teleport Jira 访问请求插件:用 Jira 看板审批 Teleport 权限请求的完整指南 导读 Teleport Jira 访问请求插件(Jira
网络安全认证鉴权运维后端Teleport 代理部署指南:深入解析 teleport-proxy-lib Helm 库 Chart 与配置项
Teleport 代理部署指南:深入解析 teleport proxy lib Helm 库 Chart 与配置项 Teleport 仓库中的 teleport
网络安全认证鉴权运维后端在 Jira Cloud 上部署 Teleport Jira 访问请求插件:从看板搭建到 Webhook 联调的完整实战指南
在 Jira Cloud 上部署 Teleport Jira 访问请求插件:从看板搭建到 Webhook 联调的完整实战指南 本指南基于 Teleport 仓库
网络安全认证鉴权运维后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考