Threat Intelligence
Sentinel automatically profiles every IP address that triggers a security event. The Threat Intelligence system builds persistent actor profiles, computes risk scores, enriches data with geolocation and IP reputation, and provides tools to manage IP blocklists and whitelists. All of this happens automatically as events flow through the pipeline — no manual configuration is required to start collecting intelligence.
Automatic Profiling
Threat Actor Profiles
Every unique IP address that triggers a security event receives a persistent profile. The profiler runs as a pipeline handler, processing each threat event asynchronously and updating the corresponding actor record in storage.
Profile Fields
| Field | Type | Description |
|---|---|---|
IP | string | The IP address that uniquely identifies this actor. |
FirstSeen | time.Time | Timestamp of the first security event from this IP. |
LastSeen | time.Time | Timestamp of the most recent security event from this IP. |
ThreatCount | int | Total number of security events attributed to this IP. |
AttackTypes | []string | Deduplicated list of attack types observed (e.g., SQLInjection, XSS, BruteForce). |
TargetedRoutes | []string | Deduplicated list of routes this actor has targeted (e.g., GET /api/users). |
RiskScore | int | Computed risk score from 0 to 100. See Risk Scoring below. |
Status | ActorStatus | Current status: Active, Blocked, or Whitelisted. |
Country | string | Country of origin (populated by geolocation if enabled). |
City | string | City of origin (populated by geolocation if enabled). |
ISP | string | Internet service provider of the IP. |
Lat / Lng | float64 | Geographic coordinates for map visualization in the dashboard. |
IsKnownBadActor | bool | Set to true if the IP has a high abuse score from IP reputation checking. |
AbuseScore | int | Abuse confidence score from AbuseIPDB (0-100). |
Profiles are created on the first event and updated incrementally on each subsequent event. Attack types and targeted routes are deduplicated, so the lists grow only when new distinct values appear.
1// ThreatActor represents a persistent profile of an attacker.2type ThreatActor struct {3 ID string `json:"id"`4 IP string `json:"ip"`5 FirstSeen time.Time `json:"first_seen"`6 LastSeen time.Time `json:"last_seen"`7 TotalRequests int `json:"total_requests"`8 ThreatCount int `json:"threat_count"`9 AttackTypes []string `json:"attack_types"`10 TargetedRoutes []string `json:"targeted_routes"`11 RiskScore int `json:"risk_score"`12 Status ActorStatus `json:"status"`13 Country string `json:"country"`14 City string `json:"city,omitempty"`15 ISP string `json:"isp"`16 IsKnownBadActor bool `json:"is_known_bad_actor"`17 AbuseScore int `json:"abuse_score"`18 Lat float64 `json:"lat"`19 Lng float64 `json:"lng"`20}
Risk Scoring
Each threat actor is assigned a risk score between 0 and 100. The score is recomputed every time a new threat event is attributed to the actor. The scoring algorithm considers four factors:
| Factor | Points | Condition |
|---|---|---|
| Attack Variety | +10 per unique type (max 50) | Each distinct attack type in AttackTypes adds 10 points, capped at 50. |
| Known Bad Actor | +20 | IP has been flagged by AbuseIPDB (IsKnownBadActor == true). |
| Recency | +10 | The most recent attack was within the last hour. |
| Volume | +20 | Total threat count exceeds 100 events. |
The final score is capped at 100. An actor with 5 unique attack types, a recent attack, over 100 events, and a known-bad reputation would score the maximum of 100.
1// ComputeRiskScore calculates a risk score (0-100) for a threat actor.2func ComputeRiskScore(actor *sentinel.ThreatActor) int {3 score := 045 // +10 for each unique attack type, max 506 attackTypeScore := len(actor.AttackTypes) * 107 if attackTypeScore > 50 {8 attackTypeScore = 509 }10 score += attackTypeScore1112 // +20 if known bad actor (AbuseIPDB)13 if actor.IsKnownBadActor {14 score += 2015 }1617 // +10 if attacked in last hour18 if time.Since(actor.LastSeen) < time.Hour {19 score += 1020 }2122 // +20 if attack count > 10023 if actor.ThreatCount > 100 {24 score += 2025 }2627 if score > 100 {28 score = 10029 }30 return score31}
Risk Score Interpretation
IP Reputation
Sentinel can optionally enrich threat actor profiles with external IP reputation data from AbuseIPDB. When enabled, the reputation checker queries the AbuseIPDB API for each IP that triggers a security event, retrieves its abuse confidence score, and can automatically block IPs that exceed a configurable threshold.
IPReputationConfig
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | bool | false | Enables IP reputation checking. |
AbuseIPDBKey | string | "" | Your AbuseIPDB API key. Required for reputation lookups. Obtain one from abuseipdb.com. |
AutoBlock | bool | false | Automatically block IPs whose abuse score meets or exceeds MinAbuseScore. |
MinAbuseScore | int | 80 | Minimum AbuseIPDB confidence score (0-100) to trigger auto-blocking. |
1sentinel.Mount(r, nil, sentinel.Config{2 IPReputation: sentinel.IPReputationConfig{3 Enabled: true,4 AbuseIPDBKey: "your-abuseipdb-api-key",5 AutoBlock: true,6 MinAbuseScore: 80, // Block IPs with 80%+ abuse confidence7 },8})
Reputation results are cached for 24 hours to minimize API calls. The cache is maintained in memory and cleared on application restart. When AutoBlock is enabled and an IP exceeds the threshold, it is immediately added to the blocklist via the IP Manager.
AbuseIPDB Rate Limits
MinAbuseScore to reduce the number of reputation lookups triggered by low-confidence threats.Geolocation
When geolocation is enabled, Sentinel resolves the geographic origin of each attacker IP and attaches country, city, coordinates, ISP, and ASN data to both the threat event and the actor profile. This data powers the geographic attack map in the dashboard.
GeoConfig
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | bool | false | Enables IP geolocation lookups. |
Provider | GeoProvider | GeoIPFree | The geolocation provider. Default uses the free ip-api.com service (no API key required). |
1sentinel.Mount(r, nil, sentinel.Config{2 Geo: sentinel.GeoConfig{3 Enabled: true,4 Provider: sentinel.GeoIPFree, // Free provider, no API key needed5 },6})
Geolocation results are cached in an LRU cache (default 10,000 entries) to minimize external API calls. Private and loopback IPs (e.g., 127.0.0.1, 10.x.x.x, 192.168.x.x) are skipped automatically.
GeoResult Fields
| Field | Type | Description |
|---|---|---|
Country | string | Full country name (e.g., United States). |
CountryCode | string | ISO 3166-1 alpha-2 country code (e.g., US). |
City | string | City name. |
Lat / Lng | float64 | Geographic coordinates. |
ISP | string | Internet service provider. |
ASN | string | Autonomous system number and name. |
Production Geolocation
ip-api.com provider is rate-limited to 45 requests per minute. For production workloads with high traffic, consider using a local MaxMind GeoLite2 database for offline lookups with no rate limits.IP Management
The IP Manager provides a centralized system for blocking and whitelisting IP addresses and CIDR ranges. It maintains an in-memory cache that syncs from storage every 30 seconds, ensuring fast per-request lookups with no database overhead on the hot path.
Blocking IPs
Blocked IPs are rejected by the middleware before reaching your application handlers. You can block individual IPs or entire CIDR ranges. Blocks can optionally have an expiration time.
1// Block a single IP with no expiration2ipManager.BlockIP(ctx, "203.0.113.50", "Repeated SQL injection attempts", nil)34// Block a CIDR range5ipManager.BlockIP(ctx, "198.51.100.0/24", "Known botnet range", nil)67// Block with expiration (auto-unblock after 24 hours)8expiry := time.Now().Add(24 * time.Hour)9ipManager.BlockIP(ctx, "203.0.113.75", "Temporary block", &expiry)
Whitelisting IPs
Whitelisted IPs bypass all security checks including the WAF, rate limiting, and Auth Shield. Use this for trusted internal services, monitoring systems, and CI/CD pipelines.
1// Whitelist a trusted IP2ipManager.WhitelistIP(ctx, "10.0.0.5")
How the Cache Works
The IP Manager loads all blocked and whitelisted IPs into memory at startup. A background goroutine re-syncs from storage every 30 seconds to pick up changes made via the API or dashboard. Blocking and whitelisting operations update both storage and the in-memory cache immediately, so changes take effect without waiting for the next sync cycle.
CIDR Matching
198.51.100.0/24, every IP within that range is matched. The IP Manager parses CIDR notation and performs subnet containment checks. Individual IP blocks use exact string matching for maximum performance.Full Configuration Example
The following example enables all threat intelligence features: profiling, geolocation, IP reputation with auto-blocking, and the WAF for event generation.
1package main23import (4 sentinel "github.com/MUKE-coder/sentinel"5 "github.com/gin-gonic/gin"6)78func main() {9 r := gin.Default()1011 sentinel.Mount(r, nil, sentinel.Config{12 // WAF generates the threat events that feed the profiler13 WAF: sentinel.WAFConfig{14 Enabled: true,15 Mode: sentinel.ModeBlock,16 },1718 // IP reputation enrichment with auto-blocking19 IPReputation: sentinel.IPReputationConfig{20 Enabled: true,21 AbuseIPDBKey: "your-abuseipdb-api-key",22 AutoBlock: true,23 MinAbuseScore: 80,24 },2526 // Geolocation for geographic attribution27 Geo: sentinel.GeoConfig{28 Enabled: true,29 Provider: sentinel.GeoIPFree,30 },31 })3233 r.GET("/api/data", func(c *gin.Context) {34 c.JSON(200, gin.H{"status": "ok"})35 })3637 r.Run(":8080")38}
Dashboard
The Sentinel dashboard provides two dedicated pages for threat intelligence:
Actors Page
The Actors page lists all threat actor profiles with sortable columns for IP, risk score, threat count, attack types, country, and last seen time. From this page you can:
- Search and filter actors by IP, status, or minimum risk score.
- View the full profile for any actor, including their complete attack history.
- Block an actor directly from their profile with a single click.
- View geographic data and the attack types observed for each actor.
IP Management Page
The IP Management page provides a visual interface for managing your blocklist and whitelist:
- View all currently blocked IPs and CIDR ranges with block reasons and timestamps.
- Add new IPs or CIDR ranges to the blocklist with an optional expiration.
- Unblock IPs that were previously blocked manually or by auto-blocking.
- Manage the whitelist for trusted IPs.
Access these pages at http://localhost:8080/sentinel/ui and navigate to the Actors or IP Management sections.
API Reference
All threat intelligence endpoints require authentication via the dashboard JWT token. Include the token in the Authorization header as Bearer <token>.
List Actors
Returns a paginated list of threat actor profiles with optional filtering.
| Property | Value |
|---|---|
| Endpoint | GET /sentinel/api/actors |
| Query Params | status, min_risk, search, page, page_size |
# List all actors, sorted by defaultcurl -s -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/actors?page=1&page_size=20"# Filter by minimum risk scorecurl -s -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/actors?min_risk=50"# Filter by statuscurl -s -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/actors?status=Active"
{"data": [{"id": "203.0.113.50","ip": "203.0.113.50","first_seen": "2025-01-15T08:23:00Z","last_seen": "2025-01-15T14:45:00Z","total_requests": 347,"threat_count": 89,"attack_types": ["SQLInjection", "XSS", "PathTraversal"],"targeted_routes": ["GET /api/users", "POST /api/search"],"risk_score": 70,"status": "Active","country": "United States","city": "New York","isp": "Example ISP","is_known_bad_actor": false,"abuse_score": 45,"lat": 40.7128,"lng": -74.006}],"meta": {"total": 42,"page": 1,"page_size": 20}}
Get Actor by IP
Returns the full profile for a specific threat actor.
| Property | Value |
|---|---|
| Endpoint | GET /sentinel/api/actors/:ip |
| URL Param | :ip — the IP address of the actor |
curl -s -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/actors/203.0.113.50"
Block Actor
Blocks a threat actor by IP. The IP is added to the blocklist and all future requests from this IP are rejected.
| Property | Value |
|---|---|
| Endpoint | POST /sentinel/api/actors/:ip/block |
| URL Param | :ip — the IP address to block |
curl -s -X POST -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/actors/203.0.113.50/block"
{"message": "Actor blocked"}
Actor Request History
Returns the threat events associated with a specific actor IP.
| Property | Value |
|---|---|
| Endpoint | GET /sentinel/api/actors/:ip/requests |
| URL Param | :ip — the IP address of the actor |
curl -s -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/actors/203.0.113.50/requests"
List Blocked IPs
Returns all currently blocked IPs and CIDR ranges.
| Property | Value |
|---|---|
| Endpoint | GET /sentinel/api/ip/blocked |
curl -s -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/ip/blocked"
{"data": [{"ip": "203.0.113.50","reason": "Blocked via dashboard","blocked_at": "2025-01-15T14:50:00Z","expires_at": null,"cidr": false},{"ip": "198.51.100.0/24","reason": "Known botnet range","blocked_at": "2025-01-14T09:00:00Z","expires_at": null,"cidr": true}]}
Block IP
Adds an IP or CIDR range to the blocklist.
| Property | Value |
|---|---|
| Endpoint | POST /sentinel/api/ip/block |
| Body | JSON with ip (required), reason, expiry (optional, RFC3339) |
# Block a single IPcurl -s -X POST -H "Authorization: Bearer <token>" \-H "Content-Type: application/json" \-d '{"ip": "203.0.113.75", "reason": "Manual block"}' \"http://localhost:8080/sentinel/api/ip/block"# Block a CIDR range with expirationcurl -s -X POST -H "Authorization: Bearer <token>" \-H "Content-Type: application/json" \-d '{"ip": "198.51.100.0/24", "reason": "Temporary block", "expiry": "2025-02-15T00:00:00Z"}' \"http://localhost:8080/sentinel/api/ip/block"
Unblock IP
Removes an IP or CIDR range from the blocklist. For CIDR ranges, replace / with _ in the URL parameter (e.g., 198.51.100.0_24).
| Property | Value |
|---|---|
| Endpoint | DELETE /sentinel/api/ip/block/:ip |
| URL Param | :ip — the IP or CIDR (with / replaced by _) |
# Unblock a single IPcurl -s -X DELETE -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/ip/block/203.0.113.75"# Unblock a CIDR range (replace / with _)curl -s -X DELETE -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/ip/block/198.51.100.0_24"
IP Reputation Lookup
Retrieves the AbuseIPDB reputation data for a specific IP (requires IP reputation to be enabled).
| Property | Value |
|---|---|
| Endpoint | GET /sentinel/api/ip/:ip/reputation |
curl -s -H "Authorization: Bearer <token>" \"http://localhost:8080/sentinel/api/ip/203.0.113.50/reputation"
Testing
After enabling the WAF or any other detection feature, threat actor profiles are created automatically. You can verify the intelligence system is working by triggering a few test events and then querying the actors API.
Step 1: Trigger Security Events
Send a few malicious requests to generate threat events. These will automatically create actor profiles.
# Trigger a SQL injection eventcurl -s "http://localhost:8080/api/data?id=1'+OR+'1'='1"# Trigger an XSS eventcurl -s "http://localhost:8080/api/data?q=<script>alert(1)</script>"# Trigger a path traversal eventcurl -s "http://localhost:8080/api/data/../../../../etc/passwd"
Step 2: Log In to the Dashboard API
# Obtain a JWT tokenTOKEN=$(curl -s -X POST http://localhost:8080/sentinel/api/auth/login \-H "Content-Type: application/json" \-d '{"username":"admin","password":"sentinel"}' | jq -r '.token')echo $TOKEN
Step 3: View Actor Profiles
# List all actorscurl -s -H "Authorization: Bearer $TOKEN" \"http://localhost:8080/sentinel/api/actors" | jq .# View a specific actor (use your test IP, likely 127.0.0.1)curl -s -H "Authorization: Bearer $TOKEN" \"http://localhost:8080/sentinel/api/actors/127.0.0.1" | jq .# View the threat history for an actorcurl -s -H "Authorization: Bearer $TOKEN" \"http://localhost:8080/sentinel/api/actors/127.0.0.1/requests" | jq .
Step 4: Test IP Blocking
# Block an actorcurl -s -X POST -H "Authorization: Bearer $TOKEN" \"http://localhost:8080/sentinel/api/actors/203.0.113.50/block" | jq .# Verify the IP appears in the blocklistcurl -s -H "Authorization: Bearer $TOKEN" \"http://localhost:8080/sentinel/api/ip/blocked" | jq .# Unblock the IPcurl -s -X DELETE -H "Authorization: Bearer $TOKEN" \"http://localhost:8080/sentinel/api/ip/block/203.0.113.50" | jq .
Local Testing Note
127.0.0.1 or ::1, so all test events will be attributed to a single actor profile. Geolocation lookups are skipped for private and loopback IPs. To test with realistic geographic data, deploy to a staging environment and send requests from external IPs.Next Steps
- WAF — Configure the WAF rules that generate threat events
- Auth Shield — Protect login endpoints from brute-force attacks
- Anomaly Detection — Add behavioral analysis for advanced threat detection
- Alerting — Get notified when high-risk actors are detected
- Dashboard — Explore the Actors and IP Management pages