feat(auth): enhance IP restriction handling with CIDR support

This commit is contained in:
CaIon
2025-12-15 17:24:09 +08:00
parent 4ea8cbd207
commit 39593052b6
12 changed files with 63 additions and 47 deletions

View File

@@ -2,6 +2,15 @@ package common
import "net"
func IsIP(s string) bool {
ip := net.ParseIP(s)
return ip != nil
}
func ParseIP(s string) net.IP {
return net.ParseIP(s)
}
func IsPrivateIP(ip net.IP) bool {
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
return true
@@ -20,3 +29,23 @@ func IsPrivateIP(ip net.IP) bool {
}
return false
}
func IsIpInCIDRList(ip net.IP, cidrList []string) bool {
for _, cidr := range cidrList {
_, network, err := net.ParseCIDR(cidr)
if err != nil {
// 尝试作为单个IP处理
if whitelistIP := net.ParseIP(cidr); whitelistIP != nil {
if ip.Equal(whitelistIP) {
return true
}
}
continue
}
if network.Contains(ip) {
return true
}
}
return false
}

View File

@@ -186,23 +186,7 @@ func isIPListed(ip net.IP, list []string) bool {
return false
}
for _, whitelistCIDR := range list {
_, network, err := net.ParseCIDR(whitelistCIDR)
if err != nil {
// 尝试作为单个IP处理
if whitelistIP := net.ParseIP(whitelistCIDR); whitelistIP != nil {
if ip.Equal(whitelistIP) {
return true
}
}
continue
}
if network.Contains(ip) {
return true
}
}
return false
return IsIpInCIDRList(ip, list)
}
// IsIPAccessAllowed 检查IP是否允许访问

View File

@@ -217,11 +217,6 @@ func IntMax(a int, b int) int {
}
}
func IsIP(s string) bool {
ip := net.ParseIP(s)
return ip != nil
}
func GetUUID() string {
code := uuid.New().String()
code = strings.Replace(code, "-", "", -1)

View File

@@ -2,12 +2,14 @@ package middleware
import (
"fmt"
"net"
"net/http"
"strconv"
"strings"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/constant"
"github.com/QuantumNous/new-api/logger"
"github.com/QuantumNous/new-api/model"
"github.com/QuantumNous/new-api/service"
"github.com/QuantumNous/new-api/setting/ratio_setting"
@@ -240,13 +242,20 @@ func TokenAuth() func(c *gin.Context) {
return
}
allowIpsMap := token.GetIpLimitsMap()
if len(allowIpsMap) != 0 {
allowIpsMap := token.GetIpLimits()
if len(allowIpsMap) > 0 {
clientIp := c.ClientIP()
if _, ok := allowIpsMap[clientIp]; !ok {
logger.LogDebug(c, "Token has IP restrictions, checking client IP %s", clientIp)
ip := net.ParseIP(clientIp)
if ip == nil {
abortWithOpenAiMessage(c, http.StatusForbidden, "无法解析客户端 IP 地址")
return
}
if common.IsIpInCIDRList(ip, allowIpsMap) == false {
abortWithOpenAiMessage(c, http.StatusForbidden, "您的 IP 不在令牌允许访问的列表中")
return
}
logger.LogDebug(c, "Client IP %s passed the token IP restrictions check", clientIp)
}
userCache, err := model.GetUserCache(token.UserId)

View File

@@ -6,7 +6,6 @@ import (
"strings"
"github.com/QuantumNous/new-api/common"
"github.com/bytedance/gopkg/util/gopool"
"gorm.io/gorm"
)
@@ -35,26 +34,26 @@ func (token *Token) Clean() {
token.Key = ""
}
func (token *Token) GetIpLimitsMap() map[string]any {
func (token *Token) GetIpLimits() []string {
// delete empty spaces
//split with \n
ipLimitsMap := make(map[string]any)
ipLimits := make([]string, 0)
if token.AllowIps == nil {
return ipLimitsMap
return ipLimits
}
cleanIps := strings.ReplaceAll(*token.AllowIps, " ", "")
if cleanIps == "" {
return ipLimitsMap
return ipLimits
}
ips := strings.Split(cleanIps, "\n")
for _, ip := range ips {
ip = strings.TrimSpace(ip)
ip = strings.ReplaceAll(ip, ",", "")
if common.IsIP(ip) {
ipLimitsMap[ip] = true
if ip != "" {
ipLimits = append(ipLimits, ip)
}
}
return ipLimitsMap
return ipLimits
}
func GetAllUserTokens(userId int, startIdx int, num int) ([]*Token, error) {

View File

@@ -557,11 +557,11 @@ const EditTokenModal = (props) => {
<Col span={24}>
<Form.TextArea
field='allow_ips'
label={t('IP白名单')}
label={t('IP白名单支持CIDR表达式')}
placeholder={t('允许的IP一行一个不填写则不限制')}
autosize
rows={1}
extraText={t('请勿过度信任此功能IP可能被伪造')}
extraText={t('请勿过度信任此功能IP可能被伪造请配合nginx和cdn等网关使用')}
showClear
style={{ width: '100%' }}
/>

View File

@@ -97,7 +97,7 @@
"Homepage URL 填": "Fill in the Homepage URL",
"ID": "ID",
"IP": "IP",
"IP白名单": "IP whitelist",
"IP白名单支持CIDR表达式": "IP whitelist (supports CIDR expressions)",
"IP限制": "IP restrictions",
"IP黑名单": "IP blacklist",
"JSON": "JSON",
@@ -1752,7 +1752,7 @@
"请先阅读并同意用户协议和隐私政策": "Please read and agree to the user agreement and privacy policy first",
"请再次输入新密码": "Please enter the new password again",
"请前往个人设置 → 安全设置进行配置。": "Please go to Personal Settings → Security Settings to configure.",
"请勿过度信任此功能IP可能被伪造": "Do not over-trust this feature, IP can be spoofed",
"请勿过度信任此功能IP可能被伪造请配合nginx和cdn等网关使用": "Do not over-trust this feature, IP can be spoofed, please use it in conjunction with gateways such as nginx and CDN",
"请在系统设置页面编辑分组倍率以添加新的分组:": "Please edit Group ratios in system settings to add new groups:",
"请填写完整的产品信息": "Please fill in complete product information",
"请填写完整的管理员账号信息": "Please fill in the complete administrator account information",

View File

@@ -99,7 +99,7 @@
"Homepage URL 填": "Remplir l'URL de la page d'accueil",
"ID": "ID",
"IP": "IP",
"IP白名单": "Liste blanche d'adresses IP",
"IP白名单支持CIDR表达式": "Liste blanche d'adresses IP (prise en charge des expressions CIDR)",
"IP限制": "Restrictions d'IP",
"IP黑名单": "Liste noire d'adresses IP",
"JSON": "JSON",
@@ -1762,7 +1762,7 @@
"请先阅读并同意用户协议和隐私政策": "Veuillez d'abord lire et accepter l'accord utilisateur et la politique de confidentialité",
"请再次输入新密码": "Veuillez saisir à nouveau le nouveau mot de passe",
"请前往个人设置 → 安全设置进行配置。": "Veuillez aller dans Paramètres personnels → Paramètres de sécurité pour configurer.",
"请勿过度信任此功能IP可能被伪造": "Ne faites pas trop confiance à cette fonctionnalité, l'IP peut être usurpée",
"请勿过度信任此功能IP可能被伪造请配合nginx和cdn等网关使用": "Ne faites pas trop confiance à cette fonctionnalité, l'IP peut être usurpée, veuillez l'utiliser en conjonction avec des passerelles telles que nginx et cdn",
"请在系统设置页面编辑分组倍率以添加新的分组:": "Veuillez modifier les ratios de groupe dans les paramètres système pour ajouter de nouveaux groupes :",
"请填写完整的产品信息": "Veuillez renseigner l'ensemble des informations produit",
"请填写完整的管理员账号信息": "Veuillez remplir les informations complètes du compte administrateur",

View File

@@ -82,7 +82,7 @@
"Homepage URL 填": "ホームページURLを入力してください",
"ID": "ID",
"IP": "IP",
"IP白名单": "IPホワイトリスト",
"IP白名单支持CIDR表达式": "IPホワイトリストCIDR表記に対応",
"IP限制": "IP制限",
"IP黑名单": "IPブラックリスト",
"JSON": "JSON",
@@ -1669,7 +1669,7 @@
"请先阅读并同意用户协议和隐私政策": "まずユーザー利用規約とプライバシーポリシーをご確認の上、同意してください",
"请再次输入新密码": "新しいパスワードを再入力してください",
"请前往个人设置 → 安全设置进行配置。": "アカウント設定 → セキュリティ設定 にて設定してください。",
"请勿过度信任此功能IP可能被伪造": "IPは偽装される可能性があるため、この機能を過信しないでください",
"请勿过度信任此功能IP可能被伪造请配合nginx和cdn等网关使用": "IPは偽装される可能性があるため、この機能を過信しないでください。nginxやCDNなどのゲートウェイと組み合わせて使用してください。",
"请在系统设置页面编辑分组倍率以添加新的分组:": "新規グループを追加するには、システム設定ページでグループ倍率を編集してください:",
"请填写完整的管理员账号信息": "管理者アカウント情報をすべて入力してください",
"请填写密钥": "APIキーを入力してください",

View File

@@ -101,7 +101,7 @@
"Homepage URL 填": "URL домашней страницы:",
"ID": "ID",
"IP": "IP",
"IP白名单": "Белый список IP",
"IP白名单支持CIDR表达式": "Белый список IP (поддерживает выражения CIDR)",
"IP限制": "Ограничения IP",
"IP黑名单": "Черный список IP",
"JSON": "JSON",
@@ -1773,7 +1773,7 @@
"请先阅读并同意用户协议和隐私政策": "Пожалуйста, сначала прочтите и согласитесь с пользовательским соглашением и политикой конфиденциальности",
"请再次输入新密码": "Пожалуйста, введите новый пароль ещё раз",
"请前往个人设置 → 安全设置进行配置。": "Пожалуйста, перейдите в Личные настройки → Настройки безопасности для конфигурации.",
"请勿过度信任此功能IP可能被伪造": "Не доверяйте этой функции чрезмерно, IP может быть подделан",
"请勿过度信任此功能IP可能被伪造请配合nginx和cdn等网关使用": "Не доверяйте этой функции чрезмерно, IP может быть подделан, используйте её вместе с nginx и CDN и другими шлюзами",
"请在系统设置页面编辑分组倍率以添加新的分组:": "Пожалуйста, отредактируйте коэффициенты групп на странице системных настроек для добавления новой группы:",
"请填写完整的产品信息": "Пожалуйста, заполните всю информацию о продукте",
"请填写完整的管理员账号信息": "Пожалуйста, заполните полную информацию об учётной записи администратора",

View File

@@ -82,7 +82,7 @@
"Homepage URL 填": "Điền URL trang chủ",
"ID": "ID",
"IP": "IP",
"IP白名单": "Danh sách trắng IP",
"IP白名单支持CIDR表达式": "Danh sách trắng IP (hỗ trợ biểu thức CIDR)",
"IP限制": "Hạn chế IP",
"IP黑名单": "Danh sách đen IP",
"JSON": "JSON",
@@ -1987,7 +1987,7 @@
"请先阅读并同意用户协议和隐私政策": "Vui lòng đọc và đồng ý với thỏa thuận người dùng và chính sách bảo mật trước",
"请再次输入新密码": "Vui lòng nhập lại mật khẩu mới",
"请前往个人设置 → 安全设置进行配置。": "Vui lòng truy cập Cài đặt cá nhân → Cài đặt bảo mật để cấu hình.",
"请勿过度信任此功能IP可能被伪造": "Đừng quá tin tưởng tính năng này, IP có thể bị giả mạo",
"请勿过度信任此功能IP可能被伪造请配合nginx和cdn等网关使用": "Đừng quá tin tưởng tính năng này, IP có thể bị giả mạo, vui lòng sử dụng cùng với nginx và các cổng khác như cdn",
"请在系统设置页面编辑分组倍率以添加新的分组:": "Vui lòng chỉnh sửa tỷ lệ nhóm trên trang cài đặt hệ thống để thêm nhóm mới:",
"请填写完整的管理员账号信息": "Vui lòng điền đầy đủ thông tin tài khoản quản trị viên",
"请填写密钥": "Vui lòng điền khóa",

View File

@@ -95,7 +95,7 @@
"Homepage URL 填": "Homepage URL 填",
"ID": "ID",
"IP": "IP",
"IP白名单": "IP白名单",
"IP白名单支持CIDR表达式": "IP白名单支持CIDR表达式",
"IP限制": "IP限制",
"IP黑名单": "IP黑名单",
"JSON": "JSON",
@@ -1740,7 +1740,7 @@
"请先阅读并同意用户协议和隐私政策": "请先阅读并同意用户协议和隐私政策",
"请再次输入新密码": "请再次输入新密码",
"请前往个人设置 → 安全设置进行配置。": "请前往个人设置 → 安全设置进行配置。",
"请勿过度信任此功能IP可能被伪造": "请勿过度信任此功能IP可能被伪造",
"请勿过度信任此功能IP可能被伪造请配合nginx和cdn等网关使用": "请勿过度信任此功能IP可能被伪造请配合nginx和cdn等网关使用",
"请在系统设置页面编辑分组倍率以添加新的分组:": "请在系统设置页面编辑分组倍率以添加新的分组:",
"请填写完整的产品信息": "请填写完整的产品信息",
"请填写完整的管理员账号信息": "请填写完整的管理员账号信息",