]> git.proxmox.com Git - pve-firewall.git/blobdiff - src/PVE/Firewall.pm
start API for aliases
[pve-firewall.git] / src / PVE / Firewall.pm
index 0c83e2fccf1848889ea844c9cf95327b7b1a9ccc..8d25326a82b2f61914da94056711191fe7d49ab4 100644 (file)
@@ -56,6 +56,14 @@ PVE::JSONSchema::register_standard_option('ipset-name', {
     maxLength => 20,                     
 });
 
+PVE::JSONSchema::register_standard_option('pve-fw-alias', {
+    description => "Alias name.",
+    type => 'string',
+    pattern => '[A-Za-z][A-Za-z0-9\-\_]+',
+    minLength => 2,
+    maxLength => 20,                     
+});
+
 PVE::JSONSchema::register_standard_option('pve-fw-loglevel' => {
     description => "Log level.",
     type => 'string', 
@@ -1214,9 +1222,10 @@ sub ruleset_generate_cmdstr {
                die "invalid security group name '$source'\n";
            }
        } elsif ($source =~ m/^${ip_alias_pattern}$/){
-           die "no such alias $source\n" if !$cluster_conf->{aliases}->{$source};
-           push @cmd, "-s $cluster_conf->{aliases}->{$source}";
-
+           my $alias = lc($source);
+           my $e = $cluster_conf->{aliases}->{$alias};
+           die "no such alias $source\n" if !$e;
+           push @cmd, "-s $e->{cidr}";
         } elsif ($source =~ m/\-/){
            push @cmd, "-m iprange --src-range $source";
 
@@ -1234,9 +1243,10 @@ sub ruleset_generate_cmdstr {
                die "invalid security group name '$dest'\n";
            }
        } elsif ($dest =~ m/^${ip_alias_pattern}$/){
-           die "no such alias $dest" if !$cluster_conf->{aliases}->{$dest};
-           push @cmd, "-d $cluster_conf->{aliases}->{$dest}";
-
+           my $alias = lc($source);
+           my $e = $cluster_conf->{aliases}->{$alias};
+           die "no such alias $dest" if !$e;
+           push @cmd, "-d $e->{cidr}";
         } elsif ($dest =~ m/^(\d+)\.(\d+).(\d+).(\d+)\-(\d+)\.(\d+).(\d+).(\d+)$/){
            push @cmd, "-m iprange --dst-range $dest";
 
@@ -1309,11 +1319,18 @@ sub ruleset_generate_rule {
        $rules = [ $rule ];
     }
 
+    # update all or nothing
+
+    my @cmds = ();
     foreach my $tmp (@$rules) {
        if (my $cmdstr = ruleset_generate_cmdstr($ruleset, $chain, $tmp, $actions, $goto, $cluster_conf)) {
-           ruleset_addrule($ruleset, $chain, $cmdstr);
+           push @cmds, $cmdstr;
        }
     }
+
+    foreach my $cmdstr (@cmds) {
+       ruleset_addrule($ruleset, $chain, $cmdstr);
+    }
 }
 
 sub ruleset_generate_rule_insert {
@@ -1498,6 +1515,8 @@ sub ruleset_generate_vm_rules {
 
     my $lc_direction = lc($direction);
 
+    my $in_accept = generate_nfqueue($options);
+
     foreach my $rule (@$rules) {
        next if $rule->{iface} && $rule->{iface} ne $netid;
        next if !$rule->{enable};
@@ -1516,13 +1535,18 @@ sub ruleset_generate_vm_rules {
 
        } else {
            next if $rule->{type} ne $lc_direction;
-           if ($direction eq 'OUT') {
-               ruleset_generate_rule($ruleset, $chain, $rule,
-                                     { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, undef, $cluster_conf);
-           } else {
-               my $accept = generate_nfqueue($options);
-               ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => $accept , REJECT => "PVEFW-reject" }, undef, $cluster_conf);
-           }
+           eval {
+               if ($direction eq 'OUT') {
+                   ruleset_generate_rule($ruleset, $chain, $rule,
+                                         { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, 
+                                         undef, $cluster_conf);
+               } else {
+                   ruleset_generate_rule($ruleset, $chain, $rule, 
+                                         { ACCEPT => $in_accept , REJECT => "PVEFW-reject" }, 
+                                         undef, $cluster_conf);
+               }
+           };
+           warn $@ if $@;
        }
     }
 }
@@ -1530,22 +1554,20 @@ sub ruleset_generate_vm_rules {
 sub generate_nfqueue {
     my ($options) = @_;
 
-    my $action = "";
-    if($options->{ips}){
-       $action = "NFQUEUE";
-       if($options->{ips_queues} && $options->{ips_queues} =~ m/^(\d+)(:(\d+))?$/) {
-           if(defined($3) && defined($1)) {
+    if ($options->{ips}) {
+       my $action = "NFQUEUE";
+       if ($options->{ips_queues} && $options->{ips_queues} =~ m/^(\d+)(:(\d+))?$/) {
+           if (defined($3) && defined($1)) {
                $action .= " --queue-balance $1:$3";
-           }elsif (defined($1)) {
+           } elsif (defined($1)) {
                $action .= " --queue-num $1";
            }
        }
        $action .= " --queue-bypass" if $feature_ipset_nomatch; #need kernel 3.10
-    }else{
-       $action = "ACCEPT";
+       return $action;
+    } else {
+       return "ACCEPT";
     }
-
-    return $action;
 }
 
 sub ruleset_generate_vm_ipsrules {
@@ -1930,20 +1952,22 @@ sub parse_clusterfw_option {
 sub parse_clusterfw_alias {
     my ($line) = @_;
 
-    my ($opt, $value);
+    # we can add single line comments to the end of the line
+    my $comment = decode('utf8', $1) if $line =~ s/\s*#\s*(.*?)\s*$//;
+
     if ($line =~ m/^(\S+)\s(\S+)$/) {
-       $opt = lc($1);
-       if($2){
-           $2 =~ s|/32$||;
-           pve_verify_ipv4_or_cidr($2) if $2;
-           $value = $2;
-       }
-    } else {
-       chomp $line;
-       die "can't parse option '$line'\n";
+       my ($name, $cidr) = ($1, $2);
+       $cidr =~ s|/32$||;
+       pve_verify_ipv4_or_cidr($cidr);
+       my $data = {
+           name => $name,
+           cidr => $cidr,
+       };
+       $data->{comment} = $comment  if $comment;
+       return $data;
     }
 
-    return ($opt, $value);
+    return undef;
 }
 
 sub parse_vm_fw_rules {
@@ -2051,6 +2075,7 @@ sub parse_cluster_fw_rules {
     my $res = { 
        rules => [], 
        options => {}, 
+       aliases => {}, 
        groups => {}, 
        group_comments => {}, 
        ipset => {} ,
@@ -2112,8 +2137,8 @@ sub parse_cluster_fw_rules {
            warn "$prefix: $@" if $@;
        } elsif ($section eq 'aliases') {
            eval {
-               my ($opt, $value) = parse_clusterfw_alias($line);
-               $res->{aliases}->{$opt} = $value;
+               my $data = parse_clusterfw_alias($line);
+               $res->{aliases}->{lc($data->{name})} = $data;
            };
            warn "$prefix: $@" if $@;
        } elsif ($section eq 'rules') {
@@ -2287,6 +2312,24 @@ my $format_options = sub {
     return $raw;
 };
 
+my $format_aliases = sub {
+    my ($aliases) = @_;
+
+    my $raw = '';
+
+    $raw .= "[ALIASES]\n\n";
+    foreach my $k (keys %$aliases) {
+       my $e = $aliases->{$k};
+       $raw .= "$e->{name} $e->{cidr}";
+       $raw .= " # " . encode('utf8', $e->{comment})
+           if $e->{comment} && $e->{comment} !~ m/^\s*$/;
+       $raw .= "\n";
+    }
+    $raw .= "\n";
+
+    return $raw;
+};
+
 my $format_ipset = sub {
     my ($options) = @_;
 
@@ -2415,9 +2458,13 @@ sub generate_ipset {
     my $nethash = {};
     foreach my $entry (@$options) {
        my $cidr = $entry->{cidr};
-       if ($cidr =~ m/^${ip_alias_pattern}$/){
-           die "no such alias $cidr" if !$aliases->{$cidr};
-           $entry->{cidr} = $aliases->{$cidr};
+       if ($cidr =~ m/^${ip_alias_pattern}$/) {
+           my $alias = lc($cidr);
+           if ($aliases->{$alias}) {
+               $entry->{cidr} = $aliases->{$alias}->{cidr};
+           } else {
+               warn "no such alias '$cidr'\n" if !$aliases->{$alias};
+           }
        }
        $nethash->{$entry->{cidr}} = $entry;
     }
@@ -2516,6 +2563,9 @@ sub save_clusterfw_conf {
     my $options = $cluster_conf->{options};
     $raw .= &$format_options($options) if scalar(keys %$options);
 
+    my $aliases = $cluster_conf->{aliases};
+    $raw .= &$format_aliases($aliases) if scalar(keys %$aliases);
     foreach my $ipset (sort keys %{$cluster_conf->{ipset}}) {
        if (my $comment = $cluster_conf->{ipset_comments}->{$ipset}) {
            my $utf8comment = encode('utf8', $comment);
@@ -2863,10 +2913,6 @@ sub apply_ruleset {
 
     enable_bridge_firewall();
 
-    update_nf_conntrack_max($hostfw_conf);
-
-    update_nf_conntrack_tcp_timeout_established($hostfw_conf);
-
     my ($ipset_create_cmdlist, $ipset_delete_cmdlist, $ipset_changes) =
        get_ipset_cmdlist($ipset_ruleset, undef, $verbose);
 
@@ -2905,6 +2951,11 @@ sub apply_ruleset {
     }
 
     die "unable to apply firewall changes\n" if $errors;
+
+    update_nf_conntrack_max($hostfw_conf);
+
+    update_nf_conntrack_tcp_timeout_established($hostfw_conf);
+
 }
 
 sub update_nf_conntrack_max {