部署安全实践
概述
部署安全是现代应用开发和运维的重要组成部分,涉及从代码开发到生产环境运行的整个生命周期。本文档详细介绍部署过程中的安全最佳实践、合规要求、安全工具配置和威胁防护策略。
安全架构模型
纵深防御架构
┌─────────────────────────────────────────────────────────────┐
│ 纵深防御架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 边界安全 │ │
│ │ (Perimeter Security) │ │
│ │ • WAF • DDoS防护 • 防火墙 • VPN • CDN │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 网络安全 │ │
│ │ (Network Security) │ │
│ │ • 网络分段 • 访问控制 • 流量监控 • IDS/IPS │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 主机安全 │ │
│ │ (Host Security) │ │
│ │ • 系统加固 • 补丁管理 • 防病毒 • 入侵检测 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 应用安全 │ │
│ │ (Application Security) │ │
│ │ • 代码审计 • 漏洞扫描 • 安全测试 • 运行时保护 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 数据安全 │ │
│ │ (Data Security) │ │
│ │ • 加密存储 • 传输加密 • 访问控制 • 数据脱敏 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
零信任安全模型
┌─────────────────────────────────────────────────────────────┐
│ 零信任安全模型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 身份验证 │───▶│ 授权管理 │───▶│ 访问控制 │ │
│ │ Identity │ │Authorization│ │Access Control│ │
│ │ Verification│ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 设备信任 │ │ 网络分段 │ │ 持续监控 │ │
│ │Device Trust │ │Network Segm.│ │Continuous │ │
│ │ │ │ │ │Monitoring │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 策略引擎 │ │
│ │Policy Engine│ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
容器安全
Docker安全配置
安全的Dockerfile
# 使用官方基础镜像
FROM node:18-alpine AS base
# 创建非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# 设置工作目录
WORKDIR /app
# 复制package文件
COPY package*.json ./
# 安装依赖(仅生产依赖)
RUN npm ci --only=production && npm cache clean --force
# 复制应用代码
COPY --chown=nextjs:nodejs . .
# 移除不必要的文件
RUN rm -rf .git .gitignore README.md docs/ tests/
# 设置环境变量
ENV NODE_ENV=production
ENV PORT=3000
# 暴露端口
EXPOSE 3000
# 切换到非root用户
USER nextjs
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 启动应用
CMD ["npm", "start"]
Docker安全扫描
#!/bin/bash
# docker-security-scan.sh
set -e
IMAGE_NAME="$1"
IMAGE_TAG="$2"
FULL_IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
echo "🔍 Starting security scan for ${FULL_IMAGE}"
# Trivy漏洞扫描
echo "📊 Running Trivy vulnerability scan..."
trivy image --exit-code 1 --severity HIGH,CRITICAL --format json --output trivy-report.json "${FULL_IMAGE}"
# Docker Bench安全检查
echo "🔒 Running Docker Bench security check..."
docker run --rm --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /etc:/etc:ro \
-v /usr/bin/containerd:/usr/bin/containerd:ro \
-v /usr/bin/runc:/usr/bin/runc:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--label docker_bench_security \
docker/docker-bench-security > docker-bench-report.txt
# Snyk容器扫描
echo "🛡️ Running Snyk container scan..."
snyk container test "${FULL_IMAGE}" --json > snyk-report.json
# Clair扫描
echo "🔍 Running Clair scan..."
clair-scanner --ip $(hostname -I | awk '{print $1}') --report clair-report.json "${FULL_IMAGE}"
# 生成综合报告
echo "📋 Generating security report..."
cat > security-report.md << EOF
# Security Scan Report for ${FULL_IMAGE}
## Scan Date
$(date)
## Trivy Vulnerabilities
\`\`\`json
$(cat trivy-report.json)
\`\`\`
## Docker Bench Results
\`\`\`
$(cat docker-bench-report.txt)
\`\`\`
## Snyk Results
\`\`\`json
$(cat snyk-report.json)
\`\`\`
## Clair Results
\`\`\`json
$(cat clair-report.json)
\`\`\`
EOF
echo "✅ Security scan completed. Report saved to security-report.md"
Kubernetes安全配置
Pod安全策略
# pod-security-policy.yaml
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted-psp
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
seccomp.security.alpha.kubernetes.io/defaultProfileName: 'runtime/default'
apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'
spec:
# 特权模式
privileged: false
# 允许特权升级
allowPrivilegeEscalation: false
# 必需的安全上下文
requiredDropCapabilities:
- ALL
# 允许的卷类型
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
# 主机网络
hostNetwork: false
hostIPC: false
hostPID: false
# 运行用户
runAsUser:
rule: 'MustRunAsNonRoot'
# 补充组
supplementalGroups:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
# FSGroup
fsGroup:
rule: 'RunAsAny'
# SELinux
seLinux:
rule: 'RunAsAny'
# 只读根文件系统
readOnlyRootFilesystem: true
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: restricted-psp-user
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames:
- restricted-psp
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: restricted-psp-all-serviceaccounts
roleRef:
kind: ClusterRole
name: restricted-psp-user
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: Group
name: system:serviceaccounts
apiGroup: rbac.authorization.k8s.io
网络策略
# network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backend-to-database
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 5432
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to: []
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
安全上下文配置
# secure-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
namespace: production
labels:
app: secure-app
spec:
replicas: 3
selector:
matchLabels:
app: secure-app
template:
metadata:
labels:
app: secure-app
annotations:
# 容器运行时安全配置
container.apparmor.security.beta.kubernetes.io/app: runtime/default
seccomp.security.alpha.kubernetes.io/pod: runtime/default
spec:
# 服务账户
serviceAccountName: secure-app-sa
# 安全上下文
securityContext:
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:latest
imagePullPolicy: Always
# 容器安全上下文
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
seccompProfile:
type: RuntimeDefault
# 资源限制
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
# 端口配置
ports:
- containerPort: 3000
name: http
protocol: TCP
# 健康检查
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: http
initialDelaySeconds: 5
periodSeconds: 5
# 环境变量
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
# 卷挂载
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
- name: config
mountPath: /app/config
readOnly: true
# 卷定义
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
- name: config
configMap:
name: app-config
# 节点选择器
nodeSelector:
security.kubernetes.io/trusted: "true"
# 容忍度
tolerations:
- key: "security"
operator: "Equal"
value: "high"
effect: "NoSchedule"
密钥管理
HashiCorp Vault集成
Vault配置
# vault.hcl
storage "consul" {
address = "127.0.0.1:8500"
path = "vault/"
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/etc/vault/tls/vault.crt"
tls_key_file = "/etc/vault/tls/vault.key"
}
api_addr = "https://vault.example.com:8200"
cluster_addr = "https://vault.example.com:8201"
ui = true
# 启用审计日志
audit {
file {
file_path = "/vault/logs/audit.log"
}
}
# 密封配置
seal "awskms" {
region = "us-west-2"
kms_key_id = "alias/vault-unseal-key"
}
# 插件目录
plugin_directory = "/etc/vault/plugins"
# 日志级别
log_level = "INFO"
# 禁用mlock
disable_mlock = true
Kubernetes Vault集成
# vault-auth.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-auth
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: role-tokenreview-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault-auth
namespace: default
---
apiVersion: v1
kind: Secret
metadata:
name: vault-auth-secret
annotations:
kubernetes.io/service-account.name: vault-auth
type: kubernetes.io/service-account-token
Vault初始化脚本
#!/bin/bash
# vault-init.sh
set -e
VAULT_ADDR="https://vault.example.com:8200"
VAULT_TOKEN="$1"
if [ -z "$VAULT_TOKEN" ]; then
echo "Usage: $0 <vault-token>"
exit 1
fi
export VAULT_ADDR
export VAULT_TOKEN
echo "🔐 Initializing Vault configuration..."
# 启用Kubernetes认证
vault auth enable kubernetes
# 配置Kubernetes认证
vault write auth/kubernetes/config \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
# 创建策略
vault policy write myapp-policy - <<EOF
path "secret/data/myapp/*" {
capabilities = ["read"]
}
path "database/creds/myapp-role" {
capabilities = ["read"]
}
path "pki/issue/myapp-role" {
capabilities = ["create", "update"]
}
EOF
# 创建角色
vault write auth/kubernetes/role/myapp \
bound_service_account_names=myapp \
bound_service_account_namespaces=production \
policies=myapp-policy \
ttl=24h
# 启用KV secrets引擎
vault secrets enable -path=secret kv-v2
# 启用数据库secrets引擎
vault secrets enable database
# 配置数据库连接
vault write database/config/postgresql \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@postgres:5432/myapp?sslmode=require" \
allowed_roles="myapp-role" \
username="vault" \
password="vault-password"
# 创建数据库角色
vault write database/roles/myapp-role \
db_name=postgresql \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";"\
default_ttl="1h" \
max_ttl="24h"
# 启用PKI引擎
vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki
# 生成根CA
vault write -field=certificate pki/root/generate/internal \
common_name="myapp.com" \
ttl=87600h > CA_cert.crt
# 配置CA和CRL URLs
vault write pki/config/urls \
issuing_certificates="$VAULT_ADDR/v1/pki/ca" \
crl_distribution_points="$VAULT_ADDR/v1/pki/crl"
# 创建角色
vault write pki/roles/myapp-role \
allowed_domains="myapp.com" \
allow_subdomains=true \
max_ttl="720h"
echo "✅ Vault initialization completed"
应用集成Vault
// vault-client.js
const vault = require('node-vault');
const fs = require('fs');
class VaultClient {
constructor() {
this.client = vault({
apiVersion: 'v1',
endpoint: process.env.VAULT_ADDR || 'https://vault.example.com:8200',
token: process.env.VAULT_TOKEN,
});
this.authenticated = false;
this.secrets = new Map();
this.refreshInterval = null;
}
// Kubernetes认证
async authenticateWithKubernetes() {
try {
const jwt = fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token', 'utf8');
const response = await this.client.kubernetesLogin({
role: 'myapp',
jwt: jwt,
});
this.client.token = response.auth.client_token;
this.authenticated = true;
// 设置token刷新
this.scheduleTokenRefresh(response.auth.lease_duration);
console.log('✅ Vault authentication successful');
return true;
} catch (error) {
console.error('❌ Vault authentication failed:', error.message);
return false;
}
}
// 获取密钥
async getSecret(path) {
try {
if (!this.authenticated) {
await this.authenticateWithKubernetes();
}
// 检查缓存
if (this.secrets.has(path)) {
const cached = this.secrets.get(path);
if (Date.now() < cached.expiry) {
return cached.data;
}
}
const response = await this.client.read(path);
const data = response.data.data;
// 缓存密钥
this.secrets.set(path, {
data,
expiry: Date.now() + (30 * 60 * 1000), // 30分钟
});
return data;
} catch (error) {
console.error(`❌ Failed to get secret from ${path}:`, error.message);
throw error;
}
}
// 获取数据库凭证
async getDatabaseCredentials(role = 'myapp-role') {
try {
const response = await this.client.read(`database/creds/${role}`);
return {
username: response.data.username,
password: response.data.password,
lease_id: response.lease_id,
lease_duration: response.lease_duration,
};
} catch (error) {
console.error('❌ Failed to get database credentials:', error.message);
throw error;
}
}
// 获取TLS证书
async getTLSCertificate(commonName, altNames = []) {
try {
const response = await this.client.write('pki/issue/myapp-role', {
common_name: commonName,
alt_names: altNames.join(','),
ttl: '720h',
});
return {
certificate: response.data.certificate,
private_key: response.data.private_key,
ca_chain: response.data.ca_chain,
serial_number: response.data.serial_number,
};
} catch (error) {
console.error('❌ Failed to get TLS certificate:', error.message);
throw error;
}
}
// 刷新token
async refreshToken() {
try {
const response = await this.client.tokenRenewSelf();
console.log('🔄 Vault token refreshed');
this.scheduleTokenRefresh(response.auth.lease_duration);
} catch (error) {
console.error('❌ Failed to refresh token:', error.message);
// 重新认证
await this.authenticateWithKubernetes();
}
}
// 计划token刷新
scheduleTokenRefresh(leaseDuration) {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
}
// 在lease到期前5分钟刷新
const refreshTime = (leaseDuration - 300) * 1000;
this.refreshInterval = setTimeout(() => {
this.refreshToken();
}, refreshTime);
}
// 清理资源
cleanup() {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
}
this.secrets.clear();
}
}
// 单例模式
const vaultClient = new VaultClient();
// 优雅关闭
process.on('SIGTERM', () => {
vaultClient.cleanup();
});
module.exports = vaultClient;
网络安全
WAF配置
AWS WAF配置
{
"Name": "MyAppWebACL",
"Scope": "CLOUDFRONT",
"DefaultAction": {
"Allow": {}
},
"Rules": [
{
"Name": "AWSManagedRulesCommonRuleSet",
"Priority": 1,
"OverrideAction": {
"None": {}
},
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet",
"ExcludedRules": []
}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "CommonRuleSetMetric"
}
},
{
"Name": "AWSManagedRulesKnownBadInputsRuleSet",
"Priority": 2,
"OverrideAction": {
"None": {}
},
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesKnownBadInputsRuleSet"
}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "KnownBadInputsMetric"
}
},
{
"Name": "RateLimitRule",
"Priority": 3,
"Action": {
"Block": {}
},
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"AggregateKeyType": "IP"
}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "RateLimitMetric"
}
},
{
"Name": "GeoBlockRule",
"Priority": 4,
"Action": {
"Block": {}
},
"Statement": {
"GeoMatchStatement": {
"CountryCodes": ["CN", "RU", "KP"]
}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "GeoBlockMetric"
}
},
{
"Name": "SQLInjectionRule",
"Priority": 5,
"Action": {
"Block": {}
},
"Statement": {
"SqliMatchStatement": {
"FieldToMatch": {
"AllQueryArguments": {}
},
"TextTransformations": [
{
"Priority": 0,
"Type": "URL_DECODE"
},
{
"Priority": 1,
"Type": "HTML_ENTITY_DECODE"
}
]
}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "SQLInjectionMetric"
}
}
],
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "MyAppWebACLMetric"
}
}
Nginx ModSecurity配置
# nginx.conf
load_module modules/ngx_http_modsecurity_module.so;
http {
# ModSecurity配置
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;
# 日志格式
log_format security '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time '
'$modsec_transaction_id';
# 限制请求大小
client_max_body_size 10M;
client_body_buffer_size 128k;
# 限制连接数
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
server {
listen 443 ssl http2;
server_name myapp.com;
# SSL配置
ssl_certificate /etc/ssl/certs/myapp.crt;
ssl_certificate_key /etc/ssl/private/myapp.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 安全头
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';" always;
# 限制
limit_conn conn_limit_per_ip 10;
limit_req zone=req_limit_per_ip burst=10 nodelay;
# 访问日志
access_log /var/log/nginx/access.log security;
error_log /var/log/nginx/error.log warn;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时设置
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
}
# 健康检查
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# 隐藏服务器信息
server_tokens off;
}
}
TLS/SSL配置
证书自动化管理
# cert-manager.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@myapp.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
- dns01:
cloudflare:
email: admin@myapp.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-tls
namespace: production
spec:
secretName: myapp-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- myapp.com
- www.myapp.com
- api.myapp.com
- admin.myapp.com
身份认证与授权
OAuth 2.0 / OpenID Connect
Keycloak配置
{
"realm": "myapp",
"enabled": true,
"sslRequired": "external",
"registrationAllowed": false,
"loginWithEmailAllowed": true,
"duplicateEmailsAllowed": false,
"resetPasswordAllowed": true,
"editUsernameAllowed": false,
"bruteForceProtected": true,
"permanentLockout": false,
"maxFailureWaitSeconds": 900,
"minimumQuickLoginWaitSeconds": 60,
"waitIncrementSeconds": 60,
"quickLoginCheckMilliSeconds": 1000,
"maxDeltaTimeSeconds": 43200,
"failureFactor": 30,
"defaultRoles": ["offline_access", "uma_authorization"],
"requiredCredentials": ["password"],
"passwordPolicy": "length(8) and digits(1) and lowerCase(1) and upperCase(1) and specialChars(1) and notUsername",
"otpPolicyType": "totp",
"otpPolicyAlgorithm": "HmacSHA1",
"otpPolicyInitialCounter": 0,
"otpPolicyDigits": 6,
"otpPolicyLookAheadWindow": 1,
"otpPolicyPeriod": 30,
"clients": [
{
"clientId": "myapp-frontend",
"enabled": true,
"clientAuthenticatorType": "client-secret",
"secret": "your-client-secret",
"redirectUris": [
"https://myapp.com/*",
"http://localhost:3000/*"
],
"webOrigins": [
"https://myapp.com",
"http://localhost:3000"
],
"protocol": "openid-connect",
"publicClient": false,
"bearerOnly": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": true,
"serviceAccountsEnabled": false,
"authorizationServicesEnabled": false,
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"defaultClientScopes": [
"web-origins",
"role_list",
"profile",
"roles",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"offline_access",
"microprofile-jwt"
]
}
],
"roles": {
"realm": [
{
"name": "admin",
"description": "Administrator role"
},
{
"name": "user",
"description": "Regular user role"
},
{
"name": "moderator",
"description": "Moderator role"
}
]
},
"groups": [
{
"name": "administrators",
"path": "/administrators",
"realmRoles": ["admin"]
},
{
"name": "users",
"path": "/users",
"realmRoles": ["user"]
}
],
"identityProviders": [
{
"alias": "google",
"providerId": "google",
"enabled": true,
"config": {
"clientId": "your-google-client-id",
"clientSecret": "your-google-client-secret",
"syncMode": "IMPORT"
}
},
{
"alias": "github",
"providerId": "github",
"enabled": true,
"config": {
"clientId": "your-github-client-id",
"clientSecret": "your-github-client-secret",
"syncMode": "IMPORT"
}
}
]
}
JWT验证中间件
// auth-middleware.js
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const { promisify } = require('util');
class AuthMiddleware {
constructor(options = {}) {
this.issuer = options.issuer || process.env.OIDC_ISSUER;
this.audience = options.audience || process.env.OIDC_AUDIENCE;
this.jwksUri = options.jwksUri || `${this.issuer}/protocol/openid-connect/certs`;
this.client = jwksClient({
jwksUri: this.jwksUri,
requestHeaders: {},
timeout: 30000,
cache: true,
cacheMaxEntries: 5,
cacheMaxAge: 600000, // 10分钟
});
this.getSigningKey = promisify(this.client.getSigningKey);
}
// 获取公钥
async getKey(header) {
try {
const key = await this.getSigningKey(header.kid);
return key.getPublicKey();
} catch (error) {
throw new Error(`Unable to find a signing key that matches '${header.kid}'`);
}
}
// 验证JWT
async verifyToken(token) {
try {
const decoded = jwt.decode(token, { complete: true });
if (!decoded || !decoded.header || !decoded.header.kid) {
throw new Error('Invalid token format');
}
const key = await this.getKey(decoded.header);
const payload = jwt.verify(token, key, {
audience: this.audience,
issuer: this.issuer,
algorithms: ['RS256'],
});
return payload;
} catch (error) {
throw new Error(`Token verification failed: ${error.message}`);
}
}
// Express中间件
middleware() {
return async (req, res, next) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({
error: 'Unauthorized',
message: 'Missing or invalid authorization header',
});
}
const token = authHeader.substring(7);
const payload = await this.verifyToken(token);
// 添加用户信息到请求对象
req.user = {
id: payload.sub,
email: payload.email,
name: payload.name,
roles: payload.realm_access?.roles || [],
groups: payload.groups || [],
permissions: payload.resource_access?.[this.audience]?.roles || [],
};
next();
} catch (error) {
console.error('Authentication error:', error.message);
return res.status(401).json({
error: 'Unauthorized',
message: 'Invalid or expired token',
});
}
};
}
// 角色检查中间件
requireRole(requiredRoles) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
error: 'Unauthorized',
message: 'Authentication required',
});
}
const userRoles = req.user.roles || [];
const hasRequiredRole = requiredRoles.some(role => userRoles.includes(role));
if (!hasRequiredRole) {
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient permissions',
});
}
next();
};
}
// 权限检查中间件
requirePermission(requiredPermissions) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({
error: 'Unauthorized',
message: 'Authentication required',
});
}
const userPermissions = req.user.permissions || [];
const hasRequiredPermission = requiredPermissions.some(permission =>
userPermissions.includes(permission)
);
if (!hasRequiredPermission) {
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient permissions',
});
}
next();
};
}
}
module.exports = AuthMiddleware;
安全监控与响应
SIEM集成
ELK Stack安全配置
# security-pipeline.yml
input {
beats {
port => 5044
}
syslog {
port => 5514
type => "syslog"
}
http {
port => 8080
type => "security_events"
}
}
filter {
# 安全事件解析
if [type] == "security_events" {
json {
source => "message"
}
# 威胁情报匹配
translate {
field => "[src_ip]"
destination => "[threat_intel]"
dictionary_path => "/etc/logstash/threat_intel.yml"
fallback => "clean"
}
# GeoIP解析
geoip {
source => "src_ip"
target => "geoip"
}
# 异常检测
if [event_type] == "failed_login" {
aggregate {
task_id => "%{src_ip}"
code => "
map['failed_attempts'] ||= 0
map['failed_attempts'] += 1
event.set('failed_attempts', map['failed_attempts'])
"
push_map_as_event_on_timeout => true
timeout_task_id_field => "src_ip"
timeout => 300
timeout_tags => ['_aggregatetimeout']
}
}
}
# WAF日志解析
if [type] == "waf" {
grok {
match => {
"message" => "%{TIMESTAMP_ISO8601:timestamp} %{WORD:action} %{IP:client_ip} %{WORD:method} %{URIPATH:uri} %{NUMBER:status} %{NUMBER:bytes} %{QS:user_agent} %{QS:rule_id}"
}
}
if [action] == "BLOCK" {
mutate {
add_tag => ["security_alert", "waf_block"]
add_field => { "alert_severity" => "medium" }
}
}
}
# 系统日志安全事件
if [type] == "syslog" {
grok {
match => {
"message" => "%{SYSLOGTIMESTAMP:timestamp} %{IPORHOST:host} %{DATA:program}(?:\[%{POSINT:pid}\])?: %{GREEDYDATA:message}"
}
overwrite => [ "message" ]
}
# 检测可疑活动
if [program] == "sshd" and [message] =~ /Failed password/ {
mutate {
add_tag => ["security_alert", "ssh_brute_force"]
add_field => { "alert_severity" => "high" }
}
}
if [program] == "sudo" and [message] =~ /authentication failure/ {
mutate {
add_tag => ["security_alert", "sudo_failure"]
add_field => { "alert_severity" => "medium" }
}
}
}
# 添加时间戳
date {
match => [ "timestamp", "ISO8601", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ]
}
}
output {
# 输出到Elasticsearch
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "security-logs-%{+YYYY.MM.dd}"
template_name => "security-logs"
template => "/usr/share/logstash/templates/security-logs.json"
}
# 安全告警
if "security_alert" in [tags] {
http {
url => "https://webhook.site/your-webhook-url"
http_method => "post"
format => "json"
mapping => {
"alert_type" => "%{[tags][1]}"
"severity" => "%{alert_severity}"
"timestamp" => "%{@timestamp}"
"source_ip" => "%{src_ip}"
"message" => "%{message}"
}
}
# 发送到Slack
slack {
url => "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
channel => "#security-alerts"
username => "Security Bot"
icon_emoji => ":warning:"
format => "🚨 Security Alert: %{[tags][1]} from %{src_ip} - %{message}"
}
}
}
入侵检测系统
Suricata配置
# suricata.yaml
vars:
address-groups:
HOME_NET: "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]"
EXTERNAL_NET: "!$HOME_NET"
HTTP_SERVERS: "$HOME_NET"
SMTP_SERVERS: "$HOME_NET"
SQL_SERVERS: "$HOME_NET"
DNS_SERVERS: "$HOME_NET"
TELNET_SERVERS: "$HOME_NET"
AIM_SERVERS: "$EXTERNAL_NET"
DC_SERVERS: "$HOME_NET"
DNP3_SERVER: "$HOME_NET"
DNP3_CLIENT: "$HOME_NET"
MODBUS_CLIENT: "$HOME_NET"
MODBUS_SERVER: "$HOME_NET"
ENIP_CLIENT: "$HOME_NET"
ENIP_SERVER: "$HOME_NET"
port-groups:
HTTP_PORTS: "80"
SHELLCODE_PORTS: "!80"
ORACLE_PORTS: 1521
SSH_PORTS: 22
DNP3_PORTS: 20000
MODBUS_PORTS: 502
FILE_DATA_PORTS: "[$HTTP_PORTS,110,143]"
FTP_PORTS: 21
GENEVE_PORTS: 6081
VXLAN_PORTS: 4789
TEREDO_PORTS: 3544
default-log-dir: /var/log/suricata/
stats:
enabled: yes
interval: 8
outputs:
- fast:
enabled: yes
filename: fast.log
append: yes
- eve-log:
enabled: yes
filetype: regular
filename: eve.json
types:
- alert:
payload: yes
payload-buffer-size: 4kb
payload-printable: yes
packet: yes
metadata: no
http-body: yes
http-body-printable: yes
tagged-packets: yes
- http:
extended: yes
- dns:
query: yes
answer: yes
- tls:
extended: yes
- files:
force-magic: no
- smtp:
- ssh
- stats:
totals: yes
threads: no
deltas: no
- flow
app-layer:
protocols:
http:
enabled: yes
libhtp:
default-config:
personality: IDS
request-body-limit: 100kb
response-body-limit: 100kb
request-body-minimal-inspect-size: 32kb
request-body-inspect-window: 4kb
response-body-minimal-inspect-size: 40kb
response-body-inspect-window: 16kb
response-body-decompress-layer-limit: 2
http-body-inline: auto
swf-decompression:
enabled: yes
type: both
compress-depth: 100kb
decompress-depth: 100kb
double-decode-path: no
double-decode-query: no
tls:
enabled: yes
detection-ports:
dp: 443
ftp:
enabled: yes
ssh:
enabled: yes
smtp:
enabled: yes
raw-extraction: no
mime:
decode-mime: yes
decode-base64: yes
decode-quoted-printable: yes
header-value-depth: 2000
extract-urls: yes
body-md5: no
inspected-tracker:
content-limit: 100000
content-inspect-min-size: 32768
content-inspect-window: 4096
dns:
tcp:
enabled: yes
detection-ports:
dp: 53
udp:
enabled: yes
detection-ports:
dp: 53
rule-files:
- suricata.rules
- /etc/suricata/rules/emerging-threats.rules
- /etc/suricata/rules/custom.rules
classification-file: /etc/suricata/classification.config
reference-config-file: /etc/suricata/reference.config
host-mode: auto
unix-command:
enabled: auto
legacy:
uricontent: enabled
engine-analysis:
rules-fast-pattern: yes
rules: yes
pcre:
match-limit: 3500
match-limit-recursion: 1500
host-os-policy:
windows: [0.0.0.0/0]
bsd: []
bsd-right: []
old-linux: []
linux: [10.0.0.0/8, 192.168.1.0/24, "!192.168.1.3"]
old-solaris: []
solaris: ["::1"]
hpux10: []
hpux11: []
irix: []
macos: []
vista: []
windows2k3: []
defrag:
memcap: 32mb
hash-size: 65536
trackers: 65535
max-frags: 65535
prealloc: yes
timeout: 60
flow:
memcap: 128mb
hash-size: 65536
prealloc: 10000
emergency-recovery: 30
managers: 1
recyclers: 1
flow-timeouts:
default:
new: 30
established: 300
closed: 0
bypassed: 100
emergency-new: 10
emergency-established: 100
emergency-closed: 0
emergency-bypassed: 50
tcp:
new: 60
established: 600
closed: 60
bypassed: 100
emergency-new: 5
emergency-established: 300
emergency-closed: 10
emergency-bypassed: 50
udp:
new: 30
established: 300
bypassed: 100
emergency-new: 10
emergency-established: 100
emergency-bypassed: 50
icmp:
new: 30
established: 300
bypassed: 100
emergency-new: 10
emergency-established: 100
emergency-bypassed: 50
stream:
memcap: 64mb
checksum-validation: yes
inline: auto
reassembly:
memcap: 256mb
depth: 1mb
toserver-chunk-size: 2560
toclient-chunk-size: 2560
randomize-chunk-size: yes
logging:
default-log-level: notice
default-output-filter:
outputs:
- console:
enabled: yes
- file:
enabled: yes
level: info
filename: /var/log/suricata/suricata.log
- syslog:
enabled: no
facility: local5
format: "[%i] <%d> -- "
自定义检测规则
# /etc/suricata/rules/custom.rules
# Web应用攻击检测
alert http any any -> $HTTP_SERVERS $HTTP_PORTS (msg:"SQL Injection Attempt"; flow:established,to_server; content:"union"; nocase; content:"select"; nocase; distance:0; within:100; classtype:web-application-attack; sid:1000001; rev:1;)
alert http any any -> $HTTP_SERVERS $HTTP_PORTS (msg:"XSS Attempt"; flow:established,to_server; content:"<script"; nocase; http_uri; classtype:web-application-attack; sid:1000002; rev:1;)
alert http any any -> $HTTP_SERVERS $HTTP_PORTS (msg:"Command Injection Attempt"; flow:established,to_server; pcre:"/[;&|`]\s*(cat|ls|pwd|id|whoami|uname)/i"; classtype:web-application-attack; sid:1000003; rev:1;)
# 暴力破解检测
alert tcp any any -> $HOME_NET 22 (msg:"SSH Brute Force Attempt"; flow:established,to_server; content:"SSH-"; offset:0; depth:4; detection_filter:track by_src, count 5, seconds 60; classtype:attempted-dos; sid:1000004; rev:1;)
alert tcp any any -> $HOME_NET 3389 (msg:"RDP Brute Force Attempt"; flow:established,to_server; dsize:>100; detection_filter:track by_src, count 10, seconds 60; classtype:attempted-dos; sid:1000005; rev:1;)
# 恶意软件通信
alert dns any any -> any any (msg:"Malware DNS Query"; dns_query; content:".tk"; endswith; classtype:trojan-activity; sid:1000006; rev:1;)
alert tls any any -> any any (msg:"Suspicious TLS Certificate"; tls_cert_subject; content:"CN=localhost"; classtype:policy-violation; sid:1000007; rev:1;)
# 数据泄露检测
alert http $HOME_NET any -> any any (msg:"Potential Data Exfiltration"; flow:established,to_server; file_data; content:"password"; nocase; content:"username"; nocase; distance:0; within:1000; classtype:policy-violation; sid:1000008; rev:1;)
# 异常网络行为
alert tcp any any -> $HOME_NET any (msg:"Port Scan Detected"; flags:S,12; detection_filter:track by_src, count 10, seconds 5; classtype:attempted-recon; sid:1000009; rev:1;)
alert icmp any any -> $HOME_NET any (msg:"ICMP Flood Detected"; detection_filter:track by_src, count 100, seconds 1; classtype:attempted-dos; sid:1000010; rev:1;)
安全事件响应
事件响应流程
自动化响应脚本
#!/bin/bash
# security-incident-response.sh
set -e
# 配置
LOG_FILE="/var/log/security/incident-response.log"
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
EMAIL_RECIPIENTS="security@company.com"
QUARANTINE_DIR="/var/quarantine"
# 日志函数
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# 发送通知
send_notification() {
local severity="$1"
local message="$2"
# Slack通知
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"🚨 Security Incident [$severity]: $message\"}" \
"$SLACK_WEBHOOK"
# 邮件通知
echo "Security Incident [$severity]: $message" | \
mail -s "Security Alert" "$EMAIL_RECIPIENTS"
}
# 隔离IP地址
quarantine_ip() {
local ip="$1"
local reason="$2"
log "Quarantining IP: $ip (Reason: $reason)"
# 添加iptables规则
iptables -I INPUT -s "$ip" -j DROP
iptables -I OUTPUT -d "$ip" -j DROP
# 记录到隔离列表
echo "$(date '+%Y-%m-%d %H:%M:%S') $ip $reason" >> /var/log/security/quarantined-ips.log
send_notification "HIGH" "IP $ip has been quarantined: $reason"
}
# 隔离用户账户
quarantine_user() {
local username="$1"
local reason="$2"
log "Quarantining user: $username (Reason: $reason)"
# 禁用用户账户
usermod -L "$username"
# 终止用户会话
pkill -u "$username" || true
# 记录到隔离列表
echo "$(date '+%Y-%m-%d %H:%M:%S') $username $reason" >> /var/log/security/quarantined-users.log
send_notification "HIGH" "User $username has been quarantined: $reason"
}
# 隔离文件
quarantine_file() {
local filepath="$1"
local reason="$2"
log "Quarantining file: $filepath (Reason: $reason)"
# 创建隔离目录
mkdir -p "$QUARANTINE_DIR"
# 移动文件到隔离区
local filename=$(basename "$filepath")
local quarantine_path="$QUARANTINE_DIR/${filename}_$(date +%s)"
mv "$filepath" "$quarantine_path"
chmod 000 "$quarantine_path"
# 记录到隔离列表
echo "$(date '+%Y-%m-%d %H:%M:%S') $filepath $quarantine_path $reason" >> /var/log/security/quarantined-files.log
send_notification "MEDIUM" "File $filepath has been quarantined: $reason"
}
# 收集系统信息
collect_evidence() {
local incident_id="$1"
local evidence_dir="/var/evidence/$incident_id"
log "Collecting evidence for incident: $incident_id"
mkdir -p "$evidence_dir"
# 系统信息
uname -a > "$evidence_dir/system_info.txt"
ps aux > "$evidence_dir/processes.txt"
netstat -tulpn > "$evidence_dir/network_connections.txt"
ss -tulpn > "$evidence_dir/socket_stats.txt"
# 用户信息
who > "$evidence_dir/logged_users.txt"
last -n 50 > "$evidence_dir/login_history.txt"
# 网络信息
iptables -L -n > "$evidence_dir/firewall_rules.txt"
route -n > "$evidence_dir/routing_table.txt"
# 日志文件
cp /var/log/auth.log "$evidence_dir/" 2>/dev/null || true
cp /var/log/syslog "$evidence_dir/" 2>/dev/null || true
cp /var/log/secure "$evidence_dir/" 2>/dev/null || true
# 创建证据包
tar -czf "$evidence_dir.tar.gz" -C "$(dirname $evidence_dir)" "$(basename $evidence_dir)"
log "Evidence collected: $evidence_dir.tar.gz"
}
# 主处理函数
handle_incident() {
local incident_type="$1"
local details="$2"
local severity="$3"
local incident_id="incident_$(date +%s)"
log "Handling security incident: $incident_type (ID: $incident_id)"
case "$incident_type" in
"malware_detected")
local filepath=$(echo "$details" | jq -r '.filepath')
quarantine_file "$filepath" "Malware detected"
collect_evidence "$incident_id"
;;
"brute_force_attack")
local source_ip=$(echo "$details" | jq -r '.source_ip')
quarantine_ip "$source_ip" "Brute force attack detected"
;;
"suspicious_user_activity")
local username=$(echo "$details" | jq -r '.username')
quarantine_user "$username" "Suspicious activity detected"
collect_evidence "$incident_id"
;;
"data_exfiltration")
local source_ip=$(echo "$details" | jq -r '.source_ip')
local username=$(echo "$details" | jq -r '.username')
quarantine_ip "$source_ip" "Data exfiltration attempt"
quarantine_user "$username" "Data exfiltration attempt"
collect_evidence "$incident_id"
;;
*)
log "Unknown incident type: $incident_type"
send_notification "MEDIUM" "Unknown security incident: $incident_type"
;;
esac
log "Incident handling completed: $incident_id"
}
# 命令行参数处理
if [ $# -lt 3 ]; then
echo "Usage: $0 <incident_type> <details_json> <severity>"
echo "Example: $0 malware_detected '{\"filepath\":\"/tmp/malware.exe\"}' HIGH"
exit 1
fi
handle_incident "$1" "$2" "$3"
合规性管理
GDPR合规
数据保护配置
// gdpr-compliance.js
const crypto = require('crypto');
const bcrypt = require('bcrypt');
class GDPRCompliance {
constructor() {
this.encryptionKey = process.env.ENCRYPTION_KEY;
this.algorithm = 'aes-256-gcm';
}
// 数据加密
encryptPersonalData(data) {
try {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipher(this.algorithm, this.encryptionKey);
cipher.setAAD(Buffer.from('gdpr-protected', 'utf8'));
let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
encrypted,
iv: iv.toString('hex'),
authTag: authTag.toString('hex'),
};
} catch (error) {
throw new Error(`Encryption failed: ${error.message}`);
}
}
// 数据解密
decryptPersonalData(encryptedData) {
try {
const { encrypted, iv, authTag } = encryptedData;
const decipher = crypto.createDecipher(this.algorithm, this.encryptionKey);
decipher.setAAD(Buffer.from('gdpr-protected', 'utf8'));
decipher.setAuthTag(Buffer.from(authTag, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return JSON.parse(decrypted);
} catch (error) {
throw new Error(`Decryption failed: ${error.message}`);
}
}
// 数据匿名化
anonymizeData(data, fields) {
const anonymized = { ...data };
fields.forEach(field => {
if (anonymized[field]) {
// 使用一致性哈希进行匿名化
const hash = crypto.createHash('sha256')
.update(anonymized[field] + process.env.ANONYMIZATION_SALT)
.digest('hex');
anonymized[field] = `anon_${hash.substring(0, 8)}`;
}
});
return anonymized;
}
// 数据脱敏
maskSensitiveData(data) {
const masked = { ...data };
// 邮箱脱敏
if (masked.email) {
const [local, domain] = masked.email.split('@');
masked.email = `${local.substring(0, 2)}***@${domain}`;
}
// 电话号码脱敏
if (masked.phone) {
masked.phone = masked.phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3');
}
// 身份证号脱敏
if (masked.idCard) {
masked.idCard = masked.idCard.replace(/(\d{6})(\d{8})(\d{4})/, '$1********$3');
}
return masked;
}
// 数据保留策略
async applyRetentionPolicy(userId, dataType) {
const retentionPolicies = {
'user_activity': 365, // 1年
'transaction_logs': 2555, // 7年
'marketing_data': 1095, // 3年
'support_tickets': 1825, // 5年
};
const retentionDays = retentionPolicies[dataType] || 365;
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
// 这里应该连接到实际的数据库
console.log(`Applying retention policy for ${dataType}: delete data older than ${cutoffDate}`);
}
// 同意管理
recordConsent(userId, consentType, granted) {
const consentRecord = {
userId,
consentType,
granted,
timestamp: new Date().toISOString(),
ipAddress: this.getClientIP(),
userAgent: this.getUserAgent(),
};
// 存储同意记录
console.log('Consent recorded:', consentRecord);
return consentRecord;
}
// 数据可携带性
async exportUserData(userId) {
// 收集用户所有数据
const userData = {
profile: await this.getUserProfile(userId),
activities: await this.getUserActivities(userId),
transactions: await this.getUserTransactions(userId),
preferences: await this.getUserPreferences(userId),
};
// 生成导出文件
const exportData = {
exportDate: new Date().toISOString(),
userId,
data: userData,
};
return JSON.stringify(exportData, null, 2);
}
// 被遗忘权
async deleteUserData(userId, reason) {
const deletionLog = {
userId,
reason,
timestamp: new Date().toISOString(),
deletedData: [],
};
// 删除各种数据
const dataTypes = ['profile', 'activities', 'transactions', 'preferences'];
for (const dataType of dataTypes) {
try {
await this.deleteDataByType(userId, dataType);
deletionLog.deletedData.push(dataType);
} catch (error) {
console.error(`Failed to delete ${dataType}:`, error.message);
}
}
// 记录删除日志
console.log('User data deletion completed:', deletionLog);
return deletionLog;
}
// 辅助方法
getClientIP() {
// 实际实现中从请求中获取
return '127.0.0.1';
}
getUserAgent() {
// 实际实现中从请求中获取
return 'Mozilla/5.0...';
}
async getUserProfile(userId) {
// 实际实现中从数据库获取
return { id: userId, name: 'User Name' };
}
async getUserActivities(userId) {
// 实际实现中从数据库获取
return [];
}
async getUserTransactions(userId) {
// 实际实现中从数据库获取
return [];
}
async getUserPreferences(userId) {
// 实际实现中从数据库获取
return {};
}
async deleteDataByType(userId, dataType) {
// 实际实现中删除对应类型的数据
console.log(`Deleting ${dataType} for user ${userId}`);
}
}
module.exports = GDPRCompliance;
SOC 2合规
访问控制策略
# soc2-access-control.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: soc2-policies
namespace: compliance
data:
access-control-policy.json: |
{
"version": "2023-01-01",
"policies": {
"authentication": {
"mfa_required": true,
"password_policy": {
"min_length": 12,
"require_uppercase": true,
"require_lowercase": true,
"require_numbers": true,
"require_special_chars": true,
"max_age_days": 90,
"history_count": 12
},
"session_management": {
"max_session_duration": 480,
"idle_timeout": 30,
"concurrent_sessions": 3
}
},
"authorization": {
"rbac_enabled": true,
"principle_of_least_privilege": true,
"regular_access_review": {
"frequency_days": 90,
"automated_deprovisioning": true
}
},
"data_protection": {
"encryption_at_rest": true,
"encryption_in_transit": true,
"data_classification": {
"public": {
"encryption_required": false,
"access_logging": false
},
"internal": {
"encryption_required": true,
"access_logging": true
},
"confidential": {
"encryption_required": true,
"access_logging": true,
"approval_required": true
},
"restricted": {
"encryption_required": true,
"access_logging": true,
"approval_required": true,
"mfa_required": true
}
}
},
"monitoring": {
"access_logging": true,
"failed_login_monitoring": true,
"privilege_escalation_monitoring": true,
"data_access_monitoring": true,
"alert_thresholds": {
"failed_logins": 5,
"privilege_changes": 1,
"data_export": 1
}
}
}
}
audit-logging-config.json: |
{
"audit_events": [
"user_login",
"user_logout",
"failed_login",
"password_change",
"role_assignment",
"permission_change",
"data_access",
"data_modification",
"data_export",
"system_configuration_change",
"security_event"
],
"log_retention": {
"security_logs": 2555,
"access_logs": 365,
"audit_logs": 2555,
"system_logs": 90
},
"log_integrity": {
"digital_signatures": true,
"tamper_detection": true,
"backup_frequency": "daily"
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: audit-logger
namespace: compliance
spec:
replicas: 2
selector:
matchLabels:
app: audit-logger
template:
metadata:
labels:
app: audit-logger
spec:
serviceAccountName: audit-logger
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
containers:
- name: audit-logger
image: audit-logger:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
env:
- name: LOG_LEVEL
value: "INFO"
- name: ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: audit-secrets
key: encryption-key
volumeMounts:
- name: audit-logs
mountPath: /var/log/audit
- name: tmp
mountPath: /tmp
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
volumes:
- name: audit-logs
persistentVolumeClaim:
claimName: audit-logs-pvc
- name: tmp
emptyDir: {}
安全最佳实践总结
核心安全原则
-
纵深防御
- 多层安全控制
- 冗余保护机制
- 失效安全设计
-
最小权限原则
- 按需授权
- 定期权限审查
- 自动权限回收
-
零信任架构
- 持续验证
- 动态访问控制
- 微分段网络
-
安全左移
- 开发阶段安全集成
- 自动化安全测试
- 持续安全监控
实施建议
-
建立安全文化
- 安全意识培训
- 安全责任制
- 持续改进机制
-
自动化安全流程
- 自动化漏洞扫描
- 自动化合规检查
- 自动化事件响应
-
持续监控和改进
- 实时威胁检测
- 定期安全评估
- 威胁情报集成
-
合规性管理
- 法规要求跟踪
- 合规性自动化
- 审计准备
通过实施这些安全实践,可以构建一个全面、可靠的部署安全体系,有效保护应用和数据安全,满足各种合规要求。