added stuff

This commit is contained in:
Charlie Root 2024-04-09 23:11:33 +02:00
commit 9d0ebdfbd0
907 changed files with 70990 additions and 0 deletions

View file

@ -0,0 +1,7 @@
{
imports = [
./system
];
system.nixos.tags = ["graphical"];
}

View file

@ -0,0 +1,8 @@
{
imports = [
./security
./services
./environment.nix
];
}

View file

@ -0,0 +1,6 @@
{
environment.variables = {
# open links with the default browser
BROWSER = "firefox";
};
}

View file

@ -0,0 +1,5 @@
{
imports = [
./polkit.nix
];
}

View file

@ -0,0 +1,4 @@
{
# enable polkit for privilege escalation
security.polkit.enable = true;
}

View file

@ -0,0 +1,7 @@
{
imports = [
./login
./xserver.nix
];
}

View file

@ -0,0 +1,8 @@
{
imports = [
./greetd.nix
./logind.nix
./pam.nix
./session.nix
];
}

View file

@ -0,0 +1,53 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkIf;
inherit (lib.strings) concatStringsSep;
inherit (lib.meta) getExe;
env = config.modules.usrEnv;
sys = config.modules.system;
# make desktop session paths available to greetd
sessionData = config.services.xserver.displayManager.sessionData.desktops;
sessionPaths = concatStringsSep ":" [
"${sessionData}/share/xsessions"
"${sessionData}/share/wayland-sessions"
];
initialSession = {
user = "${sys.mainUser}";
command = "${env.desktop}";
};
defaultSession = {
user = "greeter";
command = concatStringsSep " " [
(getExe pkgs.greetd.tuigreet)
"--time"
"--remember"
"--remember-user-session"
"--asterisks"
"--sessions '${sessionPaths}'"
];
};
in {
services.greetd = {
enable = true;
vt = 2;
restart = !sys.autoLogin;
# <https://man.sr.ht/~kennylevinsen/greetd/>
settings = {
# default session is what will be used if no session is selected
# in this case it'll be a TUI greeter
default_session = defaultSession;
# initial session
initial_session = mkIf sys.autoLogin initialSession;
};
};
}

View file

@ -0,0 +1,12 @@
{
# despite being under logind, this has nothing to do with login
# it's about power management
services.logind = {
lidSwitch = "suspend-then-hibernate";
lidSwitchExternalPower = "lock";
extraConfig = ''
HandlePowerKey=suspend-then-hibernate
HibernateDelaySec=3600
'';
};
}

View file

@ -0,0 +1,25 @@
{
# unlock GPG keyring on login
security.pam.services = let
gnupg = {
enable = true;
noAutostart = true;
storeOnly = true;
};
in {
login = {
enableGnomeKeyring = true;
inherit gnupg;
};
greetd = {
enableGnomeKeyring = true;
inherit gnupg;
};
tuigreet = {
enableGnomeKeyring = true;
inherit gnupg;
};
};
}

View file

@ -0,0 +1,27 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkIf;
env = config.modules.usrEnv;
in {
# adding dessktop items to the environment is generally handled by the programs' respective
# nixos modules, however, to unify the desktop interface I prefer handling them manually
# and ignoring the nixos modules entirely.
services.xserver.displayManager = {
startx.enable = true;
session = [
(mkIf env.desktops.i3.enable {
name = "i3wm";
manage = "desktop";
start = ''
${pkgs.xorg.xinit}/bin/startx ${pkgs.i3-rounded}/bin/i3 -- vt2 &
waitPID=$!
'';
})
];
};
}

View file

@ -0,0 +1,9 @@
{
config = {
services.xserver = {
enable = true;
displayManager.gdm.enable = false;
displayManager.lightdm.enable = false;
};
};
}

View file

@ -0,0 +1,7 @@
{
imports = [
./system
];
system.nixos.tags = ["headless"];
}

View file

@ -0,0 +1,9 @@
{
imports = [
./environment.nix
./systemd.nix
./documentation.nix
./fonts.nix
./xdg.nix
];
}

View file

@ -0,0 +1,17 @@
{lib, ...}: let
inherit (lib) mkForce mapAttrs;
in {
documentation = mapAttrs (_: mkForce) {
enable = false;
dev.enable = false;
doc.enable = false;
info.enable = false;
nixos.enable = false;
man = {
enable = false;
generateCaches = false;
man-db.enable = false;
mandoc.enable = false;
};
};
}

View file

@ -0,0 +1,45 @@
{
self,
config,
pkgs,
lib,
...
}: {
environment = {
# normally we wouldn't need any Xlibs on a headless server but for whatever reason
# this affects whether or not some programs can build - such as pipewire
# noXlibs = true;
# print the URL instead on servers
variables.BROWSER = "echo";
interactiveShellInit = let
exec = package: program: "${package}/bin/${program}";
util = exec pkgs.coreutils;
uptime = exec pkgs.procps "uptime";
grep = exec pkgs.gnugrep "grep";
countUsers = ''${util "who"} -q | ${util "head"} -n1 | ${util "tr"} ' ' \\n | ${util "uniq"} | ${util "wc"} -l'';
countSessions = ''${util "who"} -q | ${util "head"} -n1 | ${util "wc"} -w'';
in ''
(
# Get the common color codes from lib
${toString lib.common.shellColors}
# Color accent to use in any primary text
CA=$PURPLE
CAB=$BPURPLE
echo
echo -e " ''${BWHITE}Welcome back''${CO}"
echo " "
echo -e " ''${BWHITE}Hostname......:''${CAB} ${config.networking.hostName}''${CO}"
echo -e " ''${BWHITE}OS Version....:''${CO} NixOS ''${CAB}${config.system.nixos.version}''${CO}"
echo -e " ''${BWHITE}Configuration.:''${CO} ''${CAB}${self.rev or "\${BRED}()\${CO}\${BWHITE} Dirty"}''${CO}"
echo -e " ''${BWHITE}Uptime........:''${CO} $(${uptime} -p | ${util "cut"} -d ' ' -f2- | GREP_COLORS='mt=01;35' ${grep} --color=always '[0-9]*')"
echo -e " ''${BWHITE}SSH Logins....:''${CO} There are currently ''${CAB}$(${countUsers})''${CO} users logged in on ''${CAB}$(${countSessions})''${CO} sessions"
echo
)
'';
};
}

View file

@ -0,0 +1,5 @@
{lib, ...}: {
# we don't need fontconfig on a server
# since there are no fonts to be configured outside the console
fonts.fontconfig.enable = lib.mkDefault false;
}

View file

@ -0,0 +1,5 @@
{
# a headless system shoudld not mount any removable media
# without explicit user action
services.udisks2.enable = false;
}

View file

@ -0,0 +1,29 @@
{
# https://github.com/numtide/srvos/blob/main/nixos/server/default.nix
systemd = {
# given that our systems are headless, emergency mode is useless.
# we prefer the system to attempt to continue booting so
# that we can hopefully still access it remotely.
enableEmergencyMode = false;
# For more detail, see:
# https://0pointer.de/blog/projects/watchdog.html
watchdog = {
# systemd will send a signal to the hardware watchdog at half
# the interval defined here, so every 10s.
# If the hardware watchdog does not get a signal for 20s,
# it will forcefully reboot the system.
runtimeTime = "20s";
# Forcefully reboot if the final stage of the reboot
# hangs without progress for more than 30s.
# For more info, see:
# https://utcc.utoronto.ca/~cks/space/blog/linux/SystemdShutdownWatchdog
rebootTime = "30s";
};
sleep.extraConfig = ''
AllowSuspend=no
AllowHibernation=no
'';
};
}

View file

@ -0,0 +1,11 @@
{lib, ...}: let
inherit (lib) mkForce mapAttrs;
in {
xdg = mapAttrs (_: mkForce) {
sounds.enable = false;
mime.enable = false;
menus.enable = false;
icons.enable = false;
autostart.enable = false;
};
}

View file

@ -0,0 +1,8 @@
{
imports = [
./image
./system
];
system.nixos.tags = ["iso-image"];
}

View file

@ -0,0 +1,71 @@
{
modulesPath,
self,
config,
pkgs,
lib,
...
}: let
inherit (lib) mkImageMediaOverride;
in {
imports = [
"${modulesPath}/installer/cd-dvd/iso-image.nix"
# make sure our installer can detect and interact with all hardware that is supported in Nixpkgs
# this loads basically every hardware related kernel module
"${modulesPath}/profiles/all-hardware.nix"
];
# the ISO image must be completely immutable in the sense that we do not
# want the user to be able modify the ISO image after booting into it
# the below option will disable rebuild switches (i.e nixos-rebuild switch)
system.switch.enable = false;
isoImage = let
# hostname will be set as a "top-level" attribute in hosts.nix, per-host.
# therefore we can use the networking.hostName to get the hostname of the live
# system without defining it explicitly in the system-agnostic ISO role module
hostname = config.networking.hostName or "nixos";
# if the system is built from a git repository, we want to include the git revision
# in the ISO name. if the tree is dirty, we use the term "dirty" to make it explicit
rev = self.shortRev or "dirty";
# the format of the iso will always be uniform:
# $hostname-$release-$rev-$arch
# therefore we can set it once to avoid repetition later on
name = "${hostname}-${config.system.nixos.release}-${rev}-${pkgs.stdenv.hostPlatform.uname.processor}";
in {
# this will cause the resulting .iso file to be named as follows:
# $hostname-$release-$rev-$arch.iso
isoName = mkImageMediaOverride "${name}.iso";
# this will cause the label or volume ID of the generated ISO image to be as follows:
# $hostname-$release-$rev-$arch
# volumeID is used is used by stage 1 of the boot process, so it must be distintctive
volumeID = mkImageMediaOverride "${name}";
# maximum compression, in exchange for build speed
squashfsCompression = "zstd -Xcompression-level 10"; # default uses gzip
# ISO image should be an EFI-bootable volume
makeEfiBootable = true;
# ISO image should be bootable from USB
# FIXME: the module decription is as follows:
# "Whether the ISO image should be bootable from CD as well as USB."
# is this supposed to make the ISO image bootable from *CD* instead of USB?
makeUsbBootable = true;
# my module system already contains an option to add memtest86+
# to the boot menu at will but in case our system is unbootable
# lets include memtest86+ in the ISO image
# so that we may test the memory of the system
# exclusively from the ISO image
contents = [
{
source = pkgs.memtest86plus + "/memtest.bin";
target = "boot/memtest.bin";
}
];
};
}

View file

@ -0,0 +1,37 @@
{
pkgs,
lib,
...
}: let
inherit (lib.modules) mkForce;
in {
boot = {
# use the latest Linux kernel
kernelPackages = pkgs.linuxPackages_latest;
# talk to me kernel
kernelParams = lib.mkAfter ["noquiet"];
# no need for systemd in the initrd stage on an installation media
# being put in to recovery mode, or having systemd in stage one is
# entirely pointless
initrd.systemd = {
enable = lib.mkImageMediaOverride false;
emergencyAccess = lib.mkImageMediaOverride true;
};
# Needed for https://github.com/NixOS/nixpkgs/issues/58959
# tl;dr: ZFS is problematic and we don't want it
supportedFilesystems = mkForce [
"btrfs"
"vfat"
"f2fs"
"xfs"
"ntfs"
"cifs"
];
# disable software RAID
swraid.enable = mkForce false;
};
}

View file

@ -0,0 +1,13 @@
{
imports = [
./misc
./services
./boot.nix
./environment.nix
./hardware.nix
./networking.nix
./nix.nix
./users.nix
];
}

View file

@ -0,0 +1,46 @@
{
inputs,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkForce;
in {
environment = {
# our installer is a minimal, TUI-only environment. I don't find any
# good reason to keep X11 libs around while we will not be depending
# on any GUI frameworks.
noXlibs = true;
# 24.04 has brought in a stub-ld that will throw a warning if you try to run a
# dynamically linked binary. This is an installer, so we probably won't try to run
# dynamically linked binaries on this system. Besides, it's annoying.
stub-ld.enable = mkForce false;
# NixOS bundles a few packages by default
# it's not too large of a list, but I don't need it and I prefer
# my system containing only the packages I've declared.
defaultPackages = mkForce [];
# packages I might want on an installer environment
systemPackages = with pkgs; [
gitMinimal
curl
wget
pciutils
lshw
rsync
nixos-install-tools
];
etc = {
# link a copy of our nixpkgs input as the nixpkgs channel
"nix/flake-channels/nixpkgs".source = inputs.nixpkgs;
# fix an annoying warning
"mdadm.conf".text = ''
MAILADDR root
'';
};
};
}

View file

@ -0,0 +1,6 @@
{
# provide all hardware drivers, including proprietary ones
hardware = {
enableRedistributableFirmware = true;
};
}

View file

@ -0,0 +1,10 @@
{pkgs, ...}: {
# console locale
console = let
variant = "u24n";
in {
# hidpi terminal font
font = "${pkgs.terminus_font}/share/consolefonts/ter-${variant}.psf.gz";
keyMap = "trq";
};
}

View file

@ -0,0 +1,6 @@
{
imports = [
./console.nix
./sound.nix
];
}

View file

@ -0,0 +1,4 @@
{
# disable sound related programs
sound.enable = false;
}

View file

@ -0,0 +1,20 @@
{
pkgs,
lib,
...
}: let
inherit (lib.modules) mkForce;
in {
networking.networkmanager = {
enable = true;
plugins = mkForce [];
};
networking.wireless.enable = mkForce false;
# Enable SSH in the boot process.
systemd.services.sshd.wantedBy = mkForce ["multi-user.target"];
users.users.root.openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHRDg2lu1rXKP4OfyghP17ZVL2csnyJEJcy9Km3LQm4r notashelf@enyo"
];
}

View file

@ -0,0 +1,12 @@
{
nix = {
settings = {
experimental-features = ["nix-command" "flakes" "repl-flake"];
log-lines = 30;
warn-dirty = false;
http-connections = 50;
accept-flake-config = true;
auto-optimise-store = true;
};
};
}

View file

@ -0,0 +1,11 @@
{
# attempt to fix "too many open files"
security.pam.loginLimits = [
{
domain = "*";
item = "nofile";
type = "-";
value = "65536";
}
];
}

View file

@ -0,0 +1,5 @@
{
imports = [
./openssh.nix
];
}

View file

@ -0,0 +1,88 @@
{
# Hardened SSH configuration
services.openssh = {
extraConfig = ''
AllowTcpForwarding no
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com
PermitTunnel no
'';
settings = {
Ciphers = [
"aes256-gcm@openssh.com"
"aes256-ctr,aes192-ctr"
"aes128-ctr"
"aes128-gcm@openssh.com"
"chacha20-poly1305@openssh.com"
];
KbdInteractiveAuthentication = false;
KexAlgorithms = [
"curve25519-sha256"
"curve25519-sha256@libssh.org"
"diffie-hellman-group16-sha512"
"diffie-hellman-group18-sha512"
"sntrup761x25519-sha512@openssh.com"
];
Macs = [
"hmac-sha2-512-etm@openssh.com"
"hmac-sha2-256-etm@openssh.com"
"umac-128-etm@openssh.com"
];
X11Forwarding = false;
};
};
# Client side SSH configuration
programs.ssh = {
ciphers = [
"aes256-gcm@openssh.com"
"aes256-ctr,aes192-ctr"
"aes128-ctr"
"aes128-gcm@openssh.com"
"chacha20-poly1305@openssh.com"
];
hostKeyAlgorithms = [
"ssh-ed25519"
"ssh-ed25519-cert-v01@openssh.com"
"sk-ssh-ed25519@openssh.com"
"sk-ssh-ed25519-cert-v01@openssh.com"
"rsa-sha2-512"
"rsa-sha2-512-cert-v01@openssh.com"
"rsa-sha2-256"
"rsa-sha2-256-cert-v01@openssh.com"
];
kexAlgorithms = [
"curve25519-sha256"
"curve25519-sha256@libssh.org"
"diffie-hellman-group16-sha512"
"diffie-hellman-group18-sha512"
"sntrup761x25519-sha512@openssh.com"
];
knownHosts = {
github-rsa = {
hostNames = ["github.com"];
publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=";
};
github-ed25519 = {
hostNames = ["github.com"];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl";
};
gitlab-rsa = {
hostNames = ["gitlab.com"];
publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9";
};
gitlab-ed25519 = {
hostNames = ["gitlab.com"];
publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf";
};
};
macs = [
"hmac-sha2-512-etm@openssh.com"
"hmac-sha2-256-etm@openssh.com"
"umac-128-etm@openssh.com"
];
};
}

View file

@ -0,0 +1,11 @@
{
users.extraUsers.root.password = "";
users.users.nixos = {
uid = 1000;
password = "nixos";
description = "default";
isNormalUser = true;
extraGroups = ["wheel"];
};
}

View file

@ -0,0 +1,7 @@
{
imports = [
./system
];
system.nixos.tags = ["laptop"];
}

View file

@ -0,0 +1,7 @@
{
imports = [
./power
./touchpad.nix
];
}

View file

@ -0,0 +1,75 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf mkDefault;
dev = config.modules.device;
acceptedTypes = ["laptop" "hybrid"];
in {
imports = [./monitor.nix];
config = mkIf (builtins.elem dev.type acceptedTypes) {
hardware.acpilight.enable = true;
environment.systemPackages = with pkgs; [
acpi
powertop
];
services = {
# handle ACPI events
acpid.enable = true;
# allows changing system behavior based upon user-selected power profiles
power-profiles-daemon.enable = true;
# temperature target on battery
undervolt = {
tempBat = 65; # deg C
package = pkgs.undervolt;
};
# superior power management
auto-cpufreq = {
enable = true;
settings = let
MHz = x: x * 1000;
in {
battery = {
governor = "powersave";
scaling_min_freq = mkDefault (MHz 1200);
scaling_max_freq = mkDefault (MHz 1800);
turbo = "never";
};
charger = {
governor = "performance";
scaling_min_freq = mkDefault (MHz 1800);
scaling_max_freq = mkDefault (MHz 3800);
turbo = "auto";
};
};
};
# DBus service that provides power management support to applications.
upower = {
enable = true;
percentageLow = 15;
percentageCritical = 5;
percentageAction = 3;
criticalPowerAction = "Hibernate";
};
};
boot = {
kernelModules = ["acpi_call"];
extraModulePackages = with config.boot.kernelPackages; [
acpi_call
cpupower
];
};
};
}

View file

@ -0,0 +1,40 @@
{
inputs',
pkgs,
lib,
...
}: let
inherit (builtins) readFile;
inherit (lib.modules) mkForce;
inherit (lib.strings) makeBinPath;
dependencies = with pkgs;
[
coreutils
power-profiles-daemon
inotify-tools
jaq
]
++ [
inputs'.hyprland.packages.hyprland
];
in {
config = {
# Power state monitor. Switches Power profiles based on charging state.
# Plugged in - performance
# Unplugged - power-saver
systemd.services."power-monitor" = {
description = "Power Monitoring Service";
environment.PATH = mkForce "/run/wrappers/bin:${makeBinPath dependencies}";
script = readFile ./scripts/power_monitor.sh;
serviceConfig = {
Type = "simple";
Restart = "on-failure";
};
wants = ["power-profiles-daemon.service"];
wantedBy = ["default.target"];
};
};
}

View file

@ -0,0 +1,69 @@
#!/usr/bin/env bash
BAT=$(echo /sys/class/power_supply/BAT*)
BAT_STATUS="$BAT/status"
BAT_CAP="$BAT/capacity"
AC_PROFILE="performance"
BAT_PROFILE="balanced"
# low and critical battery levels
LOW_BAT_PERCENT=25
CRIT_BAT_PERCENT=5
# how long to wait before suspending
SUSPEND_WAIT=60s
# define the wait & suspend function
wait_and_suspend() {
sleep "$SUSPEND_WAIT"
# check if we're still discharging
if [[ $(cat "$BAT_STATUS") == "Discharging" ]]; then
systemctl suspend
fi
}
# wait a while if needed
[[ -z $STARTUP_WAIT ]] || sleep "$STARTUP_WAIT"
# start the monitor loop
prev=0
while true; do
# read the current state
if [[ $(cat "$BAT_STATUS") == "Discharging" ]]; then
profile=$BAT_PROFILE
else
profile=$AC_PROFILE
fi
# set the new profile
if [[ $prev != "$profile" ]]; then
echo -en "Setting power profile to ${profile}\n"
powerprofilesctl set $profile
fi
prev=$profile
if [[ $(cat "$BAT_CAP") -le $LOW_BAT_PERCENT && $BAT_STATUS == "Discharging" ]]; then
notify-send --urgency=critical --hint=int:transient:1 --icon=battery_empty "Battery Low" \
"Consider plugging in."
for i in $(hyprctl instances -j | jaq ".[].instance" -r); do
hyprctl -i "$i" --batch 'keyword decoration:blur:enabled false; keyword animations:enabled false'
done
fi
if [[ $(cat "$BAT_CAP") -le $CRIT_BAT_PERCENT && $BAT_STATUS == "Discharging" ]]; then
notify-send --urgency=critical --hint=int:transient:1 --icon=battery_empty "Battery Critically Low" \
"Computer will suspend in 60 seconds."
wait_and_suspend &
fi
if [[ $(cat "$BAT_CAP") -gt $LOW_BAT_PERCENT && $BAT_STATUS == "Charging" ]]; then
for i in $(hyprctl instances -j | jaq ".[].instance" -r); do
hyprctl -i "$i" --batch 'keyword decoration:blur:enabled true; keyword animations:enabled true'
done
fi
# wait for the next power change event
inotifywait -qq "$BAT_STATUS" "$BAT_CAP"
done

View file

@ -0,0 +1,133 @@
let
MHz = x: x * 1000;
in {
config = {
services = {
tlp = {
enable = false;
settings = {
TLP_ENABLE = 1;
TLP_DEFAULT_MODE = "BAT";
# Timeout (in seconds) for the audio power saving mode (supports Intel HDA, AC97).
# A value of 1 is recommended for Linux desktop environments with PulseAudio,
# systems without PulseAudio may require 10. The value 0 disables power save.
SOUND_POWER_SAVE_ON_AC = 10;
SOUND_POWER_SAVE_ON_BAT = 10;
# SOUND_POWER_SAVE_CONTROLLER = "Y";
START_CHARGE_THRESH_BAT0 = 80;
STOP_CHARGE_THRESH_BAT0 = 95;
RESTORE_THRESHOLDS_ON_BAT = 1;
# battery care drivers
# NATACPI_ENABLE = 1;
# TPACPI_ENABLE = 1;
# TPSMAPI_ENABLE = 1;
# DISK_DEVICES = "nvme0n1 mmcblk0";
# DISK_APM_LEVEL_ON_AC = "254 254";
# DISK_APM_LEVEL_ON_BAT = "128 128";
# DISK_IDLE_SECS_ON_AC=0;
DISK_IDLE_SECS_ON_BAT = 5;
# Timeout (in seconds) for writing unsaved data in file system buffers to disk.
# MAX_LOST_WORK_SECS_ON_AC = 15;
# MAX_LOST_WORK_SECS_ON_BAT = 60;
# RADEON_DPM_PERF_LEVEL_ON_AC = "auto";
RADEON_DPM_PERF_LEVEL_ON_BAT = "low";
# RADEON_DPM_STATE_ON_AC = "performance";
# RADEON_DPM_STATE_ON_BAT = "battery";
RADEON_POWER_PROFILE_ON_AC = "high";
RADEON_POWER_PROFILE_ON_BAT = "low";
# NMI_WATCHDOG = 0;
# Sets Wi-Fi power saving mode. Adapter support depends on kernel and driver.
# WIFI_PWR_ON_AC = "off";
# WIFI_PWR_ON_BAT = "on";
# WOL_DISABLE = "Y";
# Select the platform profile to control system operating characteristics
# around power/performance levels, thermal and fan speed.
# PLATFORM_PROFILE_ON_AC = "performance";
# PLATFORM_PROFILE_ON_BAT = "low-power";
# <https://www.kernel.org/doc/html/latest/admin-guide/pm/cpufreq.html>
CPU_SCALING_GOVERNOR_ON_AC = "schedutil";
CPU_SCALING_GOVERNOR_ON_BAT = "powersave";
CPU_SCALING_MIN_FREQ_ON_AC = MHz 1400;
CPU_SCALING_MAX_FREQ_ON_AC = MHz 1700;
CPU_SCALING_MIN_FREQ_ON_BAT = MHz 1400;
CPU_SCALING_MAX_FREQ_ON_BAT = MHz 1600;
CPU_BOOST_ON_AC = 1;
CPU_BOOST_ON_BAT = 0;
# SCHED_POWERSAVE_ON_AC = 0;
# SCHED_POWERSAVE_ON_BAT = 1;
# Restores radio device state (builtin Bluetooth, Wi-Fi, WWAN) from previous shutdown on boot.
# RESTORE_DEVICE_STATE_ON_STARTUP = 0;
DEVICES_TO_DISABLE_ON_STARTUP = "bluetooth wwan";
DEVICES_TO_ENABLE_ON_STARTUP = "wifi";
# DEVICES_TO_DISABLE_ON_SHUTDOWN = "bluetooth wifi wwan";
# DEVICES_TO_ENABLE_ON_SHUTDOWN = "bluetooth wifi wwan";
# has precedence
DEVICES_TO_ENABLE_ON_AC = "";
DEVICES_TO_DISABLE_ON_BAT = "";
DEVICES_TO_DISABLE_ON_BAT_NOT_IN_USE = "bluetooth wwan";
DEVICES_TO_DISABLE_ON_LAN_CONNECT = "wwan";
DEVICES_TO_DISABLE_ON_WIFI_CONNECT = "";
DEVICES_TO_DISABLE_ON_WWAN_CONNECT = "wifi";
DEVICES_TO_ENABLE_ON_LAN_DISCONNECT = "wifi";
DEVICES_TO_ENABLE_ON_WIFI_DISCONNECT = "";
DEVICES_TO_ENABLE_ON_WWAN_DISCONNECT = "";
DEVICES_TO_ENABLE_ON_DOCK = "wifi bluetooth";
# DEVICES_TO_DISABLE_ON_DOCK = "";
DEVICES_TO_ENABLE_ON_UNDOCK = "";
DEVICES_TO_DISABLE_ON_UNDOCK = "bluetooth";
# RUNTIME_PM_ON_AC = "on";
# RUNTIME_PM_ON_BAT = "auto";
# RUNTIME_PM_DENYLIST = "11:22.3 44:55.6";
RUNTIME_PM_DRIVER_DENYLIST = "mei_me nouveau radeon psmouse";
# RUNTIME_PM_ENABLE="11:22.3";
# RUNTIME_PM_DISABLE="44:55.6";
# PCIE_ASPM_ON_AC = "default";
PCIE_ASPM_ON_BAT = "powersupersave";
# USB_AUTOSUSPEND = 1;
# USB_DENYLIST = "1111:2222 3333:4444";
# USB_EXCLUDE_AUDIO = 1;
# USB_EXCLUDE_BTUSB = 1;
# USB_EXCLUDE_PHONE = 1;
# USB_EXCLUDE_PRINTER = 1;
# USB_EXCLUDE_WWAN = 0;
# USB_ALLOWLIST="5555:6666 7777:8888";
# USB_AUTOSUSPEND_DISABLE_ON_SHUTDOWN = 0;
};
};
};
};
}

View file

@ -0,0 +1,27 @@
{
config = {
services = {
# Input settings for libinput
xserver.libinput = {
# enable libinput
enable = true;
# disable mouse acceleration
mouse = {
accelProfile = "flat";
accelSpeed = "0";
middleEmulation = false;
};
# touchpad settings
touchpad = {
naturalScrolling = true;
tapping = true;
clickMethod = "clickfinger";
horizontalScrolling = false;
disableWhileTyping = true;
};
};
};
};
}

View file

@ -0,0 +1,7 @@
{
imports = [
./system
];
system.nixos.tags = ["microvm"];
}

View file

@ -0,0 +1,7 @@
{
imports = [
./nix
./os
./securiy
];
}

View file

@ -0,0 +1,12 @@
{pkgs, ...}: {
nix = {
settings.trusted-users = ["admin"];
package = pkgs.nixUnstable;
keep-outputs = true;
keep-derivations = true;
extra-experimental-features = [
"nix-command"
"flakes"
];
};
}

View file

@ -0,0 +1,9 @@
{
imports = [
./programs
./users
./environment.nix
./networking.nix
];
}

View file

@ -0,0 +1,31 @@
{pkgs, ...}: {
time.timeZone = "UTC";
i18n.defaultLocale = "en_US.UTF-8";
console = {
font = "ter-v32n";
packages = [pkgs.terminus-font];
};
environment = {
shells = with pkgs; [bash zsh];
systemPackages = with pkgs; [
vim
git
killall
bind.dnsutils
tcpdump
nmap
usbutils
wget
tmux
direnv
nix-direnv
sops
rage
ssh-to-age
pwgen
w3m
];
};
}

View file

@ -0,0 +1,17 @@
{lib, ...}: {
systemd.network.enable = true;
# Enable the OpenSSH daemon.
services.openssh.enable = true;
networking = {
useDHCP = false;
networkmanager.enable = false;
firewall = {
enable = true;
allowPing = lib.mkForce false;
allowedTCPPorts = lib.mkForce [];
allowedUDPPorts = lib.mkForce [];
};
};
}

View file

@ -0,0 +1,8 @@
{
imports = [
./git.nix
./neovim.nix
./tmux.nix
./zsh.nix
];
}

View file

@ -0,0 +1 @@
{programs.git.enable = true;}

View file

@ -0,0 +1,23 @@
{
programs.neovim = {
enable = true;
viAlias = true;
vimAlias = true;
configure.customRC = ''
syntax enable
set noexpandtab
set shiftwidth=2
set tabstop=2
set cindent
set smartindent
set autoindent
set foldmethod=syntax
nmap <F2> zA
nmap <F3> zR
nmap <F4> zM
'';
};
}

View file

@ -0,0 +1,20 @@
{
programs.tmux = {
enable = true;
baseIndex = 1;
clock24 = true;
historyLimit = 10000;
terminal = "tmux-256color";
extraConfig = ''
unbind C-b
set-option -g prefix C-a
bind-key C-a last-window
set-option -g set-titles on
set-option -g set-titles-string '#H:#S.#I.#P #W #T'
setw -g monitor-activity on
set-option -g status-justify left
set-option -g status-bg yellow
set-option -g status-fg black
'';
};
}

View file

@ -0,0 +1,11 @@
{
environment.pathsToLink = ["/share/zsh"];
programs.zsh = {
enable = true;
enableCompletion = true;
autosuggestions = {
enable = true;
async = true;
};
};
}

View file

@ -0,0 +1,7 @@
{
users.users.admin = {
isNormalUser = true;
extraGroups = ["wheel"];
openssh.authorizedKeys.keys = [];
};
}

View file

@ -0,0 +1,5 @@
{
imports = [
./admin.nix
];
}

View file

@ -0,0 +1,13 @@
{
security.sudo.extraRules = [
{
users = ["admin"];
commands = [
{
command = "ALL";
options = ["SETENV" "NOPASSWD"];
}
];
}
];
}

View file

@ -0,0 +1,7 @@
{
imports = [
./system
];
system.nixos.tags = ["server"];
}

View file

@ -0,0 +1,5 @@
{
imports = [
./services
];
}

View file

@ -0,0 +1,89 @@
{
inputs,
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services.bincache.atticd;
domain = "cache" + config.networking.domain;
inherit (cfg.settings) host port;
in {
imports = [inputs.atticd.nixosModules.atticd];
config = mkIf cfg.enable {
environment.systemPackages = [pkgs.attic-client];
networking.firewall.allowedTCPPorts = [port];
users = {
groups.atticd = {};
users."atticd" = {
isSystemUser = true;
group = "atticd";
};
};
systemd.services.atticd = {
serviceConfig.DynamicUser = lib.mkForce false;
};
services = {
atticd = {
enable = true;
credentialsFile = config.age.secrets.attic-env.path;
user = "atticd";
group = "atticd";
settings = {
listen = "${host}:${toString port}"; # this listens ONLY locally
database.url = "postgresql:///atticd?host=/run/postgresql";
allowed-hosts = ["${domain}"];
api-endpoint = "https://${domain}/";
require-proof-of-possession = false;
/*
storage = {
type = "s3";
region = "helios";
bucket = "attic-cache";
endpoint = "https://s3.notashelf.dev";
};
*/
chunking = let
KB = x: x * 1024;
in {
nar-size-threshold = KB 64;
min-size = KB 16;
avg-size = KB 64;
max-size = KB 256;
};
garbage-collection = {
interval = "24 hours";
default-retention-period = "6 weeks";
};
};
};
nginx.virtualHosts."${domain}" = {
extraConfig = ''
client_max_body_size 0;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
'';
locations."/" = {
recommendedProxySettings = true;
proxyPass = "http://${host}:${toString port}";
};
};
};
};
}

View file

@ -0,0 +1,6 @@
{
imports = [
./atticd.nix
./harmonia.nix
];
}

View file

@ -0,0 +1,60 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services.bincache.harmonia;
inherit (cfg.settings) port host;
in {
config = mkIf cfg.enable {
users = {
groups.harmonia = {};
users.harmonia = {
isSystemUser = true;
createHome = true;
group = "harmonia";
home = "/srv/storage/harmonia";
};
};
services = {
harmonia = {
enable = true;
# NOTE: generated via
# $ nix-store --generate-binary-cache-key cache.domain.tld-1 /var/lib/secrets/harmonia.secret /var/lib/secrets/harmonia.pub
signKeyPath = config.age.secrets.harmonia-privateKey.path;
settings = {
# default ip:hostname to bind to
bind = "${host}:${toString port}";
};
};
};
nix.settings.allowed-users = ["harmonia"];
services.nginx = {
virtualHosts."cache.notashelf.dev" =
{
locations."/".extraConfig = ''
proxy_pass http://127.0.0.1:${toString port};
proxy_set_header Host $host;
proxy_redirect http:// https://;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
zstd on;
zstd_types application/x-nix-archive;
'';
quic = true;
}
// lib.sslTemplate;
};
};
}

View file

@ -0,0 +1,9 @@
{
imports = [
./mongodb.nix
./postgresql.nix
./mysql.nix
./redis.nix
./garage.nix
];
}

View file

@ -0,0 +1,118 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.database.garage.enable {
networking.firewall.allowedTCPPorts = [3900 3901 3903];
environment.systemPackages = [
pkgs.garage
];
systemd = let
sc = config.systemd.services.garage.serviceConfig;
gc = config.services.garage.settings;
in {
tmpfiles.rules = [
"d /srv/storage/garage 0755 ${sc.User} ${sc.Group}"
"d '${gc.data_dir}' 0700 ${sc.User} ${sc.Group} - -"
"d '${gc.metadata_dir}' 0700 ${sc.User} ${sc.Group} - -"
];
services.garage = {
# this lets custom data directory work by having a real user own the service process
# the user and its group need to be created in the users section
serviceConfig = {
User = "garage";
Group = "garage";
ReadWritePaths = [gc.data_dir gc.metadata_dir];
RequiresMountsFor = [gc.data_dir];
DynamicUser = false;
PrivateTmp = true;
ProtectSystem = true;
};
environment = {
RUST_LOG = "debug";
};
};
};
users = {
groups.garage = {};
users.garage = {
isSystemUser = true;
createHome = false;
group = "garage";
};
};
services = {
garage = {
enable = true;
package = pkgs.garage;
environmentFile = config.age.secrets.garage-env.path;
settings = {
metadata_dir = "/srv/storage/garage/meta";
data_dir = "/srv/storage/garage/data";
metadata_fsync = false; # synchronous mode for the database engine
db_engine = "lmdb";
replication_mode = "none";
compression_level = -1;
# For inter-node comms
rpc_bind_addr = "[::]:3901";
rpc_secret_file = config.age.secrets.garage-env.path;
# rpc_public_addr = "127.0.0.1:3901";
# Standard S3 api endpoint
s3_api = {
s3_region = "helios";
api_bind_addr = "[::]:3900";
};
# Static file serve endpoint
/*
s3_web = {
bind_addr = "[::1]:3902";
root_domain = "s3.notashelf.dev";
index = "index.html";
};
"k2v_api" = {
"api_bind_addr" = "[::1]:3904";
};
*/
# Admin api endpoint
admin = {
api_bind_addr = "[::]:3903";
};
};
};
nginx.virtualHosts."s3.notashelf.dev" =
{
locations."/".proxyPass = "http://127.0.0.1:3900";
extraConfig = ''
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
# Disable buffering to a temporary file.
proxy_max_temp_file_size 0;
'';
}
// lib.sslTemplate;
};
};
}

View file

@ -0,0 +1,24 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.database.mongodb.enable {
services.mongodb = {
enable = true;
package = pkgs.mongodb;
enableAuth = true;
initialRootPassword = config.age.secrets.mongodb-secret.path;
#bind_ip = "0.0.0.0";
extraConfig = ''
operationProfiling.mode: all
'';
};
};
}

View file

@ -0,0 +1,29 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.database.mysql.enable {
services.mysql = {
enable = true;
package = pkgs.mariadb;
# databases and users
ensureDatabases = ["mkm"];
ensureUsers = [
{
name = "mkm";
ensurePermissions = {
"mkm.*" = "ALL PRIVILEGES";
};
}
];
};
};
}

View file

@ -0,0 +1,143 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.database.postgresql.enable {
services.postgresql = {
enable = true;
package = pkgs.postgresql_15;
dataDir = "/srv/storage/postgresql/${config.services.postgresql.package.psqlSchema}";
ensureDatabases = [
"nextcloud"
"forgejo"
"grafana"
"vaultwarden"
"roundcube"
"headscale"
"atticd"
];
ensureUsers = [
{
name = "postgres";
ensureClauses = {
superuser = true;
login = true; # not implied by superuser
createrole = true;
createdb = true;
replication = true;
};
}
{
name = "forgejo";
ensureDBOwnership = true;
}
{
name = "grafana";
ensureDBOwnership = true;
}
{
name = "vaultwarden";
ensureDBOwnership = true;
}
{
name = "nextcloud";
ensureDBOwnership = true;
}
{
name = "roundcube";
ensureDBOwnership = true;
}
{
name = "headscale";
ensureDBOwnership = true;
}
{
name = "atticd";
ensureDBOwnership = true;
}
];
checkConfig = true;
enableTCPIP = false;
# http://pgconfigurator.cybertec.at/;
# https://git.darmstadt.ccc.de/maralorn/nixos-config/-/blob/master/nixos/roles/matrix-synapse/postgres-tuning.nix
settings = {
# Connectivity;
max_connections = 100;
superuser_reserved_connections = 3;
# Memory Settings;
shared_buffers = "1024 MB";
work_mem = "32 MB";
maintenance_work_mem = "320 MB";
huge_pages = "off";
effective_cache_size = "2 GB";
effective_io_concurrency = 100; # concurrent IO only really activated if OS supports posix_fadvise function;
random_page_cost = 1.25; # speed of random disk access relative to sequential access (1.0);
# Monitoring;
shared_preload_libraries = "pg_stat_statements,auto_explain"; # per statement resource usage stats & log explain statements for slow queries
track_io_timing = "on"; # measure exact block IO times;
track_functions = "pl"; # track execution times of pl-language procedures if any;
# Replication;
wal_level = "replica"; # consider using at least "replica";
max_wal_senders = 0;
synchronous_commit = "on";
# Checkpointing: ;
checkpoint_timeout = "15 min";
checkpoint_completion_target = 0.9;
max_wal_size = "1024 MB";
min_wal_size = "512 MB";
# WAL writing;
wal_compression = "on";
wal_buffers = -1; # auto-tuned by Postgres till maximum of segment size (16MB by default);
wal_writer_delay = "200ms";
wal_writer_flush_after = "1MB";
# Background writer;
bgwriter_delay = "200ms";
bgwriter_lru_maxpages = 100;
bgwriter_lru_multiplier = 2.0;
bgwriter_flush_after = 0;
# Parallel queries: ;
max_worker_processes = 6;
max_parallel_workers_per_gather = 3;
max_parallel_maintenance_workers = 3;
max_parallel_workers = 6;
parallel_leader_participation = "on";
# Advanced features ;
enable_partitionwise_join = "on";
enable_partitionwise_aggregate = "on";
jit = "on";
jit_above_cost = 100000;
jit_inline_above_cost = 150000;
jit_optimize_above_cost = 500000;
# log slow queries
log_min_duration_statement = 100;
"auto_explain.log_min_duration" = 100;
# logging configuration
log_connections = true;
log_statement = "all";
logging_collector = true;
log_disconnections = true;
log_destination = lib.mkForce "syslog";
};
};
};
}

View file

@ -0,0 +1,49 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.database.redis.enable {
services.redis = {
vmOverCommit = true;
servers = mkIf cfg.nextcloud.enable {
nextcloud = {
enable = true;
user = "nextcloud";
port = 0;
};
searxng = mkIf cfg.searxng.enable {
enable = true;
user = "searx";
port = 6370;
databases = 16;
logLevel = "debug";
requirePass = "searxng";
};
forgejo = mkIf cfg.forgejo.enable {
enable = true;
user = "forgejo";
port = 6371;
databases = 16;
logLevel = "debug";
requirePass = "forgejo";
};
mastodon = mkIf cfg.social.mastodon.enable {
enable = true;
user = "mastodon";
port = 6372;
databases = 16;
logLevel = "debug";
};
};
};
};
}

View file

@ -0,0 +1,27 @@
{
imports = [
# essentials
./databases # mysql, postgreqsl, redis and more
./nginx # base nginx webserver configuration
# other services
./bincache # atticd and harmonia
./monitoring # prometheus, grafana, loki and uptime-kuma
./networking # wireguard and headscale
./social # mastodon and matrix
./forgejo.nix # lightweight git service, fork of gitea
./forgejo-runner.nix # self-hosted runner for forgejo
./nextcloud.nix # cloud storage (not a backup solution)
./vaultwarden.nix # bitwarden compatible password manager
./mailserver.nix # nixos-mailserver setup
./jellyfin.nix # media server
./tor.nix # tor relay
./searxng.nix # searx search engine
./reposilite.nix # self-hosted maven repository
./elasticsearch.nix # elasticsearch
./kanidm.nix # kanidm identity management
# misc
./mkm.nix # holy fuck
];
}

View file

@ -0,0 +1,22 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.elasticsearch.enable {
services.elasticsearch = {
enable = true;
single_node = true;
cluster_name = "elasticsearch-${config.networking.hostName}";
extraConf = ''
xpack.security.enabled: false
'';
};
};
}

View file

@ -0,0 +1,74 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
cfg = config.modules.system.services;
# construct each runner using the mkRunner function
# you can pass additional configuration options in the instance submodule
# it'll be merged to the below configuration
mkRunner = {
name,
settings,
}:
{
enable = true;
inherit name;
url = "https://git.notashelf.dev";
# NOTE: changing (i.e adding or removing) labels causes your old registration token to expire
# make sure your labels are final before deploying
labels = [
"debian-latest:docker://node:18-bullseye"
"ubuntu-latest:docker://node:18-bullseye"
"act:docker://ghcr.io/catthehacker/ubuntu:act-latest"
#"native:host"
];
}
// settings;
in {
config = mkIf cfg.forgejo.enable {
users = {
groups.gitea-runner = {};
users.gitea-runner = {
isSystemUser = true;
createHome = true;
home = "/var/lib/gitea-runner";
group = "gitea-runner";
extraGroups = ["docker"];
};
};
services.gitea-actions-runner = {
package = pkgs.forgejo-actions-runner;
instances = {
"runner-01" = mkRunner {
name = "runner-01";
settings = {
tokenFile = config.age.secrets.forgejo-runner-token.path;
settings = {
capacity = 4;
container.network = "host";
cache.enabled = true;
};
# packages that'll be made available to the host
# when the runner is configured with a host execution label.
hostPackages = with pkgs; [
bash
curl
coreutils
wget
gitMinimal
];
};
};
};
};
};
}

View file

@ -0,0 +1,134 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
cfg = config.modules.system.services;
domain = "git.notashelf.dev";
inherit (cfg.forgejo.settings) port;
in {
config = mkIf cfg.forgejo.enable {
modules.system.services = {
nginx.enable = true;
database = {
redis.enable = true;
postgresql.enable = true;
};
};
networking.firewall.allowedTCPPorts = [
# make sure the service is reachable from outside
config.services.forgejo.settings.server.HTTP_PORT
config.services.forgejo.settings.server.SSH_PORT
];
users = {
users.git = {
isSystemUser = true;
createHome = false;
group = "git";
};
groups.git = {};
};
services = {
forgejo = {
enable = true;
package = pkgs.forgejo;
stateDir = "/srv/storage/forgejo/data";
mailerPasswordFile = config.age.secrets.mailserver-forgejo-secret.path;
lfs.enable = true;
settings = {
default.APP_NAME = "The Secret Shelf";
actions = {
ENABLED = true;
DEFAULT_ACTIONS_URL = "https://git.notashelf.dev";
};
other = {
SHOW_FOOTER_VERSION = false;
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false;
};
session = {
COOKIE_SECURE = true;
SAME_SITE = "strict";
};
server = {
PROTOCOL = "http+unix";
HTTP_PORT = port;
ROOT_URL = "https://${domain}";
DOMAIN = "${domain}";
DISABLE_ROUTER_LOG = true;
SSH_CREATE_AUTHORIZED_KEYS_FILE = true;
LANDING_PAGE = "/explore";
START_SSH_SERVER = true;
SSH_PORT = 2222;
SSH_LISTEN_PORT = 2222;
};
database = {
DB_TYPE = lib.mkForce "postgres";
HOST = "/run/postgresql";
NAME = "forgejo";
USER = "forgejo";
PASSWD = "forgejo";
};
cache = {
ENABLED = true;
ADAPTER = "redis";
HOST = "redis://:forgejo@localhost:6371";
};
mailer = mkIf config.modules.system.services.mailserver.enable {
ENABLED = true;
PROTOCOL = "smtps";
SMTP_ADDR = "mail.notashelf.dev";
USER = "git@notashelf.dev";
};
ui.DEFAULT_THEME = "arc-green";
attachment.ALLOWED_TYPES = "*/*";
service.DISABLE_REGISTRATION = true;
packages.ENABLED = false;
repository.PREFERRED_LICENSES = "MIT,GPL-3.0,GPL-2.0,LGPL-3.0,LGPL-2.1";
log.LEVEL = "Debug";
"repository.upload" = {
FILE_MAX_SIZE = 100;
MAX_FILES = 10;
};
};
# backup
dump = {
enable = true;
backupDir = "/srv/storage/forgejo/dump";
interval = "06:00";
type = "tar.zst";
};
};
nginx.virtualHosts."git.notashelf.dev" =
{
locations."/" = {
recommendedProxySettings = true;
proxyPass = "http://unix:/run/forgejo/forgejo.sock";
};
quic = true;
}
// lib.sslTemplate;
};
};
}

View file

@ -0,0 +1,38 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.jellyfin.enable {
modules.system.services = {
nginx.enable = true;
};
services = {
jellyfin = {
enable = true;
group = "jellyfin";
user = "jellyfin";
openFirewall = true;
};
nginx.virtualHosts. "fin.notashelf.dev" =
{
locations."/" = {
# TODO: the port is not customizable in the upstream service, PR nixpkgs
proxyPass = "http://127.0.0.1:${cfg.jellyfin.settings.port}/";
proxyWebsockets = true;
extraConfig = "proxy_pass_header Authorization;";
};
quic = true;
}
// lib.sslTemplate;
};
};
}

View file

@ -0,0 +1,47 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
domain = "idm.notashelf.dev";
certDir = config.security.acme.certs.${domain}.directory;
sys = config.modules.system;
cfg = sys.services;
inherit (cfg.kanidm.settings) host port;
in {
config = mkIf cfg.kanidm.enable {
services.kanidm = {
enableServer = true;
serverSettings = {
inherit domain;
origin = "https://${domain}";
bindaddress = "${host}:${toString port}";
trust_x_forward_for = true;
#tls_chain = "${certDir}/fullchain.pem";
#tls_key = "${certDir}/key.pem";
online_backup = {
path = "/srv/storage/kanidm/backups";
schedule = "0 0 * * *"; # Every day at midnight.
};
};
};
systemd.services.kanidm = {
after = ["acme-selfsigned-internal.${domain}.target"];
serviceConfig = {
SupplementaryGroups = [config.security.acme.certs.${domain}.group];
BindReadOnlyPaths = [certDir];
};
};
services.nginx.virtualHosts.${domain} = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "https://${host}:${toString port}";
};
};
}

View file

@ -0,0 +1,193 @@
{
config,
lib,
pkgs,
inputs,
...
}: let
inherit (lib) mkIf;
inherit (config.age) secrets;
sys = config.modules.system;
cfg = sys.services;
in {
imports = [
inputs.simple-nixos-mailserver.nixosModule
];
config = mkIf cfg.mailserver.enable {
# required for roundcube
networking.firewall.allowedTCPPorts = [80 443];
mailserver = {
enable = true;
mailDirectory = "/srv/storage/mail/vmail";
dkimKeyDirectory = "/srv/storage/mail/dkim";
sieveDirectory = "/srv/storage/mail/sieve";
openFirewall = true;
enableImap = true;
enableImapSsl = true;
enablePop3 = false;
enablePop3Ssl = false;
enableSubmission = false;
enableSubmissionSsl = true;
hierarchySeparator = "/";
localDnsResolver = false;
fqdn = "mail.notashelf.dev";
certificateScheme = "acme-nginx";
domains = ["notashelf.dev"];
loginAccounts = {
"raf@notashelf.dev" = {
hashedPasswordFile = secrets.mailserver-secret.path;
aliases = [
"me"
"raf"
"me@notashelf.dev"
"admin"
"admin@notashelf.dev"
"root"
"root@notashelf.dev"
"postmaster"
"postmaster@notashelf.dev"
];
};
"noreply@notashelf.dev" = {
aliases = ["noreply"];
hashedPasswordFile = secrets.mailserver-noreply-secret.path;
sendOnly = true;
sendOnlyRejectMessage = "";
};
"git@notashelf.dev" = mkIf cfg.forgejo.enable {
aliases = ["git" "forgejo"];
hashedPasswordFile = secrets.mailserver-forgejo-secret.path;
sendOnly = true;
sendOnlyRejectMessage = "";
};
"vaultwarden@notashelf.dev" = mkIf cfg.vaultwarden.enable {
aliases = ["vaultwarden" "vault"];
hashedPasswordFile = secrets.mailserver-vaultwarden-secret.path;
sendOnly = true;
sendOnlyRejectMessage = "";
};
"matrix@notashelf.dev" = mkIf cfg.social.matrix.enable {
aliases = ["matrix"];
hashedPasswordFile = secrets.mailserver-matrix-secret.path;
sendOnly = true;
sendOnlyRejectMessage = "";
};
"cloud@notashelf.dev" = mkIf cfg.nextcloud.enable {
aliases = ["cloud" "nextcloud"];
hashedPasswordFile = secrets.mailserver-cloud-secret.path;
sendOnly = true;
sendOnlyRejectMessage = "";
};
};
mailboxes = {
Archive = {
auto = "subscribe";
specialUse = "Archive";
};
Drafts = {
auto = "subscribe";
specialUse = "Drafts";
};
Sent = {
auto = "subscribe";
specialUse = "Sent";
};
Junk = {
auto = "subscribe";
specialUse = "Junk";
};
Trash = {
auto = "subscribe";
specialUse = "Trash";
};
};
fullTextSearch = {
enable = true;
# index new email as they arrive
autoIndex = true;
# this only applies to plain text attachments, binary attachments are never indexed
indexAttachments = true;
enforced = "body";
};
vmailUserName = "vmail";
vmailGroupName = "vmail";
useFsLayout = true;
};
services = {
roundcube = {
enable = true;
database.username = "roundcube";
maxAttachmentSize = 50;
dicts = with pkgs.aspellDicts; [en tr de];
# this is the url of the vhost, not necessarily the same as the fqdn of
# the mailserver
hostName = "webmail.notashelf.dev";
extraConfig = ''
$config['imap_host'] = array(
'tls://mail.notashelf.dev' => "NotAShelf's Mail Server",
'ssl://imap.gmail.com:993' => 'Google Mail',
);
$config['username_domain'] = array(
'mail.notashelf.dev' => 'notashelf.dev',
'mail.gmail.com' => 'gmail.com',
);
$config['x_frame_options'] = false;
# starttls needed for authentication, so the fqdn required to match
# the certificate
$config['smtp_host'] = "tls://${config.mailserver.fqdn}";
$config['smtp_user'] = "%u";
$config['smtp_pass'] = "%p";
$config['plugins'] = [ "carddav" ];
'';
};
postfix = {
dnsBlacklists = [
"all.s5h.net"
"b.barracudacentral.org"
"bl.spamcop.net"
"blacklist.woody.ch"
];
dnsBlacklistOverrides = ''
notashelf.dev OK
mail.notashelf.dev OK
127.0.0.0/8 OK
192.168.0.0/16 OK
'';
headerChecks = [
{
action = "IGNORE";
pattern = "/^User-Agent.*Roundcube Webmail/";
}
];
config = {
smtp_helo_name = config.mailserver.fqdn;
};
};
phpfpm.pools.roundcube.settings = {
"listen.owner" = config.services.nginx.user;
"listen.group" = config.services.nginx.group;
};
nginx.virtualHosts = {
"mail.notashelf.dev" = {quic = true;} // lib.sslTemplate;
"webmail.notashelf.dev" = {quic = true;} // lib.sslTemplate;
};
};
};
}

View file

@ -0,0 +1,92 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf getExe';
dev = config.modules.device;
cfg = config.modules.system.services;
acceptedTypes = ["server" "hybrid"];
in {
config = mkIf ((builtins.elem dev.type acceptedTypes) && cfg.miniflux.enable) {
# https://github.com/Gerg-L/nixos/blob/master/hosts/gerg-desktop/services/miniflux.nix
# miniflux setup courtesy of the funny frog man (he's bald)
systemd.services = {
miniflux = {
description = "Miniflux service";
wantedBy = ["multi-user.target"];
requires = ["miniflux-dbsetup.service"];
after = ["network.target" "postgresql.service" "miniflux-dbsetup.service"];
script = getExe' pkgs.miniflux "miniflux";
serviceConfig = {
User = "miniflux";
RuntimeDirectory = "miniflux";
RuntimeDirectoryMode = "0770";
EnvironmentFile = config.age.secrets.miniflux-env.path;
# Hardening
CapabilityBoundingSet = [""];
DeviceAllow = [""];
LockPersonality = true;
MemoryDenyWriteExecute = true;
PrivateDevices = true;
PrivateUsers = true;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX"];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = ["@system-service" "~@privileged"];
UMask = "0077";
};
environment = {
BASE_URL = "https://flux.notashelf.dev";
LISTEN_ADDR = "/run/miniflux/miniflux.sock";
DATABASE_URL = "user=miniflux host=/run/postgresql dbname=miniflux";
RUN_MIGRATIONS = "1";
CREATE_ADMIN = "1";
};
};
miniflux-dbsetup = {
description = "Miniflux database setup";
requires = ["postgresql.service"];
after = ["network.target" "postgresql.service"];
script = ''
${lib.getExe' config.services.postgresql.package "psql"} "miniflux" -c "CREATE EXTENSION IF NOT EXISTS hstore"
'';
serviceConfig = {
Type = "oneshot";
User = config.services.postgresql.superUser;
};
};
};
users = {
groups.miniflux = {
gid = 377;
};
users = {
miniflux = {
group = "miniflux";
extraGroups = ["postgres"];
isSystemUser = true;
uid = 377;
};
${config.services.nginx.user}.extraGroups = ["miniflux"];
};
};
};
}

View file

@ -0,0 +1,31 @@
{
inputs',
config,
lib,
...
}: let
inherit (lib) mkIf;
cfg = config.modules.system.services;
in {
config = mkIf cfg.mkm.enable {
virtualisation.oci-containers = {
backend = "podman";
containers = {
"mkm-web" = mkIf (config.networking.hostName == "helios") {
autoStart = true;
environmentFiles = [
config.age.secrets.mkm-web.path
];
ports = [
"3005:3005"
"3306:3306"
];
extraOptions = ["--network=host"];
image = "mkm-web";
imageFile = inputs'.mkm.packages.dockerImage;
};
};
};
};
}

View file

@ -0,0 +1,8 @@
{
imports = [
./grafana
./prometheus.nix
./loki.nix
./uptime-kuma.nix
];
}

View file

@ -0,0 +1,26 @@
{
lib,
pkgs,
...
}: let
loadDashboard = file:
lib.pipe file [
lib.importJSON
({dashboard, ...}: rec {
name = "provision-dashboard-${dashboard.uid}.json";
path = builtins.toFile name (builtins.toJSON dashboard);
})
];
dashboardsDir =
pkgs.linkFarm
"grafana-provisioning-dashboards"
(map loadDashboard (lib.filesystem.listFilesRecursive ./objects/dashboards));
in {
services.grafana.provision.dashboards.settings = {
providers = lib.singleton {
options.path = dashboardsDir;
allowUiUpdates = true;
};
};
}

View file

@ -0,0 +1,128 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
# imports = [./dashboards.nix];
config = mkIf cfg.monitoring.grafana.enable {
networking.firewall.allowedTCPPorts = [config.services.grafana.settings.server.http_port];
modules.system.services.database = {
postgresql.enable = true;
};
services = {
grafana = {
enable = true;
settings = {
server = {
http_addr = "0.0.0.0";
http_port = 3000;
root_url = "https://dash.notashelf.dev";
domain = "dash.notashelf.dev";
enforce_domain = true;
};
database = {
type = "postgres";
host = "/run/postgresql";
name = "grafana";
user = "grafana";
ssl_mode = "disable";
};
security = {
cookie_secure = true;
disable_gravatar = true;
};
analytics = {
reporting_enabled = false;
check_for_updates = false;
};
"auth.anonymous".enabled = false;
"auth.basic".enabled = false;
users = {
allow_signup = false;
};
};
provision = {
enable = true;
datasources.settings = {
datasources = [
(mkIf sys.services.monitoring.prometheus.enable {
name = "Prometheus";
type = "prometheus";
access = "proxy";
orgId = 1;
uid = "Y4SSG429DWCGDQ3R";
url = "http://127.0.0.1:${toString config.services.prometheus.port}";
isDefault = true;
version = 1;
editable = true;
jsonData = {
graphiteVersion = "1.1";
tlsAuth = false;
tlsAuthWithCACert = false;
};
})
(mkIf sys.services.monitoring.loki.enable {
name = "Loki";
type = "loki";
access = "proxy";
url = "http://127.0.0.1:${toString config.services.loki.configuration.server.http_listen_port}";
})
(mkIf sys.services.database.postgresql.enable {
name = "PostgreSQL";
type = "postgres";
access = "proxy";
url = "http://127.0.0.1:9103";
})
];
# typos go here
deleteDatasources = [
{
name = "postgres";
orgId = 0;
}
{
name = "redis";
orgId = 0;
}
{
name = "Endlessh-go";
orgId = 0;
}
];
};
};
};
nginx.virtualHosts."dash.notashelf.dev" =
{
locations."/" = {
proxyPass = with config.services.grafana.settings.server; "http://${toString http_addr}:${toString http_port}/";
proxyWebsockets = true;
};
quic = true;
}
// {
addSSL = true;
enableACME = true;
};
};
};
}

View file

@ -0,0 +1,95 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = config.services.loki;
in {
config = mkIf sys.services.monitoring.loki.enable {
# https://gist.github.com/rickhull/895b0cb38fdd537c1078a858cf15d63e
services.loki = {
enable = true;
dataDir = "/srv/storage/loki";
extraFlags = ["--config.expand-env=true"];
configuration = {
auth_enabled = false;
server = {
http_listen_port = 3030;
log_level = "warn";
};
ingester = {
lifecycler = {
address = "127.0.0.1";
ring = {
kvstore = {
store = "inmemory";
};
replication_factor = 1;
};
};
chunk_idle_period = "1h";
max_chunk_age = "1h";
chunk_target_size = 999999;
chunk_retain_period = "30s";
max_transfer_retries = 0;
};
schema_config.configs = [
{
from = "2022-05-14";
store = "boltdb";
object_store = "filesystem";
schema = "v11";
index = {
prefix = "index_";
period = "168h";
};
}
];
storage_config = {
boltdb.directory = "${cfg.dataDir}/boltdb-index";
filesystem.directory = "${cfg.dataDir}/storage-chunks";
boltdb_shipper = {
active_index_directory = "/srv/storage/loki/boltdb-shipper-active";
cache_location = "/srv/storage/loki/boltdb-shipper-cache";
cache_ttl = "24h";
shared_store = "filesystem";
};
};
limits_config = {
reject_old_samples = true;
reject_old_samples_max_age = "168h";
};
chunk_store_config = {
max_look_back_period = "0s";
};
table_manager = {
retention_deletes_enabled = false;
retention_period = "0s";
};
compactor = {
shared_store = "filesystem";
working_directory = "${cfg.dataDir}/compactor-work";
compactor_ring = {
kvstore = {
store = "inmemory";
};
};
};
};
# user, group, dataDir, extraFlags, (configFile)
};
};
}

View file

@ -0,0 +1,97 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.monitoring.prometheus.enable {
services = {
# Prometheus exporter for Grafana
prometheus = {
enable = true;
port = 9100;
# relatively frequent scraping intervals
globalConfig = {
scrape_interval = "10s";
scrape_timeout = "2s";
};
# enabled exporters
exporters = {
node = {
enable = true;
port = 9101;
enabledCollectors = ["systemd" "processes"];
};
redis = {
enable = true;
port = 9102;
user = "redis";
};
postgres = {
enable = true;
port = 9103;
user = "postgres";
};
nginx = {
enable = false;
port = 9104;
};
smartctl = {
inherit (config.services.smartd) enable;
openFirewall = config.services.smartd.enable;
# Defaults:
user = "smartctl-exporter";
group = "disk";
port = 9110;
};
};
scrapeConfigs = [
# internal scrape jobs
{
job_name = "prometheus";
scrape_interval = "30s";
static_configs = [{targets = ["localhost:9100"];}];
}
{
job_name = "node";
scrape_interval = "30s";
static_configs = [{targets = ["localhost:9101"];}];
}
{
job_name = "redis";
scrape_interval = "30s";
static_configs = [{targets = ["localhost:9102"];}];
}
{
job_name = "postgres";
scrape_interval = "30s";
static_configs = [{targets = ["localhost:9103"];}];
}
{
job_name = "nginx";
scrape_interval = "30s";
static_configs = [{targets = ["localhost:9104"];}];
}
{
job_name = "endlessh-go";
scrape_interval = "30s";
static_configs = [{targets = ["localhost:9105"];}];
}
# TODO: exterenal scrape jobs - over tailscale/wireguard mesh
];
};
};
};
}

View file

@ -0,0 +1,41 @@
{
config,
lib,
...
}: let
domain = "up.notashelf.dev";
in {
users = {
users.uptime-kuma = {
isSystemUser = true;
group = "uptime-kuma";
};
groups.uptime-kuma = {};
};
systemd.services.uptime-kuma = {
serviceConfig = {
DynamicUser = lib.mkForce false;
User = "uptime-kuma";
Group = "uptime-kuma";
};
};
services = {
uptime-kuma = {
enable = true;
settings = {
PORT = "4000";
};
};
nginx.virtualHosts."${domain}" =
{
locations."/" = {
proxyPass = "http://127.0.0.1:${toString config.services.uptime-kuma.settings.PORT}";
proxyWebsockets = true;
};
}
// lib.sslTemplate;
};
}

View file

@ -0,0 +1,6 @@
{
imports = [
./wireguard
./headscale.nix
];
}

View file

@ -0,0 +1,171 @@
{
inputs',
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.networking.headscale.enable {
environment.systemPackages = [config.services.headscale.package];
networking.firewall.allowedUDPPorts = [8086]; # DERP
services = {
headscale = {
enable = true;
address = "127.0.0.1";
port = 8085;
settings = {
grpc_listen_addr = "127.0.0.1:50443";
grpc_allow_insecure = false;
server_url = "https://hs.notashelf.dev";
tls_cert_path = null;
tls_key_path = null;
# default headscale prefix
ip_prefixes = [
"100.64.0.0/10"
"fd7a:115c:a1e0::/48"
];
dns_config = {
override_local_dns = true;
magic_dns = true;
base_domain = "notashelf.dev";
domains = [];
nameservers = [
"9.9.9.9" # no cloudflare, nice
];
/*
extra_records = [
{
name = "idm.notashelf.dev";
type = "A";
value = "100.64.0.1"; # NOTE: this should be the address of the "host" node - which is the server
}
];
*/
};
acl_policy_path = pkgs.writeText "headscale-acl" (builtins.toJSON {
acls = [
{
# Allow client --> server traffic
# but not the other way around.
# Servers face the internet, clients
# do so far less.
action = "accept";
proto = "tcp";
src = ["tag:client"];
dst = ["tag:server:*"];
}
];
# Allow all users to SSH into their own devices in check mode.
ssh = [
{
action = "check";
src = ["autogroup:member"];
dst = ["autogroup:self"];
users = ["autogroup:nonroot" "root"];
}
];
});
derp = {
server = {
enabled = true;
region_id = 900;
region_code = "headscale";
region_name = "Headscale Embedded DERP";
stun_listen_addr = "0.0.0.0:8344";
};
urls = [];
paths = [];
auto_update_enabled = false;
update_frequency = "24h";
};
disable_check_updates = true;
ephemeral_node_inactivity_timeout = "30m";
node_update_check_interval = "10s";
/*
db_type = "postgres";
db_host = "/run/postgresql";
db_name = "headscale";
db_user = "headscale";
db_port = 5432; # not ignored for some reason
*/
metrics_listen_addr = "127.0.0.1:8087";
log = {
format = "text";
level = "info";
};
# TODO: logtail
logtail = {
enabled = false;
};
randomize_client_port = false;
};
};
nginx.virtualHosts."hs.notashelf.dev" = {
forceSSL = true;
enableACME = true;
locations = {
"/" = {
proxyPass = "http://localhost:${toString config.services.headscale.port}";
proxyWebsockets = true;
};
# see <https://github.com/gurucomputing/headscale-ui/blob/master/SECURITY.md> before
# possibly using the web frontend
"/web" = {
root = "${inputs'.nyxpkgs.packages.headscale-ui}/share";
};
};
};
};
systemd.services = {
# TODO: consider enabling postgresql storage
# postgresql is normally pretty neat, but unless you expect your setup to receive
# very frequent logins, sqlite (default) storage may be more performant
# headscale.requires = ["postgresql.service"];
create-headscale-user = {
description = "Create a headscale user and preauth keys for this server";
wantedBy = ["multi-user.target"];
after = ["headscale.service"];
serviceConfig = {
Type = "oneshot";
User = "headscale";
};
path = [pkgs.headscale];
script = ''
if ! headscale users list | grep notashelf; then
headscale users create notashelf
headscale --user notashelf preauthkeys create --reusable --expiration 100y > /var/lib/headscale/preauth.key
fi
'';
};
};
};
}

View file

@ -0,0 +1,5 @@
{
imports = [
./server.nix
];
}

View file

@ -0,0 +1,66 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
dev = config.modules.device;
acceptedTypes = ["server" "hybrid"];
in {
config = mkIf ((builtins.elem dev.type acceptedTypes) && cfg.networking.wireguard.enable) {
networking = {
nat = {
enable = true;
externalInterface = "ens3";
internalInterfaces = ["wg0"];
};
firewall = {
allowedUDPPorts = [51820];
trustedInterfaces = ["wg0"];
};
};
boot.kernelModules = [
"wireguard"
];
# Wireguard Server Peer Setup
networking.wireguard = {
enable = true;
interfaces = {
wg0 = {
# General settings
privateKeyFile = config.age.secrets.wg-server.path;
listenPort = 51820;
# IPs
ips = [
"10.255.255.10/24" # v4 general
"10.255.255.254/24" # v4 adtl
"fe80::10/64" # v6 link local
"2a01:4f9:c010:2cf9:f::10/80" #v6 general
"2a01:4f9:c010:2cf9:f::ffff/80" #v6 adtl
];
# Peers
peers = [
# enyo
{
allowedIPs = [
"10.255.255.11/32"
"fe80::11/128"
"2a01:4f9:c010:2cf9:f::11/128"
];
publicKey = "u5Riuu4NEWEH06qATdnrPO+LacZTspoghqMnoWQ+uEQ=";
}
];
};
};
};
};
}

View file

@ -0,0 +1,33 @@
{
config,
lib,
...
}: {
networking = {
firewall.allowedUDPPorts = [51820 51821];
nat.internalInterfaces = ["wg0"];
wireguard.interfaces = {
wg0 = {
ips = ["10.0.0.1/24"];
listenPort = 51820;
privateKeyFile = config.age.secrets.wireguard.path;
peers = [
{
# enyo
publicKey = "vv190fxSVr+u7Zv0ujPcwE4aYs0QcbObHwzWGwUNSUA=";
allowedIPs = ["10.0.0.2/32"];
}
/*
{
# test
publicKey = "";
allowedIPs = ["10.0.0.3/32"];
}
*/
];
};
};
};
}

View file

@ -0,0 +1,167 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib) mkIf;
domain = "cloud.notashelf.dev";
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.nextcloud.enable {
modules.system.services = {
nginx.enable = true;
database = {
redis.enable = true;
postgresql.enable = true;
};
};
services = {
nextcloud = {
enable = true;
package = pkgs.nextcloud28;
nginx.recommendedHttpHeaders = true;
https = true;
hostName = domain;
home = "/srv/storage/nextcloud";
maxUploadSize = "4G";
enableImagemagick = true;
extraApps = let
inherit (config.services.nextcloud.package.packages) apps;
in {
# wtf is this formatting
inherit (apps) mail polls onlyoffice contacts calendar tasks bookmarks deck forms cookbook impersonate groupfolders;
};
autoUpdateApps = {
enable = true;
startAt = "03:00";
};
caching = {
apcu = true;
memcached = true;
redis = true;
};
config = {
# admin user settings
# only effective during setup
adminuser = "notashelf";
adminpassFile = config.age.secrets.nextcloud-secret.path;
# database
dbtype = "pgsql";
dbhost = "/run/postgresql";
dbname = "nextcloud";
dbuser = "nextcloud";
};
settings = {
"memories.exiftool" = "${lib.getExe pkgs.exiftool}";
"memories.vod.ffmpeg" = "${pkgs.ffmpeg-headless}/bin/ffmpeg";
"memories.vod.ffprobe" = "${pkgs.ffmpeg-headless}/bin/ffprobe";
# be very specific about the preview providers
enabledPreviewProviders = [
"OC\\Preview\\BMP"
"OC\\Preview\\GIF"
"OC\\Preview\\JPEG"
"OC\\Preview\\Krita"
"OC\\Preview\\MarkDown"
"OC\\Preview\\MP3"
"OC\\Preview\\OpenDocument"
"OC\\Preview\\PNG"
"OC\\Preview\\TXT"
"OC\\Preview\\XBitmap"
"OC\\Preview\\HEIC"
];
# run maintenance jobs at low-load hours
# i.e. 01:00am UTC and 05:00am UTC
maintenance_window_start = 1;
# force https
overwriteprotocol = "https";
trusted_domains = ["https://${toString domain}"];
trusted_proxies = ["https://${toString domain}"];
redis = {
host = "/run/redis-default/redis.sock";
dbindex = 0;
timeout = 3;
};
# other stuff
default_phone_region = "TR";
lost_password_link = "disabled";
};
phpOptions = {
"opcache.enable" = "1";
"opcache.enable_cli" = "1";
"opcache.jit" = "1255";
"opcache.jit_buffer_size" = "256M";
"opcache.validate_timestamps" = "0";
"opcache.save_comments" = "1";
# fix the opcache "buffer is almost full" error in admin overview
"opcache.interned_strings_buffer" = "16";
# try to resolve delays in displaying content or incomplete page rendering
"output_buffering" = "off";
"pm" = "dynamic";
"pm.max_children" = "50";
"pm.start_servers" = "15";
"pm.min_spare_servers" = "15";
"pm.max_spare_servers" = "25";
"pm.max_requests" = "500";
};
phpExtraExtensions = ext: [ext.redis];
};
nginx.virtualHosts."cloud.notashelf.dev" =
{
quic = true;
http3 = true;
}
// lib.sslTemplate;
};
systemd.services = {
phpfpm-nextcloud.aliases = ["nextcloud.service"];
"nextcloud-setup" = {
requires = ["postgresql.service"];
after = ["postgresql.service"];
serviceConfig = {
Restart = "on-failure";
RestartSec = "10s";
};
};
/*
"nextcloud-preview" = {
description = "Generate previews for all images that haven't been rendered";
startAt = "01:00:00";
requires = ["nextcloud.service"];
after = ["nextcloud.service"];
path = [config.services.nextcloud.occ];
script = "nextcloud-occ preview:generate";
serviceConfig = {
Restart = "on-failure";
RestartSec = "10s";
};
};
*/
};
};
}

View file

@ -0,0 +1,168 @@
{
config,
pkgs,
lib,
...
}: let
inherit (lib.modules) mkIf mkDefault;
inherit (lib.strings) fileContents;
sys = config.modules.system;
cfg = sys.services;
inherit (config.networking) domain;
in {
config = mkIf cfg.nginx.enable {
security = {
acme = {
acceptTerms = true;
defaults.email = "me@notashelf.dev";
};
};
services = {
nginx = {
enable = true;
package = pkgs.nginxQuic.override {withKTLS = true;};
# makes /nginx_status endpoint available t o localhost
statusPage = true;
recommendedTlsSettings = true;
recommendedBrotliSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
recommendedZstdSettings = true;
clientMaxBodySize = mkDefault "512m";
serverNamesHashBucketSize = 1024;
appendHttpConfig = ''
# set the maximum size of the headers hash tables to 1024 bytes
# this applies to the total size of all headers in a client request
# or a server response.
proxy_headers_hash_max_size 1024;
# set the bucket size for the headers hash tables to 256 bytes
# bucket size determines how many entries can be stored in
# each hash table bucket
proxy_headers_hash_bucket_size 256;
'';
# lets be more picky on our ciphers and protocols
sslCiphers = "EECDH+aRSA+AESGCM:EDH+aRSA:EECDH+aRSA:+AES256:+AES128:+SHA1:!CAMELLIA:!SEED:!3DES:!DES:!RC4:!eNULL";
sslProtocols = "TLSv1.3 TLSv1.2";
commonHttpConfig = ''
# map the scheme (HTTP or HTTPS) to the HSTS header for HTTPS
# the header includes max-age, includeSubdomains and preload
map $scheme $hsts_header {
https "max-age=31536000; includeSubdomains; preload";
}
# add the Referrer-Policy header with a value of "no-referrer"
# which instructs the browser not to send the 'Referer' header in
# subsequent requests
add_header "Referrer-Policy" "no-referrer";
# adds the Strict-Transport-Security header with a value derived from the mapped HSTS header
# which instructs the browser to always use HTTPS instead of HTTP
add_header Strict-Transport-Security $hsts_header;
# sets the path for cookies to "/", and adds attributes "secure", "HttpOnly", and "SameSite=strict"
# "secure": ensures that the cookie is only sent over HTTPS
# "HttpOnly": prevents the cookie from being accessed by JavaScript
# "SameSite=strict": restricts the cookie to be sent only in requests originating from the same site
proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict";
# define a new map that anonymizes the remote address
# by replacing the last octet of IPv4 addresses with 0
map $remote_addr $remote_addr_anon {
~(?P<ip>\d+\.\d+\.\d+)\. $ip.0;
~(?P<ip>[^:]+:[^:]+): $ip::;
default 0.0.0.0;
}
# define a new log format that anonymizes the remote address
# and adds the remote user name, the time, the request line,
log_format combined_anon '$remote_addr_anon - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
# write the access log to a file with the combined_anon format
# and a buffer size of 32k, flushing every 5 minutes
access_log /var/log/nginx/access.log combined_anon buffer=32k flush=5m;
# define a new log format that anonymizes the remote address
# and adds remote user name, local time, the request line and http3
# this is important for debugging quic enabled requests
log_format quic '$remote_addr_anon - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" "$http3"';
# write the access log to a file with the quic format
access_log /var/log/nginx/quic-access.log quic;
# error log should log only "warn" level and above
error_log /var/log/nginx/error.log warn;
'';
virtualHosts = {
"${domain}" = {
default = true;
serverAliases = ["www.${domain}"];
locations = let
commonConfig = ''
try_files $uri $uri/ =404;
default_type text/plain;
charset utf-8;
'';
# takes a path to a file and returns a
# configuration for a location that serves that file
mkStaticPage = {
name,
root,
header ? "",
footer ? "",
}: let
builtIndex = pkgs.writeTextDir "${name}" ''
${header}
${fileContents root}
${footer}
'';
in {
index = name;
root = builtIndex.outPath;
extraConfig = commonConfig;
};
in {
# root location
"/" = mkStaticPage {
root = ./static/root.txt;
name = "root.txt";
header = builtins.readFile ./static/header.txt;
footer = "> served by ${pkgs.nginx.outPath}";
};
# /gpg endpoint for my gpg key
"/gpg" = mkStaticPage {
name = "gpg.txt";
root = ./static/gpg.txt;
};
};
};
};
};
logrotate.settings.nginx = {
enable = true;
minsize = "50M";
rotate = "4"; # 4 files of 50mb each
compress = true;
};
};
};
}

View file

@ -0,0 +1,109 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=QQXP
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -0,0 +1,5 @@
_ _ _ __
_ __ ___ | |_ __ _ ___| |__ ___| |/ _|
| '_ \ / _ \| __/ _` / __| '_ \ / _ \ | |_
| | | | (_) | || (_| \__ \ | | | __/ | _|
|_| |_|\___/ \__\__,_|___/_| |_|\___|_|_|.dev

View file

@ -0,0 +1,90 @@
Howdy!
I'm raf, more commonly known as NotAShelf on most online spaces. This
is my personal webpage, which is actually pure plaintext due to my utmosts
disdain for modern web technologies.
Frequently Asked Questions
***
Q: What do you do for a living?
A: I am a professional sailmaker. I design, manufacture and sell sails. I also
work as a part-time Political Science instructor for the time being. Sometimes
I do digital art, other times I do sailing as a sport.
***
Q: Did you study Computer Science? Why do you know programming?
A: No, I graduated as an International Relations major. I've been teaching
myself programming and Linux system administration since 2018. I mostly enjoy
open-source software, and my primary stack (as of now) is mostly backend
development in Go. Programming is something I enjoy as a tool to help me get
from point A to point B, which is why I am interested in learning new
technologies as much as I can.
***
Q: How are the International Relations?
A: They're good, thanks for asking.
***
Q: What programming languages do you know?
A: I mostly work with JavaScript, Typescript, Go, C and Nix. I also have some
experience with Rust, C++ and Python. There are other languages I've used, but
don't really consider noteworthy. I hate PHP. PHP probably hates me back. Good.
***
Q: What do you do now?
A: I am currently working on my PhD in Political Science where I conduct
research on the impact of digital technologies on political participation and
executive functions as well as the effects of open-source software. on said
fields. I would love to go into further detail but I also would like to avoid
doxxing myself. Just know that I get the prefix of "Doctor" when I'm done.
I frequently work with Nix and NixOS, embedded programming and Linux systems
in general. I also do some web development in my free time.
***
If you want to contact me for any reason, you may just shoot me an email
@ raf [at] notashelf [dot] dev. In case you were unable to reach me
via email (e.g. your smtp server uses an outdated cipher, or my domain is
blacklisted by your provider), you may contact me via Matrix or Mastodon using
the links below. I am also on Discord, going by the same handle as my github
username. My public gpg key is available at https://notashelf.dev/gpg which
is also in plaintext. If you're a bot, you're probably having a field day.
On that note, please stop scraping my website. I **do not** have anything
of your interest.
***
List of stuff you might find interesting if you came here and are a human:
- E-mail | raf [at] notashelf [dot] dev
- Matrix | https://matrix.to/#/@raf:notashelf.dev
- Mastodon | https://social.notashelf.dev/
- Github | https://github.com/notashelf
- https://github.com/schizofox/schizofox
- https://github.com/hyprland-community/hyprkeys
- https://github.com/notashelf/catApi
- https://github.com/notashelf/neovim-flake
If you have found a vulnerability on this server, I'd be very grateful if you
told me about it. Contact me through any of the ways above.
***
I can't have a footer but this is a good place to put a shoutout to the
awesome motherfuckingwebsite.com.
***

View file

@ -0,0 +1,46 @@
{
config,
lib,
inputs',
...
}: let
inherit (lib) mkIf sslTemplate;
sys = config.modules.system;
cfg = sys.services;
inherit (cfg.reposilite.settings) port;
in {
config = mkIf sys.services.reposilite.enable {
modules.system.services = {
nginx.enable = true;
};
services.reposilite = {
enable = true;
package = inputs'.nyxpkgs.packages.reposilite-bin;
dataDir = "/srv/storage/reposilite";
openFirewall = true;
user = "reposilite";
group = "reposilite";
settings = {
inherit port;
};
};
services.nginx.virtualHosts = {
"repo.notashelf.dev" =
{
locations."/".proxyPass = "http://127.0.0.1:${toString port}";
extraConfig = ''
access_log /var/log/nginx/reposilite-access.log;
error_log /var/log/nginx/reposilite-error.log;
'';
}
// sslTemplate;
};
};
}

View file

@ -0,0 +1,146 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
inherit (cfg.searxng.settings) host port;
in {
config = mkIf cfg.searxng.enable {
networking.firewall.allowedTCPPorts = [port];
modules.system.services = {
nginx.enable = true;
database.redis.enable = true;
};
users = {
users.searx = {
isSystemUser = true;
createHome = false;
group = lib.mkForce "searx-redis";
};
groups.searx-redis = {};
};
services = {
searx = {
enable = true;
package = pkgs.searxng;
environmentFile = config.age.secrets.searx-secretkey.path;
settings = {
use_default_settings = true;
general = {
instance_name = "NotASearx";
privacypolicy_url = false;
donation_url = "https://ko-fi.com/notashelf";
contact_url = "mailto:raf@notashelf.dev";
enable_metrics = true;
debug = false;
};
search = {
safe_search = 0; # 0 = None, 1 = Moderate, 2 = Strict
formats = ["html" "json" "rss"];
autocomplete = "google"; # "dbpedia", "duckduckgo", "google", "startpage", "swisscows", "qwant", "wikipedia" - leave blank to turn it off by default
default_lang = "en";
};
server = {
inherit port;
method = "GET";
secret_key = "@SEARX_SECRET_KEY@"; # set in the environment file
limiter = false;
image_proxy = false; # no thanks, lol
default_http_headers = {
X-Content-Type-Options = "nosniff";
X-XSS-Protection = "1; mode=block";
X-Download-Options = "noopen";
X-Robots-Tag = "noindex, nofollow";
Referrer-Policy = "no-referrer";
};
};
ui = {
query_in_title = true;
theme_args.simple_style = "dark"; # auto, dark, light
results_on_new_tab = false;
};
redis = {
url = "unix://searxng:localhost@/run/redis-searxng?db=0";
#url = "unix:///run/redis-searxng/redis.sock?db=0";
#url = "redis://searxng@localhost:6370/0";
};
outgoing = {
request_timeout = 15.0;
max_request_timeout = 30.0;
};
engines = [
{
name = "wikipedia";
engine = "wikipedia";
shortcut = "w";
base_url = "https://wikipedia.org/";
}
{
name = "duckduckgo";
engine = "duckduckgo";
shortcut = "ddg";
}
{
name = "google";
engine = "google";
shortcut = "g";
use_mobile_ui = false;
}
{
name = "archwiki";
engine = "archlinux";
shortcut = "aw";
}
{
name = "github";
engine = "github";
categories = "it";
shortcut = "gh";
}
{
name = "nixpkgs";
shortcut = "nx";
engine = "elasticsearch";
categories = "dev,nix";
base_url = "https://nixos-search-5886075189.us-east-1.bonsaisearch.net:443";
index = "latest-31-nixos-unstable";
query_type = "match";
}
];
};
};
nginx.virtualHosts."search.notashelf.dev" =
{
locations."/".proxyPass = "http://${host}:${toString port}";
extraConfig = ''
access_log /dev/null;
error_log /dev/null;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
'';
quic = true;
}
// lib.sslTemplate;
};
};
}

View file

@ -0,0 +1,6 @@
{
imports = [
./matrix.nix # matrix communication server
./mastodon.nix # decentralized social
];
}

View file

@ -0,0 +1,106 @@
{
inputs',
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services.social;
in {
config = mkIf cfg.mastodon.enable {
modules.system.services = {
elasticsearch.enable = true;
database = {
postgresql.enable = true;
redis.enable = true;
};
};
services = {
mastodon = {
enable = true;
package = inputs'.nyxpkgs.packages.mastodon-bird-ui;
user = "mastodon";
configureNginx = false;
trustedProxy = "127.0.0.1";
localDomain = "social.notashelf.dev";
streamingProcesses = 2;
webPort = 55001;
sidekiqPort = 55002;
enableUnixSocket = true;
sidekiqThreads = 12;
elasticsearch.host = "127.0.0.1";
redis = {
createLocally = false;
host = "localhost";
port = 6372;
};
database = {
createLocally = true;
host = "/run/postgresql";
name = "mastodon";
user = "mastodon";
};
# configure smtp
smtp = {
authenticate = true;
createLocally = false;
fromAddress = "noreply@notashelf.dev";
user = "noreply";
host = "mail.notashelf.dev";
passwordFile = config.age.secrets.mailserver-noreply-secret.path;
};
# extra config
extraConfig = {
SINGLE_USER_MODE = "true";
WEB_DOMAIN = "social.notashelf.dev";
AUTHORIZED_FETCH = "true";
};
};
# this does what configureNginx option under the mastodon service is supposed to
# to be able to fine-grain the service, we move it to its own configuration block
# and also, I don't trust nixpkgs maintainers to properly maintain a service - so this is a safety net
# in case they break another thing without proper documentation
# /rant
nginx = {
virtualHosts."social.notashelf.dev" =
{
root = "${config.services.mastodon.package}/public/";
quic = true;
locations = {
"/" = {
tryFiles = "$uri @proxy";
};
"/system/".alias = "/var/lib/mastodon/public-system/";
"@proxy" = {
proxyPass = "http://unix:/run/mastodon-web/web.socket";
proxyWebsockets = true;
};
"/api/v1/streaming/" = {
proxyPass = "http://unix:/run/mastodon-streaming/streaming.socket";
proxyWebsockets = true;
};
};
}
// lib.sslTemplate;
};
};
users.groups.mastodon.members = [config.services.nginx.user];
};
}

View file

@ -0,0 +1,201 @@
{
config,
lib,
pkgs,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services.social;
inherit (cfg.matrix.settings) port;
bindAddress = "::1";
serverConfig."m.server" = "${config.services.matrix-synapse.settings.server_name}:443";
clientConfig = {
"m.homeserver".base_url = "https://${config.networking.domain}";
"m.identity_server" = {};
};
mkWellKnown = data: ''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
add_header 'Referrer-Policy' 'origin-when-cross-origin';
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
return 200 '${builtins.toJSON data}';
'';
in {
config = mkIf cfg.matrix.enable {
networking.firewall.allowedTCPPorts = [port];
modules.system.services.database = {
postgresql.enable = true;
};
services = {
postgresql = {
initialScript = pkgs.writeText "synapse-init.sql" ''
CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
TEMPLATE template0
LC_COLLATE = "C"
LC_CTYPE = "C";
'';
};
nginx.virtualHosts = {
"notashelf.dev" =
{
serverAliases = ["matrix.notashelf.dev"];
locations = {
"= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
"= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig;
"/_matrix".proxyPass = "http://[${bindAddress}]:${toString port}";
"/_synapse/client".proxyPass = "http://[${bindAddress}]:${toString port}";
};
quic = true;
http3 = true;
}
// lib.sslTemplate;
};
matrix-synapse = {
enable = true;
extraConfigFiles = [config.age.secrets.matrix-secret.path];
settings = {
server_name = "notashelf.dev";
public_baseurl = "https://notashelf.dev";
withJemalloc = true;
enable_registration = true;
registration_requires_token = true;
bcrypt_rounds = 14;
# Don't report anonymized usage statistics
report_stats = false;
# db
database = {
name = "psycopg2";
args = {
host = "/run/postgresql";
user = "matrix-synapse";
database = "matrix-synapse";
cp_min = 5;
cp_max = 10;
};
};
# media
media_retention.remote_media_lifetime = "30d";
max_upload_size = "100M";
url_preview_enabled = true;
url_preview_ip_range_blacklist = [
"127.0.0.0/8"
"10.0.0.0/8"
"172.16.0.0/12"
"192.168.0.0/16"
"100.64.0.0/10"
"192.0.0.0/24"
"169.254.0.0/16"
"192.88.99.0/24"
"198.18.0.0/15"
"192.0.2.0/24"
"198.51.100.0/24"
"203.0.113.0/24"
"224.0.0.0/4"
"::1/128"
"fe80::/10"
"fc00::/7"
"2001:db8::/32"
"ff00::/8"
"fec0::/10"
];
thumbnail_sizes = [
{
width = 32;
height = 32;
method = "crop";
}
{
width = 96;
height = 96;
method = "crop";
}
{
width = 320;
height = 240;
method = "scale";
}
{
width = 640;
height = 480;
method = "scale";
}
{
width = 800;
height = 600;
method = "scale";
}
];
# listener configuration
listeners = [
{
inherit port;
bind_addresses = ["${bindAddress}"];
resources = [
{
names = ["client" "federation"];
compress = true;
}
];
tls = false;
type = "http";
x_forwarded = true;
}
];
experimental_features = {
msc3202_device_masquerading = true;
msc3202_transaction_extensions = true;
msc2409_to_device_messages_enabled = true;
};
logConfig = ''
version: 1
# In systemd's journal, loglevel is implicitly stored, so let's omit it
# from the message text.
formatters:
journal_fmt:
format: '%(name)s: [%(request)s] %(message)s'
filters:
context:
(): synapse.util.logcontext.LoggingContextFilter
request: ""
handlers:
journal:
class: systemd.journal.JournalHandler
formatter: journal_fmt
filters: [context]
SYSLOG_IDENTIFIER: synapse
root:
level: WARNING
handlers: [journal]
disable_existing_loggers: False
'';
};
};
};
};
}

View file

@ -0,0 +1,35 @@
{
config,
lib,
...
}: let
inherit (lib) mkIf;
sys = config.modules.system;
cfg = sys.services;
in {
config = mkIf cfg.tor.enable {
services = {
tor = {
settings = {
AutomapHostsOnResolve = true;
AutomapHostsSuffixes = [".exit" ".onion"];
EnforceDistinctSubnets = true;
ExitNodes = "{de}";
EntryNodes = "{de}";
NewCircuitPeriod = 120;
DNSPort = 9053;
BandWidthRate = "15 MBytes";
};
relay.onionServices = {
# hide ssh from script kiddies
ssh = {
version = 3;
map = [{port = builtins.elemAt config.services.openssh.ports 0;}];
};
};
};
};
};
}

Some files were not shown because too many files have changed in this diff Show more