使用 Docker 和 Kubernetes 部署 .NET Web API 时如何管理密钥配置?
针对这个部署场景,建议将 RSA 私钥移出 appsettings.json,通过 Kubernetes Secret 以文件形式挂载给应用。GitHub 仓库保存部署配置和 Secret 引用,Docker 镜像只包含程序和非敏感配置。需要集中管理多个环境的密钥时,再接入外部密钥管理服务。
各个环境如何选择
- 本地开发:普通敏感配置可以使用 .NET User Secrets。RSA 私钥也可以保存在仓库之外的受限文件中,配置里只保存路径。User Secrets 不加密存储内容,只适合开发环境。Microsoft 文档
- CI/CD:构建和推送镜像通常只需要镜像仓库凭据,不需要生产 RSA 私钥。使用 GitHub Actions 时,可以通过 Actions Secrets 提供流水线所需的凭据,但这些值不会自动成为 Kubernetes 中的运行时配置。GitHub 文档
- Kubernetes 运行环境:通过 Secret 注入敏感配置。RSA 私钥通常包含多行内容,挂载为文件便于保留原始格式,也便于应用按路径读取。Kubernetes 文档
RabbitMQ 的密码也可以按这种方式管理。各服务应只获得自己需要的凭据。
第一步:把私钥移出仓库和镜像
appsettings.json 可以继续保存日志级别、服务地址和私钥文件路径,但应删除私钥正文。
同时检查 Dockerfile 的 COPY 范围,通过 .dockerignore 排除本地密钥文件。仅添加 .gitignore,不能阻止未跟踪的文件被放进 Docker 构建上下文。
如果真实私钥已经进入 Git 提交历史、发布镜像或构建日志,应按可能泄露处理,执行密钥轮换并排查传播范围。删除当前文件无法撤回已经发布的内容。
第二步:在 Kubernetes 中创建 Secret
下面假设目标 namespace 为已存在的 my-app,私钥文件保存在仓库之外。路径和资源名称需要按实际环境替换。
kubectl -n my-app create secret generic web-api-rsa \
--from-file=private-key.pem=/secure/path/private-key.pem
该命令从文件创建 Secret,不会将私钥正文直接写入命令参数。执行端仍需保护原始文件,并具备创建目标 Secret 的权限。
不要把包含真实私钥的 Secret YAML 提交到 GitHub。Secret 的 data 字段使用 Base64 编码,这并不等于加密。Kubernetes Secret 文档
第三步:将 Secret 挂载给 Web API
以下是 Deployment 中 spec.template.spec 的配置片段。将其合并进现有部署文件时,需确保 Deployment 与 Secret 位于同一 namespace:
containers:
- name: web-api
image: your-account/your-web-api:your-tag
env:
- name: Rsa__PrivateKeyPath
value: /run/secrets/rsa/private-key.pem
volumeMounts:
- name: rsa-key
mountPath: /run/secrets/rsa
readOnly: true
volumes:
- name: rsa-key
secret:
secretName: web-api-rsa
items:
- key: private-key.pem
path: private-key.pem
这个片段只包含 Secret 名称、文件路径和非敏感配置,可以存入 GitHub。镜像名称是占位示例,需要替换。
应结合容器运行用户设置文件权限,确保应用能够读取私钥,并限制其他进程访问。
第四步:让 .NET 从文件读取私钥
以下代码假设应用使用 ASP.NET Core 的默认配置机制和 .NET 6+ 的启动方式,且私钥是文本格式文件。可以把读取逻辑放入现有的密钥加载服务中:
var builder = WebApplication.CreateBuilder(args);
var privateKeyPath = builder.Configuration["Rsa:PrivateKeyPath"]
?? throw new InvalidOperationException(
"Missing configuration: Rsa:PrivateKeyPath");
var privateKeyPem = File.ReadAllText(privateKeyPath);
// Pass privateKeyPem to the service that uses the RSA key.
// Do not write the key to logs.
默认环境变量配置提供程序会将 Rsa__PrivateKeyPath 映射为 Rsa
。如果应用自行替换了默认配置来源,需要确认仍然加载环境变量。Microsoft 配置文档读取文件和解析 RSA 私钥是两个步骤。原问题没有说明私钥格式;PEM、加密 PEM、DER 或其他格式,需要使用对应的解析方式。示例只演示配置路径和文件读取。
CI/CD 中有哪些注意事项
生产 RSA 私钥应在部署或运行阶段提供。如果测试需要私钥,使用独立的测试密钥。
如果构建过程必须访问私有依赖源,可以使用 BuildKit secret mount 提供构建凭据。不要通过 Dockerfile 的 ARG、ENV 或 COPY 写入密钥,也不要先复制密钥,再依靠后续层删除。构建步骤同样不能把挂载的凭据复制到输出产物中。Docker 构建密钥文档
部署阶段可以由受限流程创建或更新 Kubernetes Secret,也可以由集群从外部密钥管理服务获取。应限制有权读取 Secret、创建 Pod 或修改 Deployment 的主体,并确认集群已配置 Secret 静态加密。资源类型是 Secret,并不代表存储已经加密。Kubernetes 安全实践
密钥更新后是否会自动生效
Secret 更新后,挂载的文件通常会最终同步,但应用已经加载到内存中的私钥不会自动替换。上面的代码只读取一次,轮换密钥后需要重启工作负载,或另行实现安全的重新加载逻辑。
通过环境变量注入的 Secret 不会自动更新已有进程的环境变量;使用 subPath 挂载的 Secret 文件也不会自动更新。应将应用重新加载或滚动重启纳入轮换流程。Kubernetes 更新行为说明
如果后续需要跨集群共享、集中审计或自动轮换,可以引入外部密钥管理服务。对于当前场景,先将私钥从仓库和镜像中移除,在运行时挂载并限制访问,再建立轮换流程。
备注:内容仅供参考。
