尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

BuildKit Dockerfile Lint 规则 SecretsUsedInArgOrEnv 详解:为什么 ARG/ENV 不应存放敏感数据

BuildKit Dockerfile Lint 规则 SecretsUsedInArgOrEnv 详解:为什么 ARG/ENV 不应存放敏感数据 BuildKit Dockerfile Lint 规则 SecretsUsedInArgOrEnv 详解为什么 ARG/ENV 不应存放敏感数据【免费下载链接】buildkitconcurrent, cache-efficient, and Dockerfile-agnostic builder toolkit项目地址: https://gitcode.com/GitHub_Trending/bu/buildkitBuildKit 内置的 Dockerfile linterfrontend/dockerfile/linter提供了一套静态检查规则用于在镜像构建早期发现 Dockerfile 中的安全隐患。本文聚焦其中与密钥安全直接相关的SecretsUsedInArgOrEnv规则它会在ARG/ENV指令的键名疑似承载敏感数据时输出告警并引导开发者改用 secret mount 构建方案。读完本文你将掌握该规则的触发机制、底层正则判定逻辑、告警与豁免# check指令的完整用法以及如何用docker buildx build --secret将密钥安全地注入构建过程。规则告警输出当 Dockerfile 中出现违规写法时lint 会输出如下消息Potentially sensitive data should not be used in the ARG or ENV commands在源码层面该规则在 ruleset.go 中定义为RuleSecretsUsedInArgOrEnv LinterRule[func(string, string) string]{ Name: SecretsUsedInArgOrEnv, Description: Sensitive data should not be used in the ARG or ENV commands, URL: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/, Format: func(instruction, secretKey string) string { return fmt.Sprintf(Do not use ARG or ENV instructions for sensitive data (%s %q), instruction, secretKey) }, }即规则名称为SecretsUsedInArgOrEnv非实验性未设置Experimental: true属于默认启用的常规规则详细告警信息形如Do not use ARG or ENV instructions for sensitive data (ARG SECRET_PASSPHRASE)其中%s为触发指令名ARG或ENV%q为被认为敏感的具体键名。为什么禁止在 ARG/ENV 中存放密钥本地开发时通过环境变量把密钥传给运行中的进程是很常见的做法但在 Dockerfile 中使用ENV或ARG设置密钥则是不安全的因为ENV声明的变量会直接写入最终镜像的配置元数据image config 中的Env字段任何能拿到镜像的人都可以通过docker inspect直接读出ARG虽然默认不会写入最终镜像元数据但它的值会在构建历史中可见并且ARG声明的值在RUN等指令中被展开引用时同样可能以明文形式出现在层的内容里这些密钥一旦进入镜像就无法通过删除一行 Dockerfile 来“抹掉”——它们已经固化在镜像层与元数据中。因此该规则对ENV/ARG中键名暗示包含敏感数据的声明进行告警。正确做法是使用 secret mountssecret 挂载密钥只在构建过程中按需注入既不会进入最终镜像也不会写入镜像元数据从而兼顾构建期可用性与产物安全性。底层判定逻辑键名的正则匹配该规则的触发并不依赖构建时的真实值而是对ARG/ENV的键名做静态正则匹配。判定实现在 validations.go 中核心逻辑如下。先看“敏感词表”deny tokenssecretTokens : []string{ apikey, auth, credential, credentials, key, password, pword, passwd, secret, token, } pattern : (?i)(?:_|^)(?: strings.Join(secretTokens, |) )(?:_|$)再看“豁免词表”allow tokens命中的键即使含敏感词也不会告警allowTokens : []string{ public, file, version, } allowPattern : (?i)(?:_|^)(?: strings.Join(allowTokens, |) )(?:_|$)最终判定func validateNoSecretKey(instruction, key string, location []parser.Range, lint *linter.Linter) { deny, allow : getSecretsRegex() if deny.MatchString(key) !allow.MatchString(key) { msg : linter.RuleSecretsUsedInArgOrEnv.Format(instruction, key) lint.Run(linter.RuleSecretsUsedInArgOrEnv, location, msg) } }匹配细节说明正则不区分大小写(?i)因此password、PASSWORD、Secret都会命中敏感词必须作为独立单词出现键名两侧是下划线_或行首/行尾(?:_|^)与(?:_|$)所以git_key、SUPER_Secret、super_duper_secret_token都会命中而sunflower这类普通词不会单词内嵌不会误报例如secretary因为secret两侧不是_或边界不会命中豁免词的优先级更高只要键名同时包含敏感词和豁免词如public、file、version则不会告警。从源码结构看这条规则的判定输入仅是键名本身与赋给变量的值无关也就是说即使ARG password的值是空字符串只要键名敏感lint 依然会提示。这属于“宁可疑错、不可放过”的保守设计其取舍可由使用者通过# check指令按行豁免。典型触发与豁免场景测试用例佐证仓库的集成测试 dockerfile_check_test.go 完整覆盖了该规则的触发与豁免场景是理解判定边界最直接的依据。会触发告警的键名Level 1 告警FROM scratch ARG SECRET_PASSPHRASE ENV SUPER_Secretfoo ENV passwordbar secretbaz ARG super_duper_secret_tokenfoo authbar ENV apikeybar sunflowerfoo ENV git_key测试断言分别输出Do not use ARG or ENV instructions for sensitive data (ARG SECRET_PASSPHRASE) Do not use ARG or ENV instructions for sensitive data (ENV SUPER_Secret) Do not use ARG or ENV instructions for sensitive data (ENV password) Do not use ARG or ENV instructions for sensitive data (ENV secret) Do not use ARG or ENV instructions for sensitive data (ARG super_duper_secret_token) Do not use ARG or ENV instructions for sensitive data (ARG auth) Do not use ARG or ENV instructions for sensitive data (ENV apikey) Do not use ARG or ENV instructions for sensitive data (ENV git_key)注意测试中的两点细节ENV apikeybar sunflowerfoo同一行两个变量只有apikey触发sunflower不受影响ENV git_key值虽然为空键名git_key仍触发告警印证了“只看键名”的判定逻辑。不会触发的键名豁免词生效ENV PUBLIC_KEY ARG public_token ARG SECRET_PASSPHRASE_FILE ENV password_filebar secret_Filebaz ARG AUTH_MODULE_VERSION这些键名分别命中了豁免词public、file、version例如PUBLIC_KEY含key但含publicpassword_file含password但含file因此被判定为“公开内容、文件路径、模块版本”等非敏感场景不予告警。按行豁免# check指令规则覆盖到的键名并不总是真正的密钥例如测试中PUBLIC_KEY被豁免正是因为公开密钥本就不需要保密。当确实需要在某一行使用含敏感词的键名时可以通过 Dockerfile 注释中的# check指令做按行豁免而无需全局关闭# checkskipSecretsUsedInArgOrEnv // allow secret in environment ENV passwordbar # checkskipSecretsUsedInArgOrEnv // allow secret in arg ARG password也可以跳过所有规则同样只作用于紧随其后的那条指令# checkskipall // is local to only this instruction ENV alternate_passwordbar # checkskipall // is local to only this instruction ARG alternate_password这些指令的解析逻辑在 linter.go 的WithMergedConfigFromComments中实现lint 器会扫描指令上方注释解析# check前缀并合并配置。支持的选项ParseLintOptions包括选项含义示例skip规则名跳过指定规则# checkskipSecretsUsedInArgOrEnvskipall跳过全部规则# checkskipallexperimental规则名/experimentalall仅启用实验性规则# checkexperimentalInvalidDefinitionDescriptionerrortrue将告警提升为错误构建失败# checkerrortrue需要说明的是这些# check豁免是按指令局部生效的上面测试中第 4、5 行带checkskip的ENV password/ARG password均未出现在告警列表里而第 6 行未加注释的同名键名依然报错。若需要全局跳过该规则可在构建时通过 linter 配置统一关闭。正确姿势使用 secret mount 注入密钥与其想方设法豁免告警更推荐彻底改走 secret mount 路线。原始文档给出了标准示例。❌ 反面写法用 ARG 传递 AWS 凭据ARG AWS_ACCESS_KEY_ID ARG AWS_SECRET_ACCESS_KEY RUN aws s3 cp s3://my-bucket/file .凭据会以构建参数的形式暴露在构建上下文与历史中。✅ 正面写法secret mount 环境变量注入RUN --mounttypesecret,idaws_key_id,envAWS_ACCESS_KEY_ID \ --mounttypesecret,idaws_secret_key,envAWS_SECRET_ACCESS_KEY \ aws s3 cp s3://my-bucket/file .--mounttypesecret是 BuildKit 对RUN指令提供的扩展挂载类型。从 convert_secrets.go 的实现看通过env变量名可将密钥直接映射为构建进程的环境变量对应m.Env非空的分支若不指定env密钥默认挂载为文件目标路径为/run/secrets/idPOSIX 平台默认值见 convert_secrets.go也可用target自定义Windows 平台没有/run/secrets默认位置必须显式指定targetC:/path/to/secret绝对路径否则会报错。配合构建命令传入密钥$ docker buildx build \ --secret idaws_key_id,envAWS_ACCESS_KEY_ID \ --secret idaws_secret_key,envAWS_SECRET_ACCESS_KEY .--secret的参数格式为idid,env本机环境变量也可用src文件路径从本地文件读取密钥。构建时 BuildKit 会从本机环境变量或文件中取真实值仅注入到对应的RUN指令进程中不落盘、不写镜像层、不进镜像元数据。适用范围与边界适用阶段该检查发生在dockerfile2llb的指令校验阶段validations.go即 Dockerfile 被解析并转换为 LLB底层构建图时执行属于静态检查不需要真正构建镜像即可发现风险。判定依据仅基于键名正则与变量值无关ENV与ARG均在检查范围内。豁免机制# checkskipSecretsUsedInArgOrEnv单条指令或构建侧全局配置跳过public、file、version等豁免词可避免常见误报。建议对测试用例中出现的password_file、AUTH_MODULE_VERSION这类“名字敏感但实际非密钥”的变量优先考虑改名规避歧义确需保留原键名时再使用# check按行豁免避免无差别关闭规则导致真正的密钥泄露被漏检。BuildKit 的这套 Dockerfile linter 规则目录位于 frontend/dockerfile/linter/docs含规则索引_index.md与各规则说明完整规则定义集中在 ruleset.go所有规则的集成测试见 dockerfile_check_test.go供你在实际项目中参考、验证与扩展。【免费下载链接】buildkitconcurrent, cache-efficient, and Dockerfile-agnostic builder toolkit项目地址: https://gitcode.com/GitHub_Trending/bu/buildkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表