Skip to content

User, quyền và package

Trang này ghi chú hai nhóm việc quản trị Linux ở user space: tài khoản và quyền truy cập, và quản lý package. Nội dung đi theo thứ tự: user và group, tài khoản root trên board, tạo user khi build image, quyền trên file, quyền truy cập device file, capabilities, chạy daemon không cần root, sau đó là package manager, hai cách quản lý phần mềm trên thiết bị nhúng và package feed trong Yocto.

Cơ chế kiểm tra quyền bên trong kernel (LSM, seccomp, capabilities ở mức kernel) nằm ở trang Security Subsystem (tab Linux Kernel). Yêu cầu bảo mật đối với tài khoản và mật khẩu của sản phẩm nằm ở tab Security.

User và group

Mỗi process chạy với một user ID (UID) và một hoặc nhiều group ID (GID), và kernel dựa vào các định danh này để quyết định process được truy cập file nào. UID 0 là root. Process của root bỏ qua mọi kiểm tra quyền của kernel; từ Linux 2.2, đặc quyền này được chia thành các capability, xem mục Capabilities [1].

Kernel chỉ làm việc với số UID và GID. Tên user, tên group và mật khẩu lưu trong ba file văn bản:

File Nội dung Các trường của một dòng
/etc/passwd Danh sách tài khoản, mọi user đọc được tên:mật khẩu:UID:GID:GECOS:home:shell
/etc/shadow Mật khẩu đã băm, chỉ root đọc được tên:mật khẩu đã băm:... (các trường còn lại là thông tin hết hạn mật khẩu)
/etc/group Danh sách group và thành viên tên:mật khẩu:GID:danh sách user

Trường mật khẩu trong /etc/passwd thường là x, nghĩa là mật khẩu đã băm nằm trong /etc/shadow. Trường mật khẩu rỗng cho phép đăng nhập mà không hỏi mật khẩu, còn * khiến user không thể đăng nhập bằng mật khẩu [2].

Lệnh quản lý user khác nhau giữa bộ shadow-utils (máy host, image Yocto có gói shadow) và BusyBox:

Việc shadow-utils BusyBox
Xem user và group hiện tại id, whoami id, whoami
Tạo user useradd -m -s /bin/sh -G dialout alice adduser -s /bin/sh alice
Tạo group groupadd sensor addgroup sensor
Thêm user vào group usermod -aG sensor alice addgroup alice sensor
Đổi mật khẩu passwd alice passwd alice
Xóa user userdel alice deluser alice

Trong adduser của BusyBox, tùy chọn -G đặt group chính của user, khác với -G của useradd là danh sách group phụ [3]. Lệnh sudo thường không có trên image nhúng; chuyển sang root bằng su hoặc đăng nhập trực tiếp bằng root.

Tài khoản root trên board

Image dùng trong giai đoạn phát triển thường cho root đăng nhập không cần mật khẩu. Cấu hình liên quan trong hai build system:

Build system Cấu hình Tác dụng
Buildroot BR2_TARGET_ENABLE_ROOT_LOGIN Cho phép root đăng nhập bằng mật khẩu, mặc định bật
Buildroot BR2_TARGET_GENERIC_ROOT_PASSWD Mật khẩu ban đầu của root; để trống (mặc định) thì root đăng nhập không cần mật khẩu [4]
Yocto IMAGE_FEATURES gồm empty-root-password Root không có mật khẩu
Yocto allow-empty-password, allow-root-login SSH server (Dropbear, OpenSSH) chấp nhận mật khẩu rỗng, chấp nhận root đăng nhập
Yocto serial-autologin-root Cùng với empty-root-password: tự đăng nhập root trên serial console

Trong mã nguồn OpenEmbedded-Core hiện tại, các feature trên của Yocto thay cho feature debug-tweaks trước đây [5]. Khi không bật empty-root-password, bước hậu xử lý rootfs thay mật khẩu rỗng của root bằng *, tức là khóa đăng nhập root bằng mật khẩu [6].

Image sản phẩm không được để root đăng nhập không mật khẩu, và không dùng một mật khẩu mặc định chung cho mọi thiết bị. Yêu cầu này có trong các tiêu chuẩn cho thiết bị IoT, xem trang Consumer IoT (tab Security).

Tạo user khi build image

Rootfs của thiết bị nhúng thường chỉ đọc, nên user và group cho các daemon được tạo lúc build image thay vì chạy useradd trên board.

Buildroot dùng users table, khai báo trong file trỏ tới bằng BR2_ROOTFS_USERS_TABLES hoặc trong biến <PKG>_USERS của một package [7]. Mỗi dòng gồm các trường username uid group gid password home shell groups comment; giá trị -1 để Buildroot tự cấp UID hoặc GID hệ thống trong khoảng 100 đến 999 [8]:

board/myboard/users.txt
sensord -1 sensor -1 * - - dialout Sensor daemon

Dòng trên tạo user sensord không đăng nhập được (*), không có thư mục home, shell là /bin/false, group chính là sensor và thuộc thêm group dialout. Các tùy chọn khác để tùy biến rootfs của Buildroot được trình bày ở trang Buildroot.

Yocto có hai cách. Recipe của một package kế thừa class useradd để tạo user và group khi package được cài vào rootfs [9]:

sensord_1.0.bb
inherit useradd

USERADD_PACKAGES = "${PN}"
GROUPADD_PARAM:${PN} = "--system sensor"
USERADD_PARAM:${PN} = "--system --no-create-home --shell /bin/false --gid sensor sensord"

Ở mức image, class extrausers chạy các lệnh useradd, groupadd, usermod khai báo trong EXTRA_USERS_PARAMS [10]:

local.conf hoặc image recipe
IMAGE_CLASSES += "extrausers"
EXTRA_USERS_PARAMS = "\
    groupadd developers; \
    useradd -G developers tester; \
"

Quyền trên file

Mỗi file có một user sở hữu (owner), một group và ba bộ quyền đọc, ghi, thực thi (r, w, x) cho owner, cho group và cho mọi user khác. Kernel chọn một bộ quyền để kiểm tra: nếu UID của process trùng owner thì dùng bộ của owner; nếu không, nếu process thuộc group của file thì dùng bộ của group; còn lại dùng bộ của other [11].

$ ls -l /usr/bin/app
-rwxr-x--- 1 root video 18432 Nov 10 08:00 /usr/bin/app

Ký tự đầu tiên là loại file: - file thường, d thư mục, l symlink, c và b device file, p named pipe, s socket. Chín ký tự tiếp theo là ba bộ quyền. Ở ví dụ trên, root được đọc, ghi, thực thi; user trong group video được đọc và thực thi; user khác không có quyền gì.

Ý nghĩa của r, w, x khác nhau giữa file và thư mục:

Quyền Với file Với thư mục
r Đọc nội dung Liệt kê tên các file bên trong
w Ghi, sửa nội dung Tạo, xóa, đổi tên file bên trong
x Chạy như chương trình Đi vào thư mục và truy cập file bên trong

Quyền thường được viết dạng số bát phân, với r = 4, w = 2, x = 1, cộng lại cho từng bộ. Các lệnh liên quan:

chmod 755 /usr/bin/app        # rwxr-xr-x
chmod 600 ~/.ssh/id_ed25519   # rw-------, file key của SSH
chmod u+x,g-w script.sh       # dạng ký hiệu: thêm x cho owner, bỏ w của group
chown sensord:sensor /data/sensor
chown -R sensord:sensor /data/sensor   # áp dụng cho cả thư mục con
umask                         # mặt nạ quyền khi tạo file mới, thường là 0022

umask là các bit quyền bị bỏ đi khi tạo file mới. Với umask 022, file mới tạo bằng mode 666 trở thành 644, thư mục tạo bằng mode 777 trở thành 755 [12].

Ngoài chín bit quyền còn ba bit đặc biệt [11]:

Bit Giá trị Tác dụng
setuid 4000 Trên file thực thi: process chạy với UID của owner file, ví dụ lệnh passwd cần ghi /etc/shadow
setgid 2000 Trên file thực thi: process chạy với GID của group file. Trên thư mục: file và thư mục con tạo bên trong nhận group của thư mục
sticky 1000 Trên thư mục: chỉ owner của file, owner của thư mục hoặc process có đặc quyền mới được xóa hoặc đổi tên file bên trong

/tmp có mode 1777: mọi user được tạo file, nhưng mỗi user chỉ xóa được file do chính user đó sở hữu. Recipe base-files của Yocto tạo /tmp với mode này [13].

Trên máy build, file của rootfs được tạo bởi user thường nhưng trong image phải thuộc root hoặc user hệ thống. Buildroot dùng fakeroot để giả lập quyền root lúc tạo image, và đặt owner và quyền cho từng file qua device table hoặc biến <PKG>_PERMISSIONS [7]. Yocto dùng công cụ pseudo cho cùng mục đích [14].

Quyền truy cập device file

Device file cũng có owner, group và quyền như file thường. User không phải root muốn mở một thiết bị thì phải có quyền trên device file tương ứng, thường bằng cách thuộc group của file. Ví dụ, trên máy host, quy tắc udev mặc định gán /dev/ttyS*, /dev/ttyUSB* vào group dialout, nên user phải thuộc group này để mở cổng serial; chi tiết ở trang Serial console.

Trên board, owner và quyền của device node do trình quản lý /dev đặt khi node được tạo:

  • mdev của BusyBox đọc /etc/mdev.conf. Mỗi dòng có dạng <regex tên thiết bị> <uid>:<gid> <quyền bát phân>; dòng đầu tiên khớp được áp dụng, không dòng nào khớp thì dùng 0:0 660 [15].
  • udev đọc các file quy tắc trong /etc/udev/rules.d/. Ví dụ, quy tắc KERNEL=="spidev*", GROUP="spi", MODE="0660" cho group spi đọc và ghi các thiết bị spidev [16].

Capabilities

Linux chia đặc quyền của root thành các đơn vị gọi là capability, có thể cấp riêng từng cái cho một process hoặc một file thực thi [1]. Một số capability thường cần cho daemon trên thiết bị:

Capability Cho phép
CAP_NET_BIND_SERVICE Bind socket vào port nhỏ hơn 1024
CAP_NET_ADMIN Cấu hình mạng: interface, routing, firewall
CAP_NET_RAW Dùng raw socket và packet socket
CAP_SYS_TIME Đặt đồng hồ hệ thống
setcap cap_net_bind_service=+ep /usr/bin/webd   # cấp capability cho file thực thi
getcap /usr/bin/webd

setcap và getcap thuộc gói libcap. Capability của file lưu trong extended attribute security.capability [1], nên filesystem của rootfs và công cụ tạo image phải giữ được extended attribute thì capability mới còn trên board.

Chạy daemon không cần root

Mỗi daemon nên chạy với một user hệ thống riêng, không đăng nhập được, chỉ thuộc các group cần cho thiết bị nó dùng và chỉ được cấp capability cần thiết. Khi daemon có lỗ hổng, kẻ tấn công chỉ có quyền của user đó thay vì quyền root.

Với systemd, user, group và capability khai báo trong unit file [17]:

/etc/systemd/system/sensord.service
[Service]
ExecStart=/usr/bin/sensord
User=sensord
Group=sensor
SupplementaryGroups=dialout
AmbientCapabilities=CAP_NET_BIND_SERVICE

Với BusyBox init hoặc SysV init, init script khởi động daemon bằng start-stop-daemon với tùy chọn -c để đổi user và group [18]:

start-stop-daemon -S -b -c sensord:sensor -x /usr/bin/sensord

Init script và unit file được trình bày ở trang Init system.

Package manager

Package là một gói phần mềm đã build sẵn, gồm file cài đặt, metadata (tên, phiên bản, danh sách package phụ thuộc) và script chạy khi cài hoặc gỡ. Package manager cài, gỡ, nâng cấp package và tự giải quyết package phụ thuộc. Có ba định dạng phổ biến:

Định dạng Công cụ mức thấp Công cụ mức cao Dùng ở
deb dpkg apt Debian, Ubuntu, Raspberry Pi OS
rpm rpm dnf Fedora, RHEL
ipk opkg opkg OpenWrt, Yocto

Yocto tạo được cả ba định dạng, chọn bằng biến PACKAGE_CLASSES; xem trang Yocto. Bảng tương ứng các lệnh thường dùng [19][20][21]:

Việc apt, dpkg dnf, rpm opkg
Cập nhật danh sách package từ repository apt update dnf makecache opkg update
Cài package apt install pkg dnf install pkg opkg install pkg
Gỡ package apt remove pkg dnf remove pkg opkg remove pkg
Cài từ file package dpkg -i pkg.deb rpm -i pkg.rpm opkg install ./pkg.ipk
Liệt kê package đã cài dpkg -l rpm -qa opkg list-installed
Tìm package chứa một file dpkg -S /path rpm -qf /path opkg search /path
Liệt kê file của một package dpkg -L pkg rpm -ql pkg opkg files pkg

dpkg -i và rpm -i không tự cài package phụ thuộc; apt install ./pkg.deb và dnf install ./pkg.rpm thì có.

Quản lý phần mềm trên thiết bị nhúng

Phần mềm trên thiết bị nhúng được cập nhật theo một trong hai cách:

Cập nhật theo image Cập nhật theo package
Cách cập nhật Thay cả rootfs bằng image mới, thường qua OTA với bố cục A/B Cài và nâng cấp từng package trên thiết bị
Phiên bản phần mềm trên các thiết bị Mọi thiết bị chạy cùng một image đã được kiểm thử Mỗi thiết bị có thể có tổ hợp phiên bản package khác nhau
Rootfs Có thể chỉ đọc Phải ghi được, và lưu cơ sở dữ liệu của package manager
Ví dụ Buildroot; Yocto không bật package-management Distro dạng Debian cho board; Yocto bật package-management; OpenWrt

Buildroot không tạo binary package và không có package manager trên thiết bị. Tài liệu của Buildroot nêu các lý do sau [22]:

  • Theo dõi file của từng package phải bao gồm cả thư mục target, sysroot và thư mục host. Gỡ một package còn đòi hỏi build lại mọi package phụ thuộc vào nó, kể cả phụ thuộc tùy chọn mà file .mk có thể chưa khai báo.
  • Mục tiêu của Buildroot là tạo root filesystem, không phải tạo distro. Các nhà phát triển Buildroot cho rằng OpenEmbedded hoặc OpenWrt phù hợp hơn cho việc đó.
  • Với phần lớn hệ thống nhúng, cập nhật từng phần tạo ra quá nhiều tổ hợp phiên bản cần kiểm thử. Cập nhật cả rootfs bảo đảm image trên thiết bị đúng là image đã được kiểm thử.

Với Yocto, image feature package-management cài package manager vào image và giữ cơ sở dữ liệu package của nó [23]. Feature này hữu ích trong giai đoạn phát triển để cài thêm package mà không nạp lại image. Image sản phẩm có rootfs chỉ đọc thì gỡ feature này, như ví dụ ở trang Root filesystem cho thiết bị; phần mềm được cập nhật qua OTA Update.

Package feed trong Yocto

Package feed là một thư mục chứa package và file index, phục vụ qua HTTP để package manager trên board tải về. Trong giai đoạn phát triển, máy build có thể làm package feed cho board theo các bước sau, với định dạng ipk:

  1. Cấu hình trong local.conf, rồi build và nạp image:

    conf/local.conf
    PACKAGE_CLASSES = "package_ipk"
    EXTRA_IMAGE_FEATURES += "package-management"
    PACKAGE_FEED_URIS = "http://192.168.1.10:8000"
    

    Khi tạo rootfs, build system ghi file /etc/opkg/base-feeds.conf với mỗi dòng src/gz <tên> <URI>/<arch> cho từng kiến trúc package có trong thư mục deploy [24]. Biến PACKAGE_FEED_BASE_PATHS thêm đường dẫn con vào giữa URI và kiến trúc, PACKAGE_FEED_ARCHS giới hạn danh sách kiến trúc [25].

  2. Sau khi build thêm hoặc sửa package, tạo lại index của feed:

    bitbake mypackage
    bitbake package-index
    

    Recipe package-index chỉ có task do_package_index, sinh lại file index cho thư mục deploy [26].

  3. Phục vụ thư mục deploy qua HTTP trên máy build:

    cd tmp/deploy/ipk
    python3 -m http.server 8000
    
  4. Trên board, cập nhật danh sách package rồi cài:

    opkg update
    opkg install mypackage
    

Package trong feed phải được build từ cùng thư mục build và cùng cấu hình với image đang chạy trên board. Package build từ một cấu hình khác có thể phụ thuộc vào phiên bản thư viện không có trong image.

Liên quan

Tài liệu tham khảo

[1] - capabilities(7) - danh sách capability, file capability lưu trong security.capability

[2] - passwd(5) - định dạng /etc/passwd, ý nghĩa của x, mật khẩu rỗng và *

[3] - busybox/loginutils/adduser.c - tùy chọn của applet adduser

[4] - Buildroot: system/Config.in - BR2_TARGET_ENABLE_ROOT_LOGIN, BR2_TARGET_GENERIC_ROOT_PASSWD

[5] - OpenEmbedded-Core: core-image.bbclass - danh sách image feature, gồm package-management và các feature thay cho debug-tweaks

[6] - OpenEmbedded-Core: rootfs-postcommands.bbclass - hàm zap_empty_root_password

[7] - The Buildroot user manual - mục "Customizing the generated target filesystem": users table, device table, post-fakeroot script

[8] - Buildroot: makeusers syntax - cú pháp users table

[9] - OpenEmbedded-Core: useradd-example.bb - ví dụ USERADD_PACKAGES, USERADD_PARAM, GROUPADD_PARAM

[10] - OpenEmbedded-Core: extrausers.bbclass - EXTRA_USERS_PARAMS

[11] - inode(7) - mục "The file type and mode": bit quyền, setuid, setgid, sticky

[12] - umask(2)

[13] - OpenEmbedded-Core: base-files - thư mục tạo với mode 1777

[14] - Yocto Project Overview and Concepts Manual: Fakeroot and Pseudo

[15] - busybox/docs/mdev.txt - định dạng /etc/mdev.conf

[16] - udev(7) - cú pháp quy tắc udev, khóa KERNEL, GROUP, MODE

[17] - systemd.exec(5) - User=, Group=, SupplementaryGroups=, AmbientCapabilities=

[18] - busybox/debianutils/start_stop_daemon.c - tùy chọn -c USER[:GRP]

[19] - apt(8) và dpkg(1) - Debian

[20] - rpm(8) và DNF command reference

[21] - opkg (OpenWrt): src/opkg-cl.c - các lệnh update, install, remove, list-installed, search, files

[22] - Buildroot FAQ: Why doesn't Buildroot generate binary packages (.deb, .ipkg...)?

[23] - Yocto Project Reference Manual: Image Features

[24] - OpenEmbedded-Core: lib/oe/package_manager/ipk - hàm insert_feeds_uris ghi /etc/opkg/base-feeds.conf

[25] - OpenEmbedded-Core: lib/oe/package_manager - hàm construct_uris; lib/oe/rootfs.py - đọc PACKAGE_FEED_URIS, PACKAGE_FEED_BASE_PATHS, PACKAGE_FEED_ARCHS

[26] - OpenEmbedded-Core: package-index.bb