Bash-Scripting: Benutzer inkl. Passwort migrieren

Der aktuelle Linux-Server muss neu aufgesetzt werden, weil entweder die Hardware zu alt ist, die Konfiguration neu aufgesetzt oder eine andere Linux-Distribution verwendet werden soll. Die Gründe sind zahlreich und unterschiedlich, aber immer wieder gilt die Anforderung, die vorhandenen Benutzer inklusive Passwort von dem alten auf den neuen Server zu migrieren.

Linux User inkl. Passwort mithilfe Auswahlliste migrieren

In diesem Artikel wird gezeigt, wie man Linux User inklusive Passwort migriert. Zusätzlich bringen wir etwas Komfort in den Migrationsprozess, indem man mittels dialog die Benutzer bequem selektieren kann. Somit werden nur die ausgewählten Benutzer auf den neuen Server migriert.

Einführung: wo werden Benutzer und Passwörter in Linux gespeichert

Auf einem Linux System befinden sich alle User in der Datei /etc/passwd. Pro Zeile werden die unterschiedlichen Informationen mit einem ":" getrennt, welche Angaben zum Username, Shell usw. enthalten.

 # nano /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
[...]
peter:x:1000:1000:peter,,,:/home/boss:/bin/bash
sshd:x:106:65534::/var/run/sshd:/usr/sbin/nologin
vboxadd:x:999:1::/var/run/vboxadd:/bin/false

Die dazugehörigen Passwörter findet man in der Datei /etc/shadow. Das Passwort ist natürlich verschlüsselt und nicht im Klartext zu sehen. Der Aufbau ist ähnlich zur /etc/passwd, nämlich mit ":" getrennt und es werden zusätzliche Infos wie Ablaufdatum dort hinterlegt.

nano /etc/shadow
root:$6$Zo8Hcgw6$2aZSh49...LwlfOtjce6Tv.:17281:0:99999:7:::
daemon:*:17281:0:99999:7:::
bin:*:17281:0:99999:7:::
sys:*:17281:0:99999:7:::
[...]
peter:$6$92pGq1ec$lAek1bUn...305apHq8TpthM63WqrN.:17281:0:99999:7:::
sshd:*:17512:0:99999:7:::
vboxadd:!:17594::::::

Die Migration ist also relativ einfach. Man muss lediglich die Einträge aus /etc/passwd und /etc/shadow auf den neuen Linux-Server kopieren. That's it.

Etwas aufwendiger und was in diesem Artikel nicht behandelt wird, ist die Migration der Gruppen. Die migrierten Benutzer werden auf dem neuen Linux-System keinen oder falschen Gruppen zugewiesen sein, da diese nicht vorhanden oder unter anderen IDs verfügbar sind.

Linux-User-Migrations-Skript - Theorie

Starten wir mit einem theoretischen Durchlauf, was das Skript können soll:

  • Einlesen der /etc/passwd
  • Benutzer in Dialog-Box anzeigen
  • Liste der ausgewählten Benutzer durchlaufen
  • Prüfe, ob Benutzer auf dem Zielsystem existiert. Wenn ja: skip.
  • Prüfe, ob zu dem Benutzer ein Eintrag in /etc/shadow (sprich Passwort) vorhanden ist. Wenn nein: skip.
  • Wenn Prüfung fehlerfrei, dann ...
  • ... kopiere Zeile aus /etc/passwd auf das Zielsystem
  • ... kopiere Zeile aus /etc/shadowd auf das Zielsystem

Praxis

Starten wir nun mit der Praxis und dem Einlesen bzw. Prüfen der /etc/passwd und /etc/shadow. Man speichert die genannten Files des alten Servers in ein beliebiges Verzeichnis auf dem Zielserver (in diesem Beispiel unter /home/develop/linux-user-migration). Der folgende Part prüft nun, ob die Dateien vorhanden sind und durchläuft alle Einträge aus /etc/passwd.

# Variable für Verzeichnis, wo die Dateien liegen
CONFIG_WORKING_DIR=/home/develop/linux-user-migration

# Prüfe, ob die Quell-Dateien passwd und shadow vorhanden sind
if [ -z "${CONFIG_WORKING_DIR}/etc/passwd" ] || [ -z "${CONFIG_WORKING_DIR}/etc/shadow" ]; then
    echo "Could not find user or password files (/etc/passwd or /etc/shadow)"
fi

#
# Jede Zeile aus /etc/passwd wird durchlaufen.
#

echo "read user and password"
userlist=""
n=1
while read -r pass ; do
# Extrahiere Benutzername
    username=$(echo ${pass} | cut -d":" -f1)

# Generiere Liste für Dialog Anzeige
    userlist="$userlist $username $n off"

    n=$[n+1]
done < <(cat ${CONFIG_WORKING_DIR}/etc/passwd)

Das Ergebnis ist eine Liste $userlist, welche wir für die Dialog-Box verwenden:

#
# Dialog-Fenster erzeugen mit der zuvor erstellten Liste $userlist
#

choices=$(dialog --stdout --checklist 'Select users which should be migrated.' 0 0 10 $userlist)

Der Anwender kann nun alle gewünschten User selektieren oder den Vorgang auch abbrechen. Im Skript wird die Eingabe wie folgt verarbeitet:

#
# Grundgerüst: verarbeite Auswahl Dialogbox
#

if [ $? -eq 0 ]
then
    for username in $choices
    do
    echo "tue was mit ${username}"
    done
else
    echo "cancel selected"
    exit 1
fi

Hat der der Anwender ein oder mehrere Linux-User zur Migration selektiert, beginnen wir mit einigen einfachen Prüfungen, damit bei der Migration fehlerhafte Datensätze kopiert werden:

#
# Prüfe, ob User auf Zielsystem existiert:
# - grep: suche nach Eintrag beginnend mit username:
# - speichere in Variable $checkHostPasswd
# - wenn vorhanden, dann continue = skip
#

checkHostPasswd=$(cat /etc/passwd | grep -E "^${username}:(.*?)")
if [ "$checkHostPasswd" != "" ]; then
    echo "User $username still exists on system. Skip."
    continue
fi

#
# Prüfe, ob Passworteintrag in Quell-Datei /etc/shadow
#

checkBackupShadow=$(cat ${CONFIG_WORKING_DIR}/etc/shadow | grep -E "^${username}:(.*?)")
if [ "$checkBackupShadow" == "" ]; then
    warn "No password found for $username. Skip."
    continue
fi

Waren alle Prüfungen erfolgreich, werden die Zeilen aus den alten Dateien passwd und shadow auf das neue Systeme kopiert. Das ist quasi der eigentliche Migrationsprozess.

#
# Kopiere Zeilen auf Zielsystem in /etc/passwd und /etc/shadow
#
echo ${checkBackupPasswd} >> /etc/passwd
echo "... user created."
echo ${checkBackupShadow} >> /etc/shadow
echo "... password set."

Anschließend sollte es möglich sein, sich mit den migrierten Usern auf dem neuen System anzumelden.

Das vollständige Skript

CONFIG_WORKING_DIR=/home/develop/linux-user-migration

echo "read user and password"
if [ -z "${CONFIG_WORKING_DIR}/etc/passwd" ] || [ -z "${CONFIG_WORKING_DIR}/etc/shadow" ]; then
    echo "Could not find user or password files (/etc/passwd or /etc/shadow)"
fi

userlist=""
n=1
while read -r pass ; do
    username=$(echo ${pass} | cut -d":" -f1)
    userlist="$userlist $username $n off"
    n=$[n+1]
done < <(cat ${CONFIG_WORKING_DIR}/etc/passwd)

choices=$(dialog --stdout --checklist 'Select users which should be migrated' 0 0 10 $userlist)
clear
echo ""
if [ $? -eq 0 ]
then
    for username in $choices
    do
        echo "migrating \033[0;36m$username\033[0m"

        checkHostPasswd=$(cat /etc/passwd | grep -E "^${username}:(.*?)")
        if [ "$checkHostPasswd" != "" ]; then
            echo "User $username still exists on system. Skip."
            continue
        fi

        checkBackupShadow=$(cat ${CONFIG_WORKING_DIR}/etc/shadow | grep -E "^${username}:(.*?)")
        if [ "$checkBackupShadow" == "" ]; then
            echo "No password found for $username. Skip."
            continue
        fi

        echo ${checkBackupPasswd} >> /etc/passwd
        echo "... user created."
        echo ${checkBackupShadow} >> /etc/shadow
        echo "... password set."
        usermod -g www-data -G ftp,www-data,users,ssh ${username}
        echo "...set groups."
    done
else
    echo cancel selected
    exit 1
fi