Migrar um contêiner LXC não privilegiado entre usuários

7

Eu tenho uma instalação do servidor Ubuntu 14.04 que atua como um host LXC. Tem dois usuários: user1 e user2.

user1 possui um contêiner LXC não privilegiado, que usa um diretório (dentro de /home/user1/.local / ...) como armazenamento de backup.

Como faço uma cópia completa do contêiner para o usuário2? Eu não posso simplesmente copiar os arquivos porque eles são mapeados com proprietários que variam de 100.000 a 100.000 + alguma coisa, que estão vinculados ao usuário1.

Além disso, o que acredito ser basicamente a mesma pergunta, como posso fazer um backup do contêiner LXC do meu usuário1 com segurança para restaurá-lo posteriormente em outra máquina e / ou usuário?

    
por agdev84 19.09.2014 / 21:41

3 respostas

2

Eu sei agora como fazer isso. Se você não pode seguir esta explicação, por favor, pergunte de volta, mas também certifique-se de ter lido as informações sobre as leituras que estou dando na parte inferior

Pressupostos preliminares

Eu vou ficar com as seguintes suposições, ampliadas do que eu tenho da sua pergunta:

  1. o host tem user1 e user2 , se uma informação não for específica de uma, usaremos userX
  2. o container será nomeado por uma variável que renderizaremos como $container
  3. as pastas base de user1 e user2 serão fornecidas na notação conhecida do Bash como ~user1 e ~user2 .
  4. presumiremos que os intervalos subordinados de UID e GID sejam 100000..165536 para user1 e 200000..265536 para user2 apenas por brevidade
  5. a pasta FS raiz para $container será processada como $rootfs , independentemente de onde ela será finalizada ( ~userX/.local/share/lxc/$container/rootfs )
  6. a configuração do contêiner é, por padrão, em ~userX/.local/share/lxc/$container/config

Mover o contêiner

Existem dois dados relevantes que governam os contêineres userns :

  1. proprietário e grupo para os arquivos / pastas das pastas que compõem o $container
  2. os UIDs e GIDs subordinados atribuídos em dois locais: /etc/sub{uid,gid} para a conta de usuário (manipulado por meio de usermod --{add,del}-sub-{uid,gid}s ) e lxc.id_map na configuração de $container ( ~userX/.local/share/lxc/$container/config ) respectivamente
    • Não sei ao certo se é possível definir intervalos diferentes na configuração do contêiner para cada contêiner. Por exemplo. se o usuário do host userX tiver 65536 GIDs e UIDs subordinados, talvez seja possível atribuir 5000 a 65 recipientes diferentes, mas não testei essa hipótese.
    • é certo, porém, que essa configuração comunica ao LXC quais são os intervalos válidos para GID e UID no namespace filho.

Portanto, a essência é que você precisa ter certeza de que o proprietário do arquivo / pasta e o grupo do container correspondem à configuração, que por sua vez deve ser um subconjunto válido dos GIDs / UIDs subordinados do host atribuídos a user1 e user2 , respectivamente.

Se você estiver usando o Bash, por exemplo, você pode usar $((expression)) para expressões aritméticas e let para atribuir expressões aritméticas a variáveis. Isso é muito útil se você souber um valor base (100000 e 200000, respectivamente) e o GID / UID para os usuários "internos".

Os principais pontos são:

  1. é possível
  2. ou o recurso CAP_CHOWN ou direitos de superusuário são necessários

Aqui está um script que provavelmente precisará de um pouco mais de aperfeiçoamento (exemplo: migração do container criado pela raiz para o sem privilégio), mas funciona para mim com o propósito:

#!/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 : "\ ~/$USERFROM/ {print \}" /etc/subgid)
readonly UIDBASEFROM=$(awk -F : "\ ~/$USERFROM/ {print \}" /etc/subuid)
readonly GIDSIZEFROM=$(awk -F : "\ ~/$USERFROM/ {print \}" /etc/subgid)
readonly UIDSIZEFROM=$(awk -F : "\ ~/$USERFROM/ {print \}" /etc/subuid)
readonly GIDBASETO=$(awk -F : "\ ~/$USERTO/ {print \}" /etc/subgid)
readonly UIDBASETO=$(awk -F : "\ ~/$USERTO/ {print \}" /etc/subuid)
readonly GIDSIZETO=$(awk -F : "\ ~/$USERTO/ {print \}" /etc/subgid)
readonly UIDSIZETO=$(awk -F : "\ ~/$USERTO/ {print \}" /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; }

Além dos termos de licença da rede do StackExchange, estou colocando isso no domínio público. Portanto, reutilize e modifique para qualquer finalidade, mas ela é fornecida sem garantia e não posso ser responsabilizado por seu uso ou abuso.

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

Ele assume find , sort , uniq , awk ( mawk e gawk devem funcionar), id , bash , chown , chmod e assim por diante estar disponível e entender todos os switches de linha de comando que está usando. Para Bash readonly e let e expressões aritméticas são assumidas como sendo compreendidas. Por find é assumido + é um terminador válido para a ação -exec .

Esta lista provavelmente não está completa.

Backups

Sim, você pode fazer backups e restaurá-los em outro lugar, desde que você também ajuste o proprietário do arquivo e o grupo de acordo.

No entanto, supondo que você use algo como tar , há uma ressalva: tar irá ignorar soquetes, então $rootfs/dev/log irá representar um problema - outros também podem criar um problema semelhante.

Recursos:

por 22.12.2014 / 03:20
1

fuidshift foi criado para essa finalidade. Parece ser parte do LXD.

link

    
por 10.04.2017 / 14:04
1

Você pode simplesmente copiar o diretório que contém o contêiner LXC de user1 para user2 e usar esse código python para alterar UIDs e GIDs:

#!/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)

Você precisará adaptar uidmap_start , gidmap_size e possivelmente também uidmap_size e gidmap_size às suas necessidades.

Eu usei este código python para migrar os contêineres LXC privilegiados para os não privilegiados. O código python é executado mais rápido que um script de shell.

    
por 21.10.2017 / 20:32