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

This commit is contained in:
creamlike1024
2025-09-16 22:40:40 +08:00
parent 72d5b35d3f
commit b7bc609a7a
5 changed files with 99 additions and 37 deletions

View File

@@ -30,7 +30,7 @@ func DoWorkerRequest(req *WorkerRequest) (*http.Response, error) {
// SSRF防护验证请求URL
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)
}
@@ -59,7 +59,7 @@ func DoDownloadRequest(originUrl string, reason ...string) (resp *http.Response,
} else {
// SSRF防护验证请求URL非Worker模式
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)
}

View File

@@ -115,7 +115,7 @@ func sendBarkNotify(barkURL string, data dto.Notify) error {
} else {
// SSRF防护验证Bark URL非Worker模式
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)
}

View File

@@ -89,7 +89,7 @@ func SendWebhookNotify(webhookURL string, secret string, data dto.Notify) error
} else {
// SSRF防护验证Webhook URL非Worker模式
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)
}

View File

@@ -5,16 +5,20 @@ import "one-api/setting/config"
type FetchSetting struct {
EnableSSRFProtection bool `json:"enable_ssrf_protection"` // 是否启用SSRF防护
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{
EnableSSRFProtection: true, // 默认开启SSRF防护
AllowPrivateIp: false,
WhitelistDomains: []string{},
WhitelistIps: []string{},
DomainFilterMode: true,
IpFilterMode: true,
DomainList: []string{},
IpList: []string{},
AllowedPorts: []string{"80", "443", "8080", "8443"},
}

View File

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