mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-09 12:40:20 +08:00
fix(security): configure trusted Fake-IP DNS hosts
This commit is contained in:
@@ -50,12 +50,14 @@
|
||||
<ProxyConfigSection
|
||||
id="section-proxy"
|
||||
:proxy-node-id="systemConfig.system_proxy_node_id"
|
||||
:extra-trusted-dns-hosts-str="extraTrustedDnsHostsStr"
|
||||
:online-nodes="proxyNodesStore.onlineNodes"
|
||||
:all-nodes="proxyNodesStore.nodes"
|
||||
:loading="systemConfigLoading || proxyConfigLoading"
|
||||
:has-changes="hasProxyConfigChanges"
|
||||
@save="saveProxyConfig"
|
||||
@update:proxy-node-id="systemConfig.system_proxy_node_id = $event"
|
||||
@update:extra-trusted-dns-hosts-str="extraTrustedDnsHostsStr = $event"
|
||||
/>
|
||||
|
||||
<!-- 基础配置 -->
|
||||
@@ -351,6 +353,7 @@ const {
|
||||
hasCleanupConfigChanges,
|
||||
sensitiveHeadersStr,
|
||||
turnstileAllowedHostnamesStr,
|
||||
extraTrustedDnsHostsStr,
|
||||
loadSystemConfig,
|
||||
loadSystemVersion,
|
||||
saveSiteInfo,
|
||||
|
||||
@@ -40,6 +40,25 @@
|
||||
对未单独配置代理的提供商生效,覆盖大模型 API 请求、余额查询、OAuth 刷新等。不影响系统内部接口。
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-6 max-w-2xl border-t pt-5">
|
||||
<Label
|
||||
for="extra-trusted-dns-hosts"
|
||||
class="block text-sm font-medium"
|
||||
>
|
||||
额外可信 Fake-IP 域名
|
||||
</Label>
|
||||
<Textarea
|
||||
id="extra-trusted-dns-hosts"
|
||||
:model-value="extraTrustedDnsHostsStr"
|
||||
rows="4"
|
||||
class="mt-1"
|
||||
placeholder="api.example.com\nmodels.example.com"
|
||||
@update:model-value="$emit('update:extraTrustedDnsHostsStr', String($event || ''))"
|
||||
/>
|
||||
<p class="mt-1 text-xs text-muted-foreground">
|
||||
每行填写一个精确 hostname;不支持通配符、后缀、端口、路径或 IP。仅允许这些域名返回 198.18.0.0/15 Fake-IP,内置提供商域名不受影响。
|
||||
</p>
|
||||
</div>
|
||||
</CardSection>
|
||||
</template>
|
||||
|
||||
@@ -47,6 +66,7 @@
|
||||
import { computed } from 'vue'
|
||||
import Button from '@/components/ui/button.vue'
|
||||
import Label from '@/components/ui/label.vue'
|
||||
import Textarea from '@/components/ui/textarea.vue'
|
||||
import Select from '@/components/ui/select.vue'
|
||||
import SelectTrigger from '@/components/ui/select-trigger.vue'
|
||||
import SelectValue from '@/components/ui/select-value.vue'
|
||||
@@ -64,6 +84,7 @@ interface ProxyNode {
|
||||
|
||||
const props = defineProps<{
|
||||
proxyNodeId: string | null
|
||||
extraTrustedDnsHostsStr: string
|
||||
onlineNodes: ProxyNode[]
|
||||
allNodes: ProxyNode[]
|
||||
loading: boolean
|
||||
@@ -73,6 +94,7 @@ const props = defineProps<{
|
||||
defineEmits<{
|
||||
save: []
|
||||
'update:proxyNodeId': [value: string | null]
|
||||
'update:extraTrustedDnsHostsStr': [value: string]
|
||||
}>()
|
||||
|
||||
const selectableNodes = computed(() => {
|
||||
|
||||
@@ -80,4 +80,31 @@ describe('useSystemConfig', () => {
|
||||
expect(state.systemConfig.value).not.toHaveProperty('max_request_body_size')
|
||||
expect(state.systemConfig.value).not.toHaveProperty('max_response_body_size')
|
||||
})
|
||||
|
||||
it('loads and saves the extra trusted Fake-IP DNS hosts with proxy settings', async () => {
|
||||
getAllSystemConfigsMock.mockResolvedValue([
|
||||
{ key: 'system_proxy_node_id', value: 'node-1' },
|
||||
{ key: 'execution_extra_trusted_dns_hosts', value: ['custom.example.com'] },
|
||||
])
|
||||
updateSystemConfigMock.mockResolvedValue(undefined)
|
||||
|
||||
const state = useSystemConfig()
|
||||
await state.loadSystemConfig()
|
||||
|
||||
expect(state.extraTrustedDnsHostsStr.value).toBe('custom.example.com')
|
||||
state.extraTrustedDnsHostsStr.value = 'api.example.com\nmodels.example.com'
|
||||
expect(state.systemConfig.value.execution_extra_trusted_dns_hosts).toEqual([
|
||||
'api.example.com',
|
||||
'models.example.com',
|
||||
])
|
||||
|
||||
await state.saveProxyConfig()
|
||||
|
||||
expect(updateSystemConfigMock).toHaveBeenCalledWith(
|
||||
'execution_extra_trusted_dns_hosts',
|
||||
['api.example.com', 'models.example.com'],
|
||||
'执行运行时额外可信 Fake-IP 域名'
|
||||
)
|
||||
expect(state.hasProxyConfigChanges.value).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,6 +10,7 @@ export interface SystemConfig {
|
||||
site_subtitle: string
|
||||
// 网络代理
|
||||
system_proxy_node_id: string | null
|
||||
execution_extra_trusted_dns_hosts: string[]
|
||||
// 基础配置
|
||||
default_user_initial_gift_usd: number
|
||||
rate_limit_per_minute: number
|
||||
@@ -61,6 +62,7 @@ const CONFIG_KEYS = [
|
||||
'site_subtitle',
|
||||
// 网络代理
|
||||
'system_proxy_node_id',
|
||||
'execution_extra_trusted_dns_hosts',
|
||||
// 基础配置
|
||||
'default_user_initial_gift_usd',
|
||||
'rate_limit_per_minute',
|
||||
@@ -112,6 +114,7 @@ function createDefaultConfig(): SystemConfig {
|
||||
site_subtitle: 'AI Gateway',
|
||||
// 网络代理
|
||||
system_proxy_node_id: null,
|
||||
execution_extra_trusted_dns_hosts: [],
|
||||
// 基础配置
|
||||
default_user_initial_gift_usd: 10.0,
|
||||
rate_limit_per_minute: 0,
|
||||
@@ -188,6 +191,8 @@ export function useSystemConfig() {
|
||||
if (systemConfigLoading.value) return false
|
||||
if (!originalConfig.value) return false
|
||||
return systemConfig.value.system_proxy_node_id !== originalConfig.value.system_proxy_node_id
|
||||
|| JSON.stringify(systemConfig.value.execution_extra_trusted_dns_hosts) !==
|
||||
JSON.stringify(originalConfig.value.execution_extra_trusted_dns_hosts)
|
||||
})
|
||||
|
||||
const hasBasicConfigChanges = computed(() => {
|
||||
@@ -278,6 +283,16 @@ export function useSystemConfig() {
|
||||
},
|
||||
})
|
||||
|
||||
const extraTrustedDnsHostsStr = computed({
|
||||
get: () => systemConfig.value.execution_extra_trusted_dns_hosts.join('\n'),
|
||||
set: (val: string) => {
|
||||
systemConfig.value.execution_extra_trusted_dns_hosts = val
|
||||
.split(/[\n,]/)
|
||||
.map((s) => s.trim().toLowerCase().replace(/\.$/, ''))
|
||||
.filter((s) => s.length > 0)
|
||||
},
|
||||
})
|
||||
|
||||
// 加载配置
|
||||
async function loadSystemConfig() {
|
||||
systemConfigLoading.value = true
|
||||
@@ -355,13 +370,23 @@ export function useSystemConfig() {
|
||||
async function saveProxyConfig() {
|
||||
proxyConfigLoading.value = true
|
||||
try {
|
||||
await adminApi.updateSystemConfig(
|
||||
'system_proxy_node_id',
|
||||
systemConfig.value.system_proxy_node_id || null,
|
||||
'系统默认代理节点 ID'
|
||||
)
|
||||
await Promise.all([
|
||||
adminApi.updateSystemConfig(
|
||||
'system_proxy_node_id',
|
||||
systemConfig.value.system_proxy_node_id || null,
|
||||
'系统默认代理节点 ID'
|
||||
),
|
||||
adminApi.updateSystemConfig(
|
||||
'execution_extra_trusted_dns_hosts',
|
||||
systemConfig.value.execution_extra_trusted_dns_hosts,
|
||||
'执行运行时额外可信 Fake-IP 域名'
|
||||
),
|
||||
])
|
||||
if (originalConfig.value) {
|
||||
originalConfig.value.system_proxy_node_id = systemConfig.value.system_proxy_node_id
|
||||
originalConfig.value.execution_extra_trusted_dns_hosts = [
|
||||
...systemConfig.value.execution_extra_trusted_dns_hosts,
|
||||
]
|
||||
}
|
||||
success('网络代理配置已保存')
|
||||
} catch (err) {
|
||||
@@ -716,6 +741,7 @@ export function useSystemConfig() {
|
||||
// 计算属性
|
||||
sensitiveHeadersStr,
|
||||
turnstileAllowedHostnamesStr,
|
||||
extraTrustedDnsHostsStr,
|
||||
// 加载函数
|
||||
loadSystemConfig,
|
||||
loadSystemVersion,
|
||||
|
||||
Reference in New Issue
Block a user