mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 12:44:22 +00:00
6.8 KiB
6.8 KiB
IP Limit Live Integration Guide
Overview
The IP Limit feature provides IP-based access control. Clients can connect from multiple IPs up to a configured limit while maintaining security.
Architecture
Database Schema
CREATE TABLE inbound_client_ips (
id INTEGER PRIMARY KEY AUTOINCREMENT,
client_email TEXT UNIQUE NOT NULL,
ips TEXT NOT NULL DEFAULT '',
created_at INTEGER DEFAULT 0,
updated_at INTEGER DEFAULT 0
);
IP Storage Format
IPs are stored as comma-separated values:
client_email: user@example.com
ips: 192.168.1.100,203.0.113.45,198.51.100.200
Components
1. IPLimitService (web/service/ip_limit_service.go)
Core IP limit functionality:
- CheckIPLimit(email, limit, newIP) - Validates if a new IP exceeds the limit
- RecordIPAccess(email, ip) - Records IP access
- GetClientIPs(email) - Retrieves all IPs for a client
- RemoveIP(email, ipToRemove) - Removes a specific IP
- ClearAllIPs(email) - Clears all IPs for a client
2. Database Model (database/model/ip_limit_model.go)
Defines the InboundClientIPs model.
3. API Endpoints (web/controller/ip_limit_api.go)
GET /api/client/ips/:email
Returns all IPs for a client.
curl http://localhost:2053/api/client/ips/user@example.com
Response:
{
"msg": "success",
"ips": [
"192.168.1.100",
"203.0.113.45"
]
}
DELETE /api/client/ips/:email/:ip
Removes a specific IP.
curl -X DELETE http://localhost:2053/api/client/ips/user@example.com/192.168.1.100
DELETE /api/client/ips/:email
Clears all IPs for a client.
curl -X DELETE http://localhost:2053/api/client/ips/user@example.com
4. Access Service (web/service/inbound_client_access_service.go)
Integrated access control:
- CheckClientAccess(email, ip, limitIP) - Live IP validation and logging
- ValidateClientIP(email, ip, limitIP) - Pure validation
- GetClientIPList(email) - Get all registered IPs
- RemoveClientIP(email, ip) - Remove specific IP
Usage Example
In Login Handler
package controller
import (
"github.com/gin-gonic/gin"
"github.com/mhsanaei/3x-ui/v3/web/service"
)
func (a *APIController) Login(ctx *gin.Context) {
var req LoginRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.AbortWithStatusJSON(400, gin.H{"msg": "Invalid request"})
return
}
// Validate credentials
client, err := a.getClientByEmail(req.Email)
if err != nil {
ctx.AbortWithStatusJSON(401, gin.H{"msg": "Invalid credentials"})
return
}
// Get client IP
clientIP := ctx.ClientIP()
// Create inbound service
inboundSvc := &service.InboundService{}
// Check IP limit (LIVE INTEGRATION)
allowed, message, err := inboundSvc.CheckClientAccess(
client.Email,
clientIP,
client.LimitIP, // IP limit from client config
)
if !allowed {
ctx.AbortWithStatusJSON(403, gin.H{
"msg": message,
"error": "ip_limit_exceeded",
})
return
}
// Issue login token
token := generateToken(client)
ctx.JSON(200, gin.H{
"msg": "login_success",
"token": token,
})
}
In Middleware
func IPLimitMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
clientEmail := ctx.GetString("client_email")
clientIP := ctx.ClientIP()
limitIP := ctx.GetInt("limit_ip")
inboundSvc := &service.InboundService{}
allowed, _, err := inboundSvc.CheckClientAccess(
clientEmail,
clientIP,
limitIP,
)
if !allowed || err != nil {
ctx.AbortWithStatusJSON(403, gin.H{
"msg": "Access denied",
})
return
}
ctx.Next()
}
}
Configuration
Client Model Field
In database/model/client.go, add:
type Client struct {
// ... other fields ...
LimitIP int `json:"limitIP"` // Number of allowed IPs (0 = unlimited)
}
API Routes Registration
Register these routes in your router setup:
package router
import (
"github.com/gin-gonic/gin"
"github.com/mhsanaei/3x-ui/v3/web/controller"
)
func SetupRoutes(r *gin.Engine, apiController *controller.APIController) {
api := r.Group("/api")
{
client := api.Group("/client")
{
// IP management endpoints
client.GET("/ips/:email", apiController.GetClientIPs)
client.DELETE("/ips/:email/:ip", apiController.ClearClientIP)
client.DELETE("/ips/:email", apiController.ClearAllClientIPs)
}
}
}
Database Migration
Call migration in your startup code:
package main
import (
"github.com/mhsanaei/3x-ui/v3/database"
)
func init() {
db := database.GetDB()
database.MigrateIPLimit(db)
}
Live Integration Workflow
Client Login Request
↓
[Extract] Client IP from request
↓
[Validate] Credentials
↓
[Check] IP Limit via CheckClientAccess()
├─ Database lookup for existing IPs
├─ Count unique IPs from last 30 days
├─ Compare with limit threshold
└─ If exceeded: Return 403 error
↓
[Record] IP access in database
↓
[Issue] Login token
Testing
Get Client IPs
curl -X GET http://localhost:2053/api/client/ips/user@example.com
Remove Specific IP
curl -X DELETE http://localhost:2053/api/client/ips/user@example.com/192.168.1.100
Clear All IPs
curl -X DELETE http://localhost:2053/api/client/ips/user@example.com
Login Test with IP Check
curl -X POST http://localhost:2053/api/client/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"secret": "client-secret"
}'
Security Considerations
- IP Spoofing Prevention - Use
X-Forwarded-Forheader carefully in production - Stale IP Cleanup - Automatically clean up old IPs after 30 days
- Rate Limiting - Implement rate limiting on IP registration
- Logging - Log all IP registration and access attempts
- Load Balancer - Ensure consistent IP detection behind load balancers
Troubleshooting
IP Not Being Recorded
- Check if
RecordIPAccess()is being called - Verify database permissions
- Check logs for SQL errors
Limit Not Enforced
- Verify
LimitIPvalue in client configuration - Check if
CheckIPLimit()is called before granting access - Ensure database migration ran successfully
Wrong IP Detected
- Check client IP extraction logic
- Verify load balancer configuration
- Check
X-Forwarded-Forheader handling
Future Enhancements
- IP geolocation tracking
- VPN/Proxy detection
- IP reputation scoring
- Automatic IP whitelisting
- IP-based access policies
- IP anomaly detection