my $additional_param_hash = {};
-sub allow_groups {
- return 1;
+sub rule_env {
+ my ($class, $param) = @_;
+
+ die "implement this in subclass";
}
sub additional_parameters {
my $rule = {};
PVE::Firewall::copy_rule_data($rule, $param);
- PVE::Firewall::verify_rule($rule, $class->allow_groups());
+ PVE::Firewall::verify_rule($rule, $class->rule_env());
$rule->{enable} = 0 if !defined($param->{enable});
PVE::Firewall::delete_rule_properties($rule, $param->{'delete'}) if $param->{'delete'};
- PVE::Firewall::verify_rule($rule, $class->allow_groups());
+ PVE::Firewall::verify_rule($rule, $class->rule_env());
}
$class->save_rules($param, $fw_conf, $rules);
__PACKAGE__->additional_parameters({ group => get_standard_option('pve-security-group-name') });
-sub allow_groups {
- return 0;
+sub rule_env {
+ my ($class, $param) = @_;
+
+ return 'group';
}
sub load_config {
use base qw(PVE::API2::Firewall::RulesBase);
+sub rule_env {
+ my ($class, $param) = @_;
+
+ return 'cluster';
+}
+
sub load_config {
my ($class, $param) = @_;
__PACKAGE__->additional_parameters({ node => get_standard_option('pve-node')});
+sub rule_env {
+ my ($class, $param) = @_;
+
+ return 'host';
+}
+
sub load_config {
my ($class, $param) = @_;
vmid => get_standard_option('pve-vmid'),
});
+sub rule_env {
+ my ($class, $param) = @_;
+
+ return 'vm';
+}
+
+sub load_config {
+ my ($class, $param) = @_;
+
+ my $fw_conf = PVE::Firewall::load_vmfw_conf('vm', $param->{vmid});
+ my $rules = $fw_conf->{rules};
+
+ return ($fw_conf, $rules);
+}
+
+sub save_rules {
+ my ($class, $param, $fw_conf, $rules) = @_;
+
+ $fw_conf->{rules} = $rules;
+ PVE::Firewall::save_vmfw_conf($param->{vmid}, $fw_conf);
+}
+
+__PACKAGE__->register_handlers();
+
+package PVE::API2::Firewall::CTRules;
+
+use strict;
+use warnings;
+use PVE::JSONSchema qw(get_standard_option);
+
+use base qw(PVE::API2::Firewall::RulesBase);
+
+__PACKAGE__->additional_parameters({
+ node => get_standard_option('pve-node'),
+ vmid => get_standard_option('pve-vmid'),
+});
+
+sub rule_env {
+ my ($class, $param) = @_;
+
+ return 'ct';
+}
+
sub load_config {
my ($class, $param) = @_;
- my $fw_conf = PVE::Firewall::load_vmfw_conf($param->{vmid});
+ my $fw_conf = PVE::Firewall::load_vmfw_conf('ct', $param->{vmid});
my $rules = $fw_conf->{rules};
return ($fw_conf, $rules);
-package PVE::API2::Firewall::VM;
+package PVE::API2::Firewall::VMBase;
use strict;
use warnings;
use base qw(PVE::RESTHandler);
-__PACKAGE__->register_method ({
- subclass => "PVE::API2::Firewall::VMRules",
- path => 'rules',
-});
-
-__PACKAGE__->register_method ({
- subclass => "PVE::API2::Firewall::VMAliases",
- path => 'aliases',
-});
-
-__PACKAGE__->register_method({
- name => 'index',
- path => '',
- method => 'GET',
- permissions => { user => 'all' },
- description => "Directory index.",
- parameters => {
- additionalProperties => 0,
- properties => {
- node => get_standard_option('pve-node'),
- vmid => get_standard_option('pve-vmid'),
- },
- },
- returns => {
- type => 'array',
- items => {
- type => "object",
- properties => {},
- },
- links => [ { rel => 'child', href => "{name}" } ],
- },
- code => sub {
- my ($param) = @_;
-
- my $result = [
- { name => 'rules' },
- { name => 'options' },
- ];
-
- return $result;
- }});
-
my $option_properties = {
enable => {
description => "Enable host firewall rules.",
return $properties;
};
-__PACKAGE__->register_method({
- name => 'get_options',
- path => 'options',
- method => 'GET',
- description => "Get VM firewall options.",
- proxyto => 'node',
- parameters => {
- additionalProperties => 0,
- properties => {
- node => get_standard_option('pve-node'),
- vmid => get_standard_option('pve-vmid'),
+
+sub register_handlers {
+ my ($class, $rule_env) = @_;
+
+ $class->register_method({
+ name => 'index',
+ path => '',
+ method => 'GET',
+ permissions => { user => 'all' },
+ description => "Directory index.",
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ vmid => get_standard_option('pve-vmid'),
+ },
},
- },
- returns => {
- type => "object",
- #additionalProperties => 1,
- properties => $option_properties,
- },
- code => sub {
- my ($param) = @_;
-
- my $vmfw_conf = PVE::Firewall::load_vmfw_conf($param->{vmid});
-
- return PVE::Firewall::copy_opject_with_digest($vmfw_conf->{options});
- }});
-
-__PACKAGE__->register_method({
- name => 'set_options',
- path => 'options',
- method => 'PUT',
- description => "Set Firewall options.",
- protected => 1,
- proxyto => 'node',
- parameters => {
- additionalProperties => 0,
- properties => &$add_option_properties({
- node => get_standard_option('pve-node'),
- vmid => get_standard_option('pve-vmid'),
- delete => {
- type => 'string', format => 'pve-configid-list',
- description => "A list of settings you want to delete.",
- optional => 1,
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => {},
},
- digest => get_standard_option('pve-config-digest'),
- }),
- },
- returns => { type => "null" },
- code => sub {
- my ($param) = @_;
+ links => [ { rel => 'child', href => "{name}" } ],
+ },
+ code => sub {
+ my ($param) = @_;
- my $vmfw_conf = PVE::Firewall::load_vmfw_conf($param->{vmid});
+ my $result = [
+ { name => 'rules' },
+ { name => 'aliases' },
+ { name => 'options' },
+ ];
- my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($vmfw_conf->{options});
- PVE::Tools::assert_if_modified($digest, $param->{digest});
+ return $result;
+ }});
- if ($param->{delete}) {
- foreach my $opt (PVE::Tools::split_list($param->{delete})) {
- raise_param_exc({ delete => "no such option '$opt'" })
- if !$option_properties->{$opt};
- delete $vmfw_conf->{options}->{$opt};
- }
- }
-
- if (defined($param->{enable})) {
- $param->{enable} = $param->{enable} ? 1 : 0;
- }
-
- foreach my $k (keys %$option_properties) {
- next if !defined($param->{$k});
- $vmfw_conf->{options}->{$k} = $param->{$k};
- }
-
- PVE::Firewall::save_vmfw_conf($param->{vmid}, $vmfw_conf);
-
- return undef;
- }});
-
-__PACKAGE__->register_method({
- name => 'log',
- path => 'log',
- method => 'GET',
- description => "Read firewall log",
- proxyto => 'node',
- permissions => {
- check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
- },
- protected => 1,
- parameters => {
- additionalProperties => 0,
- properties => {
- node => get_standard_option('pve-node'),
- vmid => get_standard_option('pve-vmid'),
- start => {
- type => 'integer',
- minimum => 0,
- optional => 1,
- },
- limit => {
- type => 'integer',
- minimum => 0,
- optional => 1,
+
+ $class->register_method({
+ name => 'get_options',
+ path => 'options',
+ method => 'GET',
+ description => "Get VM firewall options.",
+ proxyto => 'node',
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ vmid => get_standard_option('pve-vmid'),
},
},
- },
- returns => {
- type => 'array',
- items => {
+ returns => {
type => "object",
- properties => {
- n => {
- description=> "Line number",
- type=> 'integer',
+ #additionalProperties => 1,
+ properties => $option_properties,
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $vmfw_conf = PVE::Firewall::load_vmfw_conf($rule_env, $param->{vmid});
+
+ return PVE::Firewall::copy_opject_with_digest($vmfw_conf->{options});
+ }});
+
+ $class->register_method({
+ name => 'set_options',
+ path => 'options',
+ method => 'PUT',
+ description => "Set Firewall options.",
+ protected => 1,
+ proxyto => 'node',
+ parameters => {
+ additionalProperties => 0,
+ properties => &$add_option_properties({
+ node => get_standard_option('pve-node'),
+ vmid => get_standard_option('pve-vmid'),
+ delete => {
+ type => 'string', format => 'pve-configid-list',
+ description => "A list of settings you want to delete.",
+ optional => 1,
},
- t => {
- description=> "Line text",
- type => 'string',
+ digest => get_standard_option('pve-config-digest'),
+ }),
+ },
+ returns => { type => "null" },
+ code => sub {
+ my ($param) = @_;
+
+ my $vmfw_conf = PVE::Firewall::load_vmfw_conf($rule_env, $param->{vmid});
+
+ my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($vmfw_conf->{options});
+ PVE::Tools::assert_if_modified($digest, $param->{digest});
+
+ if ($param->{delete}) {
+ foreach my $opt (PVE::Tools::split_list($param->{delete})) {
+ raise_param_exc({ delete => "no such option '$opt'" })
+ if !$option_properties->{$opt};
+ delete $vmfw_conf->{options}->{$opt};
}
}
- }
- },
- code => sub {
- my ($param) = @_;
- my $rpcenv = PVE::RPCEnvironment::get();
- my $user = $rpcenv->get_user();
- my $vmid = $param->{vmid};
+ if (defined($param->{enable})) {
+ $param->{enable} = $param->{enable} ? 1 : 0;
+ }
+
+ foreach my $k (keys %$option_properties) {
+ next if !defined($param->{$k});
+ $vmfw_conf->{options}->{$k} = $param->{$k};
+ }
+
+ PVE::Firewall::save_vmfw_conf($param->{vmid}, $vmfw_conf);
+
+ return undef;
+ }});
- my ($count, $lines) = PVE::Tools::dump_logfile("/var/log/pve-firewall.log",
- $param->{start}, $param->{limit},
- "^$vmid ");
+ $class->register_method({
+ name => 'log',
+ path => 'log',
+ method => 'GET',
+ description => "Read firewall log",
+ proxyto => 'node',
+ permissions => {
+ check => ['perm', '/vms/{vmid}', [ 'VM.Console' ]],
+ },
+ protected => 1,
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ node => get_standard_option('pve-node'),
+ vmid => get_standard_option('pve-vmid'),
+ start => {
+ type => 'integer',
+ minimum => 0,
+ optional => 1,
+ },
+ limit => {
+ type => 'integer',
+ minimum => 0,
+ optional => 1,
+ },
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => {
+ n => {
+ description=> "Line number",
+ type=> 'integer',
+ },
+ t => {
+ description=> "Line text",
+ type => 'string',
+ }
+ }
+ }
+ },
+ code => sub {
+ my ($param) = @_;
- $rpcenv->set_result_attrib('total', $count);
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $user = $rpcenv->get_user();
+ my $vmid = $param->{vmid};
+
+ my ($count, $lines) = PVE::Tools::dump_logfile("/var/log/pve-firewall.log",
+ $param->{start}, $param->{limit},
+ "^$vmid ");
+
+ $rpcenv->set_result_attrib('total', $count);
- return $lines;
- }});
+ return $lines;
+ }});
+}
+
+package PVE::API2::Firewall::VM;
+
+use strict;
+use warnings;
+
+use base qw(PVE::API2::Firewall::VMBase);
+
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Firewall::VMRules",
+ path => 'rules',
+});
+
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Firewall::VMAliases",
+ path => 'aliases',
+});
+
+__PACKAGE__->register_handlers('vm');
+
+package PVE::API2::Firewall::CT;
+
+use strict;
+use warnings;
+
+use base qw(PVE::API2::Firewall::VMBase);
+
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Firewall::CTRules",
+ path => 'rules',
+});
+
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Firewall::CTAliases",
+ path => 'aliases',
+});
+
+__PACKAGE__->register_handlers('vm');
1;
return $rules;
};
+my $rule_env_iface_lookup = {
+ 'ct' => 1,
+ 'vm' => 1,
+ 'group' => 0,
+ 'cluster' => 1,
+ 'host' => 1,
+};
+
sub verify_rule {
- my ($rule, $allow_groups, $noerr) = @_;
+ my ($rule, $rule_env, $noerr) = @_;
+
+ my $allow_groups = $rule_env eq 'group' ? 0 : 1;
+
+ my $allow_iface = $rule_env_iface_lookup->{$rule_env};
+ die "unknown rule_env '$rule_env'\n" if !defined($allow_iface); # should not happen
my $errors = {};
my $error_count = 0;
}
if ($rule->{iface}) {
+ &$add_error('type', "parameter -i not allowed for this rule type")
+ if !$allow_iface;
eval { PVE::JSONSchema::pve_verify_iface($rule->{iface}); };
&$add_error('iface', $@) if $@;
- }
+ if ($rule_env eq 'vm') {
+ &$add_error('iface', "value does not match the regex pattern 'net\\d+'")
+ if $rule->{iface} !~ m/^net(\d+)$/;
+ } elsif ($rule_env eq 'ct') {
+ &$add_error('iface', "value does not match the regex pattern '(venet|eth\\d+)'")
+ if $rule->{iface} !~ m/^(venet|eth(\d+))$/;
+ }
+ }
if ($rule->{macro}) {
if (my $preferred_name = $pve_fw_preferred_macro_names->{lc($rule->{macro})}) {
}
sub parse_fw_rule {
- my ($prefix, $line, $allow_iface, $allow_groups, $verbose) = @_;
+ my ($prefix, $line, $rule_env, $verbose) = @_;
chomp $line;
while (length($line)) {
if ($line =~ s/^-i (\S+)\s*//) {
- die "parameter -i not allowed\n" if !$allow_iface;
$rule->{iface} = $1;
next;
}
die "unable to parse rule parameters: $line\n" if length($line);
- $rule = verify_rule($rule, $allow_groups, 1);
+ $rule = verify_rule($rule, $rule_env, 1);
if ($verbose && $rule->{errors}) {
warn "$prefix - errors in rule parameters: $orig_line\n";
foreach my $p (keys %{$rule->{errors}}) {
}
sub parse_vm_fw_rules {
- my ($filename, $fh, $verbose) = @_;
+ my ($filename, $fh, $rule_env, $verbose) = @_;
my $res = {
rules => [],
}
my $rule;
- eval { $rule = parse_fw_rule($prefix, $line, 1, 1, $verbose); };
+ eval { $rule = parse_fw_rule($prefix, $line, $rule_env, $verbose); };
if (my $err = $@) {
warn "$prefix: $err";
next;
}
my $rule;
- eval { $rule = parse_fw_rule($prefix, $line, 1, 1, $verbose); };
+ eval { $rule = parse_fw_rule($prefix, $line, 'host', $verbose); };
if (my $err = $@) {
warn "$prefix: $err";
next;
warn "$prefix: $@" if $@;
} elsif ($section eq 'rules') {
my $rule;
- eval { $rule = parse_fw_rule($prefix, $line, 1, 1, $verbose); };
+ eval { $rule = parse_fw_rule($prefix, $line, 'cluster', $verbose); };
if (my $err = $@) {
warn "$prefix: $err";
next;
push @{$res->{$section}}, $rule;
} elsif ($section eq 'groups') {
my $rule;
- eval { $rule = parse_fw_rule($prefix, $line, 0, 0, $verbose); };
+ eval { $rule = parse_fw_rule($prefix, $line, 'group', $verbose); };
if (my $err = $@) {
warn "$prefix: $err";
next;
};
sub load_vmfw_conf {
- my ($vmid, $dir, $verbose) = @_;
+ my ($rule_env, $vmid, $dir, $verbose) = @_;
my $vmfw_conf = {};
my $filename = "$dir/$vmid.fw";
if (my $fh = IO::File->new($filename, O_RDONLY)) {
- $vmfw_conf = parse_vm_fw_rules($filename, $fh, $verbose);
+ $vmfw_conf = parse_vm_fw_rules($filename, $fh, $rule_env, $verbose);
}
return $vmfw_conf;
my $vmfw_configs = {};
- foreach my $vmid (keys %{$vmdata->{qemu}}, keys %{$vmdata->{openvz}}) {
- my $vmfw_conf = load_vmfw_conf($vmid, $dir, $verbose);
+ foreach my $vmid (keys %{$vmdata->{qemu}}) {
+ my $vmfw_conf = load_vmfw_conf('vm', $vmid, $dir, $verbose);
+ next if !$vmfw_conf->{options}; # skip if file does not exists
+ $vmfw_configs->{$vmid} = $vmfw_conf;
+ }
+ foreach my $vmid (keys %{$vmdata->{openvz}}) {
+ my $vmfw_conf = load_vmfw_conf('ct', $vmid, $dir, $verbose);
next if !$vmfw_conf->{options}; # skip if file does not exists
$vmfw_configs->{$vmid} = $vmfw_conf;
}