在Kubernetes与Docker部署Angular应用时,如何安全管理环境变量?
一、解决编译时配置局限:Docker与Kubernetes动态注入环境变量
Docker环境实现
改造前端构建逻辑,用运行时环境变量替代编译时硬编码
修改environment.prod.ts,改为读取全局环境变量:export const environment = { production: true, firebase: { apiKey: window.env?.firebaseApiKey || "", authDomain: window.env?.firebaseAuthDomain || "", databaseURL: window.env?.firebaseDatabaseURL || "", projectId: window.env?.firebaseProjectId || "", storageBucket: window.env?.firebaseStorageBucket || "", messagingSenderId: window.env?.firebaseMessagingSenderId || "", appId: window.env?.firebaseAppId || "", measurementId: window.env?.firebaseMeasurementId || "" }, apiServerUrl: window.env?.apiServerUrl || "" };在项目
assets目录下创建env.template.js作为环境变量模板:window.env = { apiServerUrl: "${API_SERVER_URL}", firebaseApiKey: "${FIREBASE_API_KEY}", firebaseAuthDomain: "${FIREBASE_AUTH_DOMAIN}", firebaseDatabaseURL: "${FIREBASE_DATABASE_URL}", firebaseProjectId: "${FIREBASE_PROJECT_ID}", firebaseStorageBucket: "${FIREBASE_STORAGE_BUCKET}", firebaseMessagingSenderId: "${FIREBASE_MESSAGING_SENDER_ID}", firebaseAppId: "${FIREBASE_APP_ID}", firebaseMeasurementId: "${FIREBASE_MEASUREMENT_ID}" };确保
angular.json中包含该模板文件,构建时会打包到dist目录。编写Dockerfile与启动脚本
使用多阶段构建减小镜像体积:# 构建阶段 FROM node:18-alpine as builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN ng build --configuration production # 运行阶段 FROM nginx:alpine COPY --from=builder /app/dist /usr/share/nginx/html COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] CMD ["nginx", "-g", "daemon off;"]编写
entrypoint.sh脚本,容器启动时替换模板中的环境变量:#!/bin/sh # 替换环境变量模板中的占位符 envsubst '${API_SERVER_URL} ${FIREBASE_API_KEY} ${FIREBASE_AUTH_DOMAIN} ${FIREBASE_DATABASE_URL} ${FIREBASE_PROJECT_ID} ${FIREBASE_STORAGE_BUCKET} ${FIREBASE_MESSAGING_SENDER_ID} ${FIREBASE_APP_ID} ${FIREBASE_MEASUREMENT_ID}' \ < /usr/share/nginx/html/assets/env.template.js \ > /usr/share/nginx/html/assets/env.js # 执行原启动命令 exec "$@"启动容器时直接传入环境变量:
docker run -d \ -e API_SERVER_URL="https://prod-api.example.com" \ -e FIREBASE_API_KEY="your-firebase-key" \ -p 80:80 \ your-frontend-image:latest
Kubernetes环境实现
用ConfigMap存储非敏感配置,Secret存储敏感信息,在Deployment中注入容器:
创建ConfigMap存储非敏感配置
apiVersion: v1 kind: ConfigMap metadata: name: frontend-config data: API_SERVER_URL: "https://prod-api.example.com" FIREBASE_AUTH_DOMAIN: "your-project.firebaseapp.com" FIREBASE_DATABASE_URL: "https://your-project.firebaseio.com" FIREBASE_PROJECT_ID: "your-project-id" FIREBASE_STORAGE_BUCKET: "your-project.appspot.com" FIREBASE_MESSAGING_SENDER_ID: "123456789" FIREBASE_APP_ID: "1:123456789:web:abc123" FIREBASE_MEASUREMENT_ID: "G-ABC123"创建Secret存储敏感的Firebase API Key
先将密钥转为Base64编码:echo -n "your-firebase-api-key" | base64,再编写Secret配置:apiVersion: v1 kind: Secret metadata: name: frontend-secrets type: Opaque data: FIREBASE_API_KEY: <base64编码后的密钥>在Deployment中注入环境变量
apiVersion: apps/v1 kind: Deployment metadata: name: frontend-deployment spec: replicas: 3 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: frontend image: your-frontend-image:latest env: # 从ConfigMap读取非敏感配置 - name: API_SERVER_URL valueFrom: configMapKeyRef: name: frontend-config key: API_SERVER_URL - name: FIREBASE_AUTH_DOMAIN valueFrom: configMapKeyRef: name: frontend-config key: FIREBASE_AUTH_DOMAIN # 其他ConfigMap配置项同理 # 从Secret读取敏感配置 - name: FIREBASE_API_KEY valueFrom: secretKeyRef: name: frontend-secrets key: FIREBASE_API_KEY ports: - containerPort: 80Kubernetes会自动将配置注入容器,启动时通过
entrypoint.sh替换到前端文件中,无需重新构建镜像。
二、前端敏感信息安全管理
核心认知
前端代码运行在用户浏览器中,任何直接暴露在前端的信息都可被用户通过开发者工具获取,因此绝不能将真正的敏感密钥(如服务端私钥、数据库密码)放到前端。
针对Firebase的优化
Firebase的apiKey是设计为可公开的:它仅用于标识你的Firebase项目,不具备直接访问敏感资源的权限。真正的访问控制依赖于Firebase的安全规则(数据库、存储规则)和用户身份验证。只要配置好规则,限制只有认证用户才能访问资源,apiKey暴露不会带来风险。
通用敏感信息保护方案
- 后端代理转发:所有需要敏感密钥的请求,都通过自有后端服务代理。比如前端调用你的后端API,后端再使用密钥调用Firebase Admin SDK或其他第三方服务,敏感密钥仅存在于后端服务器。
- 避免前端存储敏感数据:不要将用户令牌、密钥等存储在
localStorage/sessionStorage中,优先使用HttpOnly Cookie存储认证信息,防止XSS攻击窃取。 - 构建时清理敏感信息:确保构建过程中不会将敏感信息硬编码到产物中,所有动态配置都通过运行时环境变量注入,敏感内容仅通过Secret管理。
内容的提问来源于stack exchange,提问作者Rinkal

