diff options
Diffstat (limited to 'lib/network')
-rw-r--r-- | lib/network/drakfirewall6.pm | 381 | ||||
-rw-r--r-- | lib/network/shorewall6.pm | 251 |
2 files changed, 632 insertions, 0 deletions
diff --git a/lib/network/drakfirewall6.pm b/lib/network/drakfirewall6.pm new file mode 100644 index 0000000..6c00fd7 --- /dev/null +++ b/lib/network/drakfirewall6.pm @@ -0,0 +1,381 @@ +package network::drakfirewall6; # $Id: drakfirewall.pm 268043 2010-04-30 13:29:37Z blino $ + + + +use lib qw(/usr/lib/libDrakX); # helps perl_checker +use network::shorewall6; +use common; +use network::nfs; +use network::network; + +my @all_servers = +( + { + name => N_("Web Server"), + pkg => 'apache apache-mod_perl lighttpd nginx', + ports => '80/tcp 443/tcp', + }, + { + name => N_("Domain Name Server"), + pkg => 'bind dnsmasq mydsn', + ports => '53/tcp 53/udp', + }, + { + name => N_("SSH server"), + pkg => 'openssh-server', + ports => '22/tcp', + }, + { + name => N_("FTP server"), + pkg => 'proftpd pure-ftpd', + ports => '20/tcp 21/tcp', + }, + { + name => N_("DHCP Server"), + pkg => 'dhcp-server udhcpd', + ports => '67/udp 68/udp', + hide => 1, + }, + { + name => N_("Mail Server"), + pkg => 'sendmail postfix', + ports => '25/tcp 465/tcp 587/tcp', + }, + { + name => N_("POP and IMAP Server"), + pkg => 'imap courier-imap-pop', + ports => '109/tcp 110/tcp 143/tcp 993/tcp 995/tcp', + }, + { + name => N_("Telnet server"), + pkg => 'netkit-telnet-server', + ports => '23/tcp', + hide => 1, + }, + { + name => N_("NFS Server"), + pkg => 'nfs-utils', + ports => '111/tcp 111/udp 2049/tcp 2049/udp ' . network::nfs::list_nfs_ports(), + hide => 1, + prepare => sub { network::nfs::write_nfs_ports(network::nfs::read_nfs_ports()) }, + restart => 'nfs-common nfs-server', + }, + { + name => N_("Windows Files Sharing (SMB)"), + pkg => 'samba', + ports => '137/tcp 137/udp 138/tcp 138/udp 139/tcp 139/udp 445/tcp 445/udp 1024:1100/tcp 1024:1100/udp', + hide => 1, + }, + { + name => N_("Bacula backup"), + pkg => 'bacula-fd bacula-sd', + ports => '9101:9103/tcp', + hide => 1, + }, + { + name => N_("Syslog network logging"), + pkg => 'rsyslog syslog-ng', + ports => '514/udp', + hide => 1, + }, + { + name => N_("CUPS server"), + pkg => 'cups', + ports => '631/tcp 631/udp', + hide => 1, + }, + { + name => N_("MySQL server"), + pkg => 'mariadb', + ports => '3306/tcp 3306/udp', + hide => 1, + }, + { + name => N_("PostgreSQL server"), + pkg => 'postgresql9.4 postgresql9.6', + ports => '5432/tcp 5432/udp', + hide => 1, + }, + { + name => N_("Echo request (ping)"), + ports => '8/icmp', + force_default_selection => 0, + }, + { + name => N_("Network services autodiscovery (zeroconf and slp)"), + ports => '5353/udp 427/udp', + pkg => 'avahi cups openslp', + }, + { + name => N_("BitTorrent"), + ports => '6881:6999/tcp 6881:6999/udp', + hide => 1, + pkg => 'bittorrent deluge ktorrent vuze rtorrent transmission-common', + }, + { + name => N_("KDEConnect"), + ports => '1714:1764/udp 1714:1764/tcp', + hide => 1, + pkg => 'kdeconnect-kde', + }, + { + name => N_("Ident server"), + ports => '113/tcp', + hide => 1, + pkg => 'oidentd', + }, + { + name => N_("Windows Mobile device synchronization"), + ports => '990/tcp 999/tcp 5678/tcp 5679/udp 26675/tcp', + hide => 1, + }, +); + +my @ifw_rules = ( + { + name => N_("Port scan detection"), + ifw_rule => 'psd', + }, +); + +# global network configuration +my $net = {}; +network::network::read_net_conf($net); + +sub port2server { + my ($port) = @_; + find { + any { $port eq $_ } split(' ', $_->{ports}); + } @all_servers; +} + +sub check_ports_syntax { + my ($ports) = @_; + foreach (split ' ', $ports) { + my ($nb, $range, $nb2) = m!^(\d+)(:(\d+))?/(tcp|udp|icmp)$! or return $_; + foreach my $port ($nb, if_($range, $nb2)) { + 1 <= $port && $port <= 65535 or return $_; + } + $nb < $nb2 or return $_ if $range; + } + ''; +} + +sub to_ports { + my ($servers, $unlisted) = @_; + join(' ', (map { $_->{ports} } @$servers), if_($unlisted, $unlisted)); +} + +sub from_ports { + my ($ports) = @_; + + my @l; + my @unlisted; + foreach (split ' ', $ports) { + if (my $s = port2server($_)) { + push @l, $s; + } else { + push @unlisted, $_; + } + } + [ uniq(@l) ], join(' ', @unlisted); +} + +sub default_from_pkgs { + my ($do_pkgs) = @_; + my @pkgs = $do_pkgs->are_installed(map { split ' ', $_->{pkg} } @all_servers); + [ grep { + my $s = $_; + exists $s->{force_default_selection} ? + $s->{force_default_selection} : + any { member($_, @pkgs) } split(' ', $s->{pkg}); + } @all_servers ]; +} + +sub default_ports { + my ($do_pkgs) = @_; + to_ports(default_from_pkgs($do_pkgs), ''); +} + +sub get_ports() { + my $shorewall6 = network::shorewall6::read() or return; + $shorewall6->{ports}; +} + +sub set_ports { + my ($do_pkgs, $disabled, $ports, $log_net_drop, $o_in) = @_; + + if (!$disabled || -x "$::prefix/sbin/shorewall6") { + $do_pkgs->ensure_files_are_installed([ [ qw(shorewall shorewall) ], [ qw(shorewall-ipv6 shorewall6) ] ], $::isInstall) or return; + my $shorewall6 = network::shorewall6::read(!$disabled && $o_in); + if (!$shorewall6) { + log::l("unable to read shorewall6 configuration, skipping installation"); + return; + } + + $shorewall6->{disabled} = $disabled; + $shorewall6->{ports} = $ports; + $shorewall6->{log_net_drop} = $log_net_drop; + log::l($disabled ? "disabling shorewall6" : "configuring shorewall6 to allow ports: $ports"); + network::shorewall6::write($shorewall6, $o_in); + } +} + +sub get_conf { + my ($in, $disabled, $o_ports) = @_; + + my $possible_servers = default_from_pkgs($in->do_pkgs); + $_->{hide} = 0 foreach @$possible_servers; + + if ($o_ports) { + $disabled, from_ports($o_ports); + } elsif (my $shorewall6 = network::shorewall6::read()) { + $shorewall6->{disabled}, from_ports($shorewall6->{ports}), $shorewall6->{log_net_drop}; + } else { + $in->ask_okcancel(N("IPv6 firewall configuration"), N("drakfirewall6 configurator + +This configures a personal ipv6 firewall for this Mageia machine."), 1) or return; + + $in->ask_okcancel(N("IPv6 firewall configuration"), N("drakfirewall6 configurator + +Make sure you have configured your Network/Internet IPv6 access with +drakconnect before going any further."), 1) or return; + + $disabled, $possible_servers, ''; + } +} + +sub choose_allowed_services { + my ($in, $disabled, $servers, $unlisted, $log_net_drop) = @_; + + $_->{on} = 0 foreach @all_servers; + $_->{on} = 1 foreach @$servers; + my @l = grep { $_->{on} || !$_->{hide} } @all_servers; + + $in->ask_from_({ + title => N("Firewall IPv6"), + icon => $network::shorewall6::firewall_icon, + if_(!$::isEmbedded, banner_title => N("Firewall IPv6")), + advanced_messages => N("You can enter miscellaneous ports. +Valid examples are: 139/tcp 139/udp 600:610/tcp 600:610/udp. +Have a look at /etc/services for information."), + callbacks => { + complete => sub { + if (my $invalid_port = check_ports_syntax($unlisted)) { + $in->ask_warn('', N("Invalid port given: %s. +The proper format is \"port/tcp\" or \"port/udp\", +where port is between 1 and 65535. + +You can also give a range of ports (eg: 24300:24350/udp)", $invalid_port)); + return 1; + } + }, + } }, + [ + { label => N("Which services would you like to allow the IPv6 Internet to connect to?"), title => 1 }, + if_($net->{PROFILE} && network::network::netprofile_count() > 0, { label => N("Those settings will be saved for the network profile <b>%s</b>", $net->{PROFILE}) }), + { text => N("Everything (no firewall)"), val => \$disabled, type => 'bool' }, + (map { { text => translate($_->{name}), val => \$_->{on}, type => 'bool', disabled => sub { $disabled } } } @l), + { label => N("Other ports"), val => \$unlisted, advanced => 1, disabled => sub { $disabled } }, + { text => N("Log firewall6 messages in system logs"), val => \$log_net_drop, type => 'bool', advanced => 1, disabled => sub { $disabled } }, + ]) or return; + + $disabled, [ grep { $_->{on} } @l ], $unlisted, $log_net_drop; +} + +sub set_ifw { + my ($do_pkgs, $enabled, $rules, $ports) = @_; + if ($enabled) { + $do_pkgs->ensure_is_installed('mandi-ifw', '/etc/ifw/start', $::isInstall) or return; + + my $ports_by_proto = network::shorewall6::ports_by_proto($ports); + output_with_perm("$::prefix/etc/ifw/rules", 0644, + (map { ". /etc/ifw/rules.d/$_\n" } @$rules), + map { + my $proto = $_; + map { + my $multiport = /:/ && " -m multiport"; + "iptables -A Ifw -m conntrack --ctstate NEW -p $proto$multiport --dport $_ -j IFWLOG --log-prefix NEW\n"; + } @{$ports_by_proto->{$proto}}; + } intersection([ qw(tcp udp) ], [ keys %$ports_by_proto ]), + ); + } + + substInFile { + undef $_ if m!^INCLUDE /etc/ifw/rules|^iptables -I INPUT 2 -j Ifw!; + } "$::prefix/etc/shorewall6/start"; + network::shorewall6::set_in_file('start', $enabled, "INCLUDE /etc/ifw/start", "INCLUDE /etc/ifw/rules", "ip6tables -I INPUT 1 -j Ifw"); + network::shorewall6::set_in_file('stop', $enabled, "ip6tables -D INPUT -j Ifw", "INCLUDE /etc/ifw/stop"); +} + +sub choose_watched_services { + my ($in, $servers, $unlisted) = @_; + + my @l = (@ifw_rules, @$servers, map { { ports => $_ } } split(' ', $unlisted)); + my $enabled = 1; + $_->{ifw} = 1 foreach @l; + + $in->ask_from_({ + icon => $network::shorewall6::firewall_icon, + if_(!$::isEmbedded, banner_title => N("Interactive IPv6 Firewall")), + messages => + N("You can be warned when someone accesses to a service or tries to intrude into your computer. +Please select which network activities should be watched."), + title => N("Interactive IPv6 Firewall"), + }, + [ + { text => N("Use Interactive IPv6 Firewall"), val => \$enabled, type => 'bool' }, + map { { + text => (exists $_->{name} ? translate($_->{name}) : $_->{ports}), + val => \$_->{ifw}, + type => 'bool', disabled => sub { !$enabled }, + } } @l, + ]) or return; + my ($rules, $ports) = partition { exists $_->{ifw_rule} } grep { $_->{ifw} } @l; + set_ifw($in->do_pkgs, $enabled, [ map { $_->{ifw_rule} } @$rules ], to_ports($ports)); + + # return something to say that we are done ok + $rules, $ports; +} + +sub main { + my ($in, $disabled) = @_; + + ($disabled, my $servers, my $unlisted, my $log_net_drop) = get_conf($in, $disabled) or return; + + ($disabled, $servers, $unlisted, $log_net_drop) = choose_allowed_services($in, $disabled, $servers, $unlisted, $log_net_drop) or return; + + my $system_file = '/etc/sysconfig/drakx-net'; + my %global_settings = getVarsFromSh($system_file); + + if (!$disabled && (!defined($global_settings{IFW}) || text2bool($global_settings{IFW}))) { + choose_watched_services($in, $servers, $unlisted) or return; + } + + # preparing services when required + foreach (@$servers) { + exists $_->{prepare} and $_->{prepare}(); + } + + my $ports = to_ports($servers, $unlisted); + + set_ports($in->do_pkgs, $disabled, $ports, $log_net_drop, $in) or return; + + # restart mandi + require services; + services::is_service_running("mandi") and services::restart("mandi"); + + # restarting services if needed + foreach my $service (@$servers) { + if ($service->{restart}) { + services::is_service_running($_) and services::restart($_) foreach split(' ', $service->{restart}); + } + } + + # clearing pending ifw notifications in net_applet + system('killall -s SIGUSR1 net_applet'); + + ($disabled, $ports); +} + +1; diff --git a/lib/network/shorewall6.pm b/lib/network/shorewall6.pm new file mode 100644 index 0000000..4c7aa47 --- /dev/null +++ b/lib/network/shorewall6.pm @@ -0,0 +1,251 @@ +package network::shorewall6; # $Id: shorewall6.pm 254244 2009-03-18 22:54:32Z eugeni $ + +use lib qw(/usr/lib/libDrakX); # helps perl_checker +use detect_devices; +use network::network; +use run_program; +use common; +use log; + +my $shorewall6_root = "/etc/shorewall6"; +our $firewall_icon = $::isInstall ? 'banner-security' : '/usr/share/mcc/themes/default/firewall-mdk.png'; + +sub check_iptables() { + -f "$::prefix/etc/sysconfig/iptables" || + $::isStandalone && do { + system('modprobe iptable_nat'); + -x '/sbin/iptables' && listlength(`/sbin/iptables -t nat -nL`) > 8; + }; +} + +sub set_config_file { + my ($file, $ver, @l) = @_; + + my $done; + substInFile { + my $last_line = /^#LAST LINE/ && $_; + if (!$done && ($last_line || eof)) { + $_ = join('', map { join("\t", @$_) . "\n" } @l); + $_ .= $last_line if $last_line; + $done = 1; + } else { + $_ = '' unless + /^#/ || $file eq 'rules' && /^SECTION/; + } + } "$::prefix${shorewall6_root}${ver}/$file"; +} + +sub get_config_file { + my ($file, $o_ver) = @_; + map { [ split ' ' ] } grep { !/^#/ } cat_("$::prefix${shorewall6_root}${o_ver}/$file"); +} + +# Note: Called from drakguard and drakfirewall.pm... +# Deliberately adding shorewall6 support here now ;-) +sub set_in_file { + my ($file, $enabled, @list) = @_; + my $done; + substInFile { + my $last_line = /^#LAST LINE/ && $_; + foreach my $l (@list) { s|^$l\n|| } + if (!$done && $enabled && ($last_line || eof)) { + $_ = join('', map { "$_\n" } @list); + $_ .= $last_line if $last_line; + $done = 1; + } + } "$::prefix${shorewall6_root}/$file"; +} + +sub dev_to_shorewall6 { + my ($dev) = @_; + $dev =~ /^ippp/ && "ippp+" || + $dev =~ /^ppp/ && "ppp+" || + $dev; +} + +sub get_net_zone_interfaces { + my ($interfacesfile, $_net, $all_intf) = @_; + #- read shorewall6 configuration first + my @interfaces = map { $_->[1] } grep { $_->[0] eq 'net' } $interfacesfile; + #- else try to find the best interface available + @interfaces ? @interfaces : @{$all_intf || []}; +} + +sub get_zones { + my ($conf, $o_in) = @_; + my $interfacesfile = get_config_file('interfaces', $conf->{version} || ''); + my $net = {}; + network::network::read_net_conf($net); + #- find all interfaces but alias interfaces + my @all_intf = grep { !/:/ } uniq(keys(%{$net->{ifcfg}}), detect_devices::get_net_interfaces()); + my %net_zone = map { $_ => undef } @all_intf; + $net_zone{$_} = 1 foreach get_net_zone_interfaces($interfacesfile, $net, \@all_intf); + $o_in and $o_in->ask_from_({ + title => N("Firewall IPv6 configuration"), + icon => $firewall_icon, + messages => N("Please select the interfaces that will be protected by the firewall. + +All interfaces directly connected to Internet should be selected, +while interfaces connected to a local network may be unselected. + +If you intend to use Mageia Internet Connection sharing, +unselect interfaces which will be connected to local network. + +Which interfaces should be protected? +"), + }, [ + map { + { text => network::tools::get_interface_description($net, $_), val => \$net_zone{$_}, type => 'bool' }; + } (sort keys %net_zone) ]); + ($conf->{net_zone}, $conf->{loc_zone}) = partition { $net_zone{$_} } keys %net_zone; +} + +sub add_interface_to_net_zone { + my ($conf, $interface) = @_; + if (!member($interface, @{$conf->{net_zone}})) { + push @{$conf->{net_zone}}, $interface; + @{$conf->{loc_zone}} = grep { $_ ne $interface } @{$conf->{loc_zone}}; + } +} + +sub read { + my ($o_in, $o_ver) = @_; + my $ver = ''; + $ver = $o_ver if $o_ver; + #- read old rules file if config is not moved to rules.drakx yet + my @rules = get_config_file(-f "$::prefix${shorewall6_root}${ver}/rules.drakx" ? 'rules.drakx' : 'rules', $ver); + require services; + my %conf = (disabled => !services::starts_on_boot("shorewall6"), + version => $ver, + ports => join(' ', map { + my $e = $_; + map { "$_/$e->[3]" } split(',', $e->[4]); + } grep { $_->[0] eq 'ACCEPT' && $_->[1] eq 'net' } @rules), + ); + push @{$conf{accept_local_users}{$_->[4]}}, $_->[8] foreach grep { $_->[0] eq 'ACCEPT+' } @rules; + $conf{redirects}{$_->[3]}{$_->[4]} = $_->[2] foreach grep { $_->[0] eq 'REDIRECT' } @rules; + + if (my ($e) = get_config_file('masq', $ver)) { + ($conf{masq}{net_interface}, $conf{masq}{subnet}) = @$e; + } + + my @policy = get_config_file('policy', $ver); + $conf{log_net_drop} = @policy ? (any { $_->[0] eq 'net' && $_->[1] eq 'all' && $_->[2] eq 'DROP' && $_->[3] } @policy) : 1; + + get_zones(\%conf, $o_in); + get_config_file('zones', $ver) && \%conf; +} + +sub ports_by_proto { + my ($ports) = @_; + my %ports_by_proto; + foreach (split ' ', $ports) { + m!^(\d+(?::\d+)?)/(udp|tcp|icmp)$! or die "bad port $_\n"; + push @{$ports_by_proto{$2}}, $1; + } + \%ports_by_proto; +} + +sub write { + my ($conf, $o_in) = @_; + my $ver = $conf->{version} || ''; + my $use_pptp = any { /^ppp/ && cat_("$::prefix/etc/ppp/peers/$_") =~ /pptp/ } @{$conf->{net_zone}}; + my $ports_by_proto = ports_by_proto($conf->{ports}); + my $has_loc_zone = to_bool(@{$conf->{loc_zone} || []}); + + my ($include_drakx, $other_rules) = partition { $_ eq "INCLUDE\trules.drakx\n" } grep { !/^(#|SECTION)/ } cat_("$::prefix${shorewall6_root}${ver}/rules"); + #- warn if the config is already in rules.drakx and additionnal rules are configured + if (!is_empty_array_ref($include_drakx) && !is_empty_array_ref($other_rules)) { + my %actions = ( + keep => N("Keep custom rules"), + drop => N("Drop custom rules"), + ); + my $action = 'keep'; + !$o_in || $o_in->ask_from_( + { + messages => N("Your IPv6 firewall configuration has been manually edited and contains +rules that may conflict with the configuration that has just been set up. +What do you want to do?"), + title => N("Firewall"), + icon => 'banner-security', + }, + [ { val => \$action, type => 'list', list => [ 'keep', 'drop' ], format => sub { $actions{$_[0]} } } ]) or return; + #- reset the rules files if the user has chosen to drop modifications + undef $include_drakx if $action eq 'drop'; + } + + my $interface_settings = sub { + my ($zone, $interface) = @_; + [ $zone, $interface, 'detect', if_(detect_devices::is_bridge_interface($interface), 'bridge') ]; + }; + + set_config_file('zones', $ver, + if_($has_loc_zone, [ 'loc', 'ipv' . ($ver || '6') ]), + [ 'net', 'ipv' . ($ver || '6') ], + [ 'fw', 'firewall' ], + ); + set_config_file('interfaces', $ver, + (map { $interface_settings->('net', $_) } @{$conf->{net_zone}}), + (map { $interface_settings->('loc', $_) } @{$conf->{loc_zone} || []}), + ); + set_config_file('policy', $ver, + if_($has_loc_zone, [ 'loc', 'net', 'ACCEPT' ], [ 'loc', 'fw', 'ACCEPT' ], [ 'fw', 'loc', 'ACCEPT' ]), + [ 'fw', 'net', 'ACCEPT' ], + [ 'net', 'all', 'DROP', if_($conf->{log_net_drop}, 'info') ], + [ 'all', 'all', 'REJECT', 'info' ], + ); + if (is_empty_array_ref($include_drakx)) { + #- make sure the rules.drakx config is read, erasing user modifications + set_config_file('rules', $ver, [ 'INCLUDE', 'rules.drakx' ]); + } + output_with_perm("$::prefix${shorewall6_root}${ver}/" . 'rules.drakx', 0600, map { join("\t", @$_) . "\n" } ( + if_($use_pptp, [ 'ACCEPT', 'fw', 'loc:10.0.0.138', 'tcp', '1723' ]), + if_($use_pptp, [ 'ACCEPT', 'fw', 'loc:10.0.0.138', 'gre' ]), + (map_each { [ 'ACCEPT', 'net', 'fw', $::a, join(',', @$::b), '-' ] } %$ports_by_proto), + (map_each { + if_($::b, map { [ 'ACCEPT+', 'fw', 'net', 'tcp', $::a, '-', '-', '-', $_ ] } @$::b); + } %{$conf->{accept_local_users}}), + (map { + my $proto = $_; + #- WARNING: won't redirect ports from the firewall system if a local zone exists + #- set redirect_fw_only to workaround + map_each { + map { [ 'REDIRECT', $_, $::b, $proto, $::a, '-' ] } 'fw', if_($has_loc_zone, 'loc'); + } %{$conf->{redirects}{$proto}}; + } keys %{$conf->{redirects}}), + )); + set_config_file('masq', $ver, if_(exists $conf->{masq}, [ $conf->{masq}{net_interface}, $conf->{masq}{subnet} ])); + + require services; + if ($conf->{disabled}) { + services::disable('shorewall6', $::isInstall); + run_program::rooted($::prefix, '/sbin/shorewall6', 'clear') unless $::isInstall; + } else { + services::enable('shorewall6', $::isInstall); + } +} + +sub set_redirected_ports { + my ($conf, $proto, $dest, @ports) = @_; + if (@ports) { + $conf->{redirects}{$proto}{$_} = $dest foreach @ports; + } else { + my $r = $conf->{redirects}{$proto}; + @ports = grep { $r->{$_} eq $dest } keys %$r; + delete $r->{$_} foreach @ports; + } +} + +sub update_interfaces_list { + my ($o_intf) = @_; + if (!$o_intf || !member($o_intf, map { $_->[1] } get_config_file('interfaces'))) { + my $shorewall = network::shorewall::read(); + $shorewall && !$shorewall->{disabled} and network::shorewall::write($shorewall); + } + if (!$o_intf || !member($o_intf, map { $_->[1] } get_config_file('interfaces', 6))) { + my $shorewall6 = network::shorewall6::read(undef, 6); + $shorewall6 && !$shorewall6->{disabled} and network::shorewall::write($shorewall6); + } +} + +1; |