mirror of
https://github.com/webmin/webmin.git
synced 2026-05-06 23:30:29 +01:00
Chain CRUD functionality
This commit is contained in:
54
delete_chain.cgi
Normal file
54
delete_chain.cgi
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/perl
|
||||
# delete_chain.cgi
|
||||
# Delete an existing nftables chain
|
||||
|
||||
require './nftables-lib.pl';
|
||||
use strict;
|
||||
use warnings;
|
||||
our (%in, %text);
|
||||
&ReadParse();
|
||||
&error_setup($text{'delete_chain_err'});
|
||||
|
||||
my @tables = &get_nftables_save();
|
||||
my $table = $tables[$in{'table'}];
|
||||
$table || &error($text{'chain_notable'});
|
||||
|
||||
my $chain = $table->{'chains'}->{$in{'chain'}};
|
||||
$chain || &error($text{'chain_nochain'});
|
||||
|
||||
my @refs = grep {
|
||||
($_->{'jump'} && $_->{'jump'} eq $in{'chain'}) ||
|
||||
($_->{'goto'} && $_->{'goto'} eq $in{'chain'})
|
||||
} @{$table->{'rules'}};
|
||||
|
||||
if ($in{'confirm'}) {
|
||||
@refs && &error(&text('delete_chain_inuse', $in{'chain'}, scalar(@refs)));
|
||||
|
||||
@{$table->{'rules'}} = grep { $_->{'chain'} ne $in{'chain'} } @{$table->{'rules'}};
|
||||
delete($table->{'chains'}->{$in{'chain'}});
|
||||
|
||||
my $err = &save_configuration(@tables);
|
||||
&error(&text('delete_chain_failed', $err)) if ($err);
|
||||
&webmin_log("delete", "chain", $in{'chain'},
|
||||
{ 'table' => $table->{'name'}, 'family' => $table->{'family'} });
|
||||
&redirect("index.cgi?table=$in{'table'}");
|
||||
}
|
||||
|
||||
&ui_print_header(undef, $text{'delete_chain_title'}, "", "intro", 1, 1);
|
||||
print &ui_form_start("delete_chain.cgi");
|
||||
print &ui_hidden("table", $in{'table'});
|
||||
print &ui_hidden("chain", $in{'chain'});
|
||||
print "<center><b>",
|
||||
&text('delete_chain_confirm',
|
||||
"<tt>$in{'chain'}</tt>",
|
||||
"<tt>$table->{'family'} $table->{'name'}</tt>"),
|
||||
"</b>";
|
||||
if (@refs) {
|
||||
print "<br><br>", &text('delete_chain_inuse', $in{'chain'}, scalar(@refs));
|
||||
}
|
||||
print "<p>\n";
|
||||
print &ui_submit($text{'delete'}, "confirm");
|
||||
print "</center>\n";
|
||||
print &ui_form_end();
|
||||
&ui_print_footer("index.cgi?table=$in{'table'}", $text{'index_return'});
|
||||
|
||||
87
edit_chain.cgi
Normal file
87
edit_chain.cgi
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/perl
|
||||
# edit_chain.cgi
|
||||
# Display a form for creating or editing a chain
|
||||
|
||||
require './nftables-lib.pl';
|
||||
use strict;
|
||||
use warnings;
|
||||
our (%in, %text);
|
||||
&ReadParse();
|
||||
|
||||
my @tables = &get_nftables_save();
|
||||
my $table = $tables[$in{'table'}];
|
||||
$table || &error($text{'chain_notable'});
|
||||
|
||||
my $chain = { };
|
||||
my $chain_name = "";
|
||||
my $is_new = $in{'new'} ? 1 : 0;
|
||||
|
||||
if ($is_new) {
|
||||
&ui_print_header(undef, $text{'chain_title_new'}, "", "intro", 1, 1);
|
||||
} else {
|
||||
$chain_name = $in{'chain'};
|
||||
$chain = $table->{'chains'}->{$chain_name};
|
||||
$chain || &error($text{'chain_nochain'});
|
||||
&ui_print_header(undef, $text{'chain_title_edit'}, "", "intro", 1, 1);
|
||||
}
|
||||
|
||||
my @type_opts = (
|
||||
[ "", $text{'chain_type_none'} ],
|
||||
map { [ $_, $_ ] } qw(filter nat route)
|
||||
);
|
||||
my @hook_opts = (
|
||||
[ "", $text{'chain_hook_none'} ],
|
||||
map { [ $_, $_ ] } qw(prerouting input forward output postrouting ingress)
|
||||
);
|
||||
my @policy_opts = (
|
||||
[ "", $text{'chain_policy_none'} ],
|
||||
map { [ $_, $_ ] } qw(accept drop reject return queue continue)
|
||||
);
|
||||
|
||||
print &ui_form_start("save_chain.cgi");
|
||||
print &ui_hidden("table", $in{'table'});
|
||||
print &ui_hidden("new", $is_new);
|
||||
|
||||
print &ui_table_start($text{'chain_header'}, "width=100%", 2);
|
||||
|
||||
my $name_tags = $is_new ? undef : "readonly";
|
||||
print &ui_table_row(hlink($text{'chain_name'}, "chain_name"),
|
||||
&ui_textbox("chain_name", $chain_name, 20, 0, undef, $name_tags));
|
||||
|
||||
print &ui_table_row(hlink($text{'chain_type'}, "chain_type"),
|
||||
&ui_select("chain_type", $chain->{'type'}, \@type_opts, 1, 0, 1, 0,
|
||||
"onchange='toggle_chain_base()'"));
|
||||
print &ui_table_row(hlink($text{'chain_hook'}, "chain_hook"),
|
||||
&ui_select("chain_hook", $chain->{'hook'}, \@hook_opts, 1, 0, 1));
|
||||
print &ui_table_row(hlink($text{'chain_priority'}, "chain_priority"),
|
||||
&ui_textbox("chain_priority", $chain->{'priority'}, 10));
|
||||
print &ui_table_row(hlink($text{'chain_policy'}, "chain_policy"),
|
||||
&ui_select("chain_policy", $chain->{'policy'}, \@policy_opts, 1, 0, 1));
|
||||
|
||||
print &ui_table_end();
|
||||
|
||||
print &ui_form_end([ [ undef, $text{$is_new ? 'create' : 'save'} ] ]);
|
||||
|
||||
print <<'EOF';
|
||||
<script>
|
||||
function toggle_chain_base() {
|
||||
var type = document.getElementById('chain_type');
|
||||
var disabled = !type || !type.value;
|
||||
var ids = ['chain_hook', 'chain_priority', 'chain_policy'];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var el = document.getElementById(ids[i]);
|
||||
if (el) {
|
||||
el.disabled = disabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('load', toggle_chain_base);
|
||||
} else if (window.attachEvent) {
|
||||
window.attachEvent('onload', toggle_chain_base);
|
||||
}
|
||||
</script>
|
||||
EOF
|
||||
|
||||
&ui_print_footer("index.cgi?table=$in{'table'}", $text{'index_return'});
|
||||
|
||||
3
help/chain_hook.html
Normal file
3
help/chain_hook.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<header>Hook</header>
|
||||
<p>Base chain hook point, such as <tt>prerouting</tt>, <tt>input</tt>, <tt>forward</tt>, <tt>output</tt>, <tt>postrouting</tt>, or <tt>ingress</tt>.</p>
|
||||
<footer>nft(8)</footer>
|
||||
3
help/chain_name.html
Normal file
3
help/chain_name.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<header>Chain name</header>
|
||||
<p>Unique name for the chain within this table. Use letters, numbers, underscores, and dashes.</p>
|
||||
<footer>nft(8)</footer>
|
||||
3
help/chain_policy.html
Normal file
3
help/chain_policy.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<header>Policy</header>
|
||||
<p>Default action for this base chain, such as <tt>accept</tt>, <tt>drop</tt>, <tt>reject</tt>, <tt>queue</tt>, or <tt>continue</tt>.</p>
|
||||
<footer>nft(8)</footer>
|
||||
3
help/chain_priority.html
Normal file
3
help/chain_priority.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<header>Priority</header>
|
||||
<p>Priority for this base chain. Lower values run earlier. Common values include <tt>-300</tt>, <tt>-150</tt>, <tt>0</tt>, or <tt>100</tt>.</p>
|
||||
<footer>nft(8)</footer>
|
||||
3
help/chain_type.html
Normal file
3
help/chain_type.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<header>Chain type</header>
|
||||
<p>Base chain type, such as <tt>filter</tt>, <tt>nat</tt>, or <tt>route</tt>. Leave blank to create a regular chain with no hook.</p>
|
||||
<footer>nft(8)</footer>
|
||||
17
index.cgi
17
index.cgi
@@ -76,7 +76,8 @@ if (!@tables) {
|
||||
$rules_html .= &ui_columns_start(
|
||||
[ $text{'index_chain_col'}, $text{'index_type'},
|
||||
$text{'index_hook'}, $text{'index_priority'},
|
||||
$text{'index_policy_col'}, $text{'index_rules'} ], 100);
|
||||
$text{'index_policy_col'}, $text{'index_rules'},
|
||||
$text{'index_actions'} ], 100);
|
||||
|
||||
foreach my $c (sort keys %{$curr->{'chains'}}) {
|
||||
my $chain_def = $curr->{'chains'}->{$c} || { };
|
||||
@@ -102,18 +103,30 @@ if (!@tables) {
|
||||
&ui_link("edit_rule.cgi?table=$in{'table'}&chain=".
|
||||
&urlize($c)."&new=1", $text{'index_radd'});
|
||||
|
||||
my $actions_html =
|
||||
&ui_link("edit_chain.cgi?table=$in{'table'}&chain=".
|
||||
&urlize($c), $text{'index_cedit'})."<br>".
|
||||
&ui_link("rename_chain.cgi?table=$in{'table'}&chain=".
|
||||
&urlize($c), $text{'index_crename'})."<br>".
|
||||
&ui_link("delete_chain.cgi?table=$in{'table'}&chain=".
|
||||
&urlize($c), $text{'index_cdelete'});
|
||||
$rules_html .= &ui_columns_row([
|
||||
$c,
|
||||
$chain_def->{'type'} || "-",
|
||||
$chain_def->{'hook'} || "-",
|
||||
defined($chain_def->{'priority'}) ? $chain_def->{'priority'} : "-",
|
||||
$policy_label,
|
||||
$rules_html_row
|
||||
$rules_html_row,
|
||||
$actions_html
|
||||
]);
|
||||
}
|
||||
$rules_html .= &ui_columns_end();
|
||||
$rules_html .= &ui_hr();
|
||||
$rules_html .= &ui_buttons_start();
|
||||
$rules_html .= &ui_buttons_row(
|
||||
"edit_chain.cgi?table=$in{'table'}&new=1",
|
||||
$text{'index_chain_create'},
|
||||
$text{'index_chain_createdesc'});
|
||||
$rules_html .= &ui_buttons_row("delete_table.cgi?table=$in{'table'}",
|
||||
$text{'index_table_delete'},
|
||||
$text{'index_table_deletedesc'});
|
||||
|
||||
33
lang/en
33
lang/en
@@ -27,6 +27,10 @@ index_priority=Priority
|
||||
index_policy_col=Policy
|
||||
index_rules=Rules
|
||||
index_rules_none=No rules
|
||||
index_actions=Actions
|
||||
index_chain_create=Create chain
|
||||
index_chain_createdesc=Add a new chain to this table.
|
||||
index_cedit=Edit Chain
|
||||
index_cdelete=Delete Chain
|
||||
index_crename=Rename Chain
|
||||
index_cclear=Clear All Rules
|
||||
@@ -125,6 +129,35 @@ edit_advanced=Advanced options
|
||||
edit_oif=Outgoing Interface
|
||||
edit_iif=Incoming Interface
|
||||
edit_if_any=Any
|
||||
chain_title_new=Create chain
|
||||
chain_title_edit=Edit chain
|
||||
chain_header=Chain Details
|
||||
chain_name=Chain name
|
||||
chain_type=Type
|
||||
chain_hook=Hook
|
||||
chain_priority=Priority
|
||||
chain_policy=Policy
|
||||
chain_type_none=Regular (no hook)
|
||||
chain_hook_none=None
|
||||
chain_policy_none=None
|
||||
chain_err=Failed to save chain
|
||||
chain_failed=Failed to save chain: <pre>$1</pre>
|
||||
chain_ename=Chain name is invalid
|
||||
chain_edup=A chain with that name already exists
|
||||
chain_notable=No such table selected
|
||||
chain_nochain=No such chain selected
|
||||
chain_ebase=Base chains require type, hook, priority, and policy.
|
||||
delete_chain_title=Delete chain
|
||||
delete_chain_err=Failed to delete chain
|
||||
delete_chain_failed=Failed to delete chain: <pre>$1</pre>
|
||||
delete_chain_confirm=Are you sure you want to delete chain $1 from table $2?
|
||||
delete_chain_inuse=Chain $1 is referenced by $2 rule(s) via jump/goto. Remove those rules first.
|
||||
rename_chain_title=Rename chain
|
||||
rename_chain_header=Rename chain
|
||||
rename_chain_old=Current name
|
||||
rename_chain_new=New name
|
||||
rename_chain_ok=Rename
|
||||
rename_chain_failed=Failed to rename chain: <pre>$1</pre>
|
||||
create_title=Create table
|
||||
create_header=Create a new table
|
||||
create_family=Table family
|
||||
|
||||
@@ -181,6 +181,16 @@ return "ip6" if (defined($addr) && $addr =~ /:/);
|
||||
return "ip";
|
||||
}
|
||||
|
||||
sub validate_chain_base
|
||||
{
|
||||
my ($type, $hook, $priority, $policy) = @_;
|
||||
if (defined($type) || defined($hook) || defined($priority) || defined($policy)) {
|
||||
return 0 if (!defined($type) || !defined($hook) ||
|
||||
!defined($priority) || !defined($policy));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub format_addr_expr
|
||||
{
|
||||
my ($dir, $rule) = @_;
|
||||
|
||||
33
rename_chain.cgi
Normal file
33
rename_chain.cgi
Normal file
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/perl
|
||||
# rename_chain.cgi
|
||||
# Rename an existing chain
|
||||
|
||||
require './nftables-lib.pl';
|
||||
use strict;
|
||||
use warnings;
|
||||
our (%in, %text);
|
||||
&ReadParse();
|
||||
|
||||
my @tables = &get_nftables_save();
|
||||
my $table = $tables[$in{'table'}];
|
||||
$table || &error($text{'chain_notable'});
|
||||
|
||||
my $chain = $table->{'chains'}->{$in{'chain'}};
|
||||
$chain || &error($text{'chain_nochain'});
|
||||
|
||||
&ui_print_header(undef, $text{'rename_chain_title'}, "", "intro", 1, 1);
|
||||
print &ui_form_start("save_chain.cgi");
|
||||
print &ui_hidden("table", $in{'table'});
|
||||
print &ui_hidden("rename", 1);
|
||||
print &ui_hidden("chain_old", $in{'chain'});
|
||||
|
||||
print &ui_table_start($text{'rename_chain_header'}, "width=100%", 2);
|
||||
print &ui_table_row($text{'rename_chain_old'},
|
||||
"<tt>".&html_escape($in{'chain'})."</tt>");
|
||||
print &ui_table_row(hlink($text{'rename_chain_new'}, "chain_name"),
|
||||
&ui_textbox("chain_name", $in{'chain'}, 20));
|
||||
print &ui_table_end();
|
||||
|
||||
print &ui_form_end([ [ undef, $text{'rename_chain_ok'} ] ]);
|
||||
&ui_print_footer("index.cgi?table=$in{'table'}", $text{'index_return'});
|
||||
|
||||
99
save_chain.cgi
Normal file
99
save_chain.cgi
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/perl
|
||||
# save_chain.cgi
|
||||
# Save a new or existing chain
|
||||
|
||||
require './nftables-lib.pl';
|
||||
use strict;
|
||||
use warnings;
|
||||
our (%in, %text);
|
||||
&ReadParse();
|
||||
&error_setup($text{'chain_err'});
|
||||
|
||||
my @tables = &get_nftables_save();
|
||||
my $table = $tables[$in{'table'}];
|
||||
$table || &error($text{'chain_notable'});
|
||||
|
||||
my $is_new = $in{'new'} ? 1 : 0;
|
||||
my $is_rename = $in{'rename'} ? 1 : 0;
|
||||
my $name = $in{'chain_name'};
|
||||
$name =~ s/^\s+// if (defined($name));
|
||||
$name =~ s/\s+$// if (defined($name));
|
||||
$name =~ /^\w[\w-]*$/ || &error($text{'chain_ename'});
|
||||
|
||||
my $old = $is_rename ? $in{'chain_old'} : $name;
|
||||
$old =~ s/^\s+// if (defined($old));
|
||||
$old =~ s/\s+$// if (defined($old));
|
||||
|
||||
if ($is_new) {
|
||||
$table->{'chains'}->{$name} && &error($text{'chain_edup'});
|
||||
} elsif ($is_rename) {
|
||||
$table->{'chains'}->{$old} || &error($text{'chain_nochain'});
|
||||
if ($name ne $old && $table->{'chains'}->{$name}) {
|
||||
&error($text{'chain_edup'});
|
||||
}
|
||||
} else {
|
||||
$table->{'chains'}->{$name} || &error($text{'chain_nochain'});
|
||||
}
|
||||
|
||||
if ($is_rename) {
|
||||
if ($name eq $old) {
|
||||
&redirect("index.cgi?table=$in{'table'}");
|
||||
}
|
||||
if ($name ne $old) {
|
||||
$table->{'chains'}->{$name} = $table->{'chains'}->{$old};
|
||||
delete($table->{'chains'}->{$old});
|
||||
|
||||
foreach my $r (@{$table->{'rules'}}) {
|
||||
$r->{'chain'} = $name if ($r->{'chain'} && $r->{'chain'} eq $old);
|
||||
my $changed = 0;
|
||||
if ($r->{'jump'} && $r->{'jump'} eq $old) {
|
||||
$r->{'jump'} = $name;
|
||||
$changed = 1;
|
||||
}
|
||||
if ($r->{'goto'} && $r->{'goto'} eq $old) {
|
||||
$r->{'goto'} = $name;
|
||||
$changed = 1;
|
||||
}
|
||||
$r->{'text'} = &format_rule_text($r) if ($changed);
|
||||
}
|
||||
}
|
||||
|
||||
my $err = &save_configuration(@tables);
|
||||
&error(&text('rename_chain_failed', $err)) if ($err);
|
||||
&webmin_log("rename", "chain", $old,
|
||||
{ 'new' => $name,
|
||||
'table' => $table->{'name'},
|
||||
'family' => $table->{'family'} });
|
||||
&redirect("index.cgi?table=$in{'table'}");
|
||||
}
|
||||
|
||||
my $type = $in{'chain_type'};
|
||||
my $hook = $in{'chain_hook'};
|
||||
my $priority = $in{'chain_priority'};
|
||||
my $policy = $in{'chain_policy'};
|
||||
|
||||
for my $v (\$type, \$hook, \$priority, \$policy) {
|
||||
$$v =~ s/^\s+// if (defined($$v));
|
||||
$$v =~ s/\s+$// if (defined($$v));
|
||||
}
|
||||
$type = undef if (!defined($type) || $type eq '');
|
||||
$hook = undef if (!defined($hook) || $hook eq '');
|
||||
$priority = undef if (!defined($priority) || $priority eq '');
|
||||
$policy = undef if (!defined($policy) || $policy eq '');
|
||||
|
||||
&validate_chain_base($type, $hook, $priority, $policy) ||
|
||||
&error($text{'chain_ebase'});
|
||||
|
||||
my $chain = $table->{'chains'}->{$name} || { };
|
||||
$chain->{'type'} = $type;
|
||||
$chain->{'hook'} = $hook;
|
||||
$chain->{'priority'} = $priority;
|
||||
$chain->{'policy'} = $policy;
|
||||
$table->{'chains'}->{$name} = $chain;
|
||||
|
||||
my $err = &save_configuration(@tables);
|
||||
&error(&text('chain_failed', $err)) if ($err);
|
||||
|
||||
&webmin_log($is_new ? "create" : "modify", "chain", $name,
|
||||
{ 'table' => $table->{'name'}, 'family' => $table->{'family'} });
|
||||
&redirect("index.cgi?table=$in{'table'}");
|
||||
@@ -128,4 +128,11 @@ check_fields('ruleset r1', $rules[0], { iif => 'lo', action => 'accept' });
|
||||
check_fields('ruleset r2', $rules[1], { saddr => '192.168.1.0/24', proto => 'tcp', dport => '22', action => 'accept', comment => 'ssh' });
|
||||
check_fields('ruleset r3', $rules[2], { ct_state => 'established,related', action => 'accept' });
|
||||
|
||||
ok(&validate_chain_base('filter', 'input', '0', 'accept'),
|
||||
'chain base allows zero priority');
|
||||
ok(!&validate_chain_base('filter', 'input', undef, 'accept'),
|
||||
'chain base missing priority invalid');
|
||||
ok(&validate_chain_base(undef, undef, undef, undef),
|
||||
'chain base none set valid');
|
||||
|
||||
done_testing();
|
||||
|
||||
Reference in New Issue
Block a user