feat(security): Add security stats and Gitea mirror commands
Security Stats: - Add get_security_stats RPCD method for quick overview - Track WAN drops, firewall rejects, CrowdSec bans - Add secubox-stats CLI tool for quick stats check Gitea Mirror Commands: - Add mirror-sync to trigger mirror repository sync - Add mirror-list to show all mirrored repos - Add mirror-create to create new mirrors from GitHub URLs - Add repo-list to list all repositories - Requires API token: uci set gitea.main.api_token=<token> Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b7edc32695
commit
283f2567be
@ -22,6 +22,10 @@ define Package/luci-app-secubox-security-threats/conffiles
|
||||
endef
|
||||
|
||||
define Package/luci-app-secubox-security-threats/install
|
||||
# CLI tool
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) ./root/usr/bin/secubox-stats $(1)/usr/bin/
|
||||
|
||||
# RPCD backend (MUST be 755 for ubus calls)
|
||||
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
|
||||
$(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.secubox-security-threats $(1)/usr/libexec/rpcd/
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
# SecuBox Security Stats - Quick overview
|
||||
# Copyright (C) 2026 CyberMind.fr
|
||||
|
||||
ubus call luci.secubox-security-threats get_security_stats 2>/dev/null | jsonfilter -e '@' 2>/dev/null || echo '{"error": "RPCD not available"}'
|
||||
@ -262,6 +262,72 @@ check_block_rules() {
|
||||
config_foreach check_rule_match block_rule "$category" "$risks" "$score" "$ip"
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# SECURITY STATS (Quick Overview)
|
||||
# ==============================================================================
|
||||
|
||||
# Get overall security statistics from all sources
|
||||
get_security_stats() {
|
||||
local wan_drops=0
|
||||
local fw_rejects=0
|
||||
local cs_bans=0
|
||||
local cs_alerts_24h=0
|
||||
local haproxy_conns=0
|
||||
local invalid_conns=0
|
||||
|
||||
# WAN dropped packets (from kernel stats)
|
||||
if [ -f /sys/class/net/br-wan/statistics/rx_dropped ]; then
|
||||
wan_drops=$(cat /sys/class/net/br-wan/statistics/rx_dropped 2>/dev/null)
|
||||
elif [ -f /sys/class/net/eth1/statistics/rx_dropped ]; then
|
||||
wan_drops=$(cat /sys/class/net/eth1/statistics/rx_dropped 2>/dev/null)
|
||||
fi
|
||||
wan_drops=${wan_drops:-0}
|
||||
|
||||
# Firewall rejects from logs (last 24h)
|
||||
fw_rejects=$(logread 2>/dev/null | grep -c "reject\|drop" || echo 0)
|
||||
fw_rejects=$(echo "$fw_rejects" | tr -d '\n')
|
||||
fw_rejects=${fw_rejects:-0}
|
||||
|
||||
# CrowdSec active bans
|
||||
if [ -x "$CSCLI" ]; then
|
||||
cs_bans=$($CSCLI decisions list -o json 2>/dev/null | grep -c '"id":' || echo 0)
|
||||
cs_bans=$(echo "$cs_bans" | tr -d '\n')
|
||||
cs_bans=${cs_bans:-0}
|
||||
|
||||
# CrowdSec alerts in last 24h
|
||||
cs_alerts_24h=$($CSCLI alerts list -o json --since 24h 2>/dev/null | grep -c '"id":' || echo 0)
|
||||
cs_alerts_24h=$(echo "$cs_alerts_24h" | tr -d '\n')
|
||||
cs_alerts_24h=${cs_alerts_24h:-0}
|
||||
fi
|
||||
|
||||
# Invalid connections (conntrack)
|
||||
if [ -f /proc/net/nf_conntrack ]; then
|
||||
invalid_conns=$(grep -c "INVALID\|UNREPLIED" /proc/net/nf_conntrack 2>/dev/null || echo 0)
|
||||
fi
|
||||
invalid_conns=$(echo "$invalid_conns" | tr -d '\n')
|
||||
invalid_conns=${invalid_conns:-0}
|
||||
|
||||
# HAProxy connections (if running in LXC)
|
||||
if lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING"; then
|
||||
haproxy_conns=$(lxc-attach -n haproxy -- sh -c 'echo "show stat" | socat stdio /var/run/haproxy/admin.sock 2>/dev/null | tail -n+2 | awk -F, "{sum+=\$8} END {print sum}"' 2>/dev/null || echo 0)
|
||||
fi
|
||||
haproxy_conns=$(echo "$haproxy_conns" | tr -d '\n')
|
||||
haproxy_conns=${haproxy_conns:-0}
|
||||
|
||||
# Output JSON
|
||||
cat << EOF
|
||||
{
|
||||
"wan_dropped": $wan_drops,
|
||||
"firewall_rejects": $fw_rejects,
|
||||
"crowdsec_bans": $cs_bans,
|
||||
"crowdsec_alerts_24h": $cs_alerts_24h,
|
||||
"invalid_connections": $invalid_conns,
|
||||
"haproxy_connections": $haproxy_conns,
|
||||
"timestamp": "$(date -Iseconds)"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
# ==============================================================================
|
||||
# STATISTICS
|
||||
# ==============================================================================
|
||||
@ -304,6 +370,8 @@ case "$1" in
|
||||
list)
|
||||
# List available methods
|
||||
json_init
|
||||
json_add_object "get_security_stats"
|
||||
json_close_object
|
||||
json_add_object "status"
|
||||
json_close_object
|
||||
json_add_object "get_active_threats"
|
||||
@ -334,6 +402,10 @@ case "$1" in
|
||||
|
||||
call)
|
||||
case "$2" in
|
||||
get_security_stats)
|
||||
get_security_stats
|
||||
;;
|
||||
|
||||
status)
|
||||
json_init
|
||||
json_add_boolean "enabled" 1
|
||||
|
||||
@ -91,6 +91,15 @@ Commands:
|
||||
--password <pass>
|
||||
--email <email>
|
||||
|
||||
mirror-sync <repo> Sync a mirrored repository
|
||||
mirror-list List all mirrored repositories
|
||||
mirror-create Create a new mirror from URL
|
||||
--name <name>
|
||||
--url <github-url>
|
||||
--owner <user> (default: first admin user)
|
||||
|
||||
repo-list List all repositories
|
||||
|
||||
service-run Start service (used by init)
|
||||
service-stop Stop service (used by init)
|
||||
|
||||
@ -718,6 +727,235 @@ cmd_admin_create_user() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Get Gitea API token (from admin user or config)
|
||||
get_api_token() {
|
||||
local token
|
||||
token="$(uci_get main.api_token)"
|
||||
if [ -n "$token" ]; then
|
||||
echo "$token"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try to get token from container
|
||||
if lxc_running; then
|
||||
token=$(lxc-attach -n "$LXC_NAME" -- cat /data/api_token 2>/dev/null)
|
||||
if [ -n "$token" ]; then
|
||||
echo "$token"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Get Gitea API URL
|
||||
get_api_url() {
|
||||
load_config
|
||||
echo "http://127.0.0.1:${http_port}/api/v1"
|
||||
}
|
||||
|
||||
# Make Gitea API call
|
||||
gitea_api() {
|
||||
local method="$1"
|
||||
local endpoint="$2"
|
||||
local data="$3"
|
||||
local token
|
||||
|
||||
token=$(get_api_token) || {
|
||||
log_error "No API token configured. Set with: uci set gitea.main.api_token=<token>"
|
||||
log_error "Generate token in Gitea: Settings → Applications → Generate Token"
|
||||
return 1
|
||||
}
|
||||
|
||||
local api_url=$(get_api_url)
|
||||
local url="${api_url}${endpoint}"
|
||||
|
||||
if [ "$method" = "GET" ]; then
|
||||
wget -q -O- --header="Authorization: token $token" "$url" 2>/dev/null
|
||||
elif [ "$method" = "POST" ]; then
|
||||
if [ -n "$data" ]; then
|
||||
wget -q -O- --header="Authorization: token $token" \
|
||||
--header="Content-Type: application/json" \
|
||||
--post-data="$data" "$url" 2>/dev/null
|
||||
else
|
||||
wget -q -O- --header="Authorization: token $token" \
|
||||
--post-data="" "$url" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_mirror_sync() {
|
||||
load_config
|
||||
local repo_name="$1"
|
||||
|
||||
if [ -z "$repo_name" ]; then
|
||||
log_error "Usage: giteactl mirror-sync <owner/repo> or <repo>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! lxc_running; then
|
||||
log_error "Gitea container is not running"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# If no owner specified, try to find the repo
|
||||
if ! echo "$repo_name" | grep -q "/"; then
|
||||
# Search for repo in all users
|
||||
local found_owner
|
||||
found_owner=$(gitea_api GET "/repos/search?q=$repo_name" 2>/dev/null | \
|
||||
jsonfilter -e '@.data[0].owner.login' 2>/dev/null)
|
||||
if [ -n "$found_owner" ]; then
|
||||
repo_name="${found_owner}/${repo_name}"
|
||||
else
|
||||
log_error "Repository not found: $repo_name"
|
||||
log_error "Specify full path: owner/repo"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "Syncing mirror: $repo_name"
|
||||
|
||||
# Trigger mirror sync via API
|
||||
local result
|
||||
result=$(gitea_api POST "/repos/${repo_name}/mirror-sync" 2>&1)
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_info "Mirror sync triggered for $repo_name"
|
||||
log_info "Check progress in Gitea web UI"
|
||||
else
|
||||
log_error "Failed to sync mirror: $result"
|
||||
|
||||
# Try alternative: use gitea command directly in container
|
||||
log_info "Trying direct sync via container..."
|
||||
lxc-attach -n "$LXC_NAME" -- su-exec git /usr/local/bin/gitea admin repo-sync-releases \
|
||||
--config /data/custom/conf/app.ini 2>/dev/null || true
|
||||
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_mirror_list() {
|
||||
load_config
|
||||
|
||||
if ! lxc_running; then
|
||||
log_error "Gitea container is not running"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_info "Fetching mirror repositories..."
|
||||
|
||||
local repos
|
||||
repos=$(gitea_api GET "/repos/search?mirror=true&limit=50" 2>/dev/null)
|
||||
|
||||
if [ -z "$repos" ]; then
|
||||
echo "No mirrored repositories found (or API token not set)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Mirrored Repositories:"
|
||||
echo "======================"
|
||||
echo "$repos" | jsonfilter -e '@.data[*]' 2>/dev/null | while read repo; do
|
||||
local name=$(echo "$repo" | jsonfilter -e '@.full_name' 2>/dev/null)
|
||||
local url=$(echo "$repo" | jsonfilter -e '@.original_url' 2>/dev/null)
|
||||
local updated=$(echo "$repo" | jsonfilter -e '@.updated_at' 2>/dev/null)
|
||||
echo " $name"
|
||||
echo " Source: $url"
|
||||
echo " Updated: $updated"
|
||||
echo ""
|
||||
done
|
||||
}
|
||||
|
||||
cmd_mirror_create() {
|
||||
load_config
|
||||
|
||||
local name=""
|
||||
local url=""
|
||||
local owner=""
|
||||
|
||||
# Parse arguments
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--name) name="$2"; shift 2 ;;
|
||||
--url) url="$2"; shift 2 ;;
|
||||
--owner) owner="$2"; shift 2 ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$name" ] || [ -z "$url" ]; then
|
||||
log_error "Usage: giteactl mirror-create --name <repo-name> --url <source-url> [--owner <user>]"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! lxc_running; then
|
||||
log_error "Gitea container is not running"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Get default owner if not specified
|
||||
if [ -z "$owner" ]; then
|
||||
owner=$(gitea_api GET "/user" 2>/dev/null | jsonfilter -e '@.login' 2>/dev/null)
|
||||
if [ -z "$owner" ]; then
|
||||
log_error "Could not determine owner. Specify with --owner"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
log_info "Creating mirror repository: $owner/$name from $url"
|
||||
|
||||
local data=$(cat <<EOF
|
||||
{
|
||||
"clone_addr": "$url",
|
||||
"repo_name": "$name",
|
||||
"mirror": true,
|
||||
"private": false,
|
||||
"description": "Mirror of $url"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
local result
|
||||
result=$(gitea_api POST "/repos/migrate" "$data" 2>&1)
|
||||
|
||||
if echo "$result" | grep -q '"id":'; then
|
||||
log_info "Mirror created successfully: $owner/$name"
|
||||
log_info "First sync in progress..."
|
||||
else
|
||||
log_error "Failed to create mirror: $result"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_repo_list() {
|
||||
load_config
|
||||
|
||||
if ! lxc_running; then
|
||||
log_error "Gitea container is not running"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local repos
|
||||
repos=$(gitea_api GET "/repos/search?limit=100" 2>/dev/null)
|
||||
|
||||
if [ -z "$repos" ]; then
|
||||
echo "No repositories found (or API token not set)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Repositories:"
|
||||
echo "============="
|
||||
echo "$repos" | jsonfilter -e '@.data[*].full_name' 2>/dev/null | while read name; do
|
||||
local is_mirror=$(echo "$repos" | jsonfilter -e "@.data[?(@.full_name=='$name')].mirror" 2>/dev/null)
|
||||
if [ "$is_mirror" = "true" ]; then
|
||||
echo " [mirror] $name"
|
||||
else
|
||||
echo " $name"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
cmd_service_run() {
|
||||
require_root
|
||||
load_config
|
||||
@ -751,7 +989,11 @@ case "${1:-}" in
|
||||
*) echo "Usage: giteactl admin create-user --username <name> --password <pass> --email <email>"; exit 1 ;;
|
||||
esac
|
||||
;;
|
||||
service-run) shift; cmd_service_run "$@" ;;
|
||||
service-stop) shift; cmd_service_stop "$@" ;;
|
||||
*) usage ;;
|
||||
mirror-sync) shift; cmd_mirror_sync "$@" ;;
|
||||
mirror-list) shift; cmd_mirror_list "$@" ;;
|
||||
mirror-create) shift; cmd_mirror_create "$@" ;;
|
||||
repo-list) shift; cmd_repo_list "$@" ;;
|
||||
service-run) shift; cmd_service_run "$@" ;;
|
||||
service-stop) shift; cmd_service_stop "$@" ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
|
||||
Loading…
Reference in New Issue
Block a user