心梦后花园

Back

我的笔记本是机械革命无界15XA,摄像头模组是 SunplusIT 的,带一颗红外摄像头,出厂就是给 Windows Hello 用的。装了 CachyOS 之后这颗 IR 摄像头一直吃灰,每次解锁、sudo 都得敲密码。这次想把它救活,用的是 howdy-next——一个 C++ 重写的 Howdy 实现,识别快、维护活跃。旧版 Howdy 我几年前用过,AUR 上还得装 -git 版本、认证时拉起 Python 2,体验一般;新版体验下来基本就是 Linux 版 Windows Hello。

路线选择#

人脸识别登录这块,Linux 上能选的方案目前有三条路:

方案现状技术栈评价
howdy 2.6.1boltgolt 官方稳定版,2023 年后基本停滞Python + pam-python + dlib / face_recognition能用,但每次认证都要拉起 Python,1~2 秒起步;依赖链停在 2023 年,几年前用过,体验一般
howdy 3.0 beta官方的 Rust + OpenVINO 重写,常年 beta,beta 分支一度从仓库消失Rust不推荐,安装渠道都不稳定
howdy-next 3.4.2nathawat 维护的现代化重实现,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-devices
bash
~ ❯ 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/media1
text

这里有个小坑:--list-devices 是按 USB 物理设备分组的,四个节点全挂在同一个 HD Webcam 名下,光看这个输出根本分不出哪个节点是 IR。要逐个节点查 Card type:

~ ❯ for d in /dev/video*; do echo "== $d =="; v4l2-ctl -d $d -D | grep "Card type"; done
bash
== /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 Camera
text

这下就清楚了:video0/1 是 RGB,video2/3 是 IR,两两一组(每组第一个是采集节点、第二个是元数据节点):

设备说明支持的格式
/dev/video0RGB 普通摄像头MJPG 1280x720 / 640x480 @30fps
/dev/video2IR 红外摄像头GREY 8-bit 640x360 @30fps

有 IR 摄像头优先用 IR:照片和屏幕在红外光下成不了像,天然防照片欺骗,而且暗光环境照常工作——这才是完整的 Windows Hello 体验。

/dev/video2 这种序号重启后理论上可能漂移,用稳定的 by-path 路径更保险。怎么从一堆软链里找到 IR 那条?——拿上一步确定的设备号去 grep:

ls -l /dev/v4l/by-path/ | grep video2
bash
pci-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 -> ../../video3
text

注意会命中两组: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-index0
text

路径拆开读: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 -i
bash

本文全程有个原则:只往各服务自己的 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-next
bash

依赖全部来自官方仓库:opencv(要求 ≥5.0,CachyOS 仓库里正好是 5.0.0,而且我这台本来就已经装了)、pam、libinih、yyjson 这些,编译的只有项目本身,CMake Unity 构建,一两分钟的事。注意它和老 howdy 包冲突,别装重了。

装完确认 PAM 模块就位:

ls -l /usr/lib/security/pam_howdy.so
bash
-rwxr-xr-x 1 root root 214984 10月25日 22:08 /usr/lib/security/pam_howdy.so
text

2. 配置 IR 摄像头#

配置文件在 /etc/howdy/config.ini。注意这跟老 Howdy 的 /usr/lib/security/howdy/config.ini 不是一回事——howdy-next 挪到了 FHS 规范的 /etc 下(man howdy 的 FILES 一节写得明明白白,查 man 比网上老教程靠谱):

sudo howdy config
bash

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-index0
ini

howdy-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-index0
bash

事后翻 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 mode
text

如果提示 Face model is not ready,说明识别模型还没下载——AUR 包不带 ONNX 模型,得先 sudo howdy download-models(下一节第一步)。模型就位后 test 弹出的预览里:IR 的画面是灰度的,人脸应该很亮(红外光在脸上反光),这是正常现象。

IR 摄像头测试画面:灰度成像、人脸因红外反光明显发亮

3. 下载模型与录入人脸#

识别用的 ONNX 模型(YuNet 检测 + SFace 嵌入)AUR 包不带,要手动下一次(官方 Getting-Started 也要求这步,忘了的话 howdy add / test 都会提示 Face model is not ready):

sudo howdy download-models
bash
~ ❯ 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/models
text

然后录入:


~ ❯ 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.so
text

四个接入点,按风险从低到高逐个开,每改一个就开新终端验证上一处还能正常认证:

场景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=none
text

立刻开个新终端验证:

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
:: 正在同步软件包数据库...
text

Face 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=native
text

这样 sudo 一执行就同时刷脸和弹密码提示:人在就刷脸,人不在就直接敲密码,不用干等。

【测试时补充:识别耗时体感,可以开 [debug] end_report = true 量化】

4.2 KDE 锁屏#

sudo cp /usr/lib/pam.d/kde /etc/pam.d/kde
bash

编辑 /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-login
text

锁屏(Meta+L 或菜单锁定),随便按个键唤醒,应该直接刷脸进桌面,密码框仍保留作为回退。

4.3 登录界面(plasmalogin)#

sudo cp /usr/lib/pam.d/plasmalogin /etc/pam.d/plasmalogin
bash

编辑 /etc/pam.d/plasmalogin,在文件顶部插入:

注意: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-1
bash

同样在第一个 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-auth
text

图形提权弹窗一般没有 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 = true SSH 会话跳过刷脸;[debug] end_report = true 每次认证后打印耗时明细,想量化识别速度就开它
  • 别把 howdy 接进 sshd 能碰到的链路。本文只改了服务级 PAM 文件、没动 system-auth,SSH 远程登录不会触发本机摄像头
  • 自动化脚本调 sudo 时要注意:没人守在摄像头前,默认配置会先白等一个 timeout(4 秒)再弹密码。这就是上面推荐 workaround=native 的原因——密码提示同时出现,脚本里可以直接喂密码。真要跑无人值守任务,用 sudo -n(禁止提示)或给特定命令配 NOPASSWD,别让脚本卡在刷脸上

排障#

第一工具是日志:

journalctl -b -t pam_howdy
bash
症状处理
识别慢确认用的是 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 now
text

这是 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

参考#

Howdy-next:给 CachyOS 加上 Windows Hello 式人脸登录
https://www.liyp.cc/blog/install-howdy-next-face-recognition-on-arch-linux
Author 万古一梦
Published at 2026年10月25日
Comment seems to stuck. Try to refresh?✨