云上部署小服务:把密钥和配置从代码里拆出来
前阵子把一个只有三个接口的小服务从本地搬到云服务器上,部署完才发现一个老问题:数据库密码和第三方接口的密钥还写在代码里的一个配置文件中。当时图省事,想着就自己用、仓库也是私有的,没当回事。后来换了一台服务器、又让同事帮忙看日志,才意识到这种写法迟早要出事——代码会流转,密钥不该跟着代码走。
一、密钥写进代码,到底危险在哪
首先是版本库。哪怕仓库现在是私有的,之后改成公开、换人协作,或者把代码打包发给别人排查问题,密钥就跟着出去了。删掉提交也不保险,历史记录里还留着,清理起来很麻烦。
其次是日志和报错。服务一报错,配置对象被原样打进日志的情况并不少见;排查时随手截一张图发到群里,密钥可能就躺在截图里。还有镜像:把代码连同写死的密钥打进镜像,镜像一分发,密钥也就分发出去了。
所以原则很简单:代码只描述“需要哪些配置”,具体的值在运行时从环境里拿。
二、做法一:环境变量,小服务够用
对小服务来说,环境变量是最省事的做法。程序启动时从环境里读,读不到关键项就报错退出,别偷偷用一个默认值继续跑——静默失败比直接报错更难查。
import os
API_KEY = os.environ.get("API_KEY", "")
DB_URL = os.environ.get("DATABASE_URL", "")
if not API_KEY:
raise SystemExit("API_KEY 未设置")
本地开发时,可以把变量写进一个 .env 文件,在启动前加载;但 .env 只留在本机,绝不提交。提交到仓库的,是一份 .env.example,里面只写变量名和示例格式,比如 API_KEY=your-api-key-here,让后来的人一眼就知道要配什么。
部署到云服务器时,在启动服务前设置好这些变量,或者用启动脚本统一加载。容器部署也一样,用 -e 传入单个变量,或者用 --env-file 指定一个放在服务器上的环境文件。关键是:环境文件在服务器上生成,别跟着代码仓库走。
三、做法二:配置文件分层,非敏感项放文件里
不是所有配置都是秘密。端口、超时时间、日志级别、功能开关这些不敏感的,放配置文件里反而更清楚,比如一份 config.yaml:
server:
port: 8080
timeout: 30
log:
level: info
程序启动时先读默认配置,再用环境变量覆盖其中敏感的部分。约定好优先级:环境变量高于配置文件,配置文件高于代码里的默认值。这样做的好处是,换环境只改配置和变量,不用改代码;回滚也只需要换回上一份配置。
配置文件的读取也要留心:路径别写死,最好允许用一个环境变量指定配置文件位置,开发、测试、生产各用一份,互不干扰。
四、上云之后,记得补上这几件事
第一,检查 .gitignore,把 .env、带真实密钥的配置文件、本地证书都加进去,提交前用 git status 看一眼,确认没有多余文件。
第二,在仓库里搜一遍,看看密钥是不是早就进过提交记录。如果进过,先改掉那个密钥再上线,光删文件是不够的。
第三,给日志做掩码。返回配置、打印请求头之前,先把密钥中间部分替换成星号,只留前几位方便对账。排查问题够用了,也不会把完整密钥写进日志文件。
第四,密钥要有轮换的打算。存在环境变量里,轮换就是改变量、重启服务,比改代码重新打包要轻得多。定期换一次,泄露的影响面也小。
五、一个能照抄的检查清单
- 代码里搜不到任何真实密钥和密码
- .env.example 在仓库里,.env 不在
- 服务缺配置时启动即报错,不带默认值硬跑
- 日志里看不到完整密钥
- 换环境只改配置和环境变量,不改代码
把密钥和配置从代码里拆出来,工作量其实不大:一个读环境变量的函数、一份示例文件、一次 .gitignore 检查。换来的好处是,代码可以放心地给人看、放心地打包,密钥只活在它该在的地方——运行环境里。
- 点赞
- 收藏
- 关注作者
评论(0)