feat(dns): Validierung für DNS-Settings — upstream IPs, ACL-CIDRs, TTL-Kreuzcheck
Backend: validateSettings() prüft vor dem Reload-Trigger ob upstream_forwards gültige IPs (inkl. @port), access_acl gültige IPs/CIDRs und listen_addresses gültige IPs sind; cache_max_ttl ≥ cache_min_ttl. Frontend: Pattern-Validatoren auf upstream_forwards + access_acl; TTL- Kreuzvalidierung mit dependencies. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -990,7 +990,10 @@
|
||||
"flushCacheBtn": "DNS-Cache leeren",
|
||||
"flushCacheTooltip": "Alle gecachten Einträge verwerfen (unbound-control flush_zone .). Verwenden wenn DNS-Änderungen sofort greifen sollen.",
|
||||
"flushCacheOk": "DNS-Cache geleert",
|
||||
"flushCacheFailed": "Cache-Flush fehlgeschlagen"
|
||||
"flushCacheFailed": "Cache-Flush fehlgeschlagen",
|
||||
"upstreamForwardsInvalid": "Jeder Forwarder muss eine gültige IP sein (z.B. 1.1.1.1 oder 9.9.9.9)",
|
||||
"accessACLInvalid": "Jeder Eintrag muss eine gültige IP oder CIDR sein (z.B. 10.0.0.0/8)",
|
||||
"cacheTTLError": "Cache-Max-TTL muss ≥ Cache-Min-TTL sein"
|
||||
}
|
||||
},
|
||||
"fwd": {
|
||||
|
||||
@@ -990,7 +990,10 @@
|
||||
"flushCacheBtn": "Flush DNS cache",
|
||||
"flushCacheTooltip": "Discard all cached records (unbound-control flush_zone .). Use after DNS changes have propagated.",
|
||||
"flushCacheOk": "DNS cache flushed",
|
||||
"flushCacheFailed": "Flush failed"
|
||||
"flushCacheFailed": "Flush failed",
|
||||
"upstreamForwardsInvalid": "Each forwarder must be a valid IP (e.g. 1.1.1.1 or 9.9.9.9)",
|
||||
"accessACLInvalid": "Each entry must be a valid IP or CIDR (e.g. 10.0.0.0/8 or 192.168.1.0/24)",
|
||||
"cacheTTLError": "Cache max-TTL must be ≥ cache min-TTL"
|
||||
}
|
||||
},
|
||||
"fwd": {
|
||||
|
||||
@@ -524,12 +524,40 @@ function SettingsTab() {
|
||||
<Form.Item label={t('dns.settings.listenPort')} name="listen_port" rules={[{ required: true }]}>
|
||||
<InputNumber min={1} max={65535} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('dns.settings.upstreamForwards')} name="upstream_forwards" rules={[{ required: true }]}
|
||||
extra={t('dns.settings.upstreamForwardsExtra')}>
|
||||
<Form.Item
|
||||
label={t('dns.settings.upstreamForwards')}
|
||||
name="upstream_forwards"
|
||||
extra={t('dns.settings.upstreamForwardsExtra')}
|
||||
rules={[
|
||||
{ required: true },
|
||||
{
|
||||
validator(_, val: string) {
|
||||
if (!val) return Promise.resolve()
|
||||
const IP = /^(\d{1,3}\.){3}\d{1,3}(@\d{1,5})?$|^[0-9a-fA-F:]+(@\d{1,5})?$/
|
||||
const bad = val.split(',').map(s => s.trim()).filter(Boolean).find(s => !IP.test(s))
|
||||
return bad ? Promise.reject(new Error(t('dns.settings.upstreamForwardsInvalid'))) : Promise.resolve()
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="1.1.1.1, 9.9.9.9" />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('dns.settings.accessACL')} name="access_acl" rules={[{ required: true }]}
|
||||
extra={t('dns.settings.accessACLExtra')}>
|
||||
<Form.Item
|
||||
label={t('dns.settings.accessACL')}
|
||||
name="access_acl"
|
||||
extra={t('dns.settings.accessACLExtra')}
|
||||
rules={[
|
||||
{ required: true },
|
||||
{
|
||||
validator(_, val: string) {
|
||||
if (!val) return Promise.resolve()
|
||||
const IPCIDR = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$|^[0-9a-fA-F:]+(\/\d{1,3})?$/
|
||||
const bad = val.split(',').map(s => s.trim()).filter(Boolean).find(s => !IPCIDR.test(s))
|
||||
return bad ? Promise.reject(new Error(t('dns.settings.accessACLInvalid'))) : Promise.resolve()
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="127.0.0.0/8, 10.0.0.0/8" />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('dns.settings.dnssec')} name="dnssec" valuePropName="checked">
|
||||
@@ -539,10 +567,25 @@ function SettingsTab() {
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
<Space>
|
||||
<Form.Item label={t('dns.settings.cacheMin')} name="cache_min_ttl">
|
||||
<Form.Item label={t('dns.settings.cacheMin')} name="cache_min_ttl" dependencies={['cache_max_ttl']}>
|
||||
<InputNumber min={0} style={{ width: 120 }} />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('dns.settings.cacheMax')} name="cache_max_ttl">
|
||||
<Form.Item
|
||||
label={t('dns.settings.cacheMax')}
|
||||
name="cache_max_ttl"
|
||||
dependencies={['cache_min_ttl']}
|
||||
rules={[
|
||||
({ getFieldValue }) => ({
|
||||
validator(_, val) {
|
||||
const minTTL = getFieldValue('cache_min_ttl') as number | undefined
|
||||
if (val != null && minTTL != null && val < minTTL) {
|
||||
return Promise.reject(new Error(t('dns.settings.cacheTTLError')))
|
||||
}
|
||||
return Promise.resolve()
|
||||
},
|
||||
}),
|
||||
]}
|
||||
>
|
||||
<InputNumber min={60} style={{ width: 120 }} />
|
||||
</Form.Item>
|
||||
</Space>
|
||||
|
||||
Reference in New Issue
Block a user