From 6c8d48c2dcade00cd54eb43f021edc740a210b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=98=AD?= <81747598+lan-yonghui@users.noreply.github.com> Date: Mon, 24 Feb 2025 18:05:13 +0800 Subject: [PATCH] feat: API interface IP whitelist supports IPv6 (#7981) Refs #7836 --- backend/middleware/session.go | 29 +++++++++++++------ frontend/src/utils/util.ts | 2 +- .../setting/panel/api-interface/index.vue | 14 ++++++--- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/backend/middleware/session.go b/backend/middleware/session.go index c56ce5327..29e5410ca 100644 --- a/backend/middleware/session.go +++ b/backend/middleware/session.go @@ -7,10 +7,10 @@ import ( "github.com/1Panel-dev/1Panel/backend/app/repo" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/gin-gonic/gin" "net" "strconv" - "strings" "time" ) @@ -102,27 +102,38 @@ func isValid1PanelToken(panelToken string, panelTimestamp string) bool { func isIPInWhiteList(clientIP string) bool { ipWhiteString := global.CONF.System.IpWhiteList - ipWhiteList := strings.Split(ipWhiteString, "\n") + if len(ipWhiteString) == 0 { + global.LOG.Error("IP whitelist is empty") + return false + } + ipWhiteList, ipErr := common.HandleIPList(ipWhiteString) + if ipErr != nil { + global.LOG.Errorf("Failed to handle IP list: %v", ipErr) + return false + } + clientParsedIP := net.ParseIP(clientIP) + if clientParsedIP == nil { + return false + } + iPv4 := clientParsedIP.To4() + iPv6 := clientParsedIP.To16() for _, cidr := range ipWhiteList { - if cidr == "0.0.0.0" { + if (iPv4 != nil && (cidr == "0.0.0.0" || cidr == "0.0.0.0/0" || iPv4.String() == cidr)) || (iPv6 != nil && (cidr == "::/0" || iPv6.String() == cidr)) { return true } _, ipNet, err := net.ParseCIDR(cidr) if err != nil { - if cidr == clientIP { - return true - } continue } - if ipNet.Contains(net.ParseIP(clientIP)) { + if (iPv4 != nil && ipNet.Contains(iPv4)) || (iPv6 != nil && ipNet.Contains(iPv6)) { return true } } return false } -func GenerateMD5(input string) string { +func GenerateMD5(param string) string { hash := md5.New() - hash.Write([]byte(input)) + hash.Write([]byte(param)) return hex.EncodeToString(hash.Sum(nil)) } diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts index 2f796e675..32955d5e7 100644 --- a/frontend/src/utils/util.ts +++ b/frontend/src/utils/util.ts @@ -358,7 +358,7 @@ export function checkCidrV6(value: string): boolean { if (checkIpV6(value.split('/')[0])) { return true; } - const reg = /^(?:[1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$/; + const reg = /^(?:[0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])$/; if (!reg.test(value.split('/')[1])) { return true; } diff --git a/frontend/src/views/setting/panel/api-interface/index.vue b/frontend/src/views/setting/panel/api-interface/index.vue index cbf2b1e7f..e6c0e69bd 100644 --- a/frontend/src/views/setting/panel/api-interface/index.vue +++ b/frontend/src/views/setting/panel/api-interface/index.vue @@ -98,7 +98,7 @@ import i18n from '@/lang'; import { MsgSuccess } from '@/utils/message'; import { ElMessageBox, FormInstance } from 'element-plus'; import DrawerHeader from '@/components/drawer-header/index.vue'; -import { checkCidr, checkIp } from '@/utils/util'; +import { checkCidr, checkCidrV6, checkIpV4V6 } from '@/utils/util'; import { GlobalStore } from '@/store'; const globalStore = GlobalStore(); @@ -138,10 +138,16 @@ function checkIPs(rule: any, value: any, callback: any) { continue; } if (item.indexOf('/') !== -1) { - if (checkCidr(item)) { - return callback(new Error(i18n.global.t('firewall.addressFormatError'))); + if (item.indexOf(':') !== -1) { + if (checkCidrV6(item)) { + return callback(new Error(i18n.global.t('firewall.addressFormatError'))); + } + } else { + if (checkCidr(item)) { + return callback(new Error(i18n.global.t('firewall.addressFormatError'))); + } } - } else if (checkIp(item)) { + } else if (checkIpV4V6(item)) { return callback(new Error(i18n.global.t('firewall.addressFormatError'))); } }