From b54e20c33d9fa31e2242ba354b945ab322c790b8 Mon Sep 17 00:00:00 2001 From: Manuel Bluhm Date: Mon, 7 Oct 2024 20:38:17 +0400 Subject: [PATCH] Login user setup Signed-off-by: Manuel Bluhm --- modules/common/security/sshkeys.nix | 10 +++ modules/common/services/audio.nix | 7 -- modules/common/services/fprint.nix | 4 +- modules/common/users/accounts.nix | 83 ++++++++++++++----- modules/desktop/graphics/labwc.config.nix | 2 +- modules/desktop/graphics/labwc.nix | 2 +- .../virtualization/microvm/audiovm.nix | 11 +++ .../microvm/common/shared-directory.nix | 2 +- .../microvm/common/storagevm.nix | 22 ++++- .../microvm/virtualization/microvm/guivm.nix | 20 ++--- .../virtualization/microvm/microvm-host.nix | 28 ++++--- .../microvm/virtualization/microvm/netvm.nix | 9 ++ .../personalize/authorizedSshKeys.nix | 1 + packages/bt-launcher/default.nix | 11 ++- packages/nm-launcher/default.nix | 8 +- packages/ssh-keys-helper/default.nix | 3 +- 16 files changed, 155 insertions(+), 68 deletions(-) diff --git a/modules/common/security/sshkeys.nix b/modules/common/security/sshkeys.nix index c2cd17131..f9ded49de 100644 --- a/modules/common/security/sshkeys.nix +++ b/modules/common/security/sshkeys.nix @@ -31,6 +31,16 @@ in default = "/run/waypipe-ssh-public-key/id_ed25519.pub"; description = "The Waypipe public key"; }; + userToAudiovmSshPublicKeyFiles = mkOption { + type = types.str; + default = "/run/waypipe-ssh-public-key/id_ed25519_ad.pub"; + description = "The Waypipe public key"; + }; + userToNetvmSshPublicKeyFiles = mkOption { + type = types.str; + default = "/run/waypipe-ssh-public-key/id_ed25519_net.pub"; + description = "The Waypipe public key"; + }; sshKeyPath = mkOption { type = types.str; default = "/run/waypipe-ssh/id_ed25519"; diff --git a/modules/common/services/audio.nix b/modules/common/services/audio.nix index 5ac719fc8..ee4677e98 100644 --- a/modules/common/services/audio.nix +++ b/modules/common/services/audio.nix @@ -64,13 +64,6 @@ in }; }; - # Allow ghaf user to access pulseaudio and pipewire - users.extraUsers.ghaf.extraGroups = [ - "audio" - "video" - "pipewire" - ]; - # Start pipewire on system boot systemd.services.pipewire.wantedBy = [ "multi-user.target" ]; diff --git a/modules/common/services/fprint.nix b/modules/common/services/fprint.nix index 87654b43f..9b9d673d9 100644 --- a/modules/common/services/fprint.nix +++ b/modules/common/services/fprint.nix @@ -46,14 +46,14 @@ in // Allow user to verify fingerprints polkit.addRule(function(action, subject) { if (action.id == "net.reactivated.fprint.device.verify" && - subject.user == "ghaf") { + subject.isInGroup ("users")) { return polkit.Result.YES; } }); // Allow user to enroll fingerprints polkit.addRule(function(action, subject) { if (action.id == "net.reactivated.fprint.device.enroll" && - subject.user == "ghaf") { + subject.isInGroup ("users")) { return polkit.Result.YES; } }); diff --git a/modules/common/users/accounts.nix b/modules/common/users/accounts.nix index a474cd408..3c337101f 100644 --- a/modules/common/users/accounts.nix +++ b/modules/common/users/accounts.nix @@ -1,6 +1,10 @@ # Copyright 2022-2024 TII (SSRC) and the Ghaf contributors # SPDX-License-Identifier: Apache-2.0 -{ config, lib, ... }: +{ + config, + lib, + ... +}: # account for the development time login with sudo rights let cfg = config.ghaf.users.accounts; @@ -8,6 +12,7 @@ let mkEnableOption mkOption optionals + optionalAttrs mkIf types ; @@ -18,37 +23,75 @@ in enable = mkEnableOption "Default account Setup"; user = mkOption { default = "ghaf"; - type = with types; str; + type = types.str; description = '' - A default user to create in the system. + The admin account with sudo rights. ''; }; password = mkOption { default = "ghaf"; - type = with types; str; + type = types.str; + description = '' + Default password for the admin user. + ''; + }; + enableLoginUser = mkEnableOption "Enable login user setup for UI."; + loginuser = mkOption { + default = "manuel"; + type = types.str; + description = '' + Default user account for UI. + ''; + }; + loginuid = mkOption { + default = 1001; + type = types.int; description = '' - A default password for the user. + Default UID for the login user. ''; }; }; config = mkIf cfg.enable { users = { - mutableUsers = false; - users."${cfg.user}" = { - isNormalUser = true; - inherit (cfg) password; - #TODO add "docker" use "lib.optionals" - extraGroups = [ - "wheel" - "video" - "networkmanager" - ] ++ optionals config.security.tpm2.enable [ "tss" ]; - }; - groups."${cfg.user}" = { - name = cfg.user; - members = [ cfg.user ]; - }; + mutableUsers = cfg.enableLoginUser; + users = + { + "${cfg.user}" = { + isNormalUser = true; + inherit (cfg) password; + extraGroups = + [ + "wheel" + "video" + ] + ++ optionals config.security.tpm2.enable [ "tss" ] + ++ optionals config.ghaf.virtualization.docker.daemon.enable [ "docker" ]; + }; + } + // optionalAttrs cfg.enableLoginUser { + "${cfg.loginuser}" = { + isNormalUser = true; + uid = cfg.loginuid; + inherit (cfg) password; + extraGroups = [ + "video" + ]; + }; + }; + groups = + { + "${cfg.user}" = { + name = cfg.user; + members = [ cfg.user ]; + }; + } + // optionalAttrs cfg.enableLoginUser { + "${cfg.loginuser}" = { + name = cfg.loginuser; + members = [ cfg.loginuser ]; + }; + }; }; # to build ghaf as ghaf-user with caches diff --git a/modules/desktop/graphics/labwc.config.nix b/modules/desktop/graphics/labwc.config.nix index 44e708909..3a4c9030f 100644 --- a/modules/desktop/graphics/labwc.config.nix +++ b/modules/desktop/graphics/labwc.config.nix @@ -206,7 +206,7 @@ in services.greetd.settings = { initial_session = lib.mkIf (cfg.autologinUser != null) { - user = "ghaf"; + user = config.ghaf.users.accounts.loginuser; command = "${labwc-session}/bin/labwc-session"; }; }; diff --git a/modules/desktop/graphics/labwc.nix b/modules/desktop/graphics/labwc.nix index f17a91588..1b32f4596 100644 --- a/modules/desktop/graphics/labwc.nix +++ b/modules/desktop/graphics/labwc.nix @@ -26,7 +26,7 @@ in }; autologinUser = lib.mkOption { type = lib.types.nullOr lib.types.str; - default = config.ghaf.users.accounts.user; + default = config.ghaf.users.accounts.loginuser; description = '' Username of the account that will be automatically logged in to the desktop. If unspecified, the login manager is shown as usual. diff --git a/modules/microvm/virtualization/microvm/audiovm.nix b/modules/microvm/virtualization/microvm/audiovm.nix index e423430f5..f30e455ca 100644 --- a/modules/microvm/virtualization/microvm/audiovm.nix +++ b/modules/microvm/virtualization/microvm/audiovm.nix @@ -78,6 +78,17 @@ let ] ++ lib.optional config.ghaf.development.debug.tools.enable pkgs.alsa-utils; }; + users.users."proxy-user-audio" = { + isNormalUser = true; + uid = config.ghaf.users.accounts.loginuid; + createHome = false; + extraGroups = [ + "audio" + "video" + "pipewire" + ]; + }; + time.timeZone = config.time.timeZone; system.stateVersion = lib.trivial.release; diff --git a/modules/microvm/virtualization/microvm/common/shared-directory.nix b/modules/microvm/virtualization/microvm/common/shared-directory.nix index 80db4d668..d5f77017d 100644 --- a/modules/microvm/virtualization/microvm/common/shared-directory.nix +++ b/modules/microvm/virtualization/microvm/common/shared-directory.nix @@ -5,7 +5,7 @@ name: let cfg = config.ghaf.storagevm; shared-mountPath = "/tmp/shared/shares"; - inherit (config.ghaf.users.accounts) user; + user = config.ghaf.users.accounts.loginuser; isGuiVm = builtins.stringLength name == 0; userDir = "/home/${user}" + (if isGuiVm then "/Shares" else "/Unsafe\ share"); in diff --git a/modules/microvm/virtualization/microvm/common/storagevm.nix b/modules/microvm/virtualization/microvm/common/storagevm.nix index 15e3b84e0..aa431e23c 100644 --- a/modules/microvm/virtualization/microvm/common/storagevm.nix +++ b/modules/microvm/virtualization/microvm/common/storagevm.nix @@ -3,10 +3,19 @@ { lib, config, ... }: let cfg = config.ghaf.storagevm; + inherit (lib) + mkEnableOption + mkOption + mkIf + mkMerge + mkForce + types + optionals + ; mountPath = "/guestStorage"; in { - options.ghaf.storagevm = with lib; { + options.ghaf.storagevm = { enable = mkEnableOption "StorageVM support"; name = mkOption { @@ -85,9 +94,14 @@ in environment.persistence.${mountPath} = lib.mkMerge [ { hideMounts = true; - directories = [ - "/var/lib/nixos" - ]; + directories = + [ + "/var/lib/nixos" + ] + ++ optionals config.ghaf.users.accounts.enableLoginUser [ + # TODO Replace with userborn setup + "/etc" + ]; files = [ "/etc/ssh/ssh_host_ed25519_key.pub" diff --git a/modules/microvm/virtualization/microvm/guivm.nix b/modules/microvm/virtualization/microvm/guivm.nix index e7d0ee55a..1b1ae856a 100644 --- a/modules/microvm/virtualization/microvm/guivm.nix +++ b/modules/microvm/virtualization/microvm/guivm.nix @@ -34,6 +34,7 @@ let { ghaf = { users.accounts.enable = lib.mkDefault config.ghaf.users.accounts.enable; + users.accounts.enableLoginUser = true; profiles = { debug.enable = lib.mkDefault config.ghaf.profiles.debug.enable; applications.enable = false; @@ -68,15 +69,7 @@ let storagevm = { enable = true; name = "guivm"; - directories = [ - { - directory = "/var/lib/private/ollama"; - inherit (config.ghaf.users.accounts) user; - group = "ollama"; - mode = "u=rwx,g=,o="; - } - ]; - users.${config.ghaf.users.accounts.user}.directories = [ + users.${config.ghaf.users.accounts.loginuser}.directories = [ ".cache" ".config" ".local" @@ -94,9 +87,16 @@ let keygenScript = pkgs.writeShellScriptBin "waypipe-ssh-keygen" '' set -xeuo pipefail mkdir -p /run/waypipe-ssh + mkdir -p /run/user-ssh echo -en "\n\n\n" | ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /run/waypipe-ssh/id_ed25519 -C "" - chown ghaf:ghaf /run/waypipe-ssh/* + chown ${config.ghaf.users.accounts.user}:${config.ghaf.users.accounts.user} /run/waypipe-ssh/* cp /run/waypipe-ssh/id_ed25519.pub /run/waypipe-ssh-public-key/id_ed25519.pub + echo -en "\n\n\n" | ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /run/user-ssh/id_ed25519_net -C "proxy-user-network@net-vm" + chown ${config.ghaf.users.accounts.loginuser}:${config.ghaf.users.accounts.loginuser} /run/user-ssh/* + cp /run/user-ssh/id_ed25519_net.pub /run/waypipe-ssh-public-key/id_ed25519_net.pub + echo -en "\n\n\n" | ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /run/user-ssh/id_ed25519_ad -C "proxy-user-audio@audio-vm" + chown ${config.ghaf.users.accounts.loginuser}:${config.ghaf.users.accounts.loginuser} /run/user-ssh/* + cp /run/user-ssh/id_ed25519_ad.pub /run/waypipe-ssh-public-key/id_ed25519_ad.pub ''; in { diff --git a/modules/microvm/virtualization/microvm/microvm-host.nix b/modules/microvm/virtualization/microvm/microvm-host.nix index ad09e66a3..ab9fcd09b 100644 --- a/modules/microvm/virtualization/microvm/microvm-host.nix +++ b/modules/microvm/virtualization/microvm/microvm-host.nix @@ -9,6 +9,13 @@ }: let cfg = config.ghaf.virtualization.microvm-host; + inherit (lib) + mkEnableOption + mkOption + mkIf + mkMerge + types + ; in { imports = [ @@ -16,25 +23,25 @@ in inputs.self.nixosModules.givc-host ]; options.ghaf.virtualization.microvm-host = { - enable = lib.mkEnableOption "MicroVM Host"; - networkSupport = lib.mkEnableOption "Network support services to run host applications."; + enable = mkEnableOption "MicroVM Host"; + networkSupport = mkEnableOption "Network support services to run host applications."; sharedVmDirectory = { - enable = lib.mkEnableOption "shared directory" // { + enable = mkEnableOption "shared directory" // { default = true; }; - vms = lib.mkOption { + vms = mkOption { description = '' List of names of virtual machines for which unsafe shared folder will be enabled. ''; - type = lib.types.listOf lib.types.str; + type = types.listOf types.str; default = [ ]; }; }; }; - config = lib.mkMerge [ - (lib.mkIf cfg.enable { + config = mkMerge [ + (mkIf cfg.enable { microvm.host.enable = true; microvm.host.useNotifySockets = true; ghaf.systemd = { @@ -80,7 +87,7 @@ in }; }) - (lib.mkIf cfg.sharedVmDirectory.enable { + (mkIf cfg.sharedVmDirectory.enable { ghaf.virtualization.microvm.guivm.extraModules = [ (import ./common/shared-directory.nix "") ]; # Create directories required for sharing files with correct permissions. @@ -88,15 +95,14 @@ in let vmDirs = map ( n: - "d /storagevm/shared/shares/Unsafe\\x20${n}\\x20share/ 0700 ${config.ghaf.users.accounts.user} users" + "d /storagevm/shared/shares/Unsafe\\x20${n}\\x20share/ 0760 ${toString config.ghaf.users.accounts.loginuid} users" ) cfg.sharedVmDirectory.vms; in [ "d /storagevm/shared 0755 root root" - "d /storagevm/shared/shares 0700 ${config.ghaf.users.accounts.user} users" + "d /storagevm/shared/shares 0760 ${toString config.ghaf.users.accounts.loginuid} users" ] ++ vmDirs; - }) ]; } diff --git a/modules/microvm/virtualization/microvm/netvm.nix b/modules/microvm/virtualization/microvm/netvm.nix index 9fa2830a5..410156134 100644 --- a/modules/microvm/virtualization/microvm/netvm.nix +++ b/modules/microvm/virtualization/microvm/netvm.nix @@ -139,6 +139,15 @@ let ${config.ghaf.security.sshKeys.waypipeSshPublicKeyDir}.options = [ "ro" ]; }; + users.users."proxy-user-network" = { + isNormalUser = true; + createHome = false; + uid = config.ghaf.users.accounts.loginuid; + extraGroups = [ + "networkmanager" + ]; + }; + # SSH is very picky about to file permissions and ownership and will # accept neither direct path inside /nix/store or symlink that points # there. Therefore we copy the file to /etc/ssh/get-auth-keys (by diff --git a/modules/reference/personalize/authorizedSshKeys.nix b/modules/reference/personalize/authorizedSshKeys.nix index 111592b91..349f6a4f3 100644 --- a/modules/reference/personalize/authorizedSshKeys.nix +++ b/modules/reference/personalize/authorizedSshKeys.nix @@ -20,6 +20,7 @@ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAHVXc4s7e8j1uFsgHPBzpWvSI/hk5Zf6Btuj79D4hf3 tervis@tervis-servu" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM3w7NzqMuF+OAiIcYWyP9+J3kwvYMKQ+QeY9J8QjAXm shamma-alblooshi@tii.ae" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB/iv9RWMN6D9zmEU85XkaU8fAWJreWkv3znan87uqTW humaid@tahr" + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICwsW+YJw6ukhoWPEBLN93EFiGhN7H2VJn5yZcKId56W mb@mmm" # For ghaf-installer automated testing: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAolaKCuIUBQSBFGFZI1taNX+JTAr8edqUts7A6k2Kv7" diff --git a/packages/bt-launcher/default.nix b/packages/bt-launcher/default.nix index 518ed6b2b..706ece2b8 100644 --- a/packages/bt-launcher/default.nix +++ b/packages/bt-launcher/default.nix @@ -17,23 +17,22 @@ writeShellApplication { export DBUS_SESSION_BUS_ADDRESS=unix:path=/tmp/ssh_session_dbus.sock export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/tmp/ssh_system_dbus.sock ${openssh}/bin/ssh -M -S /tmp/control_socket_bt \ - -f -N -q ghaf@audio-vm \ - -i /run/waypipe-ssh/id_ed25519 \ + -f -N -q proxy-user-audio@audio-vm \ + -i /run/user-ssh/id_ed25519_ad \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -o StreamLocalBindUnlink=yes \ -o ExitOnForwardFailure=yes \ - -L /tmp/ssh_session_dbus.sock:/run/user/1000/bus \ + -L /tmp/ssh_session_dbus.sock:/run/user/"$UID"/bus \ -L /tmp/ssh_system_dbus.sock:/run/dbus/system_bus_socket # Use the control socket to close the ssh tunnel. close-tunnel() { - ${openssh}/bin/ssh -q -S /tmp/control_socket_bt -O exit ghaf@audio-vm + ${openssh}/bin/ssh -q -S /tmp/control_socket_bt -O exit proxy-user-audio@audio-vm } launch-blueman() { ${blueman}/bin/blueman-applet & ${blueman}/bin/blueman-manager - close-tunnel } status() { @@ -50,7 +49,6 @@ writeShellApplication { status="{\"powered\":\"$BT_POWERED\",\"discoverable\":\"$BT_DISCOVERABLE\",\"pairable\":\"$BT_PAIRABLE\",\"discovering\":\"$BT_DISCOVERING\",\"alias\":\"$BT_ALIAS\"}" echo "$status" - close-tunnel } if [ $# -eq 0 ]; then @@ -58,6 +56,7 @@ writeShellApplication { elif [ "$1" = "status" ]; then status fi + close-tunnel ''; meta = { diff --git a/packages/nm-launcher/default.nix b/packages/nm-launcher/default.nix index 5bdb38439..d433b388f 100644 --- a/packages/nm-launcher/default.nix +++ b/packages/nm-launcher/default.nix @@ -19,17 +19,17 @@ writeShellApplication { # export DBUS_SESSION_BUS_ADDRESS=unix:path=/tmp/ssh_session_dbus.sock export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/tmp/ssh_system_dbus.sock ${openssh}/bin/ssh -M -S /tmp/control_socket \ - -f -N -q ghaf@net-vm \ - -i /run/waypipe-ssh/id_ed25519 \ + -f -N -q proxy-user-network@net-vm \ + -i /run/user-ssh/id_ed25519_net \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -o StreamLocalBindUnlink=yes \ -o ExitOnForwardFailure=yes \ - -L /tmp/ssh_session_dbus.sock:/run/user/1000/bus \ + -L /tmp/ssh_session_dbus.sock:/run/user/"$UID"/bus \ -L /tmp/ssh_system_dbus.sock:/run/dbus/system_bus_socket ${networkmanagerapplet}/bin/nm-applet --indicator # Use the control socket to close the ssh tunnel. - ${openssh}/bin/ssh -q -S /tmp/control_socket -O exit ghaf@net-vm + ${openssh}/bin/ssh -q -S /tmp/control_socket -O exit proxy-user-network@net-vm ''; meta = { diff --git a/packages/ssh-keys-helper/default.nix b/packages/ssh-keys-helper/default.nix index e0dabb719..2b972ce3d 100644 --- a/packages/ssh-keys-helper/default.nix +++ b/packages/ssh-keys-helper/default.nix @@ -6,8 +6,9 @@ source = let script = pkgs.writeShellScriptBin config.ghaf.security.sshKeys.getAuthKeysFileName '' - [[ "$1" != "ghaf" ]] && exit 0 ${pkgs.coreutils}/bin/cat ${config.ghaf.security.sshKeys.waypipeSshPublicKeyFile} + ${pkgs.coreutils}/bin/cat ${config.ghaf.security.sshKeys.userToAudiovmSshPublicKeyFiles} + ${pkgs.coreutils}/bin/cat ${config.ghaf.security.sshKeys.userToNetvmSshPublicKeyFiles} ''; in "${script}/bin/${config.ghaf.security.sshKeys.getAuthKeysFileName}";