Last active
August 23, 2025 13:59
-
-
Save daym/6c0bcd137c9ba43790b4140cdf8bb7d9 to your computer and use it in GitHub Desktop.
guix howdy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/gnu/packages/linux.scm b/gnu/packages/linux.scm | |
index a3d02cf025..c6f9858eea 100644 | |
--- a/gnu/packages/linux.scm | |
+++ b/gnu/packages/linux.scm | |
@@ -115,6 +115,7 @@ (define-module (gnu packages linux) | |
#:use-module (gnu packages boost) | |
#:use-module (gnu packages calendar) | |
#:use-module (gnu packages check) | |
+ #:use-module (gnu packages cmake) | |
#:use-module (gnu packages cpio) | |
#:use-module (gnu packages cpp) | |
#:use-module (gnu packages crates-io) | |
@@ -148,6 +149,7 @@ (define-module (gnu packages linux) | |
#:use-module (gnu packages haskell-apps) | |
#:use-module (gnu packages haskell-xyz) | |
#:use-module (gnu packages image) | |
+ #:use-module (gnu packages image-processing) | |
#:use-module (gnu packages kde-frameworks) | |
#:use-module (gnu packages libevent) | |
#:use-module (gnu packages libunwind) | |
@@ -2123,6 +2125,110 @@ (define-public pam-gnupg | |
GnuPG-based password manager like @code{pass}.") | |
(license license:gpl3+))) | |
+;; It seems that the PAM module just runs the Python detector as root ?! That is terrible. | |
+;; Instead, it should be a service (just like gpg-agent; although there is no reason it should run as the regular user and plenty of reasons it should run under a service user)--and the Howdy PAM module should connect to that. | |
+;; If you absolutely have to, setuid to that service user before executing the Python program. | |
+;; I don't think that would do serializable execution correctly, though--see also <https://github.com/boltgolt/howdy/issues/895>. | |
+;; Luckily, Howdy has -U --user option that allow per-user configuration, but it's not working as I thought. When I ran: | |
+;; sudo howdy -U silverbullet069_ftp disable 1 (silverbullet069_ftp is my FTP user) It also disables Howdy at my main account (which is silverbullet069) | |
+;; If there was a service, it would also be clear who reads a custom config.ini and where it was generated from. | |
+;; Howdy does not currenty support that, but has a concept called "recorders". Every recorder is responsible for getting the video feed from the configured video device and delivering it to OpenCV. | |
+(define-public howdy | |
+ (package | |
+ (name "howdy") | |
+ (version "2.6.1") | |
+ (source (origin | |
+ (method git-fetch) | |
+ (uri (git-reference | |
+ (url "https://github.com/boltgolt/howdy.git") | |
+ (commit "c4521c14ab8c672cadbc826a3dbec9ef95b7adb1"))) ; beta branch. | |
+ (file-name (git-file-name name version)) | |
+ (sha256 | |
+ (base32 | |
+ "09gf8w7y06b1pwxfahxpbnl65rxcdmhrn100g949m7nxl27mbw6b")))) | |
+ (build-system meson-build-system) | |
+ (arguments | |
+ (list | |
+ #:imported-modules | |
+ `(,@%meson-build-system-modules | |
+ (guix build python-build-system)) | |
+ #:modules | |
+ `((guix build meson-build-system) | |
+ ((guix build python-build-system) #:prefix python:) | |
+ (guix build utils)) | |
+ #:configure-flags | |
+ #~(list "-Dwith_polkit=true" ; install admin interface's polkit policy | |
+ "-Dinstall_pam_config=false" ; some Debian thing | |
+ "-Dinstall_in_site_packages=true" ; others can import it in Python | |
+ ; not sure we need that. "-Dsysconfdir=/etc" | |
+ ;;; OK: pam_dir (default: prefix / "/lib/security") | |
+ ;;; OK: log_path (default: "/var/log/howdy") | |
+ (string-append "-Dconfig_dir=" "/etc/howdy") ; make it configurable. | |
+ "-Duser_models_dir=/var/lib/howdy/models" ; was: /etc/howdy/models | |
+ (string-append "-Dpython_path=" | |
+ (search-input-file %build-inputs "bin/python3"))) | |
+ #:phases | |
+ #~(modify-phases %standard-phases | |
+ (add-after 'unpack 'patch | |
+ (lambda* (#:key inputs #:allow-other-keys) | |
+ (write inputs) | |
+ (newline) | |
+ ;; Add python shebang. | |
+ (invoke "sed" "-i" "1i#!/usr/bin/python3" | |
+ "howdy/src/compare.py") | |
+ (chmod "howdy/src/compare.py" #o755) | |
+ (substitute* "howdy/src/pam/main.cc" | |
+ (("PYTHON_EXECUTABLE_PATH") | |
+ "\"/run/privileged/bin/pkexec\"")) | |
+ ;; It seems that howdy has no support for changing what the config_dir is at runtime. | |
+ ;; We DO need that--so we'll have to figure out a way to do it. | |
+ ;; The safest and least likely way would be to adapt the package dynamically when it's used in PAM. | |
+ ;; See ./howdy/src/paths.py.in and ./howdy-gtk/src/paths.py.in | |
+ ;; and ./howdy/src/pam/paths.hh.in:const auto CONFIG_FILE_PATH = "@config_file_path@"; | |
+ ;; Allow using global mutable config in /etc (for now). | |
+ ;; Prevent actually installing the config to /etc here. | |
+ (substitute* "howdy/src/meson.build" | |
+ ;; Note: install_tag: 'config' | |
+ (("install_data\\('config.ini'.*") "\n")))) | |
+ (add-after 'install 'install-system-models | |
+ (lambda* (#:key inputs outputs #:allow-other-keys) | |
+ (with-directory-excursion (string-append (assoc-ref outputs "out") | |
+ "/share/dlib-data") | |
+ (for-each (lambda (key) | |
+ (copy-file (assoc-ref inputs key) key) | |
+ (invoke "bunzip2" key)) | |
+ '("dlib_face_recognition_resnet_model_v1.dat.bz2" | |
+ "mmod_human_face_detector.dat.bz2" | |
+ "shape_predictor_5_face_landmarks.dat.bz2"))))) | |
+ (add-after 'glib-or-gtk-wrap 'wrap-python | |
+ (assoc-ref python:%standard-phases 'wrap))))) | |
+ (native-inputs | |
+ (list bzip2 cmake pkg-config sed | |
+ ;; We got those URL from howdy's "install.sh". | |
+ ;; The license of the data files is CC0. | |
+ ;; See also: <https://github.com/davisking/dlib-models/>. | |
+ (origin (method url-fetch) | |
+ (uri "https://github.com/davisking/dlib-models/raw/master/dlib_face_recognition_resnet_model_v1.dat.bz2") | |
+ (sha256 | |
+ (base32 | |
+ "0fjm265l1fz5zdzx5n5yphl0v0vfajyw50ffamc4cd74848gdcdb"))) | |
+ (origin (method url-fetch) | |
+ (uri "https://github.com/davisking/dlib-models/raw/master/mmod_human_face_detector.dat.bz2") | |
+ (sha256 | |
+ (base32 | |
+ "117wv582nsn585am2n9mg5q830qnn8skjr1yxgaiihcjy109x7nv"))) | |
+ (origin (method url-fetch) | |
+ (uri "https://github.com/davisking/dlib-models/raw/master/shape_predictor_5_face_landmarks.dat.bz2") | |
+ (sha256 | |
+ (base32 | |
+ "0wm4bbwnja7ik7r28pv00qrl3i1h6811zkgnjfvzv7jwpyz7ny3f"))))) | |
+ (inputs | |
+ (list opencv ffmpeg v4l-utils python python-elevate python-pamela libinih libevdev linux-pam)) | |
+ (synopsis "Authentication via face recognition") | |
+ (description "This package allows authentication via face recognition.") | |
+ (home-page "https://github.com/boltgolt/howdy") | |
+ (license license:expat))) | |
+ | |
;;; | |
;;; Kernel documentation | |
diff --git a/gnu/services/base.scm b/gnu/services/base.scm | |
index 7331c030d7..9d772f8891 100644 | |
--- a/gnu/services/base.scm | |
+++ b/gnu/services/base.scm | |
@@ -934,7 +934,9 @@ (define-record-type* <login-configuration> | |
;; Allow empty passwords by default so that first-time users can log in when | |
;; the 'root' account has just been created. | |
(allow-empty-passwords? login-configuration-allow-empty-passwords? | |
- (default #t))) ;Boolean | |
+ (default #t)) ;Boolean | |
+ (howdy? login-configuration-howdy? | |
+ (default #f))) ;Boolean | |
(define (login-pam-service config) | |
"Return the list of PAM service needed for CONF." | |
diff --git a/gnu/services/desktop.scm b/gnu/services/desktop.scm | |
index e544656182..a09803c9e5 100644 | |
--- a/gnu/services/desktop.scm | |
+++ b/gnu/services/desktop.scm | |
@@ -116,6 +116,7 @@ (define-module (gnu services desktop) | |
gvfs-service-type | |
colord-service-type | |
+ howdy-service-type | |
geoclue-application | |
geoclue-configuration | |
@@ -944,6 +945,43 @@ (define colord-service-type | |
interface to manage the color profiles of input and output devices such as | |
screens and scanners."))) | |
+ | |
+;;; | |
+;;; Howdy. | |
+;;; | |
+ | |
+(define %howdy-pam-services | |
+ ;(list (unix-pam-service "howdy")) | |
+ (let ((deny (pam-entry | |
+ (control "required") | |
+ (module "pam_deny.so")))) | |
+ (pam-service | |
+ (name "other") | |
+ (account (list deny)) | |
+ (auth (list deny)) | |
+ (password (list deny)) | |
+ (session (list deny))))) | |
+ | |
+(define-record-type* <howdy-configuration> | |
+ howdy-configuration make-howdy-configuration | |
+ howdy-configuration? | |
+ (howdy howdy-configuration-howdy | |
+ (default howdy))) | |
+ | |
+;; This is mostly that you can run the howdy interface. | |
+;; Unfortunately, the actual face detection via opencv is not run as a | |
+;; service--and it's run as root! | |
+(define howdy-service-type | |
+ (let ((udisks-package (lambda (config) | |
+ (list (howdy-configuration-howdy config))))) | |
+ (service-type (name 'howdy) | |
+ (extensions | |
+ (list (service-extension polkit-service-type howdy-package))) | |
+ (default-value (howdy-configuration)) | |
+ (description | |
+ "Run @command{howdy}, a system service that allows | |
+user authentication by facial recognition.")))) | |
+ | |
;;; | |
;;; UDisks. | |
diff --git a/gnu/system/pam.scm b/gnu/system/pam.scm | |
index 07b84b04ef..72228992cb 100644 | |
--- a/gnu/system/pam.scm | |
+++ b/gnu/system/pam.scm | |
@@ -221,7 +221,7 @@ (define unix-pam-service | |
(control "required") | |
(module "pam_env.so")))) | |
(lambda* (name #:key allow-empty-passwords? allow-root? motd | |
- login-uid? gnupg?) | |
+ login-uid? gnupg? howdy?) | |
"Return a standard Unix-style PAM service for NAME. When | |
ALLOW-EMPTY-PASSWORDS? is true, allow empty passwords. When ALLOW-ROOT? is | |
true, allow root to run the command without authentication. When MOTD is | |
@@ -229,7 +229,8 @@ (define unix-pam-service | |
When LOGIN-UID? is true, require the 'pam_loginuid' module; that module sets | |
/proc/self/loginuid, which the libc 'getlogin' function relies on. When | |
GNUPG? is true, require the 'pam_gnupg.so' module; that module hands over | |
-the login password to 'gpg-agent'." | |
+the login password to 'gpg-agent'. When HOWDY? is true, require the | |
+pam_howdy module; that module does authentication." | |
;; See <http://www.linux-pam.org/Linux-PAM-html/sag-configuration-example.html>. | |
(pam-service | |
(name name) | |
@@ -239,6 +240,11 @@ (define unix-pam-service | |
(control "sufficient") | |
(module "pam_rootok.so"))) | |
'()) | |
+ (if howdy? | |
+ (list (pam-entry | |
+ (control "sufficient") | |
+ (module (file-append howdy "/lib/security/pam_howdy.so")))) | |
+ '()) | |
(list (if allow-empty-passwords? | |
(pam-entry | |
(control "required") | |
@@ -289,19 +295,20 @@ (define (rootok-pam-service command) | |
(password (list unix)) | |
(session (list unix))))) | |
-(define* (base-pam-services #:key allow-empty-passwords?) | |
+(define* (base-pam-services #:key allow-empty-passwords? howdy?) | |
"Return the list of basic PAM services everyone would want." | |
;; TODO: Add other Shadow programs? | |
(append (list %pam-other-services) | |
;; These programs are setuid-root. | |
(map (cut unix-pam-service <> | |
- #:allow-empty-passwords? allow-empty-passwords?) | |
+ #:allow-empty-passwords? allow-empty-passwords? #:howdy? howdy?) | |
'("passwd" "chfn" "sudo")) | |
;; This is setuid-root, as well. Allow root to run "su" without | |
;; authenticating. | |
(list (unix-pam-service "su" | |
#:allow-empty-passwords? allow-empty-passwords? | |
+ #:howdy? howdy? | |
#:allow-root? #t)) | |
;; These programs are not setuid-root, and we want root to be able |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment