Odroid root auf RAID

Ich habe einen Odroid HC1 als Homeserver im Einsatz. Das Gerät hat einen SATA Port, an dem ich eine 2 TB Festplatte betreibe. Gebootet wird die Kiste aber zwangsweise von einer SD-Karte mit u-boot (universal bootloader).

Ich wollte aber das Root-Filesystem auf der Festplatte haben und es außerdem noch als RAID1, wobei die SD-Karte die zweite „Platte“ darstellt. Und so geht es:

Ubuntu herunterladen und installieren:

http://com.odroid.com/sigong/blog/blog_list.php?tag=ODROID-HC1

Achtung: Das Image auf der SD macht einen Resize der boot-Partition und schaltet den Odroid danach ab. Das ist kein Fehler! Einfach danach neu starten.

Wenn das System läuft, den folgenden Befehl ausführen:

cfdisk /dev/mmcblk1

droid_cfdisk_mmcblk1

Die Anzahl der Sektoren für /dev/mmcblk1p2 merken (hier: 14860289) und cfdisk beenden.

Jetzt die eingebaute Festplatte /dev/sda partitionieren, gerne wieder mit cfdisk (andere bevorzugen fdisk oder andere Partitionierer). Auf der Platte eine neue Partition sda1 anlegen, mit einer Größe, die auf keinen Fall größer ist (mehr Sektoren hat) als obige Partition /dev/mmcblk1p2! Ein paar Sektoren weniger ist kein Problem. Als Type Linux RAID auswählen.

Die Tools für Software RAID-verwaltung und rsync installieren:

apt-get install mdadm rsync

Jetzt ein RAID anlegen mit folgendem Befehl:

mdadm --create -l 1 -n2 /dev/md0 /dev/sda1 missing

Dieser Befehl erstellt ein RAID mit einer fehlenden Partition (degraded). Auf diesem erstellt man jetzt ein Filesystem:

mkfs.ext4 /dev/md0

Auf dieses Filesystem muss man nun das root-Filesystem kopieren. Das geht am besten mit rsync, das wir weiter oben installiert haben. Es empfiehlt sich, alle Dienste abzuschalten, die man nicht dringend braucht (wie etwa sshd). Allerdings ist das bei einer frischen Installation nicht wirklich relevant.

mount /dev/md0 /mnt
rsync -axv / /mnt/

Jetzt muss man das RAID als root filesystem in den bootloader eintragen und die fstab anpassen. Für beides brauchen wir die UUID des neuen RAIDs, das man mit folgendem Befehl erfährt.

root@odroid:/# lsblk -f
NAME        FSTYPE            LABEL    UUID                                 MOUNTPOINT
mmcblk1                                                                     
|-mmcblk1p1 vfat              boot     52AA-6867                            /media/boot
`-mmcblk1p2 ext4              odroid:1 44e7f5bc-1e93-0cab-a411-921f4d4edac0 /
sda                                                                         
|-sda1      linux_raid_member odroid:0 0e545e67-a0d3-f036-eb64-f787a908633d 
| `-md0     ext4                       de443d6e-0a07-418e-8e6e-dea29f77063f /mnt

In diesem Fall ist die UUID de443d6e-0a07-418e-8e6e-dea29f77063f. Diese muss in der Datei /mnt/etc/fstab angepasst werden (etwa mit vi oder nano). Die Datei sieht in unserem Fall so aus:

UUID=de443d6e-0a07-418e-8e6e-dea29f77063f / ext4 errors=remount-ro,noatime 0 1
LABEL=boot /media/boot vfat defaults 0 1

Jetzt muss U-Boot angepasst werden.

cd /media/boot/
cp boot.ini boot.ini.orig

Mit einem Editor die Datei boot.ini öffnen. Dort findet sich die Zeile

setenv bootrootfs "console=tty1 console=ttySAC2,115200n8 root=UUID=44e7f5bc-1e93-0cab-a411-921f4d4edac0 rootwait ro fsck.repair=yes net.ifnames=0"

Diese muss angepasst werden, damit sie so aussieht:

setenv bootrootfs "console=tty1 console=ttySAC2,115200n8 root=UUID=de443d6e-0a07-418e-8e6e-dea29f77063f rootwait rootdelay=10 ro fsck.repair=yes net.ifnames=0"

Man achte auch hier darauf, dass die UUIDs passen. Auf keinen Fall darf der Eintrag rootdelay=10 vergessen werden, da die SATA-Platte einen Moment zum Hochdrehen braucht!

Jetzt Daumen drücken und reboot tippen. Falls es nicht geklappt hat, kann man die SD-Karte in ein SD-Leser schieben und die boot.ini weiter oben wieder herstellen. Dann startet der Odroid wieder komplett von der SD-Karte.

Nach dem Reboot kommt die Kiste mit dem RAID als root filesystem wieder hoch. Mit einem df Befehl lässt sich das prüfen:

# df -h
Filesystem Size Used Avail Use% Mounted on
udev 930M 0 930M 0% /dev
tmpfs 200M 19M 181M 10% /run
/dev/md127 6.8G 1.9G 4.6G 29% /
tmpfs 998M 0 998M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 998M 0 998M 0% /sys/fs/cgroup
/dev/mmcblk1p1 128M 13M 116M 10% /media/boot
tmpfs 200M 0 200M 0% /run/user/0

Man beachte, dass sich das md Device geändert hat auf md127. Das ist nicht weiter schlimm, da wir mit UUIDs arbeiten. Ein Blick in /proc/mdstat zeigt, dass wir ein RAID haben, dem eine Partition fehlt:

# cat /proc/mdstat
Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10]
md127 : active raid1 sda1[0]
        7312880 blocks super 1.2 [2/1] [U_]
        bitmap: 5/8 pages [20KB], 65536KB chunk

Das ändern wir, in dem wir die SD-Partition dem RAID hinzufügen. Achtung! Ab hier gibt es kein Zurück mehr! Die SD wird überschrieben!

mdadm --manage --add /dev/md127 /dev/mmcblk1p2

Der Fortschritt des Syncs lässt sich mit watch verfolgen:

# watch cat /proc/mdstat

Personalities : [raid1] [linear] [multipath] [raid0] [raid6] [raid5] [raid4] [raid10]
md127 : active raid1 sda1[3] mmcblk1p2[2]
      7312880 blocks super 1.2 [2/1] [U_]
      [=============>.......]  recovery = 66.3% (4854592/7312880) finish=9.0min speed=4550K/sec

unused devices: 

Sobald der Sync abgeschlossen ist, ist das Setup fertig. Yay! Aber da wir hier ein RAID aus zwei von der Charakteristik her sehr unterschiedlichen Platten gebaut haben, ist es sinnvoll, die SD-Karte beim Lesen zu bevorzugen:

echo writemostly > /sys/block/md127/md/dev-sda1/state

Wenn es geklappt hat, sieht man das in /proc/mdstat:

md127 : active raid1 sda1[3](W) mmcblk1p2[2]
      7312880 blocks super 1.2 [2/2] [UU]

Über einen Kommentar von Leuten, die das auch probiert haben, würde ich mich freuen!

Advertisements
Odroid root auf RAID

„Let’s Encrypt“ mit vsftpd und anderen

Ich habe einen vsftpd laufen und der sollte auch ein Let’s Encrypt Zertifikat für sicheres FTP bekommen.

Ich habe einen vhost im Apache für die Domain angelegt, unter der ich den vsftpd erreichbar mache und dann über den certbot das Zertifikat erstellen lassen. Das klappt auch alles gut, aber der Cronjob für das Renewal der Certifikate startet bei einer Aktualisierung nur den Apache neu und nicht den vsftpd. Und jedes Mal den vsftpd mitneustarten ist ja auch unschön. Daher habe ich einen eigenen Cronjob dafür gebaut.

Aber erst mal die vsftpd config:

rsa_cert_file=/etc/letsencrypt/live/[MYDOMAIN]/fullchain.pem
rsa_private_key_file=/etc/letsencrypt/live/[MYDOMAIN]/privkey.pem
ssl_enable=YES
allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO
require_ssl_reuse=NO
ssl_ciphers=HIGH

Diese Zeilen einfach am Ende hinzufügen und den vsftpd neu starten.

Und das ist der dazugehörige Cronjob.

1 6,18 * * * /usr/local/sbin/certbot-auto certonly --webroot -n -d [MYDOMAIN] --post-hook "/etc/init.d/vsftpd restart" >/dev/null 2>/dev/null

Ich habe dafür gesorgt, dass er 5 Minuten vor dem regulären Cronjob läuft, damit dieses Zertifikat für [MYDOMAIN] auch tatsächlich von diesem Cron erneuert wird. Die Option -d kann auch mehrfach angegeben werden, falls das Zertifikat auf mehrere Domains lauten soll.
Das Plugin --webroot ist in meinem Fall ausreichend. Wenn man aber keinen eigenen Webserver betreibt, kann man auch mit --standalone einen temporären starten, der dann die Authentifizierungen gegenüber Let’s Encrypt übernimmt.

Ähnlich kann man das natürlich auch mit Zertifikaten für Mailserver und andere machen.

„Let’s Encrypt“ mit vsftpd und anderen

Let’s encrypt!

Ich habe meine Seiten auf meinem Debian/Apache Webserver mit SSL verschlüsselt und dabei auf die kostenlose CA Let’s Encrypt! zurückgegriffen.

Es gibt viele Wege, wie man diese CA benutzen kann, aber der certbot ist echt der Hammer. So einfach geht es:

cd /usr/local/sbin
wget https://dl.eff.org/certbot-auto
chmod 750 certbot-auto
certbot-auto --apache

Danach wird man durch alle Schritte geführt und kann inerhalb kürzester Zeit funktionierende SSL Zertifikate nutzen, die sich vollautomatisch installieren.

Die Zertifikate sind zwar nur drei Monate gültig, aber mit certbot installierte Zertifikate lassen sich über diesen automatisch aktualisieren, so dass man sich nie wieder drum kümmern muss. Ein Eintrag wie dieser im Crontab von root reicht:

1 6,18 * * * /usr/local/sbin/certbot-auto renew --post-hook "apache2ctl restart" &>/dev/null

Lasst uns gemeinsam das Internet sicherer machen und „Let’s encrypt!

letsencrypt
https://eliyah.co.il/ ist SSL gesichert!
Let’s encrypt!

Bandbreite anzeigen

Die aktuell genutzte Bandbreite eines Linux Systems wird ermittelt als übertragene Bytes pro Zeitabstand. Einen „aktuellen“ Wert zum Auslesen gibt es nicht, man muss also die Bandbreite aus zwei Deltawerten übertragener Bytes berechnen. Ich habe ein Script gefunden und etwas erweitert. Es zeigt die Bandbreite für alle Interfaces in Kbits und Mbits an. Das Script ist simpel gehalten und ohne Parameter, die irgend etwas steuern. Aber das kann man ja nachträglich einbauen. Wenn man will.

#!/bin/bash                                                                          

intervalo=2
info="/sys/class/net/"
cd $info
while true; do
for interface in eth*
do
  eval rx1$interface=`cat $info$interface/statistics/rx_bytes`
  eval tx1$interface=`cat $info$interface/statistics/tx_bytes`
done
  sleep $intervalo
for interface in eth*
do
  eval rx2$interface=`cat $info$interface/statistics/rx_bytes`
  eval tx2$interface=`cat $info$interface/statistics/tx_bytes`
done
clear
date
for interface in eth*             
do
  echo $interface
  echo ---- 
  rx1=$(eval echo \${rx1$interface})
  rx2=$(eval echo \${rx2$interface})
  tx1=$(eval echo \${tx1$interface})
  tx2=$(eval echo \${tx2$interface})
  echo RX: $((($rx2-$rx1)/($intervalo*1024))) Kbps
  echo RX: $((($rx2-$rx1)/($intervalo*1048576))) Mbps
  echo TX: $((($tx2-$tx1)/($intervalo*1024))) Kbps
  echo TX: $((($tx2-$tx1)/($intervalo*1048576))) Mbps
done
done
Bandbreite anzeigen

HTML emails mit mail.mailutils

Das Programm mail.mailutils (meist einfach mail) ist auf vielen Linux-Distributionen installiert. Man kann damit über die Shell Emails verschicken, was sehr praktisch ist für scripte und cron-jobs, die den Admin per Mail informieren sollen.

Manchmal will man auch mal ein wenig mit HTML spielen und sei es auch nur, um um den Text ein <pre> zu setzen, damit er mit einer Monospace-Schrift angezeigt wird und ASCII-Art und Tabellen ordentlich dargestellt werden.

So schickt man HTML:

# generiere random boundary
BOUNDARY=$(date | md5sum | head -c8)
echo "
--_$BOUNDARY_
Content-Type: text/html; charset=\"us-ascii\"
Content-Transfer-Encoding: quoted-printable

<html><pre>
Meine Tabelle

+-------+----------+
| id    | analysis |
+-------+----------+
| 36190 |      219 |
| 36192 |      219 |
| 36273 |      219 |
| 36275 |       -1 |
| 36276 |       -1 |
| 36289 |      219 |
| 36293 |      122 |
| 36371 |      219 |
| 36388 |      219 |
| 36393 |      219 |
| 36394 |       -1 |
| 36395 |       -1 |
| 36708 |      122 |
+-------+----------+
</pre></html>
--_$BOUNDARY_--

" | mail.mailutils -s "Mein Subject" \
-a Content-Type:"multipart/alternative; boundary="\"_$BOUNDARY_\"" \
-a From:mein@absender.tld mein@empfaenger.tld

Diese Tabelle sieht jetzt sauber aus, wenn sie den Empfänger erreicht. Die Leerzeilen vor und nach dem Boundary und/oder Headern sind wichtig!

Wenn ich ein Bild in das HTML einbetten will, wird es komplizierter. Das mail-Tool kann zwar mit -A Anhänge verschicken, aber die kann ich dann nicht im HTML nutzen, da ich die dazugehörige ID nicht kenne.
Also muss ich das händisch machen, damit ich alle Parameter kontrollieren kann. Da Email ein 7-Bit Medium ist, muss ich aus dem Binär-Bild ASCII machen und dann noch dem Mailprogramm erklären, wie es das ASCII wieder in eine Binär-Bilddatei zurückverwandelt. Ich benutze hier base64 dafür, da das standardmässig installiert ist und so ziemlich jedes Mailprogramm das versteht. Ausserdem muss ich dem Bild einen Namen geben, denn der Dateiname geht verloren beim encoden in base64, und dazu eine ID, mit der ich darauf verweisen kann im HTML-Code der Mail. So gehts:

# generiere random boundary und Image ID
BOUNDARY=$(date | md5sum | head -c8)
IMAGEID=$(date | md5sum | head -c10 | rev)
# meine Bilddatei
IMAGEFILE=graph.png
( echo "
--_$BOUNDARY_
Content-Type: text/html; charset=\"us-ascii\"
Content-Transfer-Encoding: quoted-printable

<html>
Mein Graph als Bild:<br>
<img contenttype=3D\"image/png\" src=3D\"cid:$IMAGEID\"><br>
Sieht gut aus!

</pre></html>

--_$BOUNDARY_
Content-Type: image/png;name=\"$IMAGEFILE\"
Content-Disposition: attachment;filename=\"$IMAGEFILE\"
Content-ID: <$IMAGEID>
Content-Transfer-Encoding: base64 
"
base64 $IMAGEFILE 
echo "
--_$BOUNDARY_--" ) |  mail.mailutils -s "Mein Subject" \
-a Content-Type:"multipart/related; boundary="\"_$BOUNDARY_\""; type="\"multipart/alternative\""" \
-a MIME-Version:"1.0" \
-a From:mein@absender.tld mein@empfaenger.tld

Es gibt ausser mail.mailutils noch viele andere Commandline-Mailer wie etwa mutt oder mailx. Mit Hilfe der Beispiele oben kann man auch diese Mailer dazu bringen, HTML zu verschicken. Einfach mal gucken, welche Tools auf dem System installiert sind. Auch OS X kann das.

HTML emails mit mail.mailutils

DNS und TTL

Ich kann ja verstehen, dass Google und auch OpenDNS mit ihren öffentlich zugänglichen Resolvern sich nicht so ganz an die Regeln halten (siehe unten). Sie behaupten zwar das Gegenteil:

Does Google Public DNS comply with the DNS standards set forth by the IETF?
Yes.

Aber so ganz stimmen tut das meines Erachtens nicht. Ich musste einen Dienst von einem Server auf einen anderen umziehen. So bin ich vorgegangen:

  1. Am Tag vorher TTL auf 300 Sekunden (5 Minuten) für den A record heruntersetzen und Seriennummer aktualisieren
  2. Am Tag der Umschaltung auf den Webservern (um die geht es hier) eine Baustellenseite einrichten
  3. Den DNS record umstellen, Zone neu laden und auch auf dem secondary einen Zonenrefresh anstossen
  4. Prüfen, dass die beiden autoritativen DNS Server richtig reagieren
  5. warten, dass 8.8.8.8 endlich auch die neuen Daten liefert

Dummerweise hatte ich vergessen, noch einen Hostnamen für den alten Server einzurichten, also alt.meine-domain.tld, damit ich dort noch Zugriff auf die Seite habe. Also:

  1. DNS Eintrag für A record alt eintragen

Mein SOA sieht so aus:

;; ANSWER SECTION:

meine-domain.tld. 86400 IN SOA adns1.eu-rack.com. ns.eu-rack.co.il. 2016081801 28800 7200 604800 86400

Der A record für www so:

;; ANSWER SECTION:

www.meine-domain.tld. 300 IN A 11.12.13.14

Passt also. Aber: 8.8.8.8 macht aus den 300 Sekunden eine Zufallszahl kleiner 300. Und sowohl der neue A record Eintrag für alt. und der veränderte A record für www. wurden erst nach über vier Stunden aktiv! Dabei habe ich auf der von Google bereitgestellten URL den Cache „geflushed“, wie sie es nennen: https://developers.google.com/speed/public-dns/cache

Vier Stunden passen mit keinem der Zeiten in meinem SOA zusammen. Dort gibt es 24h, 2h und noch andere, aber keine 4h. Alles ziemlich doof. Der Grund ist wohl, dass sie damit effektiv DNS tunneling und DNS DDoS Angriffe vereiteln. Aber dass es mir so das Leben zur Hölle macht, ist doch auch nicht wirklich cool.

iptables auf dem alten Server hat mir dann den Arsch gerettet. Alle Anfragen auf die IP werden weiter geleitet. Das ging aber nur, weil ich eine eigene IP für diese Webseite hatte und keine anderen Vhost darüber liefen:

echo "1" > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A PREROUTING -d [OLD_IP] -p tcp --dport 80 -j DNAT --to-destination [NEW_IP]:80
iptables -t nat -A PREROUTING -d [OLD_IP] -p tcp --dport 443 -j DNAT --to-destination [NEW_IP]:443
iptables -t nat -A POSTROUTING -j MASQUERADE

Als Google dann sich endlich erbarmt hat, habe ich sie wieder gelöscht mit

iptables -t nat --flush

Den Eintrag für alt. konnte ich natürlich in der Zeit vergessen.

Gibt es eine Möglichkeit, dafür zu sorgen, dass das beim nächsten Umzug nicht wieder passiert? Wie bekomme ich Google und OpenDNS dazu, sich so zu verhalten, wie es sich gehört?

DNS und TTL