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..19a7998 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,11 @@ "/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 + # 65.109.157.161 wstn.de + ''; # Configure network proxy if necessary # networking.proxy.default = "http://user:password@proxy:port/"; @@ -213,6 +219,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 +286,7 @@ mkvtoolnix-cli ydiff wl-clipboard-rs - protonvpn-gui + proton-vpn iperf3 pv libarchive @@ -263,5 +294,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; + #### }; +}