Shell脚本如何加密容器环境变量

wen 实用脚本 2

Shell脚本如何加密容器环境变量:安全实践与工具指南

目录导读

  1. 为什么需要加密容器环境变量
  2. 常见加密方案对比
  3. Shell脚本中加密环境变量的具体方法
    • 1 使用OpenSSL加密文件
    • 2 集成Hashicorp Vault动态获取密码
    • 3 使用sops加密YAML/JSON值
    • 4 通过Base64+自定义密钥混淆(非推荐)
  4. 实战:编写安全Shell脚本解密环境变量
  5. 禁忌与风险提示
  6. 问答环节

为什么需要加密容器环境变量

在容器化微服务架构中,环境变量常存储数据库密码、API密钥、云服务令牌等敏感信息,直接在Dockerfile或docker-compose.yml硬编码这些数据,会导致:

Shell脚本如何加密容器环境变量

  • 泄露风险:镜像仓库、CI/CD日志、版本历史都会暴露密钥
  • 合规不达标:PCI-DSS、GDPR等标准要求数据脱敏存储
  • 管理困难:密钥更新时需要重新构建所有依赖镜像

常见加密方案对比

方案 加密强度 集成复杂度 适用场景
OpenSSL AES-256 单机或少量密钥文件
Hashicorp Vault 极高 中-高 企业级、动态密钥管理
sops (Mozilla) 配置文件加密(YAML/JSON)
Base64 极低 极低 仅可用于非生产环境

注意:Base64不是加密,仅是编码,容易被解码,本文重点聚焦OpenSSL和sops两种生产级方案。

Shell脚本中加密环境变量的具体方法

1 使用OpenSSL加密文件

步骤:
加密环境变量文件(例如secrets.env):

openssl enc -aes-256-cbc -salt -in secrets.env -out secrets.enc -pass pass:YOUR_MASTER_PASSWORD

解密并加载到Shell脚本:

#!/bin/bash
MASTER_PASS=$(cat /run/secrets/master_key)  # 通过secret volume读取主密匙
decrypted=$(openssl enc -d -aes-256-cbc -salt -in /etc/secrets/secrets.enc -pass pass:$MASTER_PASS 2>/dev/null)
eval "$decrypted"
# $DB_PASSWORD 等变量已可用

2 集成Hashicorp Vault动态获取密码

使用Vault Agent或直接CLI:

#!/bin/bash
VAULT_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -s --header "X-Vault-Token: $VAULT_TOKEN" \
  https://vault.example.com/v1/secret/data/db \
  | jq -r '.data.data.password'

3 使用sops加密YAML/JSON值

安装sops后,加密配置文件(如config.sops.yaml),Shell脚本解密:

#!/bin/bash
# 注意:sops会自动读取环境变量 $SOPS_AGE_KEY 或关联SSH密钥
sops -d config.sops.yaml > /tmp/config.yaml
source <(yq -r 'to_entries | .[] | .key + "=" + .value' /tmp/config.yaml)
rm /tmp/config.yaml

4 通过Base64+自定义密钥混淆(非推荐)

虽然不建议,但部分老项目中存在:

#!/bin/bash
ENV_B64="U0VDUkVUX0tFWT1tb2NrXQ=="
echo "$ENV_B64" | base64 -d
# 输出:SECRET_KEY=[mock]

风险:任何有权访问Base64内容的人都能解码。

实战:编写安全Shell脚本解密环境变量

以下脚本实现:

  • 读取加密的.env.enc文件
  • 从安全位置(如/run/secrets)获取主密钥
  • 解密后验证时效性
  • 避免在ps命令中泄露密码
#!/bin/bash
set -euo pipefail
# 主密钥来源:Docker Secret / Kubernetes Secret Volume
MASTER_PASSWORD_FILE="/run/secrets/env_master_key"
ENCRYPTED_FILE="/etc/secrets/prod_vars.enc"
TEMP_DECRYPTED="/tmp/.env_decrypted_$$"
# 1. 检查权限
if [ ! -r "$MASTER_PASSWORD_FILE" ]; then
    echo "Error: Cannot read master key" >&2
    exit 1
fi
# 2. 解密并立即执行
MASTER_KEY=$(cat "$MASTER_PASSWORD_FILE")
openssl enc -d -aes-256-cbc -salt -in "$ENCRYPTED_FILE" -pass pass:"$MASTER_KEY" -out "$TEMP_DECRYPTED" 2>/dev/null
if [ $? -ne 0 ]; then
    echo "Decryption failed. Check master key or file integrity." >&2
    rm -f "$TEMP_DECRYPTED"
    exit 2
fi
# 3. 安全加载环境变量(避免子进程继承)
set -a
source "$TEMP_DECRYPTED"
set +a
# 4. 立即清除解密文件
shred -u "$TEMP_DECRYPTED"
# 5. 验证必要变量是否存在
if [ -z "${DB_PASSWORD:-}" ]; then
    echo "Error: DB_PASSWORD not set after decryption" >&2
    exit 3
fi
exec "$@"   # 启动主进程,环境变量已注入

禁忌与风险提示

  • 切勿将主密钥硬编码在Shell脚本中
  • 切勿在日志或错误输出中打印环境变量值
  • 切勿使用export将解密后的变量暴露给全局(使用set -a后注意作用域)
  • 注意OpenSSL的字符串密码可能被ps命令捕获(推荐使用密钥文件而非-pass pass:
  • 容器环境应优先使用Secret Volume(如Kubernetes Secrets)挂载加密文件,而非通过环境变量传递密文

问答环节

Q1:Shell脚本加密后还能被逆向吗?
A:理论上,只要攻击者同时获取加密文件和主密钥,就能解密,因此重点应放在密钥管理上(如使用硬件安全模块或云Secret Manager托管主密钥)。

Q2:sops支持哪些密钥后端?
A:sops支持GPG、Age、AWS KMS、GCP KMS、Azure Key Vault和PGP,推荐使用Age(简单且原生支持Ed25519曲线)。

Q3:如何在不重启容器的情况下更新环境变量?
A:可使用信号重载机制:Shell脚本监听SIGHUP,重新读取加密文件并更新变量,但需注意进程状态一致性,更推荐通过配置中心(如Consul/Vault)动态获取。

Q4:加密文件中可以包含引号和特殊字符吗?
A:可以,但必须确保文件格式与source命令兼容,建议使用export KEY="VALUE"格式,并用双引号包裹值,解密后的文件应通过sh -c "source file"eval安全加载。

加密容器环境变量的核心原则是分离密文与密钥,使用OpenSSL或sops对配置文件进行加密,通过安全渠道(Secret Volume、云密钥管理服务)传递解密密钥,并在脚本中实现最小化暴露窗口(解密即用、用完即毁),避免使用Base64或弱密码,定期轮换密钥。

抱歉,评论功能暂时关闭!