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 profiling is always active when any detection feature is enabled (WAF, Auth Shield, Anomaly Detection). Every security event automatically creates or updates the corresponding actor profile. You do not need to enable profiling separately.

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

FieldTypeDescription
IPstringThe IP address that uniquely identifies this actor.
FirstSeentime.TimeTimestamp of the first security event from this IP.
LastSeentime.TimeTimestamp of the most recent security event from this IP.
ThreatCountintTotal number of security events attributed to this IP.
AttackTypes[]stringDeduplicated list of attack types observed (e.g., SQLInjection, XSS, BruteForce).
TargetedRoutes[]stringDeduplicated list of routes this actor has targeted (e.g., GET /api/users).
RiskScoreintComputed risk score from 0 to 100. See Risk Scoring below.
StatusActorStatusCurrent status: Active, Blocked, or Whitelisted.
CountrystringCountry of origin (populated by geolocation if enabled).
CitystringCity of origin (populated by geolocation if enabled).
ISPstringInternet service provider of the IP.
Lat / Lngfloat64Geographic coordinates for map visualization in the dashboard.
IsKnownBadActorboolSet to true if the IP has a high abuse score from IP reputation checking.
AbuseScoreintAbuse 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.

core/models.gogo
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:

FactorPointsCondition
Attack Variety+10 per unique type (max 50)Each distinct attack type in AttackTypes adds 10 points, capped at 50.
Known Bad Actor+20IP has been flagged by AbuseIPDB (IsKnownBadActor == true).
Recency+10The most recent attack was within the last hour.
Volume+20Total 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.

intelligence/profiler.gogo
1// ComputeRiskScore calculates a risk score (0-100) for a threat actor.
2func ComputeRiskScore(actor *sentinel.ThreatActor) int {
3 score := 0
4
5 // +10 for each unique attack type, max 50
6 attackTypeScore := len(actor.AttackTypes) * 10
7 if attackTypeScore > 50 {
8 attackTypeScore = 50
9 }
10 score += attackTypeScore
11
12 // +20 if known bad actor (AbuseIPDB)
13 if actor.IsKnownBadActor {
14 score += 20
15 }
16
17 // +10 if attacked in last hour
18 if time.Since(actor.LastSeen) < time.Hour {
19 score += 10
20 }
21
22 // +20 if attack count > 100
23 if actor.ThreatCount > 100 {
24 score += 20
25 }
26
27 if score > 100 {
28 score = 100
29 }
30 return score
31}

Risk Score Interpretation

0-20: Low risk, minimal activity. 21-50: Moderate risk, multiple attack types or sustained activity. 51-80: High risk, diverse attack patterns or known bad reputation. 81-100: Critical risk, consider immediate blocking.

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

FieldTypeDefaultDescription
EnabledboolfalseEnables IP reputation checking.
AbuseIPDBKeystring""Your AbuseIPDB API key. Required for reputation lookups. Obtain one from abuseipdb.com.
AutoBlockboolfalseAutomatically block IPs whose abuse score meets or exceeds MinAbuseScore.
MinAbuseScoreint80Minimum AbuseIPDB confidence score (0-100) to trigger auto-blocking.
main.gogo
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 confidence
7 },
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

The free AbuseIPDB plan allows 1,000 checks per day. If your application processes a high volume of unique attacker IPs, consider upgrading your AbuseIPDB plan or increasing 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

FieldTypeDefaultDescription
EnabledboolfalseEnables IP geolocation lookups.
ProviderGeoProviderGeoIPFreeThe geolocation provider. Default uses the free ip-api.com service (no API key required).
main.gogo
1sentinel.Mount(r, nil, sentinel.Config{
2 Geo: sentinel.GeoConfig{
3 Enabled: true,
4 Provider: sentinel.GeoIPFree, // Free provider, no API key needed
5 },
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

FieldTypeDescription
CountrystringFull country name (e.g., United States).
CountryCodestringISO 3166-1 alpha-2 country code (e.g., US).
CitystringCity name.
Lat / Lngfloat64Geographic coordinates.
ISPstringInternet service provider.
ASNstringAutonomous system number and name.

Production Geolocation

The free 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 expiration
2ipManager.BlockIP(ctx, "203.0.113.50", "Repeated SQL injection attempts", nil)
3
4// Block a CIDR range
5ipManager.BlockIP(ctx, "198.51.100.0/24", "Known botnet range", nil)
6
7// 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 IP
2ipManager.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

When you block a CIDR range like 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.

main.gogo
1package main
2
3import (
4 sentinel "github.com/MUKE-coder/sentinel"
5 "github.com/gin-gonic/gin"
6)
7
8func main() {
9 r := gin.Default()
10
11 sentinel.Mount(r, nil, sentinel.Config{
12 // WAF generates the threat events that feed the profiler
13 WAF: sentinel.WAFConfig{
14 Enabled: true,
15 Mode: sentinel.ModeBlock,
16 },
17
18 // IP reputation enrichment with auto-blocking
19 IPReputation: sentinel.IPReputationConfig{
20 Enabled: true,
21 AbuseIPDBKey: "your-abuseipdb-api-key",
22 AutoBlock: true,
23 MinAbuseScore: 80,
24 },
25
26 // Geolocation for geographic attribution
27 Geo: sentinel.GeoConfig{
28 Enabled: true,
29 Provider: sentinel.GeoIPFree,
30 },
31 })
32
33 r.GET("/api/data", func(c *gin.Context) {
34 c.JSON(200, gin.H{"status": "ok"})
35 })
36
37 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.

PropertyValue
EndpointGET /sentinel/api/actors
Query Paramsstatus, min_risk, search, page, page_size
# List all actors, sorted by default
curl -s -H "Authorization: Bearer <token>" \
"http://localhost:8080/sentinel/api/actors?page=1&page_size=20"
# Filter by minimum risk score
curl -s -H "Authorization: Bearer <token>" \
"http://localhost:8080/sentinel/api/actors?min_risk=50"
# Filter by status
curl -s -H "Authorization: Bearer <token>" \
"http://localhost:8080/sentinel/api/actors?status=Active"
Responsejson
{
"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.

PropertyValue
EndpointGET /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.

PropertyValue
EndpointPOST /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"
Responsejson
{
"message": "Actor blocked"
}

Actor Request History

Returns the threat events associated with a specific actor IP.

PropertyValue
EndpointGET /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.

PropertyValue
EndpointGET /sentinel/api/ip/blocked
curl -s -H "Authorization: Bearer <token>" \
"http://localhost:8080/sentinel/api/ip/blocked"
Responsejson
{
"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.

PropertyValue
EndpointPOST /sentinel/api/ip/block
BodyJSON with ip (required), reason, expiry (optional, RFC3339)
# Block a single IP
curl -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 expiration
curl -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).

PropertyValue
EndpointDELETE /sentinel/api/ip/block/:ip
URL Param:ip — the IP or CIDR (with / replaced by _)
# Unblock a single IP
curl -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).

PropertyValue
EndpointGET /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 event
curl -s "http://localhost:8080/api/data?id=1'+OR+'1'='1"
# Trigger an XSS event
curl -s "http://localhost:8080/api/data?q=<script>alert(1)</script>"
# Trigger a path traversal event
curl -s "http://localhost:8080/api/data/../../../../etc/passwd"

Step 2: Log In to the Dashboard API

# Obtain a JWT token
TOKEN=$(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 actors
curl -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 actor
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/sentinel/api/actors/127.0.0.1/requests" | jq .

Step 4: Test IP Blocking

# Block an actor
curl -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 blocklist
curl -s -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/sentinel/api/ip/blocked" | jq .
# Unblock the IP
curl -s -X DELETE -H "Authorization: Bearer $TOKEN" \
"http://localhost:8080/sentinel/api/ip/block/203.0.113.50" | jq .

Local Testing Note

When testing locally, all requests originate from 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

Built with by JB