Di chuyển một container LXC không có đặc quyền giữa những người dùng


7

Tôi có cài đặt máy chủ Ubuntu 14.04 hoạt động như một máy chủ LXC. Nó có hai người dùng: user1 và user2.

user1 sở hữu một container LXC không có đặc quyền, sử dụng một thư mục (bên trong /home/user1/.local / ...) làm kho lưu trữ.

Làm cách nào để tạo một bản sao đầy đủ của container cho user2? Tôi không thể sao chép các tệp vì chúng được ánh xạ với chủ sở hữu từ 100000 đến 100000 + thứ gì đó, được ràng buộc với user1.

Ngoài ra, về cơ bản, tôi tin rằng đó là cùng một câu hỏi, làm thế nào tôi có thể sao lưu một cách an toàn bộ chứa LXC của người dùng 1 để khôi phục nó sau này trên một máy và / hoặc người dùng khác?


sử dụng cpthay vì mvvới các cờ thích hợp để giữ tất cả các thuộc tính bạn cần giữ trong tập lệnh của tôi.
0xC0000022L

Câu trả lời:


2

Tôi biết bây giờ làm thế nào để làm điều này. Nếu bạn không thể làm theo lời giải thích này, vui lòng hỏi lại, nhưng cũng đảm bảo rằng bạn đã đọc được những điều hữu ích trong các bài đọc tôi đang đưa ra ở phía dưới

Giả định sơ bộ

Tôi sẽ kiên định với các giả định sau, mở rộng từ những gì tôi có từ câu hỏi của bạn:

  1. máy chủ lưu trữ có một user1user2, nếu một thông tin không cụ thể cho một người, chúng tôi sẽ sử dụnguserX
  2. container sẽ được đặt tên theo một biến mà chúng ta sẽ kết xuất là $container
  3. thư mục nhà cho user1user2sẽ được đưa ra trong ký hiệu được biết đến từ Bash như ~user1~user2.
  4. chúng tôi sẽ giả sử phạm vi UID và GID cấp dưới là 100000..165536 cho user1và 200000..265536 user2chỉ để đơn giản
  5. thư mục FS gốc $containersẽ được hiển thị dưới dạng $rootfs, bất kể nơi nào nó sẽ kết thúc ( ~userX/.local/share/lxc/$container/rootfs)
  6. cấu hình container theo mặc định trong ~userX/.local/share/lxc/$container/config

Di chuyển container

Có hai phần dữ liệu liên quan chi phối các usernscontainer:

  1. chủ sở hữu và nhóm cho các tệp / thư mục của các thư mục chứa $container
  2. các UID và GID cấp dưới được gán ở hai vị trí: /etc/sub{uid,gid}cho tài khoản người dùng (được thao tác thông qua usermod --{add,del}-sub-{uid,gid}s) và lxc.id_maptrong $containercấu hình ( ~userX/.local/share/lxc/$container/config) tương ứng
    • Tôi không biết chắc chắn liệu có thể xác định các phạm vi khác nhau trong cấu hình vùng chứa cho mỗi vùng chứa hay không. Ví dụ: nếu người dùng máy chủ userXcó 65536 GID và UID cấp dưới, có thể chỉ định 5000 đến 65 container khác nhau, nhưng tôi chưa kiểm tra giả thuyết đó.
    • tuy nhiên, chắc chắn rằng cài đặt này giao tiếp với LXC là các phạm vi hợp lệ cho GID và UID trong không gian tên con.

Vì vậy, ý chính thực sự là bạn cần đảm bảo rằng chủ sở hữu tệp và thư mục cho vùng chứa khớp với cấu hình, đến lượt nó phải là một tập hợp con hợp lệ của các GID / UID cấp dưới được gán cho user1user2tương ứng.

Ví dụ: nếu bạn đang sử dụng Bash, bạn có thể sử dụng $((expression))cho các biểu thức số học và letđể gán các biểu thức số học cho các biến. Điều này rất hữu ích nếu bạn biết giá trị cơ bản (tương ứng 100000 và 200000) và GID / UID cho người dùng "bên trong".

Những điểm chính là:

  1. nó có thể
  2. hoặc khả năngCAP_CHOWN hoặc quyền siêu người dùng được yêu cầu

Đây là một tập lệnh có thể sẽ cần thêm một số mài giũa (ví dụ: di chuyển từ bộ chứa được tạo gốc sang không có đặc quyền), nhưng nó hoạt động với mục đích của tôi:

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

Ngoài các điều khoản cấp phép của mạng StackExchange, tôi sẽ đưa điều này vào phạm vi công cộng. Vì vậy, tái sử dụng và sửa đổi cho bất kỳ mục đích nào, nhưng nó không có bất kỳ sự bảo đảm nào và tôi không phải chịu trách nhiệm cho việc sử dụng hoặc lạm dụng nó.

Sử dụng
SYNTAX: lxc-reassign-userns.sh <from-user> <to-user> <container-name>

Nó giả định find, sort, uniq, awk( mawkgawknên làm việc), id, bash, chown, chmodvà vân vân có sẵn và để hiểu tất cả các dòng lệnh chuyển nó đang sử dụng. Đối với Bash readonlyvà các letbiểu thức số học được giả sử là được hiểu. Cho findlà giả định +là một kết thúc hợp lệ cho -exechành động.

Danh sách này có lẽ không đầy đủ.

Sao lưu

Có, bạn có thể tạo bản sao lưu và khôi phục chúng ở nơi khác, miễn là bạn cũng điều chỉnh chủ sở hữu tệp và nhóm cho phù hợp.

Tuy nhiên, giả sử bạn sử dụng một cái gì đó như tar, có một cảnh báo: tarsẽ bỏ qua các ổ cắm, do đó $rootfs/dev/logsẽ gây ra vấn đề - những người khác cũng có thể tạo ra một vấn đề tương tự.

Tài nguyên:


Thành thật mà nói, tôi đã hy vọng cho một lệnh tích hợp, sẵn sàng để sử dụng. Điều lạ với tôi là gói lxc không giao hàng. Tuy nhiên, cảm ơn rất nhiều về thời gian và công sức bạn đã viết ra một câu trả lời chi tiết như vậy và để chia sẻ mã của bạn. Tôi sẽ kiểm tra nó, sau đó tôi sẽ cung cấp cho bạn thông tin phản hồi.
agdev84

@ agdev84: Tôi hiện đang viết lại nó một chút để còn cho phép di chuyển nó từ một vinh dự một không có đặc quyền userns chứa dựa. Có thể cuối cùng làm điều đó trong Python hoặc như vậy.
0xC0000022L


2

Chỉnh sửa: fuidshift là cách tốt nhất để làm điều đó. Trong Ubuntu, vì LXD đã được chuyển đổi từ gói DEB sang snap, nên fuidshift không được vận chuyển nữa và điều này cũng sẽ không xảy ra. Bạn cần phải tự biên dịch fuidshift .

Tuy nhiên, điều này có thể được thực hiện khá dễ dàng (tải xuống mã nguồn và quá trình biên dịch được thực hiện gần như tự động), xem https://github.com/lxc/lxc/issues/3186

-

Bạn chỉ có thể sao chép thư mục chứa bộ chứa LXC từ user1 sang user2 và sử dụng mã python này để chuyển UID và 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)

Bạn sẽ cần phải thích nghi uidmap_start, gidmap_sizevà có thể cũng uidmap_sizenhư gidmap_sizevới nhu cầu của bạn.

Tôi đã sử dụng mã python này để di chuyển các container LXC đặc quyền sang các container không có đặc quyền. Mã python chạy nhanh hơn so với tập lệnh shell.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.