Documentation Index
Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
Use this file to discover all available pages before exploring further.
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
| Option | Type | Default | Description |
|---|
AllowList | []string | - | IPs/CIDRs to allow |
DenyList | []string | - | IPs/CIDRs to deny |
DenyByDefault | bool | false | Deny unless in allow list |
TrustProxy | bool | false | Use X-Forwarded-For |
ErrorHandler | func(*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:
| Format | Description | Example |
|---|
| Single IP | Exact match | 192.168.1.100 |
| IPv4 CIDR | Network range | 192.168.1.0/24 |
| IPv6 | IPv6 address | ::1 |
| IPv6 CIDR | IPv6 range | fc00::/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:
-
Network Parsing: IP addresses and CIDR blocks are parsed at initialization time using
parseNetworks(), converting them to *net.IPNet structures for efficient matching.
-
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)
-
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.
-
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)
-
Error Handling: Uses a custom
handleDenied() function that either calls the user-provided ErrorHandler or returns a default 403 Forbidden response.
- 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
- Proxy Headers - Only enable
TrustProxy if behind a trusted proxy
- IP Spoofing - X-Forwarded-For can be spoofed if not behind trusted proxy
- IPv6 - Consider both IPv4 and IPv6 addresses
- 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 Case | Description | Expected Behavior |
|---|
TestAllow/allows listed IP | Request from IP within allowed CIDR range (192.168.1.100 in 192.168.1.0/24) | Returns 200 OK |
TestAllow/allows specific IP | Request from specific allowed IP (10.0.0.1) | Returns 200 OK |
TestAllow/denies unlisted IP | Request from IP not in allow list (203.0.113.1) | Returns 403 Forbidden |
TestDeny/denies listed IP | Request from IP in deny list CIDR range (192.168.1.100 in 192.168.1.0/24) | Returns 403 Forbidden |
TestDeny/allows unlisted IP | Request from IP not in deny list (10.0.0.1) | Returns 200 OK |
TestNew_DenyTakesPrecedence | IP present in both allow and deny lists (192.168.1.100) | Returns 403 Forbidden (deny wins) |
TestNew_TrustProxy | Request with X-Forwarded-For header when TrustProxy enabled | Uses X-Forwarded-For IP for filtering |
TestNew_ErrorHandler | Custom error handler when IP is blocked | Calls 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 localhost | Returns 200 OK |
TestPrivate (8.8.8.8) | Request from public IP | Returns 403 Forbidden |
TestLocalhost/allows localhost | Request from 127.0.0.1 | Returns 200 OK |
TestLocalhost/denies non-localhost | Request from non-localhost IP (192.168.1.1) | Returns 403 Forbidden |