Development
This commit is contained in:
parent
27eaea3d73
commit
c6d2ded525
8 changed files with 188 additions and 171 deletions
|
|
@ -30,8 +30,9 @@ def add_vlan():
|
||||||
is_vpn = 'is_vpn' in request.form
|
is_vpn = 'is_vpn' in request.form
|
||||||
subnet = sanitize.ip(request.form.get('subnet', ''))
|
subnet = sanitize.ip(request.form.get('subnet', ''))
|
||||||
subnet_mask = sanitize.subnet_mask(request.form.get('subnet_mask', ''))
|
subnet_mask = sanitize.subnet_mask(request.form.get('subnet_mask', ''))
|
||||||
radius_default = 'radius_default' in request.form
|
radius_default = 'radius_default' in request.form
|
||||||
mdns_reflection = 'mdns_reflection' in request.form
|
mdns_reflection = 'mdns_reflection' in request.form
|
||||||
|
dnsmasq_log_queries = 'dnsmasq_log_queries' in request.form
|
||||||
use_blocklists = sanitize.filterlist(request.form.getlist('use_blocklists'),
|
use_blocklists = sanitize.filterlist(request.form.getlist('use_blocklists'),
|
||||||
{b.get('name') for b in load_core().get('dns_blocking', {}).get('blocklists', [])})
|
{b.get('name') for b in load_core().get('dns_blocking', {}).get('blocklists', [])})
|
||||||
|
|
||||||
|
|
@ -67,13 +68,14 @@ def add_vlan():
|
||||||
return redirect(VIEW)
|
return redirect(VIEW)
|
||||||
|
|
||||||
entry = {
|
entry = {
|
||||||
'name': name,
|
'name': name,
|
||||||
'is_vpn': is_vpn,
|
'is_vpn': is_vpn,
|
||||||
'subnet': subnet,
|
'subnet': subnet,
|
||||||
'subnet_mask': subnet_mask,
|
'subnet_mask': subnet_mask,
|
||||||
'use_blocklists': use_blocklists,
|
'dnsmasq_log_queries': dnsmasq_log_queries,
|
||||||
'radius_default': radius_default,
|
'use_blocklists': use_blocklists,
|
||||||
'mdns_reflection': mdns_reflection,
|
'radius_default': radius_default,
|
||||||
|
'mdns_reflection': mdns_reflection,
|
||||||
}
|
}
|
||||||
if is_vpn:
|
if is_vpn:
|
||||||
entry['peers'] = []
|
entry['peers'] = []
|
||||||
|
|
@ -101,8 +103,9 @@ def edit_vlan():
|
||||||
|
|
||||||
name = sanitize.name(request.form.get('name', ''))
|
name = sanitize.name(request.form.get('name', ''))
|
||||||
subnet = sanitize.ip(request.form.get('subnet', ''))
|
subnet = sanitize.ip(request.form.get('subnet', ''))
|
||||||
radius_default = 'radius_default' in request.form
|
radius_default = 'radius_default' in request.form
|
||||||
mdns_reflection = 'mdns_reflection' in request.form
|
mdns_reflection = 'mdns_reflection' in request.form
|
||||||
|
dnsmasq_log_queries = 'dnsmasq_log_queries' in request.form
|
||||||
use_blocklists = sanitize.filterlist(request.form.getlist('use_blocklists'),
|
use_blocklists = sanitize.filterlist(request.form.getlist('use_blocklists'),
|
||||||
{b.get('name') for b in load_core().get('dns_blocking', {}).get('blocklists', [])})
|
{b.get('name') for b in load_core().get('dns_blocking', {}).get('blocklists', [])})
|
||||||
|
|
||||||
|
|
@ -162,13 +165,14 @@ def edit_vlan():
|
||||||
return redirect(VIEW)
|
return redirect(VIEW)
|
||||||
|
|
||||||
existing.update({
|
existing.update({
|
||||||
'name': name,
|
'name': name,
|
||||||
'is_vpn': is_vpn,
|
'is_vpn': is_vpn,
|
||||||
'subnet': subnet,
|
'subnet': subnet,
|
||||||
'subnet_mask': final_mask,
|
'subnet_mask': final_mask,
|
||||||
'radius_default': radius_default,
|
'dnsmasq_log_queries': dnsmasq_log_queries,
|
||||||
'mdns_reflection': mdns_reflection,
|
'radius_default': radius_default,
|
||||||
'use_blocklists': use_blocklists,
|
'mdns_reflection': mdns_reflection,
|
||||||
|
'use_blocklists': use_blocklists,
|
||||||
})
|
})
|
||||||
errors = validate.validate_config(core)
|
errors = validate.validate_config(core)
|
||||||
if errors:
|
if errors:
|
||||||
|
|
|
||||||
|
|
@ -184,9 +184,8 @@ def dnsblocking_cardblocklistrefresh_refreshnow():
|
||||||
@bp.route('/action/dnsblocking_cardlogging_save', methods=['POST'])
|
@bp.route('/action/dnsblocking_cardlogging_save', methods=['POST'])
|
||||||
@require_level('administrator')
|
@require_level('administrator')
|
||||||
def dnsblocking_cardlogging_save():
|
def dnsblocking_cardlogging_save():
|
||||||
log_max_kb_raw = request.form.get('log_max_kb', '').strip()
|
log_max_kb_raw = request.form.get('log_max_kb', '').strip()
|
||||||
log_errors_only = 'log_errors_only' in request.form
|
log_errors_only = 'log_errors_only' in request.form
|
||||||
dnsmasq_log_queries = 'dnsmasq_log_queries' in request.form
|
|
||||||
|
|
||||||
log_max_kb = validate.int_range(log_max_kb_raw, 64, None)
|
log_max_kb = validate.int_range(log_max_kb_raw, 64, None)
|
||||||
if log_max_kb is None:
|
if log_max_kb is None:
|
||||||
|
|
@ -202,7 +201,6 @@ def dnsblocking_cardlogging_save():
|
||||||
'log_max_kb': log_max_kb,
|
'log_max_kb': log_max_kb,
|
||||||
'log_errors_only': log_errors_only,
|
'log_errors_only': log_errors_only,
|
||||||
})
|
})
|
||||||
core.setdefault('network_interfaces', {})['dnsmasq_log_queries'] = dnsmasq_log_queries
|
|
||||||
errors = validate.validate_config(core)
|
errors = validate.validate_config(core)
|
||||||
if errors:
|
if errors:
|
||||||
for msg in errors:
|
for msg in errors:
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,8 @@ _VIEW = '/view/view_upstream_dns'
|
||||||
@bp.route('/action/upstreamdns_cardupstreamdns_save', methods=['POST'])
|
@bp.route('/action/upstreamdns_cardupstreamdns_save', methods=['POST'])
|
||||||
@require_level('administrator')
|
@require_level('administrator')
|
||||||
def upstreamdns_cardupstreamdns_save():
|
def upstreamdns_cardupstreamdns_save():
|
||||||
strict_order = 'strict_order' in request.form
|
strict_order = 'strict_order' in request.form
|
||||||
cache_size_raw = request.form.get('cache_size', '').strip()
|
submitted = request.form.getlist('upstream_servers')
|
||||||
submitted = request.form.getlist('upstream_servers')
|
|
||||||
|
|
||||||
for s in submitted:
|
for s in submitted:
|
||||||
if not s.strip():
|
if not s.strip():
|
||||||
|
|
@ -29,26 +28,19 @@ def upstreamdns_cardupstreamdns_save():
|
||||||
return redirect(_VIEW)
|
return redirect(_VIEW)
|
||||||
upstream_servers.append(clean)
|
upstream_servers.append(clean)
|
||||||
|
|
||||||
cache_size = validate.int_range(cache_size_raw, 0, None)
|
|
||||||
if cache_size is None:
|
|
||||||
flash('Cache Size must be a non-negative integer.', 'error')
|
|
||||||
return redirect(_VIEW)
|
|
||||||
|
|
||||||
if not verify_core_hash(request.form.get('config_hash', '')):
|
if not verify_core_hash(request.form.get('config_hash', '')):
|
||||||
flash('Configuration was modified by another session. Please refresh and try again.', 'error')
|
flash('Configuration was modified by another session. Please refresh and try again.', 'error')
|
||||||
return redirect(_VIEW)
|
return redirect(_VIEW)
|
||||||
|
|
||||||
core = load_core()
|
core = load_core()
|
||||||
current = core.get('upstream_dns', {})
|
current = core.get('upstream_dns', {})
|
||||||
if (strict_order == bool(current.get('strict_order', False)) and
|
if (strict_order == bool(current.get('strict_order', False)) and
|
||||||
cache_size == int(current.get('cache_size', 0)) and
|
|
||||||
upstream_servers == current.get('upstream_servers', [])):
|
upstream_servers == current.get('upstream_servers', [])):
|
||||||
flash('No changes detected.', 'info')
|
flash('No changes detected.', 'info')
|
||||||
return redirect(_VIEW)
|
return redirect(_VIEW)
|
||||||
|
|
||||||
core.setdefault('upstream_dns', {}).update({
|
core.setdefault('upstream_dns', {}).update({
|
||||||
'strict_order': strict_order,
|
'strict_order': strict_order,
|
||||||
'cache_size': cache_size,
|
|
||||||
'upstream_servers': upstream_servers,
|
'upstream_servers': upstream_servers,
|
||||||
})
|
})
|
||||||
errors = validate.validate_config(core)
|
errors = validate.validate_config(core)
|
||||||
|
|
@ -59,3 +51,32 @@ def upstreamdns_cardupstreamdns_save():
|
||||||
save_core(core)
|
save_core(core)
|
||||||
flash(queued_msg('core apply'), 'success')
|
flash(queued_msg('core apply'), 'success')
|
||||||
return redirect(_VIEW)
|
return redirect(_VIEW)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/action/upstreamdns_cardforwardingdnsservice_save', methods=['POST'])
|
||||||
|
@require_level('administrator')
|
||||||
|
def upstreamdns_cardforwardingdnsservice_save():
|
||||||
|
cache_size = validate.int_range(request.form.get('cache_size', '').strip(), 0, None)
|
||||||
|
if cache_size is None:
|
||||||
|
flash('Cache Size must be a non-negative integer.', 'error')
|
||||||
|
return redirect(_VIEW)
|
||||||
|
|
||||||
|
if not verify_core_hash(request.form.get('config_hash', '')):
|
||||||
|
flash('Configuration was modified by another session. Please refresh and try again.', 'error')
|
||||||
|
return redirect(_VIEW)
|
||||||
|
|
||||||
|
core = load_core()
|
||||||
|
current = core.get('upstream_dns', {})
|
||||||
|
if cache_size == int(current.get('cache_size', 0)):
|
||||||
|
flash('No changes detected.', 'info')
|
||||||
|
return redirect(_VIEW)
|
||||||
|
|
||||||
|
core.setdefault('upstream_dns', {})['cache_size'] = cache_size
|
||||||
|
errors = validate.validate_config(core)
|
||||||
|
if errors:
|
||||||
|
for msg in errors:
|
||||||
|
flash(msg, 'error')
|
||||||
|
return redirect(_VIEW)
|
||||||
|
save_core(core)
|
||||||
|
flash(queued_msg('core apply'), 'success')
|
||||||
|
return redirect(_VIEW)
|
||||||
|
|
|
||||||
|
|
@ -291,7 +291,7 @@ def _config_datasource(name):
|
||||||
bl_desc = {b['name']: b.get('description', b['name']) for b in core.get('dns_blocking', {}).get('blocklists', []) if 'name' in b}
|
bl_desc = {b['name']: b.get('description', b['name']) for b in core.get('dns_blocking', {}).get('blocklists', []) if 'name' in b}
|
||||||
rows = []
|
rows = []
|
||||||
for v in sorted(vlans, key=lambda x: validate.derive_vlan_id(x.get('subnet', ''), x.get('subnet_mask', 24)) or 0):
|
for v in sorted(vlans, key=lambda x: validate.derive_vlan_id(x.get('subnet', ''), x.get('subnet_mask', 24)) or 0):
|
||||||
row = {k: v.get(k) for k in ('name', 'subnet', 'subnet_mask', 'radius_default', 'mdns_reflection', 'is_vpn')}
|
row = {k: v.get(k) for k in ('name', 'subnet', 'subnet_mask', 'radius_default', 'mdns_reflection', 'is_vpn', 'dnsmasq_log_queries')}
|
||||||
row['vlan_id'] = validate.derive_vlan_id(v.get('subnet', ''), v.get('subnet_mask', 24))
|
row['vlan_id'] = validate.derive_vlan_id(v.get('subnet', ''), v.get('subnet_mask', 24))
|
||||||
row['interface'] = _resolve_iface(v, core)
|
row['interface'] = _resolve_iface(v, core)
|
||||||
row['use_blocklists'] = json.dumps([
|
row['use_blocklists'] = json.dumps([
|
||||||
|
|
@ -587,9 +587,8 @@ def collect_tokens():
|
||||||
)
|
)
|
||||||
tokens['NETWORK_INTERFACE_STATS_SPEED_PAD'] = str(max(max_speed_len, len('Speed')))
|
tokens['NETWORK_INTERFACE_STATS_SPEED_PAD'] = str(max(max_speed_len, len('Speed')))
|
||||||
|
|
||||||
tokens['GENERAL_LOG_ERRORS_ONLY'] = 'true' if dns_blk_gen.get('log_errors_only') else 'false'
|
tokens['GENERAL_LOG_ERRORS_ONLY'] = 'true' if dns_blk_gen.get('log_errors_only') else 'false'
|
||||||
tokens['GENERAL_DNSMASQ_LOG_QUERIES'] = 'true' if net.get('dnsmasq_log_queries') else 'false'
|
tokens['GENERAL_DAILY_EXECUTE_TIME'] = str(dns_blk_gen.get('daily_execute_time_24hr_local', '-'))
|
||||||
tokens['GENERAL_DAILY_EXECUTE_TIME'] = str(dns_blk_gen.get('daily_execute_time_24hr_local', '-'))
|
|
||||||
tokens['GENERAL_APPLY_ON_SAVE'] = 'true' if net.get('apply_on_save', True) else 'false'
|
tokens['GENERAL_APPLY_ON_SAVE'] = 'true' if net.get('apply_on_save', True) else 'false'
|
||||||
|
|
||||||
pending_items = get_dashboard_pending()
|
pending_items = get_dashboard_pending()
|
||||||
|
|
@ -1431,6 +1430,13 @@ def _render_table_cell(value, render_fn, col_class='', field='', row_idx=None,
|
||||||
inner = '<span class="badge badge-disabled">Disabled</span>'
|
inner = '<span class="badge badge-disabled">Disabled</span>'
|
||||||
return f'{td_open}{inner}</td>'
|
return f'{td_open}{inner}</td>'
|
||||||
|
|
||||||
|
if render_fn == 'badge_recording_on_off':
|
||||||
|
if str(value).lower() in ('true', '1', 'yes'):
|
||||||
|
inner = '<span class="badge badge-enabled">Recording On</span>'
|
||||||
|
else:
|
||||||
|
inner = '<span class="badge badge-disabled">Recording Off</span>'
|
||||||
|
return f'{td_open}{inner}</td>'
|
||||||
|
|
||||||
if render_fn == 'badge_toggle':
|
if render_fn == 'badge_toggle':
|
||||||
if str(value).lower() in ('true', '1', 'yes', 'enabled'):
|
if str(value).lower() in ('true', '1', 'yes', 'enabled'):
|
||||||
label = 'Enabled'; badge_cls = 'badge-enabled'
|
label = 'Enabled'; badge_cls = 'badge-enabled'
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@
|
||||||
"items": [
|
"items": [
|
||||||
{ "type": "nav_item", "label": "General", "map_to": "view_general", "client_requirement": "client_is_administrator+" },
|
{ "type": "nav_item", "label": "General", "map_to": "view_general", "client_requirement": "client_is_administrator+" },
|
||||||
{ "type": "nav_item", "label": "Network Interfaces", "map_to": "view_network_interfaces", "client_requirement": "client_is_administrator+" },
|
{ "type": "nav_item", "label": "Network Interfaces", "map_to": "view_network_interfaces", "client_requirement": "client_is_administrator+" },
|
||||||
{ "type": "nav_item", "label": "Upstream DNS", "map_to": "view_upstream_dns", "client_requirement": "client_is_administrator+" },
|
{ "type": "nav_item", "label": "DNS", "map_to": "view_upstream_dns", "client_requirement": "client_is_administrator+" },
|
||||||
{ "type": "nav_item", "label": "DNS Blocking", "map_to": "view_dns_blocking", "client_requirement": "client_is_administrator+" },
|
{ "type": "nav_item", "label": "DNS Blocking", "map_to": "view_dns_blocking", "client_requirement": "client_is_administrator+" },
|
||||||
|
{ "type": "nav_item", "label": "DDNS", "map_to": "view_ddns" },
|
||||||
{ "type": "nav_item", "label": "VLANs", "map_to": "view_vlans", "client_requirement": "client_is_administrator+" },
|
{ "type": "nav_item", "label": "VLANs", "map_to": "view_vlans", "client_requirement": "client_is_administrator+" },
|
||||||
{ "type": "nav_item", "label": "Inter-VLAN Exceptions", "map_to": "view_inter_vlan", "client_requirement": "client_is_administrator+" },
|
{ "type": "nav_item", "label": "Inter-VLAN Exceptions", "map_to": "view_inter_vlan", "client_requirement": "client_is_administrator+" },
|
||||||
{ "type": "nav_item", "label": "Port Forwarding", "map_to": "view_port_forwarding", "client_requirement": "client_is_administrator+" },
|
{ "type": "nav_item", "label": "Port Forwarding", "map_to": "view_port_forwarding", "client_requirement": "client_is_administrator+" },
|
||||||
{ "type": "nav_item", "label": "DHCP", "map_to": "view_dhcp" },
|
{ "type": "nav_item", "label": "DHCP", "map_to": "view_dhcp" },
|
||||||
{ "type": "nav_item", "label": "Host Overrides", "map_to": "view_host_overrides", "client_requirement": "client_is_administrator+" },
|
{ "type": "nav_item", "label": "Host Overrides", "map_to": "view_host_overrides", "client_requirement": "client_is_administrator+" },
|
||||||
{ "type": "nav_item", "label": "DDNS", "map_to": "view_ddns" },
|
|
||||||
{ "type": "nav_item", "label": "VPN", "map_to": "view_vpn" },
|
{ "type": "nav_item", "label": "VPN", "map_to": "view_vpn" },
|
||||||
{ "type": "nav_item", "label": "Banned IPs", "map_to": "view_banned_ips", "client_requirement": "client_is_administrator+" }
|
{ "type": "nav_item", "label": "Banned IPs", "map_to": "view_banned_ips", "client_requirement": "client_is_administrator+" }
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -819,11 +819,11 @@
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"type": "h1",
|
"type": "h1",
|
||||||
"text": "Upstream DNS"
|
"text": "DNS"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "p",
|
"type": "p",
|
||||||
"text": "Upstream resolvers and caching behaviour for dnsmasq."
|
"text": "Upstream resolvers and forwarding DNS service settings."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -845,15 +845,6 @@
|
||||||
"value": "%DNS_STRICT_ORDER%",
|
"value": "%DNS_STRICT_ORDER%",
|
||||||
"hint": "Query DNS providers in list order rather than in parallel."
|
"hint": "Query DNS providers in list order rather than in parallel."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "field",
|
|
||||||
"label": "Cache Size",
|
|
||||||
"name": "cache_size",
|
|
||||||
"input_type": "number",
|
|
||||||
"value": "%DNS_CACHE_SIZE%",
|
|
||||||
"min": 0,
|
|
||||||
"hint": "Max DNS responses to cache per instance. Set to 0 to disable caching."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "editable_list",
|
"type": "editable_list",
|
||||||
"label": "DNS Providers",
|
"label": "DNS Providers",
|
||||||
|
|
@ -882,6 +873,44 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "card",
|
||||||
|
"label": "Forwarding DNS Service",
|
||||||
|
"client_requirement": "client_is_administrator+",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"action": "/action/upstreamdns_cardforwardingdnsservice_save",
|
||||||
|
"method": "post",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"label": "Cache Size",
|
||||||
|
"name": "cache_size",
|
||||||
|
"input_type": "number",
|
||||||
|
"value": "%DNS_CACHE_SIZE%",
|
||||||
|
"min": 0,
|
||||||
|
"hint": "Max DNS responses to cache per instance. Set to 0 to disable caching."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "button_row",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "button_primary",
|
||||||
|
"action": "/action/upstreamdns_cardforwardingdnsservice_save",
|
||||||
|
"method": "post",
|
||||||
|
"text": "Save"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "button_cancel",
|
||||||
|
"text": "Cancel"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -1156,60 +1185,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "card",
|
|
||||||
"label": "Logging",
|
|
||||||
"client_requirement": "client_is_administrator+",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"type": "form",
|
|
||||||
"action": "/action/dnsblocking_cardlogging_save",
|
|
||||||
"method": "post",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"type": "field",
|
|
||||||
"label": "Max Log Size (KB)",
|
|
||||||
"name": "log_max_kb",
|
|
||||||
"input_type": "number",
|
|
||||||
"value": "%GENERAL_LOG_MAX_KB%",
|
|
||||||
"min": 64,
|
|
||||||
"hint": "Log is cleared and restarted when it exceeds this size."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "field",
|
|
||||||
"label": "Errors Only",
|
|
||||||
"name": "log_errors_only",
|
|
||||||
"input_type": "checkbox",
|
|
||||||
"value": "%GENERAL_LOG_ERRORS_ONLY%",
|
|
||||||
"hint": "Only write error-level messages to the log."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "field",
|
|
||||||
"label": "Log DNS Queries",
|
|
||||||
"name": "dnsmasq_log_queries",
|
|
||||||
"input_type": "checkbox",
|
|
||||||
"value": "%GENERAL_DNSMASQ_LOG_QUERIES%",
|
|
||||||
"hint": "Log every DNS query. High volume \u2014 enable for debugging only."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "button_row",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"type": "button_primary",
|
|
||||||
"action": "/action/dnsblocking_cardlogging_save",
|
|
||||||
"method": "post",
|
|
||||||
"text": "Save"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "button_cancel",
|
|
||||||
"text": "Cancel"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "table",
|
"type": "table",
|
||||||
"datasource": "config:blocklists",
|
"datasource": "config:blocklists",
|
||||||
|
|
@ -1393,6 +1368,52 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "card",
|
||||||
|
"label": "Logging",
|
||||||
|
"client_requirement": "client_is_administrator+",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "form",
|
||||||
|
"action": "/action/dnsblocking_cardlogging_save",
|
||||||
|
"method": "post",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"label": "Max Log Size (KB)",
|
||||||
|
"name": "log_max_kb",
|
||||||
|
"input_type": "number",
|
||||||
|
"value": "%GENERAL_LOG_MAX_KB%",
|
||||||
|
"min": 64,
|
||||||
|
"hint": "Log is cleared and restarted when it exceeds this size."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"label": "Errors Only",
|
||||||
|
"name": "log_errors_only",
|
||||||
|
"input_type": "checkbox",
|
||||||
|
"value": "%GENERAL_LOG_ERRORS_ONLY%",
|
||||||
|
"hint": "Only write error-level messages to the log."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "button_row",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "button_primary",
|
||||||
|
"action": "/action/dnsblocking_cardlogging_save",
|
||||||
|
"method": "post",
|
||||||
|
"text": "Save"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "button_cancel",
|
||||||
|
"text": "Cancel"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -1465,6 +1486,12 @@
|
||||||
"field": "mdns_reflection",
|
"field": "mdns_reflection",
|
||||||
"class": "col-narrow",
|
"class": "col-narrow",
|
||||||
"render": "badge_enabled_disabled"
|
"render": "badge_enabled_disabled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "DNS Queries",
|
||||||
|
"field": "dnsmasq_log_queries",
|
||||||
|
"class": "col-narrow",
|
||||||
|
"render": "badge_recording_on_off"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"row_actions": [
|
"row_actions": [
|
||||||
|
|
@ -1498,6 +1525,10 @@
|
||||||
"col": "mdns_reflection",
|
"col": "mdns_reflection",
|
||||||
"input_type": "checkbox"
|
"input_type": "checkbox"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"col": "dnsmasq_log_queries",
|
||||||
|
"input_type": "checkbox"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"col": "use_blocklists",
|
"col": "use_blocklists",
|
||||||
"input_type": "checkbox_multi",
|
"input_type": "checkbox_multi",
|
||||||
|
|
@ -1610,6 +1641,13 @@
|
||||||
"input_type": "checkbox",
|
"input_type": "checkbox",
|
||||||
"hint": "Reflect mDNS traffic to/from this VLAN via avahi-daemon. Not supported on WireGuard interfaces."
|
"hint": "Reflect mDNS traffic to/from this VLAN via avahi-daemon. Not supported on WireGuard interfaces."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"label": "Record DNS Queries",
|
||||||
|
"name": "dnsmasq_log_queries",
|
||||||
|
"input_type": "checkbox",
|
||||||
|
"hint": "Log every DNS query. High volume — enable for debugging only."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "button_row",
|
"type": "button_row",
|
||||||
"items": [
|
"items": [
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
{
|
{
|
||||||
"network_interfaces": {
|
"network_interfaces": {
|
||||||
"wan_interface": "eno2",
|
"wan_interface": "eno2",
|
||||||
"lan_interface": "enp6s0",
|
"lan_interface": "enp6s0"
|
||||||
"dnsmasq_log_queries": false
|
|
||||||
},
|
},
|
||||||
"upstream_dns": {
|
"upstream_dns": {
|
||||||
"strict_order": false,
|
"strict_order": false,
|
||||||
|
|
@ -264,10 +263,11 @@
|
||||||
],
|
],
|
||||||
"vlans": [
|
"vlans": [
|
||||||
{
|
{
|
||||||
"vlan_id": 1,
|
|
||||||
"name": "trusted",
|
"name": "trusted",
|
||||||
"subnet": "192.168.1.0",
|
"subnet": "192.168.1.0",
|
||||||
"subnet_mask": 24,
|
"subnet_mask": 24,
|
||||||
|
"is_vpn": false,
|
||||||
|
"dnsmasq_log_queries": false,
|
||||||
"radius_default": false,
|
"radius_default": false,
|
||||||
"mdns_reflection": false,
|
"mdns_reflection": false,
|
||||||
"use_blocklists": [
|
"use_blocklists": [
|
||||||
|
|
@ -364,14 +364,14 @@
|
||||||
"dest_port": 123,
|
"dest_port": 123,
|
||||||
"redirect_to": "192.168.1.1"
|
"redirect_to": "192.168.1.1"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"is_vpn": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"vlan_id": 10,
|
|
||||||
"name": "iot",
|
"name": "iot",
|
||||||
"subnet": "192.168.10.0",
|
"subnet": "192.168.10.0",
|
||||||
"subnet_mask": 24,
|
"subnet_mask": 24,
|
||||||
|
"is_vpn": false,
|
||||||
|
"dnsmasq_log_queries": false,
|
||||||
"radius_default": false,
|
"radius_default": false,
|
||||||
"mdns_reflection": true,
|
"mdns_reflection": true,
|
||||||
"use_blocklists": [
|
"use_blocklists": [
|
||||||
|
|
@ -468,14 +468,14 @@
|
||||||
"dest_port": 123,
|
"dest_port": 123,
|
||||||
"redirect_to": "192.168.10.1"
|
"redirect_to": "192.168.10.1"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"is_vpn": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"vlan_id": 20,
|
|
||||||
"name": "guest",
|
"name": "guest",
|
||||||
"subnet": "192.168.20.0",
|
"subnet": "192.168.20.0",
|
||||||
"subnet_mask": 24,
|
"subnet_mask": 24,
|
||||||
|
"is_vpn": false,
|
||||||
|
"dnsmasq_log_queries": false,
|
||||||
"radius_default": true,
|
"radius_default": true,
|
||||||
"mdns_reflection": true,
|
"mdns_reflection": true,
|
||||||
"use_blocklists": [
|
"use_blocklists": [
|
||||||
|
|
@ -530,14 +530,14 @@
|
||||||
"dest_port": 123,
|
"dest_port": 123,
|
||||||
"redirect_to": "192.168.20.1"
|
"redirect_to": "192.168.20.1"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"is_vpn": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"vlan_id": 30,
|
|
||||||
"name": "kids",
|
"name": "kids",
|
||||||
"subnet": "192.168.30.0",
|
"subnet": "192.168.30.0",
|
||||||
"subnet_mask": 24,
|
"subnet_mask": 24,
|
||||||
|
"is_vpn": false,
|
||||||
|
"dnsmasq_log_queries": false,
|
||||||
"radius_default": false,
|
"radius_default": false,
|
||||||
"mdns_reflection": true,
|
"mdns_reflection": true,
|
||||||
"use_blocklists": [
|
"use_blocklists": [
|
||||||
|
|
@ -607,14 +607,14 @@
|
||||||
"dest_port": 123,
|
"dest_port": 123,
|
||||||
"redirect_to": "192.168.30.1"
|
"redirect_to": "192.168.30.1"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"is_vpn": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"vlan_id": 40,
|
|
||||||
"name": "vpn",
|
"name": "vpn",
|
||||||
"subnet": "192.168.40.0",
|
"subnet": "192.168.40.0",
|
||||||
"subnet_mask": 24,
|
"subnet_mask": 24,
|
||||||
|
"is_vpn": true,
|
||||||
|
"dnsmasq_log_queries": false,
|
||||||
"radius_default": false,
|
"radius_default": false,
|
||||||
"mdns_reflection": false,
|
"mdns_reflection": false,
|
||||||
"use_blocklists": [
|
"use_blocklists": [
|
||||||
|
|
@ -653,8 +653,7 @@
|
||||||
"dest_port": 123,
|
"dest_port": 123,
|
||||||
"redirect_to": "192.168.40.1"
|
"redirect_to": "192.168.40.1"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"is_vpn": true
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ddns": {
|
"ddns": {
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,6 @@ Usage:
|
||||||
import hashlib
|
import hashlib
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import json
|
import json
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
@ -108,7 +107,6 @@ PRODUCT_NAME = "routlin"
|
||||||
SCRIPT_DIR = Path(__file__).parent
|
SCRIPT_DIR = Path(__file__).parent
|
||||||
CONFIG_FILE = SCRIPT_DIR / "core.json"
|
CONFIG_FILE = SCRIPT_DIR / "core.json"
|
||||||
BLOCKLIST_DIR = SCRIPT_DIR / "blocklists"
|
BLOCKLIST_DIR = SCRIPT_DIR / "blocklists"
|
||||||
LOG_FILE = SCRIPT_DIR / "core.log"
|
|
||||||
METRICS_FILE = SCRIPT_DIR / ".dns-metrics"
|
METRICS_FILE = SCRIPT_DIR / ".dns-metrics"
|
||||||
DNSMASQ_CONF_DIR = Path(f"/etc/dnsmasq-{PRODUCT_NAME}")
|
DNSMASQ_CONF_DIR = Path(f"/etc/dnsmasq-{PRODUCT_NAME}")
|
||||||
LEASES_DIR = Path("/var/lib/misc")
|
LEASES_DIR = Path("/var/lib/misc")
|
||||||
|
|
@ -140,48 +138,6 @@ NAT_SERVICE_FILE = SYSTEMD_DIR / f"{NAT_SERVICE_NAME}.service"
|
||||||
WG_DIR = Path("/etc/wireguard")
|
WG_DIR = Path("/etc/wireguard")
|
||||||
WG_KEEPALIVE = 25
|
WG_KEEPALIVE = 25
|
||||||
|
|
||||||
log = None
|
|
||||||
|
|
||||||
# ===================================================================
|
|
||||||
# Logging
|
|
||||||
# ===================================================================
|
|
||||||
|
|
||||||
def chown_to_script_dir_owner(path):
|
|
||||||
"""Chown a file to the owner of the script directory.
|
|
||||||
This works correctly whether invoked via sudo, directly as root (e.g. systemd timer),
|
|
||||||
or as a normal user - the script directory owner is always the right target.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
stat = SCRIPT_DIR.stat()
|
|
||||||
os.chown(path, stat.st_uid, stat.st_gid)
|
|
||||||
except OSError:
|
|
||||||
pass # non-fatal
|
|
||||||
|
|
||||||
def setup_logging(max_kb, errors_only):
|
|
||||||
global log
|
|
||||||
try:
|
|
||||||
if LOG_FILE.exists() and LOG_FILE.stat().st_size > max_kb * 1024:
|
|
||||||
LOG_FILE.write_text("")
|
|
||||||
if not LOG_FILE.exists():
|
|
||||||
LOG_FILE.touch()
|
|
||||||
chown_to_script_dir_owner(LOG_FILE)
|
|
||||||
file_handler = logging.FileHandler(LOG_FILE)
|
|
||||||
except PermissionError:
|
|
||||||
print(f"WARNING: Cannot write to {LOG_FILE} (permission denied). "
|
|
||||||
f"Run with sudo or fix ownership: sudo chown $USER {LOG_FILE}")
|
|
||||||
file_handler = None
|
|
||||||
level = logging.ERROR if errors_only else logging.INFO
|
|
||||||
handlers = [logging.StreamHandler(sys.stdout)]
|
|
||||||
if file_handler:
|
|
||||||
handlers.insert(0, file_handler)
|
|
||||||
logging.basicConfig(
|
|
||||||
level=level,
|
|
||||||
format="%(asctime)s %(levelname)-8s %(message)s",
|
|
||||||
datefmt="%Y-%m-%d %H:%M:%S",
|
|
||||||
handlers=handlers,
|
|
||||||
)
|
|
||||||
log = logging.getLogger("dns-dhcp")
|
|
||||||
|
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
# Helpers
|
# Helpers
|
||||||
# ===================================================================
|
# ===================================================================
|
||||||
|
|
@ -592,7 +548,7 @@ def build_vlan_dnsmasq_conf(vlan, data, iface):
|
||||||
continue # skip IPv6 upstream -- WAN has no IPv6 address
|
continue # skip IPv6 upstream -- WAN has no IPv6 address
|
||||||
line(f"server={srv}")
|
line(f"server={srv}")
|
||||||
line(f"cache-size={dns_cfg.get('cache_size', 1000)}")
|
line(f"cache-size={dns_cfg.get('cache_size', 1000)}")
|
||||||
if general.get("dnsmasq_log_queries", False):
|
if vlan.get("dnsmasq_log_queries", False):
|
||||||
line("log-queries")
|
line("log-queries")
|
||||||
line()
|
line()
|
||||||
|
|
||||||
|
|
@ -3132,11 +3088,6 @@ def main():
|
||||||
print(f" - {e}", file=sys.stderr)
|
print(f" - {e}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
general = data.get("dns_blocking", {}).get("general", {})
|
|
||||||
setup_logging(
|
|
||||||
general.get("log_max_kb", 1024),
|
|
||||||
general.get("log_errors_only", False)
|
|
||||||
)
|
|
||||||
|
|
||||||
if args.status:
|
if args.status:
|
||||||
show_status(data)
|
show_status(data)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue