Telegraf Docker Secret Store 插件实战:从 /run/secrets 安全读取容器密钥
Telegraf Docker Secret Store 插件实战从 /run/secrets 安全读取容器密钥【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf导读Telegraf 的secretstores.docker插件自 v1.27.0 起提供允许代理进程直接读取 Docker Swarm / Compose 引擎在容器运行期挂载的 Docker Secrets这些密钥以文件形式出现在容器内的/run/secrets目录下。本文以 插件官方文档 为主线结合 插件源码 与 单元测试完整讲解配置方法、{store-id:secret_key}引用语法、动态密钥刷新机制以及完整的 docker-compose 实战编排帮助你避免把数据库口令、API Token 等敏感信息硬编码进 Telegraf 配置文件。一、插件定位与工作机制1.1 什么是 Docker SecretsDocker 提供了一套原生的密钥管理机制Swarm Secrets / Compose Secrets容器运行时引擎会把声明的密钥以只读文件的形式挂载进容器内部默认挂载路径为/run/secrets/secret-name。Telegraf 的这个 secret store 插件所做的就是把「读取这些文件」的能力以标准 Secret Store 接口接入 Telegraf 的密钥体系让配置中的任意受支持插件选项都可以引用这些密钥。插件定义于 plugins/secretstores/docker/docker.go通过init()中的secretstores.Add(docker, ...)注册到全局插件注册表见 plugins/secretstores/registry.go。1.2 关键限制只读官方文档在 Additional Information 一节明确指出该插件只支持读取密钥不能创建或修改它们。这与 Telegraf 的接口设计一致在 secretstore.go 中SecretStoreEditor接口提供Set/Remove能力是可选接口只读后端不应实现它。因此secrets set与secrets remove这两个 CLI 命令会拒绝 Docker store这也从接口层面保证了「文件系统上只读挂载的密钥」不会被意外篡改。1.3 依赖的宿主环境需要容器运行期 Docker 引擎Swarm 模式或 Compose已正确挂载 secrets进程必须对/run/secrets目录具备读取权限。官方文档给出的 Compose 示例中专门用user: ${USERID}指定了运行用户注释为 “Required to access the /run/secrets directory in container”该插件的源码与测试均无平台相关分支从 构建产物配置 看适用于所有平台。二、配置详解2.1 最小配置与全部参数# Secret store to access docker secrets [[secretstores.docker]] ## Unique identifier for the secret store. ## This id can later be used in plugins to reference the secrets ## in this secret store via {id:secret_key} (mandatory) id docker_secretstore ## Default Path to directory where docker stores the secrets file ## Current implementation in docker compose v2 only allows the following ## value for the path where the secrets are mounted at runtime # path /run/secrets ## Allow dynamic secrets that are updated during runtime of telegraf ## Dynamic Secrets work only with file or external configuration ## in secrets section of the docker-compose.yml file # dynamic false配置全文与 sample.conf 保持一致README 通过toml sample.conf指令自动注入见 docs/SECRETSTORES.md。三个参数的语义与源码行为如下表参数类型默认值是否必填作用idstring无必填全局唯一标识插件中通过{id:secret_key}引用该 store 内的密钥pathstring/run/secrets选填Docker 挂载密钥的目录Compose v2 目前只允许/run/secrets这一个挂载值dynamicboolfalse选填是否允许密钥在 Telegraf 运行期间被重新读取动态刷新2.2 初始化校验逻辑源码级Init()方法docker.go做了三件事ID 校验ID为空直接返回id missing错误——对应测试 TestInitFail默认路径填充Path为空时置为/run/secrets目录存在性检查调用os.Stat(d.Path)若目录不存在例如容器内没有挂载任何 secrets返回accessing directory %q failed: ...错误——对应测试 TestPathNonExistent。这意味着如果你在宿主机上直接用普通配置运行该插件而容器未挂载 secretsTelegraf 会启动失败。官方注释对此的解释是默认路径下不存在/run/secrets目录就说明“没有 secrets”因此直接报错比静默放行更安全。2.3 密钥读取与安全防护Get(key)方法docker.go是密钥读取的核心其中有两处值得注意的实现细节目录穿越防护先用filepath.Abs(filepath.Join(d.Path, key))规范化路径再校验filepath.Dir(secretFile)是否等于配置的d.Path若不等于则返回directory traversal detected for key %q错误。也就是说即使某个上层插件传入恶意构造的../../etc/passwd这类 key也无法逃逸出 secrets 目录读取其他文件文件读取通过os.ReadFile读取文件不存在或读取失败时返回cannot read the secrets value under the directory: ...——对应测试 TestGetNonExistent。List()方法docker.go则直接枚举os.ReadDir(d.Path)下所有文件把文件名作为密钥 key 返回供上层做密钥枚举与校验。三、在插件配置中引用密钥{store-id:secret_key}语法Secrets 定义后在 Telegraf 配置中以如下语法引用{store-id:secret_key}其中store-id就是上文id参数声明的值secret_key是/run/secrets下的文件名不带路径。并非所有插件与配置项都支持 secret store需要看对应插件 README 是否包含Secret store support章节。例如 plugins/outputs/influxdb/README.md 的该章节见其 README 第 20 行附近会详细列出哪些选项支持{...}引用。官方文档docs/includes/secret_usage.md也明确提示是否支持密钥引用以各插件文档中Secret store support一节为准。一个典型的 InfluxDB 输出配置示例密钥名为secret_for_pluginstore id 为docker_secretstore[[secretstores.docker]] id docker_secretstore [[outputs.influxdb]] urls [https://db.example.com:8086] username telegraf password {docker_secretstore:secret_for_plugin}这样数据库密码就只存在于 Docker Secrets 文件中而不是明文写在 telegraf.conf 里。四、动态密钥刷新dynamic 模式4.1 适用条件与前提默认情况下密钥在 Telegraf 启动时解析一次。若将dynamic设为trueTelegraf 可以持续监听并读取被更新过的密钥值把新值传递给相关插件实现「不重启代理即可轮换密钥」。官方文档对适用条件有两个严格限定只对 Compose 中通过file或external方式提供的 secrets 有效其引用的 Compose 规范 09-secrets 文档说明了file/external/environment三种来源的区别environment变量方式定义的 secrets 不支持动态刷新——因为环境变量在容器启动时就被固化了运行时无法再更新。4.2 底层实现原理GetResolver(key)方法docker.go返回一个闭包解析器resolver : func() ([]byte, bool, error) { s, err : d.Get(key) return s, d.Dynamic, err }闭包每次被调用都会重新调用Get去读一次文件而不是缓存旧值返回的布尔标志即Dynamic字段。依据 secretstore.go 中ResolveFunc的契约该标志为false表示静态密钥不会随时间变化为true表示动态密钥如 TOTP 或会轮换的凭据。Telegraf 内部正是依据这个标志决定是否需要在周期内反复求值刷新。对应测试 TestResolver 验证了静态模式下 resolver 能正确返回dynamicfalse与密钥内容TestResolverInvalid 验证了对不存在的 keyresolver 调用会返回cannot read the secrets value under the directory:错误。4.3 动态模式配置示例[[secretstores.docker]] id docker_secretstore dynamic true使用file方式的 Compose 声明services: telegraf: image: docker.io/telegraf:latest secrets: - secret_for_plugin secrets: secret_for_plugin: file: ./credentials/plugin_password.txt # 支持运行期更新后重新挂载五、端到端实战docker-compose 编排5.1 完整 Compose 文件官方文档给出的示例完整复现如下该示例使用environment方式注入密钥services: telegraf: image: docker.io/telegraf:latest container_name: dockersecret_telegraf user: ${USERID} # Required to access the /run/secrets directory in container secrets: - secret_for_plugin volumes: - /path/to/telegrafconf/host:/etc/telegraf/telegraf.conf:ro secrets: secret_for_plugin: environment: TELEGRAF_PLUGIN_CREDENTIAL要点拆解secrets:服务级声明secret_for_plugin被挂载进 telegraf 容器挂载后容器内路径即/run/secrets/secret_for_pluginuser: ${USERID}必须指定一个有权限读取/run/secrets的用户通常取宿主当前用户只读挂载配置telegraf.conf以:ro只读方式挂载符合“密钥不落盘、配置不泄露”的实践environment:密钥来源这里密钥值来自同目录.env文件中的环境变量TELEGRAF_PLUGIN_CREDENTIAL。注意这种来源的 secrets不支持dynamic true动态刷新。5.2.env文件示例.env与docker-compose.yml同目录内容如下TELEGRAF_PLUGIN_CREDENTIALsuperSecretStuff # determine this value by executing id -u in terminal USERID1000USERID通过终端执行id -u得到例如1000结合上一节 Telegraf 配置password {docker_secretstore:secret_for_plugin}最终解析出的值即这里的superSecretStuff。5.3 完整链路回顾docker-compose.yml 中的 secrets 声明 │ (environment / file / external 提供值) ▼ 容器内挂载为 /run/secrets/secret-name 只读文件 │ ▼ [[secretstores.docker]] 的 path 指向 /run/secrets │ ▼ 配置中 {docker_secretstore:secret-name} 被解析器读取 │ (dynamicfalse 启动时读取一次dynamictrue 周期重读) ▼ 插件如 outputs.influxdb使用解析后的真实凭据六、测试与可靠性验证插件目录 plugins/secretstores/docker 自带完整测试套件测试数据位于 testdata包含secret-file-1、secret_file_2、secretFile三个模拟密钥文件。主要覆盖场景测试验证点对应源码行为TestSampleConfig示例配置非空SampleConfig()内嵌 sample.confTestInitFail缺少id报错Init()返回id missingTestPathNonExistent目录不存在报错Init()返回accessing directory ... failedTestListGetList/Get往返一致遍历 testdata 文件并逐一比对内容TestResolver/TestResolverInvalidresolver 求值与错误路径GetResolver闭包行为TestGetNonExistent读取不存在密钥报错Get返回读取错误这些测试一方面印证了文档所述行为另一方面也说明该插件可以直接脱离真实 Docker 环境用任意本地目录如测试中的testdata作为path来验证——这同样适用于你在非容器环境下的联调只要把path指向一个存放密钥文件的目录插件就会按同样逻辑工作目录穿越防护依然生效。七、使用注意事项汇总ID 必须唯一且必填所有 secret store 的id在全局命名空间内必须唯一且必须填写否则初始化失败路径限制Docker Compose v2 目前只允许 secrets 挂载到/run/secrets自定义path主要用于容器环境以外的测试场景动态刷新的边界dynamic true只对file/external来源的 secrets 生效environment来源无效且动态刷新以“每次重新读文件”为代价适合低频、小体积的凭据轮换场景只读约束该 store 不实现SecretStoreEditortelegraf secrets set/remove命令不可用密钥的创建与轮换必须通过 Docker 侧完成权限要求容器运行用户必须能读取/run/secrets务必通过user指令或镜像内属主调整保证权限正确能力白名单并非所有插件配置项都支持{...}引用使用前务必查阅目标插件 README 的Secret store support章节避免配置被静默忽略或报错。八、相关资源插件文档plugins/secretstores/docker/README.md插件源码plugins/secretstores/docker/docker.go插件测试plugins/secretstores/docker/docker_test.go示例配置plugins/secretstores/docker/sample.confSecret Store 接口定义secretstore.goSecret Store 插件开发指南docs/SECRETSTORES.md密钥引用通用说明docs/includes/secret_usage.mdSecret Store 插件索引docs/SECRETSTORES.md 及各插件all目录如 plugins/secretstores/all【免费下载链接】telegrafAgent for collecting, processing, aggregating, and writing metrics, logs, and other arbitrary data.项目地址: https://gitcode.com/GitHub_Trending/te/telegraf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考