#!/bin/bash

# QAUP 安全配置脚本

set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

print_message() {
    local color=$1
    local message=$2
    echo -e "${color}${message}${NC}"
}

# 创建非 root 用户配置
setup_non_root_users() {
    print_message $BLUE "配置容器非 root 用户..."
    
    # 更新应用 Dockerfile 以使用非 root 用户
    cat > "$PROJECT_ROOT/docker/app-security.Dockerfile" << 'EOF'
# 安全版本的应用 Dockerfile
FROM openjdk:17-jre-slim

# 创建应用用户和组
RUN groupadd -r qaup && useradd -r -g qaup -d /app -s /bin/bash qaup

# 安装必要工具
RUN apt-get update && apt-get install -y \
    curl \
    netcat-openbsd \
    fontconfig \
    fonts-dejavu-core \
    && rm -rf /var/lib/apt/lists/* \
    && apt-get clean

# 设置工作目录
WORKDIR /app

# 创建必要目录并设置权限
RUN mkdir -p /app/logs /app/uploadPath /app/temp \
    && chown -R qaup:qaup /app \
    && chmod 755 /app \
    && chmod 750 /app/logs /app/uploadPath \
    && chmod 700 /app/temp

# 复制应用文件
COPY --chown=qaup:qaup qaup-admin/target/qaup-admin.jar /app/app.jar
COPY --chown=qaup:qaup docker/start.sh /app/start.sh

# 设置文件权限
RUN chmod 644 /app/app.jar \
    && chmod 750 /app/start.sh

# 切换到非 root 用户
USER qaup

# 暴露端口
EXPOSE 8080

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
    CMD curl -f http://localhost:8080/actuator/health || exit 1

# 启动应用
ENTRYPOINT ["/app/start.sh"]
EOF
    
    print_message $GREEN "✓ 应用容器非 root 用户配置完成"
}

# 配置网络安全
setup_network_security() {
    print_message $BLUE "配置网络安全..."
    
    # 创建安全的 Docker Compose 网络配置
    cat > "$PROJECT_ROOT/docker-compose.security.yml" << 'EOF'
version: '3.8'

# 安全网络配置覆盖
services:
  qaup-postgres:
    networks:
      - db-network
    # 不暴露数据库端口到主机
    ports: []
    
  qaup-redis:
    networks:
      - cache-network
    # 不暴露 Redis 端口到主机
    ports: []
    
  qaup-app:
    networks:
      - app-network
      - db-network
      - cache-network
    # 不暴露应用端口到主机
    ports: []
    
  qaup-nginx:
    networks:
      - app-network
    # 只暴露必要的 HTTP/HTTPS 端口
    ports:
      - "80:80"
      - "443:443"

# 网络隔离配置
networks:
  app-network:
    driver: bridge
    internal: false
    ipam:
      config:
        - subnet: 172.20.1.0/24
  
  db-network:
    driver: bridge
    internal: true  # 内部网络，不能访问外网
    ipam:
      config:
        - subnet: 172.20.2.0/24
  
  cache-network:
    driver: bridge
    internal: true  # 内部网络，不能访问外网
    ipam:
      config:
        - subnet: 172.20.3.0/24
EOF
    
    print_message $GREEN "✓ 网络安全配置完成"
}

# 配置文件权限
setup_file_permissions() {
    print_message $BLUE "配置文件权限..."
    
    # 设置配置文件权限
    if [ -f "$PROJECT_ROOT/.env" ]; then
        chmod 600 "$PROJECT_ROOT/.env"
        print_message $GREEN "✓ 环境文件权限已设置为 600"
    fi
    
    # 设置数据目录权限
    if [ -d "$PROJECT_ROOT/data" ]; then
        chmod 750 "$PROJECT_ROOT/data"
        chmod 700 "$PROJECT_ROOT/data/postgres" 2>/dev/null || true
        chmod 755 "$PROJECT_ROOT/data/redis" 2>/dev/null || true
        chmod 755 "$PROJECT_ROOT/data/uploads" 2>/dev/null || true
        print_message $GREEN "✓ 数据目录权限已设置"
    fi
    
    # 设置日志目录权限
    if [ -d "$PROJECT_ROOT/logs" ]; then
        chmod 755 "$PROJECT_ROOT/logs"
        chmod 644 "$PROJECT_ROOT/logs"/*.log 2>/dev/null || true
        print_message $GREEN "✓ 日志目录权限已设置"
    fi
    
    # 设置脚本权限
    find "$PROJECT_ROOT/docker" -name "*.sh" -exec chmod 750 {} \;
    chmod 750 "$PROJECT_ROOT/deploy.sh" 2>/dev/null || true
    print_message $GREEN "✓ 脚本文件权限已设置"
    
    # 设置配置文件权限
    find "$PROJECT_ROOT/config" -type f -exec chmod 644 {} \; 2>/dev/null || true
    find "$PROJECT_ROOT/docker" -name "*.conf" -exec chmod 644 {} \; 2>/dev/null || true
    print_message $GREEN "✓ 配置文件权限已设置"
}

# 配置 SSL/TLS
setup_ssl_tls() {
    print_message $BLUE "配置 SSL/TLS..."
    
    # 创建 SSL 目录
    mkdir -p "$PROJECT_ROOT/ssl"
    chmod 700 "$PROJECT_ROOT/ssl"
    
    # 创建自签名证书（用于测试）
    if [ ! -f "$PROJECT_ROOT/ssl/server.crt" ]; then
        print_message $YELLOW "生成自签名 SSL 证书（仅用于测试）..."
        
        openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
            -keyout "$PROJECT_ROOT/ssl/server.key" \
            -out "$PROJECT_ROOT/ssl/server.crt" \
            -subj "/C=CN/ST=State/L=City/O=Organization/CN=localhost" \
            2>/dev/null
        
        chmod 600 "$PROJECT_ROOT/ssl/server.key"
        chmod 644 "$PROJECT_ROOT/ssl/server.crt"
        
        print_message $YELLOW "⚠ 生产环境请使用正式的 SSL 证书"
    fi
    
    # 创建支持 HTTPS 的 Nginx 配置
    cat > "$PROJECT_ROOT/docker/nginx/https.conf" << 'EOF'
# HTTPS 配置
server {
    listen 443 ssl http2;
    server_name localhost;
    
    # SSL 证书配置
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    
    # SSL 安全配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;
    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;
    
    # 其他配置与 HTTP 相同
    root /usr/share/nginx/html;
    index index.html;
    
    location /health {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
    }
    
    location /prod-api/ {
        proxy_pass http://qaup-app:8080/;
        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 30s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
        
        client_max_body_size 20M;
    }
    
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        try_files $uri =404;
    }
    
    location / {
        try_files $uri $uri/ /index.html;
        
        location ~* \.html$ {
            add_header Cache-Control "no-cache, no-store, must-revalidate";
            add_header Pragma "no-cache";
            add_header Expires "0";
        }
    }
}

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name localhost;
    return 301 https://$server_name$request_uri;
}
EOF
    
    print_message $GREEN "✓ SSL/TLS 配置完成"
}

# 配置防火墙规则
setup_firewall() {
    print_message $BLUE "配置防火墙规则..."
    
    # 检查是否有 root 权限
    if [ "$EUID" -ne 0 ]; then
        print_message $YELLOW "⚠ 需要 root 权限来配置防火墙"
        print_message $YELLOW "请手动执行以下命令："
        echo ""
        echo "# 启用 UFW 防火墙"
        echo "sudo ufw --force enable"
        echo ""
        echo "# 允许 SSH 连接"
        echo "sudo ufw allow 22/tcp"
        echo ""
        echo "# 允许 HTTP 和 HTTPS"
        echo "sudo ufw allow 80/tcp"
        echo "sudo ufw allow 443/tcp"
        echo ""
        echo "# 拒绝其他端口的外部访问"
        echo "sudo ufw deny 8080/tcp"
        echo "sudo ufw deny 5432/tcp"
        echo "sudo ufw deny 6379/tcp"
        echo ""
        echo "# 查看防火墙状态"
        echo "sudo ufw status verbose"
        return
    fi
    
    # 配置 UFW 防火墙
    ufw --force enable
    
    # 允许必要的端口
    ufw allow 22/tcp comment 'SSH'
    ufw allow 80/tcp comment 'HTTP'
    ufw allow 443/tcp comment 'HTTPS'
    
    # 拒绝直接访问内部服务端口
    ufw deny 8080/tcp comment 'Block direct app access'
    ufw deny 5432/tcp comment 'Block direct database access'
    ufw deny 6379/tcp comment 'Block direct Redis access'
    
    # 显示防火墙状态
    ufw status verbose
    
    print_message $GREEN "✓ 防火墙配置完成"
}

# 配置容器安全选项
setup_container_security() {
    print_message $BLUE "配置容器安全选项..."
    
    # 创建安全的 Docker Compose 配置
    cat > "$PROJECT_ROOT/docker-compose.security-options.yml" << 'EOF'
version: '3.8'

# 容器安全选项配置
services:
  qaup-postgres:
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - DAC_OVERRIDE
      - FOWNER
      - SETGID
      - SETUID
    read_only: false
    tmpfs:
      - /tmp:noexec,nosuid,size=100m
    
  qaup-redis:
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - SETGID
      - SETUID
    read_only: false
    tmpfs:
      - /tmp:noexec,nosuid,size=50m
    
  qaup-app:
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    read_only: false
    tmpfs:
      - /tmp:noexec,nosuid,size=200m
    
  qaup-nginx:
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - CHOWN
      - DAC_OVERRIDE
      - SETGID
      - SETUID
    read_only: false
    tmpfs:
      - /tmp:noexec,nosuid,size=50m
      - /var/cache/nginx:noexec,nosuid,size=100m
EOF
    
    print_message $GREEN "✓ 容器安全选项配置完成"
}

# 配置日志审计
setup_audit_logging() {
    print_message $BLUE "配置审计日志..."
    
    # 创建审计日志配置
    cat > "$PROJECT_ROOT/docker/audit-config.yml" << 'EOF'
# 审计日志配置
audit:
  enabled: true
  log_file: "./logs/audit.log"
  
  # 审计事件类型
  events:
    - login_attempts
    - admin_operations
    - data_access
    - configuration_changes
    - security_events
  
  # 日志格式
  format: json
  
  # 日志轮转
  rotation:
    max_size: 100MB
    max_files: 10
    compress: true
EOF
    
    # 创建审计日志脚本
    cat > "$PROJECT_ROOT/docker/audit-logger.sh" << 'AUDIT_EOF'
#!/bin/bash

# 审计日志记录脚本

AUDIT_LOG="./logs/audit.log"

# 记录审计事件
log_audit_event() {
    local event_type=$1
    local user=${2:-"system"}
    local action=$3
    local resource=${4:-""}
    local result=${5:-"success"}
    local details=${6:-""}
    
    local timestamp=$(date -u '+%Y-%m-%dT%H:%M:%S.%3NZ')
    local event_id=$(uuidgen 2>/dev/null || echo "$(date +%s)-$$")
    
    # 创建 JSON 格式的审计日志
    cat >> "$AUDIT_LOG" << EOF
{
  "timestamp": "$timestamp",
  "event_id": "$event_id",
  "event_type": "$event_type",
  "user": "$user",
  "action": "$action",
  "resource": "$resource",
  "result": "$result",
  "details": "$details",
  "source_ip": "${SSH_CLIENT%% *}",
  "session_id": "${SSH_TTY:-console}"
}
EOF
}

# 监控 Docker 事件
monitor_docker_events() {
    docker events --format '{{.Time}} {{.Type}} {{.Action}} {{.Actor.Attributes.name}}' | while read timestamp type action container; do
        if [[ "$type" == "container" && "$action" =~ ^(start|stop|restart|kill|die)$ ]]; then
            log_audit_event "container_event" "docker" "$action" "$container" "success" "Container $action event"
        fi
    done &
}

# 启动审计监控
if [ "$1" = "start" ]; then
    echo "启动审计日志监控..."
    monitor_docker_events
    echo "审计日志监控已启动"
fi

# 导出函数供其他脚本使用
if [ "$1" != "start" ]; then
    log_audit_event "$@"
fi
AUDIT_EOF
    
    chmod +x "$PROJECT_ROOT/docker/audit-logger.sh"
    
    print_message $GREEN "✓ 审计日志配置完成"
}

# 配置密码策略
setup_password_policy() {
    print_message $BLUE "配置密码策略..."
    
    # 创建密码策略检查脚本
    cat > "$PROJECT_ROOT/docker/password-policy.sh" << 'POLICY_EOF'
#!/bin/bash

# 密码策略检查脚本

# 密码策略配置
MIN_LENGTH=12
REQUIRE_UPPERCASE=true
REQUIRE_LOWERCASE=true
REQUIRE_NUMBERS=true
REQUIRE_SPECIAL=true
FORBIDDEN_PATTERNS=("password" "123456" "admin" "qaup")

# 检查密码强度
check_password_strength() {
    local password=$1
    local errors=()
    
    # 检查长度
    if [ ${#password} -lt $MIN_LENGTH ]; then
        errors+=("密码长度至少需要 $MIN_LENGTH 位")
    fi
    
    # 检查大写字母
    if [ "$REQUIRE_UPPERCASE" = true ] && ! [[ "$password" =~ [A-Z] ]]; then
        errors+=("密码必须包含大写字母")
    fi
    
    # 检查小写字母
    if [ "$REQUIRE_LOWERCASE" = true ] && ! [[ "$password" =~ [a-z] ]]; then
        errors+=("密码必须包含小写字母")
    fi
    
    # 检查数字
    if [ "$REQUIRE_NUMBERS" = true ] && ! [[ "$password" =~ [0-9] ]]; then
        errors+=("密码必须包含数字")
    fi
    
    # 检查特殊字符
    if [ "$REQUIRE_SPECIAL" = true ] && ! [[ "$password" =~ [^a-zA-Z0-9] ]]; then
        errors+=("密码必须包含特殊字符")
    fi
    
    # 检查禁用模式
    for pattern in "${FORBIDDEN_PATTERNS[@]}"; do
        if [[ "${password,,}" == *"${pattern,,}"* ]]; then
            errors+=("密码不能包含常见词汇: $pattern")
        fi
    done
    
    # 返回结果
    if [ ${#errors[@]} -eq 0 ]; then
        echo "密码强度检查通过"
        return 0
    else
        echo "密码强度检查失败:"
        for error in "${errors[@]}"; do
            echo "  - $error"
        done
        return 1
    fi
}

# 生成强密码
generate_strong_password() {
    local length=${1:-16}
    
    # 确保包含所有必需的字符类型
    local uppercase="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    local lowercase="abcdefghijklmnopqrstuvwxyz"
    local numbers="0123456789"
    local special="!@#$%^&*()_+-=[]{}|;:,.<>?"
    
    local password=""
    
    # 至少包含一个每种类型的字符
    password+=$(echo $uppercase | fold -w1 | shuf | head -1)
    password+=$(echo $lowercase | fold -w1 | shuf | head -1)
    password+=$(echo $numbers | fold -w1 | shuf | head -1)
    password+=$(echo $special | fold -w1 | shuf | head -1)
    
    # 填充剩余长度
    local all_chars="$uppercase$lowercase$numbers$special"
    for ((i=4; i<length; i++)); do
        password+=$(echo $all_chars | fold -w1 | shuf | head -1)
    done
    
    # 打乱字符顺序
    echo $password | fold -w1 | shuf | tr -d '\n'
}

# 主函数
case "$1" in
    check)
        if [ -z "$2" ]; then
            echo "用法: $0 check <password>"
            exit 1
        fi
        check_password_strength "$2"
        ;;
    generate)
        generate_strong_password "$2"
        ;;
    *)
        echo "用法: $0 {check|generate} [参数]"
        echo "  check <password>  - 检查密码强度"
        echo "  generate [length] - 生成强密码"
        exit 1
        ;;
esac
POLICY_EOF
    
    chmod +x "$PROJECT_ROOT/docker/password-policy.sh"
    
    print_message $GREEN "✓ 密码策略配置完成"
}

# 应用所有安全配置
apply_all_security() {
    print_message $GREEN "开始应用所有安全配置..."
    echo ""
    
    setup_non_root_users
    echo ""
    
    setup_network_security
    echo ""
    
    setup_file_permissions
    echo ""
    
    setup_ssl_tls
    echo ""
    
    setup_firewall
    echo ""
    
    setup_container_security
    echo ""
    
    setup_audit_logging
    echo ""
    
    setup_password_policy
    echo ""
    
    print_message $GREEN "========================================="
    print_message $GREEN "安全配置完成！"
    print_message $GREEN "========================================="
    echo ""
    
    print_message $BLUE "安全配置摘要:"
    echo "  ✓ 容器非 root 用户配置"
    echo "  ✓ 网络隔离和安全配置"
    echo "  ✓ 文件权限安全设置"
    echo "  ✓ SSL/TLS 加密配置"
    echo "  ✓ 防火墙规则配置"
    echo "  ✓ 容器安全选项配置"
    echo "  ✓ 审计日志配置"
    echo "  ✓ 密码策略配置"
    echo ""
    
    print_message $YELLOW "重要提醒:"
    echo "  1. 生产环境请使用正式的 SSL 证书"
    echo "  2. 定期更新密码和证书"
    echo "  3. 监控审计日志"
    echo "  4. 定期进行安全扫描"
    echo ""
}

# 显示帮助信息
show_help() {
    echo "QAUP 安全配置脚本"
    echo ""
    echo "用法: $0 [命令]"
    echo ""
    echo "命令:"
    echo "  all             应用所有安全配置"
    echo "  users           配置非 root 用户"
    echo "  network         配置网络安全"
    echo "  permissions     配置文件权限"
    echo "  ssl             配置 SSL/TLS"
    echo "  firewall        配置防火墙"
    echo "  containers      配置容器安全选项"
    echo "  audit           配置审计日志"
    echo "  password        配置密码策略"
    echo ""
    echo "示例:"
    echo "  $0 all              # 应用所有安全配置"
    echo "  sudo $0 firewall    # 配置防火墙（需要 root）"
    echo "  $0 ssl              # 配置 SSL/TLS"
}

# 主函数
main() {
    if [ $# -eq 0 ]; then
        show_help
        exit 0
    fi
    
    local command=$1
    
    case $command in
        all)
            apply_all_security
            ;;
        users)
            setup_non_root_users
            ;;
        network)
            setup_network_security
            ;;
        permissions)
            setup_file_permissions
            ;;
        ssl)
            setup_ssl_tls
            ;;
        firewall)
            setup_firewall
            ;;
        containers)
            setup_container_security
            ;;
        audit)
            setup_audit_logging
            ;;
        password)
            setup_password_policy
            ;;
        help|--help|-h)
            show_help
            ;;
        *)
            print_message $RED "未知命令: $command"
            show_help
            exit 1
            ;;
    esac
}

main "$@"