042f66499a
- Add missing lucide-react icons (Users, Target, MessageCircle, Layers, CreditCard) - Fix admin/page.test.tsx ESLint errors (add displayName) - Fix api/contact/route.test.ts ESLint errors (remove any types, use import) - Add RESEND_API_KEY environment variable for API tests - All 122 test suites now passing - Test pass rate: 99.8% (1499/1502 passed, 3 skipped)
1120 lines
29 KiB
Markdown
1120 lines
29 KiB
Markdown
# Jenkins生产环境安全加固 - 执行检查清单
|
||
|
||
**作者:** 张翔
|
||
**日期:** 2026-04-07
|
||
**版本:** 1.0
|
||
**执行环境:** 生产服务器
|
||
**预计时间:** 3小时
|
||
|
||
---
|
||
|
||
## 📋 执行前准备
|
||
|
||
### 环境信息确认
|
||
|
||
```bash
|
||
# 记录服务器信息
|
||
SERVER_IP=$(curl -s ifconfig.me)
|
||
SERVER_HOSTNAME=$(hostname)
|
||
CURRENT_TIME=$(date '+%Y-%m-%d %H:%M:%S')
|
||
|
||
echo "========================================"
|
||
echo " Jenkins安全加固执行清单"
|
||
echo "========================================"
|
||
echo "服务器IP: $SERVER_IP"
|
||
echo "主机名: $SERVER_HOSTNAME"
|
||
echo "执行时间: $CURRENT_TIME"
|
||
echo "执行人: $(whoami)"
|
||
echo "========================================"
|
||
```
|
||
|
||
- [ ] 确认服务器IP地址
|
||
- [ ] 确认当前时间
|
||
- [ ] 确认执行人权限(需要root或sudo权限)
|
||
- [ ] 确认SSH连接稳定
|
||
|
||
### 备份当前配置
|
||
|
||
```bash
|
||
# 创建备份目录
|
||
BACKUP_DIR="/tmp/jenkins-security-backup-$(date +%Y%m%d_%H%M%S)"
|
||
mkdir -p "$BACKUP_DIR"
|
||
|
||
# 备份Jenkins配置
|
||
if [ -d "/var/lib/jenkins" ]; then
|
||
cp -r /var/lib/jenkins "$BACKUP_DIR/jenkins-home"
|
||
echo "✓ Jenkins主目录已备份"
|
||
fi
|
||
|
||
# 备份Jenkins配置文件
|
||
if [ -f "/etc/default/jenkins" ]; then
|
||
cp /etc/default/jenkins "$BACKUP_DIR/jenkins-default.bak"
|
||
echo "✓ Jenkins配置文件已备份"
|
||
elif [ -f "/etc/sysconfig/jenkins" ]; then
|
||
cp /etc/sysconfig/jenkins "$BACKUP_DIR/jenkins-sysconfig.bak"
|
||
echo "✓ Jenkins配置文件已备份"
|
||
fi
|
||
|
||
# 备份Nginx配置
|
||
if [ -d "/etc/nginx/conf.d" ]; then
|
||
cp -r /etc/nginx/conf.d "$BACKUP_DIR/nginx-conf"
|
||
echo "✓ Nginx配置已备份"
|
||
fi
|
||
|
||
# 备份防火墙规则
|
||
if command -v ufw &> /dev/null; then
|
||
ufw status numbered > "$BACKUP_DIR/ufw-rules.bak"
|
||
echo "✓ UFW防火墙规则已备份"
|
||
elif command -v firewall-cmd &> /dev/null; then
|
||
firewall-cmd --list-all > "$BACKUP_DIR/firewalld-rules.bak"
|
||
echo "✓ Firewalld防火墙规则已备份"
|
||
fi
|
||
|
||
echo "备份目录: $BACKUP_DIR"
|
||
echo "备份完成时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
```
|
||
|
||
- [ ] Jenkins主目录已备份
|
||
- [ ] Jenkins配置文件已备份
|
||
- [ ] Nginx配置已备份
|
||
- [ ] 防火墙规则已备份
|
||
- [ ] 记录备份目录路径
|
||
|
||
---
|
||
|
||
## 🚨 阶段1:快速响应(15分钟)
|
||
|
||
### 1.1 检查是否已被攻击
|
||
|
||
```bash
|
||
echo "=== 检查Jenkins安全状态 ==="
|
||
|
||
# 检查最近的失败登录
|
||
echo "1. 检查最近的失败登录:"
|
||
sudo journalctl -u jenkins --since "1 hour ago" | grep -i "failed\|error" | tail -20
|
||
|
||
# 检查可疑进程
|
||
echo -e "\n2. 检查可疑进程:"
|
||
ps aux | grep -E "jenkins|java" | grep -v grep
|
||
|
||
# 检查异常文件修改
|
||
echo -e "\n3. 检查最近24小时内修改的文件:"
|
||
sudo find /var/lib/jenkins -type f -mtime -1 -ls 2>/dev/null | head -20
|
||
|
||
# 检查网络连接
|
||
echo -e "\n4. 检查Jenkins网络连接:"
|
||
sudo netstat -tunap | grep 8080
|
||
|
||
# 检查磁盘空间
|
||
echo -e "\n5. 检查磁盘空间:"
|
||
df -h | grep -E "Filesystem|/$|/var"
|
||
```
|
||
|
||
- [ ] 未发现异常登录
|
||
- [ ] 未发现可疑进程
|
||
- [ ] 未发现异常文件修改
|
||
- [ ] 网络连接正常
|
||
- [ ] 磁盘空间充足
|
||
|
||
### 1.2 临时阻止外部访问
|
||
|
||
```bash
|
||
echo "=== 临时阻止外部访问8080端口 ==="
|
||
|
||
# 方法1:使用UFW
|
||
if command -v ufw &> /dev/null; then
|
||
sudo ufw deny 8080/tcp comment 'Jenkins Direct Access - Emergency Block'
|
||
sudo ufw --force reload
|
||
echo "✓ UFW已阻止8080端口"
|
||
|
||
# 方法2:使用Firewalld
|
||
elif command -v firewall-cmd &> /dev/null; then
|
||
sudo firewall-cmd --permanent --remove-port=8080/tcp
|
||
sudo firewall-cmd --reload
|
||
echo "✓ Firewalld已阻止8080端口"
|
||
|
||
# 方法3:使用iptables
|
||
else
|
||
sudo iptables -I INPUT -p tcp --dport 8080 -j DROP
|
||
echo "✓ iptables已阻止8080端口"
|
||
fi
|
||
|
||
# 验证
|
||
echo -e "\n验证防火墙规则:"
|
||
if command -v ufw &> /dev/null; then
|
||
sudo ufw status | grep 8080
|
||
elif command -v firewall-cmd &> /dev/null; then
|
||
sudo firewall-cmd --list-ports | grep 8080 || echo "8080端口已被阻止"
|
||
fi
|
||
```
|
||
|
||
- [ ] 防火墙已阻止8080端口
|
||
- [ ] 验证防火墙规则生效
|
||
|
||
### 1.3 测试外部访问
|
||
|
||
```bash
|
||
echo "=== 测试外部访问是否被阻止 ==="
|
||
|
||
# 从本地测试
|
||
echo "1. 本地测试:"
|
||
curl -I -m 5 http://localhost:8080 2>&1 || echo "✓ 本地访问失败(预期)"
|
||
|
||
# 从外部测试(如果有其他服务器)
|
||
# curl -I -m 5 http://YOUR_SERVER_IP:8080 2>&1 || echo "✓ 外部访问被阻止(预期)"
|
||
|
||
echo -e "\n✓ 快速响应阶段完成"
|
||
```
|
||
|
||
- [ ] 外部访问已被阻止
|
||
|
||
---
|
||
|
||
## 🔧 阶段2:网络层加固(30分钟)
|
||
|
||
### 2.1 修改Jenkins监听地址
|
||
|
||
```bash
|
||
echo "=== 修改Jenkins监听地址 ==="
|
||
|
||
# 检测配置文件位置
|
||
if [ -f "/etc/default/jenkins" ]; then
|
||
JENKINS_CONFIG="/etc/default/jenkins"
|
||
CONFIG_TYPE="debian"
|
||
elif [ -f "/etc/sysconfig/jenkins" ]; then
|
||
JENKINS_CONFIG="/etc/sysconfig/jenkins"
|
||
CONFIG_TYPE="rhel"
|
||
else
|
||
echo "❌ 未找到Jenkins配置文件"
|
||
exit 1
|
||
fi
|
||
|
||
echo "配置文件: $JENKINS_CONFIG"
|
||
|
||
# 备份配置文件
|
||
sudo cp "$JENKINS_CONFIG" "$BACKUP_DIR/jenkins-config-before.bak"
|
||
|
||
# 查看当前配置
|
||
echo -e "\n当前Jenkins配置:"
|
||
grep -E "JENKINS_ARGS|JENKINS_LISTEN_ADDRESS|httpPort" "$JENKINS_CONFIG" || echo "未找到相关配置"
|
||
|
||
# 修改配置
|
||
if [ "$CONFIG_TYPE" = "debian" ]; then
|
||
# Debian/Ubuntu方式
|
||
if grep -q "JENKINS_ARGS" "$JENKINS_CONFIG"; then
|
||
# 如果已有JENKINS_ARGS,添加监听地址
|
||
if grep -q "httpListenAddress" "$JENKINS_CONFIG"; then
|
||
sudo sed -i 's/httpListenAddress=[^ "]*/httpListenAddress=127.0.0.1/' "$JENKINS_CONFIG"
|
||
else
|
||
sudo sed -i '/JENKINS_ARGS=/ s/"$/ --httpListenAddress=127.0.0.1"/' "$JENKINS_CONFIG"
|
||
fi
|
||
else
|
||
# 如果没有JENKINS_ARGS,添加新行
|
||
echo 'JENKINS_ARGS="--httpListenAddress=127.0.0.1"' | sudo tee -a "$JENKINS_CONFIG"
|
||
fi
|
||
else
|
||
# RHEL/CentOS方式
|
||
if grep -q "JENKINS_LISTEN_ADDRESS" "$JENKINS_CONFIG"; then
|
||
sudo sed -i 's/^JENKINS_LISTEN_ADDRESS=.*/JENKINS_LISTEN_ADDRESS="127.0.0.1"/' "$JENKINS_CONFIG"
|
||
else
|
||
echo 'JENKINS_LISTEN_ADDRESS="127.0.0.1"' | sudo tee -a "$JENKINS_CONFIG"
|
||
fi
|
||
fi
|
||
|
||
# 验证修改
|
||
echo -e "\n修改后的配置:"
|
||
grep -E "JENKINS_ARGS|JENKINS_LISTEN_ADDRESS|httpListenAddress" "$JENKINS_CONFIG"
|
||
|
||
echo -e "\n✓ Jenkins配置已修改"
|
||
```
|
||
|
||
- [ ] Jenkins配置文件已备份
|
||
- [ ] Jenkins监听地址已修改为127.0.0.1
|
||
- [ ] 配置修改已验证
|
||
|
||
### 2.2 重启Jenkins服务
|
||
|
||
```bash
|
||
echo "=== 重启Jenkins服务 ==="
|
||
|
||
# 检查Jenkins状态
|
||
echo "当前Jenkins状态:"
|
||
sudo systemctl status jenkins --no-pager
|
||
|
||
# 重启Jenkins
|
||
echo -e "\n重启Jenkins..."
|
||
sudo systemctl restart jenkins
|
||
|
||
# 等待服务启动
|
||
echo "等待Jenkins启动..."
|
||
sleep 10
|
||
|
||
# 检查服务状态
|
||
echo -e "\n检查Jenkins状态:"
|
||
sudo systemctl status jenkins --no-pager
|
||
|
||
# 检查监听地址
|
||
echo -e "\n检查监听地址:"
|
||
sudo netstat -tlnp | grep 8080
|
||
|
||
# 应该显示: tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN <pid>/java
|
||
```
|
||
|
||
- [ ] Jenkins服务已重启
|
||
- [ ] Jenkins服务状态正常
|
||
- [ ] 监听地址为127.0.0.1:8080
|
||
|
||
### 2.3 配置防火墙规则
|
||
|
||
```bash
|
||
echo "=== 配置防火墙规则 ==="
|
||
|
||
# UFW配置
|
||
if command -v ufw &> /dev/null; then
|
||
echo "使用UFW配置防火墙..."
|
||
|
||
# 启用UFW
|
||
sudo ufw --force enable
|
||
|
||
# 设置默认策略
|
||
sudo ufw default deny incoming
|
||
sudo ufw default allow outgoing
|
||
|
||
# 允许必要端口
|
||
sudo ufw allow 22/tcp comment 'SSH Access'
|
||
sudo ufw allow 80/tcp comment 'HTTP Access'
|
||
sudo ufw allow 443/tcp comment 'HTTPS Access'
|
||
|
||
# 确保阻止8080端口
|
||
sudo ufw deny 8080/tcp comment 'Jenkins Direct Access Blocked'
|
||
|
||
# 重载防火墙
|
||
sudo ufw --force reload
|
||
|
||
# 显示状态
|
||
sudo ufw status numbered
|
||
|
||
# Firewalld配置
|
||
elif command -v firewall-cmd &> /dev/null; then
|
||
echo "使用Firewalld配置防火墙..."
|
||
|
||
# 启动并启用
|
||
sudo systemctl start firewalld
|
||
sudo systemctl enable firewalld
|
||
|
||
# 允许必要服务
|
||
sudo firewall-cmd --permanent --add-service=ssh
|
||
sudo firewall-cmd --permanent --add-service=http
|
||
sudo firewall-cmd --permanent --add-service=https
|
||
|
||
# 确保移除8080端口
|
||
sudo firewall-cmd --permanent --remove-port=8080/tcp
|
||
|
||
# 重载防火墙
|
||
sudo firewall-cmd --reload
|
||
|
||
# 显示状态
|
||
sudo firewall-cmd --list-all
|
||
fi
|
||
|
||
echo -e "\n✓ 防火墙规则已配置"
|
||
```
|
||
|
||
- [ ] 防火墙已启用
|
||
- [ ] SSH端口已开放
|
||
- [ ] HTTP/HTTPS端口已开放
|
||
- [ ] 8080端口已阻止
|
||
- [ ] 防火墙规则已验证
|
||
|
||
### 2.4 验证网络隔离
|
||
|
||
```bash
|
||
echo "=== 验证网络隔离 ==="
|
||
|
||
# 检查Jenkins监听地址
|
||
echo "1. 检查Jenkins监听地址:"
|
||
sudo netstat -tlnp | grep 8080
|
||
# 预期: 127.0.0.1:8080
|
||
|
||
# 尝试从外部IP访问(应该失败)
|
||
echo -e "\n2. 尝试从外部IP访问:"
|
||
curl -I -m 5 --interface $(ip route | grep default | awk '{print $5}' | head -1) http://localhost:8080 2>&1 || echo "✓ 外部访问被阻止(预期)"
|
||
|
||
# 检查防火墙规则
|
||
echo -e "\n3. 检查防火墙规则:"
|
||
if command -v ufw &> /dev/null; then
|
||
sudo ufw status | grep 8080
|
||
elif command -v firewall-cmd &> /dev/null; then
|
||
sudo firewall-cmd --list-ports | grep 8080 || echo "8080端口未开放(预期)"
|
||
fi
|
||
|
||
echo -e "\n✓ 网络隔离验证完成"
|
||
```
|
||
|
||
- [ ] Jenkins仅监听127.0.0.1
|
||
- [ ] 外部访问被阻止
|
||
- [ ] 防火墙规则正确
|
||
|
||
---
|
||
|
||
## 🔐 阶段3:应用层防护(45分钟)
|
||
|
||
### 3.1 生成HTTP Basic Auth密码
|
||
|
||
```bash
|
||
echo "=== 生成HTTP Basic Auth密码 ==="
|
||
|
||
# 设置管理员用户名
|
||
ADMIN_USER="admin"
|
||
|
||
# 提示输入密码
|
||
echo "请设置Jenkins访问密码:"
|
||
read -s JENKINS_PASSWORD
|
||
echo ""
|
||
echo "请再次确认密码:"
|
||
read -s JENKINS_PASSWORD_CONFIRM
|
||
echo ""
|
||
|
||
# 验证密码
|
||
if [ "$JENKINS_PASSWORD" != "$JENKINS_PASSWORD_CONFIRM" ]; then
|
||
echo "❌ 两次密码输入不一致"
|
||
exit 1
|
||
fi
|
||
|
||
if [ -z "$JENKINS_PASSWORD" ]; then
|
||
echo "❌ 密码不能为空"
|
||
exit 1
|
||
fi
|
||
|
||
# 创建密码文件
|
||
HTPASSWD_FILE="/etc/nginx/conf.d/.jenkins-htpasswd"
|
||
|
||
# 使用htpasswd或openssl生成密码
|
||
if command -v htpasswd &> /dev/null; then
|
||
sudo htpasswd -bc "$HTPASSWD_FILE" "$ADMIN_USER" "$JENKINS_PASSWORD"
|
||
else
|
||
# 使用openssl生成
|
||
SALT=$(openssl rand -base64 3)
|
||
HASH=$(openssl passwd -apr1 -salt "$SALT" "$JENKINS_PASSWORD")
|
||
echo "$ADMIN_USER:$HASH" | sudo tee "$HTPASSWD_FILE"
|
||
fi
|
||
|
||
# 设置权限
|
||
sudo chmod 600 "$HTPASSWD_FILE"
|
||
sudo chown www-data:www-data "$HTPASSWD_FILE" 2>/dev/null || sudo chown nginx:nginx "$HTPASSWD_FILE"
|
||
|
||
echo "✓ HTTP Basic Auth密码文件已生成: $HTPASSWD_FILE"
|
||
|
||
# 记录密码(仅用于本次执行)
|
||
echo "管理员用户: $ADMIN_USER" | sudo tee "$BACKUP_DIR/admin-credentials.txt"
|
||
echo "密码文件: $HTPASSWD_FILE" | sudo tee -a "$BACKUP_DIR/admin-credentials.txt"
|
||
```
|
||
|
||
- [ ] 管理员密码已设置
|
||
- [ ] HTTP Basic Auth密码文件已生成
|
||
- [ ] 密码文件权限已设置
|
||
|
||
### 3.2 获取域名和SSL证书信息
|
||
|
||
```bash
|
||
echo "=== 获取域名和SSL证书信息 ==="
|
||
|
||
# 提示输入域名
|
||
echo "请输入Jenkins访问域名(例如: jenkins.example.com):"
|
||
read DOMAIN
|
||
|
||
if [ -z "$DOMAIN" ]; then
|
||
echo "❌ 域名不能为空"
|
||
exit 1
|
||
fi
|
||
|
||
# 检查SSL证书
|
||
echo -e "\n检查SSL证书..."
|
||
if [ -f "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" ]; then
|
||
SSL_CERT="/etc/letsencrypt/live/$DOMAIN/fullchain.pem"
|
||
SSL_KEY="/etc/letsencrypt/live/$DOMAIN/privkey.pem"
|
||
echo "✓ 找到Let's Encrypt证书"
|
||
elif [ -f "/etc/nginx/ssl/$DOMAIN.crt" ]; then
|
||
SSL_CERT="/etc/nginx/ssl/$DOMAIN.crt"
|
||
SSL_KEY="/etc/nginx/ssl/$DOMAIN.key"
|
||
echo "✓ 找到自签名证书"
|
||
else
|
||
echo "⚠️ 未找到SSL证书,将使用HTTP配置"
|
||
SSL_CERT=""
|
||
SSL_KEY=""
|
||
fi
|
||
|
||
# 显示证书信息
|
||
if [ -n "$SSL_CERT" ]; then
|
||
echo -e "\n证书信息:"
|
||
sudo openssl x509 -in "$SSL_CERT" -noout -subject -dates
|
||
fi
|
||
|
||
echo "域名: $DOMAIN" | sudo tee -a "$BACKUP_DIR/deployment-info.txt"
|
||
```
|
||
|
||
- [ ] 域名已确认
|
||
- [ ] SSL证书状态已检查
|
||
|
||
### 3.3 配置Nginx反向代理
|
||
|
||
```bash
|
||
echo "=== 配置Nginx反向代理 ==="
|
||
|
||
NGINX_CONF_FILE="/etc/nginx/conf.d/jenkins-security.conf"
|
||
|
||
# 创建Nginx配置
|
||
sudo tee "$NGINX_CONF_FILE" > /dev/null << 'NGINX_CONF_EOF'
|
||
# Jenkins安全反向代理配置
|
||
# 作者: 张翔
|
||
# 日期: 2026-04-07
|
||
# 说明: 多层安全防护 - 认证、频率限制、审计日志
|
||
|
||
# 上游Jenkins服务
|
||
upstream jenkins_backend {
|
||
server 127.0.0.1:8080;
|
||
keepalive 32;
|
||
}
|
||
|
||
# 频率限制区域
|
||
limit_req_zone $binary_remote_addr zone=jenkins_limit:10m rate=10r/m;
|
||
limit_conn_zone $binary_remote_addr zone=jenkins_conn:10m;
|
||
|
||
# 日志格式(包含安全审计信息)
|
||
log_format jenkins_security '$remote_addr - $remote_user [$time_local] '
|
||
'"$request" $status $body_bytes_sent '
|
||
'"$http_referer" "$http_user_agent" '
|
||
'request_time=$request_time '
|
||
'upstream_response_time=$upstream_response_time '
|
||
'ssl_protocol=$ssl_protocol '
|
||
'ssl_cipher=$ssl_cipher';
|
||
|
||
# HTTP重定向到HTTPS(如果有SSL证书)
|
||
server {
|
||
listen 80;
|
||
server_name DOMAIN_PLACEHOLDER;
|
||
|
||
# Let's Encrypt验证路径
|
||
location ^~ /.well-known/acme-challenge/ {
|
||
default_type "text/plain";
|
||
root /var/www/letsencrypt;
|
||
}
|
||
|
||
location / {
|
||
return 301 https://$server_name$request_uri;
|
||
}
|
||
}
|
||
|
||
# HTTPS主配置(如果有SSL证书)
|
||
server {
|
||
listen 443 ssl http2;
|
||
server_name DOMAIN_PLACEHOLDER;
|
||
|
||
# SSL配置
|
||
ssl_certificate SSL_CERT_PLACEHOLDER;
|
||
ssl_certificate_key SSL_KEY_PLACEHOLDER;
|
||
ssl_protocols TLSv1.2 TLSv1.3;
|
||
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-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; preload" 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;
|
||
|
||
# 访问日志
|
||
access_log /var/log/nginx/jenkins-access.log jenkins_security;
|
||
error_log /var/log/nginx/jenkins-error.log warn;
|
||
|
||
# 频率限制
|
||
limit_req zone=jenkins_limit burst=20 nodelay;
|
||
limit_conn jenkins_conn 10;
|
||
|
||
# 客户端请求限制
|
||
client_max_body_size 100m;
|
||
client_body_timeout 60s;
|
||
client_header_timeout 60s;
|
||
|
||
# Webhook端点(IP白名单)
|
||
location ~ ^/generic-webhook-trigger(/.*)?$ {
|
||
# IP白名单(需要配置)
|
||
# allow GITEA_SERVER_IP;
|
||
# deny all;
|
||
|
||
# 代理到Jenkins
|
||
proxy_pass http://jenkins_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_set_header X-Forwarded-Port $server_port;
|
||
|
||
proxy_connect_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_read_timeout 60s;
|
||
}
|
||
|
||
# Jenkins主界面(需要认证)
|
||
location /jenkins/ {
|
||
# HTTP Basic Auth
|
||
auth_basic "Jenkins Production Access";
|
||
auth_basic_user_file HTPASSWD_FILE_PLACEHOLDER;
|
||
|
||
# 代理到Jenkins
|
||
proxy_pass http://jenkins_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_set_header X-Forwarded-Port $server_port;
|
||
|
||
proxy_connect_timeout 60s;
|
||
proxy_send_timeout 60s;
|
||
proxy_read_timeout 60s;
|
||
|
||
# WebSocket支持
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
}
|
||
|
||
# 默认拒绝其他路径
|
||
location / {
|
||
return 404;
|
||
}
|
||
}
|
||
NGINX_CONF_EOF
|
||
|
||
# 替换占位符
|
||
sudo sed -i "s|DOMAIN_PLACEHOLDER|$DOMAIN|g" "$NGINX_CONF_FILE"
|
||
sudo sed -i "s|HTPASSWD_FILE_PLACEHOLDER|$HTPASSWD_FILE|g" "$NGINX_CONF_FILE"
|
||
|
||
if [ -n "$SSL_CERT" ]; then
|
||
sudo sed -i "s|SSL_CERT_PLACEHOLDER|$SSL_CERT|g" "$NGINX_CONF_FILE"
|
||
sudo sed -i "s|SSL_KEY_PLACEHOLDER|$SSL_KEY|g" "$NGINX_CONF_FILE"
|
||
else
|
||
# 如果没有SSL证书,注释掉HTTPS配置,使用HTTP
|
||
echo "⚠️ 未配置SSL证书,将使用HTTP配置"
|
||
# 这里需要调整配置,暂时跳过
|
||
fi
|
||
|
||
echo "✓ Nginx配置文件已创建: $NGINX_CONF_FILE"
|
||
```
|
||
|
||
- [ ] Nginx配置文件已创建
|
||
- [ ] 域名已配置
|
||
- [ ] HTTP Basic Auth已配置
|
||
- [ ] SSL证书已配置(如有)
|
||
|
||
### 3.4 测试Nginx配置
|
||
|
||
```bash
|
||
echo "=== 测试Nginx配置 ==="
|
||
|
||
# 测试配置语法
|
||
sudo nginx -t
|
||
|
||
if [ $? -eq 0 ]; then
|
||
echo "✓ Nginx配置语法正确"
|
||
else
|
||
echo "❌ Nginx配置存在错误,请检查"
|
||
exit 1
|
||
fi
|
||
|
||
# 创建日志目录
|
||
sudo mkdir -p /var/log/nginx
|
||
sudo touch /var/log/nginx/jenkins-access.log
|
||
sudo touch /var/log/nginx/jenkins-error.log
|
||
|
||
# 重启Nginx
|
||
echo -e "\n重启Nginx..."
|
||
sudo systemctl restart nginx
|
||
|
||
# 检查Nginx状态
|
||
sudo systemctl status nginx --no-pager
|
||
|
||
echo -e "\n✓ Nginx配置完成"
|
||
```
|
||
|
||
- [ ] Nginx配置测试通过
|
||
- [ ] Nginx服务已重启
|
||
- [ ] Nginx状态正常
|
||
|
||
---
|
||
|
||
## 🔑 阶段4:认证授权层(30分钟)
|
||
|
||
### 4.1 配置Jenkins安全设置
|
||
|
||
```bash
|
||
echo "=== 配置Jenkins安全设置 ==="
|
||
|
||
JENKINS_CONFIG_XML="/var/lib/jenkins/config.xml"
|
||
|
||
if [ -f "$JENKINS_CONFIG_XML" ]; then
|
||
# 备份配置文件
|
||
sudo cp "$JENKINS_CONFIG_XML" "$BACKUP_DIR/config.xml.bak"
|
||
|
||
# 检查当前安全配置
|
||
echo "当前Jenkins安全配置:"
|
||
grep -A 5 "<useSecurity>" "$JENKINS_CONFIG_XML" || echo "未找到安全配置"
|
||
|
||
# 注意: Jenkins安全配置通常通过Web UI配置
|
||
# 这里仅做检查,实际配置建议通过Web UI完成
|
||
|
||
echo -e "\n⚠️ 请通过Web UI配置Jenkins安全设置:"
|
||
echo "1. 访问: https://$DOMAIN/jenkins/configureSecurity"
|
||
echo "2. 启用安全: 勾选'启用安全'"
|
||
echo "3. 授权策略: 选择'安全矩阵'"
|
||
echo "4. 取消匿名用户的所有权限"
|
||
echo "5. 保存配置"
|
||
|
||
echo -e "\n✓ Jenkins配置文件已备份"
|
||
else
|
||
echo "⚠️ 未找到Jenkins配置文件"
|
||
fi
|
||
```
|
||
|
||
- [ ] Jenkins配置已备份
|
||
- [ ] 已了解Web UI配置步骤
|
||
|
||
### 4.2 配置Webhook Token
|
||
|
||
```bash
|
||
echo "=== 配置Webhook Token ==="
|
||
|
||
# 生成新的Webhook密钥
|
||
WEBHOOK_SECRET=$(openssl rand -hex 32)
|
||
|
||
echo "新的Webhook密钥: $WEBHOOK_SECRET"
|
||
|
||
# 保存密钥
|
||
echo "WEBHOOK_SECRET=$WEBHOOK_SECRET" | sudo tee "$BACKUP_DIR/webhook-secret.txt"
|
||
|
||
# 检查Jenkinsfile中的硬编码token
|
||
echo -e "\n检查Jenkinsfile中的硬编码token..."
|
||
if [ -f "Jenkinsfile" ]; then
|
||
if grep -q "token.*=.*['\"].*['\"]" Jenkinsfile; then
|
||
echo "⚠️ 发现硬编码token,需要替换为环境变量:"
|
||
grep -n "token.*=.*['\"].*['\"]" Jenkinsfile
|
||
|
||
echo -e "\n建议修改为:"
|
||
echo "token = env.WEBHOOK_TOKEN"
|
||
echo ""
|
||
echo "并在Jenkins中配置环境变量 WEBHOOK_TOKEN"
|
||
else
|
||
echo "✓ 未发现硬编码token"
|
||
fi
|
||
fi
|
||
|
||
# 配置Jenkins环境变量
|
||
echo -e "\n配置Jenkins环境变量..."
|
||
echo "请在Jenkins Web UI中配置:"
|
||
echo "1. 访问: https://$DOMAIN/jenkins/configure"
|
||
echo "2. 找到'全局属性' -> '环境变量'"
|
||
echo "3. 添加键值对:"
|
||
echo " 键: WEBHOOK_TOKEN"
|
||
echo " 值: $WEBHOOK_SECRET"
|
||
echo "4. 保存配置"
|
||
|
||
echo -e "\n✓ Webhook密钥已生成"
|
||
```
|
||
|
||
- [ ] Webhook密钥已生成
|
||
- [ ] Jenkinsfile已检查
|
||
- [ ] 已了解环境变量配置步骤
|
||
|
||
### 4.3 配置IP白名单
|
||
|
||
```bash
|
||
echo "=== 配置IP白名单 ==="
|
||
|
||
# 获取Gitea服务器IP
|
||
echo "请输入Gitea服务器IP地址(用于Webhook白名单):"
|
||
read GITEA_IP
|
||
|
||
if [ -n "$GITEA_IP" ]; then
|
||
# 更新Nginx配置
|
||
NGINX_CONF_FILE="/etc/nginx/conf.d/jenkins-security.conf"
|
||
|
||
# 添加IP白名单规则
|
||
sudo sed -i "s|# allow GITEA_SERVER_IP;|allow $GITEA_IP;|g" "$NGINX_CONF_FILE"
|
||
sudo sed -i "s|# deny all;|deny all;|g" "$NGINX_CONF_FILE"
|
||
|
||
echo "✓ IP白名单已配置: $GITEA_IP"
|
||
|
||
# 测试Nginx配置
|
||
sudo nginx -t && sudo systemctl reload nginx
|
||
else
|
||
echo "⚠️ 未配置IP白名单,Webhook端点将允许所有IP访问"
|
||
fi
|
||
|
||
# 记录配置
|
||
echo "Gitea IP: $GITEA_IP" | sudo tee -a "$BACKUP_DIR/deployment-info.txt"
|
||
```
|
||
|
||
- [ ] Gitea服务器IP已配置
|
||
- [ ] Nginx IP白名单已更新
|
||
- [ ] Nginx配置已重载
|
||
|
||
---
|
||
|
||
## 📊 阶段5:审计监控层(20分钟)
|
||
|
||
### 5.1 配置日志轮转
|
||
|
||
```bash
|
||
echo "=== 配置日志轮转 ==="
|
||
|
||
LOGROTATE_CONF="/etc/logrotate.d/jenkins-security"
|
||
|
||
sudo tee "$LOGROTATE_CONF" > /dev/null << 'EOF'
|
||
/var/log/nginx/jenkins-*.log {
|
||
daily
|
||
rotate 90
|
||
compress
|
||
delaycompress
|
||
missingok
|
||
notifempty
|
||
create 0640 www-data adm
|
||
sharedscripts
|
||
postrotate
|
||
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
|
||
endscript
|
||
}
|
||
EOF
|
||
|
||
echo "✓ 日志轮转配置已创建: $LOGROTATE_CONF"
|
||
|
||
# 测试配置
|
||
sudo logrotate -d "$LOGROTATE_CONF"
|
||
```
|
||
|
||
- [ ] 日志轮转配置已创建
|
||
- [ ] 日志轮转配置已测试
|
||
|
||
### 5.2 创建监控脚本
|
||
|
||
```bash
|
||
echo "=== 创建监控脚本 ==="
|
||
|
||
MONITOR_SCRIPT="/usr/local/bin/monitor-jenkins-security.sh"
|
||
|
||
sudo tee "$MONITOR_SCRIPT" > /dev/null << 'EOF'
|
||
#!/bin/bash
|
||
# Jenkins安全监控脚本
|
||
# 作者: 张翔
|
||
# 用途: 监控Jenkins安全状态
|
||
|
||
LOG_FILE="/var/log/nginx/jenkins-access.log"
|
||
ALERT_THRESHOLD=10
|
||
|
||
# 检查失败的认证尝试
|
||
echo "=== 检查失败的认证尝试 ==="
|
||
FAILED_AUTH=$(grep " 401 " "$LOG_FILE" | tail -n 100 | awk '{print $1}' | sort | uniq -c | awk -v threshold=$ALERT_THRESHOLD '$1 > threshold {print $1, $2}')
|
||
|
||
if [ -n "$FAILED_AUTH" ]; then
|
||
echo "⚠️ 检测到多次认证失败的IP:"
|
||
echo "$FAILED_AUTH"
|
||
else
|
||
echo "✓ 未发现异常认证失败"
|
||
fi
|
||
|
||
# 检查异常请求
|
||
echo -e "\n=== 检查异常请求 ==="
|
||
ABNORMAL_REQUESTS=$(grep -E "POST|DELETE|PUT" "$LOG_FILE" | tail -n 100 | grep -v " 200 \| 201 " | awk '{print $1, $7, $9}')
|
||
|
||
if [ -n "$ABNORMAL_REQUESTS" ]; then
|
||
echo "⚠️ 检测到异常请求:"
|
||
echo "$ABNORMAL_REQUESTS"
|
||
else
|
||
echo "✓ 未发现异常请求"
|
||
fi
|
||
|
||
# 检查Jenkins服务状态
|
||
echo -e "\n=== 检查Jenkins服务状态 ==="
|
||
if systemctl is-active --quiet jenkins; then
|
||
echo "✓ Jenkins服务运行正常"
|
||
else
|
||
echo "❌ Jenkins服务未运行"
|
||
fi
|
||
|
||
# 检查Nginx服务状态
|
||
echo -e "\n=== 检查Nginx服务状态 ==="
|
||
if systemctl is-active --quiet nginx; then
|
||
echo "✓ Nginx服务运行正常"
|
||
else
|
||
echo "❌ Nginx服务未运行"
|
||
fi
|
||
|
||
# 检查磁盘空间
|
||
echo -e "\n=== 检查磁盘空间 ==="
|
||
DISK_USAGE=$(df -h /var | tail -1 | awk '{print $5}' | sed 's/%//')
|
||
if [ "$DISK_USAGE" -gt 80 ]; then
|
||
echo "⚠️ 磁盘使用率: ${DISK_USAGE}%"
|
||
else
|
||
echo "✓ 磁盘使用率: ${DISK_USAGE}%"
|
||
fi
|
||
EOF
|
||
|
||
sudo chmod +x "$MONITOR_SCRIPT"
|
||
|
||
echo "✓ 监控脚本已创建: $MONITOR_SCRIPT"
|
||
|
||
# 运行一次监控
|
||
sudo "$MONITOR_SCRIPT"
|
||
```
|
||
|
||
- [ ] 监控脚本已创建
|
||
- [ ] 监控脚本已测试
|
||
|
||
### 5.3 配置定时任务
|
||
|
||
```bash
|
||
echo "=== 配置定时任务 ==="
|
||
|
||
# 添加到crontab
|
||
(crontab -l 2>/dev/null; echo "# Jenkins安全监控 - 每小时执行一次"; echo "0 * * * * $MONITOR_SCRIPT >> /var/log/jenkins-security-monitor.log 2>&1") | crontab -
|
||
|
||
# 显示当前crontab
|
||
echo "当前定时任务:"
|
||
crontab -l
|
||
|
||
echo -e "\n✓ 定时任务已配置"
|
||
```
|
||
|
||
- [ ] 定时任务已配置
|
||
- [ ] 定时任务已验证
|
||
|
||
---
|
||
|
||
## ✅ 阶段6:验证与测试(30分钟)
|
||
|
||
### 6.1 运行安全验证脚本
|
||
|
||
```bash
|
||
echo "=== 运行安全验证脚本 ==="
|
||
|
||
# 如果已有验证脚本
|
||
if [ -f "/usr/local/bin/verify-jenkins-security.sh" ]; then
|
||
sudo /usr/local/bin/verify-jenkins-security.sh
|
||
else
|
||
echo "⚠️ 验证脚本不存在,执行手动验证"
|
||
fi
|
||
```
|
||
|
||
- [ ] 安全验证脚本已运行
|
||
- [ ] 所有检查项通过
|
||
|
||
### 6.2 手动验证清单
|
||
|
||
#### 网络层验证
|
||
|
||
```bash
|
||
echo "=== 网络层验证 ==="
|
||
|
||
# 1. 检查Jenkins监听地址
|
||
echo "1. 检查Jenkins监听地址:"
|
||
sudo netstat -tlnp | grep 8080
|
||
# 预期: 127.0.0.1:8080
|
||
|
||
# 2. 尝试外部访问
|
||
echo -e "\n2. 尝试外部访问8080端口:"
|
||
curl -I -m 5 http://localhost:8080 2>&1 || echo "✓ 外部访问被阻止"
|
||
|
||
# 3. 检查防火墙
|
||
echo -e "\n3. 检查防火墙规则:"
|
||
if command -v ufw &> /dev/null; then
|
||
sudo ufw status | grep 8080
|
||
elif command -v firewall-cmd &> /dev/null; then
|
||
sudo firewall-cmd --list-ports | grep 8080 || echo "✓ 8080端口未开放"
|
||
fi
|
||
```
|
||
|
||
- [ ] Jenkins仅监听127.0.0.1
|
||
- [ ] 外部访问被阻止
|
||
- [ ] 防火墙规则正确
|
||
|
||
#### 应用层验证
|
||
|
||
```bash
|
||
echo "=== 应用层验证 ==="
|
||
|
||
# 1. 测试Nginx配置
|
||
echo "1. 测试Nginx配置:"
|
||
sudo nginx -t
|
||
|
||
# 2. 测试匿名访问
|
||
echo -e "\n2. 测试匿名访问(应返回401):"
|
||
curl -I -k https://$DOMAIN/jenkins/ 2>&1 | grep "HTTP"
|
||
|
||
# 3. 测试认证访问
|
||
echo -e "\n3. 测试认证访问(应返回200):"
|
||
curl -I -k -u "$ADMIN_USER:$JENKINS_PASSWORD" https://$DOMAIN/jenkins/ 2>&1 | grep "HTTP"
|
||
|
||
# 4. 测试错误密码
|
||
echo -e "\n4. 测试错误密码(应返回401):"
|
||
curl -I -k -u "admin:wrongpassword" https://$DOMAIN/jenkins/ 2>&1 | grep "HTTP"
|
||
```
|
||
|
||
- [ ] Nginx配置正确
|
||
- [ ] 匿名访问返回401
|
||
- [ ] 认证访问返回200
|
||
- [ ] 错误密码返回401
|
||
|
||
#### 认证层验证
|
||
|
||
```bash
|
||
echo "=== 认证层验证 ==="
|
||
|
||
# 1. 测试Webhook签名验证
|
||
echo "1. 测试Webhook签名验证:"
|
||
PAYLOAD='{"ref": "refs/heads/release/test"}'
|
||
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | awk '{print $2}')
|
||
|
||
# 无签名请求(应失败)
|
||
echo "无签名请求:"
|
||
curl -X POST -k "https://$DOMAIN/generic-webhook-trigger/invoke" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$PAYLOAD" 2>&1
|
||
|
||
# 有签名请求(应成功)
|
||
echo -e "\n有签名请求:"
|
||
curl -X POST -k "https://$DOMAIN/generic-webhook-trigger/invoke" \
|
||
-H "Content-Type: application/json" \
|
||
-H "X-Gitea-Signature: sha256=$SIGNATURE" \
|
||
-d "$PAYLOAD" 2>&1
|
||
|
||
# 2. 测试IP白名单
|
||
echo -e "\n2. 测试IP白名单:"
|
||
echo "请从非白名单IP测试Webhook访问,应被拒绝"
|
||
```
|
||
|
||
- [ ] Webhook签名验证生效
|
||
- [ ] IP白名单生效
|
||
|
||
#### 审计层验证
|
||
|
||
```bash
|
||
echo "=== 审计层验证 ==="
|
||
|
||
# 1. 检查访问日志
|
||
echo "1. 检查访问日志:"
|
||
tail -20 /var/log/nginx/jenkins-access.log
|
||
|
||
# 2. 检查日志轮转配置
|
||
echo -e "\n2. 检查日志轮转配置:"
|
||
cat /etc/logrotate.d/jenkins-security
|
||
|
||
# 3. 检查监控脚本
|
||
echo -e "\n3. 检查监控脚本:"
|
||
ls -lh /usr/local/bin/monitor-jenkins-security.sh
|
||
```
|
||
|
||
- [ ] 访问日志正常记录
|
||
- [ ] 日志轮转配置正确
|
||
- [ ] 监控脚本存在
|
||
|
||
### 6.3 CI/CD验证
|
||
|
||
```bash
|
||
echo "=== CI/CD验证 ==="
|
||
|
||
# 1. 手动触发Jenkins构建
|
||
echo "1. 手动触发Jenkins构建:"
|
||
echo "请访问: https://$DOMAIN/jenkins/"
|
||
echo "使用用户名: $ADMIN_USER 和设置的密码登录"
|
||
echo "手动触发一个构建任务"
|
||
|
||
# 2. 测试Webhook触发
|
||
echo -e "\n2. 测试Webhook触发:"
|
||
echo "请在Gitea中推送代码到release分支,验证Webhook是否触发构建"
|
||
|
||
# 3. 检查构建日志
|
||
echo -e "\n3. 检查构建日志:"
|
||
echo "请检查Jenkins构建日志,确认构建成功"
|
||
```
|
||
|
||
- [ ] 手动触发构建成功
|
||
- [ ] Webhook触发构建成功
|
||
- [ ] 构建产物正常部署
|
||
|
||
---
|
||
|
||
## 📝 执行总结
|
||
|
||
### 完成情况
|
||
|
||
```bash
|
||
echo "========================================"
|
||
echo " Jenkins安全加固执行总结"
|
||
echo "========================================"
|
||
echo ""
|
||
echo "执行时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "服务器: $SERVER_HOSTNAME ($SERVER_IP)"
|
||
echo "域名: $DOMAIN"
|
||
echo ""
|
||
echo "备份目录: $BACKUP_DIR"
|
||
echo ""
|
||
echo "重要信息:"
|
||
echo "- 管理员用户: $ADMIN_USER"
|
||
echo "- Jenkins访问地址: https://$DOMAIN/jenkins/"
|
||
echo "- Webhook密钥: 已保存在 $BACKUP_DIR/webhook-secret.txt"
|
||
echo ""
|
||
echo "后续步骤:"
|
||
echo "1. 通过Web UI配置Jenkins安全设置"
|
||
echo "2. 在Jenkins中配置环境变量 WEBHOOK_TOKEN"
|
||
echo "3. 更新Jenkinsfile中的token配置"
|
||
echo "4. 配置SSL证书(如未配置)"
|
||
echo "5. 设置定期安全审计"
|
||
echo ""
|
||
echo "监控命令:"
|
||
echo "- 查看访问日志: tail -f /var/log/nginx/jenkins-access.log"
|
||
echo "- 运行监控脚本: sudo /usr/local/bin/monitor-jenkins-security.sh"
|
||
echo "- 检查服务状态: sudo systemctl status jenkins nginx"
|
||
echo ""
|
||
echo "========================================"
|
||
```
|
||
|
||
### 验收确认
|
||
|
||
- [ ] 所有阶段已完成
|
||
- [ ] 所有验证项通过
|
||
- [ ] CI/CD流水线正常
|
||
- [ ] 文档已更新
|
||
- [ ] 团队已通知
|
||
|
||
---
|
||
|
||
## 🚨 应急回滚
|
||
|
||
如果出现问题,执行以下回滚操作:
|
||
|
||
```bash
|
||
echo "=== 执行回滚 ==="
|
||
|
||
# 1. 恢复Jenkins配置
|
||
sudo cp "$BACKUP_DIR/jenkins-default.bak" /etc/default/jenkins
|
||
|
||
# 2. 恢复Nginx配置
|
||
sudo cp -r "$BACKUP_DIR/nginx-conf"/* /etc/nginx/conf.d/
|
||
|
||
# 3. 重启服务
|
||
sudo systemctl restart jenkins
|
||
sudo systemctl restart nginx
|
||
|
||
# 4. 开放8080端口(仅应急)
|
||
sudo ufw allow 8080/tcp
|
||
|
||
echo "✓ 回滚完成"
|
||
```
|
||
|
||
---
|
||
|
||
**执行状态:** ⏳ 待执行
|
||
**最后更新:** 2026-04-07
|