mirror of
https://github.com/webmin/webmin.git
synced 2026-10-25 04:30:40 +00:00
Add Cloudmin virtualization host firewall profile
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
## Changelog
|
||||
#### 2.671 (October, 2026)
|
||||
* Add a Cloudmin virtualization host profile to the nftables module
|
||||
* Fix two-factor authentication validation to use the Webmin configuration and data directories passed by the caller
|
||||
* Fix HTML escaping of module, script and session fields on the Webmin Actions Log details page
|
||||
* Fix mailbox attachment option injection
|
||||
|
||||
@@ -135,6 +135,8 @@ setup_profile_dns=DNS server
|
||||
setup_profile_dns_desc=Management access plus DNS, DNS-over-TLS, DHCPv6 client and mDNS
|
||||
setup_profile_virtualmin=Virtualmin hosting server
|
||||
setup_profile_virtualmin_desc=Management, web, mail, DNS, FTP, Usermin and passive FTP ports
|
||||
setup_profile_cloudmin=Cloudmin virtualization host
|
||||
setup_profile_cloudmin_desc=Management access and guest DNS/DHCP
|
||||
setup_profile_locked=Locked-down server
|
||||
setup_profile_locked_desc=Drop input, forward and output traffic with no service exceptions
|
||||
setup_profile_custom=Custom selected services
|
||||
|
||||
@@ -2990,6 +2990,14 @@ return (
|
||||
ssh_alt webmin_range usermin passive_ftp)
|
||||
]
|
||||
},
|
||||
{
|
||||
'id' => 'cloudmin',
|
||||
'name' => text('setup_profile_cloudmin'),
|
||||
'desc' => text('setup_profile_cloudmin_desc'),
|
||||
'input' => 'drop',
|
||||
'services' => [qw(ssh webmin)],
|
||||
'guest_bridges' => [ 'virbr*' ]
|
||||
},
|
||||
{
|
||||
'id' => 'locked',
|
||||
'name' => text('setup_profile_locked'),
|
||||
@@ -3852,12 +3860,16 @@ my $table = {
|
||||
}
|
||||
}
|
||||
};
|
||||
# Omitted policies leave those hooks to another manager, such as libvirt.
|
||||
foreach my $chain (keys %{$table->{'chains'}}) {
|
||||
delete $table->{'chains'}->{$chain} if (!defined($profile->{$chain}));
|
||||
}
|
||||
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') {
|
||||
if (($profile->{'output'} || '') eq 'drop') {
|
||||
add_profile_rule($table, 'output',
|
||||
'ct state established,related accept');
|
||||
add_profile_rule($table, 'output', 'oif "lo" accept');
|
||||
@@ -3884,6 +3896,14 @@ add_profile_port_set($table, $profile_id, \%ports);
|
||||
foreach my $rule (@special_rules) {
|
||||
add_profile_rule($table, 'input', $rule);
|
||||
}
|
||||
|
||||
# Guest DNS and DHCP reach the host only through the profile's bridges.
|
||||
foreach my $bridge (@{$profile->{'guest_bridges'} || [ ]}) {
|
||||
add_profile_rule($table, 'input',
|
||||
qq(iifname "$bridge" udp dport { 53, 67, 547 } accept));
|
||||
add_profile_rule($table, 'input',
|
||||
qq(iifname "$bridge" tcp dport 53 accept));
|
||||
}
|
||||
return $table;
|
||||
}
|
||||
|
||||
@@ -3999,6 +4019,7 @@ my %names = (
|
||||
'mail' => 'profile_mail',
|
||||
'dns' => 'profile_dns',
|
||||
'virtualmin' => 'profile_hosting',
|
||||
'cloudmin' => 'profile_virtualization',
|
||||
'locked' => 'profile_locked',
|
||||
'custom' => 'profile_custom',
|
||||
);
|
||||
|
||||
@@ -520,6 +520,40 @@ ok(scalar(grep { $_ eq '2022' }
|
||||
is(profile_base_table_name('virtualmin'), 'webmin_profile_hosting',
|
||||
'Virtualmin profiles use a Webmin-prefixed table name');
|
||||
|
||||
# The virtualization profile must work through both the setup UI and installer.
|
||||
my %profiles = map { $_->{id} => $_ } setup_profiles();
|
||||
is($profiles{cloudmin}->{name}, 'Cloudmin virtualization host',
|
||||
'Cloudmin profile appears in the setup profile list');
|
||||
is(profile_base_table_name('cloudmin'), 'webmin_profile_virtualization',
|
||||
'Cloudmin follows the existing Webmin table naming convention');
|
||||
my $cloudmin_table = create_profile_ruleset(
|
||||
profile_base_table_name('cloudmin'), 'cloudmin', '*');
|
||||
is_deeply([sort keys %{$cloudmin_table->{chains}}], ['input'],
|
||||
'Cloudmin leaves forwarding and output to other managers');
|
||||
is($cloudmin_table->{chains}->{input}->{policy}, 'drop',
|
||||
'Cloudmin denies unlisted host services');
|
||||
is_deeply($cloudmin_table->{sets}->{profile_virtualization_ports}->{elements},
|
||||
[ '2022', '2200', '2223', '10000' ],
|
||||
'Cloudmin allows configured SSH and Webmin ports only');
|
||||
my $cloudmin_rules = dump_nftables_save($cloudmin_table);
|
||||
like($cloudmin_rules, qr/iifname "virbr\*" udp dport \{ 53, 67, 547 \} accept/,
|
||||
'Cloudmin allows guest DNS and DHCP only on libvirt bridges');
|
||||
like($cloudmin_rules, qr/iifname "virbr\*" tcp dport 53 accept/,
|
||||
'Cloudmin allows guest DNS over TCP');
|
||||
unlike($cloudmin_rules, qr/(?:hook forward|hook postrouting|masquerade|2049)/,
|
||||
'Cloudmin does not generate guest forwarding, NAT or public NFS access');
|
||||
my $cloudmin_custom = create_profile_ruleset('custom_vm_host', 'cloudmin', ['https']);
|
||||
is_deeply($cloudmin_custom->{sets}->{profile_virtualization_tcp_ports}->{elements},
|
||||
[ '443' ], 'Cloudmin honors services selected in the setup UI');
|
||||
{
|
||||
no warnings 'redefine';
|
||||
local *get_nftables_save = sub { ($cloudmin_table) };
|
||||
is(profile_table_name('cloudmin'), 'webmin_profile_virtualization_1',
|
||||
'Cloudmin setup avoids colliding with an existing profile table');
|
||||
}
|
||||
is($profile_table->{chains}->{forward}->{policy}, 'drop',
|
||||
'Virtualmin retains its existing forwarding policy');
|
||||
|
||||
# The saved configuration is the system's own nftables file, so re-writing
|
||||
# it must not discard anything the module does not model
|
||||
my $sysfile = write_ruleset($confdir, 'system.nft', <<'EOF');
|
||||
|
||||
Reference in New Issue
Block a user