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]:
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]:
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]:
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].
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ùng0:0 660[15]. - udev đọc các file quy tắc trong
/etc/udev/rules.d/. Ví dụ, quy tắcKERNEL=="spidev*", GROUP="spi", MODE="0660"cho groupspiđọ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]:
[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]:
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
.mkcó 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:
-
Cấu hình trong
local.conf, rồi build và nạp image:conf/local.confPACKAGE_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.confvới mỗi dòngsrc/gz <tên> <URI>/<arch>cho từng kiến trúc package có trong thư mục deploy [24]. BiếnPACKAGE_FEED_BASE_PATHSthêm đường dẫn con vào giữa URI và kiến trúc,PACKAGE_FEED_ARCHSgiới hạn danh sách kiến trúc [25]. -
Sau khi build thêm hoặc sửa package, tạo lại index của feed:
Recipe
package-indexchỉ có taskdo_package_index, sinh lại file index cho thư mục deploy [26]. -
Phục vụ thư mục deploy qua HTTP trên máy build:
-
Trên board, cập nhật danh sách package rồi cài:
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¶
- Security Subsystem (Linux Kernel): LSM, seccomp và capabilities ở mức kernel.
- Consumer IoT (Security): yêu cầu không dùng mật khẩu mặc định chung.
- SSH (Security): hardening SSH server trên thiết bị.
- Buildroot và Buildroot: package và BR2_EXTERNAL: users table, device table,
<PKG>_USERSvà<PKG>_PERMISSIONS. - Yocto:
PACKAGE_CLASSES, package manager khi dựng rootfs. - Serial console: group
dialoutvà quyền mở cổng serial trên host. - Init system: unit file của systemd và init script.
- Root filesystem cho thiết bị: rootfs chỉ đọc, gỡ
package-management. - Command line và filesystem: cây thư mục, mount, device file.
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