Skip to main content

Overview

The ipfilter middleware filters requests based on client IP addresses. Use it to allow or deny access from specific IPs or IP ranges (CIDR notation). Use it when you need:
  • Whitelist access to admin panels
  • Block known malicious IPs
  • Restrict access to internal networks
  • Geo-blocking based on IP ranges

Installation

import "github.com/go-mizu/mizu/middlewares/ipfilter"

Quick Start

app := mizu.New()

// Allow only specific IPs
app.Use(ipfilter.Allow("192.168.1.0/24", "10.0.0.1"))

// Block specific IPs
app.Use(ipfilter.Deny("192.168.1.100", "10.0.0.0/8"))

Configuration

Options

OptionTypeDefaultDescription
AllowList[]string-IPs/CIDRs to allow
DenyList[]string-IPs/CIDRs to deny
DenyByDefaultboolfalseDeny unless in allow list
TrustProxyboolfalseUse X-Forwarded-For
ErrorHandlerfunc(*mizu.Ctx) error-Custom denial handler

Examples

Whitelist Mode

Only allow specific IPs, deny everything else:
app.Use(ipfilter.Allow(
    "192.168.1.0/24",  // Office network
    "10.0.0.5",        // Specific server
    "203.0.113.50",    // Remote developer
))

Blacklist Mode

Allow everything except specific IPs:
app.Use(ipfilter.Deny(
    "192.168.1.100",   // Blocked user
    "10.0.0.0/8",      // Blocked network
))

Combined Allow and Deny

app.Use(ipfilter.New(ipfilter.Options{
    AllowList: []string{"192.168.1.0/24"},
    DenyList:  []string{"192.168.1.100"},  // Even within allowed range
}))

Localhost Only

// Only allow localhost connections
app.Use(ipfilter.Localhost())

Private Networks Only

// Only allow private network IPs
app.Use(ipfilter.Private())

Behind a Proxy

app.Use(ipfilter.New(ipfilter.Options{
    AllowList:  []string{"203.0.113.0/24"},
    TrustProxy: true, // Use X-Forwarded-For header
}))

Custom Error Handler

app.Use(ipfilter.New(ipfilter.Options{
    AllowList:     []string{"192.168.1.0/24"},
    DenyByDefault: true,
    ErrorHandler: func(c *mizu.Ctx) error {
        return c.JSON(403, map[string]string{
            "error": "Access denied from your location",
            "ip":    c.ClientIP(),
        })
    },
}))

Route-Specific Filtering

// Public routes
app.Get("/", publicHandler)
app.Get("/api", apiHandler)

// Admin routes - localhost only
adminFilter := ipfilter.Localhost()
app.Get("/admin", adminHandler, adminFilter)
app.Post("/admin/config", configHandler, adminFilter)

Group Filtering

admin := app.Group("/admin")
admin.Use(ipfilter.Allow("192.168.1.0/24"))

admin.Get("/", adminDashboard)
admin.Get("/users", listUsers)

Dynamic IP List

func dynamicFilter() mizu.Middleware {
    var blockedIPs sync.Map

    // Background goroutine to update blocked IPs
    go func() {
        for {
            ips := fetchBlockedIPs() // From database/API
            for _, ip := range ips {
                blockedIPs.Store(ip, true)
            }
            time.Sleep(5 * time.Minute)
        }
    }()

    return func(next mizu.Handler) mizu.Handler {
        return func(c *mizu.Ctx) error {
            if _, blocked := blockedIPs.Load(c.ClientIP()); blocked {
                return c.Text(403, "Forbidden")
            }
            return next(c)
        }
    }
}

API Reference

Functions

// Allow creates whitelist middleware (deny by default)
func Allow(ips ...string) mizu.Middleware

// Deny creates blacklist middleware
func Deny(ips ...string) mizu.Middleware

// New creates middleware with full options
func New(opts Options) mizu.Middleware

// Private allows only private network IPs
func Private() mizu.Middleware

// Localhost allows only localhost
func Localhost() mizu.Middleware

CIDR Notation

The middleware supports both single IPs and CIDR notation:
FormatDescriptionExample
Single IPExact match192.168.1.100
IPv4 CIDRNetwork range192.168.1.0/24
IPv6IPv6 address::1
IPv6 CIDRIPv6 rangefc00::/7

Common CIDR Blocks

"10.0.0.0/8"       // 10.x.x.x (Class A private)
"172.16.0.0/12"    // 172.16-31.x.x (Class B private)
"192.168.0.0/16"   // 192.168.x.x (Class C private)
"127.0.0.0/8"      // Localhost
"0.0.0.0/0"        // All IPv4
"::/0"             // All IPv6

Technical Details

Implementation Overview

The ipfilter middleware uses Go’s net package to parse and match IP addresses and CIDR ranges efficiently. The implementation follows these key principles:
  1. Network Parsing: IP addresses and CIDR blocks are parsed at initialization time using parseNetworks(), converting them to *net.IPNet structures for efficient matching.
  2. IP Extraction: Client IPs are extracted using one of two methods:
    • Direct extraction from RemoteAddr using extractIP() when not behind a proxy
    • Using c.ClientIP() when TrustProxy is enabled (reads X-Forwarded-For header)
  3. Filtering Logic: The middleware applies a two-phase filtering process:
    • Phase 1 - Deny List Check: First checks if the IP is in the deny list. If found, immediately denies access.
    • Phase 2 - Allow List Check: If DenyByDefault is true, verifies the IP is in the allow list. Denies if not found.
  4. Single IP Normalization: Single IP addresses are automatically converted to CIDR notation:
    • IPv4 addresses: converted to /32 (e.g., 192.168.1.100/32)
    • IPv6 addresses: converted to /128 (e.g., ::1/128)
  5. Error Handling: Uses a custom handleDenied() function that either calls the user-provided ErrorHandler or returns a default 403 Forbidden response.

Performance Considerations

  • IP parsing happens once during middleware initialization, not on every request
  • Network matching uses efficient net.IPNet.Contains() method
  • Deny list is checked before allow list to fail fast on blocked IPs
  • No regex or string manipulation on hot path

Security Considerations

  1. Proxy Headers - Only enable TrustProxy if behind a trusted proxy
  2. IP Spoofing - X-Forwarded-For can be spoofed if not behind trusted proxy
  3. IPv6 - Consider both IPv4 and IPv6 addresses
  4. VPNs/Proxies - Users may bypass IP restrictions using VPNs

Best Practices

  • Use CIDR notation for ranges to simplify management
  • Enable TrustProxy only behind trusted load balancers
  • Combine with other authentication for sensitive areas
  • Log denied attempts for security monitoring

Testing

The ipfilter middleware includes comprehensive test coverage for all filtering scenarios:
Test CaseDescriptionExpected Behavior
TestAllow/allows listed IPRequest from IP within allowed CIDR range (192.168.1.100 in 192.168.1.0/24)Returns 200 OK
TestAllow/allows specific IPRequest from specific allowed IP (10.0.0.1)Returns 200 OK
TestAllow/denies unlisted IPRequest from IP not in allow list (203.0.113.1)Returns 403 Forbidden
TestDeny/denies listed IPRequest from IP in deny list CIDR range (192.168.1.100 in 192.168.1.0/24)Returns 403 Forbidden
TestDeny/allows unlisted IPRequest from IP not in deny list (10.0.0.1)Returns 200 OK
TestNew_DenyTakesPrecedenceIP present in both allow and deny lists (192.168.1.100)Returns 403 Forbidden (deny wins)
TestNew_TrustProxyRequest with X-Forwarded-For header when TrustProxy enabledUses X-Forwarded-For IP for filtering
TestNew_ErrorHandlerCustom error handler when IP is blockedCalls custom error handler with JSON response
TestPrivate (192.168.1.1)Request from private network IP (Class C)Returns 200 OK
TestPrivate (10.0.0.1)Request from private network IP (Class A)Returns 200 OK
TestPrivate (172.16.0.1)Request from private network IP (Class B)Returns 200 OK
TestPrivate (127.0.0.1)Request from localhostReturns 200 OK
TestPrivate (8.8.8.8)Request from public IPReturns 403 Forbidden
TestLocalhost/allows localhostRequest from 127.0.0.1Returns 200 OK
TestLocalhost/denies non-localhostRequest from non-localhost IP (192.168.1.1)Returns 403 Forbidden