diff --git a/Modules/System/Service/default.nix b/Modules/System/Service/default.nix new file mode 100644 index 0000000..568e902 --- /dev/null +++ b/Modules/System/Service/default.nix @@ -0,0 +1,10 @@ +{ + config, + lib, + pkgs, + ... +}: { + imports = [ + ./vnc-server.nix + ]; +} diff --git a/Modules/System/Service/vnc-server.nix b/Modules/System/Service/vnc-server.nix new file mode 100644 index 0000000..9b26188 --- /dev/null +++ b/Modules/System/Service/vnc-server.nix @@ -0,0 +1,253 @@ +# nyx-module/system/service/vnc.nix +# +# Provides: +# - TigerVNC multi-session server as a systemd service +# - Desktop session (.desktop file) for the chosen environment +# - Automatic password hashing at build time (via `vncpasswd -f`) +# - Optional firewall rule opening for chosen display port +# +# Options: +# enable → Enable the VNC server service +# user → System user that owns and runs the VNC server +# password → Plaintext password (hashed during build) +# displayNumber → X display number (e.g. :1 => TCP 5901) +# openFirewall → Open firewall for the corresponding TCP port +# session → Name of the desktop session (xfce, plasma, etc.) +# geometry → Screen resolution (e.g. 1920x1080) +# sessionCommand→ Command to start the X session +# +# Example: +# nyx-module.system.service.vnc = { +# enable = true; +# user = "myuser"; +# password = "secret12"; +# session = "xfce"; +# geometry = "1280x800"; +# }; +# +# After enabling, run: +# sudo nixos-rebuild switch +# sudo systemctl enable --now vncserver +# +{ + config, + pkgs, + lib, + ... +}: +with lib; let + cfg = config.nyx-module.system.service.vnc; + + # Resolve the user home safely even if the user isn't declared yet. + userHome = lib.attrByPath ["users" "users" cfg.user "home"] "/home/${cfg.user}" config; + + # Only generate passwd file if enabled, otherwise null + generatedPasswdFile = + if cfg.enable then + pkgs.runCommand "tigervnc-passwd" { + buildInputs = [ pkgs.tigervnc ]; + } '' + mkdir -p $out + echo -n "${cfg.password}" | vncpasswd -f > $out/passwd + '' + else + null; +in { + options.nyx-module.system.service.vnc = { + enable = mkEnableOption "Enable a TigerVNC multi-session server."; + + user = mkOption { + type = types.str; + example = "myuser"; + description = "System user that owns and runs the VNC server."; + }; + + password = mkOption { + type = types.str; + example = "secret12"; + description = '' + Plaintext VNC password. Will be hashed at build time using + `vncpasswd -f`. **Warning:** Stored in the Nix store, only + the first 8 characters are significant. + ''; + }; + + displayNumber = mkOption { + type = types.int; + default = 1; + description = "X display number (:1 => TCP 5901)."; + }; + + openFirewall = mkOption { + type = types.bool; + default = true; + description = "Open the firewall for the selected display port."; + }; + + session = mkOption { + type = types.str; + default = "xfce"; + example = "plasma"; + description = "Name of the desktop session (matches .desktop file)."; + }; + + geometry = mkOption { + type = types.str; + default = "1920x1080"; + description = "Default VNC display resolution."; + }; + + sessionCommand = mkOption { + type = types.str; + default = "${pkgs.xterm}/bin/xterm"; + example = "${pkgs.xfce4-session}/bin/startxfce4"; + description = "Command to start the X session."; + }; + }; + + config = mkIf cfg.enable { + # Install required packages + environment.systemPackages = with pkgs; [ + tigervnc + xorg.xauth + xorg.xinit + ]; + + # Enable X server and desktop environment + services.xserver = { + enable = true; + desktopManager.xfce.enable = true; + xkb.layout = "de"; + displayManager.defaultSession = cfg.session; # fixed option path + }; + + # Configure session + services.xserver.displayManager.session = [ + { + manage = "desktop"; + name = cfg.session; + start = '' + ${cfg.sessionCommand} & + waitPID=$! + ''; + } + ]; + + # Install a custom .desktop file for session + environment.etc."xdg/xsessions/${cfg.session}.desktop".text = '' + [Desktop Entry] + Name=${cfg.session} + Comment=Custom ${cfg.session} session for VNC + Exec=${cfg.sessionCommand} + Type=Application + ''; + + system.activationScripts.linkCustomSession = '' + mkdir -p /usr/share/xsessions + ln -sf /etc/xdg/xsessions/${cfg.session}.desktop \ + /usr/share/xsessions/${cfg.session}.desktop + ''; + + # Systemd service for VNC server + systemd.services.vncserver = { + description = "TigerVNC server on :${toString cfg.displayNumber}"; + after = ["network.target" "syslog.target"]; + wantedBy = ["multi-user.target"]; + + serviceConfig = { + Type = "simple"; + User = cfg.user; + TimeoutStartSec = "60s"; + WorkingDirectory = userHome; + PIDFile = "${userHome}/.vnc/%H:${toString cfg.displayNumber}.pid"; + + Environment = "PATH=${pkgs.xorg.xinit}/bin:${pkgs.tigervnc}/bin:${pkgs.xorg.xauth}/bin:/run/current-system/sw/bin"; + + ExecStartPre = pkgs.writeShellScript "prepare-vnc" '' + mkdir -p ${userHome}/.vnc + cp ${generatedPasswdFile}/passwd ${userHome}/.vnc/passwd + chmod 600 ${userHome}/.vnc/passwd + chown ${cfg.user}:users ${userHome}/.vnc/passwd + + mkdir -p ${userHome}/.config/tigervnc + cat > ${userHome}/.config/tigervnc/config < ${userHome}/.vnc/xstartup <<'EOF' + #!/bin/sh + + export DISPLAY=:${toString cfg.displayNumber} + export XAUTHORITY="$HOME/.Xauthority" + export XDG_RUNTIME_DIR="/run/user/$(id -u)" + export XKL_XMODMAP_DISABLE=1 + export GTK_USE_PORTAL=0 + export PULSE_SERVER=unix:/run/user/$(id -u)/pulse/native + export PULSE_SERVER=tcp:localhost:4714 + # Log: + echo "=== Starting VNC session on $(date) ===" >> "$HOME/.vnc/xstartup.log" 2>&1 + + # Load user profile to get PATH, XDG vars, etc. + if [ -f /etc/profile ]; then + . /etc/profile + fi + + # Source NixOS user environment + if [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then + . "$HOME/.nix-profile/etc/profile.d/nix.sh" + fi + + export XDG_SESSION_TYPE=x11 + export XDG_CURRENT_DESKTOP=XFCE + export XDG_CONFIG_HOME="$HOME/.config" + + ${cfg.sessionCommand} >> "$HOME/.vnc/xstartup.log" 2>&1 & + + wait $! + EOF + + chmod +x ${userHome}/.vnc/xstartup + chown ${cfg.user}:users ${userHome}/.vnc/xstartup + touch ${userHome}/.vnc/xstartup.log + chown ${cfg.user}:users ${userHome}/.vnc/xstartup.log + chmod 600 ${userHome}/.vnc/xstartup.log + ''; + + ExecStart = "${pkgs.tigervnc}/bin/vncserver :${toString cfg.displayNumber}"; + ExecStop = "${pkgs.procps}/bin/pkill -f 'Xvnc :${toString cfg.displayNumber}' || true"; + }; + }; + + environment.etc."X11/Xsession" = { + text = '' + #!/bin/sh + LOGFILE="$HOME/.vnc/xsession.log" + mkdir -p "$HOME/.vnc" + touch "$LOGFILE" + chmod 600 "$LOGFILE" + + echo "=== Xsession started on $(date) ===" >> "$LOGFILE" 2>&1 + + if [ -x "$HOME/.vnc/xstartup" ]; then + echo "Found xstartup, executing..." >> "$LOGFILE" 2>&1 + exec "$HOME/.vnc/xstartup" >> "$LOGFILE" 2>&1 + else + echo "No ~/.vnc/xstartup found, cannot start session." >> "$LOGFILE" 2>&1 + echo "No ~/.vnc/xstartup found, cannot start session." >&2 + exit 1 + fi + ''; + mode = "0755"; + }; + + # Optionally open firewall + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ + (5900 + cfg.displayNumber) + ]; + }; +} + diff --git a/Modules/System/default.nix b/Modules/System/default.nix index 8776d57..ff0cd2a 100644 --- a/Modules/System/default.nix +++ b/Modules/System/default.nix @@ -4,5 +4,6 @@ imports = [ ./Application ./Programming-Tools + ./Service ]; }