Migrieren Sie einen nicht privilegierten LXC-Container zwischen Benutzern

7

Ich habe eine Ubuntu 14.04-Serverinstallation, die als LXC-Host fungiert. Es hat zwei Benutzer: Benutzer1 und Benutzer2.

Benutzer1 besitzt einen nicht privilegierten LXC-Container, der ein Verzeichnis (in /home/user1/.local / ...) als Sicherungsspeicher verwendet.

Wie erstelle ich eine vollständige Kopie des Containers für Benutzer2? Ich kann die Dateien nicht einfach kopieren, da sie Besitzern zwischen 100000 und 100000 + zugeordnet sind, die an Benutzer1 gebunden sind.

Was meiner Meinung nach im Grunde die gleiche Frage ist, wie kann ich den LXC-Container meines Benutzers1 sicher sichern, um ihn später auf einem anderen Computer und / oder Benutzer wiederherzustellen?

agdev84
quelle
Verwenden Sie cpstatt mvmit den entsprechenden Flags, um alle Eigenschaften beizubehalten, die Sie in meinem Skript beibehalten müssen.
0xC0000022L

Antworten:

2

Ich weiß jetzt, wie das geht. Wenn Sie dieser Erklärung nicht folgen können, fragen Sie bitte zurück, aber stellen Sie auch sicher, dass Sie sich in den Lesungen, die ich unten gebe, über die Benutzer informiert haben

Vorläufige Annahmen

Ich werde mich an die folgenden Annahmen halten, die sich aus Ihrer Frage ergeben:

  1. Host hat ein user1und ein user2, wenn eine Information nicht spezifisch für eine ist, werden wir verwendenuserX
  2. Der Container wird durch eine Variable benannt, als die wir rendern $container
  3. Home-Ordner für user1und user2werden in der von Bash als ~user1und bekannten Notation angegeben ~user2.
  4. Wir gehen davon aus, dass die untergeordneten UID- und GID-Bereiche der Kürze user1halber 100000..165536 für und 200000..265536 sinduser2
  5. Der Root-FS-Ordner für $containerwird als gerendert $rootfs, unabhängig davon, wo er landen wird ( ~userX/.local/share/lxc/$container/rootfs)
  6. Die Containerkonfiguration ist standardmäßig in ~userX/.local/share/lxc/$container/config

Container bewegen

Es gibt zwei relevante Daten, die die usernsContainer regeln :

  1. Eigentümer und Gruppe für die Dateien / Ordner der Ordner, aus denen die $container
  2. die untergeordneten UIDs und GIDs, die an zwei Stellen zugewiesen wurden: /etc/sub{uid,gid}für das Benutzerkonto (manipuliert über usermod --{add,del}-sub-{uid,gid}s) bzw. lxc.id_mapin der $containerKonfiguration ( ~userX/.local/share/lxc/$container/config)
    • Ich weiß nicht genau, ob es möglich ist, für jeden Container unterschiedliche Bereiche in der Containerkonfiguration zu definieren. Wenn der userXHostbenutzer beispielsweise 65536 untergeordnete GIDs und UIDs hat, ist es möglicherweise möglich, 5000 bis 65 verschiedenen Containern zuzuweisen, aber ich habe diese Hypothese nicht getestet.
    • Es ist jedoch sicher, dass diese Einstellung mit LXC kommuniziert. Dies sind die gültigen Bereiche für GID und UID im untergeordneten Namespace.

So ist der Kern ist wirklich , dass Sie darauf achten müssen, dass die Datei / Ordner - Besitzer und die Gruppe für den Container die Konfiguration entsprechen, die wiederum eine gültige Teilmenge des Host untergeordneter GIDs / UIDs zugeordnet sein muss user1und user2jeweils.

Wenn Sie beispielsweise Bash verwenden, können Sie $((expression))arithmetische Ausdrücke verwenden und letVariablen arithmetische Ausdrücke zuweisen. Dies ist sehr nützlich, wenn Sie einen Basiswert (100000 bzw. 200000) und die GID / UID für die "internen" Benutzer kennen.

Die Hauptpunkte sind:

  1. es ist möglich
  2. Es sind entweder die Fähigkeits-CAP_CHOWN oder die Superuser-Rechte erforderlich

Hier ist ein Skript, das wahrscheinlich noch etwas verfeinert werden muss (Beispiel: Migration von einem vom Root erstellten Container zu einem nicht privilegierten), aber es funktioniert für mich zu diesem Zweck:

#!/usr/bin/env bash

function syntax
{
    echo "SYNTAX: ${0##*/} <from-user> <to-user> <container-name>"
    [[ -n "$1" ]] && echo -e "\nERROR: ${1}."
    exit 1
}

# Checks
[[ -n "$1" ]] || syntax "<from-user> is not set"
[[ -n "$2" ]] || syntax "<to-user> is not set"
[[ -n "$3" ]] || syntax "<container-name> is not set"
[[ "$UID" -eq "0" ]] || syntax "${0##*/}" "You must be superuser to make use of this script"
# Constants with stuff we need
readonly USERFROM=$1
readonly USERTO=$2
shift; shift
readonly CONTAINER=${1:-*}
LXCLOCAL=".local/share/lxc"
readonly HOMEFROM=$(eval echo ~$USERFROM)
readonly HOMETO=$(eval echo ~$USERTO)
readonly LXCFROM="$HOMEFROM/$LXCLOCAL"
readonly LXCTO="$HOMETO/$LXCLOCAL"
readonly GIDBASEFROM=$(awk -F : "\$1 ~/$USERFROM/ {print \$2}" /etc/subgid)
readonly UIDBASEFROM=$(awk -F : "\$1 ~/$USERFROM/ {print \$2}" /etc/subuid)
readonly GIDSIZEFROM=$(awk -F : "\$1 ~/$USERFROM/ {print \$3}" /etc/subgid)
readonly UIDSIZEFROM=$(awk -F : "\$1 ~/$USERFROM/ {print \$3}" /etc/subuid)
readonly GIDBASETO=$(awk -F : "\$1 ~/$USERTO/ {print \$2}" /etc/subgid)
readonly UIDBASETO=$(awk -F : "\$1 ~/$USERTO/ {print \$2}" /etc/subuid)
readonly GIDSIZETO=$(awk -F : "\$1 ~/$USERTO/ {print \$3}" /etc/subgid)
readonly UIDSIZETO=$(awk -F : "\$1 ~/$USERTO/ {print \$3}" /etc/subuid)
unset LXCLOCAL
# More checks
[[ -d "$LXCFROM" ]] || syntax "Could not locate '$LXCFROM'. It is not a directory as expected"
[[ -e "$LXCTO" ]] && syntax "Destination '$LXCTO' already exists. However, it must not"
for i in GIDBASEFROM UIDBASEFROM GIDBASETO UIDBASETO; do
    (($i > 0)) || syntax "Could not determine base/offset of subordinate UID/GID range"
done
for i in GIDSIZEFROM UIDSIZEFROM GIDSIZETO UIDSIZETO; do
    (($i > 0)) || syntax "Could not determine length of subordinate UID/GID range"
done

echo "Going to migrate container: $CONTAINER"
echo -e "\tfrom user $USERFROM ($HOMEFROM): subUID=${UIDBASEFROM}..$((UIDBASEFROM+UIDSIZEFROM)); subGID=${GIDBASEFROM}..$((GIDBASEFROM+GIDSIZEFROM))"
echo -e "\tto user $USERTO ($HOMETO): subUID=${UIDBASETO}..$((UIDBASETO+UIDSIZETO)); subGID=${GIDBASETO}..$((GIDBASETO+GIDSIZETO))"
while read -p "Do you want to continue? (y/N) "; do
    case ${REPLY:0:1} in
        y|Y)
            break;
            ;;
        *)
            echo "User asked to abort."
            exit 1
            ;;
    esac
done

# Find the UIDs and GIDs in use in the container
readonly SUBGIDSFROM=$(find -H "$LXCFROM" -printf '%G\n'|sort -u)
readonly SUBUIDSFROM=$(find -H "$LXCFROM" -printf '%U\n'|sort -u)

# Change group
for gid in $SUBGIDSFROM; do
    let GIDTO=$(id -g "$USERTO")
    if ((gid == $(id -g "$USERFROM"))); then
        echo "Changing group from $USERFROM ($gid) to $USERTO ($GIDTO)"
        find -H "$LXCFROM/$CONTAINER" -gid $gid -exec chgrp $GIDTO {} +
    elif ((gid >= GIDBASEFROM )) && ((gid <= GIDBASEFROM+GIDSIZEFROM)); then
        let GIDTO=$((gid-GIDBASEFROM+GIDBASETO))
        echo "Changing group $gid -> $GIDTO"
        find -H "$LXCFROM/$CONTAINER" -gid $gid -exec chgrp $GIDTO {} +
    else
        echo "ERROR: Some file/folder inside '$LXCFROM/$CONTAINER' has a group not assigned to $USERFROM (assigned subordinate GIDs)."
        echo -e "Use:\n\tfind -H '$LXCFROM/$CONTAINER' -gid $gid\nto list those files/folders."
        exit 1
    fi
done

# Change owner
for uid in $SUBUIDSFROM; do
    let UIDTO=$(id -u "$USERTO")
    if ((uid == $(id -u "$USERFROM"))); then
        echo "Changing owner from $USERFROM ($uid) to $USERTO ($UIDTO)"
        find -H "$LXCFROM/$CONTAINER" -uid $uid -exec chown $UIDTO {} +
    elif ((uid >= UIDBASEFROM )) && ((uid <= UIDBASEFROM+UIDSIZEFROM)); then
        let UIDTO=$((uid-UIDBASEFROM+UIDBASETO))
        echo "Changing owner $uid -> $UIDTO"
        find -H "$LXCFROM/$CONTAINER" -uid $uid -exec chown $UIDTO {} +
    else
        echo "ERROR: Some file/folder inside '$LXCFROM/$CONTAINER' has an owner not assigned to $USERFROM (assigned subordinate UIDs)."
        echo -e "Use:\n\tfind -H '$LXCFROM/$CONTAINER' -uid $uid\nto list those files/folders."
        exit 1
    fi
done
mv "$LXCFROM/$CONTAINER" "$LXCTO/" || { echo "ERROR: failed to move to destination: ${LXCTO}/${CONTAINER}."; exit 1; }

Zusätzlich zu den Lizenzbedingungen des StackExchange-Netzwerks stelle ich diese öffentlich zur Verfügung. Also wiederverwenden und modifizieren für jeden Zweck, aber es kommt ohne Garantie und ich kann nicht für seine Verwendung oder Missbrauch haftbar gemacht werden.

Verwendungszweck
SYNTAX: lxc-reassign-userns.sh <from-user> <to-user> <container-name>

Er geht davon aus find, sort, uniq, awk( mawkund gawksollte funktionieren) id, bash, chown, chmodund so weiter zur Verfügung zu stehen und alle Befehlszeile zu verstehen , schaltet es verwendet. Für Bash readonlyund letund arithmetische Ausdrücke wird angenommen, dass sie verstanden werden. Es findwird davon ausgegangen, dass +es sich um einen gültigen Terminator für die -execAktion handelt.

Diese Liste ist wahrscheinlich nicht vollständig.

Backups

Ja, Sie können Sicherungen erstellen und an anderer Stelle wiederherstellen, sofern Sie auch den Dateieigentümer und die Gruppe entsprechend anpassen.

Angenommen, Sie verwenden so etwas wie tar, gibt es eine Einschränkung: tarIgnoriert Sockets und $rootfs/dev/logwirft ein Problem auf - andere können ebenfalls ein ähnliches Problem verursachen.

Ressourcen:

0xC0000022L
quelle
Ehrlich gesagt hoffte ich auf einen eingebauten, gebrauchsfertigen Befehl. Es ist seltsam für mich, dass das lxc-Paket kein Paket liefert. Vielen Dank jedoch für die Zeit und Mühe, die Sie in das Schreiben einer so detaillierten Antwort und für das Teilen Ihres Codes gesteckt haben. Ich werde es testen, dann werde ich Ihnen Feedback geben.
Agdev84
@ agdev84: Ich schreibe es derzeit ein wenig um, um es auch zu ermöglichen, es von einem privilegierten in einen nicht privilegierten benutzerbasierten Container zu verschieben. Könnte aber in Python oder so enden.
0xC0000022L
2

Bearbeiten: Fuidshift ist der beste Weg, um es zu tun. In Ubuntu wird fuidshift nicht mehr ausgeliefert , da LXD von einem DEB-Paket in einen Snap konvertiert wurde, und dies wird auch nicht passieren. Sie müssen fuidshift selbst kompilieren .

Dies ist jedoch recht einfach (das Herunterladen des Quellcodes und das Kompilieren erfolgt fast automatisch), siehe https://github.com/lxc/lxc/issues/3186

- -

Sie können einfach das Verzeichnis mit dem LXC-Container von Benutzer1 nach Benutzer2 kopieren und diesen Python-Code verwenden, um UIDs und GIDs zu verschieben:

#!/usr/bin/python3

import os
import sys

uidmap_start = 100000
uidmap_size = 65536

gidmap_start = 100000
gidmap_size = 65536


def changeUidGidRecursive(path):
  changeUidGid(path)
  if os.path.isdir(path) and not os.path.islink(path):
    for filename in os.listdir(path):
      sub_path = os.path.join(path, filename)
      changeUidGidRecursive(sub_path)

def changeUidGid(path):
  stat_info = os.lstat(path)
  uid = stat_info.st_uid
  gid = stat_info.st_gid
  new_uid = uid + uidmap_start
  new_gid = gid + gidmap_start
  if (new_uid > uidmap_end):
    print("Info: New UID %d for \"%s\" would be out of range. Not changing UID." % (new_uid, path))
    new_uid = uid
  if (new_gid > gidmap_end):
    print("Info: New GID %d for \"%s\" would be out of range. Not changing GID." % (new_gid, path))
    new_gid = gid
  if (new_uid != uid or new_gid != gid):
    mode = stat_info.st_mode
    os.chown(path, new_uid, new_gid, follow_symlinks=False)
    new_mode = os.lstat(path).st_mode
    # If necessary, restore old mode
    if (new_mode != mode):
      os.chmod(path, mode)

if __name__ == '__main__':
  uidmap_end = uidmap_start + uidmap_size
  gidmap_end = gidmap_start + gidmap_size

  base_path = ''
  if len(sys.argv) > 1:
    base_path = sys.argv[1]
  else:
    print("Usage: %s <path>" % (sys.argv[0]))
    sys.exit(1)

  if not os.path.exists(base_path):
    print("Error: Path \"%s\" does not exist" % (base_path))
    print("Exiting")
    sys.exit(1)
  changeUidGidRecursive(base_path)
  sys.exit(0)

Sie müssen sich anpassen uidmap_start, gidmap_sizeund möglicherweise auch uidmap_sizeund gidmap_sizean Ihre Bedürfnisse.

Ich habe diesen Python-Code verwendet, um privilegierte LXC-Container auf nicht privilegierte zu migrieren. Der Python-Code wird schneller ausgeführt als ein Shell-Skript.

Harald Hetzner
quelle