]>
git.proxmox.com Git - pve-manager.git/blob - www/manager6/grid/FirewallRules.js
1 Ext
.define('PVE.form.FWMacroSelector', {
2 extend
: 'Proxmox.form.ComboGrid',
3 alias
: 'widget.pveFWMacroSelector',
11 header
: gettext('Macro'),
17 header
: gettext('Description'),
18 renderer
: Ext
.String
.htmlEncode
,
24 initComponent: function() {
27 var store
= Ext
.create('Ext.data.Store', {
29 fields
: ['macro', 'descr'],
33 url
: "/api2/json/cluster/firewall/macros",
49 Ext
.define('PVE.form.ICMPTypeSelector', {
50 extend
: 'Proxmox.form.ComboGrid',
51 alias
: 'widget.pveICMPTypeSelector',
59 header
: gettext('Type'),
66 header
: gettext('Name'),
74 setName: function(value
) {
79 let ICMP_TYPE_NAMES_STORE
= Ext
.create('Ext.data.Store', {
80 field
: ['type', 'name'],
82 { type
: 'any', name
: 'any' },
83 { type
: '0', name
: 'echo-reply' },
84 { type
: '3', name
: 'destination-unreachable' },
85 { type
: '3/0', name
: 'network-unreachable' },
86 { type
: '3/1', name
: 'host-unreachable' },
87 { type
: '3/2', name
: 'protocol-unreachable' },
88 { type
: '3/3', name
: 'port-unreachable' },
89 { type
: '3/4', name
: 'fragmentation-needed' },
90 { type
: '3/5', name
: 'source-route-failed' },
91 { type
: '3/6', name
: 'network-unknown' },
92 { type
: '3/7', name
: 'host-unknown' },
93 { type
: '3/9', name
: 'network-prohibited' },
94 { type
: '3/10', name
: 'host-prohibited' },
95 { type
: '3/11', name
: 'TOS-network-unreachable' },
96 { type
: '3/12', name
: 'TOS-host-unreachable' },
97 { type
: '3/13', name
: 'communication-prohibited' },
98 { type
: '3/14', name
: 'host-precedence-violation' },
99 { type
: '3/15', name
: 'precedence-cutoff' },
100 { type
: '4', name
: 'source-quench' },
101 { type
: '5', name
: 'redirect' },
102 { type
: '5/0', name
: 'network-redirect' },
103 { type
: '5/1', name
: 'host-redirect' },
104 { type
: '5/2', name
: 'TOS-network-redirect' },
105 { type
: '5/3', name
: 'TOS-host-redirect' },
106 { type
: '8', name
: 'echo-request' },
107 { type
: '9', name
: 'router-advertisement' },
108 { type
: '10', name
: 'router-solicitation' },
109 { type
: '11', name
: 'time-exceeded' },
110 { type
: '11/0', name
: 'ttl-zero-during-transit' },
111 { type
: '11/1', name
: 'ttl-zero-during-reassembly' },
112 { type
: '12', name
: 'parameter-problem' },
113 { type
: '12/0', name
: 'ip-header-bad' },
114 { type
: '12/1', name
: 'required-option-missing' },
115 { type
: '13', name
: 'timestamp-request' },
116 { type
: '14', name
: 'timestamp-reply' },
117 { type
: '17', name
: 'address-mask-request' },
118 { type
: '18', name
: 'address-mask-reply' },
121 let ICMPV6_TYPE_NAMES_STORE
= Ext
.create('Ext.data.Store', {
122 field
: ['type', 'name'],
124 { type
: '1', name
: 'destination-unreachable' },
125 { type
: '1/0', name
: 'no-route' },
126 { type
: '1/1', name
: 'communication-prohibited' },
127 { type
: '1/2', name
: 'beyond-scope' },
128 { type
: '1/3', name
: 'address-unreachable' },
129 { type
: '1/4', name
: 'port-unreachable' },
130 { type
: '1/5', name
: 'failed-policy' },
131 { type
: '1/6', name
: 'reject-route' },
132 { type
: '2', name
: 'packet-too-big' },
133 { type
: '3', name
: 'time-exceeded' },
134 { type
: '3/0', name
: 'ttl-zero-during-transit' },
135 { type
: '3/1', name
: 'ttl-zero-during-reassembly' },
136 { type
: '4', name
: 'parameter-problem' },
137 { type
: '4/0', name
: 'bad-header' },
138 { type
: '4/1', name
: 'unknown-header-type' },
139 { type
: '4/2', name
: 'unknown-option' },
140 { type
: '128', name
: 'echo-request' },
141 { type
: '129', name
: 'echo-reply' },
142 { type
: '133', name
: 'router-solicitation' },
143 { type
: '134', name
: 'router-advertisement' },
144 { type
: '135', name
: 'neighbour-solicitation' },
145 { type
: '136', name
: 'neighbour-advertisement' },
146 { type
: '137', name
: 'redirect' },
150 Ext
.define('PVE.FirewallRulePanel', {
151 extend
: 'Proxmox.panel.InputPanel',
155 list_refs_url
: undefined,
157 onGetValues: function(values
) {
160 // hack: editable ComboGrid returns nothing when empty, so we need to set ''
161 // Also, disabled text fields return nothing, so we need to set ''
163 Ext
.Array
.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'icmp-type', 'log'], function(key
) {
164 if (values
[key
] === undefined) {
169 delete values
.modified_marker
;
174 initComponent: function() {
177 if (!me
.list_refs_url
) {
178 throw "no list_refs_url specified";
183 // hack: we use this field to mark the form 'dirty' when the
184 // record has errors- so that the user can safe the unmodified
186 xtype
: 'hiddenfield',
187 name
: 'modified_marker',
191 xtype
: 'proxmoxKVComboBox',
194 comboItems
: [['in', 'in'], ['out', 'out']],
195 fieldLabel
: gettext('Direction'),
199 xtype
: 'proxmoxKVComboBox',
202 comboItems
: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']],
203 fieldLabel
: gettext('Action'),
208 if (me
.allow_iface
) {
210 xtype
: 'proxmoxtextfield',
212 deleteEmpty
: !me
.isCreate
,
214 fieldLabel
: gettext('Interface'),
218 xtype
: 'displayfield',
226 xtype
: 'displayfield',
232 xtype
: 'pveIPRefSelector',
236 base_url
: me
.list_refs_url
,
238 fieldLabel
: gettext('Source'),
240 maxLengthText
: gettext('Too long, consider using IP sets.'),
243 xtype
: 'pveIPRefSelector',
247 base_url
: me
.list_refs_url
,
249 fieldLabel
: gettext('Destination'),
251 maxLengthText
: gettext('Too long, consider using IP sets.'),
258 xtype
: 'proxmoxcheckbox',
262 fieldLabel
: gettext('Enable'),
265 xtype
: 'pveFWMacroSelector',
267 fieldLabel
: gettext('Macro'),
271 change: function(f
, value
) {
272 if (value
=== null) {
273 me
.down('field[name=proto]').setDisabled(false);
274 me
.down('field[name=sport]').setDisabled(false);
275 me
.down('field[name=dport]').setDisabled(false);
277 me
.down('field[name=proto]').setDisabled(true);
278 me
.down('field[name=proto]').setValue('');
279 me
.down('field[name=sport]').setDisabled(true);
280 me
.down('field[name=sport]').setValue('');
281 me
.down('field[name=dport]').setDisabled(true);
282 me
.down('field[name=dport]').setValue('');
288 xtype
: 'pveIPProtocolSelector',
293 fieldLabel
: gettext('Protocol'),
295 change: function(f
, value
) {
296 if (value
=== 'icmp' || value
=== 'icmpv6' || value
=== 'ipv6-icmp') {
297 me
.down('field[name=dport]').setHidden(true);
298 me
.down('field[name=dport]').setDisabled(true);
299 if (value
=== 'icmp') {
300 me
.down('#icmpv4-type').setHidden(false);
301 me
.down('#icmpv4-type').setDisabled(false);
302 me
.down('#icmpv6-type').setHidden(true);
303 me
.down('#icmpv6-type').setDisabled(true);
305 me
.down('#icmpv6-type').setHidden(false);
306 me
.down('#icmpv6-type').setDisabled(false);
307 me
.down('#icmpv4-type').setHidden(true);
308 me
.down('#icmpv4-type').setDisabled(true);
311 me
.down('#icmpv4-type').setHidden(true);
312 me
.down('#icmpv4-type').setDisabled(true);
313 me
.down('#icmpv6-type').setHidden(true);
314 me
.down('#icmpv6-type').setDisabled(true);
315 me
.down('field[name=dport]').setHidden(false);
316 me
.down('field[name=dport]').setDisabled(false);
322 xtype
: 'displayfield',
331 fieldLabel
: gettext('Source port'),
337 fieldLabel
: gettext('Dest. port'),
340 xtype
: 'pveICMPTypeSelector',
348 fieldLabel
: gettext('ICMP type'),
349 store
: ICMP_TYPE_NAMES_STORE
,
352 xtype
: 'pveICMPTypeSelector',
360 fieldLabel
: gettext('ICMP type'),
361 store
: ICMPV6_TYPE_NAMES_STORE
,
365 me
.advancedColumn1
= [
367 xtype
: 'pveFirewallLogLevels',
376 fieldLabel
: gettext('Comment'),
384 Ext
.define('PVE.FirewallRuleEdit', {
385 extend
: 'Proxmox.window.Edit',
388 list_refs_url
: undefined,
392 initComponent: function() {
396 throw "no base_url specified";
398 if (!me
.list_refs_url
) {
399 throw "no list_refs_url specified";
402 me
.isCreate
= me
.rule_pos
=== undefined;
405 me
.url
= '/api2/extjs' + me
.base_url
;
408 me
.url
= '/api2/extjs' + me
.base_url
+ '/' + me
.rule_pos
.toString();
412 var ipanel
= Ext
.create('PVE.FirewallRulePanel', {
413 isCreate
: me
.isCreate
,
414 list_refs_url
: me
.list_refs_url
,
415 allow_iface
: me
.allow_iface
,
416 rule_pos
: me
.rule_pos
,
420 subject
: gettext('Rule'),
429 success: function(response
, options
) {
430 var values
= response
.result
.data
;
431 ipanel
.setValues(values
);
432 // set icmp-type again after protocol has been set
433 if (values
["icmp-type"] !== undefined) {
434 ipanel
.setValues({ "icmp-type": values
["icmp-type"] });
437 var field
= me
.query('[isFormField][name=modified_marker]')[0];
439 Ext
.Function
.defer(function() {
440 var form
= ipanel
.up('form').getForm();
441 form
.markInvalid(values
.errors
);
447 ipanel
.setValues(me
.rec
.data
);
452 Ext
.define('PVE.FirewallGroupRuleEdit', {
453 extend
: 'Proxmox.window.Edit',
459 initComponent: function() {
462 me
.isCreate
= me
.rule_pos
=== undefined;
465 me
.url
= '/api2/extjs' + me
.base_url
;
468 me
.url
= '/api2/extjs' + me
.base_url
+ '/' + me
.rule_pos
.toString();
474 xtype
: 'hiddenfield',
479 xtype
: 'pveSecurityGroupsSelector',
482 fieldLabel
: gettext('Security Group'),
487 if (me
.allow_iface
) {
489 xtype
: 'proxmoxtextfield',
491 deleteEmpty
: !me
.isCreate
,
493 fieldLabel
: gettext('Interface'),
497 var ipanel
= Ext
.create('Proxmox.panel.InputPanel', {
498 isCreate
: me
.isCreate
,
502 xtype
: 'proxmoxcheckbox',
506 fieldLabel
: gettext('Enable'),
514 fieldLabel
: gettext('Comment'),
520 subject
: gettext('Rule'),
529 success: function(response
, options
) {
530 var values
= response
.result
.data
;
531 ipanel
.setValues(values
);
538 Ext
.define('PVE.FirewallRules', {
539 extend
: 'Ext.grid.Panel',
540 alias
: 'widget.pveFirewallRules',
542 onlineHelp
: 'chapter_pve_firewall',
545 stateId
: 'grid-firewall-rules',
548 list_refs_url
: undefined,
551 removeBtn
: undefined,
555 tbar_prefix
: undefined,
560 setBaseUrl: function(url
) {
565 if (url
=== undefined) {
566 me
.addBtn
.setDisabled(true);
568 me
.groupBtn
.setDisabled(true);
570 me
.store
.removeAll();
572 me
.addBtn
.setDisabled(false);
573 me
.removeBtn
.baseurl
= url
+ '/';
575 me
.groupBtn
.setDisabled(false);
579 url
: '/api2/json' + url
,
586 moveRule: function(from, to
) {
593 Proxmox
.Utils
.API2Request({
594 url
: me
.base_url
+ "/" + from,
596 params
: { moveto
: to
},
598 failure: function(response
, options
) {
599 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
601 callback: function() {
607 updateRule: function(rule
) {
614 rule
.enable
= rule
.enable
? 1 : 0;
620 Proxmox
.Utils
.API2Request({
621 url
: me
.base_url
+ '/' + pos
.toString(),
625 failure: function(response
, options
) {
626 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
628 callback: function() {
635 initComponent: function() {
638 if (!me
.list_refs_url
) {
639 throw "no list_refs_url specified";
642 var store
= Ext
.create('Ext.data.Store', {
643 model
: 'pve-fw-rule',
646 var reload = function() {
650 var sm
= Ext
.create('Ext.selection.RowModel', {});
652 var run_editor = function() {
653 var rec
= sm
.getSelection()[0];
657 var type
= rec
.data
.type
;
660 if (type
=== 'in' || type
=== 'out') {
661 editor
= 'PVE.FirewallRuleEdit';
662 } else if (type
=== 'group') {
663 editor
= 'PVE.FirewallGroupRuleEdit';
668 var win
= Ext
.create(editor
, {
669 digest
: rec
.data
.digest
,
670 allow_iface
: me
.allow_iface
,
671 base_url
: me
.base_url
,
672 list_refs_url
: me
.list_refs_url
,
673 rule_pos
: rec
.data
.pos
,
677 win
.on('destroy', reload
);
680 me
.editBtn
= Ext
.create('Proxmox.button.Button', {
681 text
: gettext('Edit'),
687 me
.addBtn
= Ext
.create('Ext.Button', {
688 text
: gettext('Add'),
690 handler: function() {
691 var win
= Ext
.create('PVE.FirewallRuleEdit', {
692 allow_iface
: me
.allow_iface
,
693 base_url
: me
.base_url
,
694 list_refs_url
: me
.list_refs_url
,
696 win
.on('destroy', reload
);
701 var run_copy_editor = function() {
702 let rec
= sm
.getSelection()[0];
706 let type
= rec
.data
.type
;
707 if (!(type
=== 'in' || type
=== 'out')) {
711 let win
= Ext
.create('PVE.FirewallRuleEdit', {
712 allow_iface
: me
.allow_iface
,
713 base_url
: me
.base_url
,
714 list_refs_url
: me
.list_refs_url
,
718 win
.on('destroy', reload
);
721 me
.copyBtn
= Ext
.create('Proxmox.button.Button', {
722 text
: gettext('Copy'),
724 enableFn
: ({ data
}) => data
.type
=== 'in' || data
.type
=== 'out',
726 handler
: run_copy_editor
,
729 if (me
.allow_groups
) {
730 me
.groupBtn
= Ext
.create('Ext.Button', {
731 text
: gettext('Insert') + ': ' +
732 gettext('Security Group'),
734 handler: function() {
735 var win
= Ext
.create('PVE.FirewallGroupRuleEdit', {
736 allow_iface
: me
.allow_iface
,
737 base_url
: me
.base_url
,
739 win
.on('destroy', reload
);
745 me
.removeBtn
= Ext
.create('Proxmox.button.StdRemoveButton', {
747 baseurl
: me
.base_url
+ '/',
749 getRecordName: function(rec
) {
751 return rule
.pos
.toString() +
752 '?digest=' + encodeURIComponent(rule
.digest
);
754 callback: function() {
759 let tbar
= me
.tbar_prefix
? [me
.tbar_prefix
] : [];
760 tbar
.push(me
.addBtn
, me
.copyBtn
);
762 tbar
.push(me
.groupBtn
);
764 tbar
.push(me
.removeBtn
, me
.editBtn
);
766 let render_errors = function(name
, value
, metaData
, record
) {
767 let errors
= record
.data
.errors
;
768 if (errors
&& errors
[name
]) {
769 metaData
.tdCls
= 'proxmox-invalid-row';
770 let html
= '<p>' + Ext
.htmlEncode(errors
[name
]) + '</p>';
771 metaData
.tdAttr
= 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' + html
+ '"';
778 // similar to xtype: 'rownumberer',
787 renderer: function(value
, metaData
, record
, rowIdx
, colIdx
) {
788 metaData
.tdCls
= Ext
.baseCSSPrefix
+ 'grid-cell-special';
789 let dragHandle
= "<i class='pve-grid-fa fa fa-fw fa-reorder cursor-move'></i>";
791 return dragHandle
+ value
;
797 xtype
: 'checkcolumn',
798 header
: gettext('On'),
801 checkchange: function(column
, recordIndex
, checked
) {
802 var record
= me
.getStore().getData().items
[recordIndex
];
805 Ext
.Array
.forEach(record
.getFields(), function(field
) {
806 data
[field
.name
] = record
.get(field
.name
);
808 if (!me
.allow_iface
|| !data
.iface
) {
817 header
: gettext('Type'),
819 renderer: function(value
, metaData
, record
) {
820 return render_errors('type', value
, metaData
, record
);
827 header
: gettext('Action'),
829 renderer: function(value
, metaData
, record
) {
830 return render_errors('action', value
, metaData
, record
);
837 header
: gettext('Macro'),
839 renderer: function(value
, metaData
, record
) {
840 return render_errors('macro', value
, metaData
, record
);
847 if (me
.allow_iface
) {
849 header
: gettext('Interface'),
851 renderer: function(value
, metaData
, record
) {
852 return render_errors('iface', value
, metaData
, record
);
861 header
: gettext('Protocol'),
863 renderer: function(value
, metaData
, record
) {
864 return render_errors('proto', value
, metaData
, record
);
869 header
: gettext('Source'),
871 renderer: function(value
, metaData
, record
) {
872 return render_errors('source', value
, metaData
, record
);
878 header
: gettext('S.Port'),
880 renderer: function(value
, metaData
, record
) {
881 return render_errors('sport', value
, metaData
, record
);
886 header
: gettext('Destination'),
888 renderer: function(value
, metaData
, record
) {
889 return render_errors('dest', value
, metaData
, record
);
895 header
: gettext('D.Port'),
897 renderer: function(value
, metaData
, record
) {
898 return render_errors('dport', value
, metaData
, record
);
903 header
: gettext('Log level'),
905 renderer: function(value
, metaData
, record
) {
906 return render_errors('log', value
, metaData
, record
);
911 header
: gettext('Comment'),
912 dataIndex
: 'comment',
915 renderer: function(value
, metaData
, record
) {
916 let comment
= render_errors('comment', Ext
.util
.Format
.htmlEncode(value
), metaData
, record
) || '';
917 if (comment
.length
* 12 > metaData
.column
.cellWidth
) {
918 comment
= `<span data-qtip="${comment}">${comment}</span>`;
932 ptype
: 'gridviewdragdrop',
933 dragGroup
: 'FWRuleDDGroup',
934 dropGroup
: 'FWRuleDDGroup',
938 beforedrop: function(node
, data
, dropRec
, dropPosition
) {
940 return false; // empty view
942 let moveto
= dropRec
.get('pos');
943 if (dropPosition
=== 'after') {
946 let pos
= data
.records
[0].get('pos');
947 me
.moveRule(pos
, moveto
);
950 itemdblclick
: run_editor
,
953 sortableColumns
: false,
960 me
.setBaseUrl(me
.base_url
); // load
964 Ext
.define('pve-fw-rule', {
965 extend
: 'Ext.data.Model',
967 { name
: 'enable', type
: 'boolean' },