사용자 간에 권한이 없는 LXC 컨테이너 마이그레이션

사용자 간에 권한이 없는 LXC 컨테이너 마이그레이션

LXC 호스트 역할을 하는 Ubuntu 14.04 서버가 설치되어 있습니다. user1과 user2라는 두 명의 사용자가 있습니다.

user1은 디렉터리(/home/user1/.local/... 내부)를 백업 저장소로 사용하는 권한 없는 LXC 컨테이너를 소유하고 있습니다.

user2에 대한 컨테이너의 전체 복사본을 만드는 방법은 무엇입니까? 이 파일에는 100000에서 100000+ 범위의 매핑된 소유자가 있고 해당 소유자는 user1에 바인딩되어 있으므로 복사할 수 없습니다.

또한 이것은 기본적으로 동일한 질문이라고 생각합니다. 나중에 다른 컴퓨터 및/또는 사용자에서 복원할 수 있도록 user1의 LXC 컨테이너를 어떻게 안전하게 백업합니까?

답변1

나는 지금 무엇을 해야할지 압니다. 이 설명이 이해되지 않으면 답장을 보내주시고 하단에 사용자에 대해 제가 제공한 내용도 꼭 읽어보시기 바랍니다.

예비 가설

귀하의 질문에서 확장된 다음 가정을 고수하겠습니다.

  1. user1호스트에는 a 및 a가 있습니다 user2. 정보가 특정 정보에 국한되지 않으면 사용할 것입니다.userX
  2. 컨테이너의 이름은 렌더링할 변수에 따라 지정됩니다.$container
  3. user1및 의 홈 폴더는 user2Bash에서 sum이라는 표기법으로 제공됩니다.~user1~user2
  4. 간결성을 위해 종속 UID 및 GID 범위가 100000..165536 user1및 200000..265536 이라고 가정합니다.user2
  5. 루트 FS 폴더는 끝 위치에 상관없이 $container로 렌더링됩니다 ( ).$rootfs~userX/.local/share/lxc/$container/rootfs
  6. 컨테이너 구성의 기본값은 다음과 같습니다.~userX/.local/share/lxc/$container/config

컨테이너 이동

컨테이너를 관리하는 데 관련된 두 가지 데이터가 있습니다 userns.

  1. 폴더를 구성하는 파일/폴더의 소유자 및 그룹$container
  2. /etc/sub{uid,gid}슬레이브 UID 및 GID는 사용자 계정( 작업을 통해 usermod --{add,del}-sub-{uid,gid}s)과 구성 ( ) lxc.id_map의 두 위치에 할당됩니다.$container~userX/.local/share/lxc/$container/config
    • 컨테이너 구성에서 각 컨테이너에 대해 서로 다른 범위를 정의하는 것이 가능한지 잘 모르겠습니다. 예를 들어 호스트 사용자가 userX65536개의 종속 GID 및 UID를 가지고 있는 경우 5000~65개의 서로 다른 컨테이너를 할당하는 것이 가능할 수 있지만 해당 가정을 테스트하지는 않았습니다.
    • 하지만 확실한 것은 이 설정이 하위 네임스페이스의 유효한 GID 및 UID 범위인 LXC와 통신한다는 것입니다.

따라서 중요한 점은 컨테이너의 파일/폴더 소유자 및 그룹이 구성과 일치하는지 확인해야 하며, 이는 각각 user1및 에 할당된 호스트 슬레이브 GID/UID의 유효한 하위 집합이어야 한다는 것입니다 user2.

예를 들어 Bash를 사용하는 경우 $((expression))산술 표현식에 사용하고 let산술 표현식을 변수에 할당할 수 있습니다. 이는 "내부" 사용자의 기본 값(각각 100000 및 200000)과 GID/UID를 알고 있는 경우 유용합니다.

주요 사항은 다음과 같습니다.

  1. 이것은 가능하다
  2. 어느 하나능력CAP_CHOWN또는 슈퍼유저 권한이 필요합니다.

이는 더 많은 개선이 필요할 수 있는 스크립트(예: 루트 생성 컨테이너에서 권한 없는 컨테이너로 마이그레이션)이지만 다음과 같은 목적으로 작동합니다.

#!/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; }

StackExchange Network의 라이선스 조건에 추가하여 공개 도메인에 넣겠습니다. 따라서 어떤 목적으로든 재사용하고 수정할 수 있지만 보증이 제공되지 않으며 사용이나 오용에 대해 책임을 지지 않습니다.

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

find, sort, uniq, , , 등이 사용 가능 awk하고 작동해야 한다고 mawk가정 하고 사용 중인 모든 명령줄 스위치를 이해합니다. Bash 합계 및 산술 표현식의 경우 이해가 가정됩니다. For is는 연산에 대한 유효한 종결자로 간주됩니다 .gawkidbashchownchmodreadonlyletfind+-exec

이 목록은 완전하지 않을 수 있습니다.

지원

예, 그에 따라 파일 소유자와 그룹을 조정하는 한 다른 곳에서 백업하고 복원할 수 있습니다.

그러나 비슷한 것을 사용한다고 가정하면 tar주의할 점이 있습니다. tar소켓이 무시되어 $rootfs/dev/log문제가 발생합니다. 다른 사람들도 비슷한 문제를 겪을 수 있습니다.

자원:

답변2

fuidshift이러한 목적으로 만들어졌습니다. LXD의 일부인 것 같습니다.

http://manpages.ubuntu.com/manpages/xenial/man1/fuidshift.1.html

답변3

편집하다: 유체 전달가장 좋은 방법입니다. 우분투에서는 LXD가 DEB 패키지에서 스냅으로 변환하기 때문에,유체 전달더 이상 배송이 없으며 이런 일이 발생하지 않습니다. 당신은 컴파일해야유체 전달스스로.

그러나 이 작업은 쉽게 수행할 수 있습니다(소스 코드 다운로드 및 컴파일은 거의 자동으로 수행됨).https://github.com/lxc/lxc/issues/3186

--

LXC 컨테이너가 포함된 디렉터리를 user1에서 user2로 복사하고 다음 Python 코드를 사용하여 UID와 GID를 이동하면 됩니다.

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

귀하는 적응해야 하며 uidmap_start필요 에 따라 적응 해야 gidmap_size할 수도 있습니다 .uidmap_sizegidmap_size

나는 이 Python 코드를 사용하여 권한이 있는 LXC 컨테이너를 권한이 없는 컨테이너로 마이그레이션합니다. Python 코드는 쉘 스크립트보다 빠르게 실행됩니다.

관련 정보