Explorar el Código

feat: 添加域名和ip过滤模式设置

creamlike1024 hace 5 meses
padre
commit
b7bc609a7a

+ 2 - 2
service/download.go

@@ -30,7 +30,7 @@ func DoWorkerRequest(req *WorkerRequest) (*http.Response, error) {
 
 
 	// SSRF防护:验证请求URL
 	// SSRF防护:验证请求URL
 	fetchSetting := system_setting.GetFetchSetting()
 	fetchSetting := system_setting.GetFetchSetting()
-	if err := common.ValidateURLWithFetchSetting(req.URL, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.WhitelistDomains, fetchSetting.WhitelistIps, fetchSetting.AllowedPorts); err != nil {
+	if err := common.ValidateURLWithFetchSetting(req.URL, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.DomainList, fetchSetting.IpList, fetchSetting.AllowedPorts); err != nil {
 		return nil, fmt.Errorf("request reject: %v", err)
 		return nil, fmt.Errorf("request reject: %v", err)
 	}
 	}
 
 
@@ -59,7 +59,7 @@ func DoDownloadRequest(originUrl string, reason ...string) (resp *http.Response,
 	} else {
 	} else {
 		// SSRF防护:验证请求URL(非Worker模式)
 		// SSRF防护:验证请求URL(非Worker模式)
 		fetchSetting := system_setting.GetFetchSetting()
 		fetchSetting := system_setting.GetFetchSetting()
-		if err := common.ValidateURLWithFetchSetting(originUrl, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.WhitelistDomains, fetchSetting.WhitelistIps, fetchSetting.AllowedPorts); err != nil {
+		if err := common.ValidateURLWithFetchSetting(originUrl, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.DomainList, fetchSetting.IpList, fetchSetting.AllowedPorts); err != nil {
 			return nil, fmt.Errorf("request reject: %v", err)
 			return nil, fmt.Errorf("request reject: %v", err)
 		}
 		}
 
 

+ 1 - 1
service/user_notify.go

@@ -115,7 +115,7 @@ func sendBarkNotify(barkURL string, data dto.Notify) error {
 	} else {
 	} else {
 		// SSRF防护:验证Bark URL(非Worker模式)
 		// SSRF防护:验证Bark URL(非Worker模式)
 		fetchSetting := system_setting.GetFetchSetting()
 		fetchSetting := system_setting.GetFetchSetting()
-		if err := common.ValidateURLWithFetchSetting(finalURL, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.WhitelistDomains, fetchSetting.WhitelistIps, fetchSetting.AllowedPorts); err != nil {
+		if err := common.ValidateURLWithFetchSetting(finalURL, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.DomainList, fetchSetting.IpList, fetchSetting.AllowedPorts); err != nil {
 			return fmt.Errorf("request reject: %v", err)
 			return fmt.Errorf("request reject: %v", err)
 		}
 		}
 
 

+ 1 - 1
service/webhook.go

@@ -89,7 +89,7 @@ func SendWebhookNotify(webhookURL string, secret string, data dto.Notify) error
 	} else {
 	} else {
 		// SSRF防护:验证Webhook URL(非Worker模式)
 		// SSRF防护:验证Webhook URL(非Worker模式)
 		fetchSetting := system_setting.GetFetchSetting()
 		fetchSetting := system_setting.GetFetchSetting()
-		if err := common.ValidateURLWithFetchSetting(webhookURL, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.WhitelistDomains, fetchSetting.WhitelistIps, fetchSetting.AllowedPorts); err != nil {
+		if err := common.ValidateURLWithFetchSetting(webhookURL, fetchSetting.EnableSSRFProtection, fetchSetting.AllowPrivateIp, fetchSetting.DomainList, fetchSetting.IpList, fetchSetting.AllowedPorts); err != nil {
 			return fmt.Errorf("request reject: %v", err)
 			return fmt.Errorf("request reject: %v", err)
 		}
 		}
 
 

+ 9 - 5
setting/system_setting/fetch_setting.go

@@ -5,16 +5,20 @@ import "one-api/setting/config"
 type FetchSetting struct {
 type FetchSetting struct {
 	EnableSSRFProtection bool     `json:"enable_ssrf_protection"` // 是否启用SSRF防护
 	EnableSSRFProtection bool     `json:"enable_ssrf_protection"` // 是否启用SSRF防护
 	AllowPrivateIp       bool     `json:"allow_private_ip"`
 	AllowPrivateIp       bool     `json:"allow_private_ip"`
-	WhitelistDomains     []string `json:"whitelist_domains"` // domain format, e.g. example.com, *.example.com
-	WhitelistIps         []string `json:"whitelist_ips"`     // CIDR format
-	AllowedPorts         []string `json:"allowed_ports"`     // port range format, e.g. 80, 443, 8000-9000
+	DomainFilterMode     bool     `json:"domain_filter_mode"` // 域名过滤模式,true: 白名单模式,false: 黑名单模式
+	IpFilterMode         bool     `json:"ip_filter_mode"`     // IP过滤模式,true: 白名单模式,false: 黑名单模式
+	DomainList           []string `json:"domain_list"`        // domain format, e.g. example.com, *.example.com
+	IpList               []string `json:"ip_list"`            // CIDR format
+	AllowedPorts         []string `json:"allowed_ports"`      // port range format, e.g. 80, 443, 8000-9000
 }
 }
 
 
 var defaultFetchSetting = FetchSetting{
 var defaultFetchSetting = FetchSetting{
 	EnableSSRFProtection: true, // 默认开启SSRF防护
 	EnableSSRFProtection: true, // 默认开启SSRF防护
 	AllowPrivateIp:       false,
 	AllowPrivateIp:       false,
-	WhitelistDomains:     []string{},
-	WhitelistIps:         []string{},
+	DomainFilterMode:     true,
+	IpFilterMode:         true,
+	DomainList:           []string{},
+	IpList:               []string{},
 	AllowedPorts:         []string{"80", "443", "8080", "8443"},
 	AllowedPorts:         []string{"80", "443", "8080", "8443"},
 }
 }
 
 

+ 86 - 28
web/src/components/settings/SystemSetting.jsx

@@ -29,6 +29,7 @@ import {
   TagInput,
   TagInput,
   Spin,
   Spin,
   Card,
   Card,
+  Radio,
 } from '@douyinfe/semi-ui';
 } from '@douyinfe/semi-ui';
 const { Text } = Typography;
 const { Text } = Typography;
 import {
 import {
@@ -91,8 +92,10 @@ const SystemSetting = () => {
     // SSRF防护配置
     // SSRF防护配置
     'fetch_setting.enable_ssrf_protection': true,
     'fetch_setting.enable_ssrf_protection': true,
     'fetch_setting.allow_private_ip': '',
     'fetch_setting.allow_private_ip': '',
-    'fetch_setting.whitelist_domains': [],
-    'fetch_setting.whitelist_ips': [],
+    'fetch_setting.domain_filter_mode': true, // true 白名单,false 黑名单
+    'fetch_setting.ip_filter_mode': true, // true 白名单,false 黑名单
+    'fetch_setting.domain_list': [],
+    'fetch_setting.ip_list': [],
     'fetch_setting.allowed_ports': [],
     'fetch_setting.allowed_ports': [],
   });
   });
 
 
@@ -105,8 +108,10 @@ const SystemSetting = () => {
     useState(false);
     useState(false);
   const [linuxDOOAuthEnabled, setLinuxDOOAuthEnabled] = useState(false);
   const [linuxDOOAuthEnabled, setLinuxDOOAuthEnabled] = useState(false);
   const [emailToAdd, setEmailToAdd] = useState('');
   const [emailToAdd, setEmailToAdd] = useState('');
-  const [whitelistDomains, setWhitelistDomains] = useState([]);
-  const [whitelistIps, setWhitelistIps] = useState([]);
+  const [domainFilterMode, setDomainFilterMode] = useState(true);
+  const [ipFilterMode, setIpFilterMode] = useState(true);
+  const [domainList, setDomainList] = useState([]);
+  const [ipList, setIpList] = useState([]);
   const [allowedPorts, setAllowedPorts] = useState([]);
   const [allowedPorts, setAllowedPorts] = useState([]);
 
 
   const getOptions = async () => {
   const getOptions = async () => {
@@ -125,22 +130,24 @@ const SystemSetting = () => {
             break;
             break;
           case 'fetch_setting.allow_private_ip':
           case 'fetch_setting.allow_private_ip':
           case 'fetch_setting.enable_ssrf_protection':
           case 'fetch_setting.enable_ssrf_protection':
+          case 'fetch_setting.domain_filter_mode':
+          case 'fetch_setting.ip_filter_mode':
             item.value = toBoolean(item.value);
             item.value = toBoolean(item.value);
             break;
             break;
-          case 'fetch_setting.whitelist_domains':
+          case 'fetch_setting.domain_list':
             try {
             try {
               const domains = item.value ? JSON.parse(item.value) : [];
               const domains = item.value ? JSON.parse(item.value) : [];
-              setWhitelistDomains(Array.isArray(domains) ? domains : []);
+              setDomainList(Array.isArray(domains) ? domains : []);
             } catch (e) {
             } catch (e) {
-              setWhitelistDomains([]);
+              setDomainList([]);
             }
             }
             break;
             break;
-          case 'fetch_setting.whitelist_ips':
+          case 'fetch_setting.ip_list':
             try {
             try {
               const ips = item.value ? JSON.parse(item.value) : [];
               const ips = item.value ? JSON.parse(item.value) : [];
-              setWhitelistIps(Array.isArray(ips) ? ips : []);
+              setIpList(Array.isArray(ips) ? ips : []);
             } catch (e) {
             } catch (e) {
-              setWhitelistIps([]);
+              setIpList([]);
             }
             }
             break;
             break;
           case 'fetch_setting.allowed_ports':
           case 'fetch_setting.allowed_ports':
@@ -178,6 +185,13 @@ const SystemSetting = () => {
       });
       });
       setInputs(newInputs);
       setInputs(newInputs);
       setOriginInputs(newInputs);
       setOriginInputs(newInputs);
+      // 同步模式布尔到本地状态
+      if (typeof newInputs['fetch_setting.domain_filter_mode'] !== 'undefined') {
+        setDomainFilterMode(!!newInputs['fetch_setting.domain_filter_mode']);
+      }
+      if (typeof newInputs['fetch_setting.ip_filter_mode'] !== 'undefined') {
+        setIpFilterMode(!!newInputs['fetch_setting.ip_filter_mode']);
+      }
       if (formApiRef.current) {
       if (formApiRef.current) {
         formApiRef.current.setValues(newInputs);
         formApiRef.current.setValues(newInputs);
       }
       }
@@ -317,19 +331,27 @@ const SystemSetting = () => {
   const submitSSRF = async () => {
   const submitSSRF = async () => {
     const options = [];
     const options = [];
 
 
-    // 处理域名白名单
-    if (Array.isArray(whitelistDomains)) {
+    // 处理域名过滤模式与列表
+    options.push({
+      key: 'fetch_setting.domain_filter_mode',
+      value: domainFilterMode,
+    });
+    if (Array.isArray(domainList)) {
       options.push({
       options.push({
-        key: 'fetch_setting.whitelist_domains',
-        value: JSON.stringify(whitelistDomains),
+        key: 'fetch_setting.domain_list',
+        value: JSON.stringify(domainList),
       });
       });
     }
     }
 
 
-    // 处理IP白名单
-    if (Array.isArray(whitelistIps)) {
+    // 处理IP过滤模式与列表
+    options.push({
+      key: 'fetch_setting.ip_filter_mode',
+      value: ipFilterMode,
+    });
+    if (Array.isArray(ipList)) {
       options.push({
       options.push({
-        key: 'fetch_setting.whitelist_ips',
-        value: JSON.stringify(whitelistIps),
+        key: 'fetch_setting.ip_list',
+        value: JSON.stringify(ipList),
       });
       });
     }
     }
 
 
@@ -702,25 +724,43 @@ const SystemSetting = () => {
                     style={{ marginTop: 16 }}
                     style={{ marginTop: 16 }}
                   >
                   >
                     <Col xs={24} sm={24} md={24} lg={24} xl={24}>
                     <Col xs={24} sm={24} md={24} lg={24} xl={24}>
-                      <Text strong>{t('域名白名单')}</Text>
+                      <Text strong>
+                        {t(domainFilterMode ? '域名白名单' : '域名黑名单')}
+                      </Text>
                       <Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
                       <Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
                         {t('支持通配符格式,如:example.com, *.api.example.com')}
                         {t('支持通配符格式,如:example.com, *.api.example.com')}
                       </Text>
                       </Text>
+                      <Radio.Group
+                        type='button'
+                        value={domainFilterMode ? 'whitelist' : 'blacklist'}
+                        onChange={(val) => {
+                          const isWhitelist = val === 'whitelist';
+                          setDomainFilterMode(isWhitelist);
+                          setInputs(prev => ({
+                            ...prev,
+                            'fetch_setting.domain_filter_mode': isWhitelist,
+                          }));
+                        }}
+                        style={{ marginBottom: 8 }}
+                      >
+                        <Radio value='whitelist'>{t('白名单')}</Radio>
+                        <Radio value='blacklist'>{t('黑名单')}</Radio>
+                      </Radio.Group>
                       <TagInput
                       <TagInput
-                        value={whitelistDomains}
+                        value={domainList}
                         onChange={(value) => {
                         onChange={(value) => {
-                          setWhitelistDomains(value);
+                          setDomainList(value);
                           // 触发Form的onChange事件
                           // 触发Form的onChange事件
                           setInputs(prev => ({
                           setInputs(prev => ({
                             ...prev,
                             ...prev,
-                            'fetch_setting.whitelist_domains': value
+                            'fetch_setting.domain_list': value
                           }));
                           }));
                         }}
                         }}
                         placeholder={t('输入域名后回车,如:example.com')}
                         placeholder={t('输入域名后回车,如:example.com')}
                         style={{ width: '100%' }}
                         style={{ width: '100%' }}
                       />
                       />
                       <Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
                       <Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
-                        {t('域名白名单详细说明')}
+                        {t('域名过滤详细说明')}
                       </Text>
                       </Text>
                     </Col>
                     </Col>
                   </Row>
                   </Row>
@@ -730,25 +770,43 @@ const SystemSetting = () => {
                     style={{ marginTop: 16 }}
                     style={{ marginTop: 16 }}
                   >
                   >
                     <Col xs={24} sm={24} md={24} lg={24} xl={24}>
                     <Col xs={24} sm={24} md={24} lg={24} xl={24}>
-                      <Text strong>{t('IP白名单')}</Text>
+                      <Text strong>
+                        {t(ipFilterMode ? 'IP白名单' : 'IP黑名单')}
+                      </Text>
                       <Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
                       <Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
                         {t('支持CIDR格式,如:8.8.8.8, 192.168.1.0/24')}
                         {t('支持CIDR格式,如:8.8.8.8, 192.168.1.0/24')}
                       </Text>
                       </Text>
+                      <Radio.Group
+                        type='button'
+                        value={ipFilterMode ? 'whitelist' : 'blacklist'}
+                        onChange={(val) => {
+                          const isWhitelist = val === 'whitelist';
+                          setIpFilterMode(isWhitelist);
+                          setInputs(prev => ({
+                            ...prev,
+                            'fetch_setting.ip_filter_mode': isWhitelist,
+                          }));
+                        }}
+                        style={{ marginBottom: 8 }}
+                      >
+                        <Radio value='whitelist'>{t('白名单')}</Radio>
+                        <Radio value='blacklist'>{t('黑名单')}</Radio>
+                      </Radio.Group>
                       <TagInput
                       <TagInput
-                        value={whitelistIps}
+                        value={ipList}
                         onChange={(value) => {
                         onChange={(value) => {
-                          setWhitelistIps(value);
+                          setIpList(value);
                           // 触发Form的onChange事件
                           // 触发Form的onChange事件
                           setInputs(prev => ({
                           setInputs(prev => ({
                             ...prev,
                             ...prev,
-                            'fetch_setting.whitelist_ips': value
+                            'fetch_setting.ip_list': value
                           }));
                           }));
                         }}
                         }}
                         placeholder={t('输入IP地址后回车,如:8.8.8.8')}
                         placeholder={t('输入IP地址后回车,如:8.8.8.8')}
                         style={{ width: '100%' }}
                         style={{ width: '100%' }}
                       />
                       />
                       <Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
                       <Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
-                        {t('IP白名单详细说明')}
+                        {t('IP过滤详细说明')}
                       </Text>
                       </Text>
                     </Col>
                     </Col>
                   </Row>
                   </Row>