学习目标

通过本章学习,您将能够:

  • 理解 Docker Swarm 安全架构和机制
  • 掌握集群安全配置和加固
  • 学会密钥和证书管理
  • 了解访问控制和权限管理
  • 掌握安全扫描和合规性检查

1. 安全架构概述

1.1 Swarm 安全模型

安全组件

# Docker Swarm 安全组件:

# 1. TLS 加密
# - 节点间通信加密
# - 客户端-服务器通信加密
# - 证书自动轮换

# 2. 相互认证
# - 节点身份验证
# - 客户端身份验证
# - 基于证书的认证

# 3. 授权机制
# - 基于角色的访问控制(RBAC)
# - 节点角色管理
# - 服务权限控制

# 4. 密钥管理
# - Docker Secrets
# - 密钥轮换
# - 加密存储

# 5. 网络安全
# - 网络隔离
# - 流量加密
# - 防火墙规则

安全架构图

# Swarm 安全架构:

┌─────────────────────────────────────────────────────────────┐
│                    Docker Swarm Cluster                    │
│                                                             │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐     │
│  │   Manager   │    │   Manager   │    │   Worker    │     │
│  │    Node     │◄──►│    Node     │◄──►│    Node     │     │
│  │             │    │             │    │             │     │
│  │ ┌─────────┐ │    │ ┌─────────┐ │    │ ┌─────────┐ │     │
│  │ │   TLS   │ │    │ │   TLS   │ │    │ │   TLS   │ │     │
│  │ │  Cert   │ │    │ │  Cert   │ │    │ │  Cert   │ │     │
│  │ └─────────┘ │    │ └─────────┘ │    │ └─────────┘ │     │
│  └─────────────┘    └─────────────┘    └─────────────┘     │
│           │                 │                 │            │
│           └─────────────────┼─────────────────┘            │
│                             │                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              Encrypted Communication               │   │
│  │                  (TLS 1.3)                        │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                 Secrets Store                      │   │
│  │              (Encrypted at Rest)                   │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │   External CA   │
                    │   (Optional)    │
                    └─────────────────┘

1.2 威胁模型分析

常见安全威胁

# Docker Swarm 面临的安全威胁:

# 1. 网络攻击
# - 中间人攻击
# - 网络窃听
# - DDoS 攻击
# - 端口扫描

# 2. 容器逃逸
# - 特权容器滥用
# - 内核漏洞利用
# - 资源限制绕过
# - 文件系统挂载攻击

# 3. 镜像安全
# - 恶意镜像
# - 漏洞镜像
# - 供应链攻击
# - 镜像篡改

# 4. 密钥泄露
# - 硬编码密钥
# - 环境变量泄露
# - 日志泄露
# - 内存转储

# 5. 权限提升
# - 容器特权滥用
# - 主机权限获取
# - 集群管理权限
# - 服务账户滥用

安全防护策略

# 多层防护策略:

# 1. 网络层防护
# - 防火墙配置
# - 网络分段
# - 流量加密
# - 入侵检测

# 2. 主机层防护
# - 操作系统加固
# - 访问控制
# - 审计日志
# - 安全更新

# 3. 容器层防护
# - 镜像扫描
# - 运行时保护
# - 资源限制
# - 安全配置

# 4. 应用层防护
# - 代码审计
# - 输入验证
# - 身份认证
# - 授权控制

# 5. 数据层防护
# - 数据加密
# - 访问控制
# - 备份保护
# - 合规性检查

2. 集群安全配置

2.1 TLS 配置

查看集群证书信息

# 查看集群 TLS 信息
docker info --format '{{.Swarm.Cluster.TLSInfo}}'

# 查看节点证书
docker node inspect self --format '{{.Description.TLSInfo}}'

# 查看证书有效期
docker swarm ca --cert-expiry 90d

# 查看 CA 证书
docker swarm ca --print

证书轮换

# 轮换集群证书
docker swarm ca --rotate

# 设置证书有效期
docker swarm ca --cert-expiry 30d

# 强制轮换节点证书
docker swarm update-ca --rotate

# 查看轮换状态
docker node ls --format "table {{.Hostname}}\t{{.Status}}\t{{.TLSStatus}}"

外部 CA 集成

# 配置外部 CA
docker swarm ca \
  --external-ca protocol=cfssl,url=https://ca.example.com:8888 \
  --ca-cert ca.pem \
  --ca-key ca-key.pem

# 使用外部根 CA
docker swarm init \
  --external-ca protocol=cfssl,url=https://ca.example.com:8888,cacert=ca.pem

# 验证外部 CA 配置
docker info --format '{{.Swarm.Cluster.Spec.CAConfig}}'

2.2 节点安全

节点加入令牌管理

# 查看加入令牌
docker swarm join-token worker
docker swarm join-token manager

# 轮换加入令牌
docker swarm join-token --rotate worker
docker swarm join-token --rotate manager

# 查看令牌信息
docker swarm join-token --quiet worker
docker swarm join-token --quiet manager

节点标签和约束

# 添加安全标签
docker node update --label-add security.zone=dmz node1
docker node update --label-add security.level=high node2
docker node update --label-add compliance.pci=true node3

# 基于安全标签部署服务
docker service create --name secure-app \
  --constraint 'node.labels.security.level == high' \
  --constraint 'node.labels.compliance.pci == true' \
  my-secure-app:latest

# 查看节点标签
docker node inspect node1 --format '{{.Spec.Labels}}'

2.3 安全配置脚本

集群安全加固脚本

#!/bin/bash
# swarm-security-hardening.sh

# 安全配置参数
CERT_EXPIRY="30d"
LOG_LEVEL="info"
SECURITY_OPTS="no-new-privileges:true"
READ_ONLY_ROOT="true"

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1"
}

# 检查是否为管理节点
check_manager_node() {
    if ! docker node ls > /dev/null 2>&1; then
        log "ERROR: This script must be run on a manager node"
        exit 1
    fi
}

# 配置证书安全
configure_certificate_security() {
    log "Configuring certificate security..."
    
    # 设置证书有效期
    docker swarm ca --cert-expiry $CERT_EXPIRY
    
    # 轮换加入令牌
    log "Rotating join tokens..."
    docker swarm join-token --rotate worker > /dev/null
    docker swarm join-token --rotate manager > /dev/null
    
    # 检查证书状态
    log "Certificate status:"
    docker node ls --format "table {{.Hostname}}\t{{.Status}}\t{{.TLSStatus}}"
}

# 配置节点安全标签
configure_node_security_labels() {
    log "Configuring node security labels..."
    
    # 为每个节点添加安全标签
    for node in $(docker node ls --format '{{.Hostname}}'); do
        # 检查节点角色
        role=$(docker node inspect $node --format '{{.Spec.Role}}')
        
        # 添加基础安全标签
        docker node update --label-add security.role=$role $node
        docker node update --label-add security.hardened=true $node
        docker node update --label-add security.scan.date=$(date +%Y%m%d) $node
        
        log "Security labels added to node: $node (role: $role)"
    done
}

# 配置服务安全默认值
configure_service_security_defaults() {
    log "Configuring service security defaults..."
    
    # 创建安全配置文件
    cat > /tmp/secure-service-template.yml << 'EOF'
version: '3.8'
services:
  secure-template:
    image: placeholder
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.security.hardened == true
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.1'
          memory: 128M
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp:size=100M,noexec,nosuid,nodev
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - SETGID
      - SETUID
    user: "1000:1000"
EOF
    
    log "Secure service template created at /tmp/secure-service-template.yml"
}

# 配置网络安全
configure_network_security() {
    log "Configuring network security..."
    
    # 创建加密网络
    docker network create \
        --driver overlay \
        --opt encrypted \
        --label security.encrypted=true \
        secure-network
    
    # 创建隔离网络
    docker network create \
        --driver overlay \
        --opt encrypted \
        --subnet 10.99.0.0/24 \
        --label security.isolated=true \
        isolated-network
    
    log "Secure networks created"
}

# 配置密钥管理
configure_secrets_management() {
    log "Configuring secrets management..."
    
    # 创建示例密钥
    echo "example-secret-$(date +%s)" | docker secret create example-secret -
    
    # 创建 TLS 证书密钥
    openssl req -new -x509 -days 365 -nodes \
        -out /tmp/server.crt \
        -keyout /tmp/server.key \
        -subj "/C=US/ST=CA/L=SF/O=Example/CN=example.com"
    
    docker secret create server.crt /tmp/server.crt
    docker secret create server.key /tmp/server.key
    
    # 清理临时文件
    rm -f /tmp/server.crt /tmp/server.key
    
    log "Secrets configured"
}

# 配置审计日志
configure_audit_logging() {
    log "Configuring audit logging..."
    
    # 创建审计日志配置
    cat > /etc/docker/daemon.json << 'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  },
  "log-level": "info",
  "userland-proxy": false,
  "experimental": false,
  "live-restore": true
}
EOF
    
    log "Audit logging configured (requires Docker restart)"
}

# 安全扫描
perform_security_scan() {
    log "Performing security scan..."
    
    # 检查不安全的配置
    echo "\n=== Security Scan Results ==="
    
    # 检查特权容器
    echo "\nChecking for privileged containers:"
    docker ps --filter "label=privileged=true" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" || echo "No privileged containers found"
    
    # 检查未加密网络
    echo "\nChecking for unencrypted networks:"
    for network in $(docker network ls --filter driver=overlay --format '{{.Name}}'); do
        encrypted=$(docker network inspect $network --format '{{.Options.encrypted}}')
        if [ "$encrypted" != "true" ]; then
            echo "  Unencrypted network: $network"
        fi
    done
    
    # 检查未使用的密钥
    echo "\nChecking for unused secrets:"
    for secret in $(docker secret ls --format '{{.Name}}'); do
        used=$(docker service ls --filter "secret=$secret" --format '{{.Name}}')
        if [ -z "$used" ]; then
            echo "  Unused secret: $secret"
        fi
    done
    
    # 检查节点安全状态
    echo "\nNode security status:"
    docker node ls --format "table {{.Hostname}}\t{{.Status}}\t{{.Availability}}\t{{.ManagerStatus}}"
}

# 生成安全报告
generate_security_report() {
    local report_file="/tmp/swarm-security-report-$(date +%Y%m%d_%H%M%S).txt"
    
    log "Generating security report: $report_file"
    
    {
        echo "Docker Swarm Security Report"
        echo "Generated: $(date)"
        echo "========================================"
        
        echo "\nCluster Information:"
        docker info --format 'Swarm: {{.Swarm.LocalNodeState}}'
        docker info --format 'Nodes: {{.Swarm.Nodes}}'
        docker info --format 'Managers: {{.Swarm.Managers}}'
        
        echo "\nTLS Configuration:"
        docker info --format 'TLS Info: {{.Swarm.Cluster.TLSInfo}}'
        
        echo "\nNode Status:"
        docker node ls
        
        echo "\nNetwork Security:"
        docker network ls --filter driver=overlay
        
        echo "\nSecrets:"
        docker secret ls
        
        echo "\nServices:"
        docker service ls
        
        echo "\nSecurity Scan:"
        perform_security_scan
        
    } > $report_file
    
    log "Security report generated: $report_file"
}

# 主函数
main() {
    log "Starting Docker Swarm security hardening..."
    
    check_manager_node
    
    case "${1:-all}" in
        "certificates")
            configure_certificate_security
            ;;
        "labels")
            configure_node_security_labels
            ;;
        "services")
            configure_service_security_defaults
            ;;
        "network")
            configure_network_security
            ;;
        "secrets")
            configure_secrets_management
            ;;
        "audit")
            configure_audit_logging
            ;;
        "scan")
            perform_security_scan
            ;;
        "report")
            generate_security_report
            ;;
        "all")
            configure_certificate_security
            configure_node_security_labels
            configure_service_security_defaults
            configure_network_security
            configure_secrets_management
            configure_audit_logging
            perform_security_scan
            generate_security_report
            ;;
        *)
            echo "Usage: $0 {certificates|labels|services|network|secrets|audit|scan|report|all}"
            exit 1
            ;;
    esac
    
    log "Security hardening completed"
}

# 执行主函数
main "$@"

3. 密钥和证书管理

3.1 Docker Secrets

基本密钥操作

# 创建密钥
echo "my-secret-password" | docker secret create db-password -

# 从文件创建密钥
docker secret create ssl-cert ./server.crt
docker secret create ssl-key ./server.key

# 查看密钥列表
docker secret ls

# 查看密钥详情
docker secret inspect db-password

# 删除密钥
docker secret rm db-password

服务中使用密钥

# 在服务中使用密钥
docker service create --name web \
  --secret ssl-cert \
  --secret ssl-key \
  --secret source=db-password,target=db_pass \
  nginx

# 指定密钥挂载路径和权限
docker service create --name app \
  --secret source=ssl-cert,target=/etc/ssl/cert.pem,mode=0400 \
  --secret source=ssl-key,target=/etc/ssl/key.pem,mode=0400 \
  my-app:latest

# 查看服务密钥
docker service inspect web --format '{{range .Spec.TaskTemplate.ContainerSpec.Secrets}}{{.SecretName}} -> {{.File.Name}}{{end}}'

密钥轮换

# 创建新版本密钥
echo "new-secret-password" | docker secret create db-password-v2 -

# 更新服务使用新密钥
docker service update \
  --secret-rm db-password \
  --secret-add source=db-password-v2,target=db_pass \
  web

# 删除旧密钥
docker secret rm db-password

# 重命名新密钥
docker secret create db-password-temp -
echo "final-password" | docker secret create db-password -
docker secret rm db-password-temp

3.2 密钥管理最佳实践

密钥管理脚本

#!/bin/bash
# secrets-manager.sh

SECRETS_DIR="/secure/secrets"
BACKUP_DIR="/secure/backups"
LOG_FILE="/var/log/secrets-manager.log"

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}

# 创建密钥
create_secret() {
    local secret_name=$1
    local secret_file=$2
    local description=$3
    
    if [ ! -f "$secret_file" ]; then
        log "ERROR: Secret file not found: $secret_file"
        return 1
    fi
    
    # 检查密钥是否已存在
    if docker secret inspect $secret_name > /dev/null 2>&1; then
        log "WARNING: Secret $secret_name already exists"
        return 1
    fi
    
    # 创建密钥
    docker secret create $secret_name $secret_file
    
    if [ $? -eq 0 ]; then
        # 添加标签
        docker secret update \
            --label-add description="$description" \
            --label-add created=$(date +%Y%m%d) \
            --label-add source-file=$(basename $secret_file) \
            $secret_name
        
        log "SUCCESS: Secret $secret_name created"
        
        # 备份密钥元数据
        docker secret inspect $secret_name > "$BACKUP_DIR/${secret_name}.json"
        
        return 0
    else
        log "ERROR: Failed to create secret $secret_name"
        return 1
    fi
}

# 轮换密钥
rotate_secret() {
    local secret_name=$1
    local new_secret_file=$2
    local services=$(docker service ls --filter "secret=$secret_name" --format '{{.Name}}')
    
    if [ -z "$services" ]; then
        log "WARNING: No services using secret $secret_name"
    fi
    
    log "Rotating secret: $secret_name"
    log "Affected services: $services"
    
    # 创建新版本密钥
    local new_secret_name="${secret_name}-new-$(date +%s)"
    docker secret create $new_secret_name $new_secret_file
    
    if [ $? -ne 0 ]; then
        log "ERROR: Failed to create new secret version"
        return 1
    fi
    
    # 更新使用该密钥的服务
    for service in $services; do
        log "Updating service: $service"
        
        # 获取当前密钥配置
        local secret_config=$(docker service inspect $service --format '{{range .Spec.TaskTemplate.ContainerSpec.Secrets}}{{if eq .SecretName "'$secret_name'"}}{{.File.Name}}:{{.File.Mode}}{{end}}{{end}}')
        
        if [ -n "$secret_config" ]; then
            local target_name=$(echo $secret_config | cut -d: -f1)
            local mode=$(echo $secret_config | cut -d: -f2)
            
            # 更新服务
            docker service update \
                --secret-rm $secret_name \
                --secret-add source=$new_secret_name,target=$target_name,mode=$mode \
                $service
            
            if [ $? -eq 0 ]; then
                log "SUCCESS: Service $service updated"
            else
                log "ERROR: Failed to update service $service"
            fi
        fi
    done
    
    # 删除旧密钥
    docker secret rm $secret_name
    
    # 重命名新密钥
    docker secret create $secret_name $new_secret_file
    docker secret rm $new_secret_name
    
    log "Secret rotation completed: $secret_name"
}

# 备份密钥
backup_secrets() {
    local backup_file="$BACKUP_DIR/secrets-backup-$(date +%Y%m%d_%H%M%S).tar.gz"
    
    log "Starting secrets backup..."
    
    mkdir -p $BACKUP_DIR
    
    # 导出所有密钥元数据
    for secret in $(docker secret ls --format '{{.Name}}'); do
        docker secret inspect $secret > "$BACKUP_DIR/${secret}.json"
    done
    
    # 创建备份归档
    tar czf $backup_file -C $BACKUP_DIR *.json
    
    # 清理临时文件
    rm -f $BACKUP_DIR/*.json
    
    log "Secrets backup completed: $backup_file"
}

# 密钥审计
audit_secrets() {
    log "Starting secrets audit..."
    
    echo "\n=== Secrets Audit Report ==="
    echo "Generated: $(date)"
    echo "\nSecret Inventory:"
    printf "%-20s %-15s %-20s %-30s\n" "NAME" "CREATED" "UPDATED" "SERVICES"
    printf "%-20s %-15s %-20s %-30s\n" "----" "-------" "-------" "--------"
    
    for secret in $(docker secret ls --format '{{.Name}}'); do
        local created=$(docker secret inspect $secret --format '{{.CreatedAt}}' | cut -d'T' -f1)
        local updated=$(docker secret inspect $secret --format '{{.UpdatedAt}}' | cut -d'T' -f1)
        local services=$(docker service ls --filter "secret=$secret" --format '{{.Name}}' | tr '\n' ',' | sed 's/,$//')
        
        printf "%-20s %-15s %-20s %-30s\n" "$secret" "$created" "$updated" "$services"
    done
    
    # 检查未使用的密钥
    echo "\nUnused Secrets:"
    for secret in $(docker secret ls --format '{{.Name}}'); do
        local services=$(docker service ls --filter "secret=$secret" --format '{{.Name}}')
        if [ -z "$services" ]; then
            echo "  - $secret"
        fi
    done
    
    # 检查过期密钥(基于标签)
    echo "\nExpiring Secrets (created > 90 days ago):"
    for secret in $(docker secret ls --format '{{.Name}}'); do
        local created_label=$(docker secret inspect $secret --format '{{.Spec.Labels.created}}')
        if [ -n "$created_label" ]; then
            local days_old=$(( ($(date +%s) - $(date -d $created_label +%s)) / 86400 ))
            if [ $days_old -gt 90 ]; then
                echo "  - $secret (${days_old} days old)"
            fi
        fi
    done
}

# 清理未使用的密钥
cleanup_unused_secrets() {
    log "Cleaning up unused secrets..."
    
    for secret in $(docker secret ls --format '{{.Name}}'); do
        local services=$(docker service ls --filter "secret=$secret" --format '{{.Name}}')
        if [ -z "$services" ]; then
            echo "Removing unused secret: $secret"
            docker secret rm $secret
            log "Removed unused secret: $secret"
        fi
    done
}

# 主菜单
case "$1" in
    "create")
        if [ -n "$2" ] && [ -n "$3" ]; then
            create_secret $2 $3 "$4"
        else
            echo "Usage: $0 create <secret-name> <secret-file> [description]"
        fi
        ;;
    "rotate")
        if [ -n "$2" ] && [ -n "$3" ]; then
            rotate_secret $2 $3
        else
            echo "Usage: $0 rotate <secret-name> <new-secret-file>"
        fi
        ;;
    "backup")
        backup_secrets
        ;;
    "audit")
        audit_secrets
        ;;
    "cleanup")
        cleanup_unused_secrets
        ;;
    *)
        echo "Usage: $0 {create|rotate|backup|audit|cleanup}"
        echo "  create <name> <file> [desc] - Create new secret"
        echo "  rotate <name> <file>         - Rotate existing secret"
        echo "  backup                       - Backup secrets metadata"
        echo "  audit                        - Audit secrets usage"
        echo "  cleanup                      - Remove unused secrets"
        ;;
esac

3.3 外部密钥管理集成

HashiCorp Vault 集成

# 部署 Vault 服务
docker service create --name vault \
  --publish 8200:8200 \
  --mount type=volume,source=vault-data,target=/vault/data \
  --env VAULT_DEV_ROOT_TOKEN_ID=myroot \
  --env VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200 \
  vault:latest

# Vault 密钥同步脚本
cat > vault-sync.sh << 'EOF'
#!/bin/bash

VAULT_ADDR="http://vault:8200"
VAULT_TOKEN="myroot"

# 从 Vault 获取密钥并创建 Docker Secret
sync_secret_from_vault() {
    local secret_path=$1
    local secret_name=$2
    
    # 从 Vault 获取密钥
    secret_value=$(curl -s \
        -H "X-Vault-Token: $VAULT_TOKEN" \
        $VAULT_ADDR/v1/secret/data/$secret_path | \
        jq -r '.data.data.value')
    
    if [ "$secret_value" != "null" ]; then
        # 创建或更新 Docker Secret
        echo "$secret_value" | docker secret create $secret_name - 2>/dev/null || \
        {
            # 如果密钥已存在,进行轮换
            echo "$secret_value" | docker secret create ${secret_name}-new -
            # 这里需要更新使用该密钥的服务
            docker secret rm $secret_name
            echo "$secret_value" | docker secret create $secret_name -
            docker secret rm ${secret_name}-new
        }
        
        echo "Secret $secret_name synced from Vault"
    else
        echo "Failed to retrieve secret from Vault: $secret_path"
    fi
}

# 同步多个密钥
sync_secret_from_vault "database/password" "db-password"
sync_secret_from_vault "ssl/certificate" "ssl-cert"
sync_secret_from_vault "ssl/private-key" "ssl-key"
EOF

chmod +x vault-sync.sh

4. 访问控制和权限管理

4.1 基于角色的访问控制

用户和角色管理

# Docker 不直接支持用户管理,但可以通过以下方式实现:

# 1. 使用客户端证书认证
# 生成客户端证书
openssl genrsa -out client-key.pem 4096
openssl req -new -key client-key.pem -out client.csr -subj "/CN=client"
openssl x509 -req -in client.csr -CA ca.pem -CAkey ca-key.pem -out client-cert.pem -days 365

# 2. 配置 Docker 客户端使用证书
export DOCKER_HOST=tcp://manager-node:2376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=/path/to/certs

# 3. 测试连接
docker --tlsverify --tlscacert=ca.pem --tlscert=client-cert.pem --tlskey=client-key.pem -H=manager-node:2376 version

服务权限控制

# 使用约束限制服务部署
docker service create --name restricted-app \
  --constraint 'node.role == worker' \
  --constraint 'node.labels.security.level == high' \
  --user 1000:1000 \
  --cap-drop ALL \
  --cap-add CHOWN \
  --cap-add SETGID \
  --cap-add SETUID \
  --read-only \
  --security-opt no-new-privileges:true \
  my-app:latest

# 网络访问控制
docker service create --name isolated-app \
  --network isolated-network \
  --constraint 'node.labels.network.zone == isolated' \
  my-isolated-app:latest

4.2 权限管理脚本

访问控制管理

#!/bin/bash
# access-control-manager.sh

CERTS_DIR="/etc/docker/certs"
USERS_DIR="/etc/docker/users"
LOG_FILE="/var/log/docker-access.log"

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}

# 创建用户证书
create_user_cert() {
    local username=$1
    local role=${2:-worker}  # worker, manager, admin
    local days=${3:-365}
    
    log "Creating certificate for user: $username (role: $role)"
    
    # 创建用户目录
    local user_dir="$USERS_DIR/$username"
    mkdir -p $user_dir
    
    # 生成私钥
    openssl genrsa -out $user_dir/key.pem 4096
    
    # 生成证书请求
    openssl req -new \
        -key $user_dir/key.pem \
        -out $user_dir/cert.csr \
        -subj "/CN=$username/O=$role"
    
    # 签发证书
    openssl x509 -req \
        -in $user_dir/cert.csr \
        -CA $CERTS_DIR/ca.pem \
        -CAkey $CERTS_DIR/ca-key.pem \
        -out $user_dir/cert.pem \
        -days $days \
        -extensions v3_req
    
    # 设置权限
    chmod 400 $user_dir/key.pem
    chmod 444 $user_dir/cert.pem
    chown -R $username:$username $user_dir 2>/dev/null || true
    
    # 创建用户配置文件
    cat > $user_dir/docker-env << EOF
export DOCKER_HOST=tcp://$(hostname):2376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=$user_dir
EOF
    
    # 记录用户信息
    echo "$username:$role:$(date +%Y%m%d):$days" >> $USERS_DIR/users.db
    
    log "Certificate created for user: $username"
    echo "User certificate created at: $user_dir"
    echo "To use: source $user_dir/docker-env"
}

# 撤销用户证书
revoke_user_cert() {
    local username=$1
    
    log "Revoking certificate for user: $username"
    
    local user_dir="$USERS_DIR/$username"
    
    if [ -d "$user_dir" ]; then
        # 移动到撤销目录
        local revoked_dir="$USERS_DIR/revoked/$username-$(date +%Y%m%d_%H%M%S)"
        mkdir -p $(dirname $revoked_dir)
        mv $user_dir $revoked_dir
        
        # 从用户数据库中移除
        sed -i "/^$username:/d" $USERS_DIR/users.db
        
        log "Certificate revoked for user: $username"
        echo "Certificate moved to: $revoked_dir"
    else
        log "ERROR: User directory not found: $user_dir"
        return 1
    fi
}

# 列出用户
list_users() {
    echo "=== Docker Users ==="
    printf "%-15s %-10s %-12s %-10s\n" "USERNAME" "ROLE" "CREATED" "VALIDITY"
    printf "%-15s %-10s %-12s %-10s\n" "--------" "----" "-------" "--------"
    
    if [ -f "$USERS_DIR/users.db" ]; then
        while IFS=':' read -r username role created days; do
            printf "%-15s %-10s %-12s %-10s\n" "$username" "$role" "$created" "${days}d"
        done < $USERS_DIR/users.db
    fi
}

# 检查证书有效性
check_cert_validity() {
    local username=$1
    local user_dir="$USERS_DIR/$username"
    
    if [ ! -f "$user_dir/cert.pem" ]; then
        echo "Certificate not found for user: $username"
        return 1
    fi
    
    echo "Certificate information for user: $username"
    openssl x509 -in $user_dir/cert.pem -text -noout | grep -E "Subject:|Not Before:|Not After:"
    
    # 检查是否即将过期
    local expiry_date=$(openssl x509 -in $user_dir/cert.pem -enddate -noout | cut -d= -f2)
    local expiry_timestamp=$(date -d "$expiry_date" +%s)
    local current_timestamp=$(date +%s)
    local days_until_expiry=$(( (expiry_timestamp - current_timestamp) / 86400 ))
    
    if [ $days_until_expiry -lt 30 ]; then
        echo "WARNING: Certificate expires in $days_until_expiry days"
    else
        echo "Certificate valid for $days_until_expiry days"
    fi
}

# 更新用户角色
update_user_role() {
    local username=$1
    local new_role=$2
    
    log "Updating role for user: $username to $new_role"
    
    # 更新用户数据库
    if grep -q "^$username:" $USERS_DIR/users.db; then
        sed -i "s/^$username:[^:]*:/$username:$new_role:/" $USERS_DIR/users.db
        log "Role updated for user: $username"
    else
        log "ERROR: User not found: $username"
        return 1
    fi
}

# 生成访问报告
generate_access_report() {
    local report_file="/tmp/docker-access-report-$(date +%Y%m%d_%H%M%S).txt"
    
    log "Generating access report: $report_file"
    
    {
        echo "Docker Access Control Report"
        echo "Generated: $(date)"
        echo "========================================"
        
        echo "\nUser Summary:"
        list_users
        
        echo "\nCertificate Status:"
        if [ -f "$USERS_DIR/users.db" ]; then
            while IFS=':' read -r username role created days; do
                echo "\nUser: $username"
                check_cert_validity $username
            done < $USERS_DIR/users.db
        fi
        
        echo "\nRevoked Certificates:"
        if [ -d "$USERS_DIR/revoked" ]; then
            ls -la $USERS_DIR/revoked/
        else
            echo "No revoked certificates"
        fi
        
        echo "\nAccess Log Summary (last 24 hours):"
        if [ -f "$LOG_FILE" ]; then
            grep "$(date -d '1 day ago' +%Y-%m-%d)" $LOG_FILE | tail -20
        fi
        
    } > $report_file
    
    log "Access report generated: $report_file"
}

# 主菜单
case "$1" in
    "create")
        if [ -n "$2" ]; then
            create_user_cert $2 $3 $4
        else
            echo "Usage: $0 create <username> [role] [days]"
        fi
        ;;
    "revoke")
        if [ -n "$2" ]; then
            revoke_user_cert $2
        else
            echo "Usage: $0 revoke <username>"
        fi
        ;;
    "list")
        list_users
        ;;
    "check")
        if [ -n "$2" ]; then
            check_cert_validity $2
        else
            echo "Usage: $0 check <username>"
        fi
        ;;
    "update")
        if [ -n "$2" ] && [ -n "$3" ]; then
            update_user_role $2 $3
        else
            echo "Usage: $0 update <username> <new-role>"
        fi
        ;;
    "report")
        generate_access_report
        ;;
    *)
        echo "Usage: $0 {create|revoke|list|check|update|report}"
        echo "  create <user> [role] [days] - Create user certificate"
        echo "  revoke <user>               - Revoke user certificate"
        echo "  list                        - List all users"
        echo "  check <user>                - Check certificate validity"
        echo "  update <user> <role>        - Update user role"
        echo "  report                      - Generate access report"
        ;;
esac

5. 安全扫描和合规性

5.1 镜像安全扫描

使用 Docker Scout 扫描

# 启用 Docker Scout
docker scout --help

# 扫描本地镜像
docker scout cves nginx:latest

# 扫描并显示详细信息
docker scout cves --details nginx:latest

# 扫描特定严重级别的漏洞
docker scout cves --severity critical,high nginx:latest

# 生成 SARIF 报告
docker scout cves --format sarif nginx:latest > nginx-scan.sarif

使用 Trivy 扫描

# 安装 Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

# 扫描镜像
trivy image nginx:latest

# 扫描并输出 JSON 格式
trivy image --format json nginx:latest > nginx-trivy.json

# 扫描文件系统
trivy fs /path/to/project

# 扫描 Kubernetes 配置
trivy k8s cluster

5.2 安全扫描脚本

综合安全扫描工具

#!/bin/bash
# security-scanner.sh

SCAN_DIR="/tmp/security-scans"
REPORT_DIR="/var/reports/security"
LOG_FILE="/var/log/security-scanner.log"

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}

# 创建目录
mkdir -p $SCAN_DIR $REPORT_DIR

# 扫描镜像漏洞
scan_image_vulnerabilities() {
    local image=$1
    local scan_id=$(date +%Y%m%d_%H%M%S)
    local report_file="$REPORT_DIR/image-scan-${image//[:\/]/-}-$scan_id.json"
    
    log "Scanning image vulnerabilities: $image"
    
    # 使用 Trivy 扫描
    if command -v trivy > /dev/null; then
        trivy image --format json --output $report_file $image
        
        # 提取关键信息
        local critical=$(jq '.Results[]?.Vulnerabilities[]? | select(.Severity=="CRITICAL") | .VulnerabilityID' $report_file | wc -l)
        local high=$(jq '.Results[]?.Vulnerabilities[]? | select(.Severity=="HIGH") | .VulnerabilityID' $report_file | wc -l)
        local medium=$(jq '.Results[]?.Vulnerabilities[]? | select(.Severity=="MEDIUM") | .VulnerabilityID' $report_file | wc -l)
        
        log "Vulnerabilities found in $image: Critical=$critical, High=$high, Medium=$medium"
        
        # 如果有严重漏洞,发出警告
        if [ $critical -gt 0 ] || [ $high -gt 0 ]; then
            log "WARNING: High-risk vulnerabilities found in $image"
            return 1
        fi
    else
        log "ERROR: Trivy not installed"
        return 1
    fi
    
    return 0
}

# 扫描容器配置
scan_container_config() {
    local container_id=$1
    
    log "Scanning container configuration: $container_id"
    
    local issues=()
    
    # 检查特权模式
    local privileged=$(docker inspect $container_id --format '{{.HostConfig.Privileged}}')
    if [ "$privileged" = "true" ]; then
        issues+=("Privileged mode enabled")
    fi
    
    # 检查用户
    local user=$(docker inspect $container_id --format '{{.Config.User}}')
    if [ -z "$user" ] || [ "$user" = "root" ] || [ "$user" = "0" ]; then
        issues+=("Running as root user")
    fi
    
    # 检查只读根文件系统
    local readonly=$(docker inspect $container_id --format '{{.HostConfig.ReadonlyRootfs}}')
    if [ "$readonly" != "true" ]; then
        issues+=("Root filesystem is writable")
    fi
    
    # 检查能力
    local cap_add=$(docker inspect $container_id --format '{{.HostConfig.CapAdd}}')
    if [ "$cap_add" != "<no value>" ] && [ "$cap_add" != "[]" ]; then
        issues+=("Additional capabilities granted: $cap_add")
    fi
    
    # 检查网络模式
    local network_mode=$(docker inspect $container_id --format '{{.HostConfig.NetworkMode}}')
    if [ "$network_mode" = "host" ]; then
        issues+=("Using host network mode")
    fi
    
    # 报告问题
    if [ ${#issues[@]} -gt 0 ]; then
        log "Security issues found in container $container_id:"
        for issue in "${issues[@]}"; do
            log "  - $issue"
        done
        return 1
    else
        log "No security issues found in container $container_id"
        return 0
    fi
}

# 扫描服务配置
scan_service_config() {
    local service_name=$1
    
    log "Scanning service configuration: $service_name"
    
    local issues=()
    
    # 检查服务约束
    local constraints=$(docker service inspect $service_name --format '{{.Spec.TaskTemplate.Placement.Constraints}}')
    if [ "$constraints" = "<no value>" ] || [ "$constraints" = "[]" ]; then
        issues+=("No placement constraints defined")
    fi
    
    # 检查资源限制
    local cpu_limit=$(docker service inspect $service_name --format '{{.Spec.TaskTemplate.Resources.Limits.NanoCPUs}}')
    local mem_limit=$(docker service inspect $service_name --format '{{.Spec.TaskTemplate.Resources.Limits.MemoryBytes}}')
    
    if [ "$cpu_limit" = "<no value>" ] || [ "$cpu_limit" = "0" ]; then
        issues+=("No CPU limit defined")
    fi
    
    if [ "$mem_limit" = "<no value>" ] || [ "$mem_limit" = "0" ]; then
        issues+=("No memory limit defined")
    fi
    
    # 检查网络
    local networks=$(docker service inspect $service_name --format '{{range .Spec.TaskTemplate.Networks}}{{.Target}} {{end}}')
    if [[ $networks == *"ingress"* ]] && [[ $networks != *"backend"* ]] && [[ $networks != *"frontend"* ]]; then
        issues+=("Service only connected to ingress network")
    fi
    
    # 检查密钥使用
    local secrets=$(docker service inspect $service_name --format '{{range .Spec.TaskTemplate.ContainerSpec.Secrets}}{{.SecretName}} {{end}}')
    local env_vars=$(docker service inspect $service_name --format '{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{.}} {{end}}')
    
    if [[ $env_vars == *"PASSWORD"* ]] || [[ $env_vars == *"SECRET"* ]] || [[ $env_vars == *"KEY"* ]]; then
        issues+=("Sensitive data in environment variables")
    fi
    
    # 报告问题
    if [ ${#issues[@]} -gt 0 ]; then
        log "Security issues found in service $service_name:"
        for issue in "${issues[@]}"; do
            log "  - $issue"
        done
        return 1
    else
        log "No security issues found in service $service_name"
        return 0
    fi
}

# 扫描网络配置
scan_network_config() {
    log "Scanning network configuration..."
    
    local issues=()
    
    # 检查未加密的 overlay 网络
    for network in $(docker network ls --filter driver=overlay --format '{{.Name}}'); do
        local encrypted=$(docker network inspect $network --format '{{.Options.encrypted}}')
        if [ "$encrypted" != "true" ]; then
            issues+=("Unencrypted overlay network: $network")
        fi
    done
    
    # 检查默认网络使用
    for service in $(docker service ls --format '{{.Name}}'); do
        local networks=$(docker service inspect $service --format '{{range .Spec.TaskTemplate.Networks}}{{.Target}} {{end}}')
        if [[ $networks == *"ingress"* ]] && [ $(echo $networks | wc -w) -eq 1 ]; then
            issues+=("Service $service only uses default ingress network")
        fi
    done
    
    # 报告问题
    if [ ${#issues[@]} -gt 0 ]; then
        log "Network security issues found:"
        for issue in "${issues[@]}"; do
            log "  - $issue"
        done
        return 1
    else
        log "No network security issues found"
        return 0
    fi
}

# 生成安全报告
generate_security_report() {
    local report_file="$REPORT_DIR/security-report-$(date +%Y%m%d_%H%M%S).html"
    
    log "Generating comprehensive security report: $report_file"
    
    cat > $report_file << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>Docker Swarm Security Report</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .header { background-color: #f0f0f0; padding: 10px; border-radius: 5px; }
        .section { margin: 20px 0; }
        .issue { color: red; }
        .warning { color: orange; }
        .ok { color: green; }
        table { border-collapse: collapse; width: 100%; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
    </style>
</head>
<body>
    <div class="header">
        <h1>Docker Swarm Security Report</h1>
        <p>Generated: $(date)</p>
        <p>Cluster: $(docker info --format '{{.Swarm.NodeID}}')</p>
    </div>
EOF
    
    # 添加镜像扫描结果
    echo "    <div class='section'>" >> $report_file
    echo "        <h2>Image Vulnerability Scan</h2>" >> $report_file
    echo "        <table>" >> $report_file
    echo "            <tr><th>Image</th><th>Critical</th><th>High</th><th>Medium</th><th>Status</th></tr>" >> $report_file
    
    for image in $(docker image ls --format '{{.Repository}}:{{.Tag}}' | grep -v '<none>'); do
        if scan_image_vulnerabilities $image > /dev/null 2>&1; then
            echo "            <tr><td>$image</td><td>0</td><td>0</td><td>-</td><td class='ok'>PASS</td></tr>" >> $report_file
        else
            echo "            <tr><td>$image</td><td>?</td><td>?</td><td>?</td><td class='issue'>FAIL</td></tr>" >> $report_file
        fi
    done
    
    echo "        </table>" >> $report_file
    echo "    </div>" >> $report_file
    
    # 添加服务配置扫描结果
    echo "    <div class='section'>" >> $report_file
    echo "        <h2>Service Configuration Scan</h2>" >> $report_file
    echo "        <table>" >> $report_file
    echo "            <tr><th>Service</th><th>Status</th><th>Issues</th></tr>" >> $report_file
    
    for service in $(docker service ls --format '{{.Name}}'); do
        if scan_service_config $service > /dev/null 2>&1; then
            echo "            <tr><td>$service</td><td class='ok'>PASS</td><td>None</td></tr>" >> $report_file
        else
            echo "            <tr><td>$service</td><td class='issue'>FAIL</td><td>See logs</td></tr>" >> $report_file
        fi
    done
    
    echo "        </table>" >> $report_file
    echo "    </div>" >> $report_file
    
    # 结束 HTML
    echo "</body></html>" >> $report_file
    
    log "Security report generated: $report_file"
}

# 主菜单
case "$1" in
    "image")
        if [ -n "$2" ]; then
            scan_image_vulnerabilities $2
        else
            echo "Usage: $0 image <image-name>"
        fi
        ;;
    "container")
        if [ -n "$2" ]; then
            scan_container_config $2
        else
            echo "Usage: $0 container <container-id>"
        fi
        ;;
    "service")
        if [ -n "$2" ]; then
            scan_service_config $2
        else
            echo "Usage: $0 service <service-name>"
        fi
        ;;
    "network")
        scan_network_config
        ;;
    "all")
        log "Starting comprehensive security scan..."
        
        # 扫描所有镜像
        for image in $(docker image ls --format '{{.Repository}}:{{.Tag}}' | grep -v '<none>'); do
            scan_image_vulnerabilities $image
        done
        
        # 扫描所有运行的容器
        for container in $(docker ps --format '{{.ID}}'); do
            scan_container_config $container
        done
        
        # 扫描所有服务
        for service in $(docker service ls --format '{{.Name}}'); do
            scan_service_config $service
        done
        
        # 扫描网络配置
        scan_network_config
        
        # 生成报告
        generate_security_report
        ;;
    "report")
        generate_security_report
        ;;
    *)
        echo "Usage: $0 {image|container|service|network|all|report}"
        echo "  image <name>     - Scan image vulnerabilities"
        echo "  container <id>   - Scan container configuration"
        echo "  service <name>   - Scan service configuration"
        echo "  network          - Scan network configuration"
        echo "  all              - Comprehensive security scan"
        echo "  report           - Generate security report"
        ;;
esac

5.3 合规性检查

CIS Docker Benchmark

# 下载 CIS Docker Benchmark 脚本
wget https://github.com/docker/docker-bench-security/archive/master.zip
unzip master.zip
cd docker-bench-security-master

# 运行 CIS 基准测试
./docker-bench-security.sh

# 生成 JSON 报告
./docker-bench-security.sh -l /tmp/docker-bench.log -j > docker-bench-report.json

# 只检查特定部分
./docker-bench-security.sh -c check_2,check_3

自定义合规性检查

#!/bin/bash
# compliance-checker.sh

COMPLIANCE_REPORT="/tmp/compliance-report-$(date +%Y%m%d_%H%M%S).txt"

# 合规性检查函数
check_compliance() {
    echo "Docker Swarm Compliance Check Report" > $COMPLIANCE_REPORT
    echo "Generated: $(date)" >> $COMPLIANCE_REPORT
    echo "=========================================" >> $COMPLIANCE_REPORT
    
    # 检查 1: TLS 配置
    echo "\n1. TLS Configuration:" >> $COMPLIANCE_REPORT
    if docker info --format '{{.Swarm.Cluster.TLSInfo.TrustRoot}}' | grep -q "BEGIN CERTIFICATE"; then
        echo "  ✓ TLS enabled and configured" >> $COMPLIANCE_REPORT
    else
        echo "  ✗ TLS not properly configured" >> $COMPLIANCE_REPORT
    fi
    
    # 检查 2: 节点证书
    echo "\n2. Node Certificates:" >> $COMPLIANCE_REPORT
    for node in $(docker node ls --format '{{.Hostname}}'); do
        tls_status=$(docker node inspect $node --format '{{.Description.TLSInfo.TrustRoot}}' | head -1)
        if [ -n "$tls_status" ]; then
            echo "  ✓ Node $node has valid certificate" >> $COMPLIANCE_REPORT
        else
            echo "  ✗ Node $node certificate issue" >> $COMPLIANCE_REPORT
        fi
    done
    
    # 检查 3: 网络加密
    echo "\n3. Network Encryption:" >> $COMPLIANCE_REPORT
    for network in $(docker network ls --filter driver=overlay --format '{{.Name}}'); do
        encrypted=$(docker network inspect $network --format '{{.Options.encrypted}}')
        if [ "$encrypted" = "true" ]; then
            echo "  ✓ Network $network is encrypted" >> $COMPLIANCE_REPORT
        else
            echo "  ✗ Network $network is not encrypted" >> $COMPLIANCE_REPORT
        fi
    done
    
    # 检查 4: 密钥管理
    echo "\n4. Secrets Management:" >> $COMPLIANCE_REPORT
    secret_count=$(docker secret ls --format '{{.Name}}' | wc -l)
    if [ $secret_count -gt 0 ]; then
        echo "  ✓ Docker Secrets in use ($secret_count secrets)" >> $COMPLIANCE_REPORT
        
        # 检查未使用的密钥
        unused_secrets=0
        for secret in $(docker secret ls --format '{{.Name}}'); do
            services=$(docker service ls --filter "secret=$secret" --format '{{.Name}}')
            if [ -z "$services" ]; then
                unused_secrets=$((unused_secrets + 1))
            fi
        done
        
        if [ $unused_secrets -gt 0 ]; then
            echo "  ⚠ $unused_secrets unused secrets found" >> $COMPLIANCE_REPORT
        fi
    else
        echo "  ⚠ No Docker Secrets configured" >> $COMPLIANCE_REPORT
    fi
    
    # 检查 5: 服务安全配置
    echo "\n5. Service Security:" >> $COMPLIANCE_REPORT
    for service in $(docker service ls --format '{{.Name}}'); do
        # 检查用户配置
        user=$(docker service inspect $service --format '{{.Spec.TaskTemplate.ContainerSpec.User}}')
        if [ -n "$user" ] && [ "$user" != "<no value>" ]; then
            echo "  ✓ Service $service runs as non-root user" >> $COMPLIANCE_REPORT
        else
            echo "  ✗ Service $service may run as root" >> $COMPLIANCE_REPORT
        fi
        
        # 检查只读根文件系统
        readonly=$(docker service inspect $service --format '{{.Spec.TaskTemplate.ContainerSpec.ReadOnly}}')
        if [ "$readonly" = "true" ]; then
            echo "  ✓ Service $service has read-only root filesystem" >> $COMPLIANCE_REPORT
        else
            echo "  ⚠ Service $service has writable root filesystem" >> $COMPLIANCE_REPORT
        fi
    done
    
    echo "\nCompliance check completed. Report saved to: $COMPLIANCE_REPORT"
}

# 执行合规性检查
check_compliance

6. 实践练习

练习 1:安全集群搭建

# 目标:搭建一个安全加固的 Swarm 集群

# 1. 初始化安全集群
docker swarm init --cert-expiry 24h

# 2. 配置节点安全标签
docker node update --label-add security.zone=production $(docker node ls --format '{{.Hostname}}')
docker node update --label-add security.hardened=true $(docker node ls --format '{{.Hostname}}')

# 3. 创建加密网络
docker network create --driver overlay --opt encrypted secure-net

# 4. 创建安全服务
docker service create --name secure-web \
  --network secure-net \
  --constraint 'node.labels.security.hardened == true' \
  --user 1000:1000 \
  --read-only \
  --cap-drop ALL \
  --cap-add CHOWN \
  --cap-add SETGID \
  --cap-add SETUID \
  --security-opt no-new-privileges:true \
  --limit-cpu 0.5 \
  --limit-memory 512M \
  nginx:alpine

# 5. 验证安全配置
docker service inspect secure-web --format '{{json .Spec.TaskTemplate.ContainerSpec}}' | jq .

练习 2:密钥管理实践

# 目标:实现完整的密钥管理流程

# 1. 创建应用密钥
echo "super-secret-password" | docker secret create app-db-password -
echo "jwt-secret-key-$(date +%s)" | docker secret create app-jwt-secret -

# 2. 创建 TLS 证书
openssl req -new -x509 -days 365 -nodes \
  -out app.crt -keyout app.key \
  -subj "/C=US/ST=CA/L=SF/O=MyApp/CN=app.example.com"

docker secret create app-tls-cert app.crt
docker secret create app-tls-key app.key

# 3. 部署使用密钥的服务
docker service create --name secure-app \
  --secret source=app-db-password,target=/run/secrets/db_password \
  --secret source=app-jwt-secret,target=/run/secrets/jwt_secret \
  --secret source=app-tls-cert,target=/run/secrets/tls_cert \
  --secret source=app-tls-key,target=/run/secrets/tls_key \
  --env DB_PASSWORD_FILE=/run/secrets/db_password \
  --env JWT_SECRET_FILE=/run/secrets/jwt_secret \
  --env TLS_CERT_FILE=/run/secrets/tls_cert \
  --env TLS_KEY_FILE=/run/secrets/tls_key \
  my-secure-app:latest

# 4. 验证密钥挂载
docker service ps secure-app
docker exec $(docker ps --filter "label=com.docker.swarm.service.name=secure-app" --format '{{.ID}}') \
  ls -la /run/secrets/

# 5. 密钥轮换
echo "new-super-secret-password" | docker secret create app-db-password-new -
docker service update \
  --secret-rm app-db-password \
  --secret-add source=app-db-password-new,target=/run/secrets/db_password \
  secure-app
docker secret rm app-db-password

# 清理
rm -f app.crt app.key

练习 3:安全扫描和监控

# 目标:建立安全扫描和监控体系

# 1. 安装安全扫描工具
# 安装 Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

# 2. 扫描当前镜像
for image in $(docker image ls --format '{{.Repository}}:{{.Tag}}' | grep -v '<none>'); do
  echo "Scanning $image..."
  trivy image --severity HIGH,CRITICAL $image
done

# 3. 配置安全监控
cat > security-monitor.sh << 'EOF'
#!/bin/bash
while true; do
  # 检查特权容器
  privileged_containers=$(docker ps --filter "label=privileged=true" --format '{{.Names}}')
  if [ -n "$privileged_containers" ]; then
    echo "$(date): WARNING - Privileged containers detected: $privileged_containers"
  fi
  
  # 检查根用户容器
  for container in $(docker ps --format '{{.ID}}'); do
    user=$(docker inspect $container --format '{{.Config.User}}')
    if [ -z "$user" ] || [ "$user" = "root" ] || [ "$user" = "0" ]; then
      name=$(docker inspect $container --format '{{.Name}}')
      echo "$(date): WARNING - Container running as root: $name"
    fi
  done
  
  sleep 300  # 每5分钟检查一次
done
EOF

chmod +x security-monitor.sh

# 4. 启动监控(后台运行)
./security-monitor.sh > /var/log/security-monitor.log 2>&1 &

# 5. 生成安全报告
./security-scanner.sh all

本章总结

通过本章学习,我们深入了解了 Docker Swarm 的安全管理:

关键要点

  1. 安全架构

    • TLS 加密通信
    • 相互认证机制
    • 基于角色的访问控制
    • 密钥管理系统
  2. 集群安全配置

    • 证书管理和轮换
    • 节点安全标签
    • 安全加固脚本
    • 审计日志配置
  3. 密钥和证书管理

    • Docker Secrets 使用
    • 密钥轮换策略
    • 外部密钥管理集成
    • 访问控制实现
  4. 安全扫描和合规性

    • 镜像漏洞扫描
    • 配置安全检查
    • CIS 基准测试
    • 合规性报告

最佳实践

  1. 定期安全检查

    • 自动化安全扫描
    • 定期证书轮换
    • 持续合规性监控
    • 安全事件响应
  2. 最小权限原则

    • 非特权容器运行
    • 只读根文件系统
    • 最小能力集合
    • 网络隔离策略
  3. 深度防护

    • 多层安全控制
    • 网络分段隔离
    • 数据加密保护
    • 访问审计跟踪

下一章我们将学习 Docker Swarm 的监控与日志管理,了解如何建立完善的可观测性体系。