[[Vorlage(Archiviert, )]] {{{#!vorlage Wissen [:Pakete_installieren: Installation von Programmen] [:Paketquellen_freischalten: Bearbeiten von Paketquellen] [:Terminal: Ein Terminal öffnen] [:Editor: Einen Editor öffnen] [:Dienste: Dienste beim Bootvorgang starten] }}} ## {{{#!vorlage Hinweis ## Um den Netzwerkverkehr einzuschränken kann man alternativ auch allgemein, oder nur für einen speziellen Benutzer unter dem man ein bestimmtes Programm laufen lässt (vgl. [:Programme abschotten:] ), nur bestimmte Verbindungen erlauben und alles andere verbieten. Sehr einfach konfigurierbar ist dies mit dem Paket '''firehol'''. ## }}} [[Inhaltsverzeichnis(2)]] = Anfd - Ain't No Firewall Daemon = Immer wieder werden Umsteiger von anderen Betriebssystemen bei ihren ersten Kontakten zu Ubuntu-Anwendern damit konfrontiert, dass sie bei Ubuntu keine [:Personal_Firewalls:Personal Firewalls] benötigen. Lassen sich die meisten von ihnen noch relativ leicht davon überzeugen, dass eine gepflegte Ubuntu-Installation nach außen hin auch ohne Paketfilter genau so wenig Angriffsfläche bietet, so kommt doch immer wieder der Wunsch auf, den Internetzugriff lokal laufender Applikationen zu kontrollieren. Auch dieser Wunsch ist auf einem Ubuntu-System in den meisten Fällen nicht nötig - zumindest braucht man dafür keine "Personal Firewall" mit bunten Pop-Up-Fenstern, unterschiedlichen "Warnleveln" und was man sonst noch so aus der schillernden Welt der Sicherheitssoftware-Industrie kennt. Solche Software kann einen vor ernsthafter Schadsoftware sowieso nicht ernsthaft schützen. (Siehe weiterführende Links.) In legitimer Open-Source-Software lassen sich Spywarefunktionen dagegen nicht verstecken, und sie könnten quasi sofort von den Distributoren herausgepatcht werden. Bleibt also nur noch jene Handvoll Closed-Source-Applikationen, die auch unter Linux Verwendung finden, deren Hersteller aber wenig Wert auf die Privatsphäre der Nutzer legen. Hier setzt '''anfd''' an. Wichtig ist zu begreifen, dass '''anfd''' '''keine Sicherheitssoftware''' ist, sondern '''Datenschutzsoftware'''. Der beste Schutz gegen Trojaner und verwandte Software ist immer noch, keine Software aus unbekannter Quelle zu installieren. '''Anfd''' kümmert sich nicht um Software, die der Benutzer unbewusst installiert, sondern blockiert bestimmte, aus Benutzersicht unerwünschte Internetverbindungen jener Software, deren Primärfunktionen er gerne trotzdem nutzen will. Dabei ist es möglich, einzelnen Applikationen selektiv den Internetzugang komplett zu verweigern, auf bestimmte Adressbereiche zu beschränken, oder nur bestimmte Bereiche (z.B. das Netz des Herstellers) zu blockieren. = Funktionsweise = Da die Zuordnung bestimmter Internetverbindungen zu einzelnen Applikationen innerhalb von [:iptables:] / '''netfilter''' nicht problemlos möglich ist - und die rudimentäre Unterstützung dafür im Ubuntu-Kernel deaktiviert ist - findet die Filterung mittels '''anfd''' im sog. Userspace, also außerhalb des Kernels, statt. Die Pakete werden mittels des IP_Queue-Mechanismus an den '''anfd'''-Daemon übertragen, der dann über die Legitimation der betreffenden Verbindung entscheidet. Standardmäßig erstellt '''anfd''' eine passende '''iptables'''-Regel, die auf einem System ohne (auf dem Desktop meist sowieso überflüssige) "Firewall" funktioniert. {{{#!vorlage Experten Wer '''anfd''' parallel mit anderen Filterregeln einsetzen will, kann über die Kommandozeilenoption ``-i`` eine passendere '''iptables'''-Regel definieren, oder mit der Option ``-x`` deutlich machen, dass er sich lieber selber um die Erstellung der ''QUEUE''-Regel(n) kümmern will. }}} = Installation = Folgenden Pakete müssen vor der Installation von '''anfd''' installiert werden [1]: * '''libiptables-ipv4-ipqueue-perl''' (''universe'', [2]) * '''libnet-cidr-perl''' (''universe'') * '''libnet-rawip-perl''' (''universe'') Das Skript selber kopiert man einfach an eine geeignete Stelle im Pfad, z.B. nach '''/usr/local/sbin'''. Wenn man '''anfd''' dauerhaft verwenden will, sollte man noch ein Startskript '''/etc/init.d/anfd''' erstellen [4] und beim Systemstart aktivieren [5]. = Benutzung = '''Anfd''' selber wird über Kommandozeilenoptionen und eine Konfigurationsdatei (standardmäßig '''/etc/anfd.conf''') gesteuert: {{{anfd [-D] [-i 'iptables command'] [-x] [-c configfile] [-p pidfile] anfd -k [-p pidfile] anfd (-h|-?) }}} == Optionen == * ``-h`` oder '''-?''': Hilfe. Gibt eine kurze Syntax-Beschreibung aus. * ``-k``: Stoppt einen laufenden '''anfd'''-Prozess. * ``-D``: "Debug-Modus". '''Anfd''' löst sich in diesem Fall nicht vom Terminal, sondern gibt detaillierte Angaben zu jedem geprüften Datenpaket aus. Das kann benutzt werden, um die Aktivitäten bestimmter Software zu verfolgen und die Konfiguration zu verfeinern. * ``-c file``: Alternative Konfigurationsdatei. Standard ist '''/etc/anfd.conf''' * ``-p file``: Alternative Pid-Datei. In der Pid-Datei wird die Prozess-ID des laufenden '''anfd''' hinterlegt. Das verhindert unabsichtliche Mehrfachstarts des Daemons und wird von '''anfd -k''' benutzt. Standard ist '''/var/run/anfd.pid'''. * ``-i command``: Hiermit kann man ein alternatives '''iptables'''-Kommando wählen, mit dem die Datenpakete an den '''anfd'''-Daemon geschickt werden. Den Standard erhält man mit '''anfd -h'''. * ``-x``: Der Daemon erzeugt keine '''iptables'''-Regel beim Start. Die korrekte Übergabe der Datenpakete an den '''anfd'''-Daemon wird vom Administrator anderweitig geregelt. == Konfiguration == Die Konfigurationsdatei (standardmäßig '''/etc/anfd.conf''') ist zeilenweise aufgebaut. Leerzeilen werden ignoriert, ebenso alles zwischen dem '''#'''-Zeichen und dem Zeilenende. Ansonsten bezeichnet jede Zeile die Regel(n) für eine bestimmte Applikation. Die einzelnen Bezeichner werden mit Leerzeichen getrennt. === Programmteil === Jede Zeile beginnt mit der Definition der Applikation, für die die Regel gelten soll. Leerzeichen sind innerhalb dieser Definition nicht erlaubt. Es gibt drei Möglichkeiten, diese anzugeben: * '''vollständiger Pfad''': Wird ein vollständiger Pfad (beginnend mit einem '''/''') angegeben, so gilt die Regel erwartungsgemäß genau für das bezeichnete Programm. * '''Programmname''': Der einfache Name einer ausführbaren Datei bezeichnet eine Regel, die für alle Dateien dieses Namens gilt, unabhängig von der Position im Dateisystem. * '''^Kommandozeile''': Diese Regel trifft zu, wenn eines der Kommandozeilenargumente des Programms mit der durch das Zirkumflex-Zeichen gekennzeichneten Zeichenkette übereinstimmt. Das ist vor allem sinnvoll, um Skripte in Interpretersprachen zuverlässig unterscheiden zu können. === Regelteil === Der Rest jeder Zeile beinhaltet beliebig viele Regeln. Folgende Formen sind erlaubt: * '''*''': Jeglicher Internetzugriff wird dieser Applikation verweigert. * '''IP-Adresse''' (z.B. 10.10.10.10): Zugriff auf eine bestimmte IP-Adresse wird verweigert. * '''Netzwerk-Adresse''' (z.B. 10.0.0.0/8): Zugriff auf ein komplettes Netzwerk wird verweigert. * '''IP-Bereich''' (z.B. 10.0.0.1-10.0.2.254): Zugriff auf alle IPs des angegebenen Bereichs wird verweigert. * '''!''' (z.B. !10.0.0.14): Kehrt den Sinn einer der drei o.a. Regeln um, d.h. der Zugriff auf bezeichnete IP, Netzwerk oder IP-Bereich wird erlaubt. Ein Leerzeichen zwischen dem '''!''' und der IP ist nicht gestattet. Wenn auf ein Datenpaket sowohl eine Verbots- als auch eine Erlaubnis-Regel passt, wird die Verbindung erlaubt, unabhängig von der Reihenfolge der Regeln. = Beschränkungen = * Der IP_Queue-Mechanismus kennt leider nur die Möglichkeiten ''DROP'' und ''ACCEPT'', um mit Paketen zu verfahren. Damit ge''drop''pte Verbindungen nicht bis zum Timeout hängen bleiben, erzeugt '''anfd''' selber ein passendes ''network-unreachable''-Paket. Dieses wird leider vom ''Connection Tracking'' als ''INVALID'' angesehen. Man sollte also auf der ''Loopback''-Schnittstelle keine Regel haben, die diese Pakete blockiert. * Wenn sich '''anfd''' außerplanmäßig beendet, wird die Aufräumroutine nicht durchgeführt und die '''iptables'''-QUEUE-Regel muss von Hand gelöscht werden. Das sollte aber eigentlich nicht vorkommen. * IP_Queue ist veraltet. Es gibt einen neueren Mechanismus namens '''nfnetlink_queue''' in aktuellen Kerneln. Dieser ist allerdings sehr neu, so gut wie gar nicht dokumentiert und es gibt noch kein passendes Perl-Modul. Deswegen erschien es sinnvoll, vorerst auf den alten Mechanismus zu setzen. = Links = * [:Personal_Firewalls: Personal Firewalls: Sinn und Unsinn dieser Programme] * [https://ulm.ccc.de/ChaosSeminar/2004/12_Personal_Firewalls Chaosseminar: Personal Firewalls (CCC)] {de} * [ikhaya:245:ikhaya: Test der Panda-Suite] ---- * [attachment:Skripte/anfd/anfd:Hier das Skript herunterladen.] {{{#!/usr/bin/perl -w # # anfd - Ain't no firewall daemon # # Autoren: ostcar und otzenpunk # Homepage: http://wiki.UbuntuUsers.de/Skripte/anfd # Lizenz: Gnu GPL Version 2 oder höher # # Changelog: # 18.11.2006: v0.1 # initial release use strict; use POSIX; use Getopt::Std; use IPTables::IPv4::IPQueue qw(:constants); use Net::CIDR qw(:all); use Net::RawIP; use constant VERSION =>"0.1"; my @IP_PROTO ; $IP_PROTO[0]='IP'; # Dummy protocol for IP $IP_PROTO[1]='ICMP'; # Internet Control Message Protocol $IP_PROTO[2]='IGMP'; # Internet Group Management Protocol $IP_PROTO[4]='IPIP'; # IP in IP encapsulation $IP_PROTO[6]='TCP'; # Transmission Control Protocol $IP_PROTO[17]='UDP'; # User Datagram Protocol my %opts; my (%allow,%forbidden); my (%use_cmdline); my @ipt_command = qw(/sbin/iptables -I OUTPUT -m state --state NEW -j QUEUE); my $ipt_done; # wichtig für cleanup my $default_config = '/etc/anfd.conf'; my $pid_file = '/var/run/anfd.pid'; getopts('kph?Dxc:i:', \%opts); $opts{'h'} || $opts{'?'} and usage(); $< and info("You must be root to use $0.",4); $opts{'k'} and killUs(); # We'll never come back from there sub cleanup { #in dieser funktion kein info() verwenden, da es zu einer endlosschleife führen könnte print ("\tReceiving Term-Signal. Cleaning up.\n"); if ($ipt_done) { $ipt_command[1] = '-D'; print("\tResetting iptables.\n"); system(@ipt_command) == 0 or print("\t\tWarning: iptables failed: $?\n"); } if (-e $pid_file) { print("\tRemoving pid file.\n"); unlink $pid_file or print("\t\tWarning: Can't remove pid file $pid_file.\n\n"); } print("Exiting.\n"); exit; } $SIG{INT}=$SIG{TERM}=$SIG{HUP}=\&cleanup; $pid_file = $opts{'p'} if $opts{'p'}; @ipt_command = split " ", $opts{'i'} if $opts{'i'}; (-e $pid_file) && die("Pid-File already exists. Maybe another anfd running?\n"); $opts{D} ? info("Starte anfd v".VERSION,1) : print "Starte anfd v".VERSION."\n"; readConfigFile($opts{c} || $default_config) or info("Empty configuration file", $opts{D} ? 0 : 4); unless ($opts{'x'}) { system('modprobe', 'ip_queue'); system(@ipt_command) == 0 or info("iptables failed: $?",4); $ipt_done = 1; } my($ipversion, $proto,$src_ip ,$dest_ip,$dest_port, $src_port,$proc,$cmdline); my $queue = new IPTables::IPv4::IPQueue(copy_mode => IPQ_COPY_PACKET, copy_range => 2048) or info(IPTables::IPv4::IPQueue->errstr,4); # "Daemonisieren" - vom Terminal abkoppeln # Perl-Kochbuch: (Seite 754ff) unless ($opts{'D'}) { my $pid=fork; exit if $pid; info("Error: Can't fork(): $!",4)unless defined($pid); writePid(); for my $handle (*STDIN, *STDOUT, *STDERR){ open($handle, "+<", "/dev/null") or info("Could not redirect $handle to /dev/null: $!",4); } POSIX::setsid() or info("Could not start new session: $!",4); } else { writePid(); } # Endlosschleife: while(1) { # Paket aus der Queue holen my $msg = $queue->get_message(); if (!defined $msg) { next if IPTables::IPv4::IPQueue->errstr eq 'Timeout'; info(IPTables::IPv4::IPQueue->errstr,4); } info("Packet arrived",1); info("parse",1); ($ipversion, $proto, $src_ip, $dest_ip, $src_port, $dest_port)=ripPayload($msg->payload()) if $msg->data_len(); $proto=$IP_PROTO[$proto] || 'Unknown'; info("IP-Version: $ipversion"); info("Protocol: $proto"); info("Dest-IP: $dest_ip"); info("Dest-Port: $dest_port"); if ($ipversion == 4) { if ($proto eq 'TCP' || $proto eq 'UDP') { ($proc,$cmdline)=getProcName($src_port,$proto); if ($proc) { info ("Path: $proc"); info ("cmdLine: $cmdline"); info ("",2); info ("checking rules...",1); if(canPass($proc,$cmdline,$dest_ip)){ info ("can pass"); $queue->set_verdict($msg->packet_id, NF_ACCEPT); } else { info("can not pass"); sendReject($msg->payload(),$src_ip); $queue->set_verdict($msg->packet_id, NF_DROP); } info("",2); info("Done",2); next; } else { # no process id info("can not spy out the name",3); } } else { # no tcp/udp info("$proto not supported with anfd v".VERSION,3); } } else { # IPv6 et al. info("IPv$ipversion not supported with anfd v".VERSION,3); } info("",3); # accept evt. unknown so wont break anything $queue->set_verdict($msg->packet_id, NF_ACCEPT); } #function zu einladen der conf datei sub readConfigFile { # readconfigfile($configfile) my $file = shift; my $inuse; info("Load config file",1); open(CF,"$file") or info("Warning: can not open $file",4); while(){ my($proc, @ip, @allow, @forbidden); next if /^\s*(?:$|#)/; # Bei leerer Zeile gar nicht erst weiter umwandeln s/\s*#.*//; # Löscht von # bis Zeilenende ($proc, @ip)=split(); #alle wörter der Zeile durch whitespaces trennen info("Config error: $_", 4) if !defined($ip[0]); # fehler, wenn nicht beide argumente vorhanden sind $proc =~ s/^\^// && ( $use_cmdline{$proc} = 1 ); # bei ^ am anfang cmdline benutzen for (@ip) { my @ipranges; my @h; if (s/^!//) { info("$proc can pass to $_"); @ipranges = ((@h = validIPs($_)) ? range2cidr(@h) : ()) or info("Config error: $_", 4); push @allow, @ipranges; } elsif (/^\*$/) { info("$proc can not pass by default"); push @forbidden, '0.0.0.0/0'; $inuse=1; } else { info("$proc can not pass to $_"); @ipranges = ((@h = validIPs($_)) ? range2cidr(@h) : ()) or info("Config error: $_", 4); push @forbidden, @ipranges; $inuse=1; } } $forbidden{$proc}=\@forbidden; $allow{$proc}=\@allow; } info("Done",2); return $inuse; } sub validIPs { for (@_) { next if cidrvalidate($_); if ( /^([0-9.]+)\/([0-9]+)$/ ) { next if cidrvalidate($1) && $2 <= 32; } if ( /^([0-9.]+)-([0-9.]+)$/ ) { next if cidrvalidate($1) && cidrvalidate($2); } return 0; # wenn es in kein Schema passt, ist es falsch } return (@_); # wenn wir hier ankommen, ist alles in Ordnung } #function zum Regel abgleichen sub canPass{ # $bool = can_pass($programmname, $cmdline, $ipadresse) # return 1: kann passieren # return 0: blockieren my $proc=shift; my $cmdline=shift; my $ip=shift; my(@forbidden,@allow,@cmd); #alle argumente überprüfen, und auch des program ohne pfad @cmd=split('\0',$cmdline); for (@cmd){ if(defined($use_cmdline{$_})||defined($forbidden{$_})||defined($allow{$_})){ @forbidden=@{$forbidden{$_}}; @allow=@{$allow{$_}}; } } @forbidden=@{$forbidden{$proc}} if(defined($forbidden{$proc})); @allow=@{$allow{$proc}} if(defined($allow{$proc})); # Regel: Verbieten, wenn in @forbidden, aber nicht in @allow if(@forbidden && cidrlookup($ip,@forbidden)){ if(@allow && cidrlookup($ip,@allow)){ return 1; }else{ return 0; } }else{ return 1; } } #nimmt einen quellport und gibt die inode zurück bzw 0 für fehler sub srcPort2inode{ # $inode = quellporttoinode($quellport,$protocol) # Fehler: return 0; my $src_port=shift; my $protocol=lc(shift); open(FILE, "/proc/net/$protocol") or return 0; while() { my @cline=split; next if ($cline[9]=~/uid/); my $port = hex((split(":",$cline[1]))[1]); if($port==$src_port){ close(FILE); return $cline[9]; } } close(FILE); return 0; } #nimmt eine inode und gibt eine prog nr zurück sub inode2procNr{ # $procnr = inodetoprocnr($inode); # bei Fehler: return 0; my $inode=shift; return 0 unless $inode; my($proc,$ports,$dev,$ino,$link); opendir (PROC, "/proc") or return 0; for $proc (readdir(PROC)) { # Infos zu jedem laufenden Programm liefert /proc # Wir müssen alle testen, bis eins passt. next if(!($proc=~/[0-9]+/) ); # Nach PIDs suchen. if(!opendir(PORTS,"/proc/$proc/fd")) { # fd-Ordner öffnen closedir PORTS; next; # nächstes Programm, wenn Öffnen fehlschlägt } for $ports (readdir(PORTS)) { #in jedem laufenden program die geöffneten dateien durchsuchen next if(!($ports=~/[0-9]+/)); #muss zahlen beinhalten $link=readlink("/proc/$proc/fd/$ports"); #zu der datei gehn, auf welches die nummer zeigt ($dev,$ino)=($link=~/^(socket|\[[0-9a-fA-F]*\]):\[?([0-9]*)\]?$/); if(defined($ino)&& defined($dev) && $ino==$inode && ($dev eq "[0000]" || $dev eq "socket")){ closedir PORTS; closedir PROC; return $proc; } } closedir PORTS; } closedir PROC; return 0; } #nimmt einen srcPort und gibt den programnamen und die cmdline aus sub getProcName{ # ($pfad, $cmdline) = getprocname($quellport,$protocol); # Fehler: return 0; my $procnr=inode2procNr(srcPort2inode(@_)); return 0 unless $procnr; my($pfad,$cmdline,@cmdline); $pfad=readlink("/proc/$procnr/exe"); open(CMDLINE,"/proc/$procnr/cmdline") or return 0; $cmdline=; @cmdline=split(" ",$cmdline); $cmdline[0]=~s/^\/.*\///; $cmdline=join(" ",@cmdline); close(CMDLINE); return $pfad, $cmdline; } # Nimmt ein TCP/IP-Packet und liefert IP-Version (norm. 4), Protokoll (TCP/UDP als Nummer) IPs und Ports zurück sub ripPayload { my $payload = shift; my ($version, $proto, $src_ip, $dest_ip, $src_port, $dest_port); my (@src_ip, @dest_ip); # IP-Header auseinanderpflücken: ($version, undef, $proto, undef, @src_ip[0..3], @dest_ip[0..3], $src_port, $dest_port) = unpack("C1a8C1a2C8n1n1", $payload); $version >>= 4; # IPv steht in linken vier Bits $src_ip = join ".", @src_ip; $dest_ip = join ".", @dest_ip; return ($version, $proto, $src_ip, $dest_ip, $src_port, $dest_port); } # IP_QUEUE kennt nur ACCEPT und DROP. Deswegen konstruieren wir unser eigenes Reject-Paket sub sendReject { my $payload = unpack("a28", shift); # Anfang des Originalpakets wird in ICMP verpackt my $ip = shift; Net::RawIP->new({ip => {saddr => $ip, daddr => $ip}, icmp => {type => 3, code => 0, data => $payload} })->send(); # network unreachable } INIT { my $ebene = 0; sub info { my $info = shift; my $command = shift || 0; unless ($opts{D}) { if ($command == 4) { print "$info\n"; cleanup(); } return; } if ($command==1){ #neue ebene print "\t" x $ebene.$info.": \n"; $ebene++; }elsif ($command==2){ #ebene runter $ebene--; print "\t" x $ebene .$info."\n" if $info; }elsif($command==3){ #ebene runter mit fehler $ebene--; print "\t" x $ebene."Error".($info?": $info":"")."\n"; }elsif($command==4){ #alle ebenen runter mit fehler und ende $ebene--; for(;$ebene>0;$ebene--){ print "\t" x $ebene."Critical Error".($info?": $info":"")."\n"; } cleanup(); }else{ print "\t" x $ebene.$info."\n" } } } sub usage { my $ipt = join " ", @ipt_command; print <; close PID; ($pid) = split '\D+', $pid; $pid or die("No pid found in $pid_file. You'll have to kill running anfd processes yourself.\n"); unless (kill 'TERM', $pid) { print("No process $pid. You'll have to kill running anfd processes yourself.\n"); unlink $pid_file or print "Can't delete $pid_file.\n"; } print("Killed anfd.\n"); exit; } sub writePid { if (-e $pid_file) { info("There seems to be another anfd process running. Use anfd -k to get rid of it.",4); } else { open PID, "> $pid_file" or info("Can not write pid file.\n",4); print PID $$; close PID; } } }}} [attachment:Skripte/anfd/anfd:inline:anfd] # tag: Shell, Netzwerk, Internet