From 843b2fdee270bf89e7c12a7b5b7925e07b1e83da Mon Sep 17 00:00:00 2001 From: cubernetes Date: Mon, 27 Jul 2026 15:12:02 +0200 Subject: [PATCH] Many changes, see full commit Add clamav pkg with new nixos module clamav.nix Add some extraHosts for testing Tweak xserver dpi Set environment vars for scaling, along with dpi Change protonvpn-gui to proton-vpn Add many new pkgs, bc, pstere, tmux, wget ,gnupg, mosh, openvpn, clamav, conntrack-tools, kdotool, trash-cli, bat, psmisc, mupdf, signal-cli, exfat, fprintd Enable power-profiles-daemon environment.localBinInPath = true Add full .bashrc --- flake.lock | 22 +-- flake.nix | 4 +- hosts/fw/configuration.nix | 59 +++++++- modules/home-manager/bash.nix | 248 ++++++++++++++++++++++++++++++++-- modules/nixos/clamav.nix | 168 +++++++++++++++++++++++ 5 files changed, 476 insertions(+), 25 deletions(-) create mode 100644 modules/nixos/clamav.nix diff --git a/flake.lock b/flake.lock index 62d9ebc..566e95b 100644 --- a/flake.lock +++ b/flake.lock @@ -7,16 +7,16 @@ ] }, "locked": { - "lastModified": 1779506708, - "narHash": "sha256-QOD/CNm196nCJRheux/URi4/HE66fthdOMqCJoPP1Y0=", + "lastModified": 1783740085, + "narHash": "sha256-qajyHfZY29G2oEQk+uHxmsJcRoBUBXP9maTpFlwP/dI=", "owner": "nix-community", "repo": "home-manager", - "rev": "3ee51fbdac8c8bdfe1e7e1fcaba6520a563f394f", + "rev": "3cd22efe6471dc7365c822bd9ad73a21e55f38fb", "type": "github" }, "original": { "owner": "nix-community", - "ref": "release-25.11", + "ref": "release-26.05", "repo": "home-manager", "type": "github" } @@ -28,11 +28,11 @@ ] }, "locked": { - "lastModified": 1780816331, - "narHash": "sha256-0BYqs8yKWkOz2Q7+SP18N5E5gmDKSo6LSxIVIa0wWes=", + "lastModified": 1783864904, + "narHash": "sha256-BQxN5UMg9FOevAsgBRwPxfxlh51Puj+dNn/8Dsi3sPM=", "owner": "nix-community", "repo": "nix-index-database", - "rev": "1a2ea89c917781e88508d9fd2b507f2d2a0e173c", + "rev": "1111b9bc836afb7e31a7014e8d1272de9b1c917d", "type": "github" }, "original": { @@ -43,16 +43,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1780952837, - "narHash": "sha256-Fwd1+spDtQ0hDyBwme6ufG3n4mY0UrjjFdYHv+G/Hds=", + "lastModified": 1783856661, + "narHash": "sha256-ZGP04e+Q6WyQJGA9ZvI5CL6+heGQldbAG9U1T9NGvmU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e820eb4a444b46a19b2e03e8dfd2359439ff30fe", + "rev": "569d578509928497eddc3fdbf94a799027050be4", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-25.11", + "ref": "nixos-26.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index ae28f49..8c3ca8f 100644 --- a/flake.nix +++ b/flake.nix @@ -2,10 +2,10 @@ description = "NixOs Config Flake"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-26.05"; home-manager = { - url = "github:nix-community/home-manager/release-25.11"; + url = "github:nix-community/home-manager/release-26.05"; inputs.nixpkgs.follows = "nixpkgs"; }; diff --git a/hosts/fw/configuration.nix b/hosts/fw/configuration.nix index a0b9ee0..23acecb 100644 --- a/hosts/fw/configuration.nix +++ b/hosts/fw/configuration.nix @@ -4,15 +4,19 @@ { config, + lib, pkgs, inputs, ... }: +with lib; + { imports = [ # Include the results of the hardware scan. ./hardware-configuration.nix + ../../modules/nixos/clamav.nix ]; hardware.bluetooth = { @@ -56,9 +60,10 @@ "/dev/disk/by-uuid/1dfcf980-6806-4f69-bd86-ee87c904c04b"; networking.hostName = "fw"; # Define your hostname. # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. - #networking.extraHosts = '' - # 65.109.157.161 wstn.de - #''; + networking.extraHosts = '' + # 204.168.170.249 talk.timo.social + # 204.168.170.249 turn.talk.timo.social + ''; # Configure network proxy if necessary # networking.proxy.default = "http://user:password@proxy:port/"; @@ -213,6 +218,31 @@ }; }; + services.xserver.dpi = 192; + # services.xserver.dpi = 96; + + environment.variables = { + ### ## Used by GTK 3 + ### # `GDK_SCALE` is limited to integer values + ### GDK_SCALE = "2"; + ### # Inverse of GDK_SCALE + ### GDK_DPI_SCALE = "0.5"; + + ### # Used by Qt 5 + ### QT_AUTO_SCREEN_SCALE_FACTOR = "1"; + + _JAVA_OPTIONS = "-Dsun.java2d.uiScale=2"; + + XCURSOR_SIZE = "64"; + }; + + ### # Expose variables to graphical systemd user services + ### services.xserver.displayManager.importedVariables = [ + ### "GDK_SCALE" + ### "GDK_DPI_SCALE" + ### "QT_AUTO_SCREEN_SCALE_FACTOR" + ### ]; + # List packages installed in system profile. To search, run: # $ nix search wget environment.systemPackages = with pkgs; [ @@ -255,7 +285,7 @@ mkvtoolnix-cli ydiff wl-clipboard-rs - protonvpn-gui + proton-vpn iperf3 pv libarchive @@ -263,5 +293,26 @@ traceroute pciutils usbutils + bc + pstree + tmux + wget + gnupg + mosh + openvpn + clamav + conntrack-tools + kdotool + trash-cli + bat + psmisc + mupdf + signal-cli + exfat + fprintd ]; + + services.power-profiles-daemon.enable = true; + + environment.localBinInPath = true; } diff --git a/modules/home-manager/bash.nix b/modules/home-manager/bash.nix index 7b276a8..989237d 100644 --- a/modules/home-manager/bash.nix +++ b/modules/home-manager/bash.nix @@ -5,14 +5,246 @@ enable = true; initExtra = '' xset r rate 200 60 - nxfnd () { [ $# -eq 1 ] || { echo "Expecting exactly one argument"; return 1; }; nix-locate --type x --minimal --whole-name "/$1"; } + + ##################### user specific aliases and functions ###################### + if [ -d ~/.bashrc.d ]; then + for rc in ~/.bashrc.d/*; do + bn=''${rc##*/} + if [ -f "''$rc" ] && [ -r "''$rc" ] && [ ! "''${bn::1}" = '.' ]; then + \. "''$rc" + fi + done + fi + unset rc bn + + ########################### shell settings & options ########################### + set -E + set -o pipefail + + shopt -s nullglob dotglob globstar extglob + shopt -s autocd cdable_vars cdspell + shopt -s checkwinsize + shopt -s lastpipe + shopt -s inherit_errexit + shopt -s no_empty_cmd_completion + shopt -s histreedit + shopt -s lithist + shopt -u histverify + _have bash_abbr && shopt -u expand_aliases + + ############################### shell variables ################################ + FIGNORE='.o' + HISTCONTROL=ignoreboth:erasedups + HISTTIMEFORMAT='%y-%m-%d %H:%M:%S ' + HISTSIZE=-1 + HISTFILESIZE=-1 + HISTFILE=''$HOME/.''${USER:-default}_bash_history + + ############################ environment variables ############################# + export PYTHON_BASIC_REPL=1 # I'm good at readline so I'm gonna use readline whereever possible! + export LESS='-SRFX' + export PAGER='less' GIT_PAGER='less' MANPAGER='less' SYSTEMD_PAGER='less' + export BAT_PAGER=''' + export EDITOR='vim' VISUAL='vim' FCEDIT='vim' SYSTEMD_EDITOR='vim' + export PATH + + ########################## generally useful functions ########################## + take () { + : 'Idempotently create directory recursively and cd into it' + local out + out=''$(set -o pipefail && mkdir -vp "''$@" | tail -1) || return + printf '%s\n' "''$out" + eval "out=''${out#"mkdir: created directory "}" + CDPATH= cd "''${out:-''${@: -1}}" + } + + cd () { + : 'Run cd and if it succeeds, run ls' + builtin cd "''$@" && command ls --color -Ah + } + + drop_caches () { + : 'Drop pagecache, dentries and inodes' + : 'https://unix.stackexchange.com/a/87909' + echo 3 | sudo tee /proc/sys/vm/drop_caches >/dev/null + } + + respawn_shell () { + : 'Intelligently respawn shell' + : 'pure posix sh without forking' + if [ "''${BASH_VERSINFO-}" ] && command -v bash 1>/dev/null 2>&1; then + if _is_login_shell; then + exec bash -l + else + exec bash + fi + : 'TODO: Implement more detection mechanisms for various shells (zsh, etc.)' + elif [ -x /proc/self/exe ]; then + if _is_login_shell; then + exec -l /proc/self/exe + else + exec /proc/self/exe + fi + elif command -v "''${0#-}" 1>/dev/null 2>&1; then + : 'TODO: Make it work with shells that actually start with a dash (unlikely, but possible)' + printf 'Warning: replacing current shell with unreliable ''$0, which is: %s\n' "''$0" + if _is_login_shell; then + exec -l "''${0#-}" + else + exec "''${0#-}" + fi + else + printf 'Error: Don'\'''t know how to reliably replace this shell, not exec'\'''ing and not exiting\n' + printf 'You might want to manually run `exec [-l] "''$SHELL"`, however, `''$SHELL` might not be the shell you are currently running' + return 1 + fi + } + + export_bashrc () { + CDPATH= command cd ~/.bashrc.d/ || return + : 'Not using an associative array because those are not sorted!' + declare -ar plugins=(*) + [[ ''${#plugins[@]} -eq 0 ]] || [[ ''${#plugins} -eq 1 && ''${plugins[0]} = '*' ]] && return + local IFS=' ' + declare -Ai selected + declare plugin + for plugin in "''${plugins[@]}"; do + case "''$plugin" in + # Comment below case match if you don'\'''t want to have a pre-selection of plugins to export + # Requires `shopt -s extglob` + (+([0-9])-@(util|redact|prompt|docker-tools|funcs|init-and-complete|general-aliases|bash-abbr)) selected[''$plugin]=1 ;; + (*) selected[''$plugin]= + esac + done + declare -a plugins_with_selection + while :; do + local PS3='Which plugins do you want to export/not export (enter a single number)? ' + plugins_with_selection=() + for plugin in "''${plugins[@]}"; do + if [[ ''${selected[''$plugin]} -eq 0 ]]; then + plugins_with_selection+=(''$'\033[31m'''$plugin''$'\033[m') + else + plugins_with_selection+=(''$'\033[32m'''$plugin''$'\033[m') + fi + done + select plugin in ''$'\033[31mQUIT\033[m' ''$'\033[34mDONE\033[m' "''${plugins_with_selection[@]}"; do + plugin=''${plugin#''$'\033[3'[0-7]m} + plugin=''${plugin%''$'\033[m'} + [[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break 2 + [[ -z ''$plugin ]] && { echo "Please enter a number (1-''$((''${#plugins[@]}+2)))."; continue; } + selected[''$plugin]=1-''${selected[''$plugin]} + break + done + [[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break + done + [[ -z ''$REPLY || ''$plugin = QUIT ]] && { echo "Aborting"; return; } + declare -a exported_plugins=() + for plugin in "''${!selected[@]}"; do + if [[ ''${selected[''$plugin]} -eq 1 ]]; then + exported_plugins+=(".bashrc.d/''$plugin") + fi + done + command rm -vf "exported_bashrc.tar.gz" + CDPATH= command cd - >/dev/null + CDPATH= command cd ~ + as_base64=''$(command tar -cvhzf - ".bashrc" "''${exported_plugins[@]}" | base64 -w0) + CDPATH= command cd - >/dev/null + as_base64_wrapped=''$(printf %s "''$as_base64" | sed 's#.\{,70\}#'\'''&'\'''\\\n#g' | sed ''\'''$s#\\$##') + command install -m775 /dev/stdin ~/bashrc_installer.sh <<-EOF + #! /usr/bin/env bash + + set -e + + [ "\''$BASH_VERSINFO" ] && command -v declare >/dev/null 2>&1 && command declare -p BASH_VERSINFO | { IFS=' ' read -r _ flags _; case "\''$flags" in (*r*) ;; (*) false; esac; } || { printf 'This installer must be run with bash!\\n'; exit 1; } + command -v bash >/dev/null 2>&1 || { printf 'bash must be installed!\\n'; exit 2; } + + as_base64=''$as_base64_wrapped + tempdir=\''$(mktemp -d) + trap 'rm -rf -- "\''$tempdir"' EXIT + cd -- "\''$tempdir" + base64 -d <<< "\''$as_base64" | tar xzf - + create_dir_and_move () { + src=\''$1 + dst=\''$2 + [ ! -f "\''$src" ] && { printf '%s\\n' "Source argument '\''$src' is not a regular file"; return 1; } + dst_dir=\''$(dirname -- "\''${dst//[^!-~]/-}") + [ ! -d "\''$dst_dir" ] && { printf '%s\\n' "Trying to create destination directory '\''$dst_dir'"; mkdir -p -- "\''$dst_dir"; } || : + [ -d "\''$dst_dir" ] && mv -v -t "\''$dst_dir" -- "\''$src" || : + } + ask_overwrite () { + file1=\''$1 + file2=\''$2 + if [[ ! -e \''$file2 ]]; then + create_dir_and_move "\''$file1" "\''$file2" + elif [[ -d \''$file2 ]]; then + printf 'WARNING: Destination file %s is a directory! Skipping this file\\n' "\''${file2@Q}" + return + elif [[ ! -f \''$file2 ]]; then + printf 'WARNING: Destination file %s is not a regular file! Skipping this file\\n' "\''${file2@Q}" + return + elif ! diff -q "\''$file1" "\''$file2"; then + printf 'File to be installed (%s) differs from already installed file (%s)\\n' "\''${file1@Q}" "\''${file2@Q}" + read -p 'Press enter to show diff. ' || : + diff --color -u "\''$file1" "\''$file2" || : + printf 'Giving you a plain bash shell to resolve the situation. Once done, just exit.\\n' + printf 'After you exit, the destination file WILL BE FORCEFULLY OVERWRITTEN!!!\n' + read -p 'Press enter to continue in the shell. ' || : + bash --rcfile <(printf 'cd; printf "You are running bash version \''$BASH_VERSION. Your CWD is \''$PWD. Your HOME is \''$HOME. You are \''$USER and HOSTNAME is \''$HOSTNAME.\\nUse this bash shell to make changes, backups, etc.\\nThen just exit.\\n"') --noprofile || : + printf 'Destination file %s will be overwritten. Continue? ' "\''${file2@Q}" + read || : + create_dir_and_move "\''$file1" "\''$file2" + else + printf 'File %s was not changed\\n' "\''${file2@Q}" + fi + } + ask_overwrite .bashrc ~/.bashrc + set +f + shopt -s nullglob dotglob + for file in .bashrc.d/*; do + ask_overwrite "\''$file" ~/"\''$file" + done + cd - >/dev/null + read -p 'Self destruct (y/N)? ' choice || : + [[ \''$choice = y ]] && { printf 'Trying to rm myself (%s)\\n' "\''${0@Q}"; rm -- "\''$0" && printf 'Success\\n' || printf 'Failed. Not trying further\\n'; } + EOF + } + + #################################### binds ##################################### + bind '"\e\C-r": kill-region' \ + '"\C-x\C-k": kill-whole-line' \ + '"\e\C-p": history-search-backward' \ + '"\e\C-n": history-search-forward' \ + '"\eP": history-substring-search-backward' \ + '"\eN": history-substring-search-forward' \ + '"\C-x\C-d": "\e#\C-m exit\C-m"' \ + '"\ej": "\C-e |& tac | \\cat -n | tac | \\cat -n | less -SFXR\C-m"' \ + '"\eX": ">/dev/null "' \ + '"\t": menu-complete' \ + '"\e[Z": menu-complete-backward' \ + '"\C-x\C-r": forward-search-history' \ + 'set show-all-if-ambiguous on' \ + 'set completion-query-items 0' \ + 'set menu-complete-display-prefix on' \ + 'set colored-completion-prefix on' \ + 'set colored-stats on' + + ################################## autoclear ################################### + # TODO + + # prank () { + # line_up_until_cursor=''${READLINE_LINE::READLINE_POINT} + # line_after_cursor=''${READLINE_LINE:READLINE_POINT} + # if [[ ''$((RANDOM % 5)) -eq 0 ]]; then + # READLINE_LINE="''$line_up_until_cursor''$line_after_cursor" + # #READLINE_LINE="''$line_up_until_cursor''${1^}''$line_after_cursor" + # READLINE_POINT=''$((READLINE_POINT+1)) + # else + # READLINE_LINE="''$line_up_until_cursor''$1''$line_after_cursor" + # READLINE_POINT=''$((READLINE_POINT+1)) + # fi + # } + # bind -x '" ": prank " "' + # bind -x '"a": prank "a"' ''; - shellAliases = { - rb = "sudo nixos-rebuild switch --flake /etc/nixos#fw"; - nx = "vim ~/nixos-config/hosts/fw/configuration.nix"; - hm = "vim ~/nixos-config/hosts/fw/home.nix"; - bsh = "vim ~/nixos-config/modules/home-manager/bash.nix"; - nxfmt = "find . -name '*.nix' -print0 | xargs -r0 nix run nixpkgs#nixfmt --"; - }; }; } diff --git a/modules/nixos/clamav.nix b/modules/nixos/clamav.nix new file mode 100644 index 0000000..3dd696f --- /dev/null +++ b/modules/nixos/clamav.nix @@ -0,0 +1,168 @@ +{ + lib, + pkgs, + ... +}: + +{ + services.clamav = { + daemon = { + enable = true; + + settings = { + MaxThreads = 24; + MaxQueue = 48; + MaxFileSize = "100M"; + MaxScanSize = "300M"; + MaxDirectoryRecursion = 60; + + ExcludePath = [ + "^/sys(/|$)" + "^/proc(/|$)" + "^/dev(/|$)" + "^/run(/|$)" + "^/nix/store(/|$)" + ]; + + #CommandReadTimeout = 0; + }; + }; + + updater = { + enable = true; + interval = "hourly"; + + # Number of database checks per day used by freshclam. + # Redundant if the updater is a one-shot service, though. + # frequency = 24; + }; + + scanner = { + enable = true; + interval = "17:15:00"; + }; + }; + + systemd.services.clamdscan.serviceConfig.ExecStart = lib.mkForce '' + ${pkgs.bash}/bin/bash -c ' \ + echo "Scanning directory /"; \ + exec "${pkgs.clamav}/bin/clamdscan" \ + --log=/var/log/clamav/scan.log \ + --multiscan \ + --fdpass \ + --infected \ + --verbose \ + --file-list=<(find / \\( -path /proc -o -path /sys -o -path /dev -o -path /run -o -path /nix/store \\) -prune -o -type f -print) \ + ' + ''; + + systemd.services.clamav-daemon.serviceConfig = { + LimitNOFILE = 8192; + }; + + systemd.timers.clamdscan.timerConfig = { + Persistent = true; + #RandomizedDelaySec = "2h"; + }; + + #### # ClamAV scan service + #### systemd.services.clamav-scan = { + #### description = "ClamAV system scan"; + #### documentation = [ "man:clamscan(1)" ]; + + #### after = [ + #### "clamav-freshclam.service" + #### "local-fs.target" + #### ]; + + #### wants = [ "clamav-freshclam.service" ]; + + #### script = '' + #### exec ${pkgs.clamav}/bin/clamscan \ + #### --infected \ + #### --recursive \ + #### --log=/var/log/clamav/scan.log \ + #### --exclude-dir="^/sys(/|$)" \ + #### --exclude-dir="^/proc(/|$)" \ + #### --exclude-dir="^/dev(/|$)" \ + #### --exclude-dir="^/run(/|$)" \ + #### --exclude-dir="^/nix/store(/|$)" \ + #### --max-filesize=100M \ + #### --max-scansize=300M \ + #### / + #### ''; + + #### serviceConfig = { + #### Type = "oneshot"; + #### Nice = 19; + #### IOSchedulingClass = "idle"; + + #### # clamscan returns: + #### # 0 = no infection + #### # 1 = infection found + #### # 2 = error + #### # + #### # Finding malware should be recorded, but should not make systemd + #### # describe the scanner itself as broken. + #### SuccessExitStatus = [ 0 1 ]; + + #### # Creates /var/log/clamav automatically. + #### LogsDirectory = "clamav"; + #### LogsDirectoryMode = "0750"; + + #### PrivateTmp = true; + #### PrivateDevices = true; + #### NoNewPrivileges = true; + + #### ProtectSystem = "strict"; + #### ProtectHome = "read-only"; + + #### ReadWritePaths = [ "/var/log/clamav" ]; + + #### ProtectKernelTunables = true; + #### ProtectKernelModules = true; + #### ProtectKernelLogs = true; + #### ProtectControlGroups = true; + + #### RestrictRealtime = true; + #### RestrictSUIDSGID = true; + #### LockPersonality = true; + #### MemoryDenyWriteExecute = true; + + #### SystemCallArchitectures = "native"; + #### }; + #### }; + + #### # Daily ClamAV scan timer + #### systemd.timers.clamav-scan = { + #### description = "Daily ClamAV scan at 10:00:00"; + #### wantedBy = [ "timers.target" ]; + #### timerConfig = { + #### OnCalendar = "*-*-* 09:05:00"; + #### Persistent = true; + #### Unit = "clamav-scan.service"; + #### }; + #### }; + + #### # Systemd hardening for ClamAV daemon + #### systemd.services.clamav-daemon.serviceConfig = { + #### PrivateTmp = lib.mkForce true; + + #### ProtectSystem = "strict"; + #### ProtectHome = "read-only"; + + #### ReadWritePaths = [ + #### "/var/lib/clamav" + #### "/run/clamav" + #### ]; + + #### NoNewPrivileges = true; + #### ProtectKernelTunables = true; + #### ProtectKernelModules = true; + #### ProtectKernelLogs = true; + #### ProtectControlGroups = true; + #### RestrictRealtime = true; + #### RestrictSUIDSGID = true; + #### LockPersonality = true; + #### }; +}