mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-04-28 05:08:38 +00:00
feat: 添加域名和ip过滤模式设置
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
DomainFilterMode bool `json:"domain_filter_mode"` // 域名过滤模式,true: 白名单模式,false: 黑名单模式
|
||||||
WhitelistIps []string `json:"whitelist_ips"` // CIDR format
|
IpFilterMode bool `json:"ip_filter_mode"` // IP过滤模式,true: 白名单模式,false: 黑名单模式
|
||||||
AllowedPorts []string `json:"allowed_ports"` // port range format, e.g. 80, 443, 8000-9000
|
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{},
|
DomainFilterMode: true,
|
||||||
WhitelistIps: []string{},
|
IpFilterMode: true,
|
||||||
|
DomainList: []string{},
|
||||||
|
IpList: []string{},
|
||||||
AllowedPorts: []string{"80", "443", "8080", "8443"},
|
AllowedPorts: []string{"80", "443", "8080", "8443"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.domain_filter_mode': true, // true 白名单,false 黑名单
|
||||||
'fetch_setting.whitelist_ips': [],
|
'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 [domainFilterMode, setDomainFilterMode] = useState(true);
|
||||||
const [whitelistIps, setWhitelistIps] = useState([]);
|
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',
|
key: 'fetch_setting.domain_list',
|
||||||
value: JSON.stringify(whitelistDomains),
|
value: JSON.stringify(domainList),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理IP白名单
|
// 处理IP过滤模式与列表
|
||||||
if (Array.isArray(whitelistIps)) {
|
options.push({
|
||||||
|
key: 'fetch_setting.ip_filter_mode',
|
||||||
|
value: ipFilterMode,
|
||||||
|
});
|
||||||
|
if (Array.isArray(ipList)) {
|
||||||
options.push({
|
options.push({
|
||||||
key: 'fetch_setting.whitelist_ips',
|
key: 'fetch_setting.ip_list',
|
||||||
value: JSON.stringify(whitelistIps),
|
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>
|
||||||
|
|||||||
Reference in New Issue
Block a user