mirror of
https://github.com/webmin/webmin.git
synced 2026-05-06 15:20:29 +01:00
Fix to factor nftables profile ruleset generation into library
This commit is contained in:
@@ -1331,6 +1331,325 @@ my ($bb) = $b =~ /^(\d+)/;
|
||||
return ($aa || 0) <=> ($bb || 0) || $a cmp $b;
|
||||
}
|
||||
|
||||
# setup_profiles()
|
||||
# Returns available ruleset profiles and their default policies/services
|
||||
sub setup_profiles
|
||||
{
|
||||
return (
|
||||
{ 'id' => 'allow_all',
|
||||
'name' => text('setup_profile_allow_all'),
|
||||
'desc' => text('setup_profile_allow_all_desc'),
|
||||
'input' => 'accept',
|
||||
'forward' => 'accept',
|
||||
'output' => 'accept',
|
||||
'services' => [ ] },
|
||||
{ 'id' => 'management',
|
||||
'name' => text('setup_profile_management'),
|
||||
'desc' => text('setup_profile_management_desc'),
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh webmin) ] },
|
||||
{ 'id' => 'web',
|
||||
'name' => text('setup_profile_web'),
|
||||
'desc' => text('setup_profile_web_desc'),
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh webmin http https) ] },
|
||||
{ 'id' => 'mail',
|
||||
'name' => text('setup_profile_mail'),
|
||||
'desc' => text('setup_profile_mail_desc'),
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh usermin smtp submission smtps pop3 pop3s imap imaps) ] },
|
||||
{ 'id' => 'dns',
|
||||
'name' => text('setup_profile_dns'),
|
||||
'desc' => text('setup_profile_dns_desc'),
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh webmin dhcpv6 dns dot mdns) ] },
|
||||
{ 'id' => 'virtualmin',
|
||||
'name' => text('setup_profile_virtualmin'),
|
||||
'desc' => text('setup_profile_virtualmin_desc'),
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh webmin dhcpv6 dns dot ftp http https imap imaps
|
||||
mdns pop3 pop3s smtp submission smtps ftp_data
|
||||
ssh_alt webmin_range usermin passive_ftp) ] },
|
||||
{ 'id' => 'locked',
|
||||
'name' => text('setup_profile_locked'),
|
||||
'desc' => text('setup_profile_locked_desc'),
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'drop',
|
||||
'services' => [ ] },
|
||||
{ 'id' => 'custom',
|
||||
'name' => text('setup_profile_custom'),
|
||||
'desc' => text('setup_profile_custom_desc'),
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ ] },
|
||||
);
|
||||
}
|
||||
|
||||
# setup_services()
|
||||
# Returns selectable services and ports used by ruleset profiles
|
||||
sub setup_services
|
||||
{
|
||||
my $webmin_port = get_webmin_port();
|
||||
my $usermin_port = get_usermin_port();
|
||||
return (
|
||||
{ 'id' => 'ssh', 'label' => text('setup_svc_ssh'),
|
||||
'type' => text('setup_type_service'), 'port' => '22',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 22 accept' ] },
|
||||
{ 'id' => 'webmin', 'label' => text('setup_svc_webmin'),
|
||||
'type' => text('setup_type_service'), 'port' => $webmin_port,
|
||||
'proto' => 'TCP', 'rules' => [ "tcp dport $webmin_port accept" ] },
|
||||
{ 'id' => 'dhcpv6', 'label' => text('setup_svc_dhcpv6'),
|
||||
'type' => text('setup_type_service'), 'port' => '546',
|
||||
'proto' => 'UDP',
|
||||
'rules' => [ 'ip6 daddr fe80::/64 udp dport 546 accept' ] },
|
||||
{ 'id' => 'dns', 'label' => text('setup_svc_dns'),
|
||||
'type' => text('setup_type_service'), 'port' => '53',
|
||||
'proto' => 'TCP/UDP',
|
||||
'rules' => [ 'tcp dport 53 accept', 'udp dport 53 accept' ] },
|
||||
{ 'id' => 'dot', 'label' => text('setup_svc_dot'),
|
||||
'type' => text('setup_type_service'), 'port' => '853',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 853 accept' ] },
|
||||
{ 'id' => 'ftp', 'label' => text('setup_svc_ftp'),
|
||||
'type' => text('setup_type_service'), 'port' => '21',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 21 accept' ] },
|
||||
{ 'id' => 'http', 'label' => text('setup_svc_http'),
|
||||
'type' => text('setup_type_service'), 'port' => '80',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 80 accept' ] },
|
||||
{ 'id' => 'https', 'label' => text('setup_svc_https'),
|
||||
'type' => text('setup_type_service'), 'port' => '443',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 443 accept' ] },
|
||||
{ 'id' => 'imap', 'label' => text('setup_svc_imap'),
|
||||
'type' => text('setup_type_service'), 'port' => '143',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 143 accept' ] },
|
||||
{ 'id' => 'imaps', 'label' => text('setup_svc_imaps'),
|
||||
'type' => text('setup_type_service'), 'port' => '993',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 993 accept' ] },
|
||||
{ 'id' => 'mdns', 'label' => text('setup_svc_mdns'),
|
||||
'type' => text('setup_type_service'), 'port' => '5353',
|
||||
'proto' => 'UDP',
|
||||
'rules' => [ 'ip daddr 224.0.0.251 udp dport 5353 accept',
|
||||
'ip6 daddr ff02::fb udp dport 5353 accept' ] },
|
||||
{ 'id' => 'pop3', 'label' => text('setup_svc_pop3'),
|
||||
'type' => text('setup_type_service'), 'port' => '110',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 110 accept' ] },
|
||||
{ 'id' => 'pop3s', 'label' => text('setup_svc_pop3s'),
|
||||
'type' => text('setup_type_service'), 'port' => '995',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 995 accept' ] },
|
||||
{ 'id' => 'smtp', 'label' => text('setup_svc_smtp'),
|
||||
'type' => text('setup_type_service'), 'port' => '25',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 25 accept' ] },
|
||||
{ 'id' => 'submission', 'label' => text('setup_svc_submission'),
|
||||
'type' => text('setup_type_service'), 'port' => '587',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 587 accept' ] },
|
||||
{ 'id' => 'smtps', 'label' => text('setup_svc_smtps'),
|
||||
'type' => text('setup_type_service'), 'port' => '465',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 465 accept' ] },
|
||||
{ 'id' => 'ftp_data', 'label' => text('setup_port_ftp_data'),
|
||||
'type' => text('setup_type_port'), 'port' => '20',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 20 accept' ] },
|
||||
{ 'id' => 'ssh_alt', 'label' => text('setup_port_ssh_alt'),
|
||||
'type' => text('setup_type_port'), 'port' => '2222',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 2222 accept' ] },
|
||||
{ 'id' => 'webmin_range', 'label' => text('setup_port_webmin_range'),
|
||||
'type' => text('setup_type_port'), 'port' => '10000-10100',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 10000-10100 accept' ] },
|
||||
{ 'id' => 'usermin', 'label' => text('setup_port_usermin'),
|
||||
'type' => text('setup_type_port'), 'port' => $usermin_port,
|
||||
'proto' => 'TCP', 'rules' => [ "tcp dport $usermin_port accept" ] },
|
||||
{ 'id' => 'passive_ftp', 'label' => text('setup_port_passive_ftp'),
|
||||
'type' => text('setup_type_port'), 'port' => '49152-65535',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 49152-65535 accept' ] },
|
||||
);
|
||||
}
|
||||
|
||||
# create_profile_ruleset(table-name, profile-id, allowed-service-ids|'*')
|
||||
# Builds an inet table for a selected profile and service list
|
||||
sub create_profile_ruleset
|
||||
{
|
||||
my ($table_name, $profile_id, $allow_ids) = @_;
|
||||
my %profiles = map { $_->{'id'} => $_ } setup_profiles();
|
||||
my $profile = $profiles{$profile_id} || error(text('setup_invalid_type'));
|
||||
my @services = setup_services();
|
||||
my %services = map { $_->{'id'} => $_ } @services;
|
||||
my @allow_ids;
|
||||
if (!defined($allow_ids) || $allow_ids eq '*') {
|
||||
@allow_ids = @{$profile->{'services'} || [ ]};
|
||||
}
|
||||
elsif (ref($allow_ids) eq 'ARRAY') {
|
||||
@allow_ids = @$allow_ids;
|
||||
}
|
||||
else {
|
||||
@allow_ids = grep { $_ ne '' } split(/\s*,\s*|\s+/, $allow_ids);
|
||||
}
|
||||
my %allow;
|
||||
foreach my $id (@allow_ids) {
|
||||
$services{$id} || error(text('setup_eservice', $id));
|
||||
$allow{$id} = 1;
|
||||
}
|
||||
|
||||
my $table = {
|
||||
'name' => $table_name,
|
||||
'family' => 'inet',
|
||||
'rules' => [ ],
|
||||
'sets' => { },
|
||||
'chains' => {
|
||||
'input' => {
|
||||
'type' => 'filter',
|
||||
'hook' => 'input',
|
||||
'priority' => 0,
|
||||
'policy' => $profile->{'input'}
|
||||
},
|
||||
'forward' => {
|
||||
'type' => 'filter',
|
||||
'hook' => 'forward',
|
||||
'priority' => 0,
|
||||
'policy' => $profile->{'forward'}
|
||||
},
|
||||
'output' => {
|
||||
'type' => 'filter',
|
||||
'hook' => 'output',
|
||||
'priority' => 0,
|
||||
'policy' => $profile->{'output'}
|
||||
}
|
||||
}
|
||||
};
|
||||
return $table if ($profile_id eq 'allow_all');
|
||||
|
||||
add_profile_rule($table, 'input', 'ct state established,related accept');
|
||||
add_profile_rule($table, 'input', 'iif "lo" accept');
|
||||
add_profile_rule($table, 'input', 'meta l4proto { icmp, ipv6-icmp } accept');
|
||||
if ($profile->{'output'} eq 'drop') {
|
||||
add_profile_rule($table, 'output', 'ct state established,related accept');
|
||||
add_profile_rule($table, 'output', 'oif "lo" accept');
|
||||
add_profile_rule($table, 'output', 'meta l4proto { icmp, ipv6-icmp } accept');
|
||||
}
|
||||
|
||||
my %seen;
|
||||
my %ports;
|
||||
my @special_rules;
|
||||
foreach my $id (map { $_->{'id'} } @services) {
|
||||
next if (!$allow{$id});
|
||||
foreach my $rule (@{$services{$id}->{'rules'}}) {
|
||||
next if ($seen{$rule}++);
|
||||
if ($rule =~ /^(tcp|udp)\s+dport\s+(\S+)\s+accept$/) {
|
||||
$ports{$1}->{$2} = 1;
|
||||
}
|
||||
else {
|
||||
push(@special_rules, $rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
add_profile_port_set($table, $profile_id, \%ports);
|
||||
foreach my $rule (@special_rules) {
|
||||
add_profile_rule($table, 'input', $rule);
|
||||
}
|
||||
return $table;
|
||||
}
|
||||
|
||||
# add_profile_port_set(&table, profile-id, &proto-ports)
|
||||
# Adds profile service port sets and their input accept rules
|
||||
sub add_profile_port_set
|
||||
{
|
||||
my ($table, $profile_id, $ports) = @_;
|
||||
# Keep TCP and UDP ports in separate sets when they differ, otherwise a UDP
|
||||
# accept rule would also allow TCP-only service ports.
|
||||
my @protos = grep { keys %{$ports->{$_}} } sort keys %$ports;
|
||||
return if (!@protos);
|
||||
foreach my $proto (@protos) {
|
||||
next if (!keys %{$ports->{$proto}});
|
||||
my $set_name = profile_port_set_name($profile_id, $proto, scalar(@protos));
|
||||
my @elements = normalize_port_set_elements(keys %{$ports->{$proto}});
|
||||
$table->{'sets'}->{$set_name} = {
|
||||
'name' => $set_name,
|
||||
'type' => 'inet_service',
|
||||
'flags' => (grep { /-/ } @elements) ? 'interval' : undef,
|
||||
'elements' => \@elements,
|
||||
'raw_lines' => [ ],
|
||||
};
|
||||
add_profile_rule($table, 'input', "$proto dport \@$set_name accept");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# add_profile_rule(&table, chain, rule-text)
|
||||
# Appends a generated rule to a profile table
|
||||
sub add_profile_rule
|
||||
{
|
||||
my ($table, $chain, $text) = @_;
|
||||
push(@{$table->{'rules'}}, {
|
||||
'text' => $text,
|
||||
'chain' => $chain,
|
||||
'index' => scalar(@{$table->{'rules'}}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
# profile_table_name(profile-id)
|
||||
# Returns an unused default table name for a profile
|
||||
sub profile_table_name
|
||||
{
|
||||
my ($profile) = @_;
|
||||
my $base = profile_base_table_name($profile);
|
||||
my @tables = get_nftables_save();
|
||||
my %used = map { $_->{'family'} eq 'inet' ? ($_->{'name'} => 1) : ( ) }
|
||||
@tables;
|
||||
my $name = $base;
|
||||
my $i = 1;
|
||||
while ($used{$name}) {
|
||||
$name = $base."_".$i++;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
# profile_base_table_name(profile-id)
|
||||
# Returns the base table name for a profile before uniquifying
|
||||
sub profile_base_table_name
|
||||
{
|
||||
my ($profile) = @_;
|
||||
my %names = (
|
||||
'allow_all' => 'profile_allow_all',
|
||||
'management' => 'profile_management',
|
||||
'web' => 'profile_web',
|
||||
'mail' => 'profile_mail',
|
||||
'dns' => 'profile_dns',
|
||||
'virtualmin' => 'profile_hosting',
|
||||
'locked' => 'profile_locked',
|
||||
'custom' => 'profile_custom',
|
||||
);
|
||||
return $names{$profile} || 'profile_custom';
|
||||
}
|
||||
|
||||
# profile_port_set_name(profile, proto, proto-count)
|
||||
# Returns the set name used for profile-generated service ports
|
||||
sub profile_port_set_name
|
||||
{
|
||||
my ($profile, $proto, $proto_count) = @_;
|
||||
my $name = profile_base_table_name($profile);
|
||||
$name .= "_".$proto if ($proto_count && $proto_count > 1);
|
||||
$name .= "_ports";
|
||||
$name =~ s/[^\w-]/_/g;
|
||||
return $name;
|
||||
}
|
||||
|
||||
# default_profile_table_name()
|
||||
# Returns the default table name for the default profile
|
||||
sub default_profile_table_name
|
||||
{
|
||||
return profile_table_name('virtualmin');
|
||||
}
|
||||
|
||||
# set_type_kind(type)
|
||||
# Returns addr, port or undef for a set type
|
||||
sub set_type_kind
|
||||
|
||||
@@ -31,7 +31,7 @@ if ($in{'action'} eq 'create') {
|
||||
}
|
||||
|
||||
my @allow = grep { $_ ne '' } split(/\0/, $in{'allow'} || '');
|
||||
my $table = create_profile_ruleset($profile, $table_name, \@allow);
|
||||
my $table = create_profile_ruleset($table_name, $profile, \@allow);
|
||||
assert_table_acl($table);
|
||||
push(@tables, $table);
|
||||
|
||||
@@ -49,7 +49,7 @@ if ($in{'action'} eq 'create') {
|
||||
return;
|
||||
}
|
||||
|
||||
ui_print_header(undef, $text{'setup_title'}, "", "intro", 1, 1);
|
||||
ui_print_header(undef, $text{'index_profile_setup'}, "", "intro", 1, 1);
|
||||
|
||||
print ui_form_start("setup.cgi");
|
||||
print ui_hidden("action", "create");
|
||||
@@ -95,248 +95,6 @@ print profile_javascript(@profiles);
|
||||
print ui_form_end([ [ undef, $text{'setup_create'} ] ]);
|
||||
ui_print_footer("index.cgi", $text{'index_return'});
|
||||
|
||||
# setup_profiles()
|
||||
# Returns available ruleset profiles and their default policies/services
|
||||
sub setup_profiles
|
||||
{
|
||||
return (
|
||||
{ 'id' => 'allow_all',
|
||||
'name' => $text{'setup_profile_allow_all'},
|
||||
'desc' => $text{'setup_profile_allow_all_desc'},
|
||||
'input' => 'accept',
|
||||
'forward' => 'accept',
|
||||
'output' => 'accept',
|
||||
'services' => [ ] },
|
||||
{ 'id' => 'management',
|
||||
'name' => $text{'setup_profile_management'},
|
||||
'desc' => $text{'setup_profile_management_desc'},
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh webmin) ] },
|
||||
{ 'id' => 'web',
|
||||
'name' => $text{'setup_profile_web'},
|
||||
'desc' => $text{'setup_profile_web_desc'},
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh webmin http https) ] },
|
||||
{ 'id' => 'mail',
|
||||
'name' => $text{'setup_profile_mail'},
|
||||
'desc' => $text{'setup_profile_mail_desc'},
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh usermin smtp submission smtps pop3 pop3s imap imaps) ] },
|
||||
{ 'id' => 'dns',
|
||||
'name' => $text{'setup_profile_dns'},
|
||||
'desc' => $text{'setup_profile_dns_desc'},
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh webmin dhcpv6 dns dot mdns) ] },
|
||||
{ 'id' => 'virtualmin',
|
||||
'name' => $text{'setup_profile_virtualmin'},
|
||||
'desc' => $text{'setup_profile_virtualmin_desc'},
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ qw(ssh webmin dhcpv6 dns dot ftp http https imap imaps
|
||||
mdns pop3 pop3s smtp submission smtps ftp_data
|
||||
ssh_alt webmin_range usermin passive_ftp) ] },
|
||||
{ 'id' => 'locked',
|
||||
'name' => $text{'setup_profile_locked'},
|
||||
'desc' => $text{'setup_profile_locked_desc'},
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'drop',
|
||||
'services' => [ ] },
|
||||
{ 'id' => 'custom',
|
||||
'name' => $text{'setup_profile_custom'},
|
||||
'desc' => $text{'setup_profile_custom_desc'},
|
||||
'input' => 'drop',
|
||||
'forward' => 'drop',
|
||||
'output' => 'accept',
|
||||
'services' => [ ] },
|
||||
);
|
||||
}
|
||||
|
||||
# setup_services()
|
||||
# Returns selectable services and ports used by ruleset profiles
|
||||
sub setup_services
|
||||
{
|
||||
my $webmin_port = get_webmin_port();
|
||||
my $usermin_port = get_usermin_port();
|
||||
return (
|
||||
{ 'id' => 'ssh', 'label' => $text{'setup_svc_ssh'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '22',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 22 accept' ] },
|
||||
{ 'id' => 'webmin', 'label' => text('setup_svc_webmin', $webmin_port),
|
||||
'type' => $text{'setup_type_service'}, 'port' => $webmin_port,
|
||||
'proto' => 'TCP', 'rules' => [ "tcp dport $webmin_port accept" ] },
|
||||
{ 'id' => 'dhcpv6', 'label' => $text{'setup_svc_dhcpv6'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '546',
|
||||
'proto' => 'UDP',
|
||||
'rules' => [ 'ip6 daddr fe80::/64 udp dport 546 accept' ] },
|
||||
{ 'id' => 'dns', 'label' => $text{'setup_svc_dns'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '53',
|
||||
'proto' => 'TCP/UDP',
|
||||
'rules' => [ 'tcp dport 53 accept', 'udp dport 53 accept' ] },
|
||||
{ 'id' => 'dot', 'label' => $text{'setup_svc_dot'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '853',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 853 accept' ] },
|
||||
{ 'id' => 'ftp', 'label' => $text{'setup_svc_ftp'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '21',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 21 accept' ] },
|
||||
{ 'id' => 'http', 'label' => $text{'setup_svc_http'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '80',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 80 accept' ] },
|
||||
{ 'id' => 'https', 'label' => $text{'setup_svc_https'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '443',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 443 accept' ] },
|
||||
{ 'id' => 'imap', 'label' => $text{'setup_svc_imap'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '143',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 143 accept' ] },
|
||||
{ 'id' => 'imaps', 'label' => $text{'setup_svc_imaps'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '993',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 993 accept' ] },
|
||||
{ 'id' => 'mdns', 'label' => $text{'setup_svc_mdns'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '5353',
|
||||
'proto' => 'UDP',
|
||||
'rules' => [ 'ip daddr 224.0.0.251 udp dport 5353 accept',
|
||||
'ip6 daddr ff02::fb udp dport 5353 accept' ] },
|
||||
{ 'id' => 'pop3', 'label' => $text{'setup_svc_pop3'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '110',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 110 accept' ] },
|
||||
{ 'id' => 'pop3s', 'label' => $text{'setup_svc_pop3s'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '995',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 995 accept' ] },
|
||||
{ 'id' => 'smtp', 'label' => $text{'setup_svc_smtp'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '25',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 25 accept' ] },
|
||||
{ 'id' => 'submission', 'label' => $text{'setup_svc_submission'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '587',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 587 accept' ] },
|
||||
{ 'id' => 'smtps', 'label' => $text{'setup_svc_smtps'},
|
||||
'type' => $text{'setup_type_service'}, 'port' => '465',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 465 accept' ] },
|
||||
{ 'id' => 'ftp_data', 'label' => $text{'setup_port_ftp_data'},
|
||||
'type' => $text{'setup_type_port'}, 'port' => '20',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 20 accept' ] },
|
||||
{ 'id' => 'ssh_alt', 'label' => $text{'setup_port_ssh_alt'},
|
||||
'type' => $text{'setup_type_port'}, 'port' => '2222',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 2222 accept' ] },
|
||||
{ 'id' => 'webmin_range', 'label' => $text{'setup_port_webmin_range'},
|
||||
'type' => $text{'setup_type_port'}, 'port' => '10000-10100',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 10000-10100 accept' ] },
|
||||
{ 'id' => 'usermin', 'label' => $text{'setup_port_usermin'},
|
||||
'type' => $text{'setup_type_port'}, 'port' => $usermin_port,
|
||||
'proto' => 'TCP', 'rules' => [ "tcp dport $usermin_port accept" ] },
|
||||
{ 'id' => 'passive_ftp', 'label' => $text{'setup_port_passive_ftp'},
|
||||
'type' => $text{'setup_type_port'}, 'port' => '49152-65535',
|
||||
'proto' => 'TCP', 'rules' => [ 'tcp dport 49152-65535 accept' ] },
|
||||
);
|
||||
}
|
||||
|
||||
# create_profile_ruleset(profile-id, table-name, &allowed-service-ids)
|
||||
# Builds an inet table for a selected profile and service list
|
||||
sub create_profile_ruleset
|
||||
{
|
||||
my ($profile_id, $table_name, $allow_ids) = @_;
|
||||
my %profiles = map { $_->{'id'} => $_ } setup_profiles();
|
||||
my $profile = $profiles{$profile_id} || error($text{'setup_invalid_type'});
|
||||
my @services = setup_services();
|
||||
my %services = map { $_->{'id'} => $_ } @services;
|
||||
my %allow;
|
||||
foreach my $id (@$allow_ids) {
|
||||
$services{$id} || error(text('setup_eservice', $id));
|
||||
$allow{$id} = 1;
|
||||
}
|
||||
|
||||
my $table = {
|
||||
'name' => $table_name,
|
||||
'family' => 'inet',
|
||||
'rules' => [ ],
|
||||
'sets' => { },
|
||||
'chains' => {
|
||||
'input' => {
|
||||
'type' => 'filter',
|
||||
'hook' => 'input',
|
||||
'priority' => 0,
|
||||
'policy' => $profile->{'input'}
|
||||
},
|
||||
'forward' => {
|
||||
'type' => 'filter',
|
||||
'hook' => 'forward',
|
||||
'priority' => 0,
|
||||
'policy' => $profile->{'forward'}
|
||||
},
|
||||
'output' => {
|
||||
'type' => 'filter',
|
||||
'hook' => 'output',
|
||||
'priority' => 0,
|
||||
'policy' => $profile->{'output'}
|
||||
}
|
||||
}
|
||||
};
|
||||
return $table if ($profile_id eq 'allow_all');
|
||||
|
||||
add_profile_rule($table, 'input', 'ct state established,related accept');
|
||||
add_profile_rule($table, 'input', 'iif "lo" accept');
|
||||
add_profile_rule($table, 'input', 'meta l4proto { icmp, ipv6-icmp } accept');
|
||||
if ($profile->{'output'} eq 'drop') {
|
||||
add_profile_rule($table, 'output', 'ct state established,related accept');
|
||||
add_profile_rule($table, 'output', 'oif "lo" accept');
|
||||
add_profile_rule($table, 'output', 'meta l4proto { icmp, ipv6-icmp } accept');
|
||||
}
|
||||
|
||||
my %seen;
|
||||
my %ports;
|
||||
my @special_rules;
|
||||
foreach my $id (map { $_->{'id'} } @services) {
|
||||
next if (!$allow{$id});
|
||||
foreach my $rule (@{$services{$id}->{'rules'}}) {
|
||||
next if ($seen{$rule}++);
|
||||
if ($rule =~ /^(tcp|udp)\s+dport\s+(\S+)\s+accept$/) {
|
||||
$ports{$1}->{$2} = 1;
|
||||
}
|
||||
else {
|
||||
push(@special_rules, $rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
add_profile_port_set($table, $profile_id, \%ports);
|
||||
foreach my $rule (@special_rules) {
|
||||
add_profile_rule($table, 'input', $rule);
|
||||
}
|
||||
return $table;
|
||||
}
|
||||
|
||||
# add_profile_port_set(&table, profile-id, &proto-ports)
|
||||
# Adds profile service port sets and their input accept rules
|
||||
sub add_profile_port_set
|
||||
{
|
||||
my ($table, $profile_id, $ports) = @_;
|
||||
# Keep TCP and UDP ports in separate sets when they differ, otherwise a UDP
|
||||
# accept rule would also allow TCP-only service ports.
|
||||
my @protos = grep { keys %{$ports->{$_}} } sort keys %$ports;
|
||||
return if (!@protos);
|
||||
foreach my $proto (@protos) {
|
||||
next if (!keys %{$ports->{$proto}});
|
||||
my $set_name = profile_port_set_name($profile_id, $proto, scalar(@protos));
|
||||
my @elements = normalize_port_set_elements(keys %{$ports->{$proto}});
|
||||
$table->{'sets'}->{$set_name} = {
|
||||
'name' => $set_name,
|
||||
'type' => 'inet_service',
|
||||
'flags' => (grep { /-/ } @elements) ? 'interval' : undef,
|
||||
'elements' => \@elements,
|
||||
'raw_lines' => [ ],
|
||||
};
|
||||
add_profile_rule($table, 'input', "$proto dport \@$set_name accept");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
# profile_javascript(@profiles)
|
||||
# Returns JavaScript for profile-driven table names, notes and service checks
|
||||
sub profile_javascript
|
||||
@@ -403,70 +161,3 @@ return <<EOF;
|
||||
</script>
|
||||
EOF
|
||||
}
|
||||
|
||||
# add_profile_rule(&table, chain, rule-text)
|
||||
# Appends a generated rule to a profile table
|
||||
sub add_profile_rule
|
||||
{
|
||||
my ($table, $chain, $text) = @_;
|
||||
push(@{$table->{'rules'}}, {
|
||||
'text' => $text,
|
||||
'chain' => $chain,
|
||||
'index' => scalar(@{$table->{'rules'}}),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
# profile_table_name(profile-id)
|
||||
# Returns an unused default table name for a profile
|
||||
sub profile_table_name
|
||||
{
|
||||
my ($profile) = @_;
|
||||
my $base = profile_base_table_name($profile);
|
||||
my @tables = get_nftables_save();
|
||||
my %used = map { $_->{'family'} eq 'inet' ? ($_->{'name'} => 1) : ( ) }
|
||||
@tables;
|
||||
my $name = $base;
|
||||
my $i = 1;
|
||||
while ($used{$name}) {
|
||||
$name = $base."_".$i++;
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
# profile_base_table_name(profile-id)
|
||||
# Returns the base table name for a profile before uniquifying
|
||||
sub profile_base_table_name
|
||||
{
|
||||
my ($profile) = @_;
|
||||
my %names = (
|
||||
'allow_all' => 'profile_allow_all',
|
||||
'management' => 'profile_management',
|
||||
'web' => 'profile_web',
|
||||
'mail' => 'profile_mail',
|
||||
'dns' => 'profile_dns',
|
||||
'virtualmin' => 'profile_hosting',
|
||||
'locked' => 'profile_locked',
|
||||
'custom' => 'profile_custom',
|
||||
);
|
||||
return $names{$profile} || 'profile_custom';
|
||||
}
|
||||
|
||||
# profile_port_set_name(profile, proto, proto-count)
|
||||
# Returns the set name used for profile-generated service ports
|
||||
sub profile_port_set_name
|
||||
{
|
||||
my ($profile, $proto, $proto_count) = @_;
|
||||
my $name = profile_base_table_name($profile);
|
||||
$name .= "_".$proto if ($proto_count && $proto_count > 1);
|
||||
$name .= "_ports";
|
||||
$name =~ s/[^\w-]/_/g;
|
||||
return $name;
|
||||
}
|
||||
|
||||
# default_profile_table_name()
|
||||
# Returns the default table name for the default profile
|
||||
sub default_profile_table_name
|
||||
{
|
||||
return profile_table_name('virtualmin');
|
||||
}
|
||||
|
||||
@@ -235,4 +235,20 @@ my $quick_ip_table = {
|
||||
like(add_quick_ip_rule($quick_ip_table, '2001:db8::1/64', 'allow'),
|
||||
qr/cannot contain/, 'wrong address family rejected');
|
||||
|
||||
my $profile_table = create_profile_ruleset('profile_virtualmin', 'virtualmin', '*');
|
||||
is($profile_table->{family}, 'inet', 'profile helper family');
|
||||
is($profile_table->{name}, 'profile_virtualmin', 'profile helper table name');
|
||||
ok($profile_table->{sets}->{profile_hosting_tcp_ports},
|
||||
'profile helper tcp port set');
|
||||
is($profile_table->{sets}->{profile_hosting_tcp_ports}->{flags}, 'interval',
|
||||
'profile helper tcp port set interval flag');
|
||||
is_deeply($profile_table->{sets}->{profile_hosting_udp_ports}->{elements},
|
||||
[ '53' ], 'profile helper udp port set elements');
|
||||
ok(scalar(grep { $_->{text} eq 'tcp dport @profile_hosting_tcp_ports accept' }
|
||||
@{$profile_table->{rules}}),
|
||||
'profile helper tcp set rule');
|
||||
ok(scalar(grep { $_->{text} eq 'ip6 daddr fe80::/64 udp dport 546 accept' }
|
||||
@{$profile_table->{rules}}),
|
||||
'profile helper special dhcpv6 rule');
|
||||
|
||||
done_testing();
|
||||
|
||||
Reference in New Issue
Block a user