PAM-based facial authentication for Linux
Project description
Face-Unlock
Face-Unlock is a PAM-based facial authentication system for Linux. It hooks into the PAM (Pluggable Authentication Modules) stack to provide face recognition everywhere — sudo, GDM login, polkit, TTY login, and any other PAM-aware service. No cloud, no proprietary APIs, no daemons. Just your webcam, dlib, and a few OpenCV frames.
Features
- PAM Integration — plugs into any PAM service:
sudo,gdm-password,polkit-1,login, and more - Liveness Detection — blink-based liveness via Eye Aspect Ratio (EAR) using dlib's facial landmarks
- HOG + CNN Detectors — choose between fast HOG or more accurate CNN-based face detection
- Rate Limiting & Lockout — file-based per-user rate limiting with configurable thresholds
- Secure Encoding Storage — face encodings stored as numpy arrays with HMAC-SHA256 integrity signatures (no pickle, fixing CWE-502)
- Notification + Choice Dialogs — on failed auth, a desktop notification appears with a "Try Face Again" / "Use Password" dialog
- Headless Mode — works on servers and SSH sessions where no display is available
- Automatic Session Detection — detects user displays, Wayland/X11 sessions, and greeter processes (GDM, LightDM, SDDM, etc.)
- Audit Logging — all auth events logged to syslog with
LOG_AUTHfacility - Configurable — all parameters adjustable via
/etc/face-unlock/config.ini
Quick Start
Prerequisites
- Linux (Debian/Ubuntu-based recommended)
- Python 3.8+
- A webcam
Installation
git clone https://github.com/mohit31kumar/face-unlock.git
cd face-unlock
sudo ./install.sh
The installer will:
- Install system dependencies (cmake, dlib, OpenCV, zenity, libnotify)
- Install Python packages
- Copy library files to
/usr/local/lib/face-unlock/ - Create wrapper scripts in
/usr/local/bin/ - Generate a default config at
/etc/face-unlock/config.ini - Generate a secret HMAC key at
/etc/face-unlock/secret.key - Patch PAM configuration for
gdm-password,sudo,polkit-1,login, andcommon-auth
Enroll a User
sudo face-unlock-enroll <your-username>
Follow the on-screen instructions — look at the camera while it captures 5 face samples.
Test
# Quick diagnostic
sudo face-unlock-test
# Test recognition against an enrolled user
sudo face-unlock-test --user <your-username>
# Test liveness (blink) detection
sudo face-unlock-test --liveness
# Test the auth choice dialog
sudo face-unlock-test --dialog
Try It
# This should trigger face auth via PAM
sudo -i
Toggle Authentication
# Enable face-unlock in all PAM files
sudo face-unlock enable
# Disable face-unlock from all PAM files
sudo face-unlock disable
# Set face as the default (tried first, password fallback)
sudo face-unlock default on
# Set password as the default (tried first, face fallback)
sudo face-unlock default off
# Check current state
sudo face-unlock status
Usage
CLI Reference
| Command | Description |
|---|---|
face-unlock enroll <user> |
Enroll a user interactively |
face-unlock list |
List enrolled users and encoding counts |
face-unlock remove <user> |
Remove a user's encodings |
face-unlock test |
Run diagnostics |
face-unlock config |
Edit config in $EDITOR |
face-unlock enable |
Enable face-unlock in PAM files |
face-unlock disable |
Disable face-unlock from PAM files |
face-unlock default on/off |
Set face-first or password-first auth order |
face-unlock status |
Show current PAM state for all files |
Enrollment Options
sudo face-unlock-enroll <username> [options]
Options:
--headless Auto-capture frames without GUI preview
--cnn Use CNN detector (more accurate, slower)
--device PATH Specify camera device (e.g., /dev/video1)
--samples N Number of face samples (default: 5)
--jitters N Encoding jitter iterations (default: 5)
Testing Options
sudo face-unlock-test [options]
Options:
--user USER Test recognition for an enrolled user
--liveness Test blink-based liveness detection
--dialog Test notification + choice dialog
--device PATH Specify camera device
--cnn Use CNN detector
--timeout SECS Capture timeout (default: 10)
Configuration
All settings are in /etc/face-unlock/config.ini. Edit with:
sudo face-unlock config
[core]
| Key | Default | Description |
|---|---|---|
use_cnn |
false |
Use CNN detector (slower, more accurate) |
timeout |
5 |
Total auth attempt timeout in seconds |
retries |
3 |
Number of capture retries before falling back |
disabled |
false |
Disable face auth globally |
show_auth_dialog |
true |
Show "Try Again / Use Password" dialog on failure |
auto_capture_timeout |
3 |
Seconds to wait for a face before auto-capturing |
dialog_timeout |
10 |
Seconds before the choice dialog auto-dismisses |
[camera]
| Key | Default | Description |
|---|---|---|
device_path |
(empty) | Camera device path (auto-detected if empty) |
frame_width |
640 |
Capture frame width |
frame_height |
480 |
Capture frame height |
max_height |
480 |
Maximum height for face detection |
[recognition]
| Key | Default | Description |
|---|---|---|
tolerance |
0.55 |
Face match tolerance (lower = stricter) |
num_jitters |
5 |
Encoding jitter iterations |
liveness_blinks |
1 |
Number of blinks required for liveness |
liveness_timeout |
3 |
Seconds to wait for blinks |
[ratelimit]
| Key | Default | Description |
|---|---|---|
max_attempts |
5 |
Failed attempts before lockout |
lockout_minutes |
5 |
Lockout duration in minutes |
[storage]
| Key | Default | Description |
|---|---|---|
encodings_dir |
/etc/face-unlock/encodings |
Directory for face encoding files |
How It Works
PAM Service (sudo, GDM, polkit, ...)
│
▼
pam_exec.so ──► face-unlock-auth
│
▼
authenticate.py
│
├── Rate-limit check (file-based, per-user)
├── Load encodings (numpy + HMAC verification)
├── Open camera (config-driven resolution)
├── Liveness check (EAR blink detection via dlib landmarks)
├── Capture face frame (auto or guided)
├── Detect face (HOG or CNN)
├── Compute 128-d encoding
├── Compare against enrolled encodings (cosine similarity)
│
├── Success ✔ ──► PAM_SUCCESS
│
└── Failure ✘ ──► notify-send + zenity dialog
│
├── "Try Again" ──► retry capture
└── "Use Password" ──► skip to password prompt
Architecture
| Module | Role |
|---|---|
authenticate.py |
PAM auth entry point, orchestrates the full flow |
face_engine.py |
Face detection (HOG/CNN), 128-d encoding, EAR computation |
camera.py |
OpenCV video capture, headless-safe GUI wrappers |
storage.py |
HMAC-signed numpy encoding storage, username validation |
config.py |
INI config reader with typed getters and syslog fallback |
session.py |
DISPLAY/WAYLAND session detection via /proc scanning |
dialog.py |
Desktop notification + zenity choice dialog |
enroll.py |
Interactive enrollment with GUI and headless modes |
test.py |
Diagnostics for camera, recognition, liveness, dialog |
pam.py |
PAM file manipulation (enable/disable/toggle auth order) |
cli.py |
Management CLI (enroll, list, remove, test, config, enable, disable, default, status) |
Security
- No pickle deserialization — legacy
.pklfiles are rejected; encodings usenumpy.save/numpy.loadonly (CWE-502) - HMAC-SHA256 integrity signing — each encoding file has a
.sigsidecar verified before loading; key stored at/etc/face-unlock/secret.key(chmod 600) - Username validation — alphanumeric +
._-, max 32 chars, preventing path traversal - Rate limiting — per-user file-based throttle with
fcntlfile locking to prevent race conditions; lockout directory and files created with restrictive permissions (0o700/0o600) - Fail-closed errors — rate limit file corruption results in denial, not bypass
- No network — all processing is local; no data leaves your machine
- Minimal attack surface — no daemons, no DBus services, no network sockets
Project Structure
face-unlock/
├── face_unlock/
│ ├── __init__.py
│ ├── authenticate.py # PAM auth module
│ ├── camera.py # OpenCV camera capture
│ ├── cli.py # Management CLI
│ ├── config.py # Configuration reader
│ ├── dialog.py # Notification + choice dialog
│ ├── enroll.py # User enrollment
│ ├── face_engine.py # Face detection & recognition
│ ├── pam.py # PAM file manipulation
│ ├── session.py # Display session detection
│ ├── storage.py # Encoding storage with HMAC
│ └── test.py # Diagnostics
├── tests/
│ ├── test_pam.py # Unit tests for pam.py
│ └── integration_test.sh # Integration test with real PAM files
├── install.sh # System installer
├── uninstall.sh # System uninstaller
├── requirements.txt # Python dependencies
├── pyproject.toml # Package metadata
├── LICENSE # Apache 2.0
├── CONTRIBUTING.md # Contribution guidelines
└── .github/
├── workflows/
│ └── ci.yml # GitHub Actions CI
└── ISSUE_TEMPLATE/
├── bug_report.md
└── feature_request.md
Uninstall
sudo ./uninstall.sh
The uninstaller will:
- Remove wrapper scripts and library files
- Optionally remove configuration and encodings
- Restore PAM files from backup (or remove face-unlock lines)
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
License
Copyright 2026 mohit31kumar
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file linux_face_unlock-1.0.0.tar.gz.
File metadata
- Download URL: linux_face_unlock-1.0.0.tar.gz
- Upload date:
- Size: 31.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2edaf1d3b08db7e2107f641ee50824969138228478eed126a8c42cadeb970eaf
|
|
| MD5 |
26a77c17622247b3fc65148012946eae
|
|
| BLAKE2b-256 |
f2dfb26bc2e96db4eab9b6154caf220c81a19e86214c57e848468e50cfafea8e
|
File details
Details for the file linux_face_unlock-1.0.0-py3-none-any.whl.
File metadata
- Download URL: linux_face_unlock-1.0.0-py3-none-any.whl
- Upload date:
- Size: 28.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2771b5a8d7040c01e5bc85d58ead76fefa30b87f74ad97a13cbc4ace9c09adbc
|
|
| MD5 |
678fbb9628630182d63b140f3247e95c
|
|
| BLAKE2b-256 |
ff4fef7c4455f22b61cb1a85505be8f4d82ca0922d1eae11fd0dd0a19b2f934a
|