systemd & if-up.d

Metal_Warrior

Defender of Freedom
Teammitglied
Registriert
10 Aug. 2013
Beiträge
6.061
Ort
/dev/mapper/home
Hallo zusammen,
jetzt hab ich mal ein interessanteres Problem:
Ich hab eine etwas umfangreiche Firewall hier auf einem Server laufen, die aus ca. 20 Skripten in /etc/network/if-up.d/ aufgebaut wird. Die läuft auch tadellos; wann immer ich was geändert haben möchte, schreib ich die Änderung in das jeweilige Script und starte einen der Netzwerkadapter mittels
[src=bash]sudo ifdown ethX && sudo ifup ethX[/src]
neu. Das liest mir dann hübsch die Scripte der Reihe nach ein, wie es halt der Sinn und Zweck von if-up.d ist. Das lief auch unter Wheezy und Squeeze wunderbar, nur Jessie macht Probleme beim Boot. Wann immer ich den Server mal neu starte, vergisst systemd beim Start der Adapter, dieses Directory einzulesen und die Skripte darin auszuführen, ergo startet mir die Firewall nicht. Logge ich mich dann lokal ein und restarte einen der Adapter mittels obigem Befehl, läuft die Sache wieder.

Hat irgendeiner ne Ahnung, ob das Verhalten von systemd so gewollt ist oder wie man systemd beibringen kann, dass es nach dem Boot/Start von Netzwerkadaptern das Skript starten soll? Das Problem selbst haben wohl schon mehrere gehabt, aber ich hab noch keine befriedigende Antwort gefunden, wie man derartige Hooks über systemd wieder ans Laufen bekommt...
 
Zuletzt bearbeitet:
du könntest ein systemd service folgender maßen schreiben

[src=bash][Unit]
Description=run ifup on boot
Before=network.target

[Service]
User=root
Type=oneshot
ExecStart=/sbin/ifup ethX

[Install]
WantedBy=network.target
[/src]
das ganze dann in z.B. in /etc/systemd/service/ifup.service speichern

WantedBy sorgt dafür, das es immer ausgeführt wird wenn das network target gestartet wird und Before sollte™ dafür sorgen, dass es ausgeführt wird bevor die daemons gestartet werden.
 
  • Thread Starter Thread Starter
  • #3
So, ich musste den Service etwas anders schreiben, aber er läuft.
[src=bash][Unit]
Description=run ifup on boot
After=network.target

[Service]
User=root
Type=simple
RemainAfterExit=yes
ExecStartPre=/sbin/ifdown eth1
ExecStart=/sbin/ifup eth1
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
[/src]
Was mir beim mehrfachen Booten jetzt aufgefallen ist, dass der Start der Netzwerkadapter ziemlich lang dauert, und dann hab ich mich mal eingewurstelt. Ergebnis war für mich etwas erstaunlich:
[src=bash]Feb 16 20:58:44 srv systemd[1]: Starting ifup for eth2...
Feb 16 20:58:44 srv systemd[1]: Started ifup for eth2.
Feb 16 20:58:44 srv systemd[1]: Starting ifup for eth3...
Feb 16 20:58:44 srv systemd[1]: Started ifup for eth3.
Feb 16 20:58:44 srv systemd[1]: Starting ifup for eth1...
Feb 16 20:58:44 srv systemd[1]: Started ifup for eth1.
Feb 16 20:58:44 srv systemd[1]: Starting ifup for eth0...
Feb 16 20:58:44 srv systemd[1]: Started ifup for eth0.
Feb 16 20:58:44 srv ifup[1519]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1519]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1517]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1519]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1519]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1519]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1519]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1522]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1517]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1517]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1519]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1519]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1519]: iptables: Chain already exists.
Feb 16 20:58:44 srv ifup[1517]: iptables: Chain already exists.
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1516]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1522]: Another app is currently holding the xtables loc
Feb 16 20:58:44 srv ifup[1517]: iptables: Chain already exists.[/src]
...und so geht das fröhlich 113 Sekunden weiter. Daher läuft auch hinterher die Firewall nicht, weil die während des Aufbaus am Beginn jeder Table ein REJECT ALL-Statement drin hat, das am Ende wieder raus gekickt wird. Da aber anscheinend das Skriptset 4x nahezu zeitgleich ausgeführt wird, werden am Ende 4 x-beliebige Statements gelöscht, und eines der nachfolgenden Statments ist halt dann wieder ein REJECT.

Also ist das Problem ein anderes: Systemd nutzt das alte Init-System weiter, arbeitet auch korrekt die ganzen if-up.d-Skripte ab, aber halt parallel. Und das ist das Problem. Jetzt hab ich drei Möglichkeiten: Entweder, ich versuche irgendwie, die ifup-Runs zu serialisieren, oder ich bau Prüfstatements in die Skripte, oder ich pack die Firewall woanders hin und nutze den gerade erstellten Service, um die Firewall zu starten. Ich glaube, es wird Letzteres, da offenbar die anderen Skripte, die standardmäßig in if-up.d liegen, damit kein Problem haben.
 
Also ist das Problem ein anderes: Systemd nutzt das alte Init-System weiter, arbeitet auch korrekt die ganzen if-up.d-Skripte ab, aber halt parallel. Und das ist das Problem. Jetzt hab ich drei Möglichkeiten: Entweder, ich versuche irgendwie, die ifup-Runs zu serialisieren, oder ich bau Prüfstatements in die Skripte, oder ich pack die Firewall woanders hin und nutze den gerade erstellten Service, um die Firewall zu starten. Ich glaube, es wird Letzteres, da offenbar die anderen Skripte, die standardmäßig in if-up.d liegen, damit kein Problem haben.



Kann es gerade nicht testen, aber das dürfte vermutlich nicht pauschal an systemd liegen sondern eine Extrawurst von Debian sein.
 
  • Thread Starter Thread Starter
  • #5
@mathmos: Mir ist schon bewusst, dass das wohl ne Debian-eigene Wurst ist - der Nachteil an einem hochkonservativen System ist halt, dass auch uralt-Konfigs laufen sollen, daher kann man entweder Porter schreiben, die SysInitV-Skripte nach systemd umschreiben, was wahrscheinlich selten sauber läuft, oder ein Semi-Init als Subprozess von Systemd anstoßen. Dass das manchmal nicht läuft, kreide ich den Jungs jetzt nicht an, war halt doof.

Also, wie hab ichs jetzt gelöst:

Die Firewall hab ich in ein eigenes Subdirectory in /etc/network gepackt, die Service-Unit hab ich umgeschrieben. Sie startet nun ein Skript, das nichts anderes macht, als alle Skripte der Reihe nach auszuführen:

[src=bash][Unit]
Description=Setup for firewall
After=network.target

[Service]
User=root
Type=simple
RemainAfterExit=yes
ExecStart=/etc/network/firewall/0-fw00-setup
ExecReload=/etc/network/firewall/0-fw00-setup
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target[/src]

Allerdings hab ich nun natürlich das Problem, dass bis zu einem Run des Skripts möglicherweise ne halbe Sekunde oder länger die Tore offen sind. Das hab ich gelöst, indem ich das erste Skript der Firewall etwas zusammen gekürzt in /etc/network/if-up.d kopiert habe:
[src=bash]#!/bin/sh
# Blocking all connections until firewall setup is taking over


# ==== Cleanup & preparing chains ====

# Deleting existing rules
echo 0 > /proc/sys/net/ipv4/ip_forward
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X

# Block all traffic
iptables -A INPUT -j REJECT
iptables -A OUTPUT -j REJECT
iptables -A FORWARD -j REJECT


# ==== End of execution ====
exit 0[/src]
Das läuft anscheinend schnell genug durch, um keine größeren Schwierigkeiten mit seinen parallel gestarteten Schwesterinstanzen zu produzieren. Wenns blöd läuft, steht halt das REJECT-Statement für die FORWARD-Chain doppelt drin, ist auch egal. Kurze Zeit später wird eh der Hauptprozess für die Firewall gestartet, dann wird das eh alles wieder gelöscht und sauber aufgebaut.

...und das führt uns nun zu einer Startup-Time von:
[src=bash]Startup finished in 1.819s (kernel) + 22.106s (userspace) = 23.925s[/src]

Und damit ist alles gut.
 
  • Thread Starter Thread Starter
  • #6
Ich weiß, Nekrophilie, aber ich hab noch nen Nachtrag, für die richtig harten Nerds.

Das obige Setup hat einen Nachteil, den ich jetzt grad beim Einrichten eines weiteren Adapters festgestellt hab: Startet der Adapter mit ifup, ist sofort der Server dicht. Grund: Das Mini-Firewall-Blockscript startet ja nicht mehr die Firewall. Ist blöd, vor Allem, wenn man im laufenden Betrieb einzelne Adapter neu starten muss. Also hab ich optimiert, und das Ergebnis ist Folgendes:

[src=bash]#!/bin/bash
# Blocking all connections until firewall setup is taking over

# ==== Setup ====
# Lock file
FW_LOCK="/run/fw.lock"

# Write bash process ID to lock file
echo "$BASHPID" > "$FW_LOCK"

# ==== Cleanup & preparing chains ====

# Deleting existing forwarding
echo 0 > /proc/sys/net/ipv4/ip_forward

# Block all traffic
iptables -I INPUT -j REJECT
iptables -I OUTPUT -j REJECT
iptables -I FORWARD -j REJECT


# ==== Determination of priority & firewall startup ====`
# Wait until all adapters are presumably up and execute firewall setup if being
# last to the party
sleep 2
if [ "$BASHPID" = `cat "$FW_LOCK"` ]
then
# Delete lock file first
rm "$FW_LOCK"
# Restart firewall
service firewall restart
fi

# ==== End of execution ====
exit 0[/src]

Vier Dinge:
  1. Das Script piped seine PID jetzt in eine Lockfile, das letzte Script wird damit automatisch das Zepter übernehmen
  2. Ich flushe nicht mehr alle Tables, das muss auch nicht sein. Stattdessen füge ich die REJECTs jetzt direkt am Anfang ein, vor allen anderen Statements. Spart nur minimal Zeit, aber warum nicht.
  3. Nur noch das Script, welches seine PID in der Lockfile findet, startet den Firewall-Service neu. Neustart deshalb, weil ein Start bei laufendem Firewall-Service ignoriert wird, ein Restart bei nicht laufendem Service aber Startfunktion hat.
  4. Die zwei Sekunden Wartezeit sind eine Gracetime - das Firewall-Setup dauert normal unter einer Sekunde, könnte aber theoretisch länger brauchen (zukünfigt, bei größerer Nebenbelastung etc.). Außerdem könnte es sein, dass z. B. virtuelle Adapter wesentlich schneller/langsamer sind als reale, was dazu führen könnte, dass ein letzter Prozess die Lockfile schreibt, wohingegen der vorletzte die Lockfile schon gelöscht hat und gerade beim Firewallsetup ist. Der Vorletzte kann dann ganz gemütlich fertig machen, noch bevor der Letzte merkt, dass er ja der letzte ist und damit die Firewall neu starten muss. Im Extremfall wird also die Firewall innerhalb von 2 Sekunden zweimal gestartet, aber immer mit einem Abstand von {2 - Setupzeit} Sekunden, also nicht durcheinander. Wobei ich mir ehrlich gesagt nicht sicher bin, ob SystemD nicht clever genug ist, einen zweiten Restart während einem laufenden Restart zu ignorieren.

Jedenfalls kann ich jetzt beliebig Adapter neu starten, ohne mich hart auszusperren.:coffee::cool:
 
Zurück
Oben