我的笔记本是机械革命无界15XA,摄像头模组是 SunplusIT 的,带一颗红外摄像头,出厂就是给 Windows Hello 用的。装了 CachyOS 之后这颗 IR 摄像头一直吃灰,每次解锁、sudo 都得敲密码。这次想把它救活,用的是 howdy-next——一个 C++ 重写的 Howdy 实现,识别快、维护活跃。旧版 Howdy 我几年前用过,AUR 上还得装 -git 版本、认证时拉起 Python 2,体验一般;新版体验下来基本就是 Linux 版 Windows Hello。
路线选择#
人脸识别登录这块,Linux 上能选的方案目前有三条路:
| 方案 | 现状 | 技术栈 | 评价 |
|---|---|---|---|
| howdy 2.6.1 | boltgolt 官方稳定版,2023 年后基本停滞 | Python + pam-python + dlib / face_recognition | 能用,但每次认证都要拉起 Python,1~2 秒起步;依赖链停在 2023 年,几年前用过,体验一般 |
| howdy 3.0 beta | 官方的 Rust + OpenVINO 重写,常年 beta,beta 分支一度从仓库消失 | Rust | 不推荐,安装渠道都不稳定 |
| howdy-next 3.4.2 | nathawat 维护的现代化重实现,2026-05 首发,月更节奏 | C++23 + OpenCV 5(YuNet 检测 + SFace 嵌入),原生 pam_howdy.so | 本次选择 |
选 howdy-next 的几个理由:
- 快:3.4.2 官方数据认证耗时 301ms(上一版还是 468ms),匹配速度提升 2~2.8 倍。原生二进制,没有 Python 解释器启动开销
- 识别模型先进一代:OpenCV 5 的 YuNet(人脸检测)+ SFace(特征嵌入),比老 Howdy 那套 2017 年的 dlib 模型强不少
- KDE 兼容好:3.3.1 专项修复了 KScreenLocker 认证后死锁的问题——我的锁屏正好就是它
- 新:再次安装主要就是想体验下新技术,看看跟之前有什么区别
0. 准备工作#
确认摄像头#
装 v4l-utils,列出所有视频设备:
sudo pacman -S v4l-utils
v4l2-ctl --list-devicesbash~ ❯ v4l2-ctl --list-devices
HD Webcam: HD Webcam (usb-0000:64:00.4-1):
/dev/video0
/dev/video1
/dev/video2
/dev/video3
/dev/media0
/dev/media1text这里有个小坑:--list-devices 是按 USB 物理设备分组的,四个节点全挂在同一个 HD Webcam 名下,光看这个输出根本分不出哪个节点是 IR。要逐个节点查 Card type:
~ ❯ for d in /dev/video*; do echo "== $d =="; v4l2-ctl -d $d -D | grep "Card type"; donebash== /dev/video0 ==
Card type : HD Webcam: HD Webcam
== /dev/video1 ==
Card type : HD Webcam: HD Webcam
== /dev/video2 ==
Card type : HD Webcam: IR Camera
== /dev/video3 ==
Card type : HD Webcam: IR Cameratext这下就清楚了:video0/1 是 RGB,video2/3 是 IR,两两一组(每组第一个是采集节点、第二个是元数据节点):
| 设备 | 说明 | 支持的格式 |
|---|---|---|
/dev/video0 | RGB 普通摄像头 | MJPG 1280x720 / 640x480 @30fps |
/dev/video2 | IR 红外摄像头 | GREY 8-bit 640x360 @30fps |
有 IR 摄像头优先用 IR:照片和屏幕在红外光下成不了像,天然防照片欺骗,而且暗光环境照常工作——这才是完整的 Windows Hello 体验。
/dev/video2 这种序号重启后理论上可能漂移,用稳定的 by-path 路径更保险。怎么从一堆软链里找到 IR 那条?——拿上一步确定的设备号去 grep:
ls -l /dev/v4l/by-path/ | grep video2bashpci-0000:64:00.4-usb-0:1:1.2-video-index0 -> ../../video2
pci-0000:64:00.4-usb-0:1:1.2-video-index1 -> ../../video3
pci-0000:64:00.4-usbv2-0:1:1.2-video-index0 -> ../../video2
pci-0000:64:00.4-usbv2-0:1:1.2-video-index1 -> ../../video3text注意会命中两组:usb- 和 usbv2- 开头的各一组,指向的是同一个 video2。usbv2 只是 udev 按 USB 速度(v2 = High Speed)多生成的一份别名,作用完全一样,选不带速度后缀的 usb- 那条,少一个变数。所以最终写进 howdy 配置的路径是:
/dev/v4l/by-path/pci-0000:64:00.4-usb-0:1:1.2-video-index0text路径拆开读:
pci-0000:64:00.4是摄像头挂的 USB 控制器的 PCI 地址;usb-0:1:1.2是「端口:配置:接口」——同一颗模组里 RGB 和 IR 就靠接口号区分(1.0是 RGB、1.2是 IR);video-index0是该接口下的采集节点(index1 是元数据节点,用不到)。另外我这边 by-id 只给 RGB 生成了软链,IR 只能走 by-path。
保底措施(动 PAM 前必做)#
PAM 配置改坏了,sudo 都救不回来,先给自己留后路:
# 备份现有 PAM 配置
sudo cp -r /etc/pam.d ~/pam.d.bak.$(date +%s)
# 开一个 root 终端一直放着别关
sudo -ibash本文全程有个原则:只往各服务自己的 PAM 文件里加行,完全不碰 system-auth / system-login。这样 SSH、TTY 登录的认证链路保持原样——就算锁屏或登录界面的 PAM 被我改坏了,Ctrl+Alt+F3 切 TTY 或者 SSH 进来都能救。
1. 安装 howdy-next#
AUR 里有 howdy-next(正式版)和 howdy-next-git(开发版),用 paru 装正式版:
paru -S howdy-nextbash~ ❯ paru -Qi howdy-next 11s
Installed From : 无
名字 : howdy-next
版本 : 3.4.2-1
描述 : Modern C++ reimplementation of Howdy facial authentication on Linux
架构 : x86_64
URL : https://codeberg.org/nathawat/howdy-next
软件许可 : GPL-3.0-or-later
组 : 无
提供 : howdy
依赖于 : acl curl>=7.85.0 glibc>=2.36 libevdev libinih>=59 openssl opencv>=5.0.0 pam qt6-base yyjson>=0.12.0
可选依赖 : linux-enable-ir-emitter: Enables infrared cameras that are not directly enabled out-of-the-box
依赖它 : 无
被可选依赖 : 无
与它冲突 : howdy howdy-next-git
取代 : 无
安装后大小 : 1144.23 KiB
打包者 : Unknown Packager
编译日期 : 2026年10月25日 星期日 22时08分54秒
安装日期 : 2026年10月25日 星期日 22时35分46秒
安装原因 : 单独指定安装
安装脚本 : 是
验证者 : 无
text依赖全部来自官方仓库:opencv(要求 ≥5.0,CachyOS 仓库里正好是 5.0.0,而且我这台本来就已经装了)、pam、libinih、yyjson 这些,编译的只有项目本身,CMake Unity 构建,一两分钟的事。注意它和老 howdy 包冲突,别装重了。
装完确认 PAM 模块就位:
ls -l /usr/lib/security/pam_howdy.sobash-rwxr-xr-x 1 root root 214984 10月25日 22:08 /usr/lib/security/pam_howdy.sotext2. 配置 IR 摄像头#
配置文件在 /etc/howdy/config.ini。注意这跟老 Howdy 的 /usr/lib/security/howdy/config.ini 不是一回事——howdy-next 挪到了 FHS 规范的 /etc 下(man howdy 的 FILES 一节写得明明白白,查 man 比网上老教程靠谱):
sudo howdy configbash
howdy config打开的是$EDITOR,CachyOS 默认是 micro:Ctrl+S保存(状态栏文件名旁的+消失)、Ctrl+Q退出,正常退出后才会写回真正的 config.ini。习惯 nano 的EDITOR=nano sudo howdy config(nano 是Ctrl+O回车保存、Ctrl+X退出)。
把 [video] 段的 device_path 指到 IR 摄像头的稳定路径:
[video]
device_path = /dev/v4l/by-path/pci-0000:64:00.4-usb-0:1:1.2-video-index0inihowdy-next 的默认值是
device_path = none(自动探测,一般会撞上第一个设备也就是 RGB 的/dev/video0),用 IR 必须手动指定。注意是把
none整个替换掉、保持一行。我第一次手滑在none后面另起一行写路径,保存后值实际变成了两行,之后所有 howdy 命令直接拒绝执行:
~ ❯ sudo howdy config
Editing config.ini
text修法就是进编辑器把两行合并成一行。改完用 grep 确认只剩干净的一行:
grep device_path /etc/howdy/config.ini
# device_path = /dev/v4l/by-path/pci-0000:64:00.4-usb-0:1:1.2-video-index0bash事后翻 man 才发现根本不用开编辑器:
sudo howdy set device_path /dev/v4l/by-path/...(set KEY VALUE)一行改值,就没这些幺蛾子。
顺带一提
dark_threshold(过暗判定阈值)默认 60,IR 画面通常够亮,先不动,识别有问题再调。
配置好了可以看下画面。注意:AUR 包不带识别模型,要先做下一节的 download-models 才能测:
sudo howdy test # 还支持 --device <路径> 临时换摄像头试,不用改配置bash[ WARN:[email protected]] global net_impl_backend.cpp:345 setPreferableTarget Targets are not supported by the new graph engine for now
[ WARN:[email protected]] global net_impl_backend.cpp:345 setPreferableTarget Targets are not supported by the new graph engine for now
Opening a window with a test feed
Press Ctrl+C here or Q in preview window to quit
Click on the image to enable or disable slow modetext如果提示 Face model is not ready,说明识别模型还没下载——AUR 包不带 ONNX 模型,得先 sudo howdy download-models(下一节第一步)。模型就位后 test 弹出的预览里:IR 的画面是灰度的,人脸应该很亮(红外光在脸上反光),这是正常现象。

3. 下载模型与录入人脸#
识别用的 ONNX 模型(YuNet 检测 + SFace 嵌入)AUR 包不带,要手动下一次(官方 Getting-Started 也要求这步,忘了的话 howdy add / test 都会提示 Face model is not ready):
sudo howdy download-modelsbash~ ❯ sudo howdy download-models
Downloading face_detection_yunet_2026may.onnx
Downloading face_recognition_sface_2021dec_int8.onnx
OpenCV face models ready in: /usr/share/howdy/modelstext然后录入:
~ ❯ sudo howdy add
[ WARN:[email protected]] global net_impl_backend.cpp:345 setPreferableTarget Targets are not supported by the new graph engine for now
[ WARN:[email protected]] global net_impl_backend.cpp:345 setPreferableTarget Targets are not supported by the new graph engine for now
Enter a label for this new model [automatic]:
Please look straight into the camera
Scan complete
Added a new model to xxxx
bash它会先问 Enter a label for this new model [automatic]:,直接回车用自动标签就行(想区分场景也可以起名,比如「正面」「戴眼镜」)。接着对着摄像头正常坐姿录一组即可。v3.4.1 加强了 sudo/doas/pkexec 下的身份解析,sudo howdy add 会正确录到发起 sudo 的用户名下,不会误录成 root。
录入时可能刷出两行
[ WARN:0@...] setPreferableTarget Targets are not supported by the new graph engine for now,是 OpenCV 5 新图引擎的日志噪音,无害,识别照常。
录完确认一下:
~ ❯ sudo howdy list
0 2026-10-25 22:58:18 Model #0
bash几个相关命令和路径(都来自
man howdy):识别不准可以多录几组不同光照、不同角度的模型;howdy list看 ID,howdy remove ID删单条,howdy clear全清;给别的用户录加-U 用户名。识别用的 ONNX 模型打包在/usr/share/howdy/models/,万一提示缺模型就sudo howdy download-models;录入的人脸模型存在/etc/howdy/models/。
4. 接入 PAM:sudo、锁屏、登录界面#
原理一句话:在 PAM 认证栈的最前面插一行 sufficient 的 howdy——人脸成功就直接放行,失败则继续走后面的密码认证。永远不会因为刷脸失败而把人锁在外面,最坏情况就是多等一两秒回到输密码。
howdy-next 是原生模块,PAM 行比老版简洁(老版要借道 pam-python):
auth sufficient /usr/lib/security/pam_howdy.sotext四个接入点,按风险从低到高逐个开,每改一个就开新终端验证上一处还能正常认证:
| 场景 | PAM 文件 | 操作 |
|---|---|---|
| sudo 提权 | /etc/pam.d/sudo | 直接编辑(文件本来就在 /etc 下) |
| KDE 锁屏(kscreenlocker) | /etc/pam.d/kde | 从 /usr/lib/pam.d/kde 复制过来再改(/etc 优先级更高) |
| 登录界面(plasmalogin) | /etc/pam.d/plasmalogin | 同上,这个可以不改,因为如果用人脸识别,进去后还得输入密码解锁kde钱包 |
| 图形提权弹窗(polkit) | /etc/pam.d/polkit-1 | 同上,可选 |
Plasma 6.8 开始登录管理器是 plasmalogin(不再是 SDDM),PAM 文件叫
plasmalogin。锁屏走的是 kscreenlocker 提供的kde。这俩默认只在/usr/lib/pam.d/里有,复制到/etc/pam.d/形成覆盖,别直接改/usr/lib下的(包升级会被冲掉)。
4.1 sudo#
编辑 /etc/pam.d/sudo,在第一行 auth include system-auth 之前加上 howdy 行:
#%PAM-1.0
auth sufficient /usr/lib/security/pam_howdy.so workaround=native
auth include system-auth
account include system-auth
session include system-auth
session optional pam_systemd.so class=nonetext立刻开个新终端验证:
sudo -k whoami # -k 清掉缓存的凭证,强制走一遍完整认证bash预期能看到摄像头灯亮一下(或 IR 补光灯闪一下),不输密码直接出结果。人脸失败的话会正常提示输密码,说明回退链路也是好的。实测:
~ ❯ paru -Sy
[ WARN:[email protected]] global net_impl_backend.cpp:345 setPreferableTarget Targets are not supported by the new graph engine for now
[ WARN:[email protected]] global net_impl_backend.cpp:345 setPreferableTarget Targets are not supported by the new graph engine for now
Face matched user XXXX
:: 正在同步软件包数据库...textFace matched user XXXX 就是刷脸成功的标志。那两行 WARN 是 OpenCV 5 的日志噪音,无害,而且消不掉——详见「排障」一节。
为什么建议加 workaround=native#
默认配置下 PAM 是「先刷脸、失败或超时才轮到密码」,这个「超时」由 [video] timeout 控制(默认 4 秒)。问题在于:非交互或没人守着的场景下这个等待很要命。比如脚本里调 sudo、SSH 会话里执行 sudo、或者你人已经离开电脑,就必然先白等 4 秒才弹密码提示。
man pam_howdy 给了三种 workaround 模式,选择原则是看场景有没有可用 TTY:
| 模式 | 行为 | 适用场景 |
|---|---|---|
| 不写(默认) | 先刷脸,失败/超时才到密码 | 简单,但非交互场景会白等 |
workaround=native | 密码提示与刷脸并行,谁先成功用谁 | 终端场景(sudo),需要可用 TTY |
workaround=native-input | 原生优先,无 TTY 时退回模拟 Enter 按键 | 图形场景(锁屏、polkit 弹窗) |
所以 sudo 这行建议写成:
auth sufficient /usr/lib/security/pam_howdy.so workaround=nativetext这样 sudo 一执行就同时刷脸和弹密码提示:人在就刷脸,人不在就直接敲密码,不用干等。
【测试时补充:识别耗时体感,可以开 [debug] end_report = true 量化】
4.2 KDE 锁屏#
sudo cp /usr/lib/pam.d/kde /etc/pam.d/kdebash编辑 /etc/pam.d/kde,同样在第一个 auth 行前插入。锁屏是图形场景,用 workaround=native-input(图形会话往往没有可用 TTY,native 会静默失效;native-input 会先试原生、失败再退回模拟按键):
#%PAM-1.0
auth sufficient /usr/lib/security/pam_howdy.so workaround=native-input
auth include system-local-login
account include system-local-login
password include system-local-login
session include system-local-logintext锁屏(Meta+L 或菜单锁定),随便按个键唤醒,应该直接刷脸进桌面,密码框仍保留作为回退。
4.3 登录界面(plasmalogin)#
sudo cp /usr/lib/pam.d/plasmalogin /etc/pam.d/plasmaloginbash编辑 /etc/pam.d/plasmalogin,在文件顶部插入:
#%PAM-1.0
auth sufficient /usr/lib/security/pam_howdy.so
auth include system-login
-auth optional pam_gnome_keyring.so
-auth optional pam_kwallet5.so
account include system-login
password include system-login
-password optional pam_gnome_keyring.so use_authtok
session optional pam_keyinit.so force revoke
session include system-login
-session optional pam_gnome_keyring.so auto_start
-session optional pam_kwallet5.so auto_starttext注意:
pam_kwallet5.so/pam_gnome_keyring.so需要首次认证的密码来自动解锁钱包。刷脸登录时没有输过密码,钱包不会被自动解锁,登录后可能弹一次钱包密码框——这是人脸登录方案的通病,接受不了就把这步跳过(登录界面继续用密码,锁屏和 sudo 用人脸)。kwallet 常年空密码的话就无所谓。
一个绕不过去的限制:登录界面一定是「先密码、后刷脸」#
重启实测会发现:plasmalogin 的登录界面先显示密码框,回车之后摄像头才开始工作。PAM 配置本身没错,这是 plasmalogin 的架构决定的:
- 登录管理器要先完成「选用户、选会话」这些 UI 交互,之后才调 PAM 认证
- 它在 PAM 调用前就已经把输入框画出来了,而且不会主动触发一次认证
workaround=native/native-input在这里都帮不上忙(man 里明确写了图形会话往往没有可用 TTY;且 plasmalogin 是先收集输入再调 PAM,属于 man 里描述的「Lock Screens That Collect Input Before PAM」那一类)
换句话说:登录界面这一层,人脸识别只能省掉「输密码」但不能省掉「回车」。实际体验是回车一下就开始刷脸,其实也还行。
如果觉得这一步没意义,可以把
/etc/pam.d/plasmalogin里的 howdy 行删掉——开机老老实实输密码,真正的刷脸收益放在锁屏(Meta+L唤醒)和 sudo 上,这两处才是高频且顺畅的场景。
4.4 polkit(可选)#
图形界面里点「安装软件」「修改系统设置」弹的提权对话框走 polkit。这也是图形场景,用 workaround=native-input:
sudo cp /usr/lib/pam.d/polkit-1 /etc/pam.d/polkit-1bash同样在第一个 auth 行前插入 howdy 行:
#%PAM-1.0
auth sufficient /usr/lib/security/pam_howdy.so workaround=native-input
auth include system-auth
account include system-auth
password include system-auth
session include system-authtext图形提权弹窗一般没有 TTY,
native-input的原生模式可能失效、退回「模拟按一次 Enter」的兜底路径。那条路径是通过/dev/uinput发送的,man pam_howdy明确警告:这个 Enter 不是定向发给 PAM 弹窗的,而是发给当前焦点窗口,如果焦点在别的程序上可能误触。功能上不影响安全(刷脸失败照样要输密码),但知道这点没坏处。
5. 安全与体验#
几个必须知道的点:
- 人脸弱于密码。项目 README 自己也这么写。别把它当成唯一认证手段——本文的配置下密码回退永远可用,这是底线。笔记本上有敏感数据的,考虑给重要操作保留纯密码路径(比如不给
su加 howdy) - 每次认证摄像头灯都会亮。sudo、锁屏、polkit 只要触发就是一次拍照比对,灯闪是正常现象
- howdy-next 移除了认证快照保存功能,老 Howdy「失败截图存在磁盘上泄漏人脸」的问题不存在
- 识别灵敏度和安全度的平衡靠
[face] sface_threshold(人脸匹配阈值)调,默认值先用着:认不出本人就调低、怕被误识就调高(yunet_score_threshold管的是检测环节,一般不动)。全部配置项看man howdy.ini - 临时不想刷脸(演示、外接显示器角度不对)不用动 PAM:
sudo howdy disable 1直接停用,disable 0恢复 - 几个贴心开关(
man howdy.ini):abort_if_lid_closed = true合盖用外接屏时跳过刷脸直接走密码,abort_if_ssh = trueSSH 会话跳过刷脸;[debug] end_report = true每次认证后打印耗时明细,想量化识别速度就开它 - 别把 howdy 接进 sshd 能碰到的链路。本文只改了服务级 PAM 文件、没动
system-auth,SSH 远程登录不会触发本机摄像头 - 自动化脚本调 sudo 时要注意:没人守在摄像头前,默认配置会先白等一个
timeout(4 秒)再弹密码。这就是上面推荐workaround=native的原因——密码提示同时出现,脚本里可以直接喂密码。真要跑无人值守任务,用sudo -n(禁止提示)或给特定命令配NOPASSWD,别让脚本卡在刷脸上
排障#
第一工具是日志:
journalctl -b -t pam_howdybash| 症状 | 处理 |
|---|---|
| 识别慢 | 确认用的是 IR(GREY 640x360)而不是 RGB 1280x720;日志里看耗时 |
| 识别不准(认不出本人) | howdy add 多录几组不同光照角度;或调低 [face] sface_threshold |
| 误识率偏高 | 调高 sface_threshold,并确认密码回退仍然可用 |
| 登录界面卡在人脸不放行 | TTY(Ctrl+Alt+F3)或 SSH 进去把 /etc/pam.d/plasmalogin 里的 howdy 行删掉再回来 |
howdy test 窗口开不出来 | Wayland 下 OpenCV HighGUI 偶尔犯毛病,QT_QPA_PLATFORM=xcb sudo howdy test 试试 |
| 摄像头被占用 | 确认没有别的程序(浏览器、cheese)抓着摄像头 |
| 每次认证都刷两行 OpenCV WARN | 消不掉,无害,见下文 |
有两行 OpenCV WARN 一直消不掉#
每次认证都会刷这两行:
[ WARN:[email protected]] global net_impl_backend.cpp:345 setPreferableTarget Targets are not supported by the new graph engine for now
[ WARN:[email protected]] global net_impl_backend.cpp:345 setPreferableTarget Targets are not supported by the new graph engine for nowtext这是 OpenCV 5 的已知误报,作者在 issue #26 ↗(跟踪上游修复)和 #29 ↗(用户报告)里解释得很清楚:
- howdy 加载 YuNet/SFace 模型后,会按 OpenCV 的惯例请求
DNN_TARGET_CPU - 但 OpenCV 5 的新 DNN 图引擎本来就只在 CPU 上跑,这个请求是空操作(no-op)
- 新引擎没做「静默接受 CPU target」的处理,于是每次都吐一条警告
- 推理和认证完全不受影响,这条日志也不是「回退到旧引擎」的信号
作者在 howdy 侧故意拒绝隐藏的(#29 里作者明确拒绝了这几种做法):
- 没有配置开关,
setPreferableTarget调用硬编码在对比进程里 - 作者拒绝在 howdy 内屏蔽:全局调 OpenCV 日志级别会连带隐藏其他有意义的警告
OPENCV_LOG_LEVEL=ERROR在实践中确实不好使——我实测ERROR、SILENT、WARNING三个级别跑howdy test,没用
真正的修复在上游 OpenCV:作者已提交 PR opencv/opencv#29457 ↗,合并后 CPU target 会静默、非 CPU target 仍告警。该修复会随 OpenCV 5.1 发布,
- 等 Arch 仓库的 OpenCV 升到 5.1,警告自然消失
其他问题可以直接查作者的 Troubleshooting wiki ↗。
卸载与回滚#
# 1. 把四个文件里的 howdy 行删掉,或者直接删掉复制过来的覆盖文件(恢复系统默认)
sudo rm /etc/pam.d/kde /etc/pam.d/plasmalogin /etc/pam.d/polkit-1
# /etc/pam.d/sudo 是系统文件,手动删掉那行 howdy
# 2. 卸载软件包
paru -R howdy-next
# 3. 万一 PAM 改坏了进不去:Ctrl+Alt+F3 切 TTY(或 SSH)登录,
# 此时 sudo 可能已经不可用,用 su - 切 root 再恢复备份
su -
cp -r /home/XXXX/pam.d.bak.*/. /etc/pam.d/bash