use File::Path;
use IO::File;
use Net::IP;
-use PVE::Tools qw(run_command);
+use PVE::Tools qw(run_command lock_file);
use Data::Dumper;
+my $pve_fw_lock_filename = "/var/lock/pvefw.lck";
+
my $macros;
my @ruleset = ();
-sub get_shorewall_macros {
+# todo: implement some kind of MACROS, like shorewall /usr/share/shorewall/macro.*
+sub get_firewall_macros {
return $macros if $macros;
- foreach my $path (</usr/share/shorewall/macro.*>) {
- if ($path =~ m|/macro\.(\S+)$|) {
- $macros->{$1} = 1;
- }
- }
+ #foreach my $path (</usr/share/shorewall/macro.*>) {
+ # if ($path =~ m|/macro\.(\S+)$|) {
+ # $macros->{$1} = 1;
+ # }
+ #}
+
+ $macros = {}; # fixme: implemet me
+
return $macros;
}
sub parse_address_list {
my ($str) = @_;
+ my $nbaor = 0;
foreach my $aor (split(/,/, $str)) {
if (!Net::IP->new($aor)) {
my $err = Net::IP::Error();
die "invalid IP address: $err\n";
+ }else{
+ $nbaor++;
}
}
+ return $nbaor;
}
sub parse_port_name_number_or_range {
my $cmd = "-A $chain";
+ $cmd .= " -m iprange --src-range" if $rule->{nbsource} && $rule->{nbsource} > 1;
$cmd .= " -s $rule->{source}" if $rule->{source};
+ $cmd .= " -m iprange --dst-range" if $rule->{nbdest} && $rule->{nbdest} > 1;
$cmd .= " -d $rule->{dest}" if $rule->{destination};
$cmd .= " -p $rule->{proto}" if $rule->{proto};
$cmd .= " --match multiport" if $rule->{nbdport} && $rule->{nbdport} > 1;
if($direction eq 'OUT'){
my $rule = "proxmoxfw-INPUT -m physdev --physdev-$physdevdirection $iface -j $tapchain";
-
- if(!iptables_rule_exist($rule)){
+ if(iptables_rule_exist($rule)){
iptables_addrule("-D $rule");
}
}
}
}
+sub enablehostfw {
+
+ generate_proxmoxfwinput();
+ generate_proxmoxfwoutput();
+
+ my $filename = "/etc/pve/local/host.fw";
+ my $fh = IO::File->new($filename, O_RDONLY);
+ return if !$fh;
+
+ my $rules = parse_fw_rules($filename, $fh);
+ my $inrules = $rules->{in};
+ my $outrules = $rules->{out};
+
+ #host inbound firewall
+ iptables_addrule(":host-IN - [0:0]");
+ iptables_addrule("-A host-IN -m state --state INVALID -j DROP");
+ iptables_addrule("-A host-IN -m state --state RELATED,ESTABLISHED -j ACCEPT");
+ iptables_addrule("-A host-IN -i lo -j ACCEPT");
+ iptables_addrule("-A host-IN -m addrtype --dst-type MULTICAST -j ACCEPT");
+ iptables_addrule("-A host-IN -p udp -m state --state NEW -m multiport --dports 5404,5405 -j ACCEPT");
+ iptables_addrule("-A host-IN -p udp -m udp --dport 9000 -j ACCEPT"); #corosync
+
+ if (scalar(@$inrules)) {
+ foreach my $rule (@$inrules) {
+ #we use RETURN because we need to check also tap rules
+ $rule->{action} = 'RETURN' if $rule->{action} eq 'ACCEPT';
+ iptables_generate_rule('host-IN', $rule);
+ }
+ }
+
+ iptables_addrule("-A host-IN -j LOG --log-prefix \"kvmhost-IN dropped: \" --log-level 4");
+ iptables_addrule("-A host-IN -j DROP");
+
+ #host outbound firewall
+ iptables_addrule(":host-OUT - [0:0]");
+ iptables_addrule("-A host-OUT -m state --state INVALID -j DROP");
+ iptables_addrule("-A host-OUT -m state --state RELATED,ESTABLISHED -j ACCEPT");
+ iptables_addrule("-A host-OUT -o lo -j ACCEPT");
+ iptables_addrule("-A host-OUT -m addrtype --dst-type MULTICAST -j ACCEPT");
+ iptables_addrule("-A host-OUT -p udp -m state --state NEW -m multiport --dports 5404,5405 -j ACCEPT");
+ iptables_addrule("-A host-OUT -p udp -m udp --dport 9000 -j ACCEPT"); #corosync
+
+ if (scalar(@$outrules)) {
+ foreach my $rule (@$outrules) {
+ #we use RETURN because we need to check also tap rules
+ $rule->{action} = 'RETURN' if $rule->{action} eq 'ACCEPT';
+ iptables_generate_rule('host-OUT', $rule);
+ }
+ }
+
+ iptables_addrule("-A host-OUT -j LOG --log-prefix \"kvmhost-OUT dropped: \" --log-level 4");
+ iptables_addrule("-A host-OUT -j DROP");
+
+
+ my $rule = "proxmoxfw-INPUT -j host-IN";
+ if(!iptables_rule_exist($rule)){
+ iptables_addrule("-I $rule");
+ }
+
+ $rule = "proxmoxfw-OUTPUT -j host-OUT";
+ if(!iptables_rule_exist($rule)){
+ iptables_addrule("-I $rule");
+ }
+
+ iptables_restore();
+
+
+}
+
+sub disablehostfw {
+
+ my $chain = "host-IN";
+
+ my $rule = "proxmoxfw-INPUT -j $chain";
+ if(iptables_rule_exist($rule)){
+ iptables_addrule("-D $rule");
+ }
+
+ if(iptables_chain_exist($chain)){
+ iptables_addrule("-F $chain");
+ iptables_addrule("-X $chain");
+ }
+
+ $chain = "host-OUT";
+
+ $rule = "proxmoxfw-OUTPUT -j $chain";
+ if(iptables_rule_exist($rule)){
+ iptables_addrule("-D $rule");
+ }
+
+ if(iptables_chain_exist($chain)){
+ iptables_addrule("-F $chain");
+ iptables_addrule("-X $chain");
+ }
+
+ iptables_restore();
+}
+
+sub generate_proxmoxfwinput {
+
+ if(!iptables_chain_exist("proxmoxfw-INPUT")){
+ iptables_addrule(":proxmoxfw-INPUT - [0:0]");
+ iptables_addrule("-I INPUT -j proxmoxfw-INPUT");
+ iptables_addrule("-A INPUT -j ACCEPT");
+ }
+}
+
+sub generate_proxmoxfwoutput {
+
+ if(!iptables_chain_exist("proxmoxfw-OUTPUT")){
+ iptables_addrule(":proxmoxfw-OUTPUT - [0:0]");
+ iptables_addrule("-I OUTPUT -j proxmoxfw-OUTPUT");
+ iptables_addrule("-A OUTPUT -j ACCEPT");
+ }
+
+}
+
+sub enable_group_rules {
+ my ($group) = @_;
+
+ generate_group_rules($group);
+ iptables_restore();
+}
+
+sub generate_group_rules {
+ my ($group) = @_;
+
+ my $filename = "/etc/pve/firewall/groups.fw";
+ my $fh = IO::File->new($filename, O_RDONLY);
+ return if !$fh;
+
+ my $rules = parse_fw_rules($filename, $fh, $group);
+ my $inrules = $rules->{in};
+ my $outrules = $rules->{out};
+
+ my $chain = "GROUP-".$group."-IN";
+
+ iptables_addrule(":$chain - [0:0]");
+
+ if (scalar(@$inrules)) {
+ foreach my $rule (@$inrules) {
+ iptables_generate_rule($chain, $rule);
+ }
+ }
+
+ $chain = "GROUP-".$group."-OUT";
+
+ iptables_addrule(":$chain - [0:0]");
+
+ if(!iptables_chain_exist("BRIDGEFW-OUT")){
+ iptables_addrule(":BRIDGEFW-OUT - [0:0]");
+ }
+
+ if(!iptables_chain_exist("BRIDGEFW-IN")){
+ iptables_addrule(":BRIDGEFW-IN - [0:0]");
+ }
+
+ if (scalar(@$outrules)) {
+ foreach my $rule (@$outrules) {
+ #we go the BRIDGEFW-IN because we need to check also other tap rules
+ #(and group rules can be set on any bridge, so we can't go to VMBRXX-IN)
+ $rule->{action} = 'BRIDGEFW-IN' if $rule->{action} eq 'ACCEPT';
+ iptables_generate_rule($chain, $rule);
+ }
+ }
+
+}
+
+sub disable_group_rules {
+ my ($group) = @_;
+
+ my $chain = "GROUP-".$group."-IN";
+
+ if(iptables_chain_exist($chain)){
+ iptables_addrule("-F $chain");
+ iptables_addrule("-X $chain");
+ }
+
+ $chain = "GROUP-".$group."-OUT";
+
+ if(iptables_chain_exist($chain)){
+ iptables_addrule("-F $chain");
+ iptables_addrule("-X $chain");
+ }
+
+ #iptables_restore will die if security group is linked in a tap chain
+ #maybe can we improve that, parsing each vm config, or parsing iptables -S
+ #to see if the security group is linked or not
+ iptables_restore();
+}
+
+
my $generate_input_rule = sub {
my ($zoneinfo, $rule, $net, $netid) = @_;
sub parse_fw_rules {
- my ($filename, $fh) = @_;
+ my ($filename, $fh, $group) = @_;
my $section;
+ my $securitygroup;
+ my $securitygroupexist;
my $res = { in => [], out => [] };
- my $macros = get_shorewall_macros();
+ my $macros = get_firewall_macros();
my $protocols = get_etc_protocols();
while (defined(my $line = <$fh>)) {
next if $line =~ m/^#/;
next if $line =~ m/^\s*$/;
- if ($line =~ m/^\[(in|out)\]\s*$/i) {
+ if ($line =~ m/^\[(in|out)(:(\S+))?\]\s*$/i) {
$section = lc($1);
+ $securitygroup = lc($3) if $3;
+ $securitygroupexist = 1 if $securitygroup && $securitygroup eq $group;
next;
}
next if !$section;
+ next if $group && $securitygroup ne $group;
my ($action, $iface, $source, $dest, $proto, $dport, $sport) =
split(/\s+/, $line);
$sport = undef if $sport && $sport eq '-';
my $nbdport = undef;
my $nbsport = undef;
+ my $nbsource = undef;
+ my $nbdest = undef;
eval {
- parse_address_list($source) if $source;
- parse_address_list($dest) if $dest;
+ $nbsource = parse_address_list($source) if $source;
+ $nbdest = parse_address_list($dest) if $dest;
$nbdport = parse_port_name_number_or_range($dport) if $dport;
$nbsport = parse_port_name_number_or_range($sport) if $sport;
};
iface => $iface,
source => $source,
dest => $dest,
+ nbsource => $nbsource,
+ nbdest => $nbdest,
proto => $proto,
dport => $dport,
sport => $sport,
push @{$res->{$section}}, $rule;
}
+ die "security group $group don't exist" if $group && !$securitygroupexist;
+ return $res;
+}
+
+sub run_locked {
+ my ($code, @param) = @_;
+
+ my $timeout = 10;
+
+ my $res = lock_file($pve_fw_lock_filename, $timeout, $code, @param);
+
+ die $@ if $@;
+
return $res;
}
}
sub compile {
-
my $vmdata = read_local_vm_config();
my $rules = read_vm_firewall_rules($vmdata);
# print Dumper($vmdata);
- my $swdir = '/etc/shorewall';
- mkdir $swdir;
-
- &$compile_shorewall($swdir, $vmdata, $rules);
-
- PVE::Tools::run_command(['shorewall', 'compile']);
+ die "implement me";
}
sub compile_and_start {
compile();
- PVE::Tools::run_command(['shorewall', $restart ? 'restart' : 'start']);
+ die "implement me";
}
-
1;