debian 开发,modiqi 修复 cf_load_profile 缺失问题。 9 个命令:cf-api/cf-zone/cf-dns/cf-cache/cf-ssl/cf-fw/cf-pagerule/cf-analytics/cf-hostname 支持多账号、域名自动解析 zone_id。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
175 lines
6.7 KiB
Bash
Executable file
175 lines
6.7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
# cf-fw — Cloudflare 防火墙/安全规则管理
|
||
# 用法: cf-fw rules <domain> 列出防火墙规则
|
||
# cf-fw waf <domain> WAF 托管规则状态
|
||
# cf-fw access-rules <domain> IP 访问规则
|
||
# cf-fw block <domain> <ip> [note] 封禁 IP
|
||
# cf-fw unblock <domain> <rule_id> 解封
|
||
# cf-fw whitelist <domain> <ip> [note] 白名单 IP
|
||
# cf-fw challenge <domain> <ip> [note] JS Challenge
|
||
# cf-fw security-level <domain> [level] 安全级别
|
||
# cf-fw ua-rules <domain> UA 规则列表
|
||
# cf-fw lockdown <domain> Zone Lockdown 规则
|
||
# 选项: -p <profile> 指定 profile
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||
source "$SCRIPT_DIR/cf-lib.sh"
|
||
|
||
cf_parse_profile "$@"
|
||
set -- "${CF_REMAINING_ARGS[@]}"
|
||
cf_load_profile
|
||
|
||
usage() {
|
||
cat >&2 <<'EOF'
|
||
用法: cf-fw <command> <domain> [args...]
|
||
命令:
|
||
rules <domain> 防火墙规则列表
|
||
access-rules <domain> IP 访问规则列表
|
||
block <domain> <ip> [note] 封禁 IP
|
||
unblock <domain> <rule_id> 解封(删除访问规则)
|
||
whitelist <domain> <ip> [note] 白名单 IP
|
||
challenge <domain> <ip> [note] JS Challenge
|
||
security-level <domain> [level] 安全级别(off/essentially_off/low/medium/high/under_attack)
|
||
ua-rules <domain> User-Agent 规则
|
||
lockdown <domain> Zone Lockdown 规则
|
||
rate-limits <domain> 速率限制规则
|
||
EOF
|
||
exit 1
|
||
}
|
||
|
||
[[ $# -lt 1 ]] && usage
|
||
|
||
CMD="$1"; shift
|
||
|
||
# 创建 IP 访问规则的通用函数
|
||
create_access_rule() {
|
||
local zone_id="$1" mode="$2" ip="$3" note="${4:-}"
|
||
local target_type="ip"
|
||
# 检测是否为 CIDR
|
||
[[ "$ip" == *"/"* ]] && target_type="ip_range"
|
||
local data
|
||
data=$(jq -n --arg m "$mode" --arg t "$target_type" --arg v "$ip" --arg n "$note" \
|
||
'{mode:$m, configuration:{target:$t, value:$v}, notes:$n}')
|
||
cf_call POST "/zones/$zone_id/firewall/access_rules/rules" -d "$data"
|
||
}
|
||
|
||
case "$CMD" in
|
||
rules)
|
||
[[ $# -lt 1 ]] && { echo "用法: cf-fw rules <domain>" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
resp=$(cf_call GET "/zones/$ZONE_ID/firewall/rules?per_page=50")
|
||
cf_check_error "$resp"
|
||
count=$(echo "$resp" | jq '.result | length')
|
||
if [[ "$count" == "0" ]]; then
|
||
echo "无防火墙规则"
|
||
else
|
||
echo "$resp" | jq -r '
|
||
["描述", "动作", "优先级", "启用", "ID"],
|
||
(.result[] | [.description[:40], .action, (.priority|tostring), (if .paused then "✗" else "✓" end), .id]) |
|
||
@tsv' | column -t -s $'\t'
|
||
fi
|
||
;;
|
||
|
||
access-rules)
|
||
[[ $# -lt 1 ]] && { echo "用法: cf-fw access-rules <domain>" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
resp=$(cf_call GET "/zones/$ZONE_ID/firewall/access_rules/rules?per_page=50")
|
||
cf_check_error "$resp"
|
||
count=$(echo "$resp" | jq '.result | length')
|
||
if [[ "$count" == "0" ]]; then
|
||
echo "无 IP 访问规则"
|
||
else
|
||
echo "$resp" | jq -r '
|
||
["模式", "目标", "值", "备注", "创建时间", "ID"],
|
||
(.result[] | [.mode, .configuration.target, .configuration.value, .notes[:20], .created_on[:10], .id]) |
|
||
@tsv' | column -t -s $'\t'
|
||
fi
|
||
;;
|
||
|
||
block)
|
||
[[ $# -lt 2 ]] && { echo "用法: cf-fw block <domain> <ip> [note]" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
resp=$(create_access_rule "$ZONE_ID" "block" "$2" "${3:-blocked by cf-fw}")
|
||
cf_check_error "$resp"
|
||
echo "已封禁: $2"
|
||
;;
|
||
|
||
unblock)
|
||
[[ $# -lt 2 ]] && { echo "用法: cf-fw unblock <domain> <rule_id>" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
resp=$(cf_call DELETE "/zones/$ZONE_ID/firewall/access_rules/rules/$2")
|
||
cf_check_error "$resp"
|
||
echo "已解封: $2"
|
||
;;
|
||
|
||
whitelist)
|
||
[[ $# -lt 2 ]] && { echo "用法: cf-fw whitelist <domain> <ip> [note]" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
resp=$(create_access_rule "$ZONE_ID" "whitelist" "$2" "${3:-whitelisted by cf-fw}")
|
||
cf_check_error "$resp"
|
||
echo "已加白名单: $2"
|
||
;;
|
||
|
||
challenge)
|
||
[[ $# -lt 2 ]] && { echo "用法: cf-fw challenge <domain> <ip> [note]" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
resp=$(create_access_rule "$ZONE_ID" "js_challenge" "$2" "${3:-challenged by cf-fw}")
|
||
cf_check_error "$resp"
|
||
echo "已设置 JS Challenge: $2"
|
||
;;
|
||
|
||
security-level)
|
||
[[ $# -lt 1 ]] && { echo "用法: cf-fw security-level <domain> [level]" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
if [[ $# -lt 2 ]]; then
|
||
resp=$(cf_call GET "/zones/$ZONE_ID/settings/security_level")
|
||
echo "$resp" | jq -r '.result.value'
|
||
else
|
||
resp=$(cf_call PATCH "/zones/$ZONE_ID/settings/security_level" -d "{\"value\":\"$2\"}")
|
||
cf_check_error "$resp"
|
||
echo "安全级别已设为: $2"
|
||
fi
|
||
;;
|
||
|
||
ua-rules)
|
||
[[ $# -lt 1 ]] && { echo "用法: cf-fw ua-rules <domain>" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
resp=$(cf_call GET "/zones/$ZONE_ID/firewall/ua_rules?per_page=50")
|
||
cf_check_error "$resp"
|
||
count=$(echo "$resp" | jq '.result | length')
|
||
if [[ "$count" == "0" ]]; then
|
||
echo "无 UA 规则"
|
||
else
|
||
echo "$resp" | jq -r '.result[] | "\(.mode) | \(.configuration.value[:50]) | \(.description)"'
|
||
fi
|
||
;;
|
||
|
||
lockdown)
|
||
[[ $# -lt 1 ]] && { echo "用法: cf-fw lockdown <domain>" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
resp=$(cf_call GET "/zones/$ZONE_ID/firewall/lockdowns?per_page=50")
|
||
cf_check_error "$resp"
|
||
count=$(echo "$resp" | jq '.result | length')
|
||
if [[ "$count" == "0" ]]; then
|
||
echo "无 Zone Lockdown 规则"
|
||
else
|
||
echo "$resp" | jq '.result[] | {id, description, urls, configurations}'
|
||
fi
|
||
;;
|
||
|
||
rate-limits)
|
||
[[ $# -lt 1 ]] && { echo "用法: cf-fw rate-limits <domain>" >&2; exit 1; }
|
||
ZONE_ID=$(cf_resolve_zone "$1")
|
||
resp=$(cf_call GET "/zones/$ZONE_ID/rate_limits?per_page=50")
|
||
cf_check_error "$resp"
|
||
count=$(echo "$resp" | jq '.result | length')
|
||
if [[ "$count" == "0" ]]; then
|
||
echo "无速率限制规则"
|
||
else
|
||
echo "$resp" | jq '.result[] | {id, description, match, threshold, period, action: .action.mode}'
|
||
fi
|
||
;;
|
||
|
||
*) usage ;;
|
||
esac
|