Skip to main content

Face-Unlock

License Python Platform PRs Welcome

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_AUTH facility
  • 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:

  1. Install system dependencies (cmake, dlib, OpenCV, zenity, libnotify)
  2. Install Python packages
  3. Copy library files to /usr/local/lib/face-unlock/
  4. Create wrapper scripts in /usr/local/bin/
  5. Generate a default config at /etc/face-unlock/config.ini
  6. Generate a secret HMAC key at /etc/face-unlock/secret.key
  7. Patch PAM configuration for gdm-password, sudo, polkit-1, login, and common-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 .pkl files are rejected; encodings use numpy.save/numpy.load only (CWE-502)
  • HMAC-SHA256 integrity signing — each encoding file has a .sig sidecar 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 fcntl file 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.

Metadata

Release files for linux-face-unlock 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for linux-face-unlock 1.0.0
File Size Uploaded
linux_face_unlock-1.0.0.tar.gz 31.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for linux-face-unlock 1.0.0
File Interpreter ABI Platform
linux_face_unlock-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 60.3 kB

Release files / linux_face_unlock-1.0.0.tar.gz

Download URL linux_face_unlock-1.0.0.tar.gz
Size 31.9 kB
Tags Source
SHA-256 checksum
How to use checksums
2edaf1d3b08db7e2107f641ee50824969138228478eed126a8c42cadeb970eaf
BLAKE2b-256 checksum
How to use checksums
f2dfb26bc2e96db4eab9b6154caf220c81a19e86214c57e848468e50cfafea8e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

Release files / linux_face_unlock-1.0.0-py3-none-any.whl

Download URL linux_face_unlock-1.0.0-py3-none-any.whl
Size 28.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2771b5a8d7040c01e5bc85d58ead76fefa30b87f74ad97a13cbc4ace9c09adbc
BLAKE2b-256 checksum
How to use checksums
ff4fef7c4455f22b61cb1a85505be8f4d82ca0922d1eae11fd0dd0a19b2f934a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page