'use strict';
'require dom';
'require view';
'require poll';
'require fs';
'require ui';
'require uci';
'require form';
'require tools.widgets as widgets';
/*
button handling
*/
function handleAction(ev) {
if (ev === 'restart' || ev === 'reload') {
let map = document.querySelector('.cbi-map');
return dom.callClassMethod(map, 'save')
.then(L.bind(ui.changes.apply, ui.changes))
.then(function () {
return fs.exec_direct('/etc/init.d/banip', [ev]);
})
} else {
return fs.exec_direct('/etc/init.d/banip', [ev]);
}
}
return view.extend({
load: function () {
return Promise.all([
L.resolveDefault(fs.read_direct('/etc/banip/banip.custom.feeds'), ''),
L.resolveDefault(fs.read_direct('/etc/banip/banip.feeds'), ''),
L.resolveDefault(fs.read_direct('/etc/banip/banip.countries'), ''),
uci.load('banip').catch(() => 0)
]);
},
render: function (result) {
/*
config check
*/
if (!result[3] || result[3].length === 0) {
ui.addNotification(null, E('p', _('No banIP config found!')), 'error');
return;
}
let m, s, o;
m = new form.Map('banip', 'banIP', _('Configuration of the banIP package to ban incoming and outgoing IPs via named nftables Sets. \
For further information please check the %s.'.format(`${_('online documentation')}`)));
/*
set text content helper function
*/
const setText = (id, value) => {
const el = document.getElementById(id);
if (el) {
el.textContent = value || '-';
}
};
/*
poll runtime information
*/
let parseErrCount = 0;
poll.add(function () {
return L.resolveDefault(fs.stat('/var/run/banIP/banIP.runtime.json'), null).then(function (stat) {
if (!stat) {
return;
}
return Promise.all([
L.resolveDefault(fs.read_direct('/var/run/banIP/banIP.runtime.json'), 'null'),
L.resolveDefault(fs.exec_direct('/etc/init.d/banip', ['actual']), '')
]).then(function (results) {
const res = results[0];
const actual = results[1]?.trim() || '';
const status = document.getElementById('status');
const buttons = document.querySelectorAll('.cbi-page-actions button');
let info = null;
try {
info = JSON.parse(res);
parseErrCount = 0;
} catch (e) {
info = null;
parseErrCount++;
if (status) {
status.textContent = '-';
buttons.forEach(function (btn) {
btn.disabled = false;
});
status.classList.remove('spinning');
if (parseErrCount >= 3) {
ui.addNotification(null, E('p', _('Unable to parse the banIP runtime information!')), 'error');
poll.stop();
}
}
return;
}
if (status && info) {
let statusText = info.status || '-';
if (actual) {
statusText += `: ${actual}`;
}
statusText += ` (frontend: ${info.frontend_ver || '-'} / backend: ${info.backend_ver || '-'})`;
status.textContent = statusText;
if (info.status === "processing") {
buttons.forEach(function (btn) {
btn.disabled = true;
btn.blur();
});
if (!status.classList.contains("spinning")) {
status.classList.add("spinning");
}
} else {
if (status.classList.contains("spinning")) {
status.classList.remove("spinning");
buttons.forEach(function (btn) {
btn.disabled = false;
});
}
}
}
if (info) {
setText('elements', info.element_count);
setText('feeds', info.active_feeds?.join(', ') || '-');
setText('devices', `wan-dev: ${info.wan_devices?.join(', ') || '-'} /
wan-if: ${info.wan_interfaces?.join(', ') || '-'} /
vlan-allow: ${info.vlan_allow?.join(', ') || '-'} /
vlan-block: ${info.vlan_block?.join(', ') || '-'}`);
setText('uplink', info.active_uplink?.join(', ') || '-');
setText('nft', info.nft_info);
setText('run', info.run_info);
setText('flags', info.run_flags);
setText('last', info.last_run);
setText('sys', info.system_info);
}
});
});
}, 2);
/*
runtime information and buttons
*/
s = m.section(form.NamedSection, 'global');
s.render = function (view, section_id) {
return E('div', { 'class': 'cbi-section' }, [
E('h3', _('Information')),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Status')),
E('div', { 'class': 'cbi-value-field spinning', 'id': 'status', 'style': 'margin-bottom:-5px;color:#37c;' }, '\xa0')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Element Count')),
E('div', { 'class': 'cbi-value-field', 'id': 'elements', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Active Feeds')),
E('div', { 'class': 'cbi-value-field', 'id': 'feeds', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Active Devices')),
E('div', { 'class': 'cbi-value-field', 'id': 'devices', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Active Uplink')),
E('div', { 'class': 'cbi-value-field', 'id': 'uplink', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('NFT Information')),
E('div', { 'class': 'cbi-value-field', 'id': 'nft', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Run Information')),
E('div', { 'class': 'cbi-value-field', 'id': 'run', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Run Flags')),
E('div', { 'class': 'cbi-value-field', 'id': 'flags', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('Last Run')),
E('div', { 'class': 'cbi-value-field', 'id': 'last', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
]),
E('div', { 'class': 'cbi-value' }, [
E('label', { 'class': 'cbi-value-title', 'style': 'margin-bottom:-5px;padding-top:0rem;' }, _('System Info')),
E('div', { 'class': 'cbi-value-field', 'id': 'sys', 'style': 'margin-bottom:-5px;color:#37c;' }, '-')
])
]);
};
/*
tabbed config section
*/
s = m.section(form.NamedSection, 'global', 'banip', _('Settings'));
s.addremove = false;
s.tab('general', _('General Settings'));
s.tab('advanced', _('Advanced Settings'));
s.tab('adv_chain', _('Table/Chain Settings'));
s.tab('adv_set', _('Feed/Set Settings'));
s.tab('adv_log', _('Log Settings'));
s.tab('adv_email', _('E-Mail Settings'));
s.tab('feeds', _('Feed Selection'));
/*
general settings tab
*/
o = s.taboption('general', form.DummyValue, '_sub');
o.rawhtml = true;
o.default = '' + _('Changes on this tab needs a banIP service restart to take effect.') + ''
+ '
';
o = s.taboption('general', form.Flag, 'ban_enabled', _('Enabled'), _('Enable the banIP service.'));
o.rmempty = false;
o = s.taboption('general', form.Flag, 'ban_debug', _('Verbose Debug Logging'), _('Enable verbose debug logging in case of processing errors.'));
o.rmempty = false;
o = s.taboption('general', form.Flag, 'ban_autodetect', _('Auto Detection'), _('Detect relevant network devices, interfaces, subnets, protocols and utilities automatically.'));
o.rmempty = false;
o = s.taboption('general', form.Flag, 'ban_protov4', _('IPv4 Support'), _('Enables IPv4 support.'));
o.depends('ban_autodetect', '0');
o.optional = true;
o.retain = true;
o = s.taboption('general', form.Flag, 'ban_protov6', _('IPv6 Support'), _('Enables IPv6 support.'));
o.depends('ban_autodetect', '0');
o.optional = true;
o.retain = true;
o = s.taboption('general', widgets.DeviceSelect, 'ban_dev', _('Network Devices'), _('Select the WAN network device(s).'));
o.depends('ban_autodetect', '0');
o.multiple = true;
o.nocreate = true;
o.optional = true;
o.retain = true;
o = s.taboption('general', widgets.NetworkSelect, 'ban_ifv4', _('IPv4 Network Interfaces'), _('Select the logical WAN IPv4 network interface(s).'));
o.depends('ban_autodetect', '0');
o.multiple = true;
o.nocreate = true;
o.optional = true;
o.retain = true;
o = s.taboption('general', widgets.NetworkSelect, 'ban_ifv6', _('IPv6 Network Interfaces'), _('Select the logical WAN IPv6 network interface(s).'));
o.depends('ban_autodetect', '0');
o.multiple = true;
o.nocreate = true;
o.optional = true;
o.retain = true;
o = s.taboption('general', form.ListValue, 'ban_fetchcmd', _('Download Utility'), _('Select one of the pre-configured download utilities.'));
o.depends('ban_autodetect', '0');
o.value('uclient-fetch');
o.value('wget');
o.value('curl');
o.optional = true;
o.retain = true;
o = s.taboption('general', form.Value, 'ban_fetchparm', _('Download Parameters'), _('Override the pre-configured download options for the selected download utility. The output flag, e.g. \'-o\' for curl or \'-O\' for wget, must be the last parameter.'));
o.depends('ban_autodetect', '0');
o.optional = true;
o.retain = true;
o = s.taboption('general', widgets.NetworkSelect, 'ban_trigger', _('Startup Trigger Interface'), _('List of available network interfaces to trigger the banIP start.'));
o.multiple = true;
o.nocreate = true;
o.rmempty = true;
o = s.taboption('general', form.Value, 'ban_triggerdelay', _('Trigger Delay'), _('Additional trigger delay in seconds before banIP processing begins.'));
o.placeholder = '10';
o.datatype = 'range(1,300)';
o.rmempty = true;
o = s.taboption('general', form.ListValue, 'ban_fetchretry', _('Download Retries'), _('Number of download attempts in case of an error (not supported by uclient-fetch).'));
o.value('1');
o.value('3');
o.value('5');
o.value('10');
o.value('20');
o.default = '5';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('general', form.Flag, 'ban_fetchinsecure', _('Download Insecure'), _('Don\'t check SSL server certificates during download.'));
o.rmempty = true;
o = s.taboption('general', form.Flag, 'ban_nftcount', _('Reporting Counters'), _('Enable NFT counters for Set elements and chain rules. Required for the GeoIP Map and packet statistics in the Set Reporting.'));
o.rmempty = true;
o = s.taboption('general', form.Flag, 'ban_map', _('Enable GeoIP Map'), _('Enable a GeoIP Map with suspicious Set elements. This requires external requests to get the map tiles and geolocation data.'));
o.depends('ban_nftcount', '1');
o.optional = true;
o.rmempty = true;
/*
additional settings tab
*/
o = s.taboption('advanced', form.DummyValue, '_sub');
o.rawhtml = true;
o.default = '' + _('Changes on this tab needs a banIP service restart to take effect.') + ''
+ '
';
o = s.taboption('advanced', form.ListValue, 'ban_nicelimit', _('Nice Level'), _('The selected priority will be used for banIP background processing.'));
o.value('-20', _('Highest Priority'));
o.value('-10', _('High Priority'));
o.value('0', _('Normal Priority'));
o.value('10', _('Less Priority'));
o.value('19', _('Least Priority'));
o.default = '0';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('advanced', form.ListValue, 'ban_filelimit', _('Max Open Files'), _('Increase the maximal number of open files, e.g. to handle the amount of temporary split files while loading the Sets.'));
o.value('512');
o.value('1024');
o.value('2048');
o.value('4096');
o.default = '1024';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('advanced', form.ListValue, 'ban_cores', _('CPU Cores'), _('Limit the cpu cores used by banIP to save RAM, autodetected by default.'));
o.value('1');
o.value('2');
o.value('4');
o.value('8');
o.value('16');
o.placeholder = _('-- default --');
o.optional = true;
o.rmempty = true;
o = s.taboption('advanced', form.ListValue, 'ban_splitsize', _('Set Split Size'), _('Split external Set loading after every n members to save RAM, disabled by default.'));
o.value('512');
o.value('1024');
o.value('2048');
o.value('4096');
o.value('8192');
o.value('16384');
o.placeholder = _('-- default --');
o.optional = true;
o.rmempty = true;
o = s.taboption('advanced', form.Value, 'ban_basedir', _('Base Directory'), _('Base working directory while banIP processing.'));
o.placeholder = '/tmp';
o.rmempty = true;
o = s.taboption('advanced', form.Value, 'ban_backupdir', _('Backup Directory'), _('Target directory for compressed feed backups.'));
o.placeholder = '/tmp/banIP-backup';
o.rmempty = true;
o = s.taboption('advanced', form.Value, 'ban_reportdir', _('Report Directory'), _('Target directory for banIP-related report files.'));
o.placeholder = '/tmp/banIP-report';
o.rmempty = true;
o = s.taboption('advanced', form.Value, 'ban_errordir', _('Error Directory'), _('Target directory for banIP-related error files.'));
o.placeholder = '/tmp/banIP-error';
o.rmempty = true;
o = s.taboption('advanced', form.Flag, 'ban_deduplicate', _('Deduplicate IPs'), _('Deduplicate IP addresses across all active Sets and tidy up the local blocklist.'));
o.default = 1;
o.rmempty = false;
/*
advanced chain settings tab
*/
o = s.taboption('adv_chain', form.DummyValue, '_sub');
o.rawhtml = true;
o.default = '' + _('Changes on this tab needs a banIP service restart to take effect.') + ''
+ '
';
o = s.taboption('adv_chain', form.ListValue, 'ban_nftpriority', _('Chain Priority'), _('Set the NFT chain priority within the banIP table, lower values means higher priority.'));
o.value('10');
o.value('0');
o.value('-100');
o.value('-150');
o.default = '-100';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_chain', form.Value, 'ban_allowflag', _('Allow Protocol/Ports'), _('Always allow a protocol (tcp/udp) with certain ports or port ranges in WAN-Input and WAN-Forward chain.'));
o.placeholder = 'tcp 80 443-445';
o.rmempty = true;
o = s.taboption('adv_chain', widgets.DeviceSelect, 'ban_vlanallow', _('Allow VLAN Forwards'), _('Always allow certain VLAN forwards.'));
o.multiple = true;
o.nocreate = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_chain', widgets.DeviceSelect, 'ban_vlanblock', _('Block VLAN Forwards'), _('Always block certain VLAN forwards.'));
o.multiple = true;
o.nocreate = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_chain', form.Flag, 'ban_bcp38', _('Enable BCP38'), _('Block packets with spoofed source IP addresses in all supported chains.'));
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_chain', form.ListValue, 'ban_icmplimit', _('ICMP-Threshold'), _('ICMP-Threshold in packets per second to prevent WAN-DoS attacks. To disable this safeguard set it to \'0\'.'));
o.value('0');
o.value('25');
o.value('50');
o.value('100');
o.value('250');
o.value('500');
o.value('1000');
o.default = '25';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_chain', form.ListValue, 'ban_synlimit', _('SYN-Threshold'), _('SYN-Threshold in packets per second to prevent WAN-DoS attacks. To disable this safeguard set it to \'0\'.'));
o.value('0');
o.value('10');
o.value('50');
o.value('100');
o.value('250');
o.value('500');
o.value('1000');
o.default = '10';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_chain', form.ListValue, 'ban_udplimit', _('UDP-Threshold'), _('UDP-Threshold in packets per second to prevent WAN-DoS attacks. To disable this safeguard set it to \'0\'.'));
o.value('0');
o.value('100');
o.value('250');
o.value('500');
o.value('1000');
o.value('2500');
o.value('5000');
o.default = '100';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
/*
advanced Set settings tab
*/
o = s.taboption('adv_set', form.DummyValue, '_sub');
o.rawhtml = true;
o.default = '' + _('Changes on this tab needs a banIP service restart to take effect.') + ''
+ '
';
o = s.taboption('adv_set', form.ListValue, 'ban_nftpolicy', _('Set Policy'), _('Set the NFT policy for banIP-related Sets.'));
o.value('memory', _('memory'));
o.value('performance', _('performance'));
o.default = 'memory';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_set', form.ListValue, 'ban_nftretry', _('Set Load Retries'), _('Number of Set load attempts in case of an error.'));
o.value('1');
o.value('3');
o.value('5');
o.default = '3';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_set', form.ListValue, 'ban_blockpolicy', _('Inbound Block Policy'), _('Drop packets silently or actively reject Inbound traffic.'));
o.value('drop', _('drop'));
o.value('reject', _('reject'));
o.default = 'drop';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
/*
feed parsing
*/
let feed, chain, descr;
let feeds = null;
if (result[0] && result[0].trim() !== "") {
try {
feeds = JSON.parse(result[0]);
} catch (e) {
ui.addNotification(null, E('p', _('Unable to parse the custom feed file!')), 'error');
}
}
if (!feeds && result[1] && result[1].trim() !== "") {
try {
feeds = JSON.parse(result[1]);
} catch (e) {
ui.addNotification(null, E('p', _('Unable to parse the default feed file!')), 'error');
}
}
if (feeds && Object.keys(feeds).length) {
const feedKeys = Object.keys(feeds);
o = s.taboption('adv_set', form.MultiValue, 'ban_feedin', _('Inbound Feed'), _('Override the default feed configuration and apply the feed to the inbound chain only.'));
o.value('allowlist', _('local allowlist'));
o.value('blocklist', _('local blocklist'));
feedKeys.forEach(f => o.value(f.trim()));
o.placeholder = _('-- default --');
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_set', form.MultiValue, 'ban_feedout', _('Outbound Feed'), _('Override the default feed configuration and apply the feed to the outbound chain only.'));
o.value('allowlist', _('local allowlist'));
o.value('blocklist', _('local blocklist'));
feedKeys.forEach(f => o.value(f.trim()));
o.placeholder = _('-- default --');
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_set', form.MultiValue, 'ban_feedinout', _('Inbound & Outbound Feed'), _('Override the default feed configuration and apply the feed to the inbound and outbound chain.'));
o.value('allowlist', _('local allowlist'));
o.value('blocklist', _('local blocklist'));
feedKeys.forEach(f => o.value(f.trim()));
o.placeholder = _('-- default --');
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_set', form.MultiValue, 'ban_feedreset', _('Feed Flag Reset'), _('Override the default feed configuration and remove existing port/protocol limitations.'));
o.value('allowlist', _('local allowlist'));
o.value('blocklist', _('local blocklist'));
feedKeys.forEach(f => o.value(f.trim()));
o.placeholder = _('-- default --');
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_set', form.MultiValue, 'ban_feedcomplete', _('Feed Complete'), _('Opt out specific feeds from the deduplication process.'));
o.value('allowlist', _('local allowlist'));
o.value('blocklist', _('local blocklist'));
feedKeys.forEach(f => o.value(f.trim()));
o.placeholder = _('-- default --');
o.optional = true;
o.rmempty = true;
}
/*
advanced log settings tab
*/
o = s.taboption('adv_log', form.DummyValue, '_sub');
o.rawhtml = true;
o.default = '' + _('Changes on this tab needs a banIP service restart to take effect.') + ''
+ '
';
o = s.taboption('adv_log', form.ListValue, 'ban_nftloglevel', _('NFT Log Level'), _('Set the syslog level for NFT logging.'));
o.value('emerg', _('emerg'));
o.value('alert', _('alert'));
o.value('crit', _('crit'));
o.value('err', _('err'));
o.value('warn', _('warn'));
o.value('notice', _('notice'));
o.value('info', _('info'));
o.value('debug', _('debug'));
o.default = 'warn';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_log', form.Flag, 'ban_logprerouting', _('Log Prerouting'), _('Log suspicious packets in the Prerouting chain.'));
o.rmempty = false;
o = s.taboption('adv_log', form.Flag, 'ban_loginbound', _('Log Inbound'), _('Log suspicious packets in the WAN-Input and WAN-Forward chain.'));
o.rmempty = false;
o = s.taboption('adv_log', form.Flag, 'ban_logoutbound', _('Log Outbound'), _('Log suspicious packets in the LAN-Forward chain.'));
o.rmempty = false;
o = s.taboption('adv_log', form.Value, 'ban_logreadfile', _('Logfile Location'), _('Location for parsing the log file, e.g. via syslog-ng, to deactivate the standard parsing via logread.'));
o.placeholder = '/var/log/messages';
o.rmempty = true;
o = s.taboption('adv_log', form.ListValue, 'ban_loglimit', _('Log Limit'), _('Parse only the last stated number of log entries for suspicious events. To disable the log monitor at all set it to \'0\'.'));
o.value('0');
o.value('50');
o.value('100');
o.value('250');
o.value('500');
o.value('1000');
o.default = '100';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_log', form.Value, 'ban_logcount', _('Log Count'), _('Number of failed login attempts of the same IP in the log before blocking.'));
o.placeholder = '1';
o.datatype = 'range(1,10)';
o.rmempty = true;
o = s.taboption('adv_log', form.ListValue, 'ban_logratelimit', _('Log Rate Limit'), _('Rate (per second) for the shared NFT log limit, applied globally across all logged rules. Set to \'0\' to disable rate limiting entirely, e.g. when using ulogd or other userspace log handlers.'));
o.value('0');
o.value('1');
o.value('5');
o.value('10');
o.value('25');
o.value('50');
o.value('100');
o.default = '10';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_log', form.ListValue, 'ban_logburstlimit', _('Log Burst Limit'), _('Burst size in packets for the shared NFT log limit.'));
o.depends({ ban_logratelimit: '0', '!reverse': true });
o.value('1');
o.value('5');
o.value('10');
o.value('25');
o.value('50');
o.default = '5';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_log', form.DynamicList, 'ban_logterm', _('Log Terms'), _('Regular expressions to detect suspicious IPs in the system log.'));
o.value('Exit before auth from', _('dropbear failed login'));
o.value('luci: failed login', _('LuCI failed login'));
o.value('error: maximum authentication attempts exceeded', _('sshd failed login'));
o.value('sshd.*Connection closed by.*\\[preauth\\]', _('sshd closed connection'));
o.value('SecurityEvent=\\"InvalidAccountID\\".*RemoteAddress=', _('asterisk invalid account'));
o.value('TLS Error: could not determine wrapping from \\[AF_INET\\]', _('openvpn TLS error'));
o.value('AdGuardHome.*\\[error\\].*/control/login: from ip', _('AdGuardHome login error'));
o.value('received a suspicious remote IP', _('Remote logging Event'));
o.placeholder = _('-- Please choose (optional) --');
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_log', form.Flag, 'ban_remotelog', _('Enable Remote Logging'), _('Enable the cgi interface to receive remote logging events.'));
o.default = 0;
o.optional = true;
o.rmempty = true;
o = s.taboption('adv_log', form.Value, 'ban_remotetoken', _('Remote Token'), _('Token to communicate with the cgi interface.'));
o.depends('ban_remotelog', '1');
o.datatype = 'and(minlength(3),maxlength(20))';
o.validate = function (section_id, value) {
if (!value) {
return _('Empty field not allowed');
}
if (!value.match(/^[A-Za-z0-9.:]+$/)) {
return _('Invalid characters');
}
return true;
};
o.optional = true;
o.rmempty = true;
/*
advanced email settings tab
*/
o = s.taboption('adv_email', form.DummyValue, '_sub');
o.rawhtml = true;
o.default = '' + _('To enable email notifications, set up the \'msmtp\' package and specify a valid E-Mail receiver address.') + ''
+ '
';
o = s.taboption('adv_email', form.Flag, 'ban_mailnotification', _('E-Mail Notification'), _('Receive E-Mail notifications with every banIP run.'));
o.rmempty = true;
o = s.taboption('adv_email', form.Value, 'ban_mailreceiver', _('E-Mail Receiver Address'), _('Receiver address for banIP notification E-Mails, this information is required to enable E-Mail functionality.'));
o.placeholder = 'name@example.com';
o.rmempty = true;
o = s.taboption('adv_email', form.Value, 'ban_mailsender', _('E-Mail Sender Address'), _('Sender address for banIP notification E-Mails.'));
o.placeholder = 'no-reply@banIP';
o.rmempty = true;
o = s.taboption('adv_email', form.Value, 'ban_mailtopic', _('E-Mail Topic'), _('Topic for banIP notification E-Mails.'));
o.placeholder = 'banIP notification';
o.rmempty = true;
o = s.taboption('adv_email', form.Value, 'ban_mailprofile', _('E-Mail Profile'), _('Profile used by \'msmtp\' for banIP notification E-Mails.'));
o.placeholder = 'ban_notify';
o.datatype = 'uciname';
o.rmempty = true;
/*
feeds tab
*/
o = s.taboption('feeds', form.DummyValue, '_sub');
o.rawhtml = true;
o.default = '' + _('Changes on this tab needs a banIP service reload to take effect.') + ''
+ '
'
+ '' + _('External Blocklist Feeds') + '';
if (feeds && Object.keys(feeds).length) {
o = s.taboption('feeds', form.MultiValue, 'ban_feed', _('Blocklist Feed'));
for (let i = 0; i < Object.keys(feeds).length; i++) {
feed = (Object.keys(feeds)[i] || '').trim();
chain = (feeds[feed]?.chain || 'in').trim();
descr = (feeds[feed]?.descr || '-').trim();
o.value(feed, feed + ' (' + chain + ', ' + descr + ')');
}
o.placeholder = _('-- Please choose (optional) --');
o.optional = true;
o.rmempty = true;
}
o = s.taboption('feeds', form.DummyValue, '_feeds1');
o.rawhtml = true;
o.default = '
' + _('Country Selection') + '';
let err, ccode, rir, country, countries = [];
if (result[2] && result[2].trim() !== "") {
countries = result[2].trim().split('\n');
if (countries && countries.length) {
o = s.taboption('feeds', form.MultiValue, 'ban_country', _('Countries') + ' (RIR)');
for (let i = 0; i < countries.length; i++) {
try {
ccode = countries[i].match(/^(\w+)\t/)[1].trim();
rir = countries[i].match(/^\w+\t(\w+)\t/)[1].trim();
country = countries[i].match(/^\w+\t\w+\t(.*$)/)[1].trim();
o.value(ccode, country + ' (' + rir + ')');
} catch (e) {
countries[i] = "";
if (!err) {
ui.addNotification(null, E('p', _('Unable to parse the countries file!')), 'error');
}
err = e;
}
}
o.placeholder = _('-- Please choose (optional) --');
o.optional = true;
o.rmempty = true;
}
}
o = s.taboption('feeds', form.MultiValue, 'ban_region', _('Regional Internet Registry'), _('Summary of countries based on the Regional Internet Registry (RIR).'));
o.value('AFRINIC', _('AFRINIC - serving Africa and the Indian Ocean region'));
o.value('APNIC', _('APNIC - serving the Asia Pacific region'));
o.value('ARIN', _('ARIN - serving Canada and the United States'));
o.value('LACNIC', _('LACNIC - serving the Latin American and Caribbean region'));
o.value('RIPE', _('RIPE - serving Europe, Middle East and Central Asia'));
o.placeholder = _('-- Please choose (optional) --');
o.optional = true;
o.rmempty = true;
o = s.taboption('feeds', form.Flag, 'ban_countrysplit', _('Split Country Set'), _('The selected Countries are stored in separate Sets.'));
o.rmempty = true;
o = s.taboption('feeds', form.DummyValue, '_feeds2');
o.rawhtml = true;
o.default = '
' + _('ASN Selection') + '';
o = s.taboption('feeds', form.DynamicList, 'ban_asn', _('ASNs'), _('Collection of IP addresses based on Autonomous System Numbers.'));
o.datatype = 'uinteger';
o.optional = true;
o.rmempty = true;
o = s.taboption('feeds', form.Flag, 'ban_asnsplit', _('Split ASN Set'), _('The selected ASNs are stored in separate Sets.'));
o.rmempty = true;
o = s.taboption('feeds', form.DummyValue, '_feeds3');
o.rawhtml = true;
o.default = '
' + _('External Allowlist Feeds') + '';
if (countries && countries.length) {
o = s.taboption('feeds', form.DynamicList, 'ban_allowurl', _('Allowlist Feed URLs'));
for (let i = 0; i < countries.length; i++) {
try {
ccode = countries[i].match(/^(\w+)\t/)[1].trim();
rir = countries[i].match(/^\w+\t(\w+)\t/)[1].trim();
country = countries[i].match(/^\w+\t\w+\t(.*$)/)[1].trim();
o.value('https://www.ipdeny.com/ipblocks/data/aggregated/' + ccode + '-aggregated.zone', country + ' IPv4 (' + rir + ')');
o.value('https://www.ipdeny.com/ipv6/ipaddresses/aggregated/' + ccode + '-aggregated.zone', country + ' IPv6 (' + rir + ')');
} catch (e) {
countries[i] = "";
}
}
o.placeholder = _('-- Please choose (optional) --');
o.optional = true;
o.rmempty = true;
o.validate = function (section_id, value) {
if (!value) {
return true;
}
if (!value.match(/^(https?:\/\/)[A-Za-z0-9-]+\.[A-Za-z0-9.-]+(:[0-9]+)?(\/[A-Za-z0-9._\-?&+=:~#%]*)?$/)) {
return _('Invalid URL format');
}
return true;
};
}
o = s.taboption('feeds', form.DummyValue, '_feeds4');
o.rawhtml = true;
o.default = '
' + _('Local Feed Settings') + '';
o = s.taboption('feeds', form.Flag, 'ban_autoallowlist', _('Auto Allowlist'), _('Automatically add resolved domains and uplink IPs to the local banIP allowlist.'));
o.default = 1;
o.rmempty = false;
o = s.taboption('feeds', form.ListValue, 'ban_autoallowuplink', _('Auto Allow Uplink'), _('Limit the uplink autoallow function.'));
o.depends('ban_autoallowlist', '1');
o.value('disable', _('Disable'));
o.value('subnet', _('Subnet'));
o.value('ip', _('IP'));
o.default = 'subnet';
o.placeholder = _('-- default --');
o.create = true;
o.optional = true;
o.rmempty = true;
o = s.taboption('feeds', form.Flag, 'ban_autoblocklist', _('Auto Blocklist'), _('Automatically add resolved domains and suspicious IPs to the local banIP blocklist.'));
o.default = 1;
o.rmempty = false;
o = s.taboption('feeds', form.Flag, 'ban_autoblocksubnet', _('Auto Block Subnet'), _('Automatically add entire subnets to the blocklist Set based on an additional RDAP request with the suspicious IP.'));
o.default = 0;
o.optional = true;
o.rmempty = true;
o = s.taboption('feeds', form.Value, 'ban_nftexpiry', _('Blocklist Set Expiry'), _('Expiry time for auto added blocklist Set members.'));
o.value('30ms');
o.value('10s');
o.value('1m');
o.value('5m');
o.value('1h');
o.value('2h');
o.value('1d');
o.value('7d');
o.value('2w');
o.placeholder = _('-- default --');
o.optional = true;
o.rmempty = true;
o.validate = function (section_id, value) {
if (!value) {
return true;
}
if (!value.match(/^([1-9][0-9]*(ms|s|m|h|d|w))+$/)) {
return _('Invalid expiry format, e.g. 5m, 2h, 1d or 1h30m');
}
return true;
};
o = s.taboption('feeds', form.Flag, 'ban_allowlistonly', _('Allowlist Only'), _('Restrict the internet access from/to a small number of secure IPs.'));
o.rmempty = false;
s = m.section(form.NamedSection, 'global');
s.render = L.bind(function () {
return E('div', { 'class': 'cbi-page-actions' }, [
E('button', {
'class': 'btn cbi-button cbi-button-negative important',
'style': 'float:none;margin-right:.4em;',
'click': ui.createHandlerFn(this, function () {
return handleAction('stop');
})
}, [_('Stop')]),
E('button', {
'class': 'btn cbi-button cbi-button-positive important',
'style': 'float:none;margin-right:.4em;',
'click': ui.createHandlerFn(this, function () {
return handleAction('reload');
})
}, [_('Save & Reload')]),
E('button', {
'class': 'btn cbi-button cbi-button-positive important',
'style': 'float:none',
'click': ui.createHandlerFn(this, function () {
return handleAction('restart');
})
}, [_('Save & Restart')])
]);
});
return m.render();
},
handleSaveApply: null,
handleSave: null,
handleReset: null
});