fix(searxng): resolve 502 error and apply Catppuccin theme
- Fix Redis connection by using container-to-container networking. - Apply Catppuccin (Mocha/Latte) theme via custom CSS. - Enable SearXNG module in host configuration. - Configure Caddy reverse proxy and DDclient for search.ashisgreat.xyz.
This commit is contained in:
parent
2be8de47fa
commit
6ada19e490
55 changed files with 2502 additions and 269 deletions
|
|
@ -41,7 +41,9 @@ in
|
|||
|
||||
Service = {
|
||||
ExecStartPre = pkgs.writeShellScript "antigravity2api-init" ''
|
||||
cat > ${workDir}/.env <<EOF
|
||||
export PATH="${pkgs.coreutils}/bin:$PATH"
|
||||
mkdir -p "${workDir}"
|
||||
cat > "${workDir}/.env" <<EOF
|
||||
API_KEY=${cfg.credentials.apiKey}
|
||||
ADMIN_USERNAME=${cfg.credentials.username}
|
||||
ADMIN_PASSWORD=${cfg.credentials.password}
|
||||
|
|
|
|||
12
modules/home/cosmic.nix
Normal file
12
modules/home/cosmic.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
# programs.cosmic-manager.enable = true; # Deprecated
|
||||
# Add cosmic-manager configurations here
|
||||
# Example:
|
||||
# programs.cosmic-edit.enable = true;
|
||||
|
||||
xdg.configFile."cosmic/com.system76.CosmicComp/v1/input_default".text = ''
|
||||
(
|
||||
acceleration_profile: Flat,
|
||||
)
|
||||
'';
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
./browser-container-update.nix
|
||||
./proton-cachyos-updater.nix
|
||||
./cli-tools.nix
|
||||
./nixvim.nix
|
||||
|
||||
# ./unified-router.nix
|
||||
./sillytavern.nix
|
||||
|
|
@ -24,5 +25,6 @@
|
|||
./polling-rate.nix
|
||||
./antigravity2api.nix
|
||||
./theme.nix
|
||||
./prismlauncher.nix
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,7 +232,8 @@ in
|
|||
Mod+Minus { set-column-width "-10%"; }
|
||||
Mod+Equal { set-column-width "+10%"; }
|
||||
|
||||
Mod+Shift+E { quit; }
|
||||
Mod+Shift+E { spawn "bemoji" "-t"; }
|
||||
|
||||
Print { spawn "sh" "-c" "grim -g \"$(slurp)\" - | wl-copy"; }
|
||||
|
||||
// Browsers
|
||||
|
|
|
|||
149
modules/home/nixvim.nix
Normal file
149
modules/home/nixvim.nix
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
{ pkgs, ... }:
|
||||
{
|
||||
programs.nixvim = {
|
||||
enable = true;
|
||||
defaultEditor = true;
|
||||
|
||||
colorschemes.catppuccin = {
|
||||
enable = true;
|
||||
settings = {
|
||||
flavour = "mocha";
|
||||
term_colors = true;
|
||||
};
|
||||
};
|
||||
|
||||
globals = {
|
||||
mapleader = " ";
|
||||
maplocalleader = "\\";
|
||||
};
|
||||
|
||||
opts = {
|
||||
number = true;
|
||||
relativenumber = true;
|
||||
|
||||
# Tabs
|
||||
tabstop = 2;
|
||||
softtabstop = 2;
|
||||
shiftwidth = 2;
|
||||
expandtab = true;
|
||||
smartindent = true;
|
||||
|
||||
# Search
|
||||
ignorecase = true;
|
||||
smartcase = true;
|
||||
|
||||
# UI
|
||||
cursorline = true;
|
||||
termguicolors = true;
|
||||
scrolloff = 8;
|
||||
|
||||
# System
|
||||
mouse = "a";
|
||||
clipboard = "unnamedplus";
|
||||
undofile = true;
|
||||
};
|
||||
|
||||
plugins = {
|
||||
lualine.enable = true;
|
||||
bufferline.enable = true;
|
||||
web-devicons.enable = true;
|
||||
which-key.enable = true;
|
||||
|
||||
# Treesitter
|
||||
treesitter = {
|
||||
enable = true;
|
||||
settings = {
|
||||
highlight.enable = true;
|
||||
indent.enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
# File Explorer
|
||||
neo-tree = {
|
||||
enable = true;
|
||||
closeIfLastWindow = true;
|
||||
};
|
||||
|
||||
# Fuzzy Finder
|
||||
telescope = {
|
||||
enable = true;
|
||||
keymaps = {
|
||||
"<leader>ff" = "find_files";
|
||||
"<leader>fg" = "live_grep";
|
||||
"<leader>fb" = "buffers";
|
||||
"<leader>fh" = "help_tags";
|
||||
};
|
||||
};
|
||||
|
||||
# LSP & Completion
|
||||
lsp = {
|
||||
enable = true;
|
||||
servers = {
|
||||
nixd.enable = true; # Nix
|
||||
lua_ls.enable = true; # Lua
|
||||
pyright.enable = true; # Python
|
||||
bashls.enable = true; # Bash
|
||||
html.enable = true; # HTML
|
||||
cssls.enable = true; # CSS
|
||||
ts_ls.enable = true; # TS/JS
|
||||
};
|
||||
};
|
||||
|
||||
cmp = {
|
||||
enable = true;
|
||||
autoEnableSources = true;
|
||||
settings = {
|
||||
sources = [
|
||||
{ name = "nvim_lsp"; }
|
||||
{ name = "path"; }
|
||||
{ name = "buffer"; }
|
||||
];
|
||||
mapping = {
|
||||
"<C-Space>" = "cmp.mapping.complete()";
|
||||
"<CR>" = "cmp.mapping.confirm({ select = true })";
|
||||
"<Tab>" = "cmp.mapping.select_next_item()";
|
||||
"<S-Tab>" = "cmp.mapping.select_prev_item()";
|
||||
};
|
||||
};
|
||||
};
|
||||
cmp-nvim-lsp.enable = true;
|
||||
cmp-path.enable = true;
|
||||
cmp-buffer.enable = true;
|
||||
|
||||
# UI Improvements
|
||||
notify = {
|
||||
enable = true;
|
||||
settings.background_colour = "#000000";
|
||||
};
|
||||
|
||||
noice = {
|
||||
enable = true;
|
||||
settings = {
|
||||
notify.enabled = true;
|
||||
presets = {
|
||||
bottom_search = true;
|
||||
command_palette = true;
|
||||
long_message_to_split = true;
|
||||
inc_rename = false;
|
||||
lsp_doc_border = false;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
keymaps = [
|
||||
{
|
||||
mode = "n";
|
||||
key = "<leader>e";
|
||||
action = "<cmd>Neotree toggle<cr>";
|
||||
options.desc = "Toggle Explorer";
|
||||
}
|
||||
{
|
||||
mode = "n";
|
||||
key = "<C-s>";
|
||||
action = "<cmd>w<cr>";
|
||||
options.desc = "Save File";
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
@ -9,66 +9,83 @@ let
|
|||
cfg = config.services.polling-rate-switcher;
|
||||
|
||||
switcherScript = pkgs.writeShellScriptBin "polling-rate-switcher" ''
|
||||
# Find Razer Viper V3 Pro sysfs path
|
||||
# Look for a device that supports poll_rate
|
||||
MOUSE_PATH=""
|
||||
export PATH="${
|
||||
lib.makeBinPath [
|
||||
pkgs.coreutils
|
||||
pkgs.gnugrep
|
||||
pkgs.jq
|
||||
pkgs.systemd
|
||||
pkgs.niri
|
||||
pkgs.gawk
|
||||
]
|
||||
}:$PATH"
|
||||
|
||||
find_mouse() {
|
||||
for dev in /sys/bus/hid/drivers/razermouse/*; do
|
||||
if [ -f "$dev/device_type" ] && [ -f "$dev/poll_rate" ]; then
|
||||
TYPE=$(cat "$dev/device_type")
|
||||
# Check if it looks like a mouse "Razer Viper V3 Pro" usually has unique ID/Type
|
||||
# For now, we take the first Razer device that supports polling rate
|
||||
# Or specifically filter if we knew exact string.
|
||||
# The user said "Active window... polling rate of my razer viper v3 pro"
|
||||
# We'll assume it's the main device found.
|
||||
MOUSE_PATH="$dev"
|
||||
echo "Found Razer device at $MOUSE_PATH ($TYPE)"
|
||||
break
|
||||
# Find the Razer device object path via D-Bus
|
||||
# We look for an object path that looks like /org/razer/device/...
|
||||
# AND supports setPollRate
|
||||
echo "Searching for OpenRazer device on D-Bus..."
|
||||
|
||||
RAZER_OBJ=""
|
||||
|
||||
# Simple retry loop in case the service starts before the device is ready
|
||||
for i in {1..10}; do
|
||||
# Find matches, then check introspection for setPollRate method
|
||||
CANDIDATES=$(busctl --user tree --list org.razer | grep '^/org/razer/device/') || true
|
||||
|
||||
for obj in $CANDIDATES; do
|
||||
if busctl --user introspect org.razer "$obj" 2>/dev/null | grep -q "setPollRate"; then
|
||||
RAZER_OBJ="$obj"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$RAZER_OBJ" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
sleep 2
|
||||
done
|
||||
|
||||
find_mouse
|
||||
|
||||
if [ -z "$MOUSE_PATH" ]; then
|
||||
echo "No Razer mouse found with poll_rate capability."
|
||||
if [ -z "$RAZER_OBJ" ]; then
|
||||
echo "No Razer device found on D-Bus org.razer that supports setPollRate after retries."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Starting Polling Rate Switcher for $MOUSE_PATH"
|
||||
echo "Found Razer device at $RAZER_OBJ"
|
||||
|
||||
CURRENT_MODE="unknown"
|
||||
|
||||
update_rate() {
|
||||
TARGET=$1
|
||||
# Read current to avoid redundant writes
|
||||
CURRENT=$(cat "$MOUSE_PATH/poll_rate")
|
||||
|
||||
if [ "$CURRENT" != "$TARGET" ]; then
|
||||
echo "Switching polling rate to $TARGET Hz"
|
||||
echo "$TARGET" > "$MOUSE_PATH/poll_rate"
|
||||
fi
|
||||
# Function to set polling rate via D-Bus
|
||||
set_rate() {
|
||||
local rate="$1"
|
||||
# Call OpenRazer method: setPollRate(int)
|
||||
busctl --user call org.razer "$RAZER_OBJ" razer.device.misc setPollRate i "$rate"
|
||||
}
|
||||
|
||||
while true; do
|
||||
# Get active window info from Niri
|
||||
# Niri msg active-window returns JSON
|
||||
WINDOW_INFO=$(${pkgs.niri}/bin/niri msg -j focused-window 2>/dev/null)
|
||||
current_rate=""
|
||||
|
||||
# Listen to Niri event stream
|
||||
# We filter for WindowFocusChanged events, then query the state.
|
||||
# This avoids parsing the complex event stream directly for state.
|
||||
niri msg --json event-stream | jq --unbuffered -c 'select(.WindowFocusChanged) | .WindowFocusChanged.id' | while read -r _; do
|
||||
# Query current focused window
|
||||
# The focused-window output is wrapped in Ok.FocusedWindow
|
||||
fw=$(niri msg --json focused-window 2>/dev/null || true)
|
||||
|
||||
if [ -n "$WINDOW_INFO" ]; then
|
||||
APP_ID=$(echo "$WINDOW_INFO" | ${pkgs.jq}/bin/jq -r '.app_id // ""' | tr '[:upper:]' '[:lower:]')
|
||||
TITLE=$(echo "$WINDOW_INFO" | ${pkgs.jq}/bin/jq -r '.title // ""' | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Check for Overwatch
|
||||
if [[ "$APP_ID" == *"overwatch"* ]] || [[ "$TITLE" == *"overwatch"* ]]; then
|
||||
update_rate 8000
|
||||
else
|
||||
update_rate 1000
|
||||
fi
|
||||
# If no window is focused or command fails, these will be empty
|
||||
app_id=$(echo "$fw" | jq -r '.Ok.FocusedWindow.app_id // ""' | tr '[:upper:]' '[:lower:]')
|
||||
title=$(echo "$fw" | jq -r '.Ok.FocusedWindow.title // ""' | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Default rate
|
||||
target_rate=1000
|
||||
|
||||
# High polling rate for specific games
|
||||
if [[ "$app_id" == *"overwatch"* || "$title" == *"overwatch"* ]]; then
|
||||
target_rate=8000
|
||||
fi
|
||||
|
||||
if [ "$target_rate" != "$current_rate" ]; then
|
||||
echo "Switching rate to $target_rate Hz (App: $app_id, Title: $title)"
|
||||
set_rate "$target_rate"
|
||||
current_rate="$target_rate"
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
done
|
||||
'';
|
||||
in
|
||||
|
|
|
|||
287
modules/home/prismlauncher.nix
Normal file
287
modules/home/prismlauncher.nix
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.prismlauncher;
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
|
||||
# Libraries required by Minecraft natives (LWJGL), various mods,
|
||||
# and the Microsoft authentication flow (NSS/NSPR).
|
||||
runtimeLibs = with pkgs; [
|
||||
glib
|
||||
libgbm
|
||||
libglvnd
|
||||
nspr
|
||||
nss
|
||||
alsa-lib
|
||||
libpulseaudio
|
||||
udev
|
||||
cups
|
||||
mesa
|
||||
expat
|
||||
libdrm
|
||||
libxkbcommon
|
||||
dbus
|
||||
xorg.libXcomposite
|
||||
xorg.libXdamage
|
||||
xorg.libXext
|
||||
xorg.libXfixes
|
||||
xorg.libXrandr
|
||||
xorg.libxcb
|
||||
libxml2
|
||||
xorg.libXScrnSaver
|
||||
glfw
|
||||
];
|
||||
|
||||
defaultJvmArgs = "-XX:+UnlockExperimentalVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+AlwaysActAsServerClassMachine -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+UseNUMA -XX:NmethodSweepActivity=1 -XX:ReservedCodeCacheSize=400M -XX:NonNMethodCodeHeapSize=12M -XX:ProfiledCodeHeapSize=194M -XX:NonProfiledCodeHeapSize=194M -XX:-DontCompileHugeMethods -XX:MaxNodeLimit=240000 -XX:NodeLimitFudgeFactor=8000 -XX:+UseVectorCmov -XX:+PerfDisableSharedMem -XX:+UseFastUnorderedTimeStamps -XX:+UseCriticalJavaThreadPriority -XX:ThreadPriorityPolicy=1 -XX:AllocatePrefetchStyle=3 -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGuaranteedGCInterval=1000000 -XX:AllocatePrefetchStyle=1 -XX:ConcGCThreads=4";
|
||||
|
||||
in
|
||||
{
|
||||
options.myModules.prismlauncher = {
|
||||
enable = lib.mkEnableOption "PrismLauncher Sandboxed";
|
||||
|
||||
jvmArgs = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = defaultJvmArgs;
|
||||
description = "JVM arguments to enforce in prismlauncher.cfg";
|
||||
};
|
||||
|
||||
uid = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 1000;
|
||||
description = "User ID for /run/user bind mount";
|
||||
};
|
||||
|
||||
glfwPackage = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkgs.glfw;
|
||||
description = "The GLFW package to use for the custom GLFW path.";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
home.packages = [
|
||||
(bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
id = "org.prismlauncher.PrismLauncher";
|
||||
package = pkgs.prismlauncher.overrideAttrs (old: {
|
||||
pname = "prismlauncher";
|
||||
version = old.version or "9.1"; # Fallback or keep current if valid
|
||||
buildInputs = (old.buildInputs or [ ]);
|
||||
|
||||
# Keep runtimeLibs in closure without injecting them into environment
|
||||
postInstall = (old.postInstall or "") + ''
|
||||
mkdir -p $out/share/prismlauncher-sandboxed
|
||||
echo "${lib.makeLibraryPath runtimeLibs}" > $out/share/prismlauncher-sandboxed/libs
|
||||
'';
|
||||
|
||||
qtWrapperArgs = (old.qtWrapperArgs or [ ]);
|
||||
});
|
||||
|
||||
env = {
|
||||
# Propagate XDG_DATA_DIRS so themes/icons can be found
|
||||
BROWSER = "firefox";
|
||||
QT_QPA_PLATFORM = "xcb";
|
||||
GDK_BACKEND = "x11";
|
||||
NO_AT_BRIDGE = "1";
|
||||
QT_QPA_PLATFORMTHEME = "";
|
||||
QT_STYLE_OVERRIDE = "fusion";
|
||||
|
||||
# Sanitize Desktop Environment to prevent loading conflicting platform themes
|
||||
XDG_CURRENT_DESKTOP = "X-Generic";
|
||||
XDG_SESSION_TYPE = "x11";
|
||||
GTK_USE_PORTAL = "0";
|
||||
GTK_THEME = "Adwaita"; # Force a safe theme or empty?
|
||||
|
||||
# Unset potential conflict variables
|
||||
GTK_MODULES = "";
|
||||
GTK3_MODULES = "";
|
||||
};
|
||||
};
|
||||
|
||||
sockets.x11 = true;
|
||||
sockets.wayland = true;
|
||||
flatpak.enable = false;
|
||||
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = false;
|
||||
unshareCgroup = false;
|
||||
unsharePid = false;
|
||||
unshareNet = false;
|
||||
unshareIpc = false;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri"
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /tmp"
|
||||
"--tmpfs /run"
|
||||
"--dir /run/user"
|
||||
"--dir /run/user/${toString cfg.uid}"
|
||||
# Bind ro system paths commonly needed
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
"--ro-bind /run/dbus /run/dbus"
|
||||
];
|
||||
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.local/share/fonts"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.themes"
|
||||
"$HOME/.local/share/themes"
|
||||
"$HOME/.config/qt6ct"
|
||||
"$HOME/.config/Kvantum"
|
||||
"$HOME/.config/MangoHud"
|
||||
"$HOME/Downloads"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/.local/share/PrismLauncher"
|
||||
"$HOME/.cache/PrismLauncher"
|
||||
];
|
||||
};
|
||||
|
||||
dbus.enable = false;
|
||||
|
||||
script.preCmds.stage2 =
|
||||
let
|
||||
glfwPath = "${cfg.glfwPackage}/lib/libglfw.so.3";
|
||||
|
||||
# We need to access the sandbox-utils.nix. Since it's in system modules,
|
||||
# we can't easily import it relative to here if it's not exported.
|
||||
# But the content was small, let's inline what we need or check if we can source it.
|
||||
# For now, I'll assume the dbus-proxy logic is needed.
|
||||
|
||||
# Reimplementing mkDbusProxyScript from sandbox-utils.nix inline to avoid path dependency
|
||||
mkDbusProxyScript =
|
||||
{ appId, proxyArgs }:
|
||||
let
|
||||
proxyArgsStr = lib.escapeShellArgs proxyArgs;
|
||||
appDir = "$XDG_RUNTIME_DIR/app/${appId}";
|
||||
proxySocket = "${appDir}/bus";
|
||||
in
|
||||
''
|
||||
mkdir -p "${appDir}"
|
||||
# Start xdg-dbus-proxy
|
||||
${pkgs.xdg-dbus-proxy}/bin/xdg-dbus-proxy \
|
||||
"$DBUS_SESSION_BUS_ADDRESS" "${proxySocket}" \
|
||||
${proxyArgsStr} &
|
||||
DBUS_PROXY_PID=$!
|
||||
|
||||
# Kill proxy on exit
|
||||
trap "kill $DBUS_PROXY_PID" EXIT
|
||||
|
||||
# Wait for socket to be created
|
||||
for i in {1..50}; do
|
||||
if [ -S "${proxySocket}" ]; then
|
||||
break
|
||||
fi
|
||||
if ! kill -0 $DBUS_PROXY_PID 2>/dev/null; then
|
||||
echo "xdg-dbus-proxy died unexpectedly"
|
||||
exit 1
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
'';
|
||||
|
||||
dbusScript = mkDbusProxyScript {
|
||||
appId = "org.prismlauncher.PrismLauncher";
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
"--talk=org.freedesktop.portal.*"
|
||||
"--call=org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"
|
||||
"--talk=org.freedesktop.Notifications"
|
||||
"--own=org.prismlauncher.PrismLauncher"
|
||||
"--own=org.prismlauncher.PrismLauncher.*"
|
||||
];
|
||||
};
|
||||
in
|
||||
''
|
||||
${dbusScript}
|
||||
|
||||
# Sanitize Environment
|
||||
unset QT_QPA_PLATFORMTHEME
|
||||
unset GTK_THEME
|
||||
unset XDG_CURRENT_DESKTOP
|
||||
export QT_QPA_PLATFORM=xcb
|
||||
export GDK_BACKEND=x11
|
||||
export NO_AT_BRIDGE=1
|
||||
|
||||
# Force Configs (JVM Args + GLFW)
|
||||
cfg="$HOME/.local/share/PrismLauncher/prismlauncher.cfg"
|
||||
if [ -f "$cfg" ]; then
|
||||
# JVM Args
|
||||
if ${pkgs.gnugrep}/bin/grep -q "^JvmArgs=" "$cfg"; then
|
||||
${pkgs.gnused}/bin/sed -i "s|^JvmArgs=.*|JvmArgs=${cfg.jvmArgs}|" "$cfg"
|
||||
else
|
||||
if ${pkgs.gnugrep}/bin/grep -q "^\\[General\\]" "$cfg"; then
|
||||
${pkgs.gnused}/bin/sed -i "/^\\[General\\]/a JvmArgs=${cfg.jvmArgs}" "$cfg"
|
||||
else
|
||||
echo "JvmArgs=${cfg.jvmArgs}" >> "$cfg"
|
||||
fi
|
||||
fi
|
||||
|
||||
# GLFW Settings
|
||||
# 1. CustomGLFWPath
|
||||
if ${pkgs.gnugrep}/bin/grep -q "^CustomGLFWPath=" "$cfg"; then
|
||||
${pkgs.gnused}/bin/sed -i "s|^CustomGLFWPath=.*|CustomGLFWPath=${glfwPath}|" "$cfg"
|
||||
else
|
||||
echo "CustomGLFWPath=${glfwPath}" >> "$cfg"
|
||||
fi
|
||||
|
||||
# 2. UseNativeGLFW
|
||||
if ${pkgs.gnugrep}/bin/grep -q "^UseNativeGLFW=" "$cfg"; then
|
||||
${pkgs.gnused}/bin/sed -i "s|^UseNativeGLFW=.*|UseNativeGLFW=true|" "$cfg"
|
||||
else
|
||||
echo "UseNativeGLFW=true" >> "$cfg"
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
# D-Bus proxy
|
||||
''--bind "$XDG_RUNTIME_DIR/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
# Note: The original code bound a specific path TO ./bus.
|
||||
# "''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''"
|
||||
# But mkDbusProxyScript (if standard) creates a socket.
|
||||
# The logic in prismlauncher-sandboxed.nix imported sandbox-utils.nix.
|
||||
# I'll try to match the original bind logic if possible.
|
||||
|
||||
# The original code had:
|
||||
# ''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
# But my inline mkDbusProxyScript sets up "$XDG_RUNTIME_DIR/bus" as the listen socket *inside* the script execution?
|
||||
# Wait, xdg-dbus-proxy runs inside the outer unshared namespace or outside?
|
||||
# In mkBwrapper, preCmds run *inside* the bwrap?
|
||||
# No, typically preCmds run before the final exec?
|
||||
# Actually, looking at nix-bwrapper, `preCmds.stage2` runs *inside* the sandbox?
|
||||
|
||||
# Let's start with the binds exactly as they were, assuming `sandbox-utils` logic.
|
||||
# If I can't import sandbox-utils, I have to rely on what I can see.
|
||||
# The original `sandbox-utils.nix` likely set up the proxy.
|
||||
# I will copy the binds from the original file.
|
||||
|
||||
''--bind "$XDG_RUNTIME_DIR/app/org.prismlauncher.PrismLauncher/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
];
|
||||
})
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ in
|
|||
azahar-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = azahar;
|
||||
id = "org.azahar_emu.azahar";
|
||||
id = "org.azahar_emu.Azahar";
|
||||
env = {
|
||||
QT_QPA_PLATFORM = "wayland;xcb";
|
||||
XDG_CURRENT_DESKTOP = "KDE";
|
||||
|
|
@ -51,10 +51,7 @@ in
|
|||
fhsenv.bwrap.additionalArgs = [
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
''--bind "$XDG_RUNTIME_DIR/app/org.azahar_emu.azahar/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
''--bind "$XDG_RUNTIME_DIR/app/org.azahar_emu.Azahar/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
];
|
||||
|
||||
mounts = {
|
||||
|
|
@ -74,7 +71,7 @@ in
|
|||
|
||||
dbus.enable = false;
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "org.azahar_emu.azahar";
|
||||
appId = "org.azahar_emu.Azahar";
|
||||
enableSystemBus = false;
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
# Brave Sandboxed with nix-bwrapper
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
|
|
@ -150,4 +149,26 @@ in
|
|||
};
|
||||
})
|
||||
];
|
||||
|
||||
environment.systemPackages =
|
||||
let
|
||||
vpnLauncher = pkgs.writeShellScriptBin "brave-vpn" ''
|
||||
exec /home/ashie/nixos/scripts/launch-vpn-app.sh ${pkgs.brave-sandboxed}/bin/brave "$@"
|
||||
'';
|
||||
|
||||
desktopItem = pkgs.makeDesktopItem {
|
||||
name = "brave-vpn";
|
||||
desktopName = "Brave (VPN)";
|
||||
exec = "${vpnLauncher}/bin/brave-vpn";
|
||||
icon = "brave-browser";
|
||||
categories = [
|
||||
"Network"
|
||||
"WebBrowser"
|
||||
];
|
||||
};
|
||||
in
|
||||
[
|
||||
vpnLauncher
|
||||
desktopItem
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,3 @@
|
|||
# Caddy with Cloudflare DNS-01 ACME Module
|
||||
# Provides: Caddy reverse proxy with automatic SSL via Cloudflare DNS
|
||||
#
|
||||
# Usage:
|
||||
# myModules.caddyCloudflare = {
|
||||
# enable = true;
|
||||
# email = "you@example.com";
|
||||
# cloudflareApiTokenFile = config.sops.secrets.cloudflare_api_key.path;
|
||||
# virtualHosts = {
|
||||
# "api.example.com" = { reverseProxy = "127.0.0.1:8080"; };
|
||||
# };
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
|
|
|
|||
|
|
@ -143,7 +143,9 @@ in
|
|||
# Create directories that bwrap will bind
|
||||
mkdir -p "$HOME/.cache/citron-tmp"
|
||||
mkdir -p "$HOME/.config/citron"
|
||||
mkdir -p "$HOME/.config/Citron"
|
||||
mkdir -p "$HOME/.local/share/citron"
|
||||
mkdir -p "$HOME/.local/share/Citron"
|
||||
mkdir -p "$HOME/Games/Switch"
|
||||
''
|
||||
+ (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
|
|
@ -170,13 +172,6 @@ in
|
|||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
|
||||
# Manual mounts for data persistence
|
||||
"--ro-bind-try $HOME/.config/kdedefaults $HOME/.config/kdedefaults"
|
||||
"--ro-bind-try $HOME/.local/share/color-schemes $HOME/.local/share/color-schemes"
|
||||
|
|
@ -190,7 +185,9 @@ in
|
|||
# Read-write mounts
|
||||
"--bind $HOME/Games/Switch $HOME/Games/Switch"
|
||||
"--bind $HOME/.config/citron $HOME/.config/citron"
|
||||
"--bind $HOME/.config/Citron $HOME/.config/Citron"
|
||||
"--bind $HOME/.local/share/citron $HOME/.local/share/citron"
|
||||
"--bind $HOME/.local/share/Citron $HOME/.local/share/Citron"
|
||||
"--bind $HOME/.cache/citron-tmp $HOME/.cache/citron-tmp"
|
||||
];
|
||||
};
|
||||
|
|
|
|||
14
modules/system/cosmic.nix
Normal file
14
modules/system/cosmic.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
services.desktopManager.cosmic.enable = true;
|
||||
services.displayManager.cosmic-greeter.enable = false;
|
||||
|
||||
# Optimization
|
||||
services.system76-scheduler.enable = true;
|
||||
|
||||
# Clipboard support (unstable protocol)
|
||||
environment.sessionVariables.COSMIC_DATA_CONTROL_ENABLED = "1";
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@
|
|||
imports = [
|
||||
./common.nix
|
||||
./security.nix
|
||||
./mac-randomization.nix
|
||||
./usbguard.nix
|
||||
./kernel-hardening.nix
|
||||
./secure-boot.nix
|
||||
./dns-over-tls.nix
|
||||
|
|
@ -31,5 +33,8 @@
|
|||
./spotify-sandboxed.nix
|
||||
./performance.nix
|
||||
./vesktop-sandboxed.nix
|
||||
./tutanota-sandboxed.nix
|
||||
./hardened-malloc.nix
|
||||
./searxng.nix
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,13 +138,6 @@ in
|
|||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/io.github.faugus.Launcher/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
|
||||
# dconf for GTK settings
|
||||
"--bind-try /run/user/${toString config.users.users.ashie.uid}/dconf /run/user/${toString config.users.users.ashie.uid}/dconf"
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
# Firefox Sandboxed with nix-bwrapper
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
|
|
|
|||
32
modules/system/hardened-malloc.nix
Normal file
32
modules/system/hardened-malloc.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Hardened Malloc Module (Scudo)
|
||||
# Provides: Userspace memory corruption mitigations via LLVM Scudo
|
||||
#
|
||||
# Usage:
|
||||
# myModules.hardenedMalloc = {
|
||||
# enable = true;
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.hardenedMalloc;
|
||||
in
|
||||
{
|
||||
options.myModules.hardenedMalloc = {
|
||||
enable = lib.mkEnableOption "hardened memory allocator (Scudo)";
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.memoryAllocator.provider = "scudo";
|
||||
|
||||
# Scudo options:
|
||||
# ZeroContents=1: Zero chunks on allocation/deallocation (mitigates use-after-free info leaks)
|
||||
# PatternFillContents=1: (Alternative to Zero) Fill with pattern to catch bugs
|
||||
environment.variables.SCUDO_OPTIONS = "ZeroContents=1";
|
||||
};
|
||||
}
|
||||
|
|
@ -34,6 +34,12 @@
|
|||
"/var/lib/bluetooth" # Bluetooth pairings
|
||||
"/var/lib/sbctl" # Secure Boot Keys
|
||||
"/etc/NetworkManager/system-connections" # Wifi/Ethernet profiles
|
||||
"/var/lib/sonarr"
|
||||
"/var/lib/radarr"
|
||||
"/var/lib/prowlarr"
|
||||
"/var/lib/qbittorrent"
|
||||
"/var/lib/jellyfin"
|
||||
"/var/lib/jellyseerr"
|
||||
];
|
||||
|
||||
files = [
|
||||
|
|
@ -47,6 +53,7 @@
|
|||
"Music"
|
||||
"Pictures"
|
||||
"Videos"
|
||||
"Torrents"
|
||||
"nixos" # Config repo
|
||||
".local/share/PrismLauncher" # Minecraft
|
||||
".local/share/containers" # Rootless podman
|
||||
|
|
@ -58,10 +65,12 @@
|
|||
"git" # Git Repositories
|
||||
".local/state" # Application State
|
||||
".config/Antigravity" # Antigravity Config
|
||||
".config/modprobed-db" # Local modconfig database
|
||||
".config/VSCodium" # Codium Config
|
||||
".config/sops" # Sops Keys
|
||||
".config/gh" # Github CLI Auth
|
||||
".local/share/keyrings" # Gnome Keyrings (Passwords)
|
||||
".local/share/nvim" # NeoVim data (LazyVim, Mason, etc.)
|
||||
".local/share/flatpak" # Flatpak Apps
|
||||
".vscode" # VSCode Extensions
|
||||
".vscode-oss" # VSCodium Extensions
|
||||
|
|
@ -71,6 +80,10 @@
|
|||
".config/citron"
|
||||
".local/share/citron"
|
||||
".cache/lutris"
|
||||
".config/azahar"
|
||||
".local/share/azahar"
|
||||
".config/Citron"
|
||||
".local/share/Citron"
|
||||
".local/share/umu"
|
||||
".cache/mesa_shader_cache"
|
||||
# ".local/share/Steam" # Symlinked to /games/Steam (Already Persistent)
|
||||
|
|
@ -78,6 +91,8 @@
|
|||
".config/steamtinkerlaunch" # Example of extra tools
|
||||
".local/share/applications" # Desktop entries
|
||||
".local/share/icons" # Application icons
|
||||
".local/bin" # User scripts
|
||||
".local/share/qBittorrent"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,6 +66,9 @@ in
|
|||
"randomize_kstack_offset=on"
|
||||
"vsyscall=none"
|
||||
"oops=panic"
|
||||
"debugfs=off"
|
||||
"module.sig_enforce=1"
|
||||
"lockdown=confidentiality"
|
||||
];
|
||||
|
||||
# Kernel sysctl hardening
|
||||
|
|
@ -104,6 +107,13 @@ in
|
|||
"net.ipv4.tcp_rmem" = "4096 87380 2500000";
|
||||
"net.ipv4.tcp_wmem" = "4096 65536 2500000";
|
||||
"net.core.netdev_max_backlog" = 5000;
|
||||
|
||||
# Advanced Security
|
||||
"net.core.bpf_jit_harden" = 2;
|
||||
"fs.suid_dumpable" = 0;
|
||||
"kernel.sysrq" = 0;
|
||||
"net.ipv4.conf.all.accept_source_route" = 0;
|
||||
"net.ipv6.conf.all.accept_source_route" = 0;
|
||||
};
|
||||
|
||||
# Set IO Scheduler to kyber for NVMe and bfq for SATA
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ in
|
|||
];
|
||||
|
||||
readWrite = [
|
||||
"$HOME/Games"
|
||||
"$HOME/Games/windows"
|
||||
"$HOME/.local/share/icons"
|
||||
"$HOME/.config/lutris"
|
||||
"$HOME/.local/share/lutris"
|
||||
|
|
|
|||
42
modules/system/mac-randomization.nix
Normal file
42
modules/system/mac-randomization.nix
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# MAC Address Randomization Module
|
||||
# Provides: MAC address randomization for Wi-Fi and Ethernet
|
||||
#
|
||||
# Usage:
|
||||
# myModules.macRandomization = {
|
||||
# enable = true;
|
||||
# mode = "stable-ssid"; # "random", "stable", "stable-ssid" (default)
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.macRandomization;
|
||||
in
|
||||
{
|
||||
options.myModules.macRandomization = {
|
||||
enable = lib.mkEnableOption "MAC address randomization";
|
||||
|
||||
mode = lib.mkOption {
|
||||
type = lib.types.enum [ "random" "stable" "stable-ssid" ];
|
||||
default = "stable-ssid";
|
||||
description = ''
|
||||
MAC randomization mode:
|
||||
- random: Randomize for every connection (highest privacy, might break captive portals).
|
||||
- stable: Generate a stable random MAC per connection profile.
|
||||
- stable-ssid: Generate a stable random MAC per SSID (default).
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
networking.networkmanager.wifi.macAddress = cfg.mode;
|
||||
|
||||
# Optional: Ethernet randomization (can cause issues on some LANs)
|
||||
# networking.networkmanager.ethernet.macAddress = cfg.mode;
|
||||
};
|
||||
}
|
||||
167
modules/system/media.nix
Normal file
167
modules/system/media.nix
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
# 1. Create the 'media' group (optional now if running as user)
|
||||
users.groups.media = { };
|
||||
|
||||
# 2. OCI Container Configuration
|
||||
virtualisation.oci-containers.containers = {
|
||||
# Prowlarr
|
||||
prowlarr = {
|
||||
image = "lscr.io/linuxserver/prowlarr:latest";
|
||||
ports = [ "9696:9696" ];
|
||||
environment = {
|
||||
PUID = "1000";
|
||||
PGID = "100"; # users group
|
||||
TZ = "Europe/Berlin";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/prowlarr:/config"
|
||||
];
|
||||
};
|
||||
|
||||
# Sonarr
|
||||
sonarr = {
|
||||
image = "lscr.io/linuxserver/sonarr:latest";
|
||||
ports = [ "8989:8989" ];
|
||||
environment = {
|
||||
PUID = "1000";
|
||||
PGID = "100";
|
||||
TZ = "Europe/Berlin";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/sonarr:/config"
|
||||
"/data:/data"
|
||||
];
|
||||
};
|
||||
|
||||
# Radarr
|
||||
radarr = {
|
||||
image = "lscr.io/linuxserver/radarr:latest";
|
||||
ports = [ "7878:7878" ];
|
||||
environment = {
|
||||
PUID = "1000";
|
||||
PGID = "100";
|
||||
TZ = "Europe/Berlin";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/radarr:/config"
|
||||
"/data:/data"
|
||||
];
|
||||
};
|
||||
# FlareSolverr (Cloudflare Bypass)
|
||||
flaresolverr = {
|
||||
image = "ghcr.io/flaresolverr/flaresolverr:latest";
|
||||
ports = [ "8191:8191" ];
|
||||
environment = {
|
||||
TZ = "Europe/Berlin";
|
||||
LOG_LEVEL = "info";
|
||||
};
|
||||
};
|
||||
|
||||
# Jellyfin (Media Server)
|
||||
jellyfin = {
|
||||
image = "lscr.io/linuxserver/jellyfin:latest";
|
||||
ports = [ "8096:8096" ];
|
||||
environment = {
|
||||
PUID = "1000";
|
||||
PGID = "100";
|
||||
TZ = "Europe/Berlin";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/jellyfin:/config"
|
||||
"/data:/data"
|
||||
];
|
||||
};
|
||||
|
||||
# VPN (Gluetun)
|
||||
# WARNING: You must configure your VPN provider details in 'environmentFiles' or 'environment'
|
||||
vpn = {
|
||||
image = "qmcgaw/gluetun";
|
||||
ports = [
|
||||
"8080:8080" # qBittorrent WebUI
|
||||
"6881:6881" # Torrent Port TCP
|
||||
"6881:6881/udp" # Torrent Port UDP
|
||||
];
|
||||
environmentFiles = [ config.sops.templates."gluetun.env".path ];
|
||||
environment = {
|
||||
TZ = "Europe/Berlin";
|
||||
DOT = "off";
|
||||
DNS_ADDRESS = "1.1.1.1";
|
||||
WIREGUARD_MTU = "1420";
|
||||
};
|
||||
extraOptions = [
|
||||
"--cap-add=NET_ADMIN"
|
||||
"--cap-add=NET_RAW"
|
||||
"--device=/dev/net/tun:/dev/net/tun"
|
||||
];
|
||||
};
|
||||
|
||||
# qBittorrent (Networked via VPN)
|
||||
torrent = {
|
||||
image = "lscr.io/linuxserver/qbittorrent:latest";
|
||||
# Ports are exposed via vpn container, not here
|
||||
extraOptions = [ "--network=container:vpn" ];
|
||||
dependsOn = [ "vpn" ];
|
||||
environment = {
|
||||
PUID = "1000";
|
||||
PGID = "100";
|
||||
TZ = "Europe/Berlin";
|
||||
WEBUI_PORT = "8080";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/qbittorrent:/config"
|
||||
"/data:/data"
|
||||
];
|
||||
};
|
||||
|
||||
# Jellyseerr (Request Management)
|
||||
jellyseerr = {
|
||||
image = "docker.io/fallenbagel/jellyseerr:latest";
|
||||
ports = [ "5055:5055" ];
|
||||
environment = {
|
||||
LOG_LEVEL = "debug";
|
||||
TZ = "Europe/Berlin";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/jellyseerr:/app/config"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# Ensure config directories exist and are owned by the user (1000)
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /data 0755 ashie users - -"
|
||||
"d /var/lib/prowlarr 0755 ashie users - -"
|
||||
"d /var/lib/sonarr 0755 ashie users - -"
|
||||
"d /var/lib/radarr 0755 ashie users - -"
|
||||
"d /var/lib/qbittorrent 0755 ashie users - -"
|
||||
"d /var/lib/jellyfin 0755 ashie users - -"
|
||||
"d /var/lib/jellyseerr 0755 ashie users - -"
|
||||
# Recursively fix permissions on restart to ensure 1000 owns the config
|
||||
"Z /var/lib/prowlarr - ashie users - -"
|
||||
"Z /var/lib/sonarr - ashie users - -"
|
||||
"Z /var/lib/radarr - ashie users - -"
|
||||
"Z /var/lib/qbittorrent - ashie users - -"
|
||||
"Z /var/lib/jellyfin - ashie users - -"
|
||||
"Z /var/lib/jellyseerr - ashie users - -"
|
||||
];
|
||||
|
||||
# Firewall rules
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
9696
|
||||
8989
|
||||
7878
|
||||
8191
|
||||
8080
|
||||
8096
|
||||
5055
|
||||
6881
|
||||
];
|
||||
networking.firewall.allowedUDPPorts = [ 6881 ];
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
config = lib.mkIf config.myModules.performance.enable {
|
||||
services.scx = {
|
||||
enable = true;
|
||||
scheduler = "scx_rustland";
|
||||
scheduler = "scx_lavd";
|
||||
package = pkgs.scx.full;
|
||||
};
|
||||
|
||||
|
|
@ -46,6 +46,8 @@
|
|||
"net.ipv4.tcp_wmem" = lib.mkForce "4096 65536 16777216";
|
||||
};
|
||||
|
||||
powerManagement.cpuFreqGovernor = lib.mkDefault "performance";
|
||||
|
||||
# faster boot
|
||||
systemd.services.NetworkManager-wait-online.enable = lib.mkForce false;
|
||||
systemd.services.systemd-networkd-wait-online.enable = lib.mkForce false;
|
||||
|
|
|
|||
|
|
@ -53,28 +53,29 @@ in
|
|||
prismlauncher-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
id = "org.prismlauncher.PrismLauncher";
|
||||
package =
|
||||
inputs.prismlauncher.packages.${pkgs.stdenv.hostPlatform.system}.prismlauncher.overrideAttrs
|
||||
(old: {
|
||||
pname = "prismlauncher";
|
||||
version = old.version or "9.1";
|
||||
buildInputs = (old.buildInputs or [ ]) ++ runtimeLibs ++ [ pkgs.mimalloc ];
|
||||
package = pkgs.prismlauncher.overrideAttrs (old: {
|
||||
pname = "prismlauncher";
|
||||
version = old.version or "9.1";
|
||||
buildInputs = (old.buildInputs or [ ]) ++ [ pkgs.mimalloc ];
|
||||
|
||||
qtWrapperArgs = (old.qtWrapperArgs or [ ]) ++ [
|
||||
"--set MIMALLOC_PATH ${pkgs.mimalloc}/lib/libmimalloc.so"
|
||||
"--prefix LD_PRELOAD : ${pkgs.mimalloc}/lib/libmimalloc.so"
|
||||
"--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath runtimeLibs}"
|
||||
"--prefix QT_PLUGIN_PATH : ${pkgs.kdePackages.qtstyleplugin-kvantum}/lib/qt6/plugins"
|
||||
];
|
||||
});
|
||||
# Keep runtimeLibs in closure without injecting them into environment
|
||||
postInstall = (old.postInstall or "") + ''
|
||||
mkdir -p $out/share/prismlauncher-sandboxed
|
||||
echo "${lib.makeLibraryPath runtimeLibs}" > $out/share/prismlauncher-sandboxed/libs
|
||||
'';
|
||||
|
||||
qtWrapperArgs = (old.qtWrapperArgs or [ ]) ++ [
|
||||
"--set MIMALLOC_PATH ${pkgs.mimalloc}/lib/libmimalloc.so"
|
||||
"--prefix LD_PRELOAD : ${pkgs.mimalloc}/lib/libmimalloc.so"
|
||||
];
|
||||
});
|
||||
|
||||
env = {
|
||||
# Propagate XDG_DATA_DIRS so themes/icons can be found
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
GTK_THEME = "catppuccin-mocha-mauve-standard";
|
||||
QT_QPA_PLATFORMTHEME = "gtk3";
|
||||
QT_STYLE_OVERRIDE = "kvantum";
|
||||
# XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
BROWSER = "firefox";
|
||||
QT_QPA_PLATFORMTHEME = "";
|
||||
QT_STYLE_OVERRIDE = "fusion";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -103,7 +104,7 @@ in
|
|||
"--dir /run/user"
|
||||
"--dir /run/user/${toString config.users.users.ashie.uid}"
|
||||
# Bind ro system paths commonly needed
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
# "--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--dir /run/systemd/resolve"
|
||||
|
|
|
|||
|
|
@ -109,13 +109,6 @@ in
|
|||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/${appId}/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
|
||||
# Wayland socket
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
|
||||
# PipeWire + Pulse
|
||||
''--bind "$XDG_RUNTIME_DIR/pipewire-0" "$XDG_RUNTIME_DIR/pipewire-0"''
|
||||
''--bind "$XDG_RUNTIME_DIR/pulse" "$XDG_RUNTIME_DIR/pulse"''
|
||||
|
||||
# Manual mounts for data persistence
|
||||
"--ro-bind-try $HOME/.config/kdedefaults $HOME/.config/kdedefaults"
|
||||
"--ro-bind-try $HOME/.local/share/color-schemes $HOME/.local/share/color-schemes"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ in
|
|||
enable = lib.mkEnableOption "sched-ext (scx) schedulers";
|
||||
|
||||
scheduler = lib.mkOption {
|
||||
type = lib.types.enum [ "scx_rustland" "scx_lavd" "scx_rusty" "scx_bpfland" ];
|
||||
type = lib.types.enum [ "scx_lavd" "scx_rusty" "scx_bpfland" ];
|
||||
default = "scx_lavd";
|
||||
description = "The scx scheduler to run.";
|
||||
};
|
||||
|
|
|
|||
303
modules/system/searxng.nix
Normal file
303
modules/system/searxng.nix
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
# SearXNG Module (Rootless Podman)
|
||||
# Provides: Private meta-search engine running in a rootless container
|
||||
#
|
||||
# Usage:
|
||||
# myModules.searxng = {
|
||||
# enable = true;
|
||||
# port = 8888;
|
||||
# domain = "search.ashisgreat.xyz";
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.searxng;
|
||||
|
||||
catppuccinCss = pkgs.writeText "searxng-catppuccin.css" ''
|
||||
:root {
|
||||
/* Mocha (Dark) */
|
||||
--cat-rosewater: #f5e0dc;
|
||||
--cat-flamingo: #f2cdcd;
|
||||
--cat-pink: #f5c2e7;
|
||||
--cat-mauve: #cba6f7;
|
||||
--cat-red: #f38ba8;
|
||||
--cat-maroon: #eba0ac;
|
||||
--cat-peach: #fab387;
|
||||
--cat-yellow: #f9e2af;
|
||||
--cat-green: #a6e3a1;
|
||||
--cat-teal: #94e2d5;
|
||||
--cat-sky: #89dceb;
|
||||
--cat-sapphire: #74c7ec;
|
||||
--cat-blue: #89b4fa;
|
||||
--cat-lavender: #b4befe;
|
||||
--cat-text: #cdd6f4;
|
||||
--cat-subtext1: #bac2de;
|
||||
--cat-subtext0: #a6adc8;
|
||||
--cat-overlay2: #9399b2;
|
||||
--cat-overlay1: #7f849c;
|
||||
--cat-overlay0: #6c7086;
|
||||
--cat-surface2: #585b70;
|
||||
--cat-surface1: #45475a;
|
||||
--cat-surface0: #313244;
|
||||
--cat-base: #1e1e2e;
|
||||
--cat-mantle: #181825;
|
||||
--cat-crust: #11111b;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
/* Latte (Light) */
|
||||
--cat-rosewater: #dc8a78;
|
||||
--cat-flamingo: #dd7878;
|
||||
--cat-pink: #ea76cb;
|
||||
--cat-mauve: #8839ef;
|
||||
--cat-red: #d20f39;
|
||||
--cat-maroon: #e64553;
|
||||
--cat-peach: #fe640b;
|
||||
--cat-yellow: #df8e1d;
|
||||
--cat-green: #40a02b;
|
||||
--cat-teal: #179287;
|
||||
--cat-sky: #04a5e5;
|
||||
--cat-sapphire: #209fb5;
|
||||
--cat-blue: #1e66f5;
|
||||
--cat-lavender: #7287fd;
|
||||
--cat-text: #4c4f69;
|
||||
--cat-subtext1: #5c5f77;
|
||||
--cat-subtext0: #6c6f85;
|
||||
--cat-overlay2: #7c7f93;
|
||||
--cat-overlay1: #8c8fa1;
|
||||
--cat-overlay0: #9ca0b0;
|
||||
--cat-surface2: #acb0be;
|
||||
--cat-surface1: #bcc0cc;
|
||||
--cat-surface0: #ccd0da;
|
||||
--cat-base: #eff1f5;
|
||||
--cat-mantle: #e6e9ef;
|
||||
--cat-crust: #dce0e8;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply variables */
|
||||
:root {
|
||||
--color-base-font: var(--cat-text);
|
||||
--color-base-background: var(--cat-base);
|
||||
--color-base-background-mobile: var(--cat-base);
|
||||
--color-url-font: var(--cat-mauve);
|
||||
--color-url-visited-font: var(--cat-mauve);
|
||||
--color-header-background: var(--cat-mantle);
|
||||
--color-header-border: var(--cat-mantle);
|
||||
--color-footer-background: var(--cat-mantle);
|
||||
--color-footer-border: var(--cat-mantle);
|
||||
--color-sidebar-border: var(--cat-base);
|
||||
--color-sidebar-font: var(--cat-text);
|
||||
--color-sidebar-background: var(--cat-base);
|
||||
--color-backtotop-font: var(--cat-subtext1);
|
||||
--color-backtotop-border: var(--cat-surface0);
|
||||
--color-backtotop-background: var(--cat-surface0);
|
||||
--color-btn-background: var(--cat-mauve);
|
||||
--color-btn-font: var(--cat-base);
|
||||
--color-show-btn-background: var(--cat-mauve);
|
||||
--color-show-btn-font: var(--cat-base);
|
||||
--color-search-border: var(--cat-surface0);
|
||||
--color-search-shadow: 0 2px 8px var(--cat-crust);
|
||||
--color-search-background: var(--cat-surface0);
|
||||
--color-search-font: var(--cat-text);
|
||||
--color-search-background-hover: var(--cat-mauve);
|
||||
--color-error: var(--cat-red);
|
||||
--color-error-background: var(--cat-surface0);
|
||||
--color-warning: var(--cat-yellow);
|
||||
--color-warning-background: var(--cat-surface0);
|
||||
--color-success: var(--cat-green);
|
||||
--color-success-background: var(--cat-surface0);
|
||||
--color-categories-item-selected-font: var(--cat-text);
|
||||
--color-categories-item-border-selected: var(--cat-mauve);
|
||||
--color-autocomplete-font: var(--cat-subtext1);
|
||||
--color-autocomplete-border: var(--cat-surface0);
|
||||
--color-autocomplete-shadow: 0 2px 8px var(--cat-crust);
|
||||
--color-autocomplete-background: var(--cat-surface0);
|
||||
--color-autocomplete-background-hover: var(--cat-surface1);
|
||||
--color-answer-font: var(--cat-text);
|
||||
--color-answer-background: var(--cat-mantle);
|
||||
--color-result-background: var(--cat-mantle);
|
||||
--color-result-border: var(--cat-base);
|
||||
--color-result-url-font: var(--cat-subtext1);
|
||||
--color-result-vim-selected: var(--cat-surface0);
|
||||
--color-result-vim-arrow: var(--cat-mauve);
|
||||
--color-result-description-highlight-font: var(--cat-text);
|
||||
--color-result-link-font: var(--cat-mauve);
|
||||
--color-result-link-font-highlight: var(--cat-mauve);
|
||||
--color-result-link-visited-font: var(--cat-mauve);
|
||||
--color-result-publishdate-font: var(--cat-surface2);
|
||||
--color-result-engines-font: var(--cat-surface2);
|
||||
--color-result-search-url-border: var(--cat-surface2);
|
||||
--color-result-search-url-font: var(--cat-text);
|
||||
--color-result-detail-font: var(--cat-text);
|
||||
--color-result-detail-label-font: var(--cat-subtext0);
|
||||
--color-result-detail-background: var(--cat-base);
|
||||
--color-result-detail-hr: var(--cat-base);
|
||||
--color-result-detail-link: var(--cat-mauve);
|
||||
--color-result-detail-loader-border: rgba(255, 255, 255, 0.2);
|
||||
--color-result-detail-loader-borderleft: var(--cat-crust);
|
||||
--color-result-image-span-font: var(--cat-text);
|
||||
--color-result-image-span-font-selected: var(--cat-base);
|
||||
--color-result-image-background: var(--cat-mantle);
|
||||
--color-settings-tr-hover: var(--cat-surface0);
|
||||
--color-settings-engine-description-font: var(--cat-text);
|
||||
--color-settings-engine-group-background: var(--cat-surface0);
|
||||
--color-toolkit-badge-font: var(--cat-text);
|
||||
--color-toolkit-badge-background: var(--cat-surface0);
|
||||
--color-toolkit-kbd-font: var(--cat-text);
|
||||
--color-toolkit-kbd-background: var(--cat-mantle);
|
||||
--color-toolkit-dialog-border: var(--cat-mantle);
|
||||
--color-toolkit-dialog-background: var(--cat-mantle);
|
||||
--color-toolkit-tabs-label-border: var(--cat-base);
|
||||
--color-toolkit-tabs-section-border: var(--cat-base);
|
||||
--color-toolkit-select-background: var(--cat-surface0);
|
||||
--color-toolkit-select-border: var(--cat-surface0);
|
||||
--color-toolkit-select-background-hover: var(--cat-surface1);
|
||||
--color-toolkit-input-text-font: var(--cat-text);
|
||||
--color-toolkit-checkbox-onoff-off-background: var(--cat-surface0);
|
||||
--color-toolkit-checkbox-onoff-on-background: var(--cat-surface0);
|
||||
--color-toolkit-checkbox-onoff-on-mark-background: var(--cat-green);
|
||||
--color-toolkit-checkbox-onoff-on-mark-color: var(--cat-mantle);
|
||||
--color-toolkit-checkbox-onoff-off-mark-background: var(--cat-red);
|
||||
--color-toolkit-checkbox-onoff-off-mark-color: var(--cat-mantle);
|
||||
--color-toolkit-checkbox-label-background: var(--cat-base);
|
||||
--color-toolkit-checkbox-label-border: var(--cat-mantle);
|
||||
--color-toolkit-checkbox-input-border: var(--cat-mauve);
|
||||
--color-toolkit-engine-tooltip-border: var(--cat-surface0);
|
||||
--color-toolkit-engine-tooltip-background: var(--cat-surface0);
|
||||
--color-toolkit-loader-border: rgba(255, 255, 255, 0.2);
|
||||
--color-toolkit-loader-borderleft: var(--cat-crust);
|
||||
--color-doc-code: var(--cat-rosewater);
|
||||
--color-doc-code-background: var(--cat-mantle);
|
||||
}
|
||||
|
||||
#search_logo svg :not([fill="none"]) {
|
||||
fill: var(--cat-mauve) !important;
|
||||
}
|
||||
#search_logo svg :not([stroke="none"]) {
|
||||
stroke: var(--cat-mauve) !important;
|
||||
}
|
||||
|
||||
/* Additional cute tweaks */
|
||||
article.result {
|
||||
background-color: var(--color-result-background);
|
||||
border-radius: 0.75em;
|
||||
padding: 0.75em;
|
||||
margin: 0.5em;
|
||||
border: 1px solid var(--cat-surface0);
|
||||
}
|
||||
article.category-images {
|
||||
padding-bottom: 4em;
|
||||
}
|
||||
input[type="text"] {
|
||||
border-radius: 2em !important;
|
||||
}
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.myModules.searxng = {
|
||||
enable = lib.mkEnableOption "SearXNG meta-search engine";
|
||||
|
||||
port = lib.mkOption {
|
||||
type = lib.types.port;
|
||||
default = 8888;
|
||||
description = "Port to expose SearXNG on localhost";
|
||||
};
|
||||
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "search.ashisgreat.xyz";
|
||||
description = "Public domain name for SearXNG";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Ensure Podman is enabled
|
||||
myModules.podman.enable = true;
|
||||
|
||||
# 1. Redis Container (Cache/Limiter)
|
||||
virtualisation.oci-containers.containers."searxng-redis" = {
|
||||
image = "docker.io/library/redis:alpine";
|
||||
cmd = [ "redis-server" "--save" "" "--appendonly" "no" ]; # Ephemeral cache, no persistence needed
|
||||
ports = [ "127.0.0.1:6379:6379" ];
|
||||
};
|
||||
|
||||
# 2. SearXNG Container
|
||||
virtualisation.oci-containers.containers."searxng" = {
|
||||
image = "docker.io/searxng/searxng:latest";
|
||||
ports = [ "127.0.0.1:${toString cfg.port}:8080" ];
|
||||
environment = {
|
||||
"SEARXNG_BASE_URL" = "https://${cfg.domain}";
|
||||
"SEARXNG_REDIS_URL" = "redis://searxng-redis:6379"; # Talk to Redis directly via container DNS
|
||||
"SEARXNG_URL_BASE" = "https://${cfg.domain}";
|
||||
};
|
||||
environmentFiles = [
|
||||
# Contains SEARXNG_SECRET_KEY
|
||||
config.sops.templates."searxng.env".path
|
||||
];
|
||||
extraOptions = [
|
||||
"--cap-drop=ALL"
|
||||
"--cap-add=CHOWN"
|
||||
"--cap-add=SETGID"
|
||||
"--cap-add=SETUID"
|
||||
"--cap-add=DAC_OVERRIDE"
|
||||
"--add-host=host.containers.internal:host-gateway"
|
||||
];
|
||||
volumes = [
|
||||
"${config.sops.templates."searxng_settings.yml".path}:/etc/searxng/settings.yml:ro"
|
||||
"${catppuccinCss}:/etc/searxng/custom.css:ro"
|
||||
];
|
||||
};
|
||||
|
||||
# 3. Secrets Configuration
|
||||
# We generate the settings.yml dynamically using sops templates to inject secrets if needed,
|
||||
# or just to manage the config declaratively.
|
||||
sops.templates."searxng.env".content = ''
|
||||
SEARXNG_SECRET_KEY=${config.sops.placeholder.searxng_secret_key}
|
||||
'';
|
||||
|
||||
sops.templates."searxng_settings.yml".content = ''
|
||||
use_default_settings: true
|
||||
|
||||
general:
|
||||
debug: false
|
||||
instance_name: "Ashie Search"
|
||||
donations:
|
||||
patreon: false
|
||||
buymeacoffee: false
|
||||
|
||||
search:
|
||||
safe_search: 0
|
||||
autocomplete: "google"
|
||||
default_lang: "en-US"
|
||||
formats:
|
||||
- html
|
||||
- json
|
||||
|
||||
server:
|
||||
port: 8080
|
||||
bind_address: "0.0.0.0"
|
||||
secret_key: "${config.sops.placeholder.searxng_secret_key}"
|
||||
limiter: true
|
||||
image_proxy: true
|
||||
|
||||
ui:
|
||||
static_use_hash: true
|
||||
custom_css: custom.css
|
||||
theme_args:
|
||||
simple_style: "auto"
|
||||
|
||||
redis:
|
||||
url: redis://searxng-redis:6379/0
|
||||
'';
|
||||
|
||||
# Placeholder secret definition (User must add this to secrets.yaml!)
|
||||
sops.secrets.searxng_secret_key = { };
|
||||
};
|
||||
}
|
||||
|
|
@ -1,14 +1,3 @@
|
|||
# Security Hardening Module
|
||||
# Provides: doas (sudo replacement), audit logging, AppArmor, core dump prevention
|
||||
#
|
||||
# Usage:
|
||||
# myModules.security = {
|
||||
# enable = true;
|
||||
# enableAudit = true; # default: true
|
||||
# enableAppArmor = true; # default: true
|
||||
# useDoas = true; # default: true (replaces sudo)
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
|
|
@ -66,6 +55,14 @@ in
|
|||
}
|
||||
];
|
||||
|
||||
# Proc hardening (Hide other users' processes)
|
||||
boot.specialFileSystems."/proc".options = [
|
||||
"nosuid"
|
||||
"nodev"
|
||||
"noexec"
|
||||
"hidepid=2"
|
||||
];
|
||||
|
||||
# Security audit logging
|
||||
security.auditd.enable = cfg.enableAudit;
|
||||
security.audit = lib.mkIf cfg.enableAudit {
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ in
|
|||
"$HOME/.local/share/color-schemes"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/Games"
|
||||
"$HOME/.steam"
|
||||
"$HOME/.local/share/Steam"
|
||||
"$HOME/.local/share/umu"
|
||||
|
|
@ -104,6 +103,7 @@ in
|
|||
"$HOME/.local/share/icons"
|
||||
"$HOME/.local/share/Larian Studios"
|
||||
"$HOME/Desktop"
|
||||
"/games/steam"
|
||||
];
|
||||
};
|
||||
|
||||
|
|
|
|||
119
modules/system/tutanota-sandboxed.nix
Normal file
119
modules/system/tutanota-sandboxed.nix
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
inputs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
bwrapperPkgs = pkgs.extend inputs.nix-bwrapper.overlays.default;
|
||||
|
||||
pname = "tutanota-desktop";
|
||||
version = "319.260107.1";
|
||||
|
||||
src = pkgs.fetchurl {
|
||||
url = "https://github.com/tutao/tutanota/releases/download/tutanota-desktop-release-${version}/tutanota-desktop-linux.AppImage";
|
||||
sha256 = "0gjvh3f70mmr85kx3kz4yd8gfxpk4kj8wkh697a4gy34mgxpqnka";
|
||||
};
|
||||
|
||||
appimageContents = pkgs.appimageTools.extractType2 {
|
||||
inherit pname version src;
|
||||
};
|
||||
|
||||
tutanota = pkgs.appimageTools.wrapType2 {
|
||||
inherit pname version src;
|
||||
|
||||
extraInstallCommands = ''
|
||||
install -m 444 -D ${appimageContents}/tutanota-desktop.desktop $out/share/applications/tutanota-desktop.desktop
|
||||
install -m 444 -D ${appimageContents}/tutanota-desktop.png \
|
||||
$out/share/icons/hicolor/512x512/apps/tutanota-desktop.png
|
||||
substituteInPlace $out/share/applications/tutanota-desktop.desktop \
|
||||
--replace 'Exec=AppRun' 'Exec=tutanota-desktop'
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
tutanota-sandboxed = bwrapperPkgs.mkBwrapper {
|
||||
app = {
|
||||
package = tutanota;
|
||||
id = "com.tutanota.Tutanota";
|
||||
env = {
|
||||
XDG_DATA_DIRS = "$XDG_DATA_DIRS";
|
||||
};
|
||||
};
|
||||
|
||||
flatpak.enable = false;
|
||||
|
||||
# Basic sandboxing
|
||||
fhsenv.opts = {
|
||||
unshareUser = true;
|
||||
unshareUts = true;
|
||||
unshareCgroup = true;
|
||||
unsharePid = true;
|
||||
unshareNet = false; # Needs network
|
||||
unshareIpc = true;
|
||||
};
|
||||
|
||||
fhsenv.bwrap.baseArgs = lib.mkForce [
|
||||
"--new-session"
|
||||
"--proc /proc"
|
||||
"--dev /dev"
|
||||
"--dev-bind /dev/dri /dev/dri" # GPU acceleration
|
||||
"--tmpfs /home"
|
||||
"--tmpfs /tmp"
|
||||
"--tmpfs /run"
|
||||
"--dir /run/user"
|
||||
"--dir /run/user/${toString config.users.users.ashie.uid}"
|
||||
# System paths
|
||||
"--ro-bind /sys /sys"
|
||||
"--ro-bind-try /run/current-system /run/current-system"
|
||||
"--ro-bind-try /run/opengl-driver /run/opengl-driver"
|
||||
"--ro-bind-try /run/opengl-driver-32 /run/opengl-driver-32"
|
||||
"--dir /run/systemd/resolve"
|
||||
"--ro-bind-try /run/systemd/resolve /run/systemd/resolve"
|
||||
];
|
||||
|
||||
mounts = {
|
||||
read = [
|
||||
"$HOME/.config/fontconfig"
|
||||
"$HOME/.local/share/fonts"
|
||||
"$HOME/.icons"
|
||||
"$HOME/.themes"
|
||||
"$HOME/.local/share/themes"
|
||||
"$HOME/.config/kdedefaults"
|
||||
"$HOME/.local/share/color-schemes"
|
||||
];
|
||||
readWrite = [
|
||||
"$HOME/.config/tutanota-desktop"
|
||||
"$HOME/Downloads"
|
||||
];
|
||||
};
|
||||
|
||||
dbus.enable = false;
|
||||
script.preCmds.stage2 = (import ./sandbox-utils.nix { inherit pkgs lib; }).mkDbusProxyScript {
|
||||
appId = "com.tutanota.Tutanota";
|
||||
enableSystemBus = false;
|
||||
proxyArgs = [
|
||||
"--filter"
|
||||
''--talk="org.freedesktop.portal.*"''
|
||||
''--talk="org.freedesktop.Notifications"''
|
||||
''--talk="org.freedesktop.secrets"''
|
||||
''--talk="org.gnome.keyring.SystemPrompter"'' # Often needed for secrets
|
||||
''--call="org.freedesktop.portal.*=*@/org/freedesktop/portal/desktop"''
|
||||
''--own="com.tutanota.Tutanota"''
|
||||
];
|
||||
};
|
||||
|
||||
fhsenv.bwrap.additionalArgs = [
|
||||
# D-Bus session proxy only
|
||||
''--bind "$XDG_RUNTIME_DIR/app/com.tutanota.Tutanota/bus" "$XDG_RUNTIME_DIR/bus"''
|
||||
# Wayland
|
||||
''--bind "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY"''
|
||||
];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
52
modules/system/usbguard.nix
Normal file
52
modules/system/usbguard.nix
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# USBGuard Module
|
||||
# Provides: Policy enforcement for USB devices
|
||||
#
|
||||
# Usage:
|
||||
# myModules.usbguard = {
|
||||
# enable = true;
|
||||
# generatePolicy = true; # Auto-generate policy from currently connected devices
|
||||
# };
|
||||
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.myModules.usbguard;
|
||||
in
|
||||
{
|
||||
options.myModules.usbguard = {
|
||||
enable = lib.mkEnableOption "USBGuard for USB device control";
|
||||
|
||||
generatePolicy = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = "Generate an initial policy from currently connected devices on activation (requires reboot/service restart)";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
services.usbguard = {
|
||||
enable = true;
|
||||
dbus.enable = true;
|
||||
|
||||
# Block new devices by default
|
||||
implicitPolicyTarget = "block";
|
||||
|
||||
# Treat present devices as allowed (until policy is generated)
|
||||
presentDevicePolicy = "apply-policy"; # or "keep" or "allow"
|
||||
};
|
||||
|
||||
# Helper script to generate policy
|
||||
environment.systemPackages = lib.mkIf cfg.generatePolicy [
|
||||
(pkgs.writeShellScriptBin "generate-usbguard-policy" ''
|
||||
sudo usbguard generate-policy > /etc/usbguard/rules.conf
|
||||
sudo systemctl restart usbguard
|
||||
echo "USBGuard policy generated from connected devices."
|
||||
'')
|
||||
];
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue