ipfiltercaddy

package module
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 7, 2026 License: MIT Imports: 7 Imported by: 0

README ΒΆ

IPFilter Caddy Plugin

Go Reference Go Report Card

A Caddy v2 plugin that provides geolocation-based request filtering using the jpillora/ipfilter library with IP2Location LITE data. Perfect for restricting access to specific countries without requiring external databases or API keys.

✨ Features

  • πŸš€ Zero Configuration: No database downloads, API keys, or external dependencies required
  • 🌍 Country-Based Filtering: Allow or deny requests based on visitor country
  • 🌐 CIDR Block Support: Allow or deny specific IP addresses and CIDR ranges (IPv4 and IPv6)
  • πŸ”’ Free Geolocation Data: Uses IP2Location LITE database (completely free)
  • ⚑ High Performance: Embedded geolocation data with no network calls
  • πŸ›‘οΈ Thread-Safe: Safe for concurrent request handling
  • πŸ“ Full Caddy Support: JSON and Caddyfile configuration support
  • πŸ”„ Easy Integration: Drop-in Caddy HTTP matcher

πŸ“¦ Installation

Option 1: Download Pre-built Binary

Download a Caddy binary with this plugin pre-installed from the releases page.

Option 2: Build from Source
  1. Install xcaddy (if not already installed):

    go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
    
  2. Build Caddy with the plugin:

    xcaddy build --with github.com/jpillora/ipfilter-caddy
    
  3. Replace your existing Caddy binary:

    sudo cp caddy /usr/bin/caddy
    sudo systemctl restart caddy
    
Option 3: Add to Existing Installation

If you have Caddy installed via package manager:

caddy add-package github.com/jpillora/ipfilter-caddy

πŸš€ Usage

JSON Configuration
{
  "apps": {
    "http": {
      "servers": {
        "example": {
          "listen": [":443"],
          "routes": [
            {
              "match": [
                {
                  "ipfilter_geolocation": {
                    "allow_ips": ["10.0.0.0/8", "192.168.0.0/16"],
                    "allow_countries": ["AU", "US", "CA"],
                    "block_by_default": true
                  }
                }
              ],
              "handle": [
                {
                  "handler": "static_response",
                  "body": "Welcome!"
                }
              ]
            },
            {
              "handle": [
                {
                  "handler": "static_response",
                  "status_code": 403,
                  "body": "Access restricted"
                }
              ]
            }
          ]
        }
      }
    }
  }
}
Caddyfile Configuration
example.com {
    @allowed_countries {
        ipfilter_geolocation {
            allow_countries AU US CA
            block_by_default true
        }
    }

    handle @allowed_countries {
        respond "Welcome from Australia, USA, or Canada!"
    }

    handle {
        respond "Access restricted to specified countries" 403
    }
}

# Block specific countries
api.example.com {
    @blocked_countries {
        ipfilter_geolocation {
            deny_countries RU CN
        }
    }

    handle @blocked_countries {
        respond "Access denied from restricted countries" 403
    }

    # Continue with normal processing for allowed countries
    reverse_proxy localhost:8080
}

βš™οΈ Configuration Options

JSON Fields
Field Type Default Description
allow_ips []string [] List of IP addresses or CIDR blocks to allow
deny_ips []string [] List of IP addresses or CIDR blocks to deny
allow_countries []string [] List of ISO country codes to allow
deny_countries []string [] List of ISO country codes to deny
block_by_default bool false Block all requests by default unless explicitly allowed
Caddyfile Directives
ipfilter_geolocation {
    allow_ips <ip_or_cidr...>
    deny_ips <ip_or_cidr...>
    allow_countries <country_codes...>
    deny_countries <country_codes...>
    block_by_default <true|false>
}
Rule Precedence

Rules are evaluated in the following order (highest to lowest priority):

  1. Explicit IP matches - Single IP addresses checked first
  2. CIDR subnet matches - Allow takes precedence over deny within subnets
  3. Country code matches - Geolocation-based rules
  4. Default behavior - block_by_default setting applies if no rules match

🌍 Country Codes

Uses standard ISO 3166-1 alpha-2 country codes:

  • AU - Australia
  • US - United States
  • CA - Canada
  • GB - United Kingdom
  • DE - Germany
  • FR - France
  • JP - Japan
  • CN - China
  • RU - Russia
  • And many more...

Note: Unknown or private IPs (like 127.0.0.1, ::1) return country code ZZ.

πŸ“‹ Examples

1. Australia-Only Access
australia-only.example.com {
    @australia {
        ipfilter_geolocation {
            allow_countries AU
            block_by_default true
        }
    }

    handle @australia {
        respond "G'day! Welcome from Australia πŸ‡¦πŸ‡Ί"
    }

    handle {
        respond "This site is only accessible from Australia" 403
    }
}
2. Block Bad Actors
api.example.com {
    @restricted {
        ipfilter_geolocation {
            deny_countries RU CN KP
        }
    }

    handle @restricted {
        respond "Access denied" 403
    }

    reverse_proxy localhost:3000
}
3. Multi-Country Support
global.example.com {
    @eu_countries {
        ipfilter_geolocation {
            allow_countries DE FR GB IT ES NL
            block_by_default true
        }
    }

    handle @eu_countries {
        respond "Welcome from Europe! πŸ‡ͺπŸ‡Ί"
    }

    handle {
        respond "This content is geo-restricted to European countries" 403
    }
}
4. CIDR Block Filtering

Allow internal networks and specific countries while blocking everything else:

internal.example.com {
    @allowed {
        ipfilter_geolocation {
            allow_ips 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12 ::1/128
            allow_countries AU US
            block_by_default true
        }
    }

    handle @allowed {
        reverse_proxy localhost:3000
    }

    handle {
        respond "Access denied" 403
    }
}
5. Mixed IP and Country Rules

Block specific IP ranges while allowing certain countries:

api.example.com {
    @allowed {
        ipfilter_geolocation {
            deny_ips 203.0.113.0/24 198.51.100.0/24
            allow_countries AU US CA GB
            block_by_default true
        }
    }

    handle @allowed {
        reverse_proxy localhost:8080
    }

    handle {
        respond "Forbidden" 403
    }
}
6. Debug Logging

Enable debug logging to see IP-to-country mappings:

{
  "logging": {
    "logs": {
      "default": {
        "level": "debug"
      }
    }
  }
}

This will show logs like:

{"level":"debug","logger":"http.matchers.ipfilter_geolocation","msg":"IPFilter geolocation check","ip":"1.2.3.4","country":"AU","allowed":true}

πŸ”§ Technical Details

Plugin Architecture
  • Module ID: http.matchers.ipfilter_geolocation
  • Type: HTTP Request Matcher
  • Dependencies: jpillora/ipfilter (with embedded IP2Location LITE data)
How It Works
  1. IP Extraction: Gets the client IP from Caddy's request context
  2. Geolocation Lookup: Uses embedded IP2Location database to find country
  3. Rule Evaluation: Applies allow/deny rules based on configuration
  4. Request Routing: Continues or blocks request processing
Performance
  • Lookup Time: Sub-millisecond geolocation lookups
  • Memory Usage: ~50MB for embedded database
  • Thread Safety: Safe for concurrent access
  • No External Calls: All data is local
Accuracy Notes

IP geolocation is inherently approximate:

  • Mobile networks may show incorrect locations
  • VPNs and proxies can mask real locations
  • Corporate networks often appear in headquarters country
  • Accuracy is typically 95%+ for broadband connections

πŸ§ͺ Testing

Manual Testing
  1. Start your server:

    caddy run --config your-config.json
    
  2. Test from different locations:

    # Test from current location
    curl https://your-domain.com/
    
    # Test with VPN (if available)
    # Connect to VPN in different country and test again
    
  3. Check logs for geolocation data:

    caddy run --config your-config.json 2>&1 | grep "IPFilter geolocation"
    
Automated Testing
package main

import (
    "net/http"
    "net/http/httptest"
    "github.com/caddyserver/caddy/v2/modules/caddyhttp"
    "github.com/jpillora/ipfilter-caddy"
)

func TestAustraliaFilter(t *testing.T) {
    // Create matcher
    matcher := &IPFilterGeolocation{
        AllowCountries: []string{"AU"},
        BlockByDefault: true,
    }

    // Mock Australian IP request
    req := httptest.NewRequest("GET", "/", nil)
    req = req.WithContext(caddyhttp.WithRemoteAddr(req.Context(), "1.1.1.1:12345")) // Cloudflare AU IP

    if !matcher.Match(req) {
        t.Error("Expected Australian IP to be allowed")
    }
}

🀝 Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

Development Setup
  1. Clone the repository:

    git clone https://github.com/jpillora/ipfilter-caddy.git
    cd ipfilter-caddy
    
  2. Install dependencies:

    go mod download
    
  3. Build and test:

    go build
    go test ./...
    
  4. Build with Caddy:

    xcaddy build --with ./...
    
Guidelines
  • Follow Go best practices
  • Add tests for new features
  • Update documentation
  • Ensure thread safety
  • Keep dependencies minimal

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

πŸ“ž Support


Made with ❀️ for the Caddy community

Documentation ΒΆ

Index ΒΆ

Constants ΒΆ

This section is empty.

Variables ΒΆ

This section is empty.

Functions ΒΆ

This section is empty.

Types ΒΆ

type IPFilterGeolocation ΒΆ

type IPFilterGeolocation struct {
	// A list of countries that the filter will allow.
	// If you specify this, you should not specify DenyCountries.
	// If both are specified, DenyCountries will take precedence.
	// All countries that are not in this list will be denied.
	// You can specify the special value "UNK" to match unrecognized countries.
	AllowCountries []string `json:"allow_countries,omitempty"`

	// A list of countries that the filter will deny.
	// If you specify this, you should not specify AllowCountries.
	// If both are specified, DenyCountries will take precedence.
	// All countries that are not in this list will be allowed.
	// You can specify the special value "UNK" to match unrecognized countries.
	DenyCountries []string `json:"deny_countries,omitempty"`

	// A list of IP addresses or CIDR blocks to allow.
	// Takes precedence over country rules and deny_ips.
	// Examples: "192.168.1.1", "10.0.0.0/8", "2001:db8::/32"
	AllowIPs []string `json:"allow_ips,omitempty"`

	// A list of IP addresses or CIDR blocks to deny.
	// Takes precedence over country rules but not allow_ips.
	// Examples: "192.168.1.1", "10.0.0.0/8", "2001:db8::/32"
	DenyIPs []string `json:"deny_ips,omitempty"`

	// BlockByDefault sets the default behavior when no allow/deny rules match.
	// When true, requests are blocked by default unless explicitly allowed.
	// When false, requests are allowed by default unless explicitly denied.
	BlockByDefault bool `json:"block_by_default,omitempty"`
	// contains filtered or unexported fields
}

IPFilterGeolocation allows filtering requests based on source IP country using jpillora/ipfilter.

func (IPFilterGeolocation) CaddyModule ΒΆ

func (IPFilterGeolocation) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information.

func (*IPFilterGeolocation) Cleanup ΒΆ

func (m *IPFilterGeolocation) Cleanup() error

Cleanup cleans up the module resources.

func (*IPFilterGeolocation) Match ΒΆ

func (m *IPFilterGeolocation) Match(r *http.Request) bool

Match checks if the request matches the geolocation criteria.

func (*IPFilterGeolocation) Provision ΒΆ

func (m *IPFilterGeolocation) Provision(ctx caddy.Context) error

Provision sets up the module.

func (*IPFilterGeolocation) UnmarshalCaddyfile ΒΆ

func (m *IPFilterGeolocation) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

UnmarshalCaddyfile parses the Caddyfile configuration.

The matcher configuration will have a single block with the following parameters:

- `allow_countries`: a space-separated list of allowed countries - `deny_countries`: a space-separated list of denied countries - `allow_ips`: a space-separated list of allowed IPs or CIDR blocks - `deny_ips`: a space-separated list of denied IPs or CIDR blocks - `block_by_default`: whether to block by default (true/false)

Examples:

ipfilter_geolocation {
	allow_countries AU US CA
	block_by_default true
}

ipfilter_geolocation {
	deny_countries RU CN
}

ipfilter_geolocation {
	allow_ips 192.168.1.0/24 10.0.0.0/8
	block_by_default true
}

ipfilter_geolocation {
	deny_ips 203.0.113.0/24
	allow_countries AU US
}

func (*IPFilterGeolocation) Validate ΒΆ

func (m *IPFilterGeolocation) Validate() error

Validate validates the module configuration.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL