From 1b347abe25b9e9dfdf59216ce515baa52ec56531 Mon Sep 17 00:00:00 2001 From: Joris Date: Fri, 19 Apr 2024 16:49:53 +0200 Subject: [PATCH] Inhibit short press on remote control power off Manage short press upon power off ourselves, and not with systemd anymore, so that we only listen to the Power Button, and not to power off buttons located on keyboards or remote controls. This means pressing the Power Button on the computer with a short press directly shutdowns the computer, and pressing power off on a remote control with a short press does nothing. --- application.nix | 17 +---- application/power-management/default.nix | 73 +++++++++++++++++++ .../power-management/power-button-shutdown.py | 23 ++++++ 3 files changed, 100 insertions(+), 13 deletions(-) create mode 100644 application/power-management/default.nix create mode 100644 application/power-management/power-button-shutdown.py diff --git a/application.nix b/application.nix index 51f6a962..c92d2024 100644 --- a/application.nix +++ b/application.nix @@ -26,7 +26,10 @@ rec { module = { config, lib, pkgs, ... }: { - imports = [ ./application/playos-status.nix ]; + imports = [ + ./application/playos-status.nix + ./application/power-management/default.nix + ]; # Kiosk runs as a non-privileged user users.users.play = { @@ -118,18 +121,6 @@ rec { }; }; - # Ignore system control keys that do not make sense for kiosk applications - services.logind.extraConfig = '' - HandleSuspendKey=ignore - HandleRebootKey=ignore - HandleHibernateKey=ignore - HandlePowerKey=poweroff - HandlePowerKeyLongPress=poweroff - HandleRebootKeyLongPress=poweroff - HandleSuspendKeyLongPress=poweroff - HandleHibernateKeyLongPress=poweroff - ''; - # Driver service systemd.services."dividat-driver" = { description = "Dividat Driver"; diff --git a/application/power-management/default.nix b/application/power-management/default.nix new file mode 100644 index 00000000..241e4f48 --- /dev/null +++ b/application/power-management/default.nix @@ -0,0 +1,73 @@ +{ pkgs, config, lib, ... }: + +let + + power-button-shutdown = pkgs.stdenv.mkDerivation { + name = "power-button-shutdown"; + propagatedBuildInputs = [ + (pkgs.python3.withPackages (pythonPackages: with pythonPackages; [ + evdev + ])) + ]; + dontUnpack = true; + installPhase = "install -Dm755 ${./power-button-shutdown.py} $out/bin/power-button-shutdown"; + }; + +in { + + # Ignore system control keys that do not make sense for kiosk applications + services.logind.extraConfig = '' + HandleSuspendKey=ignore + HandleRebootKey=ignore + HandleHibernateKey=ignore + HandlePowerKey=ignore + HandlePowerKeyLongPress=poweroff + HandleRebootKeyLongPress=poweroff + HandleSuspendKeyLongPress=poweroff + HandleHibernateKeyLongPress=poweroff + ''; + + hardware.uinput.enable = lib.mkDefault true; + + systemd.services.power-button-shutdown = { + enable = true; + description = "Detect power off key press, by power button device only, then shutdown computer"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${power-button-shutdown}/bin/power-button-shutdown"; + Restart = "always"; + + # Access to input devices without being root + # DynamicUser = true; # Currently prevent to shutdown the system + SupplementaryGroups = with config.users.groups; [ input.name uinput.name ]; + + # Hardening, see https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/hardware/kanata.nix#L117 + # Not using DeviceAllow and DevicePolicy, as I couldn’t get access to device list with that. + CapabilityBoundingSet = [ "" ]; + IPAddressDeny = [ "any" ]; + LockPersonality = true; + MemoryDenyWriteExecute = true; + PrivateNetwork = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + RestrictAddressFamilies = [ "AF_UNIX" ]; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = [ "native" ]; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "~@resources" + ]; + UMask = "0077"; + }; + }; +} diff --git a/application/power-management/power-button-shutdown.py b/application/power-management/power-button-shutdown.py new file mode 100644 index 00000000..9e771b03 --- /dev/null +++ b/application/power-management/power-button-shutdown.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 + +import evdev +import logging +import os +import sys + +logging.basicConfig(level=logging.INFO) + +devices = [evdev.InputDevice(path) for path in evdev.list_devices()] +device_names = ', '.join([d.name for d in devices]) +logging.info(f'Found devices: {device_names}') + +power_off_device = next((d for d in devices if d.name == 'Power Button'), None) +if power_off_device is None: + logging.error(f'Power Off device not found') + sys.exit(1) + +logging.info(f'Listenning to Power Off on device {power_off_device.path}') +for event in power_off_device.read_loop(): + if event.type == evdev.ecodes.EV_KEY and evdev.ecodes.KEY[event.code] == 'KEY_POWER' and event.value: + logging.info('KEY_POWER detected on Power Off device, shutting down') + os.system('shutdown now')