NOTE · Engineering Systems

Ubuntu 命令行:安全的系统排查清单

按文件、进程、服务、权限、防火墙、存储和端口整理常用命令,并标注高风险操作。

这份清单遵循一条原则:先确认目标和当前状态,再修改;能用普通权限完成,就不使用 root。

文件与空间

大小与文件系统

df -hT
du -xhd1 .
du -sh PATH
findmnt
lsblk -o NAME,SIZE,FSTYPE,FSVER,LABEL,UUID,MOUNTPOINTS

df 看文件系统剩余空间,du 看目录实际占用。二者明显不一致时,可继续检查已删除但仍被进程占用的文件:

sudo lsof +L1

压缩与解压

tar -czf archive.tar.gz DIRECTORY
tar -tzf archive.tar.gz
tar -xzf archive.tar.gz -C DESTINATION

zip -r archive.zip DIRECTORY
unzip -l archive.zip
unzip archive.zip -d DESTINATION

先用 tar -tunzip -l 查看归档内容,可以避免把大量文件解到错误目录。

.tar.xz 的对应命令与常见参数含义如下:

tar -cJf archive.tar.xz DIRECTORY
tar -tJf archive.tar.xz
tar -xJf archive.tar.xz -C DESTINATION
参数含义
-c / -x / -t创建 / 解包 / 仅列出
-z / -Jgzip / xz 压缩
-f后一个参数是归档文件名
-C在目标目录中执行解包
-v显示详细文件列表,文件很多时可省略

zip 归档目录需要 -r;路径中有空格时使用引号。解包来自不可信来源的归档时,先检查是否含绝对路径或 ../ 路径穿越条目。

比较文件与目录

sha256sum FILE_A FILE_B

目录内容校验应在两个待比较目录中分别执行,使用相对路径:

find . -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum

这个结果同时受文件内容和相对文件名影响;权限、时间戳和空目录不在校验范围内。

进程与资源

ps -eo pid,ppid,user,stat,etime,%cpu,%mem,cmd --sort=-%cpu
pgrep -af PROCESS_PATTERN
free -h
uptime

终止进程时先发送默认的 SIGTERM

kill PID

只有确认进程无法正常退出且不会破坏数据时,才考虑 kill -KILL PID。不要用模糊模式直接批量杀进程。

systemd 服务与日志

systemctl status SERVICE_NAME
systemctl is-active SERVICE_NAME
systemctl is-enabled SERVICE_NAME
journalctl -u SERVICE_NAME --since today
journalctl -u SERVICE_NAME -f

修改服务配置后,按实际需要执行:

sudo systemctl daemon-reload
sudo systemctl restart SERVICE_NAME

用户服务使用独立作用域:

systemctl --user status SERVICE_NAME
journalctl --user -u SERVICE_NAME

系统服务和用户服务的日志、启用状态不是同一套,排查时不要混用。

文件传输

scp -P SSH_PORT LOCAL_FILE USER_NAME@SERVER_HOST:REMOTE_PATH
scp -P SSH_PORT USER_NAME@SERVER_HOST:REMOTE_FILE LOCAL_PATH
scp -r -P SSH_PORT LOCAL_DIRECTORY USER_NAME@SERVER_HOST:REMOTE_PATH

-P 是 SSH 端口,必须大写。大目录或需要断点续传时更适合使用 rsync

rsync -avh --progress -e 'ssh -p SSH_PORT' SOURCE USER_NAME@SERVER_HOST:DESTINATION

--delete 会删除目标端多余文件,未做预演和备份前不要使用。

在 Windows PowerShell 中,带盘符或空格的本地路径应加引号。例如四种传输方向可以写成:

# 上传文件 / 下载文件
scp "C:\Users\username\Documents\test.txt" username@192.168.1.10:/home/ubuntu/
scp username@192.168.1.10:/home/ubuntu/test.txt "D:\Downloads\"

# 上传目录 / 下载目录
scp -r "C:\Users\username\Documents\myfolder" username@192.168.1.10:/home/ubuntu/
scp -r username@192.168.1.10:/home/ubuntu/myfolder "D:\Downloads\"

这里的用户名和 192.168.1.10 都是示例。PowerShell、Git Bash 与 WSL 对 Windows 路径的解析不同;若路径被错误拆分,先确认自己正在使用哪个 shell,而不是反复改变远端权限。

权限

stat PATH
namei -l PATH
getfacl PATH

常见修改:

sudo chown USER_NAME:GROUP_NAME PATH
chmod u+rwX,go-rwx PATH
setfacl -m u:USER_NAME:rwX PATH

递归 chownchmod 会影响整棵目录。执行前应先用 find PATH -maxdepth 2 -ls 确认范围,不把 chmod 777 当作排错手段。

ACL:给特定用户增加权限

chown 改所有者,chmod 改 owner/group/other 三组基础权限,ACL 则能单独授权某个用户或组。下面以公开网名 syhdtc668 为例:

getfacl /srv/shared
sudo setfacl -m u:syhdtc668:rwx /srv/shared
sudo setfacl -x u:syhdtc668 /srv/shared

目录上的 x 表示允许进入/遍历,不等同于“执行目录”。如果希望新建的子项继承权限,应同时设置 default ACL:

sudo setfacl -m d:u:syhdtc668:rwx /srv/shared
getfacl /srv/shared

递归修改只影响已有对象,default ACL 只影响之后新建的对象。需要同时处理两者时先备份 getfacl -R 输出、限制目标目录,再分别设置;不要把一条 setfacl -R ...:rwx 无差别应用到普通文件,因为它会给文件增加执行位。ACL 的有效权限还受 mask:: 项限制,应以 getfacl 输出中的 effective 结果为准。

UFW 防火墙

先查看状态:

sudo ufw status verbose
sudo ufw status numbered

如果正在通过 SSH 远程操作,启用 UFW 之前先放行实际 SSH 服务或端口:

sudo ufw allow OpenSSH
sudo ufw enable

自定义端口示例:

sudo ufw allow SSH_PORT/tcp

删除规则时先看编号,再删除:

sudo ufw status numbered
sudo ufw delete RULE_NUMBER

规则编号会在每次删除后变化,不要连续使用旧编号。

存储挂载

先识别设备、文件系统、UUID 和已有挂载:

lsblk -f
findmnt
sudo blkid

临时挂载示例:

sudo mkdir -p /mnt/DISK_LABEL
sudo mount /dev/DEVICE_PARTITION /mnt/DISK_LABEL
findmnt /mnt/DISK_LABEL

/dev/DEVICE_PARTITION 必须替换为已经核对过的分区,不能照抄。卸载前离开挂载目录并检查占用:

sudo lsof +f -- /mnt/DISK_LABEL
sudo umount /mnt/DISK_LABEL

umount -l 是延迟分离,不是“更强制且一定安全”的卸载。设备写入完成前不要直接拔盘。

持久挂载使用 UUID 写入 /etc/fstab。编辑前备份,编辑后先验证:

sudo findmnt --verify --verbose

错误的 fstab 可能影响启动。固定挂载 U 盘可以使用下面这个必须替换占位符的模板:

UUID=DEVICE_UUID  /mnt/DISK_LABEL  FILESYSTEM_TYPE  defaults,nofail,x-systemd.automount  0  2

DEVICE_UUID、挂载点和文件系统类型必须来自当前机器的 blkid/lsblk -fnofail 避免可移动设备缺席时把正常启动当成硬失败,x-systemd.automount 是按访问触发挂载,不等同于桌面环境的热插拔自动挂载。写入后先验证,再在保留管理员会话的情况下测试:

sudo findmnt --verify --verbose
sudo mount -a
findmnt /mnt/DISK_LABEL

mount -a 会尝试执行尚未挂载的条目;验证有错误时不要运行。临时 U 盘通常更适合 udisksctl,无需写入 fstab

端口与连接

ss -lntup
ss -tnp
sudo lsof -nP -iTCP:PORT_NUMBER
sudo lsof -nP -iUDP:PORT_NUMBER

查看进程的监听端口:

sudo lsof -nP -a -p PID -i

已知进程名时,可以先精确找 PID,再查它的 socket:

pgrep -ax PROCESS_NAME
sudo lsof -nP -a -c PROCESS_NAME -i

lsof -c 按命令名前缀匹配;服务由多个 worker 组成时应核对每个 PID。反过来已知端口时,sudo lsof -nP -iTCP:PORT_NUMBER -sTCP:LISTEN 只显示 TCP 监听者,避免把客户端连接误认成 server。

抓包前先确定接口、过滤器和授权边界:

ip -brief link
sudo tcpdump -i INTERFACE -nn 'host PEER_IP and port PORT_NUMBER'

抓包可能包含凭据、Cookie、内网地址和业务数据,不应把原始 pcap 直接放到公开站点。

输出重定向

COMMAND >stdout.log
COMMAND 2>stderr.log
COMMAND >all.log 2>&1
COMMAND >>append.log 2>&1

> 会覆盖文件,>> 才是追加。需要同时在终端查看时:

COMMAND 2>&1 | tee run.log

Swap 的只读检查

swapon --show
free -h
cat /proc/sys/vm/swappiness

free 命令输出示例

是否增加 swap 要结合内存压力、磁盘空间和工作负载判断。不要在不知道现有 swap 文件、文件系统能力和 fstab 状态时直接照抄创建脚本。

受控创建 swapfile(历史步骤修订)

下面以 4 GiB/var/swapfile 为例。只有确认目标文件不存在、磁盘空间充足、文件系统支持普通 swapfile,且当前没有同路径条目后才执行:

swapon --show
findmnt -no TARGET,FSTYPE,OPTIONS /var
sudo test ! -e /var/swapfile
grep -nF '/var/swapfile' /etc/fstab

上述检查均符合预期后,创建并启用:

sudo fallocate -l 4G /var/swapfile
sudo chmod 600 /var/swapfile
sudo mkswap /var/swapfile
sudo swapon /var/swapfile
swapon --show
free -h

确认本次启用正常后,才把以下行加入 /etc/fstab

/var/swapfile none swap defaults 0 0

mkswap 会改写目标文件,路径检查不能省略。Btrfs、CoW、稀疏文件、休眠以及加密存储对 swapfile 还有额外约束;遇到 swapon failed: Invalid argument 时应查当前文件系统规则,不能反复重建或关闭安全机制。

RAR 与更多归档格式

Ubuntu 默认工具已覆盖 tar、gzip、xz 和 zip。确实需要处理 RAR 时,先查看当前发行版可提供的包:

apt-cache policy unrar unrar-free rar

安装可用实现后,常见读取操作是:

unrar l ARCHIVE.rar
unrar x ARCHIVE.rar DESTINATION/

先用 l 查看内容,再解压到新目录。RAR 创建工具可能受仓库组件与许可限制,不把某一发行版的包名当成通用前提。

可移动设备的安全弹出

先区分“分区”和“整块设备”:

lsblk -o NAME,PATH,TYPE,SIZE,FSTYPE,LABEL,MOUNTPOINTS,TRAN

lsblk 输出示例

例如确认 /dev/DEVICE_PARTITION 是目标分区、/dev/DEVICE_DISK 是它所属的整块 USB 设备后:

udisksctl unmount -b /dev/DEVICE_PARTITION
udisksctl power-off -b /dev/DEVICE_DISK

power-off 的目标通常是整块设备,不是分区。执行前必须从 lsblk 关系中核对,不能把示例中的 /dev/sdb1 当作固定目标。命令成功后再物理拔出。

命令行弹出 U 盘的第三方笔记可作为历史参考,但其中把 power-off 目标写成分区的做法不能照抄;应按上面的 lsblk 父子关系对整块设备执行。

Windows 与 Linux 文件校验

对同一文件使用同一种算法。Windows PowerShell/命令提示符可用:

certutil -hashfile FILE_PATH SHA256

Linux:

sha256sum FILE_PATH

MD5 仍可用于发现普通传输损坏,但不适合验证对抗性环境中的完整性或来源。目录摘要还会受到相对文件名影响;若要核对权限、软链接和空目录,应使用专门清单或归档格式,而不是只比较一个摘要。

root 账户的只读检查与锁定

Ubuntu 通常锁定 root 的密码登录。只读查看状态:

sudo passwd -S root
sudo sshd -T | grep -i '^permitrootlogin\|^passwordauthentication\|^pubkeyauthentication'

passwd -S 输出中的 L 通常表示密码已锁定,P 表示存在可用密码哈希;具体仍应结合本机 PAM 和 SSH 配置判断。若此前误启用了 root 密码且确认没有依赖它,可锁定:

sudo passwd -l root

不要为了方便执行 sudo passwd root 或启用 PermitRootLogin yes。远程维护应先验证普通用户密钥登录,再关闭不需要的密码入口,并保留当前会话直到新会话测试成功。

包文件与命令来源

dpkg -L PACKAGE_NAME
dpkg -S /PATH/TO/FILE
apt-cache policy PACKAGE_NAME
command -v COMMAND_NAME
type -a COMMAND_NAME

dpkg -L 列出已安装 Debian 包登记的文件;dpkg -S 反查文件属于哪个包。它们不会列出用户手工下载、pip/Conda 或源码安装的全部内容。

Vim 快捷键速查

普通模式:

操作按键
移动hjkl
下一/上一单词wb
文件开头/结尾ggG
上/下半页Ctrl-uCtrl-d
删除当前行dd
复制当前行yy
粘贴p
撤销/重做uCtrl-r
搜索/下一个/PATTERNn

进入编辑:i 在光标前插入,a 在光标后插入,I 到行首,A 到行尾。按 Esc 返回普通模式。

命令模式:

:w          保存
:q          退出
:wq         保存并退出
:q!         放弃未保存修改并退出
:set number 显示行号

可视模式按 v,移动光标选择后可用 y 复制、d 删除。普通模式中的 yawy(复制)加 aw(a word 文本对象),通常会把相邻空白也纳入;yiw 使用 inner word,不含外围空白。数字前缀和动作也可组合,例如 4j 下移四行、y4j 复制到下方四行、dfy 删除到下一个 y 字符;边界受当前位置影响,不熟悉时先在临时文件练习。

systemctl/journalctl 打开分页器时,Space 下一页、b 上一页、g 开头、G 结尾、q 退出;也可给命令加 --no-pager

systemctl 分页输出示例

systemd 变更命令

先确认服务名称和当前状态:

systemctl list-unit-files --type=service
systemctl status SERVICE_NAME

常见系统服务操作:

sudo systemctl start SERVICE_NAME
sudo systemctl stop SERVICE_NAME
sudo systemctl restart SERVICE_NAME
sudo systemctl reload SERVICE_NAME
sudo systemctl enable SERVICE_NAME
sudo systemctl disable SERVICE_NAME

reload 只有服务自身支持时才有效;enable 控制启动关联,不等同于立即启动。用户服务去掉 sudo 并增加 --user

systemctl --user enable --now SERVICE_NAME
systemctl --user disable --now SERVICE_NAME

编辑 unit 或 drop-in 后:

sudo systemctl daemon-reload
sudo systemctl restart SERVICE_NAME

修改前可用 systemctl cat SERVICE_NAME 查看最终合并配置,避免只检查某一个 unit 文件。

Python、pip 与包路径诊断

按“系统 Python、用户 Python、Conda Python”列出的固定路径只适用于特定环境;/usr/bin/python3.8~/.local/lib/python3.8/ 等属于当时版本。当前机器应让解释器自己报告真实位置:

command -v python3
readlink -f "$(command -v python3)"
python3 -m pip --version
python3 -c 'import sys; print(sys.executable); print(sys.version)'
python3 -c 'import site; print("USER_SITE=", site.getusersitepackages()); print(*site.getsitepackages(), sep="\n")'
python3 -c 'import sys; print(*sys.path, sep="\n")'

激活虚拟环境或 Conda 环境后应重新运行同一组命令,确认 pythonpython -m pip 指向同一个环境。不要只运行裸 pip 后根据命令名猜它绑定哪个解释器。

Debian/Ubuntu 常见来源可以概括为:

安装来源常见位置管理方式
apt install python3-PACKAGE/usr/lib/python3/dist-packages/由 apt/dpkg 管理,不手工删除
用户级 pip~/.local/lib/pythonX.Y/site-packages/python -m pip uninstall
venvVENV/lib/pythonX.Y/site-packages/使用该 venv 的解释器/pip
CondaCONDA_PREFIX/lib/pythonX.Y/site-packages/在对应 Conda 环境中管理

/usr/local/...dist-packagessite-packages 的实际优先级受发行版、解释器构建方式和环境变量影响,不能只看表格下结论。定位某个导入到底来自哪里:

python3 -c 'import PACKAGE_NAME; print(PACKAGE_NAME.__file__)'
python3 -m pip show PACKAGE_NAME

一个实际排障现象是:运行 Python 3.12 时,sys.path 中出现 /opt/ros/noetic/lib/python3/dist-packages,于是看到了面向 Python 3.8 的 ROS Noetic 包。这通常来自当前 shell 先前 source /opt/ros/noetic/setup.bash 所注入的环境,而不是 Python 3.12 “自动安装”了这些包。应在未 source ROS 的新 shell 或干净虚拟环境中复现,再比较 env | rg '^(PYTHONPATH|ROS_|AMENT_|CMAKE_PREFIX_PATH)';不要直接删除 /opt/ros/noetic 下的系统包。

Editable install 的现代排查

在目标虚拟环境中安装当前项目:

python -m pip install -e .
python -m pip show PACKAGE_NAME
python -c 'import PACKAGE_NAME; print(PACKAGE_NAME.__file__)'

卸载:

python -m pip uninstall PACKAGE_NAME

现代 editable install 可能通过 PEP 660 生成元数据和导入钩子,不一定存在旧式 .egg-link/easy-install.pth。不要按固定 Python 3.8 路径手工删注册文件,也不要把删除源码目录当作卸载步骤。若卸载异常,先核对解释器、pip 和构建后端,再检查 site-packages

UFW 更多常用操作

查看和添加明确规则:

sudo ufw status verbose
sudo ufw app list
sudo ufw app info OpenSSH
sudo ufw allow PORT_NUMBER/tcp
sudo ufw allow from SOURCE_CIDR to any port PORT_NUMBER proto tcp
sudo ufw deny PORT_NUMBER/udp

设置默认策略前必须理解现有远程入口。服务器常见基线是拒绝未经允许的入站、允许出站,但不能不经检查直接套用:

sudo ufw default deny incoming
sudo ufw default allow outgoing

ufw reset 会移除现有规则,ufw disable 会停止过滤;二者都不作为普通排错步骤。

Dukto 端口案例(历史)

Dukto 在启用 UFW 后无法发现局域网设备的一个案例中,lsof 显示该进程同时监听 TCP/UDP 4644。这个应用和端口只作为历史案例,不能据此断定当前程序也使用同一端口:

sudo lsof -nP -a -c dukto -i
sudo ufw allow from LAN_CIDR to any port 4644 proto tcp
sudo ufw allow from LAN_CIDR to any port 4644 proto udp
sudo ufw status numbered

LAN_CIDR 必须替换为实际受信任局域网网段。sudo ufw allow 4644 会对所有来源同时开放 TCP 和 UDP,范围过大;这里只在先确认监听进程和协议后做最小来源放行。若应用已经停用,应删除对应规则,而不是长期保留。

HTTP/HTTPS、应用配置和日志入口可归纳为以下规则:

sudo ufw allow http
sudo ufw allow https
sudo ufw app list
sudo ufw app info "Nginx Full"
sudo ufw logging low

日志位置依赖 Ubuntu/UFW/rsyslog 配置;优先用 journalctl 查当前系统,再检查是否存在传统文件:

sudo journalctl -k -g 'UFW' --since today
sudo test -f /var/log/ufw.log && sudo tail -n 100 /var/log/ufw.log

防火墙日志可能含公网/内网地址和端口扫描信息,公开截图前应审查。logging full 会显著增加日志量,不作为长期默认值。

输入与输出重定向补充

文件描述符 012 分别表示标准输入、标准输出和标准错误:

COMMAND <input.txt
COMMAND >stdout.txt
COMMAND 2>stderr.txt
COMMAND >all.txt 2>&1
COMMAND >>all.txt 2>&1

顺序有意义:>all.txt 2>&1 让标准错误跟随已经重定向的标准输出。Bash 中 &>all.txt 可同时重定向,但 >all.txt 2>&1 在更多 shell 场景中更直观。

覆盖重要文件前使用 set -o noclobber 或明确核对目标。需要边看边保存:

COMMAND 2>&1 | tee run.log

tcpdump 的有限采样

先找接口,并限制主机、协议、端口和包数:

ip -brief address
sudo tcpdump -i INTERFACE -nn -c 20 'host PEER_IP and udp port PORT_NUMBER'

-nn 同时禁止主机名和服务名解析,-c 20 防止无限抓取。-vvv-X 会暴露更多协议字段和负载,只应在有授权的本机诊断中使用;截图、终端输出和 pcap 都可能含敏感信息。

历史做法与当前边界

  • 启用 root 密码或 root SSH 登录;
  • sudo pip 和手工修改 Python 系统包;
  • 未确认 SSH 放行就启用 UFW;
  • umount -l 描述成普通“强制卸载”;
  • fstab 描述成移动硬盘热插拔方案;
  • 包含真实姓名、密码、Token、私钥或邮箱授权码的命令。