From 727c6e6d3254ba0118ff41d786eb447aa05cb061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=98=AD?= <81747598+lan-yonghui@users.noreply.github.com> Date: Tue, 18 Mar 2025 17:16:44 +0800 Subject: [PATCH] feat: API interface IP whitelist supports IPv6 (#8182) --- agent/app/api/v2/container.go | 2 +- core/middleware/session.go | 41 ++++++++++++++----- core/utils/common/common.go | 19 +++++++++ frontend/src/utils/util.ts | 2 +- .../setting/panel/api-interface/index.vue | 10 +++-- 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/agent/app/api/v2/container.go b/agent/app/api/v2/container.go index aeb64d29e..68717a83d 100644 --- a/agent/app/api/v2/container.go +++ b/agent/app/api/v2/container.go @@ -410,7 +410,7 @@ func (b *BaseApi) ContainerOperation(c *gin.Context) { // @Tags Container // @Summary Container stats -// @Param id path integer true "容器id" +// @Param id path string true "容器id" // @Success 200 {object} dto.ContainerStats // @Security ApiKeyAuth // @Security Timestamp diff --git a/core/middleware/session.go b/core/middleware/session.go index 298e6aabc..d258d24d2 100644 --- a/core/middleware/session.go +++ b/core/middleware/session.go @@ -12,6 +12,7 @@ import ( "github.com/1Panel-dev/1Panel/core/app/repo" "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" + "github.com/1Panel-dev/1Panel/core/utils/common" "github.com/gin-gonic/gin" ) @@ -77,18 +78,25 @@ func SessionAuth() gin.HandlerFunc { func isValid1PanelTimestamp(panelTimestamp string) bool { apiKeyValidityTime := global.Api.ApiKeyValidityTime apiTime, err := strconv.Atoi(apiKeyValidityTime) - if err != nil { + if err != nil || apiTime < 0 { + global.LOG.Errorf("apiTime %d, err: %v", apiTime, err) return false } + if apiTime == 0 { + return true + } panelTime, err := strconv.ParseInt(panelTimestamp, 10, 64) if err != nil { + global.LOG.Errorf("panelTimestamp %s, panelTime %d, apiTime %d, err: %v", panelTimestamp, apiTime, panelTime, err) return false } nowTime := time.Now().Unix() - if panelTime > nowTime { + tolerance := int64(60) + if panelTime > nowTime+tolerance { + global.LOG.Errorf("Valid Panel Timestamp, apiTime %d, panelTime %d, nowTime %d, err: %v", apiTime, panelTime, nowTime, err) return false } - return apiTime == 0 || nowTime-panelTime <= int64(apiTime*60) + return nowTime-panelTime <= int64(apiTime)*60+tolerance } func isValid1PanelToken(panelToken string, panelTimestamp string) bool { @@ -98,27 +106,38 @@ func isValid1PanelToken(panelToken string, panelTimestamp string) bool { func isIPInWhiteList(clientIP string) bool { ipWhiteString := global.Api.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/core/utils/common/common.go b/core/utils/common/common.go index f45fbe4e2..8919bb688 100644 --- a/core/utils/common/common.go +++ b/core/utils/common/common.go @@ -186,3 +186,22 @@ func incIP(ip net.IP) { } } } + +func HandleIPList(content string) ([]string, error) { + ipList := strings.Split(content, "\n") + var res []string + for _, ip := range ipList { + if ip == "" { + continue + } + if net.ParseIP(ip) != nil { + res = append(res, ip) + continue + } + if _, _, err := net.ParseCIDR(ip); err != nil { + return nil, err + } + res = append(res, ip) + } + return res, nil +} diff --git a/frontend/src/utils/util.ts b/frontend/src/utils/util.ts index 5546a70be..a2215e803 100644 --- a/frontend/src/utils/util.ts +++ b/frontend/src/utils/util.ts @@ -401,7 +401,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 0646b26cb..6e4928e42 100644 --- a/frontend/src/views/setting/panel/api-interface/index.vue +++ b/frontend/src/views/setting/panel/api-interface/index.vue @@ -75,7 +75,7 @@ import { Rules } from '@/global/form-rules'; import i18n from '@/lang'; import { MsgSuccess } from '@/utils/message'; import { ElMessageBox, FormInstance } from 'element-plus'; -import { checkCidr, checkIp } from '@/utils/util'; +import { checkCidr, checkCidrV6, checkIpV4V6 } from '@/utils/util'; import { GlobalStore } from '@/store'; const globalStore = GlobalStore(); @@ -146,10 +146,14 @@ function checkIPs(rule: any, value: any, callback: any) { continue; } if (item.indexOf('/') !== -1) { - if (checkCidr(item)) { + 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'))); } }