修复3个问题: 1) setup-bind.sh确保服务启动 2) DNSSEC配置验证options块包裹 3) 添加IPv6开关功能
This commit is contained in:
@@ -1083,6 +1083,24 @@ def _parse_listen_on_v4(content):
|
||||
return items
|
||||
|
||||
|
||||
def _parse_listen_on_v6(content):
|
||||
"""Return 'any'/'none'/'localhost' — the IPv6 listen mode.
|
||||
Returns 'none' if directive is absent (disabled by default).
|
||||
"""
|
||||
m = re.search(r'listen-on-v6(?:\s+port\s+\d+)?\s*\{([^}]*)\}', content, re.DOTALL)
|
||||
if not m:
|
||||
return "none"
|
||||
body = m.group(1)
|
||||
# Look for the main token: { any; } or { none; } or { localhost; }
|
||||
for tm in re.finditer(r'"([^"]+)"|([\w./:-]+)', body):
|
||||
token = tm.group(1) or tm.group(2)
|
||||
if token in ("any", "none", "localhost"):
|
||||
return token
|
||||
# Fallback: if the block is not empty, assume "custom" mode
|
||||
body_stripped = body.strip().strip(';').strip()
|
||||
return "any" if body_stripped else "none"
|
||||
|
||||
|
||||
def _parse_recursion(content):
|
||||
"""Return 'yes' (default), 'no', or specific value from 'recursion <v>;'."""
|
||||
m = re.search(r'recursion\s+(\w+)\s*;', content)
|
||||
@@ -1296,6 +1314,7 @@ def config_view():
|
||||
dnssec_value = _parse_options_dnssec(options_content)
|
||||
listen_on_v4 = _parse_listen_on_v4(options_content)
|
||||
recursion_value = _parse_recursion(options_content)
|
||||
listen_on_v6 = _parse_listen_on_v6(options_content)
|
||||
|
||||
return render_template("config.html",
|
||||
options_content=options_content,
|
||||
@@ -1308,6 +1327,7 @@ def config_view():
|
||||
dnssec_value=dnssec_value,
|
||||
dnssec_values=_VALIDATION_VALUES,
|
||||
listen_on_v4=listen_on_v4,
|
||||
listen_on_v6=listen_on_v6,
|
||||
recursion_value=recursion_value)
|
||||
|
||||
|
||||
@@ -1319,6 +1339,11 @@ def config_options_save():
|
||||
run_cmd(f"cp {BIND_CONF_OPTIONS} {backup}")
|
||||
|
||||
# Write to temp and validate
|
||||
# NOTE: BIND_CONF_OPTIONS content must have 'options { ... }' wrapper (it's a complete file)
|
||||
# that gets included from named.conf, so named-checkconf can validate it directly
|
||||
if not re.search(r'^\s*options\s*\{', content, re.MULTILINE):
|
||||
# User may have accidentally removed the options wrapper in text editor
|
||||
content = f"options {{\n{content}\n}};\n"
|
||||
with open("/tmp/named_check.tmp", 'w') as f:
|
||||
f.write(content)
|
||||
rc, out, err = run_cmd("named-checkconf /tmp/named_check.tmp")
|
||||
@@ -1330,7 +1355,7 @@ def config_options_save():
|
||||
|
||||
with open(BIND_CONF_OPTIONS, 'w') as f:
|
||||
f.write(content)
|
||||
run_cmd(f"chown root:bind {BIND_CONF_OPTIONS}")
|
||||
run_cmd(f"chown root:bind {BIND_CONF_OPTIONS} 2>/dev/null || chown root:named {BIND_CONF_OPTIONS} 2>/dev/null")
|
||||
|
||||
bind_reload()
|
||||
|
||||
@@ -1392,6 +1417,9 @@ def config_upstream_save():
|
||||
raw_fwd = request.form.get("forwarders", "")
|
||||
raw_rc = request.form.get("recursion", "")
|
||||
raw_listen = request.form.get("listen_on_v4", "")
|
||||
listen_on_v6_in = (request.form.get("listen_on_v6") or "none").strip().lower()
|
||||
if listen_on_v6_in not in ("any", "none", "localhost"):
|
||||
listen_on_v6_in = "none"
|
||||
dnssec_in = (request.form.get("dnssec") or "auto").strip().lower()
|
||||
if dnssec_in not in _VALIDATION_VALUES:
|
||||
dnssec_in = "auto"
|
||||
@@ -1430,12 +1458,20 @@ def config_upstream_save():
|
||||
new_content, "listen-on",
|
||||
_format_list_block("listen-on port 53", new_listen),
|
||||
)
|
||||
new_content = _replace_or_append_option(
|
||||
new_content, "listen-on-v6",
|
||||
_format_list_block("listen-on-v6 port 53", [listen_on_v6_in]),
|
||||
)
|
||||
new_content = _replace_or_append_option(
|
||||
new_content, "recursion",
|
||||
f"recursion {recursion_in};",
|
||||
f"recursion {recursion_in};\n",
|
||||
)
|
||||
|
||||
# Validate BEFORE writing to the live file
|
||||
# Ensure content has options block wrapper (required for named-checkconf)
|
||||
if not re.search(r'^\s*options\s*\{', new_content, re.MULTILINE):
|
||||
# No options block found — wrap the entire content
|
||||
new_content = f"options {{\n{new_content}\n}};\n"
|
||||
tmp = "/tmp/named_check.tmp"
|
||||
with open(tmp, 'w') as f:
|
||||
f.write(new_content)
|
||||
@@ -1457,11 +1493,11 @@ def config_upstream_save():
|
||||
u = current_user()
|
||||
AuditLog.log(u.username, "修改上游DNS转发配置",
|
||||
f"forwarders={new_fwd}; allow-recursion={new_rc}; "
|
||||
f"dnssec={dnssec_in}; listen-on={new_listen}; recursion={recursion_in}")
|
||||
f"dnssec={dnssec_in}; listen-on={new_listen}; listen-on-v6={listen_on_v6_in}; recursion={recursion_in}")
|
||||
flash(
|
||||
f"已保存:forwarders {len(new_fwd)} 条,allow-recursion {len(new_rc)} 条,"
|
||||
f"dnssec-validation {dnssec_in},listen-on {len(new_listen)} 项,"
|
||||
f"recursion {recursion_in}",
|
||||
f"listen-on-v6 {listen_on_v6_in},recursion {recursion_in}",
|
||||
"success",
|
||||
)
|
||||
return redirect(url_for("config_view"))
|
||||
|
||||
Reference in New Issue
Block a user