Bitte denken Sie daran: Sie dürfen zwar die Online-Version ausdrucken, aber diesen Druck nicht fotokopieren oder verkaufen.
Wünschen Sie mehr Informationen zu der gedruckten Version des Buches "Linux: Wegweiser durch das Netzwerk" dann klicken Sie hier.
Leider können wir in diesem Buch nicht alle Netzwerk-Anwendungen besprechen. Wenn Sie eine der hier nicht behandelten Anwendungen wie talk, gopher oder Mosaic installieren wollen, müssen wir Sie auf die entsprechenden Manpages verweisen.
Daher verwenden nahezu alle UNIX-Installationen einen »Super-Server«, der Sockets für eine Reihe von Diensten erzeugt und mit Hilfe des select(2)-Systemaufrufs simultan abhört. Fordert ein entfernter Host einen Dienst an, wird dies vom Super-Server registriert, und er startet den für diesen Port zuständigen Server.
Der im allgemeinen verwendete Super-Server ist inetd, der »Internet Dämon«. Er wird während der Bootphase des Systems gestartet und liest eine Liste der Dienste, die er verwalten soll, aus der Datei /etc/inetd.conf. Zusätzlich zu den von inetd gesteuerten Servern gibt es eine Reihe trivialer Dienste, sogenannte interne Dienste, die inetd selbständig ausführt. Dazu gehören beispielsweise chargen, das einfach eine Zeichenkette erzeugt, und daytime, das die nach Meinung des Systems aktuelle Tageszeit zurückliefert.
Ein Eintrag in dieser Datei besteht aus einer einzelnen Zeile, die sich aus den folgenden Feldern zusammensetzt:
service type protocol wait user server cmdlineDie Bedeutung der einzelnen Felder wird nachfolgend erklärt:
stream
(bei verbindungsorientierten Protokollen) oder als dgram
(für Datagramm-Protokolle). TCP-basierte Dienste sollten daher immer stream
verwenden, während UDP-basierte Dienste immer dgram
verwenden sollten.
dgram
-Sockets gültig. Sie kann entweder wait
oder nowait
lauten. Wird wait
angegeben, führt inetd nur jeweils einen Server für den angegebenen Port aus. Anderenfalls beginnt es wieder umgehend an diesem Port zu horchen, sobald der Server gestartet wurde.
Das ist sinnvoll bei sog. »Single-Threaded-Servern«, die alle eingehenden Datagramme lesen, bis keine weiteren mehr ankommen, und sich dann automatisch beenden. Die meisten RPC-Server sind von dieser Art und sollten daher immer wait
verwenden. Der entgegengesetzte Typ, der »Multi-Threaded-Server«, erlaubt eine unbeschränkte Anzahl von gleichzeitig laufenden Instanzen und wird nur selten benutzt. Dieser Server-Typ sollte nowait
angeben.
stream
-Sockets sollten immer nowait
verwenden.
internal
gekennzeichnet.
Dieses Feld ist bei internen Diensten leer.
# # inetd-Dienste ftp stream tcp nowait root /usr/sbin/ftpd in.ftpd -l telnet stream tcp nowait root /usr/sbin/telnetd in.telnetd -b/etc/issue #finger stream tcp nowait bin /usr/sbin/fingerd in.fingerd #tftp dgram udp wait nobody /usr/sbin/tftpd in.tftpd #tftp dgram udp wait nobody /usr/sbin/tftpd in.tftpd /boot/diskless login stream tcp nowait root /usr/sbin/rlogind in.rlogind shell stream tcp nowait root /usr/sbin/rshd in.rshd exec stream tcp nowait root /usr/sbin/rexecd in.rexecd # # inetd-interne Dienste # daytime stream tcp nowait root internal daytime dgram udp nowait root internal time stream tcp nowait root internal time dgram udp nowait root internal echo stream tcp nowait root internal echo dgram udp nowait root internal discard stream tcp nowait root internal discard dgram udp nowait root internal chargen stream tcp nowait root internal chargen dgram udp nowait root internaltftp ist ebenfalls auskommentiert. tftp implementiert das Trivial File Transfer Protocol (TFTP), das es einem erlaubt, allgemein lesbare Dateien ohne Paßwortprüfung etc. von Ihrem System zu übertragen. Das ist besonders bei der Datei /etc/passwd gefährlich, um so mehr, wenn Sie nicht mit Shadow-Paßwörtern arbeiten.
TFTP wird üblicherweise von Clients ohne eigenes Diskettenlaufwerk und von X-Terminals genutzt, um deren Kode vom einem Bootserver herunterzuladen. Muß tftpd aus diesem Grund ausgeführt werden, sollten Sie sicherstellen, daß Sie den Zugriff nur auf solche Verzeichnisse erlauben, aus denen die Clients die entsprechenden Dateien lesen. Diese Verzeichnisse können Sie in der tftpd-Kommandozeile angeben, was in der zweiten tftp-Zeile des Beispiels zu erkennen ist.
Weil das Erweitern eines Computers um den Netzwerkzugriff viele Sicherheitsrisiken in sich birgt, sind die Anwendungen so entwickelt worden, daß sie sich vor verschiedenen Arten von Angriffen schützen können. Allerdings sind einige Sicherheitsmerkmale mangelhaft (was der RTM Internet-Wurm auf drastische Weise demonstriert hat), oder es wird nicht unterschieden zwischen einem sicheren Host, von dem Anforderungen nach einem bestimmten Service akzeptiert werden können, und einem unsicheren Host, dessen Anforderungen abgelehnt werden müssen. Die Dienste finger und tftp haben wir oben ja bereits angesprochen. Sicher würden Sie den Zugriff auf diese Dienste gerne auf »vertrauenswürdige Hosts« beschränken, was aber mit dem normalen Setup nicht möglich ist, bei dem inetd den Dienst entweder allen Clients anbietet, oder keinem.
Ein für solche Fälle nützliches Werkzeug ist tcpd, ein sogenannter Dämon-Wrapper. Bei TCP-Diensten, die Sie überwachen oder sichern wollen, wird es anstelle des normalen Server-Programms gestartet. tcpd schickt eine Meldung über die Anforderungen an den syslog-Dämon, prüft, ob der entfernte Host diesen Dienst überhaupt benutzen darf und führt nur dann das eigentliche Server-Programm aus. Beachten Sie, daß dies bei UDP-basierten Diensten leider nicht funktioniert.(1)
Um beispielsweise den finger-Dämon mit einem Wrapper zu schützen, müssen Sie die entsprechende Zeile in der inetd.conf wie folgt ändern:
# finger-Dämon mit Wrapper schützen finger stream tcp nowait root /usr/sbin/tcpd in.fingerdOhne eine zusätzliche Zugriffskontrolle erscheint dies für den Client wie ein ganz gewöhnliches finger-Setup, mit der Ausnahme, daß alle Anforderungen in der
auth
-Einrichtung von syslog aufgezeichnet werden.
Die Zugriffskontrolle wird mit Hilfe der beiden Dateien /etc/hosts.allow und /etc /hosts.deny implementiert. Sie enthalten Einträge, die den Zugriff auf bestimmte Dienste und Hosts erlauben bzw. sperren. Erhält tcpd eine Anforderung für einen Dienst wie finger von einem Client-Host namens biff.foobar.com, durchsucht es hosts.allow und hosts.deny (in dieser Reihenfolge) nach einem Eintrag, bei dem sowohl der Dienst als auch der Client übereinstimmen. Wird ein passender Eintrag in hosts.allow gefunden, wird der Zugriff freigegeben, gleichgültig, ob es noch einen Eintrag in hosts.deny gibt. Wird eine Übereinstimmung in hosts.deny gefunden, wird die Anforderung abgewiesen, und die Verbindung wird unterbrochen. Wird überhaupt kein passender Eintrag gefunden, wird die Anforderung ebenfalls akzeptiert.
Die Einträge in den Zugriffsdateien sehen wie folgt aus:
servicelist: hostlist [:shellcmd]servicelist ist eine Liste mit Servicenamen aus /etc/services oder das Schlüsselwort
ALL
. Um alle Dienste außer finger und tftp zu akzeptieren, müssen Sie »ALL
EXCEPT
finger, tftp
« eingeben.
hostlist
ist eine Liste mit Hostnamen oder IP-Adressen oder den Schlüsselwörtern ALL
, LOCAL
oder UNKNOWN
. ALL
steht für alle Hosts, während LOCAL
nur Hostnamen vergleicht, die keinen Punkt enthalten.(2) UNKNOWN
gilt für jeden Host, dessen Name oder Adresse durch einen Lookup nicht ermittelt werden konnte. Ein mit einem Punkt beginnender Name steht für eine Domain und wählt alle Hosts aus, die zu dieser Domain gehören. Zum Beispiel würde der Eintrag .foobar.com das System biff.foobar.com akzeptieren. IP-Netzwerk-Adressen und Subnetz-Nummern werden ebenfalls unterstützt.
Um den Zugriff auf finger und tftp für alle Hosts, mit Ausnahme der lokalen Hosts, zu unterbinden, müssen Sie den folgenden Eintrag in die /etc/hosts.deny eintragen, wobei /etc/hosts.allow nicht verändert wird:
in.tftpd, in.fingerd: ALL EXCEPT LOCAL, .Ihre.DomainDas optionale Feld
shellcmd
kann einen Shell-Befehl enthalten, der ausgeführt wird, wenn der Vergleich erfolgreich war. Dies ist sinnvoll, wenn Sie zusätzliche Fallen einbauen wollen, um mögliche Angreifer zu enttarnen:
in.ftpd: ALL EXCEPT LOCAL, .vbrew.com : \ echo "request from %d@%h: >> /var/log/finger.log; \ if [ %h != "vlager.vbrew.com:" ]; then \ finger -l @%h >> /var/log/finger.log \ fi
Die Argumente %h
und %d
werden von tcpd durch den Client-Hostnamen und den Servicenamen ersetzt. Details entnehmen Sie bitte der Manpage hosts_access(5).
Die Portnummer, über die einige der »Standarddienste« angeboten werden, sind im »Assigned Numbers RFC« definiert. Um es Server- und Client-Programmen zu ermöglichen, die Servicenamen in diese Nummern umzuwandeln, ist zumindest einTeil dieser Liste auf jedem Host vorhanden und wird in einer Datei namens /etc/services gespeichert. Ein Eintrag setzt sich aus den folgenden Feldern zusammen:
Service Port/Protokoll [Aliase]Service gibt den Namen des Dienstes an,
Port
beschreibt, auf welchem Port dieser Dienst angeboten wird, und Protokoll
definiert das zu verwendende Transportprotokoll. Üblicherweise ist dies entweder udp
oder tcp
. Ein Dienst kann auch für mehr als ein Protokoll angeboten werden. Verschiedene Dienste können denselben Port benutzen, solange die Protokolle verschieden sind. Im Feld Aliase
können Sie alternative Namen für denselben Service definieren.
Normalerweise müssen Sie die Services-Datei nicht ändern, die mit der Netzwerk-Software für Ihr Linux-System geliefert wird. Dennoch wollen wir Ihnen in Beispiel 9--2 einen kleinen Ausschnitt dieser Datei zeigen.
Beispiel 9-2. Ausschnitt aus /etc/services (Beispiel)
# Services-Datei: # # bekannte Dienste: echo 7/tcp # Echo echo 7/udp # discard 9/tcp sink null # Discard discard 9/udp sink null # daytime 13/tcp # Daytime daytime 13/udp # chargen 19/tcp ttytst source # Character Generator chargen 19/udp ttytst source # ftp-data 20/tcp # File Transfer Protocol (Data) ftp 21/tcp # File Transfer Protocol (Control) telnet 23/tcp # Virtual Terminal Protocol smtp 25/tcp # Simple Mail Transfer Protocol nntp 119/tcp readnews # Network News Transfer Protocol # # UNIX services exec 512/tcp # rexecd (BSD) biff 512/udp comsat # Mail-Benachrichtigung login 513/tcp # remote Login who 513/udp whod # who und uptime (remote) shell 514/tcp cmd # Befehl (remote) ohne Paßworteingabe syslog 514/udp # System-Logging (remote) printer 515/tcp spooler # Druckspooler (remote) route 520/udp router routed # Router-InformationsprotokollZum Beispiel wird der Dienst echo sowohl für TCP als auch für UDP über Port 7 angeboten. Port 512 wird für zwei verschiedene Dienste verwendet: für die Programmausführung auf entfernten Systemen mittels rexec (TCP) und für den COMSAT-Dämon (UDP), der Benutzer über neu eingegangene Post informiert.
Analog zur services-Datei benötigt die Netzwerk-Bibliothek noch eine Möglichkeit, Protokollnamen (beispielsweise die in der Services-Datei verwendeten) in Protokollnummern zu übersetzen, die der IP-Layer auf dem anderen Host versteht. Dies wird mit Hilfe der Datei /etc/protocols erreicht. Sie besteht aus einem Eintrag pro Zeile, der den Protokollnamen und die zugewiesene Nummer enthält. Daß Sie sich mit dieser Datei auseinandersetzen müssen, ist sogar noch unwahrscheinlicher als bei /etc/services. Ein Beispiel ist in Beispiel 9--3 zu sehen.
Beispiel 9-3. Ausschnitt aus /etc/protocols (Beispiel)
# # Internet-Protokolle (IP) # ip 0 IP # Internet-Protokoll, Pseudo-Protokollnummer icmp 1 ICMP # Internet Control Message Protocol igmp 2 IGMP # Internet Group Multicast Protocol tcp 6 TCP # Transmission Control Protocol udp 17 UDP # User Datagram Protocol raw 255 RAW # RAW IP-Interface
Ein RPC-Server kennt eine Reihe von Prozeduren, die ein Client aufrufen kann, indem er eine RPC-Anforderung zusammen mit den benötigten Parametern an den Server schickt. Der Server führt die Prozedur im Auftrag des Client aus und liefert das Ergebnis zurück, wenn es eines gibt. Um maschinenunabhängig zu sein, werden alle zwischen dem Client und dem Server ausgetauschten Daten vom Sender in das sogenannte XDR-Format (External Data Representation) um- und vom Empfänger wieder in die lokale Repräsentation zurückgewandelt. Sun hat RPC großzügigerweise in die Public Domain freigegeben. Es wird in einer Reihe von RFCs beschrieben.
Manchmal führen Verbesserungen an einer RPC-Anwendung zu Inkompatibilitäten im Interface der Prozeduraufrufe. Natürlich würde ein einfaches Wechseln des Servers alle Anwendungen zum Absturz bringen, die noch das ursprüngliche Verhalten erwarten. Darum verfügt jedes RPC-Programm über eine Versionsnummer, die normalerweise bei 1 beginnt und bei jeder neuen RPC-Version erhöht wird. Häufig bietet ein Server mehrere Versionen gleichzeitig an, und die Clients geben durch die Versionsnummer in ihren Anforderungen an, welche Implementierung des Dienstes verwendet werden soll.
Die Netzwerk-Kommunikation zwischen RPC-Servern und -Clients ist etwas ungewöhnlich. Ein RPC-Server bietet eine Sammlung von Prozeduren an, die als Programm bezeichnet wird und durch eine Programm-Nummer eindeutig definiert ist. Eine Liste, die Servicenamen auf Programm-Nummern abbildet, wird normalerweise in der Datei /etc/rpc gespeichert. Einen Ausschnitt dieser Datei sehen Sie in Beispiel 9--4.
Beispiel 9-4. Ausschnitt aus /etc/rpc
# # /etc/rpc -- verschiedene RPC-basierte Dienste # portmapper 100000 portmap sunrpc rstatd 100001 rstat rstat_svc rup perfmeter rusersd 100002 rusers nfs 100003 nfsprog ypserv 100004 ypprog mountd 100005 mount showmount ypbind 100007 walld 100008 rwall shutdown yppasswdd 100009 yppasswd bootparam 100026 ypupdated 100028 ypupdateBei TCP/IP-Netzwerken wurden die Autoren von RPC mit dem Problem konfrontiert, Programm-Nummern auf Netzwerkdienste abzubilden. Sie entschieden, daß jeder Server sowohl einen TCP- als auch einen UDP-Port für jedes Programm und jede Version bereitstellen soll. Normalerweise verwenden RPC-Anwendungen UDP, um Daten zu übertragen, und greifen nur dann auf TCP zurück, wenn die zu transportierenden Daten nicht in ein einziges UDP-Datagramm passen. Es gibt allerdings einige wenige Dienste, die nur eines der beiden Transportprotokolle anbieten.
Natürlich müssen Client-Programme eine Möglichkeit haben herauszufinden, auf welche Programm-Nummer ein Port abgebildet ist. Die Verwendung einer Konfigurationsdatei zu diesem Zweck wäre zu unflexibel. Weil RPC-Anwendungen keine reservierten Ports verwenden, gibt es keine Garantie, daß ein ursprünglich von unserer Datenbank-Anwendung zu verwendender Port nicht von einem anderen Prozeß besetzt wurde. Darum nimmt eine RPC-Anwendung den ersten Port, den sie kriegen kann, und teilt ihn dem sogenannten Portmapper-Dämon mit. Der Portmapper fungiert als Service-Vermittler für alle RPC-Servern, die auf dieser Maschine laufen. Ein Client, der einen Dienst mit einer gegebenen Programm-Nummer nutzen möchte, wird zuerst den Portmapper auf dem Host des Servers abfragen, der dann die TCP- und UDP-Portnummern zurückliefert, über die der Dienst erreicht werden kann.
Diese Methode hat den Nachteil, daß sie -- genau wie inetd für die normalen Berkeley-Dienste -- einen »single point of failure« darstellt. Nun ist dieser Fall noch ein wenig schlimmer, weil alle RPC-Port-Informationen verlorengehen, wenn der Portmapper stirbt. Das bedeutet üblicherweise, daß Sie alle RPC-Server manuell neu starten, oder sogar die gesamte Maschine neu hochfahren müssen.
Manchmal ist es wünschenswert, für bestimmte Benutzer die Autorisierungs-Prüfungen zu vereinfachen. Wenn Sie beispielsweise sehr häufig auf eine andere Maschine in Ihrem LAN zugreifen müssen, wäre es für Sie sehr angenehm, wenn Sie nicht jedesmal Ihr Paßwort eingeben müßten.
Es gibt zwei Wege, die Autorisierungs-Prüfungen für die r-Befehle zu deaktivieren. Zum einen kann der Superuser einigen oder allen Benutzern auf einigen oder allen Hosts (letzteres wäre eine sehr schlechte Idee) erlauben, sich einzuloggen, ohne daß nach einem Paßwort gefragt wird. Dieser Zugriff wird in einer Datei namens /etc/hosts.equiv kontrolliert. Sie enthält eine Liste mit Host- und Benutzernamen, die mit den Benutzern auf dem lokalen Host gleichgesetzt werden. Eine alternative Möglichkeit wäre, daß ein Benutzer anderen Anwendern auf verschiedenen Hosts den Zugriff auf seinen Account erlaubt. Dies kann in der Datei .rhosts im Home-Verzeichnis des Benutzers eingetragen werden. Aus Sicherheitsgründen muß diese Datei dem Benutzer oder dem Superuser gehören und darf nicht einfach ein symbolischer Link sein. Anderenfalls wird sie ignoriert.(3)
Wenn ein Client einen der r-Dienste anfordert, wird der entsprechende Host- und Benutzername in der Datei /etc/hosts.equiv gesucht. Diese Prozedur wird danach in der .rhosts des Benutzers, bei dem der Login erfolgen soll, wiederholt. Nehmen wir beispielsweise an, daß
janet an gauss arbeitet und versucht, sich in
joes Account auf
euler einzuloggen. Während des nachfolgenden Beispiels sprechen wir von Janet als Client-Benutzer und Joe als lokalem Benutzer. Janet gibt auf
gauss den folgenden Befehl ein:
Die Datei hosts.equiv auf
euler sieht wie folgt aus:
Wenn, wie in der letzten Zeile des obigen Beispiels, dem Hostnamen ein Benutzername folgt, wird dem Benutzer paßwortfreier Zugang zu allen Accounts gewährt (mit Ausnahme des root-Accounts).
Vor dem Hostnamen kann auch ein Minuszeichen stehen, wie im Eintrag
-public. In diesem Fall wird die Autorisierung für alle Accounts auf
public benötigt, gleichgültig, welche Rechte von einzelnen Benutzern in deren jeweiligen .rhosts vergeben wurden.
Das Format von .rhosts ist mit dem von hosts.equiv identisch, die Bedeutung ist aber etwas unterschiedlich. Sehen wir uns mal Joes .rhosts auf euler an:
Beachten Sie, daß der Hostname des Client ermittelt wird, indem die Adresse des Anrufers auf einen Namen abgebildet wird (»Reverse Mapping«). Diese Operation schlägt aber fehl, wenn Hosts dem Resolver nicht bekannt sind. Es wird erwartet, daß der Hostname des Client dem Namen in den Hostdateien in einer der folgenden Formen entspricht:
Bei Linux wird der Portmapper rpc.portmap genannt und ist in /usr/sbin zu finden. Sie müssen nur sicherstellen, daß er von rc.inet2 aus gestartet wird; weitere Konfigurationsarbeiten sind nicht notwendig.
Konfiguration der r-Befehle
Es gibt eine Reihe von Befehlen zur Ausführung von Befehlen auf entfernten (remote) Hosts. Diese sind rlogin, rsh und rcp. Alle führen eine Shell auf dem entfernten Host aus und erlauben dem Benutzer die Ausführung von Befehlen. Natürlich benötigt der Client einen Account auf dem Host, auf dem die Befehle ausgeführt werden sollen. Aus diesem Grund führen all diese Befehle eine Autorisierungsprozedur durch. Üblicherweise teilt der Client dem Server den Loginnamen des Benutzers mit, der wiederum ein Paßwort anfordert, das auf die übliche Weise kontrolliert wird.
Das Deaktivieren der Autorisierung ist nur bei einer kleinen Anzahl von Hosts ratsam, deren Paßwort-Datenbanken abgeglichen sind, oder bei einer kleinen Anzahl privilegierter Benutzer, die aus administrativen Gründen auf viele Maschinen zugreifen müssen. Wenn Sie Leuten das Login auf Ihr System ohne die Angabe eines Login-ID oder eines Paßworts gewähren wollen, müssen Sie darauf achten, nicht versehentlich jemand anderem den Zugriff zu ermöglichen.
$ rlogin -l joe euler
Der Server durchsucht zuerst hosts.equiv,(4) um zu sehen, ob Janet freier Zugang gewährt werden soll. Schlägt dies fehl, wird versucht, sie in .rhosts im Home-Verzeichnis von joe zu finden.
gauss
euler
-public
quark.physics.groucho.edu andres
Ein Eintrag besteht aus dem Hostnamen, dem optional ein Benutzername folgt. Besteht der Eintrag nur aus dem Hostnamen, ist allen Benutzern dieses Host der Zugang zu diesem Host ohne weitere Prüfungen erlaubt. In unserem obigen Beispiel könnte sich Janet unter ihrem Account
janet einloggen, wenn sie von
gauss käme. Dasselbe würde für alle anderen Benutzer, mit Ausnahme von
root, gelten. Wenn Janet sich allerdings als
joe einloggen würde, müßte sie, wie gewöhnlich, das Paßwort eingeben.
gauss janet
Der erste Eintrag erlaubt
joe freien Zugriff, wenn er sich von
chomp.cs.
groucho.edu aus einloggt, hat aber keinen Einfluß auf die Rechte anderer Accounts auf
euler oder chomp. Der zweite Eintrag ist eine leicht abgewandelte Variante des ersten Eintrags und erlaubt janet den freien Zugriff auf Joes Account, wenn sie sich von
gauss aus einloggt.
Fußnoten
Kapitel 8
Kapitel 10