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