Skip to main content

PAM-based facial authentication for Linux

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

linux_face_unlock-1.0.0.tar.gz (31.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

linux_face_unlock-1.0.0-py3-none-any.whl (28.4 kB view details)

Uploaded Python 3

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

Hashes for linux_face_unlock-1.0.0.tar.gz
Algorithm Hash digest
SHA256 2edaf1d3b08db7e2107f641ee50824969138228478eed126a8c42cadeb970eaf
MD5 26a77c17622247b3fc65148012946eae
BLAKE2b-256 f2dfb26bc2e96db4eab9b6154caf220c81a19e86214c57e848468e50cfafea8e

See more details on using hashes here.

File details

Details for the file linux_face_unlock-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for linux_face_unlock-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2771b5a8d7040c01e5bc85d58ead76fefa30b87f74ad97a13cbc4ace9c09adbc
MD5 678fbb9628630182d63b140f3247e95c
BLAKE2b-256 ff4fef7c4455f22b61cb1a85505be8f4d82ca0922d1eae11fd0dd0a19b2f934a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page