Plugin idea: Bad IP checker

I’d love to have a plugin that checks request IPs against AbuseIPDB and the like and block them if considered bots or otherwise potentially malicious.

2 Likes

Take a shot at writing it yourself. Caddy plugins are very easy to write, even for Go beginners:

2 Likes

Even did Caddy plugin development before (caddy-remote-host) and might actually give this a shot at some point. Still wanted to drop the idea here, just in case someone is eager to jump right onto it.

3 Likes

As a starter for 10. The Go would be fairly close to the following. :slight_smile:

Based on the CURL info from AbuseIPDB indfo below.

Go code:

package main

import (
    "fmt"
    "net/http"
    "net/url"
)

// Response struct to unmarshal JSON response
type Response struct {
    TotalReports bool `json:"totalReports"`
}


func main() {
    apiUrl := "https://api.abuseipdb.com/api/v2/check"
    ipAddress := "118.25.6.39"
    maxAgeInDays := "90"
    apiKey := "RANDOMAPIKEY"
	
	result := checkIP(ipAddress,maxAgeInDays )
    fmt.Println("Result:", result)

}
Returns false if there is an error or is not in the AbuseIPDB 
else returns 127.0.0.1 if is in the abuse DB

func checkIP( apiKey string , ipAddress string, maxAgeInDays integer) bool{

    // Create HTTP client
    client := &http.Client{}

    // Prepare request
    req, err := http.NewRequest("GET", apiUrl, nil)
    if err != nil {
        fmt.Println("Error creating request:", err)
        return false
    }

    // Add query parameters
    queryParams := req.URL.Query()
    queryParams.Add("ipAddress", ipAddress)
    queryParams.Add("maxAgeInDays", maxAgeInDays)
    queryParams.Add("verbose", "")
    req.URL.RawQuery = queryParams.Encode()

    // Add headers
    req.Header.Set("Key", apiKey)
    req.Header.Set("Accept", "application/json")

    // Send request
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error sending request:", err)
        return false
    }
    defer resp.Body.Close()

    // Handle response
    // You can read the response body here and parse it as needed
    // For example, you can use json.Decoder to decode JSON response
    // resp.Body contains the response body
    fmt.Println("Response Status:", resp.Status)
	
	 // Decode JSON response
    var response Response
    if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
        fmt.Println("Error decoding JSON:", err)
        return false// Return original IP address on error
    }

    // Check if badblock is true
    if response.BadBlock {
        return true
    }

    return false
	
}

From AbuseIPDB info on using the API:

# The -G option will convert form parameters (-d options) into query parameters.
# The CHECK endpoint is a GET request.
curl -G https://api.abuseipdb.com/api/v2/check \
  --data-urlencode "ipAddress=118.25.6.39" \
  -d maxAgeInDays=90 \
  -d verbose \
  -H "Key: YOUR_OWN_API_KEY" \
  -H "Accept: application/json"

This will yield the following JSON response:

  {
    "data": {
      "ipAddress": "118.25.6.39",
      "isPublic": true,
      "ipVersion": 4,
      "isWhitelisted": false,
      "abuseConfidenceScore": 100,
      "countryCode": "CN",
      "countryName": "China",
      "usageType": "Data Center/Web Hosting/Transit",
      "isp": "Tencent Cloud Computing (Beijing) Co. Ltd",
      "domain": "tencent.com",
      "hostnames": [],
      "isTor": false,
      "totalReports": 1,
      "numDistinctUsers": 1,
      "lastReportedAt": "2018-12-20T20:55:14+00:00",
      "reports": [
        {
          "reportedAt": "2018-12-20T20:55:14+00:00",
          "comment": "Dec 20 20:55:14 srv206 sshd[13937]: Invalid user oracle from 118.25.6.39",
          "categories": [
            18,
            22
          ],
          "reporterId": 1,
          "reporterCountryCode": "US",
          "reporterCountryName": "United States"
        }
      ]
    }
  }

Implementing this would be trivial. I’m only worried about the added latency. Every first incoming request would be blocked by a round trip to the AbusdeIPDB API.