fragen stichworte

Wie kann man die PID des Prozesses ermitteln, der Pakete sendet (Netzwerkverkehr generiert)?

Vor einigen Wochen hatte ich ein Problem, bei dem ich die DNS-Adressen in einem großen Netzwerk mit etwa 300 Knoten geändert habe. Danach fragten einige Knoten weiterhin nach alten DNS-Servern, obwohl resolv.conf in Ordnung war und host/nslookup neue DNS-Server abfragte.

Beim Betrachten von tcpdump und dem Versuch, Anforderungen mit der iptables-Protokollierung aufzuzeichnen, bestätigte ich, dass einige der Hosts tatsächlich immer noch Abfragen an alte Nameserver senden.

Ich habe einen der Hosts aus der Produktion genommen und angefangen, Services/Stracing-Prozesse herunterzufahren, um den Täter herauszufinden.

Am Ende - es war der lldpd-Dämon, der die Nameserver beim Start offensichtlich zwischengespeichert hat und Änderungen in resolv.conf nicht bemerkt hat.

Meine Frage lautet also: Gibt es einen intelligenteren Weg, um herauszufinden, welcher PId bestimmte Art von Verkehr erzeugt? Ich habe es mit auditctl versucht, aber ohne großen Erfolg. CentOS 6 ist in Frage, aber wenn es eine Lösung für eine Linux-Distribution gibt, würde ich es schätzen.

antworten

Was ist los mit dem auditctl?

Sie würden es so machen

1) Definieren Sie Ihre Überwachungsregel, um sendmsg und sendto Systemaufrufe zu prüfen. Diese Systemaufrufe werden während der Namensauflösung verwendet.

auditctl -a exit,always -F arch=b64 -S sendmsg -S sendto -k send

2) Suchen Sie nun nach Ihren Prüfprotokollen. Sie können auf der Basis der Remote-DNS-IP hier

grep
ausearch -k send -i|grep -A2 "serv:53"

Im folgenden Beispiel sehen Sie, dass die Anwendung, die für den Systemaufruf verantwortlich war, dig

heißt
ausearch -k send -i|grep -A2 "serv:53"
type=SOCKADDR msg=audit(10/31/2016 15:24:56.264:176998) : saddr=inet host:172.16.0.23 serv:53 
type=SYSCALL msg=audit(10/31/2016 15:24:56.264:176998) : arch=x86_64 syscall=sendmsg success=yes exit=29 a0=14 a1=7fa1919f9ac0 a2=0 a3=7fa1919f9780 items=0 ppid=31729 pid=32047 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts5 ses=52 comm=dig exe=/usr/bin/dig subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=send


comm=dig exe=/usr/bin/dig

Hier können Sie unterscheiden, an welche Remote-DNS-Anfrage gesendet werden soll. Sie müssen also nur für einen bestimmten DNS-Host grep.

saddr=inet host:172.16.0.23 serv:53 

Oder noch besser - sehen Sie, welche DNS-Hosts verwendet werden (in diesem Beispiel habe ich nur einen)

ausearch -k send -i|grep "serv:53"|awk '{print $6}'|sort|uniq -c
      3 host:172.16.0.23

Und dann eingrenzen, welche Apps diese Hosts verwenden.

Edit 1: Eigentlich habe ich nur einen einfachen Ping an einen Host gemacht. Scheint, als würde sendmsg nicht immer verwendet. Folgendes sehe ich

socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.16.0.23")}, 16) = 0
gettimeofday({1477929832, 712018}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}])
sendto(4, "\3\326\1\0\0\1\0\0\0\0\0\0\tvkontakte\2ru\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
poll([{fd=4, events=POLLIN}], 1, 5000)  = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [62])                = 0
recvfrom(4, "\3\326\201\200\0\1\0\2\0\0\0\0\tvkontakte\2ru\0\0\1\0\1\300\f"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.16.0.23")}, [16]) = 62
close(4)                                = 0

Mein vorheriges Beispiel basierte auf der dig-App, die in Bezug auf Systemaufrufe eine etwas andere Route darstellt.
In den meisten Fällen sieht es also so aus, als wäre diese Regel

auditctl -a exit,always -F arch=b64 -S connect -k connect

Gefolgt von ausearch

ausearch -k connect -i|grep saddr|grep "serv:53"|awk '{print $6}'|sort|uniq -c

Es gibt viele Optionen für netstat, die Kombinationen von Listening/open sockets über tcp/udp/both anzeigen. Etwas wie:

$> sudo netstat -pan
Active Internet connections (servers and established)
Proto  Recv-Q Send-Q Local Addr            Foreign Addr           State       PID/Program name
...
tcp    0      1      192.168.66.1:39219    192.168.66.139:2003    SYN_SENT    2045/logstash-forwa

... hätte Ihnen eine Menge ausgegeben, aber die Quelle, das Ziel, die Portnummern und die PID des Prozesses, der diese Ports besitzt, enthalten.

Ich habe vor ein paar Tagen mit dem gleichen Problem gerungen und eine sehr einfache Methode entwickelt. Es basiert auf der Tatsache, dass der sendende Prozess darauf wartet, dass eine DNS-Antwort kommt, auf dem gleichen Port, an den er die Anfrage von gesendet hat:

  1. Ermitteln Sie den Quellport der ausgehenden DNS-Anfrage mit iptables -j LOG
  2. Verwenden Sie lsof -i UDP:<source_port>, um herauszufinden, welcher Prozess auf eine Antwort an diesem Port wartet.

Da die Antwort innerhalb von Millisekunden eintrifft, können Sie dies natürlich nicht manuell tun. Darüber hinaus gibt es auch im automatisierten Modus keine Garantie dafür, dass Sie das System abfragen können, bevor die DNS-Antwort eintrifft und der Sendeprozess abbricht. Deshalb konfiguriere ich vor dem Ausführen der obigen Schritte auch den Kernel-Traffic-Controller, zu verzögern ausgehende Pakete, die an einen bestimmten IP/Port gerichtet sind (unter Verwendung des tc -Moduls netem). Dies erlaubt mir, das Zeitfenster, das ich habe, um das System zu fragen, welche PID auf die DNS-Antwort wartet, auf dem Quell-UDP-Port, der in Schritt 1 erhalten wurde, zu kontrollieren.

Ich habe die obigen Schritte, einschließlich der tc Verzögerung, in einem kleinen Skript namens ptrap automatisiert (das ist eine allgemeinere Lösung, nicht auf DNS-Anfragen beschränkt, daher geeignet für die Erkennung von Prozessen mit jedes TCP/UDP-basierte Protokoll). Mit seiner Hilfe habe ich herausgefunden, dass in meinem Fall der Dienst, der den alten DNS-Server kontaktiert, sendmail war.

+1 für Dmitrys Antwort oben; das hat gut für mich funktioniert:

auditctl -a exit,always -F arch=b64 -F a0=2 -S socket -k SOCKET

Um die resultierenden Einträge zu sehen, grep die Protokolldatei für diese "-k" Zeichenfolge

grep SOCKET/var/log/audit/audit.log

Um nur die interessanten Felder zu erhalten,

grep SOCKET/var/log/audit/audit.log | \\
  cut -d' ' -f 4- | \\
  sed "s|^|@\\n|g;s| |\\n|g" | \\
  grep -E "^((exe|uid|comm)=|@)" | \\
  tr '\\n@' ' \\n' |\\
  sort -u

(Erklärung: cut -d '' -f 4 - -> hacke die Zeile in Felder mit Leerzeichen (-d '') als Trennzeichen, Zeige Felder vorletzter (4-))

(Erklärung: sed "s \ | ^ \ | @ \\ n \ | g; s \ | \ | \\ n \ | g" -> Zeile bearbeiten, vorangestellt "@" char-plus-newline bis Zeilenbeginn, ändere Leerzeichen in Zeilenumbrüche)

(Erklärung: grep -E "^ ((uid \ | comm \ | exe) = \ | @)" -> wie jedes Feld der ursprünglichen Zeile jetzt auf seiner eigenen Zeile ist, Wählen Sie die interessanten Felder aus: user-id, command, executable - und die Zeile start '@' char.)

(Erklärung: tr '\\ n @' '\\ n' -> jetzt nur die gewünschten Felder haben, die Zeilenumbrüche wieder in Leerzeichen und das vorangestellte '@' wieder in ein Neue Zeile (die die Felder in eine Zeile zusammenfügt)

(Erklärung: sort -u -> Zeilen sortieren, nur eindeutige Zeilen anzeigen)

gibt mir:

uid=0 comm="atop" exe="/usr/bin/atop" 
uid=0 comm="http" exe="/usr/lib/apt/methods/http" 
uid=0 comm="links" exe="/usr/bin/links" 
uid=0 comm="ntpdate" exe="/usr/sbin/ntpdate" 
uid=0 comm="ufdbguardd" exe="/usr/local/ufdbguard/bin/ufdbguardd" 
uid=1000 comm=536F636B657420546872656164 exe="/usr/lib/firefox/firefox" 
uid=1000 comm="clock-applet" exe="/usr/lib/mate-panel/clock-applet" 
uid=1000 comm="pool" exe="/usr/lib/mate-panel/clock-applet" 
uid=105 comm="http" exe="/usr/lib/apt/methods/http" 
uid=105 comm="https" exe="/usr/lib/apt/methods/https" 
uid=135 comm="unbound" exe="/usr/sbin/unbound" 
uid=13 comm="squid" exe="/usr/src/squid-4-master/src/squid" 
uid=1 comm="debsecan" exe="/usr/bin/python2.7"

Befehle, die Leerzeichen enthalten, werden in einer einfachen ascii-to-hex-Methode codiert (siehe audit_logging.c). Zum Dekodieren ersetzen Sie "FF" durch "& amp; #xFF;" und recode das von HTML zu ASCII:

grep SOCKET/var/log/audit/audit.log | \\
  cut -d' ' -f 4- | sed "s|^|@\\n|g;s| |\\n|g" | \\
  grep -E "^((exe|uid|comm)=|@)" | tr '\\n@' ' \\n' | \\
  sort -u  | sed "s|^[^=]*=||g;s| [^ ]*=| |g" | \\
  while read U C E ; do \\
    echo "$C" | grep -q '"' || \\
      { C=\\"`echo $C | sed "s|\\(..\\)|\\&#x\\1;|g" | recode h4..u8`\\" ; } ; \\
    echo "uid=$U comm=$C exe=$E" ; 
  done

(Erklärung: sed "s \ | ^ [^ =] = \ | \ | g; s \ | [^] = \ | \ | g" -> entferne den 'xxx =' Teil der Zeilen - zuerst: line-start (^) gefolgt von any-char-außer - '=' wird durch Leerzeichen ersetzt, dann Leerzeichen gefolgt von any-char-except- '' durch Leerzeichen ersetzt) ​​

(Erklärung: beim Lesen von UCE; do ... done -> Schleife über jede Zeile, Einlesen von jeweils drei Datenbits in U, C, E (Benutzer-ID, Befehl, ausführbare Datei) ))

(Erklärung: echo "$ C" \ | grep -q '"' \ | \ | -> teste das Befehlsfeld, um zu sehen, ob es ein doublequote enthält - wenn nicht ('\ | \ | ') dann mache folgendes:)

(Erklärung: {C = \\ "echo $C | sed "s|\\(..\\)|\\&#x\\1;|g" | recode h4..ascii \\";} -> drucke die Befehlszeichenfolge, bearbeite jedes Zeichenpaar 'FF' zu '& amp; #xFF; ', passiere dann gnu' recode ', um sie von HTML-Entitäten in ASCII-Zeichen umzuwandeln.)

(Erklärung: echo "uid = $ U comm = $ C exe = $ E" -> drucke die modifizierte Zeile aus)

Dies gibt mir Ausgabe (nur die decodierte Zeile):

uid=1000 comm="Socket Thread" exe="/usr/lib/firefox/firefox

/j

lsof wäre ein geeignetes Werkzeug, um einen bestimmten Port zu überwachen und die PID zu ermitteln, durch die Verkehr erzeugt wird. Zum Beispiel überwache ich hier auf einem Server den DNS/Domain-TCP-Port 53, damit ich feststellen kann, welche PID die DNS-Suche verursacht:

$ lsof -PniTCP:53 -r 1 | grep :53

Nun würde ich etwas curl -Verkehr an den DNS-Server senden:

$ curl -v telnet://192.168.3.182:53
* About to connect() to 192.168.3.182 port 53 (#0)
*   Trying 192.168.3.182...
* Connected to 192.168.3.182 (192.168.3.182) port 53 (#0)

Diese Art der Ausgabe wird vom obigen Befehl angezeigt:

curl    4953 root    3u  IPv4 197807920      0t0  TCP 192.168.23.48:50688->192.168.3.182:53 (ESTABLISHED)
curl    4953 root    3u  IPv4 197807920      0t0  TCP 192.168.23.48:50688->192.168.3.182:53 (ESTABLISHED)
curl    4953 root    3u  IPv4 197807920      0t0  TCP 192.168.23.48:50688->192.168.3.182:53 (ESTABLISHED)

Wie es funktioniert

Der obige Befehl, der auf Port 53-Verkehr achtet, funktioniert, indem er lsof in eine sich wiederholende Schleife setzt, die alle 1 Sekunde ausgeführt wird, -r 1. Dann weisen wir lsof an, nur über den Verkehr zu berichten, der Port 53, iTCP:53 verwendet. Der -Pn weist lsof an, Hostnamen und Ports als Zahlen und nicht als tatsächliche Namen anzuzeigen.

Wir verwenden dann ein grep, um die von lsof kommende Ausgabe zu lesen und die Ausgabe so zu filtern, dass nur der Portverkehr :53 angezeigt wird.

Die PID des Prozesses, der den Datenverkehr sendet, wird ebenfalls in der Ausgabe von lsof angezeigt. Die zweite Spalte zeigt die PID, 4953.

Es gibt atop. Es gibt ein Kernel-Modul (netatop) und einen Daemon, durch den atop die Netzwerknutzung nach Prozess verfolgt.

Sie sollten zuerst atop

installieren

So installieren Sie das Kernel-Modul. Dies gilt, wenn der Beitrag geschrieben wurde, er kann jedoch veraltet sein:

sudo apt install linux-headers-$(uname -r) make zlib1g-dev
wget https://www.atoptool.nl/download/netatop-2.0.tar.gz
tar xvf netatop-2.0.tar.gz
cd netatop-2.0
make
sudo make install
sudo modprobe -v netatop

Wenn Sie über systemd verfügen, erstellen Sie die Servicedatei netatopd.service in /etc/systemd/system/. Es würde enthalten:

[Unit]
Description=NetAtop Daemon

[Service]
Type=forking
ExecStart=/usr/sbin/netatopd

[Install]
WantedBy=multi-user.target

Jetzt können Sie den Dämon aktivieren:

sudo systemctl enable netatopd

So sehen Sie die Live-Nutzung pro Prozess im Netzwerk:

sudo atop -n

Um die Top 3 im Laufe des Tages netzintensiv anzuzeigen:

atopsar -N

man atopsar für weitere Optionen.