No advertising, no support, no bug fixes, payment in advance.
— AT&T Unix Policy (1984)

Traffic Analysis in pf

Für devnull-as-a-serivce hoste ich auf Port 9 über den inetd einen Discard Service. Aus historischen Gründen. Backwards compatibility. Letztens hatte ich bereits über ein paar Leute geschrieben, die sehr lange Connections zu Port 9 offen hielten. Über Monate. Aber wie viel Traffic haben diese Connections eigentlich verbraucht. Und überhaupt, welche Services verbrauchen auf meiner Kiste wie viel Traffic?

Seit ich Tor Relays auf meinen Maschinen betreibe habe ich darauf nochmal ein extra Augenmerk. Einfach um abwägen zu können wie viel Bandwith ich Tor zuweisen kann und wo ich Limits setzen muss. Zum Vergleich, Rechnung meines Providers vor und nach Tor Installation:

pf bietet viele verschiedene Möglichkeiten Traffic zu analysieren. Drei davon habe ich mir mal angesehen.

pflog

Das erste worauf man stößt ist pflog. Das Pseudo-Interface, von dem per default eines unter /dev/pflog0 existiert aber beliebig viele pflogX nachgeladen werden können.

Logging wird einfach in die spezielle Regel eingebettet.

1
pass in log (all, to pflog0) on $extif proto tcp from any to any port 9001

das sympathische daran ist, dass sowohl über Regeln als auch über den eigentlichen tcpdump der zum Auslesen des Logdevices benutzt wird, noch gefiltert werden kann.

1
2
3
4
$ tcpdump -n -e -ttt -i pflog0
tcpdump: WARNING: snaplen raised from 116 to 160
tcpdump: listening on pflog0, link-type PFLOG
Mar 16 09:38:10.163701 rule 38/(match) pass in on em0: 166.84.7.148.55675 > 185.34.0.188.9001: P 629511846:629512411(565) ack 110187399 win 605 <nop,nop,timestamp 131188075 3701794136>

Über mehrere Logdevices uns vordefinierte tcpdump Calls kann daraus ein ziemlich advancedes Setup entstehen.

pflow

pf ist außerdem im Stande Netflow Daten zu generieren. Das hört sich ziemlich overengineert an für eine einzelne Kiste. NetFlow wollt ich aber auch schon immer mal ausprobieren.

Kurz gesagt braucht man nur einen NetFlow Sensor und einen NetFlow Collector Zuerst installiert man einen Collector Dienst. Am einfachsten und minimalistischsten ist hier flowd, der von Damien Miller geschrieben wurde. Kann eigentlich nur gut sein. Ein paar Restrictions und das Ziel für das Logfile konfiguriert und fertig.

1
2
3
4
5
6
7
8
9
$ pkg_add flowd
$ cat /etc/flowd.conf
logfile "/var/log/flowd"
listen on 127.0.0.1:3001
flow source 127.0.0.1
store ALL
discard all
accept agent 127.0.0.1
$ flowd -gf /etc/flowd.conf

Der Sensor ist im Grunde ein weiteres Pseudo Interface, dass mit ifconfig eingerichtet wird. Die Parameter des Interfaces bestimmen Quelle und Ziel der Netflow Daten.

1
2
3
4
5
6
$ ifconfig pflow0 flowsrc 127.0.0.1 flowdst 127.0.0.1:3001
$ ifconfig pflow0
pflow0: flags=20041<UP,RUNNING,NOINET6> mtu 1492
        priority: 0
        pflow: sender: 127.0.0.1 receiver: 127.0.0.1:3001 version: 5
        groups: pflow

Aber ohne Infos aus pf hilft auch das schönste NetFlow Interface nichts. Wie bei pflog werden entweder bestimmte Rules via Parameter dazu bewogen die State Informationen an das pflow0 Interface zu schicken oder eben gleich alle States per default.

1
2
3
pass in on $extif proto tcp from any to any port 9001 keep state (pflow)
# or
set state-defaults pflow

Um die Daten dann auszulesen, lässt man das das mitgebrachte Tool flowd-reader auf das flowd Logfile los.

1
2
3
4
5
$ flowd-reader /var/log/flowd
FLOW recv_time 2015-03-16T10:28:07.883062 proto 6 tcpflags 00 tos 00 agent [127.0.0.1] src [188.226.189.53]:60608 dst [185.34.0.188]:9001 packets 12 octets 2862
FLOW recv_time 2015-03-16T10:28:07.883062 proto 6 tcpflags 00 tos 00 agent [127.0.0.1] src [185.34.0.188]:9001 dst [188.226.189.53]:60608 packets 8 octets 2727
FLOW recv_time 2015-03-16T10:28:45.982868 proto 6 tcpflags 00 tos 00 agent [127.0.0.1] src [93.115.94.243]:10361 dst [185.34.0.188]:9001 packets 10 octets 2647
FLOW recv_time 2015-03-16T10:28:45.982868 proto 6 tcpflags 00 tos 00 agent [127.0.0.1] src [185.34.0.188]:9001 dst [93.115.94.243]:10361 packets 8 octets 2631

Hier können mit Filter usw. spezifischer Traffic im definierten Output Format ausgelesen werden. Ziemlich nice.

pf labels

Die letzte (und mir sympathischste) Variante sind labels.

Hinter einer bestimmten Rule in der pf.conf kann entsprechendes Label angebracht werden.

1
pass in on $extif proto tcp from any to any port 9001 label tor

Nun werden Bytes(in/out), Packets(in/out), Statechanges usw. die auf dieser Regel matchen erfasst. Reihenfolge am Besten in der Manpage nachlesen.

1
2
3
4
5
$ pfctl -sl
ssh 22115 247697 74839158 130093 9043197 117604 65795961 1467
tor 17813 1329213 988230585 603981 291521453 725232 696709132 9923
monitoring 17813 54035 11840438 27936 5725257 26099 6115181 3145
discard 17813 4 200 2 120 2 80 2

Da die meisten Rulesets mit Tables für die Ports versehen sind, ist die Lösung mit dem statischen String als Label aber ungeeignet. Dafür gibts auch eine Lösung, da im Labelstring auch ein Set aus Variablen verwendet werden kann.

The following macros can be used in labels:
$dstaddr The destination IP address.
$dstport The destination port specification.
$if The interface.
$nr The rule number.
$proto The protocol name.
$srcaddr The source IP address.
$srcport The source port specification.

Die pf.conf könnte dann wie folgt aussehen.

1
2
3
4
5
6
# Port tables
tcpin = "{ ssh, smtp, domain, www, https, ftp, ftp-data }"
udpin = "{ 9, domain }"
# Incoming rule
pass in on $extif proto tcp from any to any port $tcpin label "tcp:in:$dstport"
pass in on $extif proto udp from any to any port $udpin label "udp:in:$dstport"

und der dynamisch generierte Output:

1
2
tcp:out:80 151 2099 1121133 1105 996878 994 124255 6
tcp:out:443 151 17524 14295556 9411 13062701 8113 1232855 35

Fazit

Was mir tatsächlich fehlt ist etwas das dynamische Auswerten von Source IP und verbrauchtem Traffic. Was pf zwar beherrscht ist einer bestimmten Regel eine Bandwith (über queues) zu Garantieren/Drosseln aber tatsächlich konsumierte Bandbreite limitieren funktioniert nicht so ohne weiteres.

Dazu müsste man sich schon etwas eigenes Basteln, das Traffic zählt und dann darauf reagiert. Erschwerend kommt hinzu, dass sich bei tcpdump kein tatsächliches Outputformat definieren lässt. Zum Beispiel nur SRC IP und Bytes. Wenn man danach im Netz sucht, existieren zwar allerhand wilde sed und awk konstrukte, aber nichts auf das ich mich wirklich verlassen möchte.

Also gibts das erstmal nicht.

Dwarf Fortress unter OS X Yosemite

Dwarf Fortress unter OSX Yosemite zum Laufen zu bekommen ist garnichtmal so einfach. Bisher hab ich immer direkt die Version von DF von den Entwicklern heruntergeladen. Diesmal die OS X Homebrew Version heruntergeladen. Die Probleme sind aber die gleichen.

Installation

Viel mehr als die Sources herunterladen und an eine bestimmte Stelle auspacken, wird bei der Brew Version auch nicht gemacht.

1
2
brew tap homebrew/games
brew install dwarf-fortress

Da Dwarf Fortress auf viel X11 und deren Libraries aufbaut, Xquartz installieren.

Fehlerbehebung

1
2
3
4
5
6
$ ./df
dyld: Library not loaded: /usr/X11R6/lib/libfreetype.6.dylib
  Referenced from:
    /usr/local/Cellar/dwarf-fortress/0.40.23/libexec/libs/SDL_ttf.framework/Versions/A/SDL_ttf
  Reason: no suitable image found.  Did find:
    /usr/local/lib/libfreetype.6.dylib: mach-o, but wrong architecture

Der obige Fehler entsteht, da die default Location der Libraries nicht mit Xquartz kommt. Daher diese umkopieren.

1
2
sudo mkdir /usr/X11R6
sudo cp -a /usr/X11/* /usr/X11R6/

Ein weiteres Problem gibt es allerdings mit Retina Displays. Die Standardauflösung bzw. der Darstellungsmodus ist dafür nicht gemacht.

Dafür noch den PRINT_MODE von 2D auf STANDARD umstellen.

1
2
3
4
$ vim /usr/local/Cellar/dwarf-fortress/0.40.23/libexec/data/init/init.txt
[...]
[PRINT_MODE:STANDARD]
[...]

Schon läufts.

OpenBL in pf

Das Projekt OpenBL, dass ursprünglich die OpenSSH Blacklist war, hat auf der ganzen Welt verteilte Honeypots rumstehen und publiziert regelmäßig Blacklisten sortiert nach Zeitraum und Protokoll des Angriffs.

Das Resultat daraus kann jeder frei benutzen und zum Beispiel in seine Firewall einbauen. Realisiert hab ich das über einen pf Table.

1
2
3
table <blacklist> persist
pass in quick from <admins>
block quick from <blacklist>

Die Regel ist relativ unspektakulär, abgesehen davon, dass die pass-Regel für die Admin IPs vor der Blacklist-Block Regel matchen sollte. Nur für den Fall dass man mal selbst auf so einer Liste landen sollte. Ein kleines Bash Script, dass mit Cron einmal die Woche die IPs updated.

1
2
3
4
5
6
7
8
9
#!/usr/local/bin/bash
STORE="/tmp/"
RULES="base.txt.gz"
TABLE="blacklist"
/usr/local/bin/wget -q https://www.openbl.org/lists/$RULES -P $STORE
gunzip $STORE/$RULES
pfctl -t $TABLE -T flush
pfctl -t $TABLE -T add -f $STORE/${RULES%.*}
rm $STORE/${RULES%.*}

Es gibt natürlich auch 10.000 andere Listen, die sowas bewerkstelligen. Darunter auch ganze Wrapper die solche Listen parsen. War mir aber dann etwas zu hart overengineert.

Digital Ocean FreeBSD

Kommentar eines Users zum Launch von FreeBSD bei DigitalOcean. Einfach nur w0rd.

Should have gone with OpenBSD instead to be honest. Half the requests on your UserVoice are for OpenBSD. All the coolest stuff in FreeBSD comes from OpenBSD.

OpenBSD – the world’s simplest and most secure Unix-like OS. Creator of the world’s most used SSH implementation OpenSSH, the world’s most elegant firewall PF, the world’s most elegant mail server OpenSMTPD, the OpenSSL rewrite LibreSSL, and the NTP rewrite OpenNTPD. OpenBSD – the cleanest kernel, the cleanest userland, the cleanest configuration syntax and some of the world’s best documentation.

FreeBSD, on the other hand, is becoming more of a testbed for experimental, some would even say unnecessary technologies: https://news.ycombinator.com/item?id=8546756 … It’s also having a hard time catching up to OpenBSD: http://itwire.com/business-it-news/open-source/62641-crypto-freebsd-playing-catch-up-says-de-raadt

Ziemlich interessante Diskussion in der Kommentarsektion. Weiterlesen lohnt.

Trotz allem, gute Sache. Mehr Provider sollten mal *BSD VMs anbieten. Wegen mangelnder Location in .de wirds wohl aber erstmal rootbsd.

leave

Die Tage bin ich auf ein über 30 Jahre altes Stück Software gestoßen, dass seinen Ursprung in NetBSD hat. leave ist klein, simpel und nützlich. Tools die aus Jahrzehnten stammen, wo man als Admin noch Multi-User Großrechner administirert hat und CLI Programme um das Arbeitsleben herum programmiert hat.

leave waits until the specified time, then reminds you that you have to leave. You are reminded 5 minutes and 1 minute before the actual time, at the time, and every minute thereafter. When you log off, leave exits just before it would have printed the next message.

Auch heute noch ist es in NetBSD und OpenBSD per default enthalten. Unter Debian nachinstallierbar.

Es kann auch etwas penetrant werden, jetzt endlich heim zu gehen.

In Zeiten in denen man mit am Retina Display mit 4 Splitwindows, >15 Terminaltabs und multiplen Ebenen von tmux-Sessions sitzt aber leider eher unpraktisch.

cmddocs

“Ich habe mein eigenes Wiki programmiert. So einfach ist das.”

In letzter Zeit hab ich damit den ein oder anderen seltsamen Blick kassiert. Tatsächlich ist aber alles etwas anders.

gitit

Ich war lange ein zufriedener User von gitit. Zugegeben, nur wenn es dann wirklich mal kompiliert war. Ich wollte nicht zum 3. mal nach neuem OpenBSD Release 4-5 Stunden verschwenden, das Stück Haskell gebaut zu bekommen. Außerdem war ich das Webtinterface irgendwie Leid.

Übergang

Also was will ich überhaupt? Ich will auf CLI markdown schreiben und die Files mit git unter Versionskontrolle halten. Möglichst einfach durchsuchen und bearbeiten können. Ich dachte okay, gitit speichert Markdown Files. Ich kopier einfach alles weg, lege mir in einem Verzeichnis auf meiner OpenBSD CLI Maschine an, in dem ich alles finde was ich brauche. Das passt super ins Konzept mit mutt, taskwarrior, jrnl, weechat und was ich sonst so verwende.

Auf längere Sicht wurde es aber ziemlich Tippintensiv. Jedes mal alle git add, git commit, grep, tree Commands nutzen. Es hat sich irgendwie müßig angefühlt. Hab nicht mehr gerne damit gerarbeitet, kam zu dem Schluss das ich auf Commandline bleiben, aber mir das dokumentieren/notieren etwas erleichtern will.

cmddocs

Zuerst machte ich mich auf die suche nach CLI-Wikis, aber die da sowas so gut wie garnicht zu existieren scheint, fing ich an mir ein kleines Commandline Interface mit Python zu bauen. Dazu benutzt hab ich das Cli Modul, was so gefühlt der heilige Gral der CLI Module ist. Tabcompletion, Helps, History, CTRL-R (unendlich wichtig), ESC-., ist alles schon drin.

Die Codebase von cmddocs war nicht besonders schön, aber tat das was ich wollte. Ich war erstmal zufrieden. Dann kam #31c3, ich traf posativ wieder, der mich erstmal darauf Hinwies, wie hässlich eigentlich. Nach seinen Tipps fing ich dann an das ganze etwas umzustrukturieren. Eigentlich wollte ich das gute Stück nie irgendwo publishen, aber durch die Änderungen sieht der Code jetzt doch einigermaßen annehmbar aus. Ein Github Repo+Readme angelegt und gepushed.

Demo hier: asciinema cmddocs demo (etwas outdated)

Es fehlen noch ein paar Features, der View-Mode bzw. der aus 6 Zeilen bestehende Markdown->ANSI Converter hat noch ein paar Problemchen, aber alles in Allem bin ich wirklich zufrieden was daraus geworden ist.

Router Advertisments in OpenBSD

Seit OpenBSD 5.6 hat sich die Konfiguration für IPv6 Router Advertisements geändert.

Bisher hat man über sysctl ein globales Flag eingeschaltet, das die automatische Konfiguration von IPv6 Adressen erlaubt. Das Flag enabled die Option aber automatisch für alle Interfaces. Deshalb wurde es entfernt und durch ein Flag in ifconfig ersetzt.

1
2
$ sysctl net.inet6.ip6.accept_rtadv=1
sysctl: fourth level name accept_rtadv in net.inet6.ip6.accept_rtadv is invalid

Die neue Konfiguration wird nun in /etc/hostname.em0 angezogen.

1
2
3
$ cat hostname.em0
dhcp
inet6 autoconf

Ist also pro Interface einzeln konfigurierbar. Bin darüber gestolpert, da mir das beim (etwas dürftig formulierten) Changelog nicht aufgefallen ist. Nach etwas Mailinglisten stöbern hier und da konnt ichs aber finden.

Danach noch ein sh /etc/netstart em0 und IPv6 geht wieder.

SMTPS OpenSMTPD

Mein Mailprovider ist mittlerweile ein halbes Jahr neomailbox.net. Seit einiger Zeit hatte ich aber Probleme beim Einliefern von Mails zum SMTP Server. Mutt resettet beim SMTP mit CRAM-MD5 über SSL immer wieder die Verbindung. Kein Einliefern möglich.

Eigentlich ist die .muttrc ziemlich straight-forward was das betrifft

1
2
3
4
5
set smtp_url="smtp://user@neomailbox.net"
set smtp_pass="PW"
set ssl_starttls = yes
set smtp_authenticators = "cram-md5"
set ssl_force_tls = yes

Debugging

Also erstmal openssl angeworfen um damit zu schauen was die Serverseite so erzählt. Zuvor aber Username und Password in BASE64 encodiert vorbereiten:

1
2
perl -MMIME::Base64 -e 'print encode_base64("passwort");'
perl -MMIME::Base64 -e 'print encode_base64("username");'

Dann Verbindung zum SMTPS aufbauen und bisschen Tippern.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
openssl s_client -connect 5.148.176.58:465
---
220 s3.neomailbox.net ESMTP
ehlo noc.n0q.org
250-s3.neomailbox.net Hello noc.n0q.org [127.0.0.1]
250-SIZE 52428800
250-PIPELINING
250-AUTH CRAM-MD5 PLAIN LOGIN
250-STARTTLS
250 HELP
AUTH LOGIN
334 VXNlcm5hbWU6
XYZABCDEFGHIJ
334 UGFzc3dvcmQ6
ABCDEFGHIJKLMNO
235 Authentication succeeded
MAIL FROM:example@example.org
250 OK
RCPT TO:example@example.org
RENEGOTIATING
depth=1 C = BE, O = GlobalSign nv-sa, CN = AlphaSSL CA - SHA256 - G2
verify error:num=20:unable to get local issuer certificate
verify return:0

Ich bin mir bis jetzt nicht sicher ob das renegotiaten nach MAIL FROM: normal ist. Danach war jedenfalls auch meine Plaintext-Session vorbei. Fand ich komisch. Ich dachte auch ob es vielleicht am LibreSSL des OpenBSD auf der Kiste liegt, die ich benutze. Ein Test mit Debian bewies aber dann das Gegenteil.

OpenSMTP als lokaler MTA

So konnte das ja auch nicht bleiben. Mails verschicken können wär schon schön. Da es sowieso ein OpenBSD ist, auf der mein mutt läuft war der OpenSMTPD schon da.

Was folgt ist eine kurze Anleitung, alle Mails an einen remote SMTP Server mit Authentifizierung weiterzuleiten.

Das secrets File erstellen

1
2
3
4
$ echo "neo user:pw" > /etc/mail/secrets
$ chown root:_smtpd /etc/mail/secrets
$ chmod 640 /etc/mail/secrets
$ makemap /etc/mail/secrets

und die smtpd.conf wie folgt anpassen:

1
2
3
4
5
listen on lo0
table aliases db:/etc/mail/aliases.db
table secrets db:/etc/mail/secrets.db
accept for local alias <aliases> deliver to mbox
accept for any relay via secure+auth://neo@neomailbox.net:465 auth <secrets>

Die .muttrc auf localhost umbiegen

1
set smtp_url="smtp://localhost:25"

Das hat jetzt nicht nur den Vorteil, dass ich wieder Mails versenden kann. Mir gefällt auch, dass ich jetzt bei eventualler nicht-Verfügbarkeit des Provider-SMTPs eine queuende Instanz habe. Ausserdem Logfiles in denen ich wirklich sehen kann wann eine Mail mein System verlassen hat. Negativ: Eine Komponente mehr die potentiell kaputt gehen kann.

Von gebundenen Ports die nicht hören wollen

Ich komme immer mal wieder in die Verlegenheit in der Arbeit solo zu benutzen. Um doppelte Läufe von Cronjobs zu verhindern beispielsweise.

Der betroffene Cronjob konnte nun aber schon mehrere Stunden nicht mehr ausgeführt werden.

1
2
./solo -port=3005 ./script.sh
solo(3005): Address already in use

Scheinbar läuft dieser Prozess noch, vermutlich ist er der eigentliche Prozess aber auf irgendeine Weise gestorben. Also machte ich mich auf die Suche nach dem Prozess, der den Port blockiert.

1
2
3
4
5
6
7
8
$ netstat -tapn | grep 3005
$ lsof -Pnl +M -i4 | grep 3005
$ fuser 3005/tcp
$ lsof -i :3005
$ socklist | grep 3005
$ cat /proc/net/tcp
$ cat /proc/net/udp
$ ss -pl |grep 3005

Man sieht schon, ich hab “ein bisschen was” versucht um das herauszubekommen. Ich habe schon an solo an sich gezweifelt, und danach angefangen zu verifizieren, ob der Port wirklich in Verwendung ist.

1
2
$ netcat -lvp 3005
retrying local 0.0.0.0:3005 : Address already in use

Ich habe auch mit strace mitgehört, was Perl da tut.

1
2
3
$ strace -o /tmp/foo ./solo -port=3005 "echo anfang ; sleep 5 ; echo ende"
[...]
> bind(3, {sa_family=AF_INET, sin_port=htons(3005), sin_addr=inet_addr("127.0.0.1")}, 16) = 0

Okay. Der Port wird verwendet. Bis mir der Unterschied zwischen bind() und listen() auf OS-Ebene wirklich klar wurde verging eine peinlich lange Zeit. Was bedeutet das innerhalb des Betriebssystems eigentlich. Ich kann diesen Port/Socket mit den oben genannten Tools garnicht finden. Weil er lediglich “gebunden” ist aber nicht “hört”, nicht für andere zur Kommunikation bereitsteht.

Das war auch so dieser Moment, in dem ich mir dachte dass mir ein paar Uni-Vorträge in Betriebssystem-Lehre wohl nicht geschadet hätten.

Die einzige Weise mit der ich den Prozess finden konnte war mit einem Hinweis aus dem Netz. Bei nicht erkennbarem Protokoll werden in lsof mit can't identify protocol gekennzeichnet.

1
2
$ lsof | grep identify
2727            root    3u     sock                0,6      0t0   41804186 can't identify protocol

Das war aber auch schon das einzige was mir einfiel. Problem hatte sich dann für mich erledigt. Prozess beseitigt. Funktionierte wieder. Sollte jemand aber einen Hint haben, wie man gezielter danach suchen kann, bitte her damit!