OmarchyにHowdy Nextを入れて、顔だけでロック解除できるようにした
Omarchyのロック画面を、パスワードを入力せずに顔認証で解除できるようにした。
使ったのはHowdy Next。Linux向けの顔認証ソフトウェアで、HowdyをC++で再実装したものだ。
インストールと顔の登録だけで終わるかと思ったが、今回はロック画面側にも変更が必要だった。最終的には、Omarchyのロック画面プラグインをユーザー用に複製し、顔認証とパスワード認証を別々に動かす構成にした。
この記事は2026年10月15日に手元の環境で設定した記録。Omarchyのバージョンやロック画面の実装が異なる場合は、そのまま適用できるとは限らない。
環境
- Omarchy 4.0.4
- Howdy Next 3.4.2(AURパッケージは
howdy-next 3.4.2-1) - Omarchy標準のQuickshell製ロック画面
- 使用カメラ:Anker AnkerWork C310(Linux上のデバイス名:
AnkerWork C310 Webcam)
今回設定したのは、ログイン後の画面ロック解除。ログイン画面や sudo の顔認証設定は変更していない。
Howdy Nextをインストールする
公式READMEでは、Arch Linux向けにAURの howdy-next と howdy-next-git が案内されている。今回はリリース版の howdy-next を使った。
Omarchyでは次のコマンドで導入できる。
omarchy pkg aur add howdy-next
今回のビルドでは、パッケージに含まれる104件のテストがすべて成功した。
バージョン確認は次のとおり。--version ではなく、version サブコマンドを使う。
howdy version
Howdy Next 3.4.2 (554051d399)
導入したAURパッケージには、polkitのヘルパーがカメラなどにアクセスできるようにするsystemdの設定も含まれていた。
[Service]
PrivateDevices=no
DeviceAllow=char-video4linux rw
DeviceAllow=/dev/uinput rw
これはpolkitヘルパーのサンドボックス設定を一部緩和する変更になる。今回のロック画面用設定では、後述する入力注入のworkaroundは使っていない。
カメラを設定する
使用したのは、USB接続の Anker AnkerWork C310。以下はこのカメラで実際に設定し、ロック解除まで動作を確認した手順になる。
まず、カメラのデバイスを確認した。
ls -l /dev/v4l/by-id/
手元のカメラには video-index0 と video-index1 の2つがあった。v4l2-ctl で確認し、映像キャプチャ用の video-index0 を使うことにした。
v4l2-ctl --device /dev/video0 --all
/dev/video0 の番号は接続順などで変わる可能性があるので、設定には /dev/v4l/by-id/ 以下のパスを使った。
以下の <カメラのID> は、自分の環境で確認した名前に置き換える。
sudo howdy set device_path /dev/v4l/by-id/<カメラのID>-video-index0
設定ファイルは /etc/howdy/config.ini。変更後の該当部分は次のようになる。
[video]
device_path = /dev/v4l/by-id/<カメラのID>-video-index0
howdy config でエディタが見つからないと言われた
設定ファイルを開こうとして、最初にここでつまずいた。
sudo howdy config
Error: No suitable text editor found.
Set EDITOR to an executable name or path, or install one of: micro, nano, vi.
手元にはNeovimが入っていたので、EDITOR を明示すれば開ける。
sudo EDITOR=nvim howdy config
今回はカメラパスの変更だけだったので、実際の設定には howdy set を使った。
モデルを取得し、顔を登録する
続いて認識用モデルをダウンロードし、自分の顔を登録する。
sudo howdy download-models
sudo howdy add
sudo howdy test
顔の登録状況は次のコマンドで確認できる。<ユーザー名> は実際にロック解除するユーザーを指定する。
sudo howdy -U <ユーザー名> list
ここまでで、Howdy側のカメラ設定と顔登録ができた。
PAMに追加するだけでは、期待する動作にならなかった
この環境のOmarchyロック画面は、パスワード認証に次のPAM設定を使っていた。
/etc/pam.d/omarchy-lock-password
最初はここに次の行を追加した。
auth sufficient pam_howdy.so
このとき、追加する位置にも注意が必要だった。ファイルの末尾に置くと、それより前のパスワード認証失敗処理で終了し、Howdyまで到達しない。
pam_faillock.so preauth の後、パスワード認証の前に移動すると、カメラによる認証は動くようになった。
しかし、まだ期待した使い勝手ではなかった。
ロック画面で何かを入力してEnterを押してから、カメラが動く。
見た目には「パスワードを入れてから、さらに顔認証を求められる」ように感じる。実際には顔認証が成功すれば入力したパスワードの正しさは要求されないが、入力自体が必要だった。
ロック画面が空欄の送信を受け付けていなかった
ロック画面の実装を確認すると、次の条件があった。
function submitPassword(value) {
var password = String(value || "")
if (!lockRequested || authenticatingPassword || password.length === 0) return
// この後で passwordPam.start() を呼ぶ
}
空文字の場合は認証処理を始めずに戻る。つまり、空欄でEnterを押してもPAMは呼ばれず、Howdyも動かない。
パスワード用のPAM設定にHowdyを追加するだけでは、「画面をロックしたら自動で顔認証」という動作にはならないことが分かった。
顔認証とパスワード認証を分ける
最終的には、次の構成に変更した。
| 認証方法 | 開始するタイミング | PAMサービス |
|---|---|---|
| 顔認証 | セッションのロックが確立したとき | omarchy-lock-face |
| パスワード認証 | パスワードを入力して送信したとき | omarchy-lock-password |
どちらか一方が成功すれば解除する。顔認証が動いている間も、パスワード入力は受け付ける。
顔認証専用のPAM設定
/etc/pam.d/omarchy-lock-face を作成した。
#%PAM-1.0
auth requisite pam_faillock.so preauth silent deny=10 unlock_time=120
auth requisite pam_howdy.so
auth required pam_faillock.so authsucc
account include system-local-login
既存のパスワード側に合わせて、失敗回数によるアカウントロックを確認する。顔認証に成功したときだけ、authsucc へ進むようにした。
顔認証の不一致を繰り返すだけでパスワードまでロックされないよう、この顔認証専用スタックには pam_faillock.so authfail を追加していない。
一方、omarchy-lock-password からは、途中で追加した pam_howdy.so の行を削除した。パスワード認証がカメラを待つことを避けるためだ。
PAM設定は変更前にバックアップを取った。認証設定を編集するときは、作業中のターミナルを残し、解除できることを確認してから閉じるようにしたい。
ロック画面をユーザー用プラグインにする
Omarchy本体の次のファイルを直接変更すると、更新で上書きされる。
/usr/share/omarchy/shell/plugins/lock/Service.qml
そこで、ロック画面をユーザー用プラグインとして複製した。今回の配置先は次のとおり。
~/.config/omarchy/plugins/polidog.lock/
Omarchyには複製用コマンドがある。
omarchy plugin clone omarchy.lock
ただし、今回はこのコマンドによる再スキャン中にシェルのIPCが応答しなくなり、複製処理が失敗した。そのため、作業用ディレクトリで変更を仕上げ、ユーザープラグインの場所に配置してからシェルを再起動した。
以下は最終的な設定内容。複製コマンドだけで最後まで完了したわけではない。
manifest.json では、複製元を clonedFrom で指定した。
{
"schemaVersion": 1,
"id": "polidog.lock",
"name": "Lock Screen with Face Authentication",
"version": "1.0.0",
"author": "Omarchy",
"description": "Custom lock screen with independent facial authentication.",
"omarchy": {
"capabilities": ["authentication"],
"clonedFrom": "omarchy.lock"
},
"kinds": ["service"],
"keepLoaded": true,
"entryPoints": {
"service": "Service.qml"
}
}
shell.json では、既存の設定を残しつつ、次の項目を追加した。これは設定全体ではなく、追加部分の抜粋。
{
"plugins": [{ "id": "polidog.lock" }],
"disabledPlugins": ["omarchy.lock"],
"cloneSourceRestores": ["polidog.lock"]
}
プラグインには Service.qml だけでなく、元のロック画面に含まれる LockView.qml などの関連ファイルも配置した。
顔認証用の PamContext を追加する
Service.qml のルートに、顔認証が実行中かどうかを保持するプロパティを追加した。
property bool faceAuthenticating: false
認証の開始処理は次のようにした。画面のロックが確立していることを確認してから開始し、重複した認証処理は走らせない。
function startFace() {
if (!lockRequested || !sessionLock.secure || facePam.active || faceAuthenticating) return
faceAuthenticating = true
if (!facePam.start()) {
faceAuthenticating = false
faceRetryTimer.restart()
}
}
顔認証用の PamContext と再試行用のタイマーも追加した。
PamContext {
id: facePam
config: "omarchy-lock-face"
user: root.userName
onResponseRequiredChanged: {
if (responseRequired) respond("")
}
onCompleted: function(result) {
root.faceAuthenticating = false
if (!root.lockRequested) return
if (result === PamResult.Success) root.finishUnlock()
else faceRetryTimer.restart()
}
onError: function(error) {
root.faceAuthenticating = false
if (root.lockRequested) faceRetryTimer.restart()
}
}
Timer {
id: faceRetryTimer
interval: 2000
repeat: false
onTriggered: root.startFace()
}
顔認証専用のPAMにはパスワード認証を含めていない。ここでの respond("") はパスワードを空で通す処理ではなく、PAM側から応答要求があった場合に空文字を返すもの。解除できるかどうかはPAMの成功結果で判断する。
再試行は「2秒ごとに必ずスキャンする」という意味ではない。1回の認証が失敗またはエラーで終了してから、2秒後に次を開始する。
ロック確立後に開始し、解除時に止める
既存の WlSessionLock にある onSecureStateChanged の if (secure) ブロックへ、次の呼び出しを追加した。
root.startFace()
指紋認証を開始している箇所と同じタイミングになる。
また、既存の resetAuthenticationState() に次を追加した。
faceRetryTimer.stop()
faceAuthenticating = false
if (facePam.active) facePam.abort()
解除された後もカメラ認証や再試行が残らないようにするためだ。
既存のパスワード処理は維持した。顔認証とパスワード認証のどちらからでも、成功したら既存の finishUnlock() へ進む構成になっている。
反映と確認
設定を配置した後、画面がロックされていない状態でシェルを再起動した。
omarchy restart shell
今回の環境では、起動直後のIPC応答に時間がかかることがあった。状態確認には待ち時間を長めに指定した。
OMARCHY_SHELL_IPC_TIMEOUT=20s omarchy-shell lock status
カスタマイズしたステータスに face: true が表示され、新しいロック画面が読み込まれていることを確認できた。この face 項目は今回追加した表示で、Howdyの設定やモデルを自動診断するものではない。
最後に実際に画面をロックして、何も入力せず、カメラに顔を向けるだけで解除できることを確認した。
気をつけたいこと
Howdy Nextの公式READMEでは、顔認証はパスワードより弱く、似た顔や写真などにだまされる可能性があると説明されている。IRカメラはなりすまし対策に役立つが、今回はIRを使う構成の確認までは行っていない。
今回もパスワード解除を残している。また、顔認証はロック中に再試行するため、カメラが繰り返し動作する構成になる。
ユーザープラグインはOmarchyの更新で直接上書きされない一方、標準ロック画面に入った修正も自動では取り込まれない。特に認証やロックの修正が入ったときは、元のプラグインとの差分を確認して追従する必要がある。
おわりに
一番のつまずきは、Howdyの認証設定と、ロック画面が認証を開始するタイミングが別だったこと。
PAMにHowdyを追加すれば顔認証は使える。しかし、今回のOmarchyではパスワード欄への入力が認証開始の条件だったため、それだけでは操作なしの解除にならなかった。
顔認証用のPAMと PamContext を分け、ロックの確立後に自動で開始するようにしたことで、ようやく「カメラに顔を向けるだけ」の解除ができるようになった。
参考
- komagataのブログ:参考記事
- Howdy Next リポジトリ・公式README
- AUR: howdy-next
- インストールされた
howdy(1)、howdy.ini(5)、pam_howdy(8) - 手元のOmarchy 4.0.4に含まれるロック画面とプラグイン管理の実装