mirror of
https://github.com/nmasur/dotfiles
synced 2025-07-05 13:50:13 +00:00
move all files to new nixfmt rfc
This commit is contained in:
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options = {
|
||||
calibre = {
|
||||
@ -16,6 +22,8 @@
|
||||
};
|
||||
|
||||
# Forces Calibre to use dark mode
|
||||
environment.sessionVariables = { CALIBRE_USE_DARK_PALETTE = "1"; };
|
||||
environment.sessionVariables = {
|
||||
CALIBRE_USE_DARK_PALETTE = "1";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
{ ... }: {
|
||||
|
||||
imports = [ ./calibre.nix ./nautilus.nix ];
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
imports = [
|
||||
./calibre.nix
|
||||
./nautilus.nix
|
||||
];
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options = {
|
||||
nautilus = {
|
||||
@ -28,18 +34,15 @@
|
||||
# Generates a QR code and previews it with sushi
|
||||
programs.fish.functions = {
|
||||
qr = {
|
||||
body =
|
||||
"${pkgs.qrencode}/bin/qrencode $argv[1] -o /tmp/qr.png | ${pkgs.gnome.sushi}/bin/sushi /tmp/qr.png";
|
||||
body = "${pkgs.qrencode}/bin/qrencode $argv[1] -o /tmp/qr.png | ${pkgs.gnome.sushi}/bin/sushi /tmp/qr.png";
|
||||
};
|
||||
};
|
||||
|
||||
# Set Nautilus as default for opening directories
|
||||
xdg.mimeApps = {
|
||||
associations.added."inode/directory" = [ "org.gnome.Nautilus.desktop" ];
|
||||
defaultApplications."inode/directory" =
|
||||
lib.mkBefore [ "org.gnome.Nautilus.desktop" ];
|
||||
defaultApplications."inode/directory" = lib.mkBefore [ "org.gnome.Nautilus.desktop" ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
# Delete Trash files older than 1 week
|
||||
@ -48,7 +51,5 @@
|
||||
wantedBy = [ "default.target" ];
|
||||
script = "${pkgs.trash-cli}/bin/trash-empty 7";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
{ ... }: {
|
||||
|
||||
imports =
|
||||
[ ./applications ./gaming ./graphical ./hardware ./services ./system ];
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
imports = [
|
||||
./applications
|
||||
./gaming
|
||||
./graphical
|
||||
./hardware
|
||||
./services
|
||||
./system
|
||||
];
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.gaming.chiaki.enable =
|
||||
lib.mkEnableOption "Chiaki PlayStation remote play client.";
|
||||
options.gaming.chiaki.enable = lib.mkEnableOption "Chiaki PlayStation remote play client.";
|
||||
|
||||
config = lib.mkIf config.gaming.chiaki.enable {
|
||||
environment.systemPackages = with pkgs; [ chiaki ];
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
imports = [
|
||||
./chiaki.nix
|
||||
|
@ -1,29 +1,34 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.gaming.dwarf-fortress.enable =
|
||||
lib.mkEnableOption "Dwarf Fortress free edition.";
|
||||
options.gaming.dwarf-fortress.enable = lib.mkEnableOption "Dwarf Fortress free edition.";
|
||||
|
||||
config = lib.mkIf config.gaming.dwarf-fortress.enable {
|
||||
unfreePackages = [ "dwarf-fortress" ];
|
||||
environment.systemPackages = let
|
||||
dfDesktopItem = pkgs.makeDesktopItem {
|
||||
name = "dwarf-fortress";
|
||||
desktopName = "Dwarf Fortress";
|
||||
exec = "${pkgs.dwarf-fortress-packages.dwarf-fortress-full}/bin/dfhack";
|
||||
terminal = false;
|
||||
};
|
||||
dtDesktopItem = pkgs.makeDesktopItem {
|
||||
name = "dwarftherapist";
|
||||
desktopName = "Dwarf Therapist";
|
||||
exec =
|
||||
"${pkgs.dwarf-fortress-packages.dwarf-fortress-full}/bin/dwarftherapist";
|
||||
terminal = false;
|
||||
};
|
||||
in [
|
||||
pkgs.dwarf-fortress-packages.dwarf-fortress-full
|
||||
dfDesktopItem
|
||||
dtDesktopItem
|
||||
];
|
||||
environment.systemPackages =
|
||||
let
|
||||
dfDesktopItem = pkgs.makeDesktopItem {
|
||||
name = "dwarf-fortress";
|
||||
desktopName = "Dwarf Fortress";
|
||||
exec = "${pkgs.dwarf-fortress-packages.dwarf-fortress-full}/bin/dfhack";
|
||||
terminal = false;
|
||||
};
|
||||
dtDesktopItem = pkgs.makeDesktopItem {
|
||||
name = "dwarftherapist";
|
||||
desktopName = "Dwarf Therapist";
|
||||
exec = "${pkgs.dwarf-fortress-packages.dwarf-fortress-full}/bin/dwarftherapist";
|
||||
terminal = false;
|
||||
};
|
||||
in
|
||||
[
|
||||
pkgs.dwarf-fortress-packages.dwarf-fortress-full
|
||||
dfDesktopItem
|
||||
dtDesktopItem
|
||||
];
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let home-packages = config.home-manager.users.${config.user}.home.packages;
|
||||
let
|
||||
home-packages = config.home-manager.users.${config.user}.home.packages;
|
||||
in
|
||||
{
|
||||
|
||||
in {
|
||||
|
||||
options.gaming.legendary.enable =
|
||||
lib.mkEnableOption "Legendary Epic Games launcher.";
|
||||
options.gaming.legendary.enable = lib.mkEnableOption "Legendary Epic Games launcher.";
|
||||
|
||||
config = lib.mkIf config.gaming.legendary.enable {
|
||||
environment.systemPackages = with pkgs; [
|
||||
@ -28,29 +33,28 @@ in {
|
||||
log_level = error
|
||||
'';
|
||||
|
||||
home.file = let
|
||||
ignorePatterns = ''
|
||||
.wine/
|
||||
drive_c/'';
|
||||
in {
|
||||
".rgignore".text = ignorePatterns;
|
||||
".fdignore".text = ignorePatterns;
|
||||
};
|
||||
|
||||
programs.fish.functions =
|
||||
lib.mkIf (builtins.elem pkgs.fzf home-packages) {
|
||||
epic-games = {
|
||||
body = ''
|
||||
set game (legendary list 2>/dev/null \
|
||||
| awk '/^ \* / { print $0; }' \
|
||||
| sed -e 's/ (.*)$//' -e 's/ \* //' \
|
||||
| fzf)
|
||||
and legendary launch "$game" &> /dev/null
|
||||
'';
|
||||
};
|
||||
home.file =
|
||||
let
|
||||
ignorePatterns = ''
|
||||
.wine/
|
||||
drive_c/'';
|
||||
in
|
||||
{
|
||||
".rgignore".text = ignorePatterns;
|
||||
".fdignore".text = ignorePatterns;
|
||||
};
|
||||
|
||||
programs.fish.functions = lib.mkIf (builtins.elem pkgs.fzf home-packages) {
|
||||
epic-games = {
|
||||
body = ''
|
||||
set game (legendary list 2>/dev/null \
|
||||
| awk '/^ \* / { print $0; }' \
|
||||
| sed -e 's/ (.*)$//' -e 's/ \* //' \
|
||||
| fzf)
|
||||
and legendary launch "$game" &> /dev/null
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.gaming.lutris.enable = lib.mkEnableOption "Lutris game installer.";
|
||||
|
||||
@ -9,5 +15,4 @@
|
||||
wineWowPackages.stable # 32-bit and 64-bit wineWowPackages
|
||||
];
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
@ -6,8 +11,8 @@ let
|
||||
publicPort = 49732;
|
||||
rconPort = 25575;
|
||||
rconPassword = "thiscanbeanything";
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
config = lib.mkIf config.services.minecraft-server.enable {
|
||||
|
||||
@ -52,7 +57,9 @@ in {
|
||||
# https://dataswamp.org/~solene/2022-08-20-on-demand-minecraft-with-systemd.html
|
||||
|
||||
# Prevent Minecraft from starting by default
|
||||
systemd.services.minecraft-server = { wantedBy = pkgs.lib.mkForce [ ]; };
|
||||
systemd.services.minecraft-server = {
|
||||
wantedBy = pkgs.lib.mkForce [ ];
|
||||
};
|
||||
|
||||
# Listen for connections on the public port, to trigger the actual
|
||||
# listen-minecraft service.
|
||||
@ -65,18 +72,25 @@ in {
|
||||
# Proxy traffic to local port, and trigger hook-minecraft
|
||||
systemd.services.listen-minecraft = {
|
||||
path = [ pkgs.systemd ];
|
||||
requires = [ "hook-minecraft.service" "listen-minecraft.socket" ];
|
||||
after = [ "hook-minecraft.service" "listen-minecraft.socket" ];
|
||||
serviceConfig.ExecStart =
|
||||
"${pkgs.systemd.out}/lib/systemd/systemd-socket-proxyd 127.0.0.1:${
|
||||
toString localPort
|
||||
}";
|
||||
requires = [
|
||||
"hook-minecraft.service"
|
||||
"listen-minecraft.socket"
|
||||
];
|
||||
after = [
|
||||
"hook-minecraft.service"
|
||||
"listen-minecraft.socket"
|
||||
];
|
||||
serviceConfig.ExecStart = "${pkgs.systemd.out}/lib/systemd/systemd-socket-proxyd 127.0.0.1:${toString localPort}";
|
||||
};
|
||||
|
||||
# Start Minecraft if required and wait for it to be available
|
||||
# Then unlock the listen-minecraft.service
|
||||
systemd.services.hook-minecraft = {
|
||||
path = with pkgs; [ systemd libressl busybox ];
|
||||
path = with pkgs; [
|
||||
systemd
|
||||
libressl
|
||||
busybox
|
||||
];
|
||||
|
||||
# Start Minecraft and the auto-shutdown timer
|
||||
script = ''
|
||||
@ -87,9 +101,7 @@ in {
|
||||
# Keep checking until the service is available
|
||||
postStart = ''
|
||||
for i in $(seq 60); do
|
||||
if ${pkgs.libressl.nc}/bin/nc -z 127.0.0.1 ${
|
||||
toString localPort
|
||||
} > /dev/null ; then
|
||||
if ${pkgs.libressl.nc}/bin/nc -z 127.0.0.1 ${toString localPort} > /dev/null ; then
|
||||
exit 0
|
||||
fi
|
||||
${pkgs.busybox.out}/bin/sleep 1
|
||||
@ -144,7 +156,5 @@ in {
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,17 +1,19 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.gaming.ryujinx.enable =
|
||||
lib.mkEnableOption "Ryujinx Nintendo Switch application.";
|
||||
options.gaming.ryujinx.enable = lib.mkEnableOption "Ryujinx Nintendo Switch application.";
|
||||
|
||||
config = lib.mkIf config.gaming.ryujinx.enable {
|
||||
environment.systemPackages = with pkgs; [ ryujinx ];
|
||||
|
||||
home-manager.users.${config.user}.xdg.desktopEntries.ryujinx =
|
||||
lib.mkIf pkgs.stdenv.isLinux {
|
||||
name = "Ryujinx";
|
||||
exec =
|
||||
"env DOTNET_EnableAlternateStackCheck=1 Ryujinx -r /home/${config.user}/media/games/ryujinx/ %f";
|
||||
};
|
||||
home-manager.users.${config.user}.xdg.desktopEntries.ryujinx = lib.mkIf pkgs.stdenv.isLinux {
|
||||
name = "Ryujinx";
|
||||
exec = "env DOTNET_EnableAlternateStackCheck=1 Ryujinx -r /home/${config.user}/media/games/ryujinx/ %f";
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,21 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.gaming.steam.enable = lib.mkEnableOption "Steam game launcher.";
|
||||
|
||||
config = lib.mkIf (config.gaming.steam.enable && pkgs.stdenv.isLinux) {
|
||||
hardware.steam-hardware.enable = true;
|
||||
unfreePackages = [ "steam" "steam-original" "steamcmd" "steam-run" ];
|
||||
unfreePackages = [
|
||||
"steam"
|
||||
"steam-original"
|
||||
"steamcmd"
|
||||
"steam-run"
|
||||
];
|
||||
|
||||
programs.steam = {
|
||||
enable = true;
|
||||
@ -13,8 +24,7 @@
|
||||
# Adapted in part from: https://github.com/Shawn8901/nix-configuration/blob/1c48be94238a9f463cf0bbd1e1842a4454286514/modules/nixos/steam-compat-tools/default.nix
|
||||
# Based on: https://github.com/NixOS/nixpkgs/issues/73323
|
||||
extraEnv = {
|
||||
STEAM_EXTRA_COMPAT_TOOLS_PATHS =
|
||||
lib.makeBinPath [ pkgs.proton-ge-custom ];
|
||||
STEAM_EXTRA_COMPAT_TOOLS_PATHS = lib.makeBinPath [ pkgs.proton-ge-custom ];
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -27,13 +37,10 @@
|
||||
|
||||
# Allow downloading of GE-Proton and other versions
|
||||
protonup-qt
|
||||
|
||||
];
|
||||
|
||||
# Seems like NetworkManager can help speed up Steam launch
|
||||
# https://www.reddit.com/r/archlinux/comments/qguhco/steam_startup_time_arch_1451_seconds_fedora_34/hi8opet/
|
||||
networking.networkmanager.enable = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{ lib, ... }: {
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
imports = [
|
||||
./dunst.nix
|
||||
@ -49,7 +50,5 @@
|
||||
type = lib.types.path;
|
||||
description = "Wallpaper background image file";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
|
||||
@ -8,7 +13,5 @@
|
||||
|
||||
home-manager.users.${config.user}.home.packages = [ pkgs.dmenu ];
|
||||
gui.launcherCommand = "${pkgs.dmenu}/bin/dmenu_run";
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -25,7 +25,5 @@
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let fontName = "Victor Mono";
|
||||
|
||||
in {
|
||||
let
|
||||
fontName = "Victor Mono";
|
||||
in
|
||||
{
|
||||
|
||||
config = lib.mkIf (config.gui.enable && pkgs.stdenv.isLinux) {
|
||||
|
||||
@ -24,7 +30,5 @@ in {
|
||||
programs.kitty.font.name = fontName;
|
||||
services.dunst.settings.global.font = "Hack Nerd Font 14";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options = {
|
||||
gtk.theme = {
|
||||
@ -18,20 +24,21 @@
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
gtk = let
|
||||
gtkExtraConfig = {
|
||||
gtk-application-prefer-dark-theme = config.theme.dark;
|
||||
gtk =
|
||||
let
|
||||
gtkExtraConfig = {
|
||||
gtk-application-prefer-dark-theme = config.theme.dark;
|
||||
};
|
||||
in
|
||||
{
|
||||
enable = true;
|
||||
theme = {
|
||||
name = config.gtk.theme.name;
|
||||
package = config.gtk.theme.package;
|
||||
};
|
||||
gtk3.extraConfig = gtkExtraConfig;
|
||||
gtk4.extraConfig = gtkExtraConfig;
|
||||
};
|
||||
in {
|
||||
enable = true;
|
||||
theme = {
|
||||
name = config.gtk.theme.name;
|
||||
package = config.gtk.theme.package;
|
||||
};
|
||||
gtk3.extraConfig = gtkExtraConfig;
|
||||
gtk4.extraConfig = gtkExtraConfig;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
# Required for setting GTK theme (for preferred-color-scheme in browser)
|
||||
@ -44,8 +51,8 @@
|
||||
package = config.gtk.theme.package;
|
||||
};
|
||||
|
||||
environment.sessionVariables = { GTK_THEME = config.gtk.theme.name; };
|
||||
|
||||
environment.sessionVariables = {
|
||||
GTK_THEME = config.gtk.theme.name;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,18 +1,23 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
lockCmd =
|
||||
"${pkgs.betterlockscreen}/bin/betterlockscreen --lock --display 1 --blur 0.5 --span";
|
||||
lockUpdate =
|
||||
"${pkgs.betterlockscreen}/bin/betterlockscreen --update ${config.wallpaper} --display 1 --span";
|
||||
|
||||
in {
|
||||
lockCmd = "${pkgs.betterlockscreen}/bin/betterlockscreen --lock --display 1 --blur 0.5 --span";
|
||||
lockUpdate = "${pkgs.betterlockscreen}/bin/betterlockscreen --update ${config.wallpaper} --display 1 --span";
|
||||
in
|
||||
{
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isLinux {
|
||||
|
||||
services.xserver.windowManager = {
|
||||
i3 = { enable = config.services.xserver.enable; };
|
||||
i3 = {
|
||||
enable = config.services.xserver.enable;
|
||||
};
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs; [
|
||||
@ -23,237 +28,221 @@ in {
|
||||
home-manager.users.${config.user} = {
|
||||
xsession.windowManager.i3 = {
|
||||
enable = config.services.xserver.enable;
|
||||
config = let
|
||||
modifier = "Mod4"; # Super key
|
||||
ws1 = "1:I";
|
||||
ws2 = "2:II";
|
||||
ws3 = "3:III";
|
||||
ws4 = "4:IV";
|
||||
ws5 = "5:V";
|
||||
ws6 = "6:VI";
|
||||
ws7 = "7:VII";
|
||||
ws8 = "8:VIII";
|
||||
ws9 = "9:IX";
|
||||
ws10 = "10:X";
|
||||
in {
|
||||
modifier = modifier;
|
||||
assigns = {
|
||||
"${ws1}" = [{ class = "Firefox"; }];
|
||||
"${ws2}" = [
|
||||
{ class = "kitty"; }
|
||||
{ class = "aerc"; }
|
||||
{ class = "obsidian"; }
|
||||
config =
|
||||
let
|
||||
modifier = "Mod4"; # Super key
|
||||
ws1 = "1:I";
|
||||
ws2 = "2:II";
|
||||
ws3 = "3:III";
|
||||
ws4 = "4:IV";
|
||||
ws5 = "5:V";
|
||||
ws6 = "6:VI";
|
||||
ws7 = "7:VII";
|
||||
ws8 = "8:VIII";
|
||||
ws9 = "9:IX";
|
||||
ws10 = "10:X";
|
||||
in
|
||||
{
|
||||
modifier = modifier;
|
||||
assigns = {
|
||||
"${ws1}" = [ { class = "Firefox"; } ];
|
||||
"${ws2}" = [
|
||||
{ class = "kitty"; }
|
||||
{ class = "aerc"; }
|
||||
{ class = "obsidian"; }
|
||||
];
|
||||
"${ws3}" = [ { class = "discord"; } ];
|
||||
"${ws4}" = [
|
||||
{ class = "steam"; }
|
||||
{ class = "Steam"; }
|
||||
];
|
||||
};
|
||||
bars = [ { command = "echo"; } ]; # Disable i3bar
|
||||
colors =
|
||||
let
|
||||
background = config.theme.colors.base00;
|
||||
inactiveBackground = config.theme.colors.base01;
|
||||
border = config.theme.colors.base01;
|
||||
inactiveBorder = config.theme.colors.base01;
|
||||
text = config.theme.colors.base07;
|
||||
inactiveText = config.theme.colors.base04;
|
||||
urgentBackground = config.theme.colors.base08;
|
||||
indicator = "#00000000";
|
||||
in
|
||||
{
|
||||
background = config.theme.colors.base00;
|
||||
focused = {
|
||||
inherit
|
||||
background
|
||||
indicator
|
||||
text
|
||||
border
|
||||
;
|
||||
childBorder = background;
|
||||
};
|
||||
focusedInactive = {
|
||||
inherit indicator;
|
||||
background = inactiveBackground;
|
||||
border = inactiveBorder;
|
||||
childBorder = inactiveBackground;
|
||||
text = inactiveText;
|
||||
};
|
||||
# placeholder = { };
|
||||
unfocused = {
|
||||
inherit indicator;
|
||||
background = inactiveBackground;
|
||||
border = inactiveBorder;
|
||||
childBorder = inactiveBackground;
|
||||
text = inactiveText;
|
||||
};
|
||||
urgent = {
|
||||
inherit text indicator;
|
||||
background = urgentBackground;
|
||||
border = urgentBackground;
|
||||
childBorder = urgentBackground;
|
||||
};
|
||||
};
|
||||
floating.modifier = modifier;
|
||||
focus = {
|
||||
mouseWarping = true;
|
||||
newWindow = "urgent";
|
||||
followMouse = false;
|
||||
};
|
||||
keybindings = {
|
||||
|
||||
# Adjust screen brightness
|
||||
"Shift+F12" =
|
||||
# Disable dynamic sleep
|
||||
# https://github.com/rockowitz/ddcutil/issues/323
|
||||
"exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 + 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --disable-dynamic-sleep --display 2 setvcp 10 + 30";
|
||||
"Shift+F11" = "exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 - 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --disable-dynamic-sleep --display 2 setvcp 10 - 30";
|
||||
"XF86MonBrightnessUp" = "exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 + 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --disable-dynamic-sleep --display 2 setvcp 10 + 30";
|
||||
"XF86MonBrightnessDown" = "exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 - 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --disable-dynamic-sleep --display 2 setvcp 10 - 30";
|
||||
|
||||
# Media player controls
|
||||
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
|
||||
"XF86AudioStop" = "exec ${pkgs.playerctl}/bin/playerctl stop";
|
||||
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
|
||||
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
|
||||
|
||||
# Launchers
|
||||
"${modifier}+Return" = "exec --no-startup-id kitty; workspace ${ws2}; layout tabbed";
|
||||
"${modifier}+space" = "exec --no-startup-id ${config.launcherCommand}";
|
||||
"${modifier}+Shift+s" = "exec --no-startup-id ${config.systemdSearch}";
|
||||
"${modifier}+Shift+a" = "exec --no-startup-id ${config.audioSwitchCommand}";
|
||||
"Mod1+Tab" = "exec --no-startup-id ${config.altTabCommand}";
|
||||
"${modifier}+Shift+period" = "exec --no-startup-id ${config.powerCommand}";
|
||||
"${modifier}+Shift+m" = "exec --no-startup-id ${config.brightnessCommand}";
|
||||
"${modifier}+c" = "exec --no-startup-id ${config.calculatorCommand}";
|
||||
"${modifier}+Shift+c" = "reload";
|
||||
"${modifier}+Shift+r" = "restart";
|
||||
"${modifier}+Shift+q" = ''exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"'';
|
||||
"${modifier}+Shift+x" = "exec ${lockCmd}";
|
||||
"${modifier}+Mod1+h" = "exec --no-startup-id kitty sh -c '${pkgs.home-manager}/bin/home-manager switch --flake ${config.dotfilesPath}#${config.networking.hostName} || read'";
|
||||
"${modifier}+Mod1+r" = "exec --no-startup-id kitty sh -c 'doas nixos-rebuild switch --flake ${config.dotfilesPath}#${config.networking.hostName} || read'";
|
||||
|
||||
# Window options
|
||||
"${modifier}+q" = "kill";
|
||||
"${modifier}+b" = "exec ${config.toggleBarCommand}";
|
||||
"${modifier}+f" = "fullscreen toggle";
|
||||
"${modifier}+h" = "focus left";
|
||||
"${modifier}+j" = "focus down";
|
||||
"${modifier}+k" = "focus up";
|
||||
"${modifier}+l" = "focus right";
|
||||
"${modifier}+Left" = "focus left";
|
||||
"${modifier}+Down" = "focus down";
|
||||
"${modifier}+Up" = "focus up";
|
||||
"${modifier}+Right" = "focus right";
|
||||
"${modifier}+Shift+h" = "move left";
|
||||
"${modifier}+Shift+j" = "move down";
|
||||
"${modifier}+Shift+k" = "move up";
|
||||
"${modifier}+Shift+l" = "move right";
|
||||
"${modifier}+Shift+Left" = "move left";
|
||||
"${modifier}+Shift+Down" = "move down";
|
||||
"${modifier}+Shift+Up" = "move up";
|
||||
"${modifier}+Shift+Right" = "move right";
|
||||
|
||||
# Tiling
|
||||
"${modifier}+i" = "split h";
|
||||
"${modifier}+v" = "split v";
|
||||
"${modifier}+s" = "layout stacking";
|
||||
"${modifier}+t" = "layout tabbed";
|
||||
"${modifier}+e" = "layout toggle split";
|
||||
"${modifier}+Shift+space" = "floating toggle";
|
||||
"${modifier}+Control+space" = "focus mode_toggle";
|
||||
"${modifier}+a" = "focus parent";
|
||||
|
||||
# Workspaces
|
||||
"${modifier}+1" = "workspace ${ws1}";
|
||||
"${modifier}+2" = "workspace ${ws2}";
|
||||
"${modifier}+3" = "workspace ${ws3}";
|
||||
"${modifier}+4" = "workspace ${ws4}";
|
||||
"${modifier}+5" = "workspace ${ws5}";
|
||||
"${modifier}+6" = "workspace ${ws6}";
|
||||
"${modifier}+7" = "workspace ${ws7}";
|
||||
"${modifier}+8" = "workspace ${ws8}";
|
||||
"${modifier}+9" = "workspace ${ws9}";
|
||||
"${modifier}+0" = "workspace ${ws10}";
|
||||
|
||||
# Move windows
|
||||
"${modifier}+Shift+1" = "move container to workspace ${ws1}; workspace ${ws1}";
|
||||
"${modifier}+Shift+2" = "move container to workspace ${ws2}; workspace ${ws2}";
|
||||
"${modifier}+Shift+3" = "move container to workspace ${ws3}; workspace ${ws3}";
|
||||
"${modifier}+Shift+4" = "move container to workspace ${ws4}; workspace ${ws4}";
|
||||
"${modifier}+Shift+5" = "move container to workspace ${ws5}; workspace ${ws5}";
|
||||
"${modifier}+Shift+6" = "move container to workspace ${ws6}; workspace ${ws6}";
|
||||
"${modifier}+Shift+7" = "move container to workspace ${ws7}; workspace ${ws7}";
|
||||
"${modifier}+Shift+8" = "move container to workspace ${ws8}; workspace ${ws8}";
|
||||
"${modifier}+Shift+9" = "move container to workspace ${ws9}; workspace ${ws9}";
|
||||
"${modifier}+Shift+0" = "move container to workspace ${ws10}; workspace ${ws10}";
|
||||
|
||||
# Move screens
|
||||
"${modifier}+Control+l" = "move workspace to output right";
|
||||
"${modifier}+Control+h" = "move workspace to output left";
|
||||
|
||||
# Resizing
|
||||
"${modifier}+r" = ''mode "resize"'';
|
||||
"${modifier}+Control+Shift+h" = "resize shrink width 10 px or 10 ppt";
|
||||
"${modifier}+Control+Shift+j" = "resize grow height 10 px or 10 ppt";
|
||||
"${modifier}+Control+Shift+k" = "resize shrink height 10 px or 10 ppt";
|
||||
"${modifier}+Control+Shift+l" = "resize grow width 10 px or 10 ppt";
|
||||
};
|
||||
modes = { };
|
||||
startup = [
|
||||
{
|
||||
command = "feh --bg-fill ${config.wallpaper}";
|
||||
always = true;
|
||||
notification = false;
|
||||
}
|
||||
{
|
||||
command = "i3-msg workspace ${ws2}, move workspace to output right";
|
||||
notification = false;
|
||||
}
|
||||
{
|
||||
command = "i3-msg workspace ${ws1}, move workspace to output left";
|
||||
notification = false;
|
||||
}
|
||||
];
|
||||
"${ws3}" = [{ class = "discord"; }];
|
||||
"${ws4}" = [ { class = "steam"; } { class = "Steam"; } ];
|
||||
};
|
||||
bars = [{ command = "echo"; }]; # Disable i3bar
|
||||
colors = let
|
||||
background = config.theme.colors.base00;
|
||||
inactiveBackground = config.theme.colors.base01;
|
||||
border = config.theme.colors.base01;
|
||||
inactiveBorder = config.theme.colors.base01;
|
||||
text = config.theme.colors.base07;
|
||||
inactiveText = config.theme.colors.base04;
|
||||
urgentBackground = config.theme.colors.base08;
|
||||
indicator = "#00000000";
|
||||
in {
|
||||
background = config.theme.colors.base00;
|
||||
focused = {
|
||||
inherit background indicator text border;
|
||||
childBorder = background;
|
||||
};
|
||||
focusedInactive = {
|
||||
inherit indicator;
|
||||
background = inactiveBackground;
|
||||
border = inactiveBorder;
|
||||
childBorder = inactiveBackground;
|
||||
text = inactiveText;
|
||||
};
|
||||
# placeholder = { };
|
||||
unfocused = {
|
||||
inherit indicator;
|
||||
background = inactiveBackground;
|
||||
border = inactiveBorder;
|
||||
childBorder = inactiveBackground;
|
||||
text = inactiveText;
|
||||
};
|
||||
urgent = {
|
||||
inherit text indicator;
|
||||
background = urgentBackground;
|
||||
border = urgentBackground;
|
||||
childBorder = urgentBackground;
|
||||
window = {
|
||||
border = 0;
|
||||
hideEdgeBorders = "smart";
|
||||
titlebar = false;
|
||||
};
|
||||
workspaceAutoBackAndForth = false;
|
||||
workspaceOutputAssign = [ ];
|
||||
# gaps = {
|
||||
# bottom = 8;
|
||||
# top = 8;
|
||||
# left = 8;
|
||||
# right = 8;
|
||||
# horizontal = 15;
|
||||
# vertical = 15;
|
||||
# inner = 15;
|
||||
# outer = 0;
|
||||
# smartBorders = "off";
|
||||
# smartGaps = false;
|
||||
# };
|
||||
};
|
||||
floating.modifier = modifier;
|
||||
focus = {
|
||||
mouseWarping = true;
|
||||
newWindow = "urgent";
|
||||
followMouse = false;
|
||||
};
|
||||
keybindings = {
|
||||
|
||||
# Adjust screen brightness
|
||||
"Shift+F12" =
|
||||
# Disable dynamic sleep
|
||||
# https://github.com/rockowitz/ddcutil/issues/323
|
||||
"exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 + 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --disable-dynamic-sleep --display 2 setvcp 10 + 30";
|
||||
"Shift+F11" =
|
||||
"exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 - 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --disable-dynamic-sleep --display 2 setvcp 10 - 30";
|
||||
"XF86MonBrightnessUp" =
|
||||
"exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 + 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --disable-dynamic-sleep --display 2 setvcp 10 + 30";
|
||||
"XF86MonBrightnessDown" =
|
||||
"exec ${pkgs.ddcutil}/bin/ddcutil --display 1 setvcp 10 - 30 && sleep 1; exec ${pkgs.ddcutil}/bin/ddcutil --disable-dynamic-sleep --display 2 setvcp 10 - 30";
|
||||
|
||||
# Media player controls
|
||||
"XF86AudioPlay" = "exec ${pkgs.playerctl}/bin/playerctl play-pause";
|
||||
"XF86AudioStop" = "exec ${pkgs.playerctl}/bin/playerctl stop";
|
||||
"XF86AudioNext" = "exec ${pkgs.playerctl}/bin/playerctl next";
|
||||
"XF86AudioPrev" = "exec ${pkgs.playerctl}/bin/playerctl previous";
|
||||
|
||||
# Launchers
|
||||
"${modifier}+Return" =
|
||||
"exec --no-startup-id kitty; workspace ${ws2}; layout tabbed";
|
||||
"${modifier}+space" =
|
||||
"exec --no-startup-id ${config.launcherCommand}";
|
||||
"${modifier}+Shift+s" =
|
||||
"exec --no-startup-id ${config.systemdSearch}";
|
||||
"${modifier}+Shift+a" =
|
||||
"exec --no-startup-id ${config.audioSwitchCommand}";
|
||||
"Mod1+Tab" = "exec --no-startup-id ${config.altTabCommand}";
|
||||
"${modifier}+Shift+period" =
|
||||
"exec --no-startup-id ${config.powerCommand}";
|
||||
"${modifier}+Shift+m" =
|
||||
"exec --no-startup-id ${config.brightnessCommand}";
|
||||
"${modifier}+c" =
|
||||
"exec --no-startup-id ${config.calculatorCommand}";
|
||||
"${modifier}+Shift+c" = "reload";
|
||||
"${modifier}+Shift+r" = "restart";
|
||||
"${modifier}+Shift+q" = ''
|
||||
exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -B 'Yes, exit i3' 'i3-msg exit'"'';
|
||||
"${modifier}+Shift+x" = "exec ${lockCmd}";
|
||||
"${modifier}+Mod1+h" =
|
||||
"exec --no-startup-id kitty sh -c '${pkgs.home-manager}/bin/home-manager switch --flake ${config.dotfilesPath}#${config.networking.hostName} || read'";
|
||||
"${modifier}+Mod1+r" =
|
||||
"exec --no-startup-id kitty sh -c 'doas nixos-rebuild switch --flake ${config.dotfilesPath}#${config.networking.hostName} || read'";
|
||||
|
||||
# Window options
|
||||
"${modifier}+q" = "kill";
|
||||
"${modifier}+b" = "exec ${config.toggleBarCommand}";
|
||||
"${modifier}+f" = "fullscreen toggle";
|
||||
"${modifier}+h" = "focus left";
|
||||
"${modifier}+j" = "focus down";
|
||||
"${modifier}+k" = "focus up";
|
||||
"${modifier}+l" = "focus right";
|
||||
"${modifier}+Left" = "focus left";
|
||||
"${modifier}+Down" = "focus down";
|
||||
"${modifier}+Up" = "focus up";
|
||||
"${modifier}+Right" = "focus right";
|
||||
"${modifier}+Shift+h" = "move left";
|
||||
"${modifier}+Shift+j" = "move down";
|
||||
"${modifier}+Shift+k" = "move up";
|
||||
"${modifier}+Shift+l" = "move right";
|
||||
"${modifier}+Shift+Left" = "move left";
|
||||
"${modifier}+Shift+Down" = "move down";
|
||||
"${modifier}+Shift+Up" = "move up";
|
||||
"${modifier}+Shift+Right" = "move right";
|
||||
|
||||
# Tiling
|
||||
"${modifier}+i" = "split h";
|
||||
"${modifier}+v" = "split v";
|
||||
"${modifier}+s" = "layout stacking";
|
||||
"${modifier}+t" = "layout tabbed";
|
||||
"${modifier}+e" = "layout toggle split";
|
||||
"${modifier}+Shift+space" = "floating toggle";
|
||||
"${modifier}+Control+space" = "focus mode_toggle";
|
||||
"${modifier}+a" = "focus parent";
|
||||
|
||||
# Workspaces
|
||||
"${modifier}+1" = "workspace ${ws1}";
|
||||
"${modifier}+2" = "workspace ${ws2}";
|
||||
"${modifier}+3" = "workspace ${ws3}";
|
||||
"${modifier}+4" = "workspace ${ws4}";
|
||||
"${modifier}+5" = "workspace ${ws5}";
|
||||
"${modifier}+6" = "workspace ${ws6}";
|
||||
"${modifier}+7" = "workspace ${ws7}";
|
||||
"${modifier}+8" = "workspace ${ws8}";
|
||||
"${modifier}+9" = "workspace ${ws9}";
|
||||
"${modifier}+0" = "workspace ${ws10}";
|
||||
|
||||
# Move windows
|
||||
"${modifier}+Shift+1" =
|
||||
"move container to workspace ${ws1}; workspace ${ws1}";
|
||||
"${modifier}+Shift+2" =
|
||||
"move container to workspace ${ws2}; workspace ${ws2}";
|
||||
"${modifier}+Shift+3" =
|
||||
"move container to workspace ${ws3}; workspace ${ws3}";
|
||||
"${modifier}+Shift+4" =
|
||||
"move container to workspace ${ws4}; workspace ${ws4}";
|
||||
"${modifier}+Shift+5" =
|
||||
"move container to workspace ${ws5}; workspace ${ws5}";
|
||||
"${modifier}+Shift+6" =
|
||||
"move container to workspace ${ws6}; workspace ${ws6}";
|
||||
"${modifier}+Shift+7" =
|
||||
"move container to workspace ${ws7}; workspace ${ws7}";
|
||||
"${modifier}+Shift+8" =
|
||||
"move container to workspace ${ws8}; workspace ${ws8}";
|
||||
"${modifier}+Shift+9" =
|
||||
"move container to workspace ${ws9}; workspace ${ws9}";
|
||||
"${modifier}+Shift+0" =
|
||||
"move container to workspace ${ws10}; workspace ${ws10}";
|
||||
|
||||
# Move screens
|
||||
"${modifier}+Control+l" = "move workspace to output right";
|
||||
"${modifier}+Control+h" = "move workspace to output left";
|
||||
|
||||
# Resizing
|
||||
"${modifier}+r" = ''mode "resize"'';
|
||||
"${modifier}+Control+Shift+h" =
|
||||
"resize shrink width 10 px or 10 ppt";
|
||||
"${modifier}+Control+Shift+j" =
|
||||
"resize grow height 10 px or 10 ppt";
|
||||
"${modifier}+Control+Shift+k" =
|
||||
"resize shrink height 10 px or 10 ppt";
|
||||
"${modifier}+Control+Shift+l" = "resize grow width 10 px or 10 ppt";
|
||||
};
|
||||
modes = { };
|
||||
startup = [
|
||||
{
|
||||
command = "feh --bg-fill ${config.wallpaper}";
|
||||
always = true;
|
||||
notification = false;
|
||||
}
|
||||
{
|
||||
command =
|
||||
"i3-msg workspace ${ws2}, move workspace to output right";
|
||||
notification = false;
|
||||
}
|
||||
{
|
||||
command =
|
||||
"i3-msg workspace ${ws1}, move workspace to output left";
|
||||
notification = false;
|
||||
}
|
||||
];
|
||||
window = {
|
||||
border = 0;
|
||||
hideEdgeBorders = "smart";
|
||||
titlebar = false;
|
||||
};
|
||||
workspaceAutoBackAndForth = false;
|
||||
workspaceOutputAssign = [ ];
|
||||
# gaps = {
|
||||
# bottom = 8;
|
||||
# top = 8;
|
||||
# left = 8;
|
||||
# right = 8;
|
||||
# horizontal = 15;
|
||||
# vertical = 15;
|
||||
# inner = 15;
|
||||
# outer = 0;
|
||||
# smartBorders = "off";
|
||||
# smartGaps = false;
|
||||
# };
|
||||
};
|
||||
extraConfig = "";
|
||||
};
|
||||
|
||||
@ -266,22 +255,26 @@ in {
|
||||
|
||||
# Update lock screen cache only if cache is empty
|
||||
home.activation.updateLockScreenCache =
|
||||
let cacheDir = "${config.homePath}/.cache/betterlockscreen/current";
|
||||
in lib.mkIf config.services.xserver.enable
|
||||
(config.home-manager.users.${config.user}.lib.dag.entryAfter
|
||||
[ "writeBoundary" ] ''
|
||||
let
|
||||
cacheDir = "${config.homePath}/.cache/betterlockscreen/current";
|
||||
in
|
||||
lib.mkIf config.services.xserver.enable (
|
||||
config.home-manager.users.${config.user}.lib.dag.entryAfter [ "writeBoundary" ] ''
|
||||
if [ ! -d ${cacheDir} ] || [ -z "$(ls ${cacheDir})" ]; then
|
||||
$DRY_RUN_CMD ${lockUpdate}
|
||||
fi
|
||||
'');
|
||||
|
||||
''
|
||||
);
|
||||
};
|
||||
|
||||
# Ref: https://github.com/betterlockscreen/betterlockscreen/blob/next/system/betterlockscreen%40.service
|
||||
systemd.services.lock = {
|
||||
enable = config.services.xserver.enable;
|
||||
description = "Lock the screen on resume from suspend";
|
||||
before = [ "sleep.target" "suspend.target" ];
|
||||
before = [
|
||||
"sleep.target"
|
||||
"suspend.target"
|
||||
];
|
||||
serviceConfig = {
|
||||
User = config.user;
|
||||
Type = "simple";
|
||||
@ -290,9 +283,10 @@ in {
|
||||
ExecStart = lockCmd;
|
||||
ExecStartPost = "${pkgs.coreutils-full}/bin/sleep 1";
|
||||
};
|
||||
wantedBy = [ "sleep.target" "suspend.target" ];
|
||||
wantedBy = [
|
||||
"sleep.target"
|
||||
"suspend.target"
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.services.xserver.enable) {
|
||||
home-manager.users.${config.user} = {
|
||||
@ -31,18 +37,21 @@
|
||||
];
|
||||
shadow = false;
|
||||
shadowExclude = [ ];
|
||||
shadowOffsets = [ (-10) (-10) ];
|
||||
shadowOffsets = [
|
||||
(-10)
|
||||
(-10)
|
||||
];
|
||||
shadowOpacity = 0.5;
|
||||
vSync = true;
|
||||
};
|
||||
|
||||
xsession.windowManager.i3.config.startup = [{
|
||||
command = "systemctl --user restart picom";
|
||||
always = true;
|
||||
notification = false;
|
||||
}];
|
||||
|
||||
xsession.windowManager.i3.config.startup = [
|
||||
{
|
||||
command = "systemctl --user restart picom";
|
||||
always = true;
|
||||
notification = false;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.services.xserver.enable) {
|
||||
|
||||
@ -46,33 +52,36 @@
|
||||
# wm-restack = "i3";
|
||||
# override-redirect = true;
|
||||
};
|
||||
"module/i3" = let padding = 2;
|
||||
in {
|
||||
type = "internal/i3";
|
||||
pin-workspaces = false;
|
||||
show-urgent = true;
|
||||
strip-wsnumbers = true;
|
||||
index-sort = true;
|
||||
enable-click = true;
|
||||
wrapping-scroll = true;
|
||||
fuzzy-match = true;
|
||||
format = "<label-state> <label-mode>";
|
||||
label-focused = "%name%";
|
||||
label-focused-foreground = config.theme.colors.base01;
|
||||
label-focused-background = config.theme.colors.base05;
|
||||
label-focused-underline = config.theme.colors.base03;
|
||||
label-focused-padding = padding;
|
||||
label-unfocused = "%name%";
|
||||
label-unfocused-padding = padding;
|
||||
label-visible = "%name%";
|
||||
label-visible-underline = config.theme.colors.base01;
|
||||
label-visible-padding = padding;
|
||||
label-urgent = "%name%";
|
||||
label-urgent-foreground = config.theme.colors.base00;
|
||||
label-urgent-background = config.theme.colors.base08;
|
||||
label-urgent-underline = config.theme.colors.base0F;
|
||||
label-urgent-padding = padding;
|
||||
};
|
||||
"module/i3" =
|
||||
let
|
||||
padding = 2;
|
||||
in
|
||||
{
|
||||
type = "internal/i3";
|
||||
pin-workspaces = false;
|
||||
show-urgent = true;
|
||||
strip-wsnumbers = true;
|
||||
index-sort = true;
|
||||
enable-click = true;
|
||||
wrapping-scroll = true;
|
||||
fuzzy-match = true;
|
||||
format = "<label-state> <label-mode>";
|
||||
label-focused = "%name%";
|
||||
label-focused-foreground = config.theme.colors.base01;
|
||||
label-focused-background = config.theme.colors.base05;
|
||||
label-focused-underline = config.theme.colors.base03;
|
||||
label-focused-padding = padding;
|
||||
label-unfocused = "%name%";
|
||||
label-unfocused-padding = padding;
|
||||
label-visible = "%name%";
|
||||
label-visible-underline = config.theme.colors.base01;
|
||||
label-visible-padding = padding;
|
||||
label-urgent = "%name%";
|
||||
label-urgent-foreground = config.theme.colors.base00;
|
||||
label-urgent-background = config.theme.colors.base08;
|
||||
label-urgent-underline = config.theme.colors.base0F;
|
||||
label-urgent-padding = padding;
|
||||
};
|
||||
"module/xworkspaces" = {
|
||||
type = "internal/xworkspaces";
|
||||
label-active = "%name%";
|
||||
@ -105,24 +114,24 @@
|
||||
type = "custom/script";
|
||||
interval = 10;
|
||||
format = "<label>";
|
||||
exec = builtins.toString (pkgs.writeShellScript "mailcount.sh" ''
|
||||
${pkgs.notmuch}/bin/notmuch new --quiet 2>&1>/dev/null
|
||||
UNREAD=$(
|
||||
${pkgs.notmuch}/bin/notmuch count \
|
||||
is:inbox and \
|
||||
is:unread and \
|
||||
folder:main/Inbox \
|
||||
2>/dev/null
|
||||
)
|
||||
if [ $UNREAD = "0" ]; then
|
||||
echo ""
|
||||
else
|
||||
echo "%{T2}%{T-} $UNREAD "
|
||||
fi
|
||||
'');
|
||||
click-left =
|
||||
"i3-msg 'exec --no-startup-id kitty --class aerc aerc'; sleep 0.15; i3-msg '[class=aerc] focus'";
|
||||
|
||||
exec = builtins.toString (
|
||||
pkgs.writeShellScript "mailcount.sh" ''
|
||||
${pkgs.notmuch}/bin/notmuch new --quiet 2>&1>/dev/null
|
||||
UNREAD=$(
|
||||
${pkgs.notmuch}/bin/notmuch count \
|
||||
is:inbox and \
|
||||
is:unread and \
|
||||
folder:main/Inbox \
|
||||
2>/dev/null
|
||||
)
|
||||
if [ $UNREAD = "0" ]; then
|
||||
echo ""
|
||||
else
|
||||
echo "%{T2}%{T-} $UNREAD "
|
||||
fi
|
||||
''
|
||||
);
|
||||
click-left = "i3-msg 'exec --no-startup-id kitty --class aerc aerc'; sleep 0.15; i3-msg '[class=aerc] focus'";
|
||||
};
|
||||
"module/network" = {
|
||||
type = "internal/network";
|
||||
@ -220,13 +229,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
xsession.windowManager.i3.config.startup = [{
|
||||
command = "pkill polybar; polybar -r main";
|
||||
always = true;
|
||||
notification = false;
|
||||
}];
|
||||
|
||||
xsession.windowManager.i3.config.startup = [
|
||||
{
|
||||
command = "pkill polybar; polybar -r main";
|
||||
always = true;
|
||||
notification = false;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,148 +1,167 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
rofi = config.home-manager.users.${config.user}.programs.rofi.finalPackage;
|
||||
in
|
||||
{
|
||||
|
||||
in {
|
||||
|
||||
imports = [ ./rofi/power.nix ./rofi/brightness.nix ];
|
||||
imports = [
|
||||
./rofi/power.nix
|
||||
./rofi/brightness.nix
|
||||
];
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.services.xserver.enable) {
|
||||
|
||||
# Set the Rofi-Systemd terminal for viewing logs
|
||||
environment.sessionVariables.ROFI_SYSTEMD_TERM =
|
||||
lib.mkIf config.kitty.enable "${pkgs.kitty}/bin/kitty";
|
||||
environment.sessionVariables.ROFI_SYSTEMD_TERM = lib.mkIf config.kitty.enable "${pkgs.kitty}/bin/kitty";
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
home.packages = with pkgs;
|
||||
[
|
||||
jq # Required for rofi-systemd
|
||||
];
|
||||
home.packages = with pkgs; [
|
||||
jq # Required for rofi-systemd
|
||||
];
|
||||
|
||||
programs.rofi = {
|
||||
enable = true;
|
||||
cycle = true;
|
||||
location = "center";
|
||||
pass = { };
|
||||
plugins = [ pkgs.rofi-calc pkgs.rofi-emoji pkgs.rofi-systemd ];
|
||||
theme = let
|
||||
inherit (config.home-manager.users.${config.user}.lib.formats.rasi)
|
||||
mkLiteral;
|
||||
in {
|
||||
plugins = [
|
||||
pkgs.rofi-calc
|
||||
pkgs.rofi-emoji
|
||||
pkgs.rofi-systemd
|
||||
];
|
||||
theme =
|
||||
let
|
||||
inherit (config.home-manager.users.${config.user}.lib.formats.rasi) mkLiteral;
|
||||
in
|
||||
{
|
||||
|
||||
# Inspired by https://github.com/sherubthakur/dotfiles/blob/master/users/modules/desktop-environment/rofi/launcher.rasi
|
||||
# Inspired by https://github.com/sherubthakur/dotfiles/blob/master/users/modules/desktop-environment/rofi/launcher.rasi
|
||||
|
||||
"*" = {
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
foreground-color = mkLiteral config.theme.colors.base07;
|
||||
text-color = mkLiteral config.theme.colors.base07;
|
||||
border-color = mkLiteral config.theme.colors.base04;
|
||||
};
|
||||
"*" = {
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
foreground-color = mkLiteral config.theme.colors.base07;
|
||||
text-color = mkLiteral config.theme.colors.base07;
|
||||
border-color = mkLiteral config.theme.colors.base04;
|
||||
};
|
||||
|
||||
# Holds the entire window
|
||||
"#window" = {
|
||||
transparency = "real";
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
text-color = mkLiteral config.theme.colors.base07;
|
||||
border = mkLiteral "4px";
|
||||
border-color = mkLiteral config.theme.colors.base04;
|
||||
border-radius = mkLiteral "4px";
|
||||
width = mkLiteral "850px";
|
||||
padding = mkLiteral "15px";
|
||||
};
|
||||
# Holds the entire window
|
||||
"#window" = {
|
||||
transparency = "real";
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
text-color = mkLiteral config.theme.colors.base07;
|
||||
border = mkLiteral "4px";
|
||||
border-color = mkLiteral config.theme.colors.base04;
|
||||
border-radius = mkLiteral "4px";
|
||||
width = mkLiteral "850px";
|
||||
padding = mkLiteral "15px";
|
||||
};
|
||||
|
||||
# Wrapper around bar and results
|
||||
"#mainbox" = {
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
border = mkLiteral "0px";
|
||||
border-radius = mkLiteral "0px";
|
||||
border-color = mkLiteral config.theme.colors.base04;
|
||||
children = map mkLiteral [ "inputbar" "message" "listview" ];
|
||||
spacing = mkLiteral "10px";
|
||||
padding = mkLiteral "10px";
|
||||
};
|
||||
# Wrapper around bar and results
|
||||
"#mainbox" = {
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
border = mkLiteral "0px";
|
||||
border-radius = mkLiteral "0px";
|
||||
border-color = mkLiteral config.theme.colors.base04;
|
||||
children = map mkLiteral [
|
||||
"inputbar"
|
||||
"message"
|
||||
"listview"
|
||||
];
|
||||
spacing = mkLiteral "10px";
|
||||
padding = mkLiteral "10px";
|
||||
};
|
||||
|
||||
# Unknown
|
||||
"#textbox-prompt-colon" = {
|
||||
expand = false;
|
||||
str = ":";
|
||||
margin = mkLiteral "0px 0.3em 0em 0em";
|
||||
text-color = mkLiteral config.theme.colors.base07;
|
||||
};
|
||||
# Unknown
|
||||
"#textbox-prompt-colon" = {
|
||||
expand = false;
|
||||
str = ":";
|
||||
margin = mkLiteral "0px 0.3em 0em 0em";
|
||||
text-color = mkLiteral config.theme.colors.base07;
|
||||
};
|
||||
|
||||
# Command prompt left of the input
|
||||
"#prompt" = { enabled = false; };
|
||||
# Command prompt left of the input
|
||||
"#prompt" = {
|
||||
enabled = false;
|
||||
};
|
||||
|
||||
# Actual text box
|
||||
"#entry" = {
|
||||
placeholder-color = mkLiteral config.theme.colors.base03;
|
||||
expand = true;
|
||||
horizontal-align = "0";
|
||||
placeholder = "";
|
||||
padding = mkLiteral "0px 0px 0px 5px";
|
||||
blink = true;
|
||||
};
|
||||
# Actual text box
|
||||
"#entry" = {
|
||||
placeholder-color = mkLiteral config.theme.colors.base03;
|
||||
expand = true;
|
||||
horizontal-align = "0";
|
||||
placeholder = "";
|
||||
padding = mkLiteral "0px 0px 0px 5px";
|
||||
blink = true;
|
||||
};
|
||||
|
||||
# Top bar
|
||||
"#inputbar" = {
|
||||
children = map mkLiteral [ "prompt" "entry" ];
|
||||
border = mkLiteral "1px";
|
||||
border-radius = mkLiteral "4px";
|
||||
padding = mkLiteral "6px";
|
||||
};
|
||||
# Top bar
|
||||
"#inputbar" = {
|
||||
children = map mkLiteral [
|
||||
"prompt"
|
||||
"entry"
|
||||
];
|
||||
border = mkLiteral "1px";
|
||||
border-radius = mkLiteral "4px";
|
||||
padding = mkLiteral "6px";
|
||||
};
|
||||
|
||||
# Results
|
||||
"#listview" = {
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
padding = mkLiteral "0px";
|
||||
columns = 1;
|
||||
lines = 12;
|
||||
spacing = "5px";
|
||||
cycle = true;
|
||||
dynamic = true;
|
||||
layout = "vertical";
|
||||
};
|
||||
# Results
|
||||
"#listview" = {
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
padding = mkLiteral "0px";
|
||||
columns = 1;
|
||||
lines = 12;
|
||||
spacing = "5px";
|
||||
cycle = true;
|
||||
dynamic = true;
|
||||
layout = "vertical";
|
||||
};
|
||||
|
||||
# Each result
|
||||
"#element" = {
|
||||
orientation = "vertical";
|
||||
border-radius = mkLiteral "0px";
|
||||
padding = mkLiteral "5px 0px 5px 5px";
|
||||
};
|
||||
"#element.selected" = {
|
||||
border = mkLiteral "1px";
|
||||
border-radius = mkLiteral "4px";
|
||||
border-color = mkLiteral config.theme.colors.base07;
|
||||
background-color = mkLiteral config.theme.colors.base04;
|
||||
text-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
# Each result
|
||||
"#element" = {
|
||||
orientation = "vertical";
|
||||
border-radius = mkLiteral "0px";
|
||||
padding = mkLiteral "5px 0px 5px 5px";
|
||||
};
|
||||
"#element.selected" = {
|
||||
border = mkLiteral "1px";
|
||||
border-radius = mkLiteral "4px";
|
||||
border-color = mkLiteral config.theme.colors.base07;
|
||||
background-color = mkLiteral config.theme.colors.base04;
|
||||
text-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
|
||||
"#element-text" = {
|
||||
expand = true;
|
||||
# horizontal-align = mkLiteral "0.5";
|
||||
vertical-align = mkLiteral "0.5";
|
||||
margin = mkLiteral "0px 2.5px 0px 2.5px";
|
||||
};
|
||||
"#element-text.selected" = {
|
||||
background-color = mkLiteral config.theme.colors.base04;
|
||||
text-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
"#element-text" = {
|
||||
expand = true;
|
||||
# horizontal-align = mkLiteral "0.5";
|
||||
vertical-align = mkLiteral "0.5";
|
||||
margin = mkLiteral "0px 2.5px 0px 2.5px";
|
||||
};
|
||||
"#element-text.selected" = {
|
||||
background-color = mkLiteral config.theme.colors.base04;
|
||||
text-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
|
||||
# Not sure how to get icons
|
||||
"#element-icon" = {
|
||||
size = mkLiteral "18px";
|
||||
border = mkLiteral "0px";
|
||||
padding = mkLiteral "2px 5px 2px 2px";
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
# Not sure how to get icons
|
||||
"#element-icon" = {
|
||||
size = mkLiteral "18px";
|
||||
border = mkLiteral "0px";
|
||||
padding = mkLiteral "2px 5px 2px 2px";
|
||||
background-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
"#element-icon.selected" = {
|
||||
background-color = mkLiteral config.theme.colors.base04;
|
||||
text-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
};
|
||||
"#element-icon.selected" = {
|
||||
background-color = mkLiteral config.theme.colors.base04;
|
||||
text-color = mkLiteral config.theme.colors.base00;
|
||||
};
|
||||
|
||||
};
|
||||
xoffset = 0;
|
||||
yoffset = -20;
|
||||
extraConfig = {
|
||||
@ -158,21 +177,21 @@ in {
|
||||
recursive = true;
|
||||
source = ./rofi/themes;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
launcherCommand = ''
|
||||
${rofi}/bin/rofi -modes drun -show drun -theme-str '@import "launcher.rasi"' '';
|
||||
launcherCommand = ''${rofi}/bin/rofi -modes drun -show drun -theme-str '@import "launcher.rasi"' '';
|
||||
systemdSearch = "${pkgs.rofi-systemd}/bin/rofi-systemd";
|
||||
altTabCommand = "${rofi}/bin/rofi -show window -modi window";
|
||||
calculatorCommand = "${rofi}/bin/rofi -modes calc -show calc";
|
||||
audioSwitchCommand = "${
|
||||
(pkgs.writeShellApplication {
|
||||
name = "switch-audio";
|
||||
runtimeInputs = [ pkgs.ponymix rofi ];
|
||||
text = builtins.readFile ./rofi/pulse-sink.sh;
|
||||
})
|
||||
}/bin/switch-audio";
|
||||
(pkgs.writeShellApplication {
|
||||
name = "switch-audio";
|
||||
runtimeInputs = [
|
||||
pkgs.ponymix
|
||||
rofi
|
||||
];
|
||||
text = builtins.readFile ./rofi/pulse-sink.sh;
|
||||
})
|
||||
}/bin/switch-audio";
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -2,14 +2,14 @@
|
||||
let
|
||||
|
||||
rofi = config.home-manager.users.${config.user}.programs.rofi.finalPackage;
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
# Adapted from:
|
||||
# A rofi powered menu to execute brightness choices.
|
||||
|
||||
config.brightnessCommand = builtins.toString
|
||||
(pkgs.writeShellScript "brightness" ''
|
||||
config.brightnessCommand = builtins.toString (
|
||||
pkgs.writeShellScript "brightness" ''
|
||||
|
||||
dimmer=""
|
||||
medium=""
|
||||
@ -45,6 +45,6 @@ in {
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
|
||||
'');
|
||||
|
||||
''
|
||||
);
|
||||
}
|
||||
|
@ -2,62 +2,63 @@
|
||||
let
|
||||
|
||||
rofi = config.home-manager.users.${config.user}.programs.rofi.finalPackage;
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
# Adapted from:
|
||||
# https://gitlab.com/vahnrr/rofi-menus/-/blob/b1f0e8a676eda5552e27ef631b0d43e660b23b8e/scripts/rofi-power
|
||||
# A rofi powered menu to execute power related action.
|
||||
|
||||
config.powerCommand = builtins.toString (pkgs.writeShellScript "powermenu" ''
|
||||
power_off=''
|
||||
reboot=''
|
||||
lock=''
|
||||
suspend=''
|
||||
log_out=''
|
||||
config.powerCommand = builtins.toString (
|
||||
pkgs.writeShellScript "powermenu" ''
|
||||
power_off=''
|
||||
reboot=''
|
||||
lock=''
|
||||
suspend=''
|
||||
log_out=''
|
||||
|
||||
chosen=$(printf '%s;%s;%s;%s;%s\n' \
|
||||
"$power_off" \
|
||||
"$reboot" \
|
||||
"$lock" \
|
||||
"$suspend" \
|
||||
"$log_out" \
|
||||
| ${rofi}/bin/rofi \
|
||||
-theme-str '@import "power.rasi"' \
|
||||
-hover-select \
|
||||
-me-select-entry "" \
|
||||
-me-accept-entry MousePrimary \
|
||||
-dmenu \
|
||||
-sep ';' \
|
||||
-selected-row 2)
|
||||
chosen=$(printf '%s;%s;%s;%s;%s\n' \
|
||||
"$power_off" \
|
||||
"$reboot" \
|
||||
"$lock" \
|
||||
"$suspend" \
|
||||
"$log_out" \
|
||||
| ${rofi}/bin/rofi \
|
||||
-theme-str '@import "power.rasi"' \
|
||||
-hover-select \
|
||||
-me-select-entry "" \
|
||||
-me-accept-entry MousePrimary \
|
||||
-dmenu \
|
||||
-sep ';' \
|
||||
-selected-row 2)
|
||||
|
||||
confirm () {
|
||||
${builtins.readFile ./rofi-prompt.sh}
|
||||
}
|
||||
confirm () {
|
||||
${builtins.readFile ./rofi-prompt.sh}
|
||||
}
|
||||
|
||||
case "$chosen" in
|
||||
"$power_off")
|
||||
confirm 'Shutdown?' && doas shutdown now
|
||||
;;
|
||||
case "$chosen" in
|
||||
"$power_off")
|
||||
confirm 'Shutdown?' && doas shutdown now
|
||||
;;
|
||||
|
||||
"$reboot")
|
||||
confirm 'Reboot?' && doas reboot
|
||||
;;
|
||||
"$reboot")
|
||||
confirm 'Reboot?' && doas reboot
|
||||
;;
|
||||
|
||||
"$lock")
|
||||
${pkgs.betterlockscreen}/bin/betterlockscreen --lock --display 1 --blur 0.5 --span
|
||||
;;
|
||||
"$lock")
|
||||
${pkgs.betterlockscreen}/bin/betterlockscreen --lock --display 1 --blur 0.5 --span
|
||||
;;
|
||||
|
||||
"$suspend")
|
||||
systemctl suspend
|
||||
;;
|
||||
"$suspend")
|
||||
systemctl suspend
|
||||
;;
|
||||
|
||||
"$log_out")
|
||||
confirm 'Logout?' && i3-msg exit
|
||||
;;
|
||||
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
'');
|
||||
"$log_out")
|
||||
confirm 'Logout?' && i3-msg exit
|
||||
;;
|
||||
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
''
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.gui.enable {
|
||||
|
||||
@ -20,16 +26,13 @@
|
||||
extraSeatDefaults = ''
|
||||
greeter-hide-users = false
|
||||
'';
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
environment.systemPackages = with pkgs;
|
||||
[
|
||||
xclip # Clipboard
|
||||
];
|
||||
environment.systemPackages = with pkgs; [
|
||||
xclip # Clipboard
|
||||
];
|
||||
|
||||
home-manager.users.${config.user} = {
|
||||
|
||||
@ -37,9 +40,6 @@
|
||||
pbcopy = "xclip -selection clipboard -in";
|
||||
pbpaste = "xclip -selection clipboard -out";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
@ -27,8 +32,8 @@ let
|
||||
${pkgs.volnoti}/bin/volnoti-show $volume
|
||||
fi
|
||||
'';
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
config = lib.mkIf (pkgs.stdenv.isLinux && config.gui.enable) {
|
||||
sound.enable = true;
|
||||
@ -57,28 +62,23 @@ in {
|
||||
|
||||
# Make sure that Volnoti actually starts (home-manager doesn't start
|
||||
# user daemon's automatically)
|
||||
startup = [{
|
||||
command =
|
||||
"systemctl --user restart volnoti --alpha 0.15 --radius 40 --timeout 0.2";
|
||||
always = true;
|
||||
notification = false;
|
||||
}];
|
||||
startup = [
|
||||
{
|
||||
command = "systemctl --user restart volnoti --alpha 0.15 --radius 40 --timeout 0.2";
|
||||
always = true;
|
||||
notification = false;
|
||||
}
|
||||
];
|
||||
|
||||
# i3 keybinds for changing the volume
|
||||
keybindings = {
|
||||
"XF86AudioRaiseVolume" =
|
||||
"exec --no-startup-id ${increaseVolume}/bin/increaseVolume";
|
||||
"XF86AudioLowerVolume" =
|
||||
"exec --no-startup-id ${decreaseVolume}/bin/decreaseVolume";
|
||||
"XF86AudioRaiseVolume" = "exec --no-startup-id ${increaseVolume}/bin/increaseVolume";
|
||||
"XF86AudioLowerVolume" = "exec --no-startup-id ${decreaseVolume}/bin/decreaseVolume";
|
||||
"XF86AudioMute" = "exec --no-startup-id ${toggleMute}/bin/toggleMute";
|
||||
# We can mute the mic by adding "--default-source"
|
||||
"XF86AudioMicMute" =
|
||||
"exec --no-startup-id ${pkgs.pamixer}/bin/pamixer --default-source --toggle-mute";
|
||||
"XF86AudioMicMute" = "exec --no-startup-id ${pkgs.pamixer}/bin/pamixer --default-source --toggle-mute";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
boot.loader = lib.mkIf (config.physical && !config.server) {
|
||||
grub = {
|
||||
@ -44,5 +50,4 @@
|
||||
|
||||
# Use latest released Linux kernel by default
|
||||
boot.kernelPackages = lib.mkDefault pkgs.linuxPackages_latest;
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{ lib, ... }: {
|
||||
{ lib, ... }:
|
||||
{
|
||||
|
||||
imports = [
|
||||
./audio.nix
|
||||
@ -18,5 +19,4 @@
|
||||
physical = lib.mkEnableOption "Whether this machine is a physical device.";
|
||||
server = lib.mkEnableOption "Whether this machine is a server.";
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,12 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
# Enable fstrim, which tracks free space on SSDs for garbage collection
|
||||
# More info: https://www.reddit.com/r/NixOS/comments/rbzhb1/if_you_have_a_ssd_dont_forget_to_enable_fstrim/
|
||||
services.fstrim.enable = true;
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
{ config, lib, modulesPath, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
modulesPath,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
# options.iso.enable = lib.mkEnableOption "Enable creating as an ISO.";
|
||||
@ -14,6 +19,4 @@
|
||||
# isoImage.makeUsbBootable = true;
|
||||
#
|
||||
# };
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.physical {
|
||||
|
||||
@ -9,7 +15,6 @@
|
||||
# Keyboard responsiveness
|
||||
autoRepeatDelay = 250;
|
||||
autoRepeatInterval = 40;
|
||||
|
||||
};
|
||||
|
||||
# Use capslock as escape and/or control
|
||||
@ -18,18 +23,19 @@
|
||||
keyboards = {
|
||||
default = {
|
||||
ids = [ "*" ];
|
||||
settings = { main = { capslock = "overload(control, esc)"; }; };
|
||||
settings = {
|
||||
main = {
|
||||
capslock = "overload(control, esc)";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# For some reason, keyd doesn't restart properly when updating
|
||||
system.activationScripts.keyd.text =
|
||||
"${pkgs.systemd}/bin/systemctl restart keyd.service";
|
||||
system.activationScripts.keyd.text = "${pkgs.systemd}/bin/systemctl restart keyd.service";
|
||||
|
||||
# Enable num lock on login
|
||||
home-manager.users.${config.user}.xsession.numlock.enable = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.gui.enable {
|
||||
|
||||
environment.systemPackages = with pkgs;
|
||||
[
|
||||
ddcutil # Monitor brightness control
|
||||
];
|
||||
environment.systemPackages = with pkgs; [
|
||||
ddcutil # Monitor brightness control
|
||||
];
|
||||
|
||||
# Reduce blue light at night
|
||||
services.redshift = {
|
||||
@ -44,7 +49,5 @@
|
||||
--output DVI-1 --off \
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.gui.enable {
|
||||
|
||||
@ -15,7 +21,5 @@
|
||||
accelProfile = "flat";
|
||||
accelSpeed = "1.15";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.physical {
|
||||
|
||||
@ -22,10 +28,7 @@
|
||||
};
|
||||
|
||||
environment.systemPackages = [
|
||||
(pkgs.writeShellScriptBin "wake-tempest"
|
||||
"${pkgs.wakeonlan}/bin/wakeonlan --ip=192.168.1.255 74:56:3C:40:37:5D")
|
||||
(pkgs.writeShellScriptBin "wake-tempest" "${pkgs.wakeonlan}/bin/wakeonlan --ip=192.168.1.255 74:56:3C:40:37:5D")
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
{ config, lib, ... }: {
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.server {
|
||||
|
||||
# Servers need a bootloader or they won't start
|
||||
boot.loader.systemd-boot.enable = true;
|
||||
boot.loader.efi.canTouchEfiVariables = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf (config.physical && !config.server) {
|
||||
|
||||
@ -22,7 +28,5 @@
|
||||
ACTION=="add", SUBSYSTEM=="usb", DRIVER=="usb", ATTR{power/wakeup}="disabled"
|
||||
ACTION=="add", SUBSYSTEM=="i2c", ATTR{power/wakeup}="disabled"
|
||||
'';
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf (config.physical && pkgs.stdenv.isLinux) {
|
||||
|
||||
@ -7,7 +13,5 @@
|
||||
|
||||
# Allows the user to control the WiFi settings.
|
||||
networking.wireless.userControlled.enable = true;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
{ config, lib, ... }: {
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
|
||||
options = { zfs.enable = lib.mkEnableOption "ZFS file system."; };
|
||||
options = {
|
||||
zfs.enable = lib.mkEnableOption "ZFS file system.";
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.server && config.zfs.enable) {
|
||||
|
||||
@ -8,14 +11,9 @@
|
||||
boot.kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
|
||||
boot.kernelParams = [ "nohibernate" ];
|
||||
boot.supportedFilesystems = [ "zfs" ];
|
||||
services.prometheus.exporters.zfs.enable =
|
||||
config.prometheus.exporters.enable;
|
||||
services.prometheus.exporters.zfs.enable = config.prometheus.exporters.enable;
|
||||
prometheus.scrapeTargets = [
|
||||
"127.0.0.1:${
|
||||
builtins.toString config.services.prometheus.exporters.zfs.port
|
||||
}"
|
||||
"127.0.0.1:${builtins.toString config.services.prometheus.exporters.zfs.port}"
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
@ -25,10 +30,12 @@ let
|
||||
apiKey = config.secrets.sabnzbdApiKey.dest;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
in {
|
||||
|
||||
options = { arrs.enable = lib.mkEnableOption "Arr services"; };
|
||||
options = {
|
||||
arrs.enable = lib.mkEnableOption "Arr services";
|
||||
};
|
||||
|
||||
config = lib.mkIf config.arrs.enable {
|
||||
|
||||
@ -74,76 +81,95 @@ in {
|
||||
# Group means that routes with the same name are mutually exclusive,
|
||||
# so they are split between the appropriate services.
|
||||
group = "download";
|
||||
match = [{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/sonarr*" ];
|
||||
}];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
# We're able to reference the url and port of the service dynamically
|
||||
upstreams = [{ dial = arrConfig.sonarr.url; }];
|
||||
}];
|
||||
match = [
|
||||
{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/sonarr*" ];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
# We're able to reference the url and port of the service dynamically
|
||||
upstreams = [ { dial = arrConfig.sonarr.url; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
group = "download";
|
||||
match = [{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/radarr*" ];
|
||||
}];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = arrConfig.radarr.url; }];
|
||||
}];
|
||||
match = [
|
||||
{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/radarr*" ];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = arrConfig.radarr.url; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
group = "download";
|
||||
match = [{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/prowlarr*" ];
|
||||
}];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
# Prowlarr doesn't offer a dynamic config, so we have to hardcode it
|
||||
upstreams = [{ dial = "localhost:9696"; }];
|
||||
}];
|
||||
match = [
|
||||
{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/prowlarr*" ];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
# Prowlarr doesn't offer a dynamic config, so we have to hardcode it
|
||||
upstreams = [ { dial = "localhost:9696"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
group = "download";
|
||||
match = [{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/bazarr*" ];
|
||||
}];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{
|
||||
# Bazarr only dynamically sets the port, not the host
|
||||
dial = "localhost:${
|
||||
builtins.toString config.services.bazarr.listenPort
|
||||
}";
|
||||
}];
|
||||
}];
|
||||
match = [
|
||||
{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/bazarr*" ];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [
|
||||
{
|
||||
# Bazarr only dynamically sets the port, not the host
|
||||
dial = "localhost:${builtins.toString config.services.bazarr.listenPort}";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
group = "download";
|
||||
match = [{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/sabnzbd*" ];
|
||||
}];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = arrConfig.sabnzbd.url; }];
|
||||
}];
|
||||
match = [
|
||||
{
|
||||
host = [ config.hostnames.download ];
|
||||
path = [ "/sabnzbd*" ];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = arrConfig.sabnzbd.url; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
group = "download";
|
||||
match = [{ host = [ config.hostnames.download ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{
|
||||
dial =
|
||||
"localhost:${builtins.toString config.services.jellyseerr.port}";
|
||||
}];
|
||||
}];
|
||||
match = [ { host = [ config.hostnames.download ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = "localhost:${builtins.toString config.services.jellyseerr.port}"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
@ -160,19 +186,17 @@ in {
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
DynamicUser = true;
|
||||
ExecStart = let
|
||||
# Sabnzbd doesn't accept the URI path, unlike the others
|
||||
url = if name != "sabnzbd" then
|
||||
"http://${attrs.url}/${name}"
|
||||
else
|
||||
"http://${attrs.url}";
|
||||
ExecStart =
|
||||
let
|
||||
# Sabnzbd doesn't accept the URI path, unlike the others
|
||||
url = if name != "sabnzbd" then "http://${attrs.url}/${name}" else "http://${attrs.url}";
|
||||
in
|
||||
# Exportarr is trained to pull from the arr services
|
||||
in ''
|
||||
${pkgs.exportarr}/bin/exportarr ${name} \
|
||||
--url ${url} \
|
||||
--port ${attrs.exportarrPort}'';
|
||||
EnvironmentFile =
|
||||
lib.mkIf (builtins.hasAttr "apiKey" attrs) attrs.apiKey;
|
||||
''
|
||||
${pkgs.exportarr}/bin/exportarr ${name} \
|
||||
--url ${url} \
|
||||
--port ${attrs.exportarrPort}'';
|
||||
EnvironmentFile = lib.mkIf (builtins.hasAttr "apiKey" attrs) attrs.apiKey;
|
||||
Restart = "on-failure";
|
||||
ProtectHome = true;
|
||||
ProtectSystem = "strict";
|
||||
@ -216,11 +240,14 @@ in {
|
||||
};
|
||||
|
||||
# Prometheus scrape targets (expose Exportarr to Prometheus)
|
||||
prometheus.scrapeTargets = map (key:
|
||||
prometheus.scrapeTargets = map (
|
||||
key:
|
||||
"127.0.0.1:${
|
||||
lib.attrsets.getAttrFromPath [ key "exportarrPort" ] arrConfig
|
||||
}") (builtins.attrNames arrConfig);
|
||||
|
||||
lib.attrsets.getAttrFromPath [
|
||||
key
|
||||
"exportarrPort"
|
||||
] arrConfig
|
||||
}"
|
||||
) (builtins.attrNames arrConfig);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
# This is my setup for backing up SQlite databases and other systems to S3 or
|
||||
# S3-equivalent services (like Backblaze B2).
|
||||
|
||||
{ config, lib, ... }: {
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
|
||||
options = {
|
||||
|
||||
@ -22,7 +23,6 @@
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.backup.s3.endpoint != null) {
|
||||
@ -65,7 +65,5 @@
|
||||
# timerConfig = { OnCalendar = "00:05:00"; };
|
||||
# environmentFile = backup.s3File;
|
||||
# };
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,13 @@
|
||||
# (compiled with an overlay) to insert a plugin for managing DNS validation
|
||||
# with Cloudflare's DNS API.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options = {
|
||||
caddy = {
|
||||
@ -40,51 +46,59 @@
|
||||
|
||||
# Force Caddy to 403 if not coming from allowlisted source
|
||||
caddy.cidrAllowlist = [ "127.0.0.1/32" ];
|
||||
caddy.routes = [{
|
||||
match = [{ not = [{ remote_ip.ranges = config.caddy.cidrAllowlist; }]; }];
|
||||
handle = [{
|
||||
handler = "static_response";
|
||||
status_code = "403";
|
||||
}];
|
||||
}];
|
||||
caddy.routes = [
|
||||
{
|
||||
match = [ { not = [ { remote_ip.ranges = config.caddy.cidrAllowlist; } ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "static_response";
|
||||
status_code = "403";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
services.caddy = {
|
||||
adapter = "''"; # Required to enable JSON
|
||||
configFile = pkgs.writeText "Caddyfile" (builtins.toJSON {
|
||||
apps.http.servers.main = {
|
||||
listen = [ ":443" ];
|
||||
configFile = pkgs.writeText "Caddyfile" (
|
||||
builtins.toJSON {
|
||||
apps.http.servers.main = {
|
||||
listen = [ ":443" ];
|
||||
|
||||
# These routes are pulled from the rest of this repo
|
||||
routes = config.caddy.routes;
|
||||
errors.routes = config.caddy.blocks;
|
||||
# These routes are pulled from the rest of this repo
|
||||
routes = config.caddy.routes;
|
||||
errors.routes = config.caddy.blocks;
|
||||
|
||||
logs = { }; # Uncommenting collects access logs
|
||||
};
|
||||
apps.http.servers.metrics = { }; # Enables Prometheus metrics
|
||||
apps.tls.automation.policies = config.caddy.tlsPolicies;
|
||||
|
||||
# Setup logging to file
|
||||
logging.logs.main = {
|
||||
encoder = { format = "console"; };
|
||||
writer = {
|
||||
output = "file";
|
||||
filename = "${config.services.caddy.logDir}/caddy.log";
|
||||
roll = true;
|
||||
roll_size_mb = 1;
|
||||
logs = { }; # Uncommenting collects access logs
|
||||
};
|
||||
level = "INFO";
|
||||
};
|
||||
|
||||
});
|
||||
apps.http.servers.metrics = { }; # Enables Prometheus metrics
|
||||
apps.tls.automation.policies = config.caddy.tlsPolicies;
|
||||
|
||||
# Setup logging to file
|
||||
logging.logs.main = {
|
||||
encoder = {
|
||||
format = "console";
|
||||
};
|
||||
writer = {
|
||||
output = "file";
|
||||
filename = "${config.services.caddy.logDir}/caddy.log";
|
||||
roll = true;
|
||||
roll_size_mb = 1;
|
||||
};
|
||||
level = "INFO";
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
# Allows Caddy to serve lower ports (443, 80)
|
||||
systemd.services.caddy.serviceConfig.AmbientCapabilities =
|
||||
"CAP_NET_BIND_SERVICE";
|
||||
systemd.services.caddy.serviceConfig.AmbientCapabilities = "CAP_NET_BIND_SERVICE";
|
||||
|
||||
# Required for web traffic to reach this machine
|
||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||
networking.firewall.allowedTCPPorts = [
|
||||
80
|
||||
443
|
||||
];
|
||||
|
||||
# HTTP/3 QUIC uses UDP (not sure if being used)
|
||||
networking.firewall.allowedUDPPorts = [ 443 ];
|
||||
@ -92,7 +106,5 @@
|
||||
# Caddy exposes Prometheus metrics with the admin API
|
||||
# https://caddyserver.com/docs/api
|
||||
prometheus.scrapeTargets = [ "127.0.0.1:2019" ];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -4,13 +4,18 @@
|
||||
# - Hostname defined with config.hostnames.books
|
||||
# - File directory backed up to S3 on a cron schedule.
|
||||
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
libraryPath = "/data/books";
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
options = {
|
||||
backups.calibre = lib.mkOption {
|
||||
@ -33,20 +38,22 @@ in {
|
||||
};
|
||||
|
||||
# Allow web traffic to Caddy
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.hostnames.books ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{
|
||||
dial = "localhost:${
|
||||
builtins.toString config.services.calibre-web.listen.port
|
||||
}";
|
||||
}];
|
||||
# This is required when calibre-web is behind a reverse proxy
|
||||
# https://github.com/janeczku/calibre-web/issues/19
|
||||
headers.request.add."X-Script-Name" = [ "/calibre-web" ];
|
||||
}];
|
||||
}];
|
||||
caddy.routes = [
|
||||
{
|
||||
match = [ { host = [ config.hostnames.books ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [
|
||||
{ dial = "localhost:${builtins.toString config.services.calibre-web.listen.port}"; }
|
||||
];
|
||||
# This is required when calibre-web is behind a reverse proxy
|
||||
# https://github.com/janeczku/calibre-web/issues/19
|
||||
headers.request.add."X-Script-Name" = [ "/calibre-web" ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains = [ config.hostnames.books ];
|
||||
@ -80,7 +87,5 @@ in {
|
||||
--endpoint-url=https://${config.backup.s3.endpoint}
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -52,7 +52,9 @@
|
||||
# Catch-all if no match (should never happen anyway)
|
||||
default = "http_status:404";
|
||||
# Match from ingress of any valid server name to SSH access
|
||||
ingress = { "*.masu.rs" = "ssh://localhost:22"; };
|
||||
ingress = {
|
||||
"*.masu.rs" = "ssh://localhost:22";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -79,8 +81,7 @@
|
||||
# if there is no existing AuthenticationMethods
|
||||
AuthenticationMethods publickey
|
||||
'';
|
||||
services.openssh.settings.Macs =
|
||||
[ "hmac-sha2-512" ]; # Fix for failure to find matching mac
|
||||
services.openssh.settings.Macs = [ "hmac-sha2-512" ]; # Fix for failure to find matching mac
|
||||
|
||||
# Create credentials file for Cloudflare
|
||||
secrets.cloudflared = {
|
||||
@ -91,11 +92,8 @@
|
||||
permissions = "0440";
|
||||
};
|
||||
systemd.services.cloudflared-secret = {
|
||||
requiredBy =
|
||||
[ "cloudflared-tunnel-${config.cloudflareTunnel.id}.service" ];
|
||||
requiredBy = [ "cloudflared-tunnel-${config.cloudflareTunnel.id}.service" ];
|
||||
before = [ "cloudflared-tunnel-${config.cloudflareTunnel.id}.service" ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,12 @@
|
||||
# DNS validation plugin to connect to Cloudflare and automatically create
|
||||
# validation DNS records for our generated certificates.
|
||||
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
@ -39,10 +44,9 @@ let
|
||||
"2405:8100::/32"
|
||||
"2a06:98c0::/29"
|
||||
"2c0f:f248::/32"
|
||||
|
||||
];
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
options.cloudflare.enable = lib.mkEnableOption "Use Cloudflare.";
|
||||
|
||||
@ -59,23 +63,26 @@ in {
|
||||
|
||||
# Tell Caddy to use Cloudflare DNS for ACME challenge validation
|
||||
services.caddy.package = pkgs.caddy-cloudflare; # Patched overlay
|
||||
caddy.tlsPolicies = [{
|
||||
issuers = [{
|
||||
module = "acme";
|
||||
challenges = {
|
||||
dns = {
|
||||
provider = {
|
||||
name = "cloudflare";
|
||||
api_token = "{env.CLOUDFLARE_API_TOKEN}";
|
||||
caddy.tlsPolicies = [
|
||||
{
|
||||
issuers = [
|
||||
{
|
||||
module = "acme";
|
||||
challenges = {
|
||||
dns = {
|
||||
provider = {
|
||||
name = "cloudflare";
|
||||
api_token = "{env.CLOUDFLARE_API_TOKEN}";
|
||||
};
|
||||
resolvers = [ "1.1.1.1" ];
|
||||
};
|
||||
};
|
||||
resolvers = [ "1.1.1.1" ];
|
||||
};
|
||||
};
|
||||
}];
|
||||
}];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
# Allow Caddy to read Cloudflare API key for DNS validation
|
||||
systemd.services.caddy.serviceConfig.EnvironmentFile =
|
||||
config.secrets.cloudflare-api.dest;
|
||||
systemd.services.caddy.serviceConfig.EnvironmentFile = config.secrets.cloudflare-api.dest;
|
||||
|
||||
# API key must have access to modify Cloudflare DNS records
|
||||
secrets.cloudflare-api = {
|
||||
@ -95,59 +102,58 @@ in {
|
||||
services.nextcloud.settings.trusted_proxies = cloudflareIpRanges;
|
||||
|
||||
# Allows Transmission to trust Cloudflare IPs
|
||||
services.transmission.settings.rpc-whitelist =
|
||||
builtins.concatStringsSep "," ([ "127.0.0.1" ] ++ cloudflareIpRanges);
|
||||
services.transmission.settings.rpc-whitelist = builtins.concatStringsSep "," (
|
||||
[ "127.0.0.1" ] ++ cloudflareIpRanges
|
||||
);
|
||||
|
||||
services.cloudflare-dyndns = lib.mkIf
|
||||
((builtins.length config.services.cloudflare-dyndns.domains) > 0) {
|
||||
enable = true;
|
||||
proxied = true;
|
||||
deleteMissing = true;
|
||||
apiTokenFile = config.secrets.cloudflare-api.dest;
|
||||
};
|
||||
services.cloudflare-dyndns =
|
||||
lib.mkIf ((builtins.length config.services.cloudflare-dyndns.domains) > 0)
|
||||
{
|
||||
enable = true;
|
||||
proxied = true;
|
||||
deleteMissing = true;
|
||||
apiTokenFile = config.secrets.cloudflare-api.dest;
|
||||
};
|
||||
|
||||
# Wait for secret to exist to start
|
||||
systemd.services.cloudflare-dyndns =
|
||||
lib.mkIf config.services.cloudflare-dyndns.enable {
|
||||
after = [ "cloudflare-api-secret.service" ];
|
||||
requires = [ "cloudflare-api-secret.service" ];
|
||||
};
|
||||
systemd.services.cloudflare-dyndns = lib.mkIf config.services.cloudflare-dyndns.enable {
|
||||
after = [ "cloudflare-api-secret.service" ];
|
||||
requires = [ "cloudflare-api-secret.service" ];
|
||||
};
|
||||
|
||||
# Run a second copy of dyn-dns for non-proxied domains
|
||||
# Adapted from: https://github.com/NixOS/nixpkgs/blob/nixos-unstable/nixos/modules/services/networking/cloudflare-dyndns.nix
|
||||
systemd.services.cloudflare-dyndns-noproxy =
|
||||
lib.mkIf ((builtins.length config.cloudflare.noProxyDomains) > 0) {
|
||||
description = "CloudFlare Dynamic DNS Client (no proxy)";
|
||||
after = [ "network.target" "cloudflare-api-secret.service" ];
|
||||
requires = [ "cloudflare-api-secret.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
startAt = "*:0/5";
|
||||
lib.mkIf ((builtins.length config.cloudflare.noProxyDomains) > 0)
|
||||
{
|
||||
description = "CloudFlare Dynamic DNS Client (no proxy)";
|
||||
after = [
|
||||
"network.target"
|
||||
"cloudflare-api-secret.service"
|
||||
];
|
||||
requires = [ "cloudflare-api-secret.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
startAt = "*:0/5";
|
||||
|
||||
environment = {
|
||||
CLOUDFLARE_DOMAINS = toString config.cloudflare.noProxyDomains;
|
||||
environment = {
|
||||
CLOUDFLARE_DOMAINS = toString config.cloudflare.noProxyDomains;
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
DynamicUser = true;
|
||||
StateDirectory = "cloudflare-dyndns-noproxy";
|
||||
EnvironmentFile = config.services.cloudflare-dyndns.apiTokenFile;
|
||||
ExecStart =
|
||||
let
|
||||
args =
|
||||
[ "--cache-file /var/lib/cloudflare-dyndns-noproxy/ip.cache" ]
|
||||
++ (if config.services.cloudflare-dyndns.ipv4 then [ "-4" ] else [ "-no-4" ])
|
||||
++ (if config.services.cloudflare-dyndns.ipv6 then [ "-6" ] else [ "-no-6" ])
|
||||
++ lib.optional config.services.cloudflare-dyndns.deleteMissing "--delete-missing";
|
||||
in
|
||||
"${pkgs.cloudflare-dyndns}/bin/cloudflare-dyndns ${toString args}";
|
||||
};
|
||||
};
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
DynamicUser = true;
|
||||
StateDirectory = "cloudflare-dyndns-noproxy";
|
||||
EnvironmentFile = config.services.cloudflare-dyndns.apiTokenFile;
|
||||
ExecStart = let
|
||||
args =
|
||||
[ "--cache-file /var/lib/cloudflare-dyndns-noproxy/ip.cache" ]
|
||||
++ (if config.services.cloudflare-dyndns.ipv4 then
|
||||
[ "-4" ]
|
||||
else
|
||||
[ "-no-4" ]) ++ (if config.services.cloudflare-dyndns.ipv6 then
|
||||
[ "-6" ]
|
||||
else
|
||||
[ "-no-6" ])
|
||||
++ lib.optional config.services.cloudflare-dyndns.deleteMissing
|
||||
"--delete-missing";
|
||||
|
||||
in "${pkgs.cloudflare-dyndns}/bin/cloudflare-dyndns ${toString args}";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
# This file imports all the other files in this directory for use as modules in
|
||||
# my config.
|
||||
|
||||
{ ... }: {
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
imports = [
|
||||
./arr.nix
|
||||
@ -36,5 +37,4 @@
|
||||
./victoriametrics.nix
|
||||
./wireguard.nix
|
||||
];
|
||||
|
||||
}
|
||||
|
@ -4,11 +4,15 @@
|
||||
# the Runners don't necessarily need to be running Gitea. All we need is an API
|
||||
# key for Gitea to connect to it and register ourselves as a Runner.
|
||||
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
options.giteaRunner.enable =
|
||||
lib.mkEnableOption "Enable Gitea Actions runner.";
|
||||
options.giteaRunner.enable = lib.mkEnableOption "Enable Gitea Actions runner.";
|
||||
|
||||
config = lib.mkIf config.giteaRunner.enable {
|
||||
|
||||
@ -38,8 +42,7 @@
|
||||
};
|
||||
|
||||
# Make sure the runner doesn't start until after Gitea
|
||||
systemd.services."gitea-runner-${config.networking.hostName}".after =
|
||||
[ "gitea.service" ];
|
||||
systemd.services."gitea-runner-${config.networking.hostName}".after = [ "gitea.service" ];
|
||||
|
||||
# API key needed to connect to Gitea
|
||||
secrets.giteaRunnerToken = {
|
||||
@ -58,7 +61,5 @@
|
||||
}.service"
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,14 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let giteaPath = "/var/lib/gitea"; # Default service directory
|
||||
|
||||
in {
|
||||
let
|
||||
giteaPath = "/var/lib/gitea"; # Default service directory
|
||||
in
|
||||
{
|
||||
|
||||
config = lib.mkIf config.services.gitea.enable {
|
||||
services.gitea = {
|
||||
@ -54,27 +60,30 @@ in {
|
||||
caddy.routes = [
|
||||
# Prevent public access to Prometheus metrics.
|
||||
{
|
||||
match = [{
|
||||
host = [ config.hostnames.git ];
|
||||
path = [ "/metrics*" ];
|
||||
}];
|
||||
handle = [{
|
||||
handler = "static_response";
|
||||
status_code = "403";
|
||||
}];
|
||||
match = [
|
||||
{
|
||||
host = [ config.hostnames.git ];
|
||||
path = [ "/metrics*" ];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "static_response";
|
||||
status_code = "403";
|
||||
}
|
||||
];
|
||||
}
|
||||
# Allow access to primary server.
|
||||
{
|
||||
match = [{ host = [ config.hostnames.git ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{
|
||||
dial = "localhost:${
|
||||
builtins.toString
|
||||
config.services.gitea.settings.server.HTTP_PORT
|
||||
}";
|
||||
}];
|
||||
}];
|
||||
match = [ { host = [ config.hostnames.git ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [
|
||||
{ dial = "localhost:${builtins.toString config.services.gitea.settings.server.HTTP_PORT}"; }
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
@ -83,18 +92,14 @@ in {
|
||||
|
||||
# Scrape the metrics endpoint for Prometheus.
|
||||
prometheus.scrapeTargets = [
|
||||
"127.0.0.1:${
|
||||
builtins.toString config.services.gitea.settings.server.HTTP_PORT
|
||||
}"
|
||||
"127.0.0.1:${builtins.toString config.services.gitea.settings.server.HTTP_PORT}"
|
||||
];
|
||||
|
||||
## Backup config
|
||||
|
||||
# Open to groups, allowing for backups
|
||||
systemd.services.gitea.serviceConfig.StateDirectoryMode =
|
||||
lib.mkForce "0770";
|
||||
systemd.tmpfiles.rules =
|
||||
[ "f ${giteaPath}/data/gitea.db 0660 gitea gitea" ];
|
||||
systemd.services.gitea.serviceConfig.StateDirectoryMode = lib.mkForce "0770";
|
||||
systemd.tmpfiles.rules = [ "f ${giteaPath}/data/gitea.db 0660 gitea gitea" ];
|
||||
|
||||
# Allow litestream and gitea to share a sqlite database
|
||||
users.users.litestream.extraGroups = [ "gitea" ];
|
||||
@ -103,13 +108,12 @@ in {
|
||||
# Backup sqlite database with litestream
|
||||
services.litestream = {
|
||||
settings = {
|
||||
dbs = [{
|
||||
path = "${giteaPath}/data/gitea.db";
|
||||
replicas = [{
|
||||
url =
|
||||
"s3://${config.backup.s3.bucket}.${config.backup.s3.endpoint}/gitea";
|
||||
}];
|
||||
}];
|
||||
dbs = [
|
||||
{
|
||||
path = "${giteaPath}/data/gitea.db";
|
||||
replicas = [ { url = "s3://${config.backup.s3.bucket}.${config.backup.s3.endpoint}/gitea"; } ];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@ -129,24 +133,21 @@ in {
|
||||
};
|
||||
|
||||
# Backup Gitea repos to object storage
|
||||
systemd.services.gitea-backup =
|
||||
lib.mkIf (config.backup.s3.endpoint != null) {
|
||||
description = "Backup Gitea data";
|
||||
environment.AWS_ACCESS_KEY_ID = config.backup.s3.accessKeyId;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "gitea";
|
||||
Group = "backup";
|
||||
EnvironmentFile = config.secrets.backup.dest;
|
||||
};
|
||||
script = ''
|
||||
${pkgs.awscli2}/bin/aws s3 sync --exclude */gitea.db* \
|
||||
${giteaPath}/ \
|
||||
s3://${config.backup.s3.bucket}/gitea-data/ \
|
||||
--endpoint-url=https://${config.backup.s3.endpoint}
|
||||
'';
|
||||
systemd.services.gitea-backup = lib.mkIf (config.backup.s3.endpoint != null) {
|
||||
description = "Backup Gitea data";
|
||||
environment.AWS_ACCESS_KEY_ID = config.backup.s3.accessKeyId;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "gitea";
|
||||
Group = "backup";
|
||||
EnvironmentFile = config.secrets.backup.dest;
|
||||
};
|
||||
|
||||
script = ''
|
||||
${pkgs.awscli2}/bin/aws s3 sync --exclude */gitea.db* \
|
||||
${giteaPath}/ \
|
||||
s3://${config.backup.s3.bucket}/gitea-data/ \
|
||||
--endpoint-url=https://${config.backup.s3.endpoint}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
# GPG is an encryption tool. This isn't really in use for me at the moment.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.gpg.enable = lib.mkEnableOption "GnuPG encryption.";
|
||||
|
||||
@ -16,5 +22,4 @@
|
||||
};
|
||||
home = lib.mkIf config.gui.enable { packages = with pkgs; [ pinentry ]; };
|
||||
};
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,12 @@
|
||||
|
||||
# Currently has some issues that don't make this viable.
|
||||
|
||||
{ config, lib, pkgs, ... }:
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
# Taken from:
|
||||
# https://dataswamp.org/~solene/2022-09-29-iblock-implemented-in-nixos.html
|
||||
@ -13,9 +18,12 @@
|
||||
|
||||
let
|
||||
|
||||
portsToBlock = [ 25545 25565 25570 ];
|
||||
portsString =
|
||||
builtins.concatStringsSep "," (builtins.map builtins.toString portsToBlock);
|
||||
portsToBlock = [
|
||||
25545
|
||||
25565
|
||||
25570
|
||||
];
|
||||
portsString = builtins.concatStringsSep "," (builtins.map builtins.toString portsToBlock);
|
||||
|
||||
# Block IPs for 20 days
|
||||
expire = 60 * 60 * 24 * 20;
|
||||
@ -27,19 +35,17 @@ let
|
||||
"INPUT -i eth0 -p udp -m set --match-set ${table} src -j nixos-fw-refuse"
|
||||
];
|
||||
|
||||
create-rules = lib.concatStringsSep "\n"
|
||||
(builtins.map (rule: "iptables -C " + rule + " || iptables -A " + rule)
|
||||
(rules "blocked") ++ builtins.map
|
||||
(rule: "ip6tables -C " + rule + " || ip6tables -A " + rule)
|
||||
(rules "blocked6"));
|
||||
create-rules = lib.concatStringsSep "\n" (
|
||||
builtins.map (rule: "iptables -C " + rule + " || iptables -A " + rule) (rules "blocked")
|
||||
++ builtins.map (rule: "ip6tables -C " + rule + " || ip6tables -A " + rule) (rules "blocked6")
|
||||
);
|
||||
|
||||
delete-rules = lib.concatStringsSep "\n"
|
||||
(builtins.map (rule: "iptables -C " + rule + " && iptables -D " + rule)
|
||||
(rules "blocked") ++ builtins.map
|
||||
(rule: "ip6tables -C " + rule + " && ip6tables -D " + rule)
|
||||
(rules "blocked6"));
|
||||
|
||||
in {
|
||||
delete-rules = lib.concatStringsSep "\n" (
|
||||
builtins.map (rule: "iptables -C " + rule + " && iptables -D " + rule) (rules "blocked")
|
||||
++ builtins.map (rule: "ip6tables -C " + rule + " && ip6tables -D " + rule) (rules "blocked6")
|
||||
);
|
||||
in
|
||||
{
|
||||
|
||||
options.honeypot.enable = lib.mkEnableOption "Honeypot fail2ban system.";
|
||||
|
||||
@ -54,9 +60,7 @@ in {
|
||||
then
|
||||
ipset restore -! < /var/lib/ipset.conf
|
||||
else
|
||||
ipset -exist create blocked hash:ip ${
|
||||
if expire > 0 then "timeout ${toString expire}" else ""
|
||||
}
|
||||
ipset -exist create blocked hash:ip ${if expire > 0 then "timeout ${toString expire}" else ""}
|
||||
ipset -exist create blocked6 hash:ip family inet6 ${
|
||||
if expire > 0 then "timeout ${toString expire}" else ""
|
||||
}
|
||||
@ -66,9 +70,7 @@ in {
|
||||
|
||||
# Save list when shutting down
|
||||
extraStopCommands = ''
|
||||
ipset -exist create blocked hash:ip ${
|
||||
if expire > 0 then "timeout ${toString expire}" else ""
|
||||
}
|
||||
ipset -exist create blocked hash:ip ${if expire > 0 then "timeout ${toString expire}" else ""}
|
||||
ipset -exist create blocked6 hash:ip family inet6 ${
|
||||
if expire > 0 then "timeout ${toString expire}" else ""
|
||||
}
|
||||
@ -76,5 +78,4 @@ in {
|
||||
${delete-rules}
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{ config, ... }: {
|
||||
{ config, ... }:
|
||||
{
|
||||
|
||||
# Wait for secret to be placed on the machine
|
||||
systemd.services.wait-for-identity = {
|
||||
@ -18,5 +19,4 @@
|
||||
done
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,8 @@
|
||||
# infinite retention separate from our other metrics, which can be nice for
|
||||
# recording health information, for example.
|
||||
|
||||
{ config, lib, ... }: {
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.services.influxdb2.enable {
|
||||
|
||||
@ -30,11 +31,10 @@
|
||||
group = "influxdb2";
|
||||
permissions = "0440";
|
||||
};
|
||||
systemd.services.influxdb2Password-secret =
|
||||
lib.mkIf config.services.influxdb2.enable {
|
||||
requiredBy = [ "influxdb2.service" ];
|
||||
before = [ "influxdb2.service" ];
|
||||
};
|
||||
systemd.services.influxdb2Password-secret = lib.mkIf config.services.influxdb2.enable {
|
||||
requiredBy = [ "influxdb2.service" ];
|
||||
before = [ "influxdb2.service" ];
|
||||
};
|
||||
secrets.influxdb2Token = lib.mkIf config.services.influxdb2.enable {
|
||||
source = ../../../private/influxdb2-token.age;
|
||||
dest = "${config.secretsDirectory}/influxdb2-token";
|
||||
@ -42,23 +42,24 @@
|
||||
group = "influxdb2";
|
||||
permissions = "0440";
|
||||
};
|
||||
systemd.services.influxdb2Token-secret =
|
||||
lib.mkIf config.services.influxdb2.enable {
|
||||
requiredBy = [ "influxdb2.service" ];
|
||||
before = [ "influxdb2.service" ];
|
||||
};
|
||||
systemd.services.influxdb2Token-secret = lib.mkIf config.services.influxdb2.enable {
|
||||
requiredBy = [ "influxdb2.service" ];
|
||||
before = [ "influxdb2.service" ];
|
||||
};
|
||||
|
||||
caddy.routes = lib.mkIf config.services.influxdb2.enable [{
|
||||
match = [{ host = [ config.hostnames.influxdb ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:8086"; }];
|
||||
}];
|
||||
}];
|
||||
caddy.routes = lib.mkIf config.services.influxdb2.enable [
|
||||
{
|
||||
match = [ { host = [ config.hostnames.influxdb ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = "localhost:8086"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains = [ config.hostnames.influxdb ];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{ config, lib, ... }: {
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.services.thelounge.enable {
|
||||
|
||||
@ -16,20 +17,19 @@
|
||||
# sudo su - thelounge -s /bin/sh -c "thelounge add myuser"
|
||||
|
||||
# Allow web traffic to Caddy
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.hostnames.irc ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{
|
||||
dial =
|
||||
"localhost:${builtins.toString config.services.thelounge.port}";
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
caddy.routes = [
|
||||
{
|
||||
match = [ { host = [ config.hostnames.irc ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = "localhost:${builtins.toString config.services.thelounge.port}"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains = [ config.hostnames.irc ];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,32 +1,46 @@
|
||||
# Jellyfin is a self-hosted video streaming service. This means I can play my
|
||||
# server's videos from a webpage, mobile app, or TV client.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.services.jellyfin.enable {
|
||||
|
||||
services.jellyfin.group = "media";
|
||||
users.users.jellyfin = { isSystemUser = true; };
|
||||
users.users.jellyfin = {
|
||||
isSystemUser = true;
|
||||
};
|
||||
|
||||
caddy.routes = [
|
||||
# Prevent public access to Prometheus metrics.
|
||||
{
|
||||
match = [{
|
||||
host = [ config.hostnames.stream ];
|
||||
path = [ "/metrics*" ];
|
||||
}];
|
||||
handle = [{
|
||||
handler = "static_response";
|
||||
status_code = "403";
|
||||
}];
|
||||
match = [
|
||||
{
|
||||
host = [ config.hostnames.stream ];
|
||||
path = [ "/metrics*" ];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "static_response";
|
||||
status_code = "403";
|
||||
}
|
||||
];
|
||||
}
|
||||
# Allow access to normal route.
|
||||
{
|
||||
match = [{ host = [ config.hostnames.stream ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:8096"; }];
|
||||
}];
|
||||
match = [ { host = [ config.hostnames.stream ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = "localhost:8096"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
@ -52,15 +66,15 @@
|
||||
"VDPAU_DRIVER" = "radeonsi";
|
||||
"LIBVA_DRIVER_NAME" = "radeonsi";
|
||||
};
|
||||
users.users.jellyfin.extraGroups =
|
||||
[ "render" "video" ]; # Access to /dev/dri
|
||||
users.users.jellyfin.extraGroups = [
|
||||
"render"
|
||||
"video"
|
||||
]; # Access to /dev/dri
|
||||
|
||||
# Fix issue where Jellyfin-created directories don't allow access for media group
|
||||
systemd.services.jellyfin.serviceConfig.UMask = lib.mkForce "0007";
|
||||
|
||||
# Requires MetricsEnable is true in /var/lib/jellyfin/config/system.xml
|
||||
prometheus.scrapeTargets = [ "127.0.0.1:8096" ];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
# Keybase is an encrypted communications tool with a synchronized encrypted
|
||||
# filestore that can be mounted onto a machine's filesystem.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.keybase.enable = lib.mkEnableOption "Keybase.";
|
||||
|
||||
@ -19,16 +25,16 @@
|
||||
systemd.user.services.kbfs.Service.PrivateTmp = lib.mkForce false;
|
||||
|
||||
home.packages = [ (lib.mkIf config.gui.enable pkgs.keybase-gui) ];
|
||||
home.file = let
|
||||
ignorePatterns = ''
|
||||
keybase/
|
||||
kbfs/'';
|
||||
in {
|
||||
".rgignore".text = ignorePatterns;
|
||||
".fdignore".text = ignorePatterns;
|
||||
};
|
||||
home.file =
|
||||
let
|
||||
ignorePatterns = ''
|
||||
keybase/
|
||||
kbfs/'';
|
||||
in
|
||||
{
|
||||
".rgignore".text = ignorePatterns;
|
||||
".fdignore".text = ignorePatterns;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
# Mullvad is a VPN service. This isn't currently in use for me at the moment.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.mullvad.enable = lib.mkEnableOption "Mullvad VPN.";
|
||||
|
||||
@ -8,7 +14,5 @@
|
||||
|
||||
services.mullvad-vpn.enable = true;
|
||||
environment.systemPackages = [ pkgs.mullvad-vpn ];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
# n8n is an automation integration tool for connecting data from services
|
||||
# together with triggers.
|
||||
|
||||
{ config, lib, ... }: {
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
|
||||
options = {
|
||||
n8nServer = lib.mkOption {
|
||||
@ -23,14 +24,16 @@
|
||||
};
|
||||
};
|
||||
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.n8nServer ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:5678"; }];
|
||||
}];
|
||||
}];
|
||||
|
||||
caddy.routes = [
|
||||
{
|
||||
match = [ { host = [ config.n8nServer ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = "localhost:5678"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
# Netdata is an out-of-the-box monitoring tool that exposes many different
|
||||
# metrics. Not currently in use, in favor of VictoriaMetrics and Grafana.
|
||||
|
||||
{ config, lib, ... }: {
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
|
||||
options.netdata.enable = lib.mkEnableOption "Netdata metrics.";
|
||||
|
||||
@ -11,9 +12,9 @@
|
||||
enable = true;
|
||||
|
||||
# Disable local dashboard (unsecured)
|
||||
config = { web.mode = "none"; };
|
||||
config = {
|
||||
web.mode = "none";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.services.nextcloud.enable {
|
||||
|
||||
@ -46,107 +52,137 @@
|
||||
users.users.caddy.extraGroups = [ "nextcloud" ];
|
||||
|
||||
# Point Caddy to Nginx
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.hostnames.content ]; }];
|
||||
handle = [{
|
||||
handler = "subroute";
|
||||
routes = [
|
||||
# Sets variables and headers
|
||||
caddy.routes = [
|
||||
{
|
||||
match = [ { host = [ config.hostnames.content ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handle = [
|
||||
handler = "subroute";
|
||||
routes = [
|
||||
# Sets variables and headers
|
||||
{
|
||||
handler = "vars";
|
||||
# Grab the webroot out of the written config
|
||||
# The webroot is a symlinked combined Nextcloud directory
|
||||
root =
|
||||
config.services.nginx.virtualHosts.${config.services.nextcloud.hostName}.root;
|
||||
handle = [
|
||||
{
|
||||
handler = "vars";
|
||||
# Grab the webroot out of the written config
|
||||
# The webroot is a symlinked combined Nextcloud directory
|
||||
root = config.services.nginx.virtualHosts.${config.services.nextcloud.hostName}.root;
|
||||
}
|
||||
{
|
||||
handler = "headers";
|
||||
response.set.Strict-Transport-Security = [ "max-age=31536000;" ];
|
||||
}
|
||||
];
|
||||
}
|
||||
# Reroute carddav and caldav traffic
|
||||
{
|
||||
handler = "headers";
|
||||
response.set.Strict-Transport-Security =
|
||||
[ "max-age=31536000;" ];
|
||||
match = [
|
||||
{
|
||||
path = [
|
||||
"/.well-known/carddav"
|
||||
"/.well-known/caldav"
|
||||
];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "static_response";
|
||||
headers = {
|
||||
Location = [ "/remote.php/dav" ];
|
||||
};
|
||||
status_code = 301;
|
||||
}
|
||||
];
|
||||
}
|
||||
# Block traffic to sensitive files
|
||||
{
|
||||
match = [
|
||||
{
|
||||
path = [
|
||||
"/.htaccess"
|
||||
"/data/*"
|
||||
"/config/*"
|
||||
"/db_structure"
|
||||
"/.xml"
|
||||
"/README"
|
||||
"/3rdparty/*"
|
||||
"/lib/*"
|
||||
"/templates/*"
|
||||
"/occ"
|
||||
"/console.php"
|
||||
];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "static_response";
|
||||
status_code = 404;
|
||||
}
|
||||
];
|
||||
}
|
||||
# Redirect index.php to the homepage
|
||||
{
|
||||
match = [
|
||||
{
|
||||
file = {
|
||||
try_files = [ "{http.request.uri.path}/index.php" ];
|
||||
};
|
||||
not = [ { path = [ "*/" ]; } ];
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "static_response";
|
||||
headers = {
|
||||
Location = [ "{http.request.orig_uri.path}/" ];
|
||||
};
|
||||
status_code = 308;
|
||||
}
|
||||
];
|
||||
}
|
||||
# Rewrite paths to be relative
|
||||
{
|
||||
match = [
|
||||
{
|
||||
file = {
|
||||
split_path = [ ".php" ];
|
||||
try_files = [
|
||||
"{http.request.uri.path}"
|
||||
"{http.request.uri.path}/index.php"
|
||||
"index.php"
|
||||
];
|
||||
};
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "rewrite";
|
||||
uri = "{http.matchers.file.relative}";
|
||||
}
|
||||
];
|
||||
}
|
||||
# Send all PHP traffic to Nextcloud PHP service
|
||||
{
|
||||
match = [ { path = [ "*.php" ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
transport = {
|
||||
protocol = "fastcgi";
|
||||
split_path = [ ".php" ];
|
||||
};
|
||||
upstreams = [ { dial = "unix//run/phpfpm/nextcloud.sock"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
# Finally, send the rest to the file server
|
||||
{ handle = [ { handler = "file_server"; } ]; }
|
||||
];
|
||||
}
|
||||
# Reroute carddav and caldav traffic
|
||||
{
|
||||
match =
|
||||
[{ path = [ "/.well-known/carddav" "/.well-known/caldav" ]; }];
|
||||
handle = [{
|
||||
handler = "static_response";
|
||||
headers = { Location = [ "/remote.php/dav" ]; };
|
||||
status_code = 301;
|
||||
}];
|
||||
}
|
||||
# Block traffic to sensitive files
|
||||
{
|
||||
match = [{
|
||||
path = [
|
||||
"/.htaccess"
|
||||
"/data/*"
|
||||
"/config/*"
|
||||
"/db_structure"
|
||||
"/.xml"
|
||||
"/README"
|
||||
"/3rdparty/*"
|
||||
"/lib/*"
|
||||
"/templates/*"
|
||||
"/occ"
|
||||
"/console.php"
|
||||
];
|
||||
}];
|
||||
handle = [{
|
||||
handler = "static_response";
|
||||
status_code = 404;
|
||||
}];
|
||||
}
|
||||
# Redirect index.php to the homepage
|
||||
{
|
||||
match = [{
|
||||
file = { try_files = [ "{http.request.uri.path}/index.php" ]; };
|
||||
not = [{ path = [ "*/" ]; }];
|
||||
}];
|
||||
handle = [{
|
||||
handler = "static_response";
|
||||
headers = { Location = [ "{http.request.orig_uri.path}/" ]; };
|
||||
status_code = 308;
|
||||
}];
|
||||
}
|
||||
# Rewrite paths to be relative
|
||||
{
|
||||
match = [{
|
||||
file = {
|
||||
split_path = [ ".php" ];
|
||||
try_files = [
|
||||
"{http.request.uri.path}"
|
||||
"{http.request.uri.path}/index.php"
|
||||
"index.php"
|
||||
];
|
||||
};
|
||||
}];
|
||||
handle = [{
|
||||
handler = "rewrite";
|
||||
uri = "{http.matchers.file.relative}";
|
||||
}];
|
||||
}
|
||||
# Send all PHP traffic to Nextcloud PHP service
|
||||
{
|
||||
match = [{ path = [ "*.php" ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
transport = {
|
||||
protocol = "fastcgi";
|
||||
split_path = [ ".php" ];
|
||||
};
|
||||
upstreams = [{ dial = "unix//run/phpfpm/nextcloud.sock"; }];
|
||||
}];
|
||||
}
|
||||
# Finally, send the rest to the file server
|
||||
{ handle = [{ handler = "file_server"; }]; }
|
||||
];
|
||||
}];
|
||||
terminal = true;
|
||||
}];
|
||||
terminal = true;
|
||||
}
|
||||
];
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains = [ config.hostnames.content ];
|
||||
@ -168,8 +204,7 @@
|
||||
users.users.${config.user}.extraGroups = [ "nextcloud" ];
|
||||
|
||||
# Open to groups, allowing for backups
|
||||
systemd.services.phpfpm-nextcloud.serviceConfig.StateDirectoryMode =
|
||||
lib.mkForce "0770";
|
||||
systemd.services.phpfpm-nextcloud.serviceConfig.StateDirectoryMode = lib.mkForce "0770";
|
||||
|
||||
# Log metrics to prometheus
|
||||
networking.hosts."127.0.0.1" = [ config.hostnames.content ];
|
||||
@ -180,15 +215,11 @@
|
||||
passwordFile = config.services.nextcloud.config.adminpassFile;
|
||||
};
|
||||
prometheus.scrapeTargets = [
|
||||
"127.0.0.1:${
|
||||
builtins.toString config.services.prometheus.exporters.nextcloud.port
|
||||
}"
|
||||
"127.0.0.1:${builtins.toString config.services.prometheus.exporters.nextcloud.port}"
|
||||
];
|
||||
# Allows nextcloud-exporter to read passwordFile
|
||||
users.users.nextcloud-exporter.extraGroups =
|
||||
lib.mkIf config.services.prometheus.exporters.nextcloud.enable
|
||||
[ "nextcloud" ];
|
||||
|
||||
[ "nextcloud" ];
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Paperless-ngx is a document scanning and management solution.
|
||||
|
||||
{ config, lib, ... }: {
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
|
||||
config = lib.mkIf config.services.paperless.enable {
|
||||
|
||||
@ -8,8 +9,7 @@
|
||||
mediaDir = "/data/generic/paperless";
|
||||
passwordFile = config.secrets.paperless.dest;
|
||||
settings = {
|
||||
PAPERLESS_OCR_USER_ARGS =
|
||||
builtins.toJSON { invalidate_digital_signatures = true; };
|
||||
PAPERLESS_OCR_USER_ARGS = builtins.toJSON { invalidate_digital_signatures = true; };
|
||||
|
||||
# Enable if changing the path name in Caddy
|
||||
# PAPERLESS_FORCE_SCRIPT_NAME = "/paperless";
|
||||
@ -18,23 +18,25 @@
|
||||
};
|
||||
|
||||
# Allow Nextcloud and user to see files
|
||||
users.users.nextcloud.extraGroups =
|
||||
lib.mkIf config.services.nextcloud.enable [ "paperless" ];
|
||||
users.users.nextcloud.extraGroups = lib.mkIf config.services.nextcloud.enable [ "paperless" ];
|
||||
users.users.${config.user}.extraGroups = [ "paperless" ];
|
||||
|
||||
caddy.routes = [{
|
||||
match = [{
|
||||
host = [ config.hostnames.paperless ];
|
||||
# path = [ "/paperless*" ]; # Change path name in Caddy
|
||||
}];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{
|
||||
dial =
|
||||
"localhost:${builtins.toString config.services.paperless.port}";
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
caddy.routes = [
|
||||
{
|
||||
match = [
|
||||
{
|
||||
host = [ config.hostnames.paperless ];
|
||||
# path = [ "/paperless*" ]; # Change path name in Caddy
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = "localhost:${builtins.toString config.services.paperless.port}"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains = [ config.hostnames.paperless ];
|
||||
@ -53,11 +55,7 @@
|
||||
|
||||
# Fix paperless shared permissions
|
||||
systemd.services.paperless-web.serviceConfig.UMask = lib.mkForce "0026";
|
||||
systemd.services.paperless-scheduler.serviceConfig.UMask =
|
||||
lib.mkForce "0026";
|
||||
systemd.services.paperless-task-queue.serviceConfig.UMask =
|
||||
lib.mkForce "0026";
|
||||
|
||||
systemd.services.paperless-scheduler.serviceConfig.UMask = lib.mkForce "0026";
|
||||
systemd.services.paperless-task-queue.serviceConfig.UMask = lib.mkForce "0026";
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
services.postgresql = {
|
||||
package = pkgs.postgresql_15;
|
||||
@ -12,19 +18,19 @@
|
||||
root root postgres
|
||||
admin ${config.user} admin
|
||||
'';
|
||||
ensureUsers = [{
|
||||
name = "admin";
|
||||
ensureClauses = {
|
||||
createdb = true;
|
||||
createrole = true;
|
||||
login = true;
|
||||
};
|
||||
}];
|
||||
ensureUsers = [
|
||||
{
|
||||
name = "admin";
|
||||
ensureClauses = {
|
||||
createdb = true;
|
||||
createrole = true;
|
||||
login = true;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
home-manager.users.${config.user}.home.packages =
|
||||
lib.mkIf config.services.postgresql.enable [
|
||||
pkgs.pgcli # Postgres client with autocomplete
|
||||
];
|
||||
|
||||
home-manager.users.${config.user}.home.packages = lib.mkIf config.services.postgresql.enable [
|
||||
pkgs.pgcli # Postgres client with autocomplete
|
||||
];
|
||||
}
|
||||
|
@ -4,7 +4,13 @@
|
||||
# Instead of running traditional Prometheus, I generally run VictoriaMetrics as
|
||||
# a more efficient drop-in replacement.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.prometheus = {
|
||||
exporters.enable = lib.mkEnableOption "Enable Prometheus exporters";
|
||||
@ -15,105 +21,99 @@
|
||||
};
|
||||
};
|
||||
|
||||
config = let
|
||||
config =
|
||||
let
|
||||
|
||||
# If hosting Grafana, host local Prometheus and listen for inbound jobs. If
|
||||
# not hosting Grafana, send remote Prometheus writes to primary host.
|
||||
isServer = config.services.grafana.enable;
|
||||
# If hosting Grafana, host local Prometheus and listen for inbound jobs. If
|
||||
# not hosting Grafana, send remote Prometheus writes to primary host.
|
||||
isServer = config.services.grafana.enable;
|
||||
in
|
||||
{
|
||||
|
||||
in {
|
||||
|
||||
# Turn on exporters if any Prometheus scraper is running
|
||||
prometheus.exporters.enable = builtins.any (x: x) [
|
||||
config.services.prometheus.enable
|
||||
config.services.victoriametrics.enable
|
||||
config.services.vmagent.enable
|
||||
];
|
||||
|
||||
prometheus.scrapeTargets = [
|
||||
"127.0.0.1:${
|
||||
builtins.toString config.services.prometheus.exporters.node.port
|
||||
}"
|
||||
"127.0.0.1:${
|
||||
builtins.toString config.services.prometheus.exporters.systemd.port
|
||||
}"
|
||||
"127.0.0.1:${
|
||||
builtins.toString config.services.prometheus.exporters.process.port
|
||||
}"
|
||||
];
|
||||
|
||||
services.prometheus = {
|
||||
exporters.node.enable = config.prometheus.exporters.enable;
|
||||
exporters.node.enabledCollectors = [ ];
|
||||
exporters.node.disabledCollectors = [ "cpufreq" ];
|
||||
exporters.systemd.enable = config.prometheus.exporters.enable;
|
||||
exporters.process.enable = config.prometheus.exporters.enable;
|
||||
exporters.process.settings.process_names = [
|
||||
# Remove nix store path from process name
|
||||
{
|
||||
name = "{{.Matches.Wrapped}} {{ .Matches.Args }}";
|
||||
cmdline = [ "^/nix/store[^ ]*/(?P<Wrapped>[^ /]*) (?P<Args>.*)" ];
|
||||
}
|
||||
# Turn on exporters if any Prometheus scraper is running
|
||||
prometheus.exporters.enable = builtins.any (x: x) [
|
||||
config.services.prometheus.enable
|
||||
config.services.victoriametrics.enable
|
||||
config.services.vmagent.enable
|
||||
];
|
||||
extraFlags = lib.mkIf isServer [ "--web.enable-remote-write-receiver" ];
|
||||
scrapeConfigs = [{
|
||||
job_name = config.networking.hostName;
|
||||
static_configs = [{ targets = config.scrapeTargets; }];
|
||||
}];
|
||||
webExternalUrl =
|
||||
lib.mkIf isServer "https://${config.hostnames.prometheus}";
|
||||
# Web config file: https://prometheus.io/docs/prometheus/latest/configuration/https/
|
||||
webConfigFile = lib.mkIf isServer
|
||||
((pkgs.formats.yaml { }).generate "webconfig.yml" {
|
||||
basic_auth_users = {
|
||||
# Generate password: htpasswd -nBC 10 "" | tr -d ':\n'
|
||||
# Encrypt and place in private/prometheus.age
|
||||
"prometheus" =
|
||||
"$2y$10$r7FWHLHTGPAY312PdhkPEuvb05aGn9Nk1IO7qtUUUjmaDl35l6sLa";
|
||||
};
|
||||
});
|
||||
remoteWrite = lib.mkIf (!isServer) [{
|
||||
name = config.networking.hostName;
|
||||
url = "https://${config.hostnames.prometheus}/api/v1/write";
|
||||
basic_auth = {
|
||||
# Uses password hashed with bcrypt above
|
||||
username = "prometheus";
|
||||
password_file = config.secrets.prometheus.dest;
|
||||
};
|
||||
}];
|
||||
};
|
||||
|
||||
# Create credentials file for remote Prometheus push
|
||||
secrets.prometheus =
|
||||
lib.mkIf (config.services.prometheus.enable && !isServer) {
|
||||
prometheus.scrapeTargets = [
|
||||
"127.0.0.1:${builtins.toString config.services.prometheus.exporters.node.port}"
|
||||
"127.0.0.1:${builtins.toString config.services.prometheus.exporters.systemd.port}"
|
||||
"127.0.0.1:${builtins.toString config.services.prometheus.exporters.process.port}"
|
||||
];
|
||||
|
||||
services.prometheus = {
|
||||
exporters.node.enable = config.prometheus.exporters.enable;
|
||||
exporters.node.enabledCollectors = [ ];
|
||||
exporters.node.disabledCollectors = [ "cpufreq" ];
|
||||
exporters.systemd.enable = config.prometheus.exporters.enable;
|
||||
exporters.process.enable = config.prometheus.exporters.enable;
|
||||
exporters.process.settings.process_names = [
|
||||
# Remove nix store path from process name
|
||||
{
|
||||
name = "{{.Matches.Wrapped}} {{ .Matches.Args }}";
|
||||
cmdline = [ "^/nix/store[^ ]*/(?P<Wrapped>[^ /]*) (?P<Args>.*)" ];
|
||||
}
|
||||
];
|
||||
extraFlags = lib.mkIf isServer [ "--web.enable-remote-write-receiver" ];
|
||||
scrapeConfigs = [
|
||||
{
|
||||
job_name = config.networking.hostName;
|
||||
static_configs = [ { targets = config.scrapeTargets; } ];
|
||||
}
|
||||
];
|
||||
webExternalUrl = lib.mkIf isServer "https://${config.hostnames.prometheus}";
|
||||
# Web config file: https://prometheus.io/docs/prometheus/latest/configuration/https/
|
||||
webConfigFile = lib.mkIf isServer (
|
||||
(pkgs.formats.yaml { }).generate "webconfig.yml" {
|
||||
basic_auth_users = {
|
||||
# Generate password: htpasswd -nBC 10 "" | tr -d ':\n'
|
||||
# Encrypt and place in private/prometheus.age
|
||||
"prometheus" = "$2y$10$r7FWHLHTGPAY312PdhkPEuvb05aGn9Nk1IO7qtUUUjmaDl35l6sLa";
|
||||
};
|
||||
}
|
||||
);
|
||||
remoteWrite = lib.mkIf (!isServer) [
|
||||
{
|
||||
name = config.networking.hostName;
|
||||
url = "https://${config.hostnames.prometheus}/api/v1/write";
|
||||
basic_auth = {
|
||||
# Uses password hashed with bcrypt above
|
||||
username = "prometheus";
|
||||
password_file = config.secrets.prometheus.dest;
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# Create credentials file for remote Prometheus push
|
||||
secrets.prometheus = lib.mkIf (config.services.prometheus.enable && !isServer) {
|
||||
source = ../../../private/prometheus.age;
|
||||
dest = "${config.secretsDirectory}/prometheus";
|
||||
owner = "prometheus";
|
||||
group = "prometheus";
|
||||
permissions = "0440";
|
||||
};
|
||||
systemd.services.prometheus-secret =
|
||||
lib.mkIf (config.services.prometheus.enable && !isServer) {
|
||||
systemd.services.prometheus-secret = lib.mkIf (config.services.prometheus.enable && !isServer) {
|
||||
requiredBy = [ "prometheus.service" ];
|
||||
before = [ "prometheus.service" ];
|
||||
};
|
||||
|
||||
caddy.routes = lib.mkIf (config.services.prometheus.enable && isServer) [{
|
||||
match = [{ host = [ config.hostnames.prometheus ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams =
|
||||
[{ dial = "localhost:${config.services.prometheus.port}"; }];
|
||||
}];
|
||||
}];
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains =
|
||||
if (config.services.prometheus.enable && isServer) then
|
||||
[ config.hostnames.prometheus ]
|
||||
else
|
||||
[ ];
|
||||
|
||||
};
|
||||
caddy.routes = lib.mkIf (config.services.prometheus.enable && isServer) [
|
||||
{
|
||||
match = [ { host = [ config.hostnames.prometheus ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = "localhost:${config.services.prometheus.port}"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains =
|
||||
if (config.services.prometheus.enable && isServer) then [ config.hostnames.prometheus ] else [ ];
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Samba is a Windows-compatible file-sharing service.
|
||||
|
||||
{ config, lib, ... }: {
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
|
||||
config = {
|
||||
|
||||
@ -21,15 +22,12 @@
|
||||
networking.firewall.allowedUDPPorts = [ 3702 ];
|
||||
|
||||
# Allow client browsing Samba and virtual filesystem shares
|
||||
services.gvfs =
|
||||
lib.mkIf (config.gui.enable && config.nautilus.enable) { enable = true; };
|
||||
services.gvfs = lib.mkIf (config.gui.enable && config.nautilus.enable) { enable = true; };
|
||||
|
||||
# # Permissions required to mount Samba with GVFS, if not using desktop environment
|
||||
# environment.systemPackages = lib.mkIf (config.gui.enable
|
||||
# && config.nautilus.enable
|
||||
# && config.services.xserver.windowManager.i3.enable)
|
||||
# [ pkgs.lxqt.lxqt-policykit ];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,13 @@
|
||||
|
||||
# In my case, I pre-encrypt my secrets and commit them to git.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options = {
|
||||
|
||||
@ -14,42 +20,43 @@
|
||||
};
|
||||
|
||||
secrets = lib.mkOption {
|
||||
type = lib.types.attrsOf (lib.types.submodule {
|
||||
options = {
|
||||
source = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = "Path to encrypted secret.";
|
||||
type = lib.types.attrsOf (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
source = lib.mkOption {
|
||||
type = lib.types.path;
|
||||
description = "Path to encrypted secret.";
|
||||
};
|
||||
dest = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Resulting path for decrypted secret.";
|
||||
};
|
||||
owner = lib.mkOption {
|
||||
default = "root";
|
||||
type = lib.types.str;
|
||||
description = "User to own the secret.";
|
||||
};
|
||||
group = lib.mkOption {
|
||||
default = "root";
|
||||
type = lib.types.str;
|
||||
description = "Group to own the secret.";
|
||||
};
|
||||
permissions = lib.mkOption {
|
||||
default = "0400";
|
||||
type = lib.types.str;
|
||||
description = "Permissions expressed as octal.";
|
||||
};
|
||||
prefix = lib.mkOption {
|
||||
default = "";
|
||||
type = lib.types.str;
|
||||
description = "Prefix for secret value (for environment files).";
|
||||
};
|
||||
};
|
||||
dest = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Resulting path for decrypted secret.";
|
||||
};
|
||||
owner = lib.mkOption {
|
||||
default = "root";
|
||||
type = lib.types.str;
|
||||
description = "User to own the secret.";
|
||||
};
|
||||
group = lib.mkOption {
|
||||
default = "root";
|
||||
type = lib.types.str;
|
||||
description = "Group to own the secret.";
|
||||
};
|
||||
permissions = lib.mkOption {
|
||||
default = "0400";
|
||||
type = lib.types.str;
|
||||
description = "Permissions expressed as octal.";
|
||||
};
|
||||
prefix = lib.mkOption {
|
||||
default = "";
|
||||
type = lib.types.str;
|
||||
description = "Prefix for secret value (for environment files).";
|
||||
};
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
description = "Set of secrets to decrypt to disk.";
|
||||
default = { };
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isLinux {
|
||||
@ -80,7 +87,6 @@
|
||||
chown '${attrs.owner}':'${attrs.group}' '${attrs.dest}'
|
||||
chmod '${attrs.permissions}' '${attrs.dest}'
|
||||
'';
|
||||
|
||||
};
|
||||
}) config.secrets;
|
||||
|
||||
@ -92,7 +98,5 @@
|
||||
# group = "my-app";
|
||||
# permissions = "0440";
|
||||
# };
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
# SSHD service for allowing SSH access to my machines.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options = {
|
||||
publicKeys = lib.mkOption {
|
||||
@ -27,8 +33,9 @@
|
||||
};
|
||||
};
|
||||
|
||||
users.users.${config.user}.openssh.authorizedKeys.keys =
|
||||
lib.mkIf (config.publicKeys != null) config.publicKeys;
|
||||
users.users.${config.user}.openssh.authorizedKeys.keys = lib.mkIf (
|
||||
config.publicKeys != null
|
||||
) config.publicKeys;
|
||||
|
||||
# Implement a simple fail2ban service for sshd
|
||||
services.sshguard.enable = true;
|
||||
@ -39,5 +46,4 @@
|
||||
# - Will disable until fixed
|
||||
environment.enableAllTerminfo = pkgs.stdenv.isLinux && pkgs.stdenv.isx86_64;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,85 +1,103 @@
|
||||
# Transmission is a bittorrent client, which can run in the background for
|
||||
# automated downloads with a web GUI.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = let
|
||||
namespace = config.networking.wireguard.interfaces.wg0.interfaceNamespace;
|
||||
vpnIp = lib.strings.removeSuffix "/32"
|
||||
(builtins.head config.networking.wireguard.interfaces.wg0.ips);
|
||||
in lib.mkIf config.services.transmission.enable {
|
||||
config =
|
||||
let
|
||||
namespace = config.networking.wireguard.interfaces.wg0.interfaceNamespace;
|
||||
vpnIp = lib.strings.removeSuffix "/32" (
|
||||
builtins.head config.networking.wireguard.interfaces.wg0.ips
|
||||
);
|
||||
in
|
||||
lib.mkIf config.services.transmission.enable {
|
||||
|
||||
# Setup transmission
|
||||
services.transmission = {
|
||||
settings = {
|
||||
port-forwarding-enabled = false;
|
||||
rpc-authentication-required = true;
|
||||
rpc-port = 9091;
|
||||
rpc-bind-address = "0.0.0.0";
|
||||
rpc-username = config.user;
|
||||
# This is a salted hash of the real password
|
||||
# https://github.com/tomwijnroks/transmission-pwgen
|
||||
rpc-password = "{c4c5145f6e18bcd3c7429214a832440a45285ce26jDOBGVW";
|
||||
rpc-host-whitelist = config.hostnames.transmission;
|
||||
rpc-host-whitelist-enabled = true;
|
||||
rpc-whitelist = lib.mkDefault "127.0.0.1"; # Overwritten by Cloudflare
|
||||
rpc-whitelist-enabled = true;
|
||||
# Setup transmission
|
||||
services.transmission = {
|
||||
settings = {
|
||||
port-forwarding-enabled = false;
|
||||
rpc-authentication-required = true;
|
||||
rpc-port = 9091;
|
||||
rpc-bind-address = "0.0.0.0";
|
||||
rpc-username = config.user;
|
||||
# This is a salted hash of the real password
|
||||
# https://github.com/tomwijnroks/transmission-pwgen
|
||||
rpc-password = "{c4c5145f6e18bcd3c7429214a832440a45285ce26jDOBGVW";
|
||||
rpc-host-whitelist = config.hostnames.transmission;
|
||||
rpc-host-whitelist-enabled = true;
|
||||
rpc-whitelist = lib.mkDefault "127.0.0.1"; # Overwritten by Cloudflare
|
||||
rpc-whitelist-enabled = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains = [ config.hostnames.transmission ];
|
||||
|
||||
# Bind transmission to wireguard namespace
|
||||
systemd.services.transmission = lib.mkIf config.wireguard.enable {
|
||||
bindsTo = [ "netns@${namespace}.service" ];
|
||||
requires = [
|
||||
"network-online.target"
|
||||
"transmission-secret.service"
|
||||
];
|
||||
after = [
|
||||
"wireguard-wg0.service"
|
||||
"transmission-secret.service"
|
||||
];
|
||||
unitConfig.JoinsNamespaceOf = "netns@${namespace}.service";
|
||||
serviceConfig.NetworkNamespacePath = "/var/run/netns/${namespace}";
|
||||
};
|
||||
|
||||
# Create reverse proxy for web UI
|
||||
caddy.routes =
|
||||
let
|
||||
# Set if the download domain is the same as the Transmission domain
|
||||
useDownloadDomain = config.hostnames.download == config.hostnames.transmission;
|
||||
in
|
||||
lib.mkAfter [
|
||||
{
|
||||
group = if useDownloadDomain then "download" else "transmission";
|
||||
match = [
|
||||
{
|
||||
host = [ config.hostnames.transmission ];
|
||||
path = if useDownloadDomain then [ "/transmission*" ] else null;
|
||||
}
|
||||
];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [
|
||||
{ dial = "localhost:${builtins.toString config.services.transmission.settings.rpc-port}"; }
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Caddy and Transmission both try to set rmem_max for larger UDP packets.
|
||||
# We will choose Transmission's recommendation (4 MB).
|
||||
boot.kernel.sysctl."net.core.rmem_max" = 4194304;
|
||||
|
||||
# Allow inbound connections to reach namespace
|
||||
systemd.services.transmission-web-netns = lib.mkIf config.wireguard.enable {
|
||||
description = "Forward to transmission in wireguard namespace";
|
||||
requires = [ "transmission.service" ];
|
||||
after = [ "transmission.service" ];
|
||||
serviceConfig = {
|
||||
Restart = "on-failure";
|
||||
TimeoutStopSec = 300;
|
||||
};
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
script = ''
|
||||
${pkgs.iproute2}/bin/ip netns exec ${namespace} ${pkgs.iproute2}/bin/ip link set dev lo up
|
||||
${pkgs.socat}/bin/socat tcp-listen:9091,fork,reuseaddr exec:'${pkgs.iproute2}/bin/ip netns exec ${namespace} ${pkgs.socat}/bin/socat STDIO "tcp-connect:${vpnIp}:9091"',nofork
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains = [ config.hostnames.transmission ];
|
||||
|
||||
# Bind transmission to wireguard namespace
|
||||
systemd.services.transmission = lib.mkIf config.wireguard.enable {
|
||||
bindsTo = [ "netns@${namespace}.service" ];
|
||||
requires = [ "network-online.target" "transmission-secret.service" ];
|
||||
after = [ "wireguard-wg0.service" "transmission-secret.service" ];
|
||||
unitConfig.JoinsNamespaceOf = "netns@${namespace}.service";
|
||||
serviceConfig.NetworkNamespacePath = "/var/run/netns/${namespace}";
|
||||
};
|
||||
|
||||
# Create reverse proxy for web UI
|
||||
caddy.routes = let
|
||||
# Set if the download domain is the same as the Transmission domain
|
||||
useDownloadDomain = config.hostnames.download
|
||||
== config.hostnames.transmission;
|
||||
in lib.mkAfter [{
|
||||
group = if useDownloadDomain then "download" else "transmission";
|
||||
match = [{
|
||||
host = [ config.hostnames.transmission ];
|
||||
path = if useDownloadDomain then [ "/transmission*" ] else null;
|
||||
}];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{
|
||||
dial = "localhost:${
|
||||
builtins.toString config.services.transmission.settings.rpc-port
|
||||
}";
|
||||
}];
|
||||
}];
|
||||
}];
|
||||
|
||||
# Caddy and Transmission both try to set rmem_max for larger UDP packets.
|
||||
# We will choose Transmission's recommendation (4 MB).
|
||||
boot.kernel.sysctl."net.core.rmem_max" = 4194304;
|
||||
|
||||
# Allow inbound connections to reach namespace
|
||||
systemd.services.transmission-web-netns = lib.mkIf config.wireguard.enable {
|
||||
description = "Forward to transmission in wireguard namespace";
|
||||
requires = [ "transmission.service" ];
|
||||
after = [ "transmission.service" ];
|
||||
serviceConfig = {
|
||||
Restart = "on-failure";
|
||||
TimeoutStopSec = 300;
|
||||
};
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
script = ''
|
||||
${pkgs.iproute2}/bin/ip netns exec ${namespace} ${pkgs.iproute2}/bin/ip link set dev lo up
|
||||
${pkgs.socat}/bin/socat tcp-listen:9091,fork,reuseaddr exec:'${pkgs.iproute2}/bin/ip netns exec ${namespace} ${pkgs.socat}/bin/socat STDIO "tcp-connect:${vpnIp}:9091"',nofork
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -2,11 +2,17 @@
|
||||
# service, which allows for self-hosting the synchronization of a Bitwarden
|
||||
# password manager client.
|
||||
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let vaultwardenPath = "/var/lib/bitwarden_rs"; # Default service directory
|
||||
|
||||
in {
|
||||
let
|
||||
vaultwardenPath = "/var/lib/bitwarden_rs"; # Default service directory
|
||||
in
|
||||
{
|
||||
|
||||
config = lib.mkIf config.services.vaultwarden.enable {
|
||||
services.vaultwarden = {
|
||||
@ -39,18 +45,20 @@ in {
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 3012 ];
|
||||
|
||||
caddy.routes = [{
|
||||
match = [{ host = [ config.hostnames.secrets ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{
|
||||
dial = "localhost:${
|
||||
builtins.toString config.services.vaultwarden.config.ROCKET_PORT
|
||||
}";
|
||||
}];
|
||||
headers.request.add."X-Real-IP" = [ "{http.request.remote.host}" ];
|
||||
}];
|
||||
}];
|
||||
caddy.routes = [
|
||||
{
|
||||
match = [ { host = [ config.hostnames.secrets ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [
|
||||
{ dial = "localhost:${builtins.toString config.services.vaultwarden.config.ROCKET_PORT}"; }
|
||||
];
|
||||
headers.request.add."X-Real-IP" = [ "{http.request.remote.host}" ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains = [ config.hostnames.secrets ];
|
||||
@ -58,8 +66,7 @@ in {
|
||||
## Backup config
|
||||
|
||||
# Open to groups, allowing for backups
|
||||
systemd.services.vaultwarden.serviceConfig.StateDirectoryMode =
|
||||
lib.mkForce "0770";
|
||||
systemd.services.vaultwarden.serviceConfig.StateDirectoryMode = lib.mkForce "0770";
|
||||
systemd.tmpfiles.rules = [
|
||||
"f ${vaultwardenPath}/db.sqlite3 0660 vaultwarden vaultwarden"
|
||||
"f ${vaultwardenPath}/db.sqlite3-shm 0660 vaultwarden vaultwarden"
|
||||
@ -73,13 +80,14 @@ in {
|
||||
# Backup sqlite database with litestream
|
||||
services.litestream = {
|
||||
settings = {
|
||||
dbs = [{
|
||||
path = "${vaultwardenPath}/db.sqlite3";
|
||||
replicas = [{
|
||||
url =
|
||||
"s3://${config.backup.s3.bucket}.${config.backup.s3.endpoint}/vaultwarden";
|
||||
}];
|
||||
}];
|
||||
dbs = [
|
||||
{
|
||||
path = "${vaultwardenPath}/db.sqlite3";
|
||||
replicas = [
|
||||
{ url = "s3://${config.backup.s3.bucket}.${config.backup.s3.endpoint}/vaultwarden"; }
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
@ -117,7 +125,5 @@ in {
|
||||
--exclude ".db.sqlite3*"
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,37 +1,44 @@
|
||||
# VictoriaMetrics is a more efficient drop-in replacement for Prometheus and
|
||||
# InfluxDB (timeseries databases built for monitoring system metrics).
|
||||
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
username = "prometheus";
|
||||
|
||||
prometheusConfig = (pkgs.formats.yaml { }).generate "prometheus.yml" {
|
||||
scrape_configs = [{
|
||||
job_name = config.networking.hostName;
|
||||
stream_parse = true;
|
||||
static_configs = [{ targets = config.prometheus.scrapeTargets; }];
|
||||
}];
|
||||
scrape_configs = [
|
||||
{
|
||||
job_name = config.networking.hostName;
|
||||
stream_parse = true;
|
||||
static_configs = [ { targets = config.prometheus.scrapeTargets; } ];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
authConfig = (pkgs.formats.yaml { }).generate "auth.yml" {
|
||||
users = [{
|
||||
username = username;
|
||||
password = "%{PASSWORD}";
|
||||
url_prefix =
|
||||
"http://localhost${config.services.victoriametrics.listenAddress}";
|
||||
}];
|
||||
users = [
|
||||
{
|
||||
username = username;
|
||||
password = "%{PASSWORD}";
|
||||
url_prefix = "http://localhost${config.services.victoriametrics.listenAddress}";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
authPort = "8427";
|
||||
|
||||
in {
|
||||
in
|
||||
{
|
||||
|
||||
config = {
|
||||
|
||||
services.victoriametrics.extraOptions =
|
||||
[ "-promscrape.config=${prometheusConfig}" ];
|
||||
services.victoriametrics.extraOptions = [ "-promscrape.config=${prometheusConfig}" ];
|
||||
|
||||
systemd.services.vmauth = lib.mkIf config.services.victoriametrics.enable {
|
||||
description = "VictoriaMetrics basic auth proxy";
|
||||
@ -55,39 +62,38 @@ in {
|
||||
dest = "${config.secretsDirectory}/vmauth";
|
||||
prefix = "PASSWORD=";
|
||||
};
|
||||
systemd.services.vmauth-secret =
|
||||
lib.mkIf config.services.victoriametrics.enable {
|
||||
requiredBy = [ "vmauth.service" ];
|
||||
before = [ "vmauth.service" ];
|
||||
};
|
||||
systemd.services.vmauth-secret = lib.mkIf config.services.victoriametrics.enable {
|
||||
requiredBy = [ "vmauth.service" ];
|
||||
before = [ "vmauth.service" ];
|
||||
};
|
||||
|
||||
caddy.routes = lib.mkIf config.services.victoriametrics.enable [{
|
||||
match = [{ host = [ config.hostnames.prometheus ]; }];
|
||||
handle = [{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [{ dial = "localhost:${authPort}"; }];
|
||||
}];
|
||||
}];
|
||||
caddy.routes = lib.mkIf config.services.victoriametrics.enable [
|
||||
{
|
||||
match = [ { host = [ config.hostnames.prometheus ]; } ];
|
||||
handle = [
|
||||
{
|
||||
handler = "reverse_proxy";
|
||||
upstreams = [ { dial = "localhost:${authPort}"; } ];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
# Configure Cloudflare DNS to point to this machine
|
||||
services.cloudflare-dyndns.domains =
|
||||
if config.services.victoriametrics.enable then
|
||||
[ config.hostnames.prometheus ]
|
||||
else
|
||||
[ ];
|
||||
if config.services.victoriametrics.enable then [ config.hostnames.prometheus ] else [ ];
|
||||
|
||||
# VMAgent
|
||||
|
||||
services.vmagent.prometheusConfig = prometheusConfig; # Overwritten below
|
||||
systemd.services.vmagent.serviceConfig =
|
||||
lib.mkIf config.services.vmagent.enable {
|
||||
ExecStart = lib.mkForce ''
|
||||
${pkgs.victoriametrics}/bin/vmagent \
|
||||
-promscrape.config=${prometheusConfig} \
|
||||
-remoteWrite.url="https://${config.hostnames.prometheus}/api/v1/write" \
|
||||
-remoteWrite.basicAuth.username=${username} \
|
||||
-remoteWrite.basicAuth.passwordFile=${config.secrets.vmagent.dest}'';
|
||||
};
|
||||
systemd.services.vmagent.serviceConfig = lib.mkIf config.services.vmagent.enable {
|
||||
ExecStart = lib.mkForce ''
|
||||
${pkgs.victoriametrics}/bin/vmagent \
|
||||
-promscrape.config=${prometheusConfig} \
|
||||
-remoteWrite.url="https://${config.hostnames.prometheus}/api/v1/write" \
|
||||
-remoteWrite.basicAuth.username=${username} \
|
||||
-remoteWrite.basicAuth.passwordFile=${config.secrets.vmagent.dest}'';
|
||||
};
|
||||
|
||||
secrets.vmagent = lib.mkIf config.services.vmagent.enable {
|
||||
source = ../../../private/prometheus.age;
|
||||
@ -99,7 +105,5 @@ in {
|
||||
requiredBy = [ "vmagent.service" ];
|
||||
before = [ "vmagent.service" ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,13 @@
|
||||
# Wireguard is a VPN protocol that can be setup to create a mesh network
|
||||
# between machines on different LANs. This is currently not in use in my setup.
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options.wireguard.enable = lib.mkEnableOption "Wireguard VPN setup.";
|
||||
|
||||
@ -21,7 +27,6 @@
|
||||
|
||||
# Move to network namespace for isolating programs
|
||||
interfaceNamespace = "wg";
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -45,7 +50,5 @@
|
||||
source = ../../../private/wireguard.age;
|
||||
dest = "${config.secretsDirectory}/wireguard";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
# This setting only applies to NixOS, different on Darwin
|
||||
nix.gc.dates = "09:03"; # Run every morning (but before upgrade)
|
||||
@ -19,8 +25,10 @@
|
||||
|
||||
# Create an email notification service for failed jobs
|
||||
systemd.services."notify-email@" =
|
||||
let address = "system@${config.mail.server}";
|
||||
in {
|
||||
let
|
||||
address = "system@${config.mail.server}";
|
||||
in
|
||||
{
|
||||
enable = config.mail.enable;
|
||||
environment.SERVICE_ID = "%i";
|
||||
script = ''
|
||||
@ -40,8 +48,7 @@
|
||||
};
|
||||
|
||||
# Send an email whenever auto upgrade fails
|
||||
systemd.services.nixos-upgrade.onFailure =
|
||||
lib.mkIf config.systemd.services."notify-email@".enable
|
||||
[ "notify-email@%i.service" ];
|
||||
|
||||
systemd.services.nixos-upgrade.onFailure = lib.mkIf config.systemd.services."notify-email@".enable [
|
||||
"notify-email@%i.service"
|
||||
];
|
||||
}
|
||||
|
@ -1,14 +1,22 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
imports =
|
||||
[ ./auto-upgrade.nix ./doas.nix ./journald.nix ./user.nix ./timezone.nix ];
|
||||
imports = [
|
||||
./auto-upgrade.nix
|
||||
./doas.nix
|
||||
./journald.nix
|
||||
./user.nix
|
||||
./timezone.nix
|
||||
];
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isLinux {
|
||||
|
||||
# Pin a state version to prevent warnings
|
||||
system.stateVersion =
|
||||
config.home-manager.users.${config.user}.home.stateVersion;
|
||||
|
||||
system.stateVersion = config.home-manager.users.${config.user}.home.stateVersion;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
# Replace sudo with doas
|
||||
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isLinux {
|
||||
|
||||
@ -18,24 +24,25 @@
|
||||
|
||||
# Pass environment variables from user to root
|
||||
# Also requires specifying that we are removing password here
|
||||
extraRules = [{
|
||||
groups = [ "wheel" ];
|
||||
noPass = true;
|
||||
keepEnv = true;
|
||||
}];
|
||||
extraRules = [
|
||||
{
|
||||
groups = [ "wheel" ];
|
||||
noPass = true;
|
||||
keepEnv = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
home-manager.users.${config.user}.programs = {
|
||||
|
||||
# Alias sudo to doas for convenience
|
||||
fish.shellAliases = { sudo = "doas"; };
|
||||
fish.shellAliases = {
|
||||
sudo = "doas";
|
||||
};
|
||||
|
||||
# Disable overriding our sudo alias with a TERMINFO alias
|
||||
kitty.settings.shell_integration = "no-sudo";
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{ ... }: {
|
||||
{ ... }:
|
||||
{
|
||||
|
||||
# How long to keep journalctl entries
|
||||
# This helps to make sure log disk usage doesn't grow too unwieldy
|
||||
@ -7,5 +8,4 @@
|
||||
MaxFileSec=1month
|
||||
MaxRetentionSec=2month
|
||||
'';
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,19 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
config = lib.mkIf pkgs.stdenv.isLinux {
|
||||
|
||||
# Service to determine location for time zone
|
||||
services.geoclue2.enable = true;
|
||||
services.geoclue2.enableWifi = false; # Breaks when it can't connect
|
||||
location = { provider = "geoclue2"; };
|
||||
location = {
|
||||
provider = "geoclue2";
|
||||
};
|
||||
|
||||
# Enable local time based on time zone
|
||||
services.localtimed.enable = true;
|
||||
@ -17,7 +25,5 @@
|
||||
# Fix "Failed to set timezone"
|
||||
# https://github.com/NixOS/nixpkgs/issues/68489#issuecomment-1484030107
|
||||
services.geoclue2.enableDemoAgent = lib.mkForce true;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
{ config, pkgs, lib, ... }: {
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
options = {
|
||||
|
||||
@ -8,7 +14,6 @@
|
||||
default = null;
|
||||
# Test it by running: mkpasswd -m sha-512 --salt "PZYiMGmJIIHAepTM"
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
@ -28,7 +33,6 @@
|
||||
extraGroups = [
|
||||
"wheel" # Sudo privileges
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
# Allow writing custom scripts outside of Nix
|
||||
@ -59,10 +63,10 @@
|
||||
desktop = "$HOME/other/desktop";
|
||||
publicShare = "$HOME/other/public";
|
||||
templates = "$HOME/other/templates";
|
||||
extraConfig = { XDG_DEV_DIR = "$HOME/dev"; };
|
||||
extraConfig = {
|
||||
XDG_DEV_DIR = "$HOME/dev";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user