用 envsubst 白名单模式安全渲染配置模板
用 envsubst 白名单模式安全渲染配置模板
问题场景
交付配置时,常见做法是维护一个 config.tpl,由部署脚本注入端口、数据库地址等环境相关值。模板里往往还包含其他系统的占位符,例如日志级别 $LOG_LEVEL、备份目录 ${BACKUP_DIR},这些占位符需要原样留给后续模板引擎处理。
如果直接执行:
envsubst < config.tpl > rendered.conf
envsubst 会尝试替换所有可识别的环境变量。未设置的变量会变成空字符串,已设置的变量会被提前替换,导致下游拿不到原始占位符。白名单模式只替换明确列出的变量,适合这种边界清晰的场景。
最小可运行示例
目录结构:
envsubst-demo/
├── config.tpl
├── expected.conf
└── render.sh
config.tpl:
server {
listen $APP_PORT;
proxy_pass http://$DB_HOST:5432;
# 以下占位符由下游系统处理,必须保留
log_level $LOG_LEVEL;
backup_dir ${BACKUP_DIR};
owner ${OWNER_NAME};
}
expected.conf 是白名单渲染后的期望结果:
server {
listen 8080;
proxy_pass http://db.internal:5432;
# 以下占位符由下游系统处理,必须保留
log_level $LOG_LEVEL;
backup_dir ${BACKUP_DIR};
owner ${OWNER_NAME};
}
render.sh:
#!/usr/bin/env bash
set -euo pipefail
# 业务变量:按实际环境注入;这里使用占位值
export APP_PORT="${APP_PORT:-8080}"
export DB_HOST="${DB_HOST:-db.internal}"
# 故意设置下游变量,验证白名单不会替换它们
export LOG_LEVEL="${LOG_LEVEL:-debug}"
export BACKUP_DIR="${BACKUP_DIR:-/var/backups}"
export OWNER_NAME="${OWNER_NAME:-demo}"
command -v envsubst >/dev/null 2>&1 || {
echo 'envsubst not found; install GNU gettext first' >&2
exit 1
}
# 关键:单引号包住变量列表,避免被当前 shell 提前展开
envsubst '$APP_PORT $DB_HOST' < config.tpl > rendered.conf
# 与期望文件逐行比较;无输出且退出码为 0 表示通过
diff -u expected.conf rendered.conf
echo 'render ok: only APP_PORT and DB_HOST were substituted'
运行:
chmod +x render.sh
./render.sh
如果通过,终端只输出:
render ok: only APP_PORT and DB_HOST were substituted
diff -u expected.conf rendered.conf 没有任何差异输出。这里故意给 LOG_LEVEL、BACKUP_DIR、OWNER_NAME 设置了环境变量;如果白名单失效,expected.conf 与 rendered.conf 会出现差异,脚本因 set -e 直接失败。
关键点与边界
-
单引号不能省
envsubst '$APP_PORT $DB_HOST'中的变量列表必须用单引号包住。若写成双引号,当前 shell 会先展开$APP_PORT和$DB_HOST,传给envsubst的列表就不再是变量名。 -
白名单按变量名匹配,不按位置匹配
只要变量名在列表中,模板中所有该变量的出现位置都会被替换,包括注释和字符串内部。如果同一变量在某个位置需要保留字面量,不能靠白名单区分,应改用不同的占位符,或在渲染后做受控替换。
-
白名单中的变量若未设置会变成空字符串
envsubst本身不报错。建议在脚本中先校验:: "${APP_PORT:?APP_PORT is required}" : "${DB_HOST:?DB_HOST is required}"不要把
${VAR:-default}这类 shell 默认值语法依赖在模板里;默认值逻辑放在渲染脚本中更可控。 -
diff的退出码在
set -e下,diff发现差异会返回 1,使脚本退出,这正是校验目的。若只想查看模板到渲染结果的变化,可单独执行diff -u config.tpl rendered.conf || true,不要把|| true加到正式校验命令上。 -
环境依赖
envsubst通常由 GNU gettext 提供。不同基础镜像的包名可能不同,例如 Debian 系常用gettext-base,Alpine 系常用gettext。在 CI 镜像中应先确认命令存在。
通过“只列出需要替换的变量 + diff 对照期望结果”,可以把模板渲染的影响范围限制在 APP_PORT 与 DB_HOST 两个变量内,避免下游占位符被提前清空或替换。
- 点赞
- 收藏
- 关注作者
评论(0)