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