]> git.proxmox.com Git - pve-firewall.git/blobdiff - src/PVE/Firewall.pm
generate /etc/pve/firewall directory automatically
[pve-firewall.git] / src / PVE / Firewall.pm
index b2bcc32e5ca685cfafd84cc05019094c3ee6a925..e8c05ebfbfcb51de02eafc929f95939774cc2a1f 100644 (file)
@@ -19,7 +19,8 @@ use PVE::Tools qw(run_command lock_file dir_glob_foreach);
 use Encode;
 
 my $hostfw_conf_filename = "/etc/pve/local/host.fw";
-my $clusterfw_conf_filename = "/etc/pve/firewall/cluster.fw";
+my $pvefw_conf_dir = "/etc/pve/firewall";
+my $clusterfw_conf_filename = "$pvefw_conf_dir/cluster.fw";
 
 # dynamically include PVE::QemuServer and PVE::OpenVZ
 # to avoid dependency problems
@@ -1971,8 +1972,6 @@ for (my $i = 0; $i < $MAX_NETS; $i++)  {
 sub parse_fw_rule {
     my ($prefix, $line, $cluster_conf, $fw_conf, $rule_env, $verbose) = @_;
 
-    chomp $line;
-
     my $orig_line = $line;
 
     my $rule = {};
@@ -2066,7 +2065,6 @@ sub parse_vmfw_option {
        $opt = lc($1);
        $value = $2;
     } else {
-       chomp $line;
        die "can't parse option '$line'\n"
     }
 
@@ -2090,7 +2088,6 @@ sub parse_hostfw_option {
        $opt = lc($1);
        $value = int($2);
     } else {
-       chomp $line;
        die "can't parse option '$line'\n"
     }
 
@@ -2109,7 +2106,6 @@ sub parse_clusterfw_option {
        $opt = lc($1);
        $value = uc($3);
     } else {
-       chomp $line;
        die "can't parse option '$line'\n"
     }
 
@@ -2164,6 +2160,8 @@ sub generic_fw_config_parser {
        next if $line =~ m/^#/;
        next if $line =~ m/^\s*$/;
 
+       chomp $line;
+
        my $linenr = $fh->input_line_number();
        my $prefix = "$filename (line $linenr)";
 
@@ -2284,6 +2282,7 @@ sub generic_fw_config_parser {
                }
            };
            if (my $err = $@) {
+               chomp $err;
                $errors->{cidr} = $err;
            }
 
@@ -2292,6 +2291,13 @@ sub generic_fw_config_parser {
            $entry->{comment} = $comment if $comment;
            $entry->{errors} =  $errors if $errors;
 
+           if ($verbose && $errors) {
+               warn "$prefix - errors in ipset '$group': $line\n";
+               foreach my $p (keys %{$errors}) {
+                   warn "  $p: $errors->{$p}\n";
+               }
+           }
+
            push @{$res->{$section}->{$group}}, $entry;
        } else {
            warn "$prefix: skip line - unknown section\n";
@@ -2396,7 +2402,7 @@ sub load_vmfw_conf {
 
     my $vmfw_conf = {};
 
-    $dir = "/etc/pve/firewall" if !defined($dir);
+    $dir = $pvefw_conf_dir if !defined($dir);
 
     my $filename = "$dir/$vmid.fw";
     if (my $fh = IO::File->new($filename, O_RDONLY)) {
@@ -2516,21 +2522,23 @@ sub save_vmfw_conf {
     my $raw = '';
 
     my $options = $vmfw_conf->{options};
-    $raw .= &$format_options($options) if scalar(keys %$options);
+    $raw .= &$format_options($options) if $options && scalar(keys %$options);
 
     my $aliases = $vmfw_conf->{aliases};
-    $raw .= &$format_aliases($aliases) if scalar(keys %$aliases);
+    $raw .= &$format_aliases($aliases) if $aliases && scalar(keys %$aliases);
 
-    $raw .= &$format_ipsets($vmfw_conf);
+    $raw .= &$format_ipsets($vmfw_conf) if $vmfw_conf->{ipset};
 
     my $rules = $vmfw_conf->{rules} || [];
-    if (scalar(@$rules)) {
+    if ($rules && scalar(@$rules)) {
        $raw .= "[RULES]\n\n";
        $raw .= &$format_rules($rules, 1);
        $raw .= "\n";
     }
 
-    my $filename = "/etc/pve/firewall/$vmid.fw";
+    mkdir $pvefw_conf_dir;
+
+    my $filename = "$pvefw_conf_dir/$vmid.fw";
     PVE::Tools::file_set_contents($filename, $raw);
 }
 
@@ -2629,6 +2637,7 @@ sub generate_ipset {
     # remove duplicates
     my $nethash = {};
     foreach my $entry (@$options) {
+       next if $entry->{errors}; # skip entries with errors
        eval {
            my $cidr = resolve_alias($clusterfw_conf, $fw_conf, $entry->{cidr});
            $nethash->{$cidr} = { cidr => $cidr, nomatch => $entry->{nomatch} };
@@ -2679,33 +2688,36 @@ sub save_clusterfw_conf {
     my $raw = '';
 
     my $options = $cluster_conf->{options};
-    $raw .= &$format_options($options) if scalar(keys %$options);
+    $raw .= &$format_options($options) if $options && scalar(keys %$options);
 
     my $aliases = $cluster_conf->{aliases};
-    $raw .= &$format_aliases($aliases) if scalar(keys %$aliases);
+    $raw .= &$format_aliases($aliases) if $aliases && scalar(keys %$aliases);
 
-    $raw .= &$format_ipsets($cluster_conf);
+    $raw .= &$format_ipsets($cluster_conf) if $cluster_conf->{ipset};
  
     my $rules = $cluster_conf->{rules};
-    if (scalar(@$rules)) {
+    if ($rules && scalar(@$rules)) {
        $raw .= "[RULES]\n\n";
        $raw .= &$format_rules($rules, 1);
        $raw .= "\n";
     }
 
-    foreach my $group (sort keys %{$cluster_conf->{groups}}) {
-       my $rules = $cluster_conf->{groups}->{$group};
-       if (my $comment = $cluster_conf->{group_comments}->{$group}) {
-           my $utf8comment = encode('utf8', $comment);
-           $raw .= "[group $group] # $utf8comment\n\n";
-       } else {
-           $raw .= "[group $group]\n\n";
-       }
+    if ($cluster_conf->{groups}) {
+       foreach my $group (sort keys %{$cluster_conf->{groups}}) {
+           my $rules = $cluster_conf->{groups}->{$group};
+           if (my $comment = $cluster_conf->{group_comments}->{$group}) {
+               my $utf8comment = encode('utf8', $comment);
+               $raw .= "[group $group] # $utf8comment\n\n";
+           } else {
+               $raw .= "[group $group]\n\n";
+           }
 
-       $raw .= &$format_rules($rules, 0);
-       $raw .= "\n";
+           $raw .= &$format_rules($rules, 0);
+           $raw .= "\n";
+       }
     }
 
+    mkdir $pvefw_conf_dir;
     PVE::Tools::file_set_contents($clusterfw_conf_filename, $raw);
 }
 
@@ -2727,10 +2739,10 @@ sub save_hostfw_conf {
     my $raw = '';
 
     my $options = $hostfw_conf->{options};
-    $raw .= &$format_options($options) if scalar(keys %$options);
+    $raw .= &$format_options($options) if $options && scalar(keys %$options);
 
     my $rules = $hostfw_conf->{rules};
-    if (scalar(@$rules)) {
+    if ($rules && scalar(@$rules)) {
        $raw .= "[RULES]\n\n";
        $raw .= &$format_rules($rules, 1);
        $raw .= "\n";
@@ -2775,6 +2787,8 @@ sub compile {
 
     push @{$cluster_conf->{ipset}->{management}}, { cidr => $localnet };
 
+    return ({}, {}) if !$cluster_conf->{options}->{enable};
+
     my $ruleset = {};
 
     ruleset_create_chain($ruleset, "PVEFW-INPUT");
@@ -2963,8 +2977,9 @@ sub get_ruleset_cmdlist {
     }
 
     foreach my $h (qw(INPUT OUTPUT FORWARD)) {
-       if (!$hooks->{$h}) {
-           $cmdlist .= "-A $h -j PVEFW-$h\n";
+       my $chain = "PVEFW-$h";
+       if ($ruleset->{$chain} && !$hooks->{$h}) {
+           $cmdlist .= "-A $h -j $chain\n";
        }
     }
 
@@ -3164,6 +3179,17 @@ sub remove_pvefw_chains {
     $cmdlist .= "COMMIT\n";
 
     iptables_restore_cmdlist($cmdlist);
+
+    my $ipset_chains = ipset_get_chains();
+
+    $cmdlist = "";
+    foreach my $chain (keys %$ipset_chains) {
+       $cmdlist .= "flush $chain\n";
+       $cmdlist .= "destroy $chain\n";
+    }
+
+    ipset_restore_cmdlist($cmdlist) if $cmdlist; 
 }
 
 sub init {
@@ -3182,11 +3208,7 @@ sub update {
        my $cluster_conf = load_clusterfw_conf();
        my $cluster_options = $cluster_conf->{options};
 
-       my $enable = $cluster_options->{enable};
-
-       die "Firewall is disabled - cannot start\n" if !$enable;
-
-       if (!$enable) {
+       if (!$cluster_options->{enable}) {
            PVE::Firewall::remove_pvefw_chains();
            return;
        }