f9d84bfb673ceb357629b9d647fa660b8af8767b
[project/luci.git] / applications / luci-app-firewall / htdocs / luci-static / resources / view / firewall / zones.js
1 'use strict';
2 'require view';
3 'require rpc';
4 'require uci';
5 'require form';
6 'require network';
7 'require firewall';
8 'require tools.firewall as fwtool';
9 'require tools.widgets as widgets';
10
11 return view.extend({
12 callConntrackHelpers: rpc.declare({
13 object: 'luci',
14 method: 'getConntrackHelpers',
15 expect: { result: [] }
16 }),
17
18 load: function() {
19 return Promise.all([
20 this.callConntrackHelpers(),
21 firewall.getDefaults()
22 ]);
23 },
24
25 render: function(data) {
26 if (fwtool.checkLegacySNAT())
27 return fwtool.renderMigration();
28 else
29 return this.renderZones(data);
30 },
31
32 renderZones: function(data) {
33 var ctHelpers = data[0],
34 fwDefaults = data[1],
35 m, s, o, inp, out;
36
37 m = new form.Map('firewall', _('Firewall - Zone Settings'),
38 _('The firewall creates zones over your network interfaces to control network traffic flow.'));
39
40 s = m.section(form.TypedSection, 'defaults', _('General Settings'));
41 s.anonymous = true;
42 s.addremove = false;
43
44 o = s.option(form.Flag, 'synflood_protect', _('Enable SYN-flood protection'));
45 o.cfgvalue = function(section_id) {
46 var val = uci.get('firewall', section_id, 'synflood_protect');
47 return (val != null) ? val : uci.get('firewall', section_id, 'syn_flood');
48 };
49 o.write = function(section_id, value) {
50 uci.unset('firewall', section_id, 'syn_flood');
51 uci.set('firewall', section_id, 'synflood_protect', value);
52 };
53 o.remove = function(section_id) {
54 uci.unset('firewall', section_id, 'syn_flood');
55 uci.unset('firewall', section_id, 'synflood_protect');
56 };
57
58 o = s.option(form.Flag, 'drop_invalid', _('Drop invalid packets'));
59
60 var p = [
61 s.option(form.ListValue, 'input', _('Input')),
62 s.option(form.ListValue, 'output', _('Output')),
63 s.option(form.ListValue, 'forward', _('Forward'))
64 ];
65
66 for (var i = 0; i < p.length; i++) {
67 p[i].value('REJECT', _('reject'));
68 p[i].value('DROP', _('drop'));
69 p[i].value('ACCEPT', _('accept'));
70 }
71
72 /* Netfilter flow offload support */
73
74 if (L.hasSystemFeature('offloading')) {
75 s = m.section(form.TypedSection, 'defaults', _('Routing/NAT Offloading'),
76 _('Experimental feature. Not fully compatible with QoS/SQM.'));
77
78 s.anonymous = true;
79 s.addremove = false;
80
81 o = s.option(form.Flag, 'flow_offloading',
82 _('Software flow offloading'),
83 _('Software based offloading for routing/NAT'));
84 o.optional = true;
85
86 o = s.option(form.Flag, 'flow_offloading_hw',
87 _('Hardware flow offloading'),
88 _('Requires hardware NAT support. Implemented at least for mt7621'));
89 o.optional = true;
90 o.depends('flow_offloading', '1');
91 }
92
93
94 s = m.section(form.GridSection, 'zone', _('Zones'));
95 s.addremove = true;
96 s.anonymous = true;
97 s.sortable = true;
98
99 s.handleRemove = function(section_id, ev) {
100 return firewall.deleteZone(section_id).then(L.bind(function() {
101 return this.super('handleRemove', [section_id, ev]);
102 }, this));
103 };
104
105 s.tab('general', _('General Settings'));
106 s.tab('advanced', _('Advanced Settings'));
107 s.tab('conntrack', _('Conntrack Settings'));
108 s.tab('extra', _('Extra iptables arguments'));
109
110 o = s.taboption('general', form.DummyValue, '_generalinfo');
111 o.rawhtml = true;
112 o.modalonly = true;
113 o.cfgvalue = function(section_id) {
114 var name = uci.get('firewall', section_id, 'name');
115 if (name == null)
116 name = _("this new zone");
117 return _('This section defines common properties of %q. The <em>input</em> and <em>output</em> options set the default policies for traffic entering and leaving this zone while the <em>forward</em> option describes the policy for forwarded traffic between different networks within the zone. <em>Covered networks</em> specifies which available networks are members of this zone.')
118 .replace(/%s/g, name).replace(/%q/g, '"' + name + '"');
119 };
120
121 o = s.taboption('general', form.Value, 'name', _('Name'));
122 o.placeholder = _('Unnamed zone');
123 o.modalonly = true;
124 o.rmempty = false;
125 o.datatype = 'and(uciname,maxlength(11))';
126 o.write = function(section_id, formvalue) {
127 var cfgvalue = this.cfgvalue(section_id);
128
129 if (cfgvalue == null || cfgvalue == '')
130 return uci.set('firewall', section_id, 'name', formvalue);
131 else if (cfgvalue != formvalue)
132 return firewall.renameZone(cfgvalue, formvalue);
133 };
134
135 o = s.option(widgets.ZoneForwards, '_info', _('Zone ⇒ Forwardings'));
136 o.editable = true;
137 o.modalonly = false;
138 o.cfgvalue = function(section_id) {
139 return uci.get('firewall', section_id, 'name');
140 };
141
142 var p = [
143 s.taboption('general', form.ListValue, 'input', _('Input')),
144 s.taboption('general', form.ListValue, 'output', _('Output')),
145 s.taboption('general', form.ListValue, 'forward', _('Forward'))
146 ];
147
148 for (var i = 0; i < p.length; i++) {
149 p[i].value('REJECT', _('reject'));
150 p[i].value('DROP', _('drop'));
151 p[i].value('ACCEPT', _('accept'));
152 p[i].editable = true;
153 }
154
155 p[0].default = fwDefaults.getInput();
156 p[1].default = fwDefaults.getOutput();
157 p[2].default = fwDefaults.getForward();
158
159 o = s.taboption('general', form.Flag, 'masq', _('Masquerading'));
160 o.editable = true;
161 o.tooltip = function(section_id) {
162 var masq_src = uci.get('firewall', section_id, 'masq_src')
163 var masq_dest = uci.get('firewall', section_id, 'masq_dest')
164 if (masq_src || masq_dest)
165 return _('Limited masquerading enabled');
166
167 return null;
168 };
169
170 o = s.taboption('general', form.Flag, 'mtu_fix', _('MSS clamping'));
171 o.modalonly = true;
172
173 o = s.taboption('general', widgets.NetworkSelect, 'network', _('Covered networks'));
174 o.modalonly = true;
175 o.multiple = true;
176 o.cfgvalue = function(section_id) {
177 return uci.get('firewall', section_id, 'network');
178 };
179 o.write = function(section_id, formvalue) {
180 var name = uci.get('firewall', section_id, 'name'),
181 cfgvalue = this.cfgvalue(section_id),
182 oldNetworks = L.toArray(cfgvalue),
183 newNetworks = L.toArray(formvalue);
184
185 oldNetworks.sort();
186 newNetworks.sort();
187
188 if (oldNetworks.join(' ') == newNetworks.join(' '))
189 return;
190
191 var tasks = [ firewall.getZone(name) ];
192
193 if (Array.isArray(formvalue))
194 for (var i = 0; i < newNetworks.length; i++) {
195 var netname = newNetworks[i];
196 tasks.push(network.getNetwork(netname).then(L.bind(function(netname, net) {
197 return net || network.addNetwork(netname, { 'proto': 'none' });
198 }, this, netname)));
199 }
200
201 return Promise.all(tasks).then(function(zone_networks) {
202 if (zone_networks[0]) {
203 zone_networks[0].clearNetworks();
204 for (var i = 1; i < zone_networks.length; i++)
205 zone_networks[0].addNetwork(zone_networks[i].getName());
206 }
207 });
208 };
209
210 o = s.taboption('advanced', form.DummyValue, '_advancedinfo');
211 o.rawhtml = true;
212 o.modalonly = true;
213 o.cfgvalue = function(section_id) {
214 var name = uci.get('firewall', section_id, 'name');
215 if (name == null)
216 name = _("this new zone");
217 return _('The options below control the forwarding policies between this zone (%s) and other zones. <em>Destination zones</em> cover forwarded traffic <strong>originating from %q</strong>. <em>Source zones</em> match forwarded traffic from other zones <strong>targeted at %q</strong>. The forwarding rule is <em>unidirectional</em>, e.g. a forward from lan to wan does <em>not</em> imply a permission to forward from wan to lan as well.')
218 .format(name);
219 };
220
221 o = s.taboption('advanced', widgets.DeviceSelect, 'device', _('Covered devices'), _('Use this option to classify zone traffic by raw, non-<em>uci</em> managed network devices.'));
222 o.modalonly = true;
223 o.noaliases = true;
224 o.multiple = true;
225
226 o = s.taboption('advanced', form.DynamicList, 'subnet', _('Covered subnets'), _('Use this option to classify zone traffic by source or destination subnet instead of networks or devices.'));
227 o.datatype = 'neg(cidr("true"))';
228 o.modalonly = true;
229 o.multiple = true;
230
231 o = s.taboption('advanced', form.ListValue, 'family', _('Restrict to address family'));
232 o.value('', _('IPv4 and IPv6'));
233 o.value('ipv4', _('IPv4 only'));
234 o.value('ipv6', _('IPv6 only'));
235 o.modalonly = true;
236
237 o = s.taboption('advanced', form.DynamicList, 'masq_src', _('Restrict Masquerading to given source subnets'));
238 o.depends('family', '');
239 o.depends('family', 'ipv4');
240 o.datatype = 'list(neg(or(uciname,hostname,ipmask4)))';
241 o.placeholder = '0.0.0.0/0';
242 o.modalonly = true;
243
244 o = s.taboption('advanced', form.DynamicList, 'masq_dest', _('Restrict Masquerading to given destination subnets'));
245 o.depends('family', '');
246 o.depends('family', 'ipv4');
247 o.datatype = 'list(neg(or(uciname,hostname,ipmask4)))';
248 o.placeholder = '0.0.0.0/0';
249 o.modalonly = true;
250
251 o = s.taboption('conntrack', form.Flag, 'masq_allow_invalid', _('Allow "invalid" traffic'), _('Do not install extra rules to reject forwarded traffic with conntrack state <em>invalid</em>. This may be required for complex asymmetric route setups.'));
252 o.modalonly = true;
253
254 o = s.taboption('conntrack', form.Flag, 'auto_helper', _('Automatic helper assignment'), _('Automatically assign conntrack helpers based on traffic protocol and port'));
255 o.default = o.enabled;
256 o.modalonly = true;
257
258 o = s.taboption('conntrack', form.MultiValue, 'helper', _('Conntrack helpers'), _('Explicitly choses allowed connection tracking helpers for zone traffic'));
259 o.depends('auto_helper', '0');
260 o.modalonly = true;
261 for (var i = 0; i < ctHelpers.length; i++)
262 o.value(ctHelpers[i].name, E('<span><span class="hide-close">%s (%s)</span><span class="hide-open">%s</span></span>'.format(ctHelpers[i].description, ctHelpers[i].name.toUpperCase(), ctHelpers[i].name.toUpperCase())));
263
264 o = s.taboption('advanced', form.Flag, 'log', _('Enable logging on this zone'));
265 o.modalonly = true;
266
267 o = s.taboption('advanced', form.Value, 'log_limit', _('Limit log messages'));
268 o.depends('log', '1');
269 o.placeholder = '10/minute';
270 o.modalonly = true;
271
272 o = s.taboption('extra', form.DummyValue, '_extrainfo');
273 o.rawhtml = true;
274 o.modalonly = true;
275 o.cfgvalue = function(section_id) {
276 return _('Passing raw iptables arguments to source and destination traffic classification rules allows to match packets based on other criteria than interfaces or subnets. These options should be used with extreme care as invalid values could render the firewall ruleset broken, completely exposing all services.');
277 };
278
279 o = s.taboption('extra', form.Value, 'extra_src', _('Extra source arguments'), _('Additional raw <em>iptables</em> arguments to classify zone source traffic, e.g. <code>-p tcp --sport 443</code> to only match inbound HTTPS traffic.'));
280 o.modalonly = true;
281 o.cfgvalue = function(section_id) {
282 return uci.get('firewall', section_id, 'extra_src') || uci.get('firewall', section_id, 'extra');
283 };
284 o.write = function(section_id, value) {
285 uci.unset('firewall', section_id, 'extra');
286 uci.set('firewall', section_id, 'extra_src', value);
287 };
288
289 o = s.taboption('extra', form.Value, 'extra_dest', _('Extra destination arguments'), _('Additional raw <em>iptables</em> arguments to classify zone destination traffic, e.g. <code>-p tcp --dport 443</code> to only match outbound HTTPS traffic.'));
290 o.modalonly = true;
291 o.cfgvalue = function(section_id) {
292 return uci.get('firewall', section_id, 'extra_dest') || uci.get('firewall', section_id, 'extra_src') || uci.get('firewall', section_id, 'extra');
293 };
294 o.write = function(section_id, value) {
295 uci.unset('firewall', section_id, 'extra');
296 uci.set('firewall', section_id, 'extra_dest', value);
297 };
298
299 o = s.taboption('general', form.DummyValue, '_forwardinfo');
300 o.rawhtml = true;
301 o.modalonly = true;
302 o.cfgvalue = function(section_id) {
303 var name = uci.get('firewall', section_id, 'name');
304 if (name == null)
305 name = _("this new zone");
306 return _('The options below control the forwarding policies between this zone (%s) and other zones. <em>Destination zones</em> cover forwarded traffic <strong>originating from %q</strong>. <em>Source zones</em> match forwarded traffic from other zones <strong>targeted at %q</strong>. The forwarding rule is <em>unidirectional</em>, e.g. a forward from lan to wan does <em>not</em> imply a permission to forward from wan to lan as well.')
307 .format(name);
308 };
309
310 out = o = s.taboption('general', widgets.ZoneSelect, 'out', _('Allow forward to <em>destination zones</em>:'));
311 o.nocreate = true;
312 o.multiple = true;
313 o.modalonly = true;
314 o.filter = function(section_id, value) {
315 return (uci.get('firewall', section_id, 'name') != value);
316 };
317 o.cfgvalue = function(section_id) {
318 var out = (this.option == 'out'),
319 zone = this.lookupZone(uci.get('firewall', section_id, 'name')),
320 fwds = zone ? zone.getForwardingsBy(out ? 'src' : 'dest') : [],
321 value = [];
322
323 for (var i = 0; i < fwds.length; i++)
324 value.push(out ? fwds[i].getDestination() : fwds[i].getSource());
325
326 return value;
327 };
328 o.write = o.remove = function(section_id, formvalue) {
329 var out = (this.option == 'out'),
330 zone = this.lookupZone(uci.get('firewall', section_id, 'name')),
331 fwds = zone ? zone.getForwardingsBy(out ? 'src' : 'dest') : [];
332
333 if (formvalue == null)
334 formvalue = [];
335
336 if (Array.isArray(formvalue)) {
337 for (var i = 0; i < fwds.length; i++) {
338 var cmp = out ? fwds[i].getDestination() : fwds[i].getSource();
339 if (!formvalue.filter(function(d) { return d == cmp }).length)
340 zone.deleteForwarding(fwds[i]);
341 }
342
343 for (var i = 0; i < formvalue.length; i++)
344 if (out)
345 zone.addForwardingTo(formvalue[i]);
346 else
347 zone.addForwardingFrom(formvalue[i]);
348 }
349 };
350
351 inp = o = s.taboption('general', widgets.ZoneSelect, 'in', _('Allow forward from <em>source zones</em>:'));
352 o.nocreate = true;
353 o.multiple = true;
354 o.modalonly = true;
355 o.write = o.remove = out.write;
356 o.filter = out.filter;
357 o.cfgvalue = out.cfgvalue;
358
359 return m.render();
360 }
361 });