2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 03:07:20 +08:00

feat: 支持cidr,close #764

This commit is contained in:
2025-05-26 00:34:24 +08:00
parent adacee1851
commit 03c0b191fe
6 changed files with 72 additions and 8 deletions

View File

@@ -11,7 +11,7 @@ import (
"io"
"net"
"net/http"
"slices"
"strings"
"time"
"github.com/go-rat/utils/str"
@@ -144,7 +144,26 @@ func (r userTokenRepo) ValidateReq(req *http.Request) (uint, error) {
if err != nil {
ip = req.RemoteAddr
}
if !slices.Contains(userToken.IPs, ip) {
allowed := false
requestIP := net.ParseIP(ip)
if requestIP != nil {
for _, allowedIP := range userToken.IPs {
if strings.Contains(allowedIP, "/") {
// CIDR
if _, ipNet, err := net.ParseCIDR(allowedIP); err == nil && ipNet.Contains(requestIP) {
allowed = true
break
}
} else {
// IP
if allowedIP == ip {
allowed = true
break
}
}
}
}
if !allowed {
return 0, errors.New(r.t.Get("invalid request ip: %s", ip))
}
}

View File

@@ -47,9 +47,30 @@ func Entrance(t *gotext.Locale, conf *koanf.Koanf, session *sessions.Manager) fu
if err != nil {
ip = r.RemoteAddr
}
if len(conf.Strings("http.bind_ip")) > 0 && !slices.Contains(conf.Strings("http.bind_ip"), ip) {
Abort(w, http.StatusTeapot, t.Get("invalid request ip: %s", ip))
return
if len(conf.Strings("http.bind_ip")) > 0 {
allowed := false
requestIP := net.ParseIP(ip)
if requestIP != nil {
for _, allowedIP := range conf.Strings("http.bind_ip") {
if strings.Contains(allowedIP, "/") {
// CIDR
if _, ipNet, err := net.ParseCIDR(allowedIP); err == nil && ipNet.Contains(requestIP) {
allowed = true
break
}
} else {
// IP
if allowedIP == ip {
allowed = true
break
}
}
}
}
if !allowed {
Abort(w, http.StatusTeapot, t.Get("invalid request ip: %s", ip))
return
}
}
if len(conf.Strings("http.bind_ua")) > 0 && !slices.Contains(conf.Strings("http.bind_ua"), r.UserAgent()) {
Abort(w, http.StatusTeapot, t.Get("invalid request user agent: %s", r.UserAgent()))

View File

@@ -25,7 +25,7 @@ type SettingPanel struct {
func (r *SettingPanel) Rules(_ *http.Request) map[string]string {
return map[string]string{
"BindDomain.*": "required",
"BindIP.*": "required|ip",
"BindIP.*": "required|ipcidr",
"BindUA.*": "required",
}
}

View File

@@ -15,7 +15,7 @@ type UserTokenCreate struct {
func (r *UserTokenCreate) Rules(_ *http.Request) map[string]string {
return map[string]string{
"IPs.*": "required|ip",
"IPs.*": "required|ipcidr",
}
}
@@ -27,6 +27,6 @@ type UserTokenUpdate struct {
func (r *UserTokenUpdate) Rules(_ *http.Request) map[string]string {
return map[string]string{
"IPs.*": "required|ip",
"IPs.*": "required|ipcidr",
}
}

View File

@@ -0,0 +1,22 @@
package rule
import "net"
// IPCIDR 验证一个值是否是一个有效的 IP 或 CIDR 格式
type IPCIDR struct{}
func NewIPCIDR() *IPCIDR {
return &IPCIDR{}
}
func (r *IPCIDR) Passes(val any, options ...any) bool {
if str, ok := val.(string); ok {
if ip := net.ParseIP(str); ip != nil {
return true // 是有效的 IP
}
if _, _, err := net.ParseCIDR(str); err == nil {
return true // 是有效的 CIDR
}
}
return false // 既不是 IP 也不是 CIDR
}

View File

@@ -11,11 +11,13 @@ func GlobalRules(db *gorm.DB) {
"notExists": NewNotExists(db).Passes,
"password": NewPassword().Passes,
"cron": NewCron().Passes,
"ipcidr": NewIPCIDR().Passes,
})
validate.AddGlobalMessages(map[string]string{
"exists": "{field} 不存在",
"notExists": "{field} 已存在",
"password": "密码不满足要求8-20位至少包含字母、数字、特殊字符中的两种",
"cron": "Cron 表达式不合法",
"ipcidr": "IP 或 CIDR 格式不合法",
})
}