]> git.proxmox.com Git - pve-firewall.git/blobdiff - src/PVE/API2/Firewall/Cluster.pm
api: lock configs
[pve-firewall.git] / src / PVE / API2 / Firewall / Cluster.pm
index 028600cf4174b62e737df8655c6e9c92115aa9ce..c9c3e67a16c5212b1a4984c2081e4adf7fc07198 100644 (file)
@@ -6,25 +6,37 @@ use PVE::Exception qw(raise raise_param_exc raise_perm_exc);
 use PVE::JSONSchema qw(get_standard_option);
 
 use PVE::Firewall;
+use PVE::API2::Firewall::Aliases;
 use PVE::API2::Firewall::Rules;
 use PVE::API2::Firewall::Groups;
+use PVE::API2::Firewall::IPSet;
 
 #fixme: locking?
 
-use Data::Dumper; # fixme: remove
 
 use base qw(PVE::RESTHandler);
 
 __PACKAGE__->register_method ({
-    subclass => "PVE::API2::Firewall::Groups",  
+    subclass => "PVE::API2::Firewall::Groups",
     path => 'groups',
 });
 
 __PACKAGE__->register_method ({
-    subclass => "PVE::API2::Firewall::ClusterRules",  
+    subclass => "PVE::API2::Firewall::ClusterRules",
     path => 'rules',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Firewall::ClusterIPSetList",
+    path => 'ipset',
+});
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Firewall::ClusterAliases",
+    path => 'aliases',
+});
+
+
 __PACKAGE__->register_method({
     name => 'index',
     path => '',
@@ -46,60 +58,55 @@ __PACKAGE__->register_method({
        my ($param) = @_;
 
        my $result = [
+           { name => 'aliases' },
            { name => 'rules' },
            { name => 'options' },
            { name => 'groups' },
-           { name => 'netgroups' },
+           { name => 'ipset' },
            { name => 'macros' },
+           { name => 'refs' },
            ];
 
        return $result;
     }});
 
+my $option_properties = $PVE::Firewall::cluster_option_properties;
+
+my $add_option_properties = sub {
+    my ($properties) = @_;
+
+    foreach my $k (keys %$option_properties) {
+       $properties->{$k} = $option_properties->{$k};
+    }
+
+    return $properties;
+};
+
+
 __PACKAGE__->register_method({
     name => 'get_options',
     path => 'options',
     method => 'GET',
     description => "Get Firewall options.",
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit' ]],
+    },
     parameters => {
        additionalProperties => 0,
     },
     returns => {
        type => "object",
        #additionalProperties => 1,
-       properties => {
-           enable => {
-               type => 'boolean',
-               optional => 1,
-           },
-       },
+       properties => $option_properties,
     },
     code => sub {
        my ($param) = @_;
 
        my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
 
-       my $options = $cluster_conf->{options};
-
-       return $options;
+       return PVE::Firewall::copy_opject_with_digest($cluster_conf->{options});
     }});
 
-my $option_properties = {
-    enable => {
-       type => 'boolean',
-       optional => 1,
-    },
-};
-
-my $add_option_properties = sub {
-    my ($properties) = @_;
-
-    foreach my $k (keys %$option_properties) {
-       $properties->{$k} = $option_properties->{$k};
-    }
-    
-    return $properties;
-};
 
 __PACKAGE__->register_method({
     name => 'set_options',
@@ -107,6 +114,9 @@ __PACKAGE__->register_method({
     method => 'PUT',
     description => "Set Firewall options.",
     protected => 1,
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Modify' ]],
+    },
     parameters => {
        additionalProperties => 0,
        properties => &$add_option_properties({
@@ -115,27 +125,44 @@ __PACKAGE__->register_method({
                description => "A list of settings you want to delete.",
                optional => 1,
            },
+           digest => get_standard_option('pve-config-digest'),
        }),
     },
     returns => { type => "null" },
     code => sub {
        my ($param) = @_;
 
-       my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+       PVE::Firewall::lock_clusterfw_conf(10, sub {
+           my $cluster_conf = PVE::Firewall::load_clusterfw_conf();
+
+           my (undef, $digest) = PVE::Firewall::copy_opject_with_digest($cluster_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 $cluster_conf->{options}->{$opt};
+           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 $cluster_conf->{options}->{$opt};
+               }
            }
-       }
 
-       if (defined($param->{enable})) {
-           $cluster_conf->{options}->{enable} = $param->{enable} ? 1 : 0;
-       }
+           if (defined($param->{enable}) && ($param->{enable} > 1)) {
+               $param->{enable} = time();
+           }
+
+           foreach my $k (keys %$option_properties) {
+               next if !defined($param->{$k});
+               $cluster_conf->{options}->{$k} = $param->{$k};
+           }
 
-       PVE::Firewall::save_clusterfw_conf($cluster_conf);
+           PVE::Firewall::save_clusterfw_conf($cluster_conf);
+       });
+
+       # instant firewall update when using double (anti-lockout) API call
+       # -> not waiting for a firewall update at the first (timestamp enable) set
+       if (defined($param->{enable}) && ($param->{enable} > 1)) {
+           PVE::Firewall::update();
+       }
 
        return undef;
     }});
@@ -145,6 +172,7 @@ __PACKAGE__->register_method({
     path => 'macros',
     method => 'GET',
     description => "List available macros",
+    permissions => { user => 'all' },
     parameters => {
        additionalProperties => 0,
     },
@@ -178,4 +206,82 @@ __PACKAGE__->register_method({
        return $res;
     }});
 
+__PACKAGE__->register_method({
+    name => 'refs',
+    path => 'refs',
+    method => 'GET',
+    description => "Lists possible IPSet/Alias reference which are allowed in source/dest properties.",
+    permissions => {
+       check => ['perm', '/', [ 'Sys.Audit' ]],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           type => {
+               description => "Only list references of specified type.",
+               type => 'string',
+               enum => ['alias', 'ipset'],
+               optional => 1,
+           },
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => {
+               type => {
+                   type => 'string',
+                   enum => ['alias', 'ipset'],
+               },
+               name => {
+                   type => 'string',
+               },
+               ref => {
+                   type => 'string',
+               },
+               comment => {
+                   type => 'string',
+                   optional => 1,
+               },
+           },
+       },
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $conf = PVE::Firewall::load_clusterfw_conf();
+
+       my $res = [];
+
+       if (!$param->{type} || $param->{type} eq 'ipset') {
+           foreach my $name (keys %{$conf->{ipset}}) {
+               my $data = {
+                   type => 'ipset',
+                   name => $name,
+                   ref => "+$name",
+               };
+               if (my $comment = $conf->{ipset_comments}->{$name}) {
+                   $data->{comment} = $comment;
+               }
+               push @$res, $data;
+           }
+       }
+
+       if (!$param->{type} || $param->{type} eq 'alias') {
+           foreach my $name (keys %{$conf->{aliases}}) {
+               my $e = $conf->{aliases}->{$name};
+               my $data = {
+                   type => 'alias',
+                   name => $name,
+                   ref => $name,
+               };
+               $data->{comment} = $e->{comment} if $e->{comment};
+               push @$res, $data;
+           }
+       }
+
+       return $res;
+    }});
+
 1;