53 Commits

Author SHA1 Message Date
b7549e63f5 prototype 2023-04-26 14:46:55 -06:00
306ce8bc3f Move s0 to systemd-boot 2023-04-25 23:41:08 -06:00
b5dd983ba3 Automatically set machine hostname 2023-04-24 20:52:17 -06:00
832894edfc Gitea runner 2023-04-23 10:29:18 -06:00
feb6270952 Update options for newer nixpkgs 2023-04-23 10:28:55 -06:00
b4dd2d4a92 update TODOs 2023-04-23 10:16:54 -06:00
38c2e5aece Fix properties.nix path loading 2023-04-21 23:24:05 -06:00
0ef689b750 flake.lock: Update
Flake lock file updates:

• Updated input 'agenix':
    'github:ryantm/agenix/b7ffcfe77f817d9ee992640ba1f270718d197f28' (2023-01-31)
  → 'github:ryantm/agenix/2994d002dcff5353ca1ac48ec584c7f6589fe447' (2023-04-21)
• Updated input 'deploy-rs':
    'github:serokell/deploy-rs/8c9ea9605eed20528bf60fae35a2b613b901fd77' (2023-01-19)
  → 'github:serokell/deploy-rs/c2ea4e642dc50fd44b537e9860ec95867af30d39' (2023-04-21)
• Updated input 'flake-utils':
    'github:numtide/flake-utils/5aed5285a952e0b949eb3ba02c12fa4fcfef535f' (2022-11-02)
  → 'github:numtide/flake-utils/cfacdce06f30d2b68473a46042957675eebb3401' (2023-04-11)
• Added input 'flake-utils/systems':
    'github:nix-systems/default/da67096a3b9bf56a91d16901293e51ba5b49a27e' (2023-04-09)
• Updated input 'nix-index-database':
    'github:Mic92/nix-index-database/4306fa7c12e098360439faac1a2e6b8e509ec97c' (2023-02-26)
  → 'github:Mic92/nix-index-database/68ec961c51f48768f72d2bbdb396ce65a316677e' (2023-04-15)
• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/78c4d33c16092e535bc4ba1284ba49e3e138483a' (2023-03-03)
  → 'github:NixOS/nixpkgs/8dafae7c03d6aa8c2ae0a0612fbcb47e994e3fb8' (2023-04-22)
2023-04-21 21:22:00 -06:00
e72e19b7e8 Fix auto upgrade 2023-04-21 18:58:54 -06:00
03603119e5 Fix invalid import issue. 2023-04-21 18:57:06 -06:00
71baa09bd2 Refactor imports and secrets. Add per system properties and role based secret access.
Highlights
- No need to update flake for every machine anymore, just add a properties.nix file.
- Roles are automatically generated from all machine configurations.
- Roles and their secrets automatically are grouped and show up in agenix secrets.nix
- Machines and their service configs may now query the properties of all machines.
- Machine configuration and secrets are now competely isolated into each machine's directory.
- Safety checks to ensure no mixing of luks unlocking secrets and hosts with primary ones.
- SSH pubkeys no longer centrally stored but instead per machine where the private key lies for better cleanup.
2023-04-21 12:58:11 -06:00
a02775a234 Update install steps 2023-04-19 21:17:45 -06:00
5800359214 Update install steps 2023-04-19 21:17:03 -06:00
0bd42f1850 Update install steps 2023-04-19 21:15:58 -06:00
40f0e5d2ac Add Phil 2023-04-19 18:12:42 -06:00
f90b9f85fd try out appvm 2023-04-18 23:15:21 -06:00
5b084fffcc moonlander 2023-04-18 23:15:03 -06:00
4dd6401f8c update TODOs 2023-04-18 23:14:49 -06:00
260bbc1ffd Use doas instead of sudo 2023-04-10 22:03:57 -06:00
c8132a67d0 Use lf as terminal file explorer 2023-04-10 22:03:29 -06:00
3412d5caf9 Use hashed passwordfile just to be safe 2023-04-09 23:00:10 -06:00
1065cc4b59 Enable gitea email notifications 2023-04-09 22:05:23 -06:00
154b37879b Cross off finished TODOs 2023-04-09 22:04:51 -06:00
a34238b3a9 Easily run restic commands on a backup group 2023-04-09 13:06:15 -06:00
42e2ebd294 Allow marking folders as omitted from backup 2023-04-09 12:35:20 -06:00
378cf47683 restic backups 2023-04-08 21:25:55 -06:00
f68a4f4431 nixpkgs-fmt everything 2023-04-04 23:30:28 -06:00
3c683e7b9e NixOS router is now in active use :) 2023-04-04 20:53:38 -06:00
68bd70b525 Basic router working using the wip hostapd module from upstream 2023-04-04 12:57:16 -06:00
2189ab9a1b Improve cifs mounts. Newer protocol version, helpful commands, better network connection resiliency. 2023-03-31 11:43:12 -06:00
acbbb8a37a encrypted samba vault with gocryptfs 2023-03-25 15:49:07 -06:00
d1e6d21d66 iperf server 2023-03-25 15:48:39 -06:00
1a98e039fe Cleanup fio tests 2023-03-25 15:48:24 -06:00
3459ce5058 Add joplin 2023-03-18 22:04:31 -06:00
c48b1995f8 Remove zerotier 2023-03-18 20:41:09 -06:00
53c0e7ba1f Add Webmail 2023-03-14 23:28:07 -06:00
820cd392f1 Choose random PIA server in a specified region instead of hardcoded. And more TODOs addressed. 2023-03-12 22:55:46 -06:00
759fe04185 with lib; 2023-03-12 21:50:46 -06:00
db441fcf98 Add ability to refuse PIA ports 2023-03-12 21:46:36 -06:00
83e9280bb4 Use the NixOS firewall instead to block unwanted PIA VPN traffic 2023-03-12 20:49:39 -06:00
478235fe32 Enable firewall for PIA VPN wireguard interface 2023-03-12 20:29:20 -06:00
440401a391 Add ponyo to deploy-rs config 2023-03-12 19:50:55 -06:00
42c0dcae2d Port forwarding for transmission 2023-03-12 19:50:29 -06:00
7159868b57 update todo's 2023-03-12 19:46:51 -06:00
ab2cc0cc0a Cleanup services 2023-03-12 17:51:10 -06:00
aaa1800d0c Cleanup mail domains 2023-03-12 13:29:12 -06:00
a795c65c32 Cleanup mail domains 2023-03-12 13:25:34 -06:00
5ed02e924d Remove liza 2023-03-12 00:15:06 -07:00
1d620372b8 Remove leftovers of removed compute nodes 2023-03-12 00:14:49 -07:00
9684a975e2 Migrate nextcloud to ponyo 2023-03-12 00:10:14 -07:00
c3c3a9e77f disable searx for now 2023-03-12 00:09:40 -07:00
ecb6d1ef63 Migrate mailserver to ponyo 2023-03-11 23:40:36 -07:00
a5f7bb8a22 Fix vpn systemd service restart issues 2023-03-09 13:07:20 -07:00
117 changed files with 2828 additions and 1232 deletions

View File

@@ -3,10 +3,9 @@
### Source Layout ### Source Layout
- `/common` - common configuration imported into all `/machines` - `/common` - common configuration imported into all `/machines`
- `/boot` - config related to bootloaders, cpu microcode, and unlocking LUKS root disks over tor - `/boot` - config related to bootloaders, cpu microcode, and unlocking LUKS root disks over tor
- `/network` - config for tailscale, zeroteir, and NixOS container with automatic vpn tunneling via PIA - `/network` - config for tailscale, and NixOS container with automatic vpn tunneling via PIA
- `/pc` - config that a graphical desktop computer should have. Use `de.enable = true;` to enable everthing. - `/pc` - config that a graphical desktop computer should have. Use `de.enable = true;` to enable everthing.
- `/server` - config that creates new nixos services or extends existing ones to meet my needs - `/server` - config that creates new nixos services or extends existing ones to meet my needs
- `/ssh.nix` - all ssh public host and user keys for all `/machines`
- `/machines` - all my NixOS machines along with their machine unique configuration for hardware and services - `/machines` - all my NixOS machines along with their machine unique configuration for hardware and services
- `/kexec` - a special machine for generating minimal kexec images. Does not import `/common` - `/kexec` - a special machine for generating minimal kexec images. Does not import `/common`
- `/secrets` - encrypted shared secrets unlocked through `/machines` ssh host keys - `/secrets` - encrypted shared secrets unlocked through `/machines` ssh host keys

51
TODO.md
View File

@@ -10,24 +10,12 @@
- https://nixos.wiki/wiki/Comparison_of_NixOS_setups - https://nixos.wiki/wiki/Comparison_of_NixOS_setups
### Housekeeping ### Housekeeping
- Format everything here using nixfmt
- Cleanup the line between hardware-configuration.nix and configuration.nix in machine config - Cleanup the line between hardware-configuration.nix and configuration.nix in machine config
- CI https://gvolpe.com/blog/nixos-binary-cache-ci/
- remove `options.currentSystem` - remove `options.currentSystem`
- allow `hostname` option for webservices to be null to disable configuring nginx - allow `hostname` option for webservices to be null to disable configuring nginx
### NAS ### NAS
- helios64 extra led lights
- safely turn off NAS on power disconnect - safely turn off NAS on power disconnect
- hardware de/encoding for rk3399 helios64 https://forum.pine64.org/showthread.php?tid=14018
- tor unlock
### bcachefs
- bcachefs health alerts via email
- bcachefs periodic snapshotting
- use mount.bcachefs command for mounting
- bcachefs native encryption
- just need a kernel module? https://github.com/firestack/bcachefs-tools-flake/blob/kf/dev/mvp/nixos/module/bcachefs.nix#L40
### Shell Comands ### Shell Comands
- tailexitnode = `sudo tailscale up --exit-node=<exit-node-ip> --exit-node-allow-lan-access=true` - tailexitnode = `sudo tailscale up --exit-node=<exit-node-ip> --exit-node-allow-lan-access=true`
@@ -52,21 +40,7 @@
- https://ampache.org/ - https://ampache.org/
- replace nextcloud with seafile - replace nextcloud with seafile
### VPN container
- use wireguard for vpn
- https://github.com/triffid/pia-wg/blob/master/pia-wg.sh
- https://github.com/pia-foss/manual-connections
- port forwarding for vpn
- transmission using forwarded port
- https://www.wireguard.com/netns/
- one way firewall for vpn container
### Networking
- tailscale for p2p connections
- remove all use of zerotier
### Archive ### Archive
- https://www.backblaze.com/b2/cloud-storage.html
- email - email
- https://github.com/Disassembler0/dovecot-archive/blob/main/src/dovecot_archive.py - https://github.com/Disassembler0/dovecot-archive/blob/main/src/dovecot_archive.py
- http://kb.unixservertech.com/software/dovecot/archiveserver - http://kb.unixservertech.com/software/dovecot/archiveserver
@@ -75,7 +49,32 @@
- https://christine.website/blog/paranoid-nixos-2021-07-18 - https://christine.website/blog/paranoid-nixos-2021-07-18
- https://nixos.wiki/wiki/Impermanence - https://nixos.wiki/wiki/Impermanence
# Setup CI
- CI
- hydra
- https://docs.cachix.org/continuous-integration-setup/
- Binary Cache
- Maybe use cachix https://gvolpe.com/blog/nixos-binary-cache-ci/
- Self hosted binary cache? https://www.tweag.io/blog/2019-11-21-untrusted-ci/
- https://github.com/edolstra/nix-serve
- https://nixos.wiki/wiki/Binary_Cache
- https://discourse.nixos.org/t/introducing-attic-a-self-hostable-nix-binary-cache-server/24343
- Both
- https://garnix.io/
- https://nixbuild.net
# Secrets
- consider using headscale
- Replace luks over tor for remote unlock with luks over tailscale using ephemeral keys
- Rollover luks FDE passwords
- /secrets on personal computers should only be readable using a trusted ssh key, preferably requiring a yubikey
- Rollover shared yubikey secrets
- offsite backup yubikey, pw db, and ssh key with /secrets access
### Misc ### Misc
- for automated kernel upgrades on luks systems, need to kexec with initrd that contains luks key
- https://github.com/flowztul/keyexec/blob/master/etc/default/kexec-cryptroot
- https://github.com/pop-os/system76-scheduler - https://github.com/pop-os/system76-scheduler
- improve email a little bit https://helloinbox.email - improve email a little bit https://helloinbox.email
- remap razer keys https://github.com/sezanzeb/input-remapper - remap razer keys https://github.com/sezanzeb/input-remapper

View File

@@ -1,14 +1,25 @@
{ config, lib, ... }: { config, pkgs, lib, ... }:
# Modify auto-update so that it pulls a flake # Modify auto-update so that it pulls a flake
let let
cfg = config.system.autoUpgrade; cfg = config.system.autoUpgrade;
in { in
config = lib.mkIf cfg.enable { {
system.autoUpgrade = { config = lib.mkIf cfg.enable (lib.mkMerge [
flake = "git+https://git.neet.dev/zuckerberg/nix-config.git"; {
flags = [ "--recreate-lock-file" ]; # ignore lock file, just pull the latest system.autoUpgrade = {
}; flake = "git+https://git.neet.dev/zuckerberg/nix-config.git";
}; flags = [ "--recreate-lock-file" "--no-write-lock-file" ]; # ignore lock file, just pull the latest
}
# dates = "03:40";
# kexecWindow = lib.mkDefault { lower = "01:00"; upper = "05:00"; };
# randomizedDelaySec = "45min";
};
system.autoUpgrade.allowKexec = lib.mkDefault true;
luks.enableKexec = cfg.allowKexec && builtins.length config.luks.devices > 0;
}
]);
}

78
common/backups.nix Normal file
View File

@@ -0,0 +1,78 @@
{ config, lib, pkgs, ... }:
let
cfg = config.backup;
hostname = config.networking.hostName;
mkRespository = group: "s3:s3.us-west-004.backblazeb2.com/D22TgIt0-main-backup/${group}";
mkBackup = group: paths: {
repository = mkRespository group;
inherit paths;
initialize = true;
timerConfig = {
OnCalendar = "daily";
RandomizedDelaySec = "1h";
};
extraBackupArgs = [
''--exclude-if-present ".nobackup"''
];
pruneOpts = [
"--keep-daily 7" # one backup for each of the last n days
"--keep-weekly 5" # one backup for each of the last n weeks
"--keep-monthly 12" # one backup for each of the last n months
"--keep-yearly 75" # one backup for each of the last n years
];
environmentFile = "/run/agenix/backblaze-s3-backups";
passwordFile = "/run/agenix/restic-password";
};
# example usage: "sudo restic_samba unlock" (removes lockfile)
mkResticGroupCmd = group: pkgs.writeShellScriptBin "restic_${group}" ''
if [ "$EUID" -ne 0 ]
then echo "Run as root"
exit
fi
. /run/agenix/backblaze-s3-backups
export AWS_SECRET_ACCESS_KEY
export AWS_ACCESS_KEY_ID
export RESTIC_PASSWORD_FILE=/run/agenix/restic-password
export RESTIC_REPOSITORY="${mkRespository group}"
exec ${pkgs.restic}/bin/restic "$@"
'';
in
{
options.backup = {
group = lib.mkOption {
default = null;
type = lib.types.nullOr (lib.types.attrsOf (lib.types.submodule {
options = {
paths = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
Paths to backup
'';
};
};
}));
};
};
config = lib.mkIf (cfg.group != null) {
services.restic.backups = lib.concatMapAttrs
(group: groupCfg: {
${group} = mkBackup group groupCfg.paths;
})
cfg.group;
age.secrets.backblaze-s3-backups.file = ../secrets/backblaze-s3-backups.age;
age.secrets.restic-password.file = ../secrets/restic-password.age;
environment.systemPackages = map mkResticGroupCmd (builtins.attrNames cfg.group);
};
}

View File

@@ -3,7 +3,8 @@
with lib; with lib;
let let
cfg = config.bios; cfg = config.bios;
in { in
{
options.bios = { options.bios = {
enable = mkEnableOption "enable bios boot"; enable = mkEnableOption "enable bios boot";
device = mkOption { device = mkOption {
@@ -25,4 +26,4 @@ in {
}; };
}; };
}; };
} }

View File

@@ -5,6 +5,8 @@
./firmware.nix ./firmware.nix
./efi.nix ./efi.nix
./bios.nix ./bios.nix
./kexec-luks.nix
./luks.nix
./remote-luks-unlock.nix ./remote-luks-unlock.nix
]; ];
} }

View File

@@ -3,7 +3,8 @@
with lib; with lib;
let let
cfg = config.efi; cfg = config.efi;
in { in
{
options.efi = { options.efi = {
enable = mkEnableOption "enable efi boot"; enable = mkEnableOption "enable efi boot";
}; };
@@ -19,7 +20,7 @@ in {
version = 2; version = 2;
efiSupport = true; efiSupport = true;
useOSProber = true; useOSProber = true;
# memtest86.enable = true; # memtest86.enable = true;
configurationLimit = 20; configurationLimit = 20;
theme = pkgs.nixos-grub2-theme; theme = pkgs.nixos-grub2-theme;
}; };

View File

@@ -3,7 +3,8 @@
with lib; with lib;
let let
cfg = config.firmware; cfg = config.firmware;
in { in
{
options.firmware.x86_64 = { options.firmware.x86_64 = {
enable = mkEnableOption "enable x86_64 firmware"; enable = mkEnableOption "enable x86_64 firmware";
}; };
@@ -14,4 +15,4 @@ in {
}; };
# services.fwupd.enable = true; # services.fwupd.enable = true;
} }

121
common/boot/kexec-luks.nix Normal file
View File

@@ -0,0 +1,121 @@
# Allows kexec'ing as an alternative to rebooting for machines that
# have luks encrypted partitions that need to be mounted at boot.
# These luks partitions will be automatically unlocked, no password,
# or any interaction needed whatsoever.
# This is accomplished by fetching the luks key(s) while the system is running,
# then building a temporary initrd that contains the luks key(s), and kexec'ing.
{ config, lib, pkgs, ... }:
{
options.luks = {
enableKexec = lib.mkEnableOption "Enable support for transparent passwordless kexec while using luks";
};
config = lib.mkIf config.luks.enableKexec {
luks.fallbackToPassword = true;
luks.disableKeyring = true;
boot.initrd.luks.devices = lib.listToAttrs
(builtins.map
(item:
{
name = item;
value = {
masterKeyFile = "/etc/${item}.key";
};
})
config.luks.deviceNames);
systemd.services.prepare-luks-kexec-image = {
description = "Prepare kexec automatic LUKS unlock on kexec reboot without a password";
wantedBy = [ "kexec.target" ];
unitConfig.DefaultDependencies = false;
serviceConfig.Type = "oneshot";
path = with pkgs; [ file kexec-tools coreutils-full cpio findutils gzip xz zstd lvm2 xxd gawk ];
# based on https://github.com/flowztul/keyexec
script = ''
system=/nix/var/nix/profiles/system
old_initrd=$(readlink -f "$system/initrd")
umask 0077
CRYPTROOT_TMPDIR="$(mktemp -d --tmpdir=/dev/shm)"
cleanup() {
shred -fu "$CRYPTROOT_TMPDIR/initrd_contents/etc/"*.key || true
shred -fu "$CRYPTROOT_TMPDIR/new_initrd" || true
shred -fu "$CRYPTROOT_TMPDIR/secret/"* || true
rm -rf "$CRYPTROOT_TMPDIR"
}
# trap cleanup INT TERM EXIT
mkdir -p "$CRYPTROOT_TMPDIR"
cd "$CRYPTROOT_TMPDIR"
# Determine the compression type of the initrd image
compression=$(file -b --mime-type "$old_initrd" | awk -F'/' '{print $2}')
# Decompress the initrd image based on its compression type
case "$compression" in
gzip)
gunzip -c "$old_initrd" > initrd.cpio
;;
xz)
unxz -c "$old_initrd" > initrd.cpio
;;
zstd)
zstd -d -c "$old_initrd" > initrd.cpio
;;
*)
echo "Unsupported compression type: $compression"
exit 1
;;
esac
# Extract the contents of the cpio archive
mkdir -p initrd_contents
cd initrd_contents
cpio -idv < ../initrd.cpio
# Generate keys and add them to the extracted initrd filesystem
luksDeviceNames=(${builtins.concatStringsSep " " config.luks.deviceNames})
for item in "''${luksDeviceNames[@]}"; do
dmsetup --showkeys table "$item" | cut -d ' ' -f5 | xxd -ps -g1 -r > "./etc/$item.key"
done
# Add normal initrd secrets too
${lib.concatStringsSep "\n" (lib.mapAttrsToList (dest: source:
let source' = if source == null then dest else builtins.toString source; in
''
mkdir -p $(dirname "./${dest}")
cp -a ${source'} "./${dest}"
''
) config.boot.initrd.secrets)
}
# Create a new cpio archive with the modified contents
find . | cpio -o -H newc -v > ../new_initrd.cpio
# Compress the new cpio archive using the original compression type
cd ..
case "$compression" in
gzip)
gunzip -c new_initrd.cpio > new_initrd
;;
xz)
unxz -c new_initrd.cpio > new_initrd
;;
zstd)
zstd -c new_initrd.cpio > new_initrd
;;
esac
kexec --load "$system/kernel" --append "init=$system/init ${builtins.concatStringsSep " " config.boot.kernelParams}" --initrd "$CRYPTROOT_TMPDIR/new_initrd"
'';
};
};
}

74
common/boot/luks.nix Normal file
View File

@@ -0,0 +1,74 @@
# Makes it a little easier to configure luks partitions for boot
# Additionally, this solves a circular dependency between kexec luks
# and NixOS's luks module.
{ config, lib, ... }:
let
cfg = config.luks;
deviceCount = builtins.length cfg.devices;
deviceMap = lib.imap
(i: item: {
device = item;
name =
if deviceCount == 1 then "enc-pv"
else "enc-pv${builtins.toString (i + 1)}";
})
cfg.devices;
in
{
options.luks = {
devices = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
};
allowDiscards = lib.mkOption {
type = lib.types.bool;
default = true;
};
fallbackToPassword = lib.mkEnableOption
"Fallback to interactive passphrase prompt if the cannot be found.";
disableKeyring = lib.mkEnableOption
"When opening LUKS2 devices, don't use the kernel keyring";
# set automatically, don't touch
deviceNames = lib.mkOption {
type = lib.types.listOf lib.types.str;
};
};
config = lib.mkMerge [
{
assertions = [
{
assertion = deviceCount == builtins.length (builtins.attrNames config.boot.initrd.luks.devices);
message = ''
All luks devices must be specified using `luks.devices` not `boot.initrd.luks.devices`.
'';
}
];
}
(lib.mkIf (deviceCount != 0) {
luks.deviceNames = builtins.map (device: device.name) deviceMap;
boot.initrd.luks.devices = lib.listToAttrs (
builtins.map
(item:
{
name = item.name;
value = {
device = item.device;
allowDiscards = cfg.allowDiscards;
fallbackToPassword = cfg.fallbackToPassword;
disableKeyring = cfg.disableKeyring;
};
})
deviceMap);
})
];
}

View File

@@ -2,7 +2,8 @@
let let
cfg = config.remoteLuksUnlock; cfg = config.remoteLuksUnlock;
in { in
{
options.remoteLuksUnlock = { options.remoteLuksUnlock = {
enable = lib.mkEnableOption "enable luks root remote decrypt over ssh/tor"; enable = lib.mkEnableOption "enable luks root remote decrypt over ssh/tor";
enableTorUnlock = lib.mkOption { enableTorUnlock = lib.mkOption {
@@ -32,11 +33,6 @@ in {
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
# boot.initrd.luks.devices.${cfg.device.name} = {
# device = cfg.device.path;
# allowDiscards = cfg.device.allowDiscards;
# };
# Unlock LUKS disk over ssh # Unlock LUKS disk over ssh
boot.initrd.network.enable = true; boot.initrd.network.enable = true;
boot.initrd.kernelModules = cfg.kernelModules; boot.initrd.kernelModules = cfg.kernelModules;
@@ -61,33 +57,38 @@ in {
copy_bin_and_libs ${pkgs.haveged}/bin/haveged copy_bin_and_libs ${pkgs.haveged}/bin/haveged
''; '';
boot.initrd.network.postCommands = lib.mkMerge [ boot.initrd.network.postCommands = lib.mkMerge [
('' (
# Add nice prompt for giving LUKS passphrase over ssh ''
echo 'read -s -p "Unlock Passphrase: " passphrase && echo $passphrase > /crypt-ramfs/passphrase && exit' >> /root/.profile # Add nice prompt for giving LUKS passphrase over ssh
'') echo 'read -s -p "Unlock Passphrase: " passphrase && echo $passphrase > /crypt-ramfs/passphrase && exit' >> /root/.profile
''
(let torRc = (pkgs.writeText "tor.rc" '' )
DataDirectory /etc/tor
SOCKSPort 127.0.0.1:9050 IsolateDestAddr
SOCKSPort 127.0.0.1:9063
HiddenServiceDir /etc/tor/onion/bootup
HiddenServicePort 22 127.0.0.1:22
''); in lib.mkIf cfg.enableTorUnlock ''
echo "tor: preparing onion folder"
# have to do this otherwise tor does not want to start
chmod -R 700 /etc/tor
echo "make sure localhost is up" (
ip a a 127.0.0.1/8 dev lo let torRc = (pkgs.writeText "tor.rc" ''
ip link set lo up DataDirectory /etc/tor
SOCKSPort 127.0.0.1:9050 IsolateDestAddr
SOCKSPort 127.0.0.1:9063
HiddenServiceDir /etc/tor/onion/bootup
HiddenServicePort 22 127.0.0.1:22
''); in
lib.mkIf cfg.enableTorUnlock ''
echo "tor: preparing onion folder"
# have to do this otherwise tor does not want to start
chmod -R 700 /etc/tor
echo "haveged: starting haveged" echo "make sure localhost is up"
haveged -F & ip a a 127.0.0.1/8 dev lo
ip link set lo up
echo "tor: starting tor" echo "haveged: starting haveged"
tor -f ${torRc} --verify-config haveged -F &
tor -f ${torRc} &
'') echo "tor: starting tor"
tor -f ${torRc} --verify-config
tor -f ${torRc} &
''
)
]; ];
}; };
} }

View File

@@ -1,12 +1,8 @@
{ config, pkgs, ... }: { config, pkgs, ... }:
let
ssh = import ./ssh.nix;
sshUserKeys = ssh.users;
sshHigherTrustKeys = ssh.higherTrustUserKeys;
in
{ {
imports = [ imports = [
./backups.nix
./flakes.nix ./flakes.nix
./auto-update.nix ./auto-update.nix
./shell.nix ./shell.nix
@@ -14,6 +10,8 @@ in
./boot ./boot
./server ./server
./pc ./pc
./machine-info
./ssh.nix
]; ];
nix.flakes.enable = true; nix.flakes.enable = true;
@@ -40,7 +38,8 @@ in
wget wget
kakoune kakoune
htop htop
git git-lfs git
git-lfs
dnsutils dnsutils
tmux tmux
nethogs nethogs
@@ -53,6 +52,7 @@ in
helix helix
lm_sensors lm_sensors
picocom picocom
lf
]; ];
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
@@ -65,17 +65,25 @@ in
"dialout" # serial "dialout" # serial
]; ];
shell = pkgs.fish; shell = pkgs.fish;
openssh.authorizedKeys.keys = sshUserKeys; openssh.authorizedKeys.keys = config.machines.ssh.userKeys;
hashedPassword = "$6$TuDO46rILr$gkPUuLKZe3psexhs8WFZMpzgEBGksE.c3Tjh1f8sD0KMC4oV89K2pqAABfl.Lpxu2jVdr5bgvR5cWnZRnji/r/"; hashedPassword = "$6$TuDO46rILr$gkPUuLKZe3psexhs8WFZMpzgEBGksE.c3Tjh1f8sD0KMC4oV89K2pqAABfl.Lpxu2jVdr5bgvR5cWnZRnji/r/";
uid = 1000; uid = 1000;
}; };
users.users.root = { users.users.root = {
openssh.authorizedKeys.keys = sshHigherTrustKeys; openssh.authorizedKeys.keys = config.machines.ssh.deployKeys;
}; };
nix.settings = { nix.settings = {
trusted-users = [ "root" "googlebot" ]; trusted-users = [ "root" "googlebot" ];
}; };
# don't use sudo
security.doas.enable = true;
security.sudo.enable = false;
security.doas.extraRules = [
# don't ask for password every time
{ groups = [ "wheel" ]; persist = true; }
];
nix.gc.automatic = true; nix.gc.automatic = true;
security.acme.acceptTerms = true; security.acme.acceptTerms = true;

View File

@@ -2,7 +2,8 @@
with lib; with lib;
let let
cfg = config.nix.flakes; cfg = config.nix.flakes;
in { in
{
options.nix.flakes = { options.nix.flakes = {
enable = mkEnableOption "use nix flakes"; enable = mkEnableOption "use nix flakes";
}; };

View File

@@ -0,0 +1,200 @@
# Gathers info about each machine to constuct overall configuration
# Ex: Each machine already trusts each others SSH fingerprint already
{ config, lib, pkgs, ... }:
let
machines = config.machines.hosts;
in
{
imports = [
./ssh.nix
./roles.nix
];
options.machines = {
hosts = lib.mkOption {
type = lib.types.attrsOf
(lib.types.submodule {
options = {
hostNames = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of hostnames for this machine. The first one is the default so it is the target of deployments.
Used for automatically trusting hosts for ssh connections.
'';
};
arch = lib.mkOption {
type = lib.types.enum [ "x86_64-linux" "aarch64-linux" ];
description = ''
The architecture of this machine.
'';
};
systemRoles = lib.mkOption {
type = lib.types.listOf lib.types.str; # TODO: maybe use an enum?
description = ''
The set of roles this machine holds. Affects secrets available. (TODO add service config as well using this info)
'';
};
hostKey = lib.mkOption {
type = lib.types.str;
description = ''
The system ssh host key of this machine. Used for automatically trusting hosts for ssh connections
and for decrypting secrets with agenix.
'';
};
remoteUnlock = lib.mkOption {
default = null;
type = lib.types.nullOr (lib.types.submodule {
options = {
hostKey = lib.mkOption {
type = lib.types.str;
description = ''
The system ssh host key of this machine used for luks boot unlocking only.
'';
};
clearnetHost = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
The hostname resolvable over clearnet used to luks boot unlock this machine
'';
};
onionHost = lib.mkOption {
default = null;
type = lib.types.nullOr lib.types.str;
description = ''
The hostname resolvable over tor used to luks boot unlock this machine
'';
};
};
});
};
userKeys = lib.mkOption {
default = [ ];
type = lib.types.listOf lib.types.str;
description = ''
The list of user keys. Each key here can be used to log into all other systems as `googlebot`.
TODO: consider auto populating other programs that use ssh keys such as gitea
'';
};
deployKeys = lib.mkOption {
default = [ ];
type = lib.types.listOf lib.types.str;
description = ''
The list of deployment keys. Each key here can be used to log into all other systems as `root`.
'';
};
configurationPath = lib.mkOption {
type = lib.types.path;
description = ''
The path to this machine's configuration directory.
'';
};
};
});
};
};
config = {
assertions = (lib.concatLists (lib.mapAttrsToList
(
name: cfg: [
{
assertion = builtins.length cfg.hostNames > 0;
message = ''
Error with config for ${name}
There must be at least one hostname.
'';
}
{
assertion = builtins.length cfg.systemRoles > 0;
message = ''
Error with config for ${name}
There must be at least one system role.
'';
}
{
assertion = cfg.remoteUnlock == null || cfg.remoteUnlock.hostKey != cfg.hostKey;
message = ''
Error with config for ${name}
Unlock hostkey and hostkey cannot be the same because unlock hostkey is in /boot, unencrypted.
'';
}
{
assertion = cfg.remoteUnlock == null || (cfg.remoteUnlock.clearnetHost != null || cfg.remoteUnlock.onionHost != null);
message = ''
Error with config for ${name}
At least one of clearnet host or onion host must be defined.
'';
}
{
assertion = cfg.remoteUnlock == null || cfg.remoteUnlock.clearnetHost == null || builtins.elem cfg.remoteUnlock.clearnetHost cfg.hostNames == false;
message = ''
Error with config for ${name}
Clearnet unlock hostname cannot be in the list of hostnames for security reasons.
'';
}
{
assertion = cfg.remoteUnlock == null || cfg.remoteUnlock.onionHost == null || lib.strings.hasSuffix ".onion" cfg.remoteUnlock.onionHost;
message = ''
Error with config for ${name}
Tor unlock hostname must be an onion address.
'';
}
{
assertion = builtins.elem "personal" cfg.systemRoles || builtins.length cfg.userKeys == 0;
message = ''
Error with config for ${name}
There must be at least one userkey defined for personal machines.
'';
}
{
assertion = builtins.elem "deploy" cfg.systemRoles || builtins.length cfg.deployKeys == 0;
message = ''
Error with config for ${name}
Only deploy machines are allowed to have deploy keys for security reasons.
'';
}
]
)
machines));
# Set per machine properties automatically using each of their `properties.nix` files respectively
machines.hosts =
let
properties = dir: lib.concatMapAttrs
(name: path: {
${name} =
import path
//
{ configurationPath = builtins.dirOf path; };
})
(propertiesFiles dir);
propertiesFiles = dir:
lib.foldl (lib.mergeAttrs) { } (propertiesFiles' dir);
propertiesFiles' = dir:
let
propFiles = lib.filter (p: baseNameOf p == "properties.nix") (lib.filesystem.listFilesRecursive dir);
dirName = path: builtins.baseNameOf (builtins.dirOf path);
in
builtins.map (p: { "${dirName p}" = p; }) propFiles;
in
properties ../../machines;
};
}

View File

@@ -0,0 +1,15 @@
# Allows getting machine-info outside the scope of nixos configuration
{ nixpkgs ? import <nixpkgs> { }
, assertionsModule ? <nixpkgs/nixos/modules/misc/assertions.nix>
}:
{
machines =
(nixpkgs.lib.evalModules {
modules = [
./default.nix
assertionsModule
];
}).config.machines;
}

View File

@@ -0,0 +1,19 @@
{ config, lib, ... }:
# Maps roles to their hosts
{
options.machines.roles = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
};
config = {
machines.roles = lib.zipAttrs
(lib.mapAttrsToList
(host: cfg:
lib.foldl (lib.mergeAttrs) { }
(builtins.map (role: { ${role} = host; })
cfg.systemRoles))
config.machines.hosts);
};
}

View File

@@ -0,0 +1,44 @@
{ config, lib, ... }:
let
machines = config.machines;
sshkeys = keyType: lib.foldl (l: cfg: l ++ cfg.${keyType}) [ ] (builtins.attrValues machines.hosts);
in
{
options.machines.ssh = {
userKeys = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
List of user keys aggregated from all machines.
'';
};
deployKeys = lib.mkOption {
default = [ ];
type = lib.types.listOf lib.types.str;
description = ''
List of deploy keys aggregated from all machines.
'';
};
hostKeysByRole = lib.mkOption {
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
description = ''
Machine host keys divided into their roles.
'';
};
};
config = {
machines.ssh.userKeys = sshkeys "userKeys";
machines.ssh.deployKeys = sshkeys "deployKeys";
machines.ssh.hostKeysByRole = lib.mapAttrs
(role: hosts:
builtins.map
(host: machines.hosts.${host}.hostKey)
hosts)
machines.roles;
};
}

View File

@@ -7,13 +7,11 @@ let
in in
{ {
imports = [ imports = [
./hosts.nix
./pia-openvpn.nix ./pia-openvpn.nix
./pia-wireguard.nix ./pia-wireguard.nix
./ping.nix ./ping.nix
./tailscale.nix ./tailscale.nix
./vpn.nix ./vpn.nix
./zerotier.nix
]; ];
options.networking.ip_forward = mkEnableOption "Enable ip forwarding"; options.networking.ip_forward = mkEnableOption "Enable ip forwarding";
@@ -22,4 +20,4 @@ in
boot.kernel.sysctl."net.ipv4.ip_forward" = 1; boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1; boot.kernel.sysctl."net.ipv6.conf.all.forwarding" = 1;
}; };
} }

View File

@@ -1,110 +0,0 @@
{ config, lib, ... }:
with builtins;
let
# TODO: remove when all systems are updated to new enough nixpkgs
concatMapAttrs =
f: with lib; flip pipe [ (mapAttrs f) attrValues (foldl' mergeAttrs { }) ];
system = (import ../ssh.nix).system;
# hostnames that resolve on clearnet for LUKS unlocking
unlock-clearnet-hosts = {
ponyo = "unlock.ponyo.neet.dev";
s0 = "s0";
};
# hostnames that resolve on tor for LUKS unlocking
unlock-onion-hosts = {
liza = "5synsrjgvfzywruomjsfvfwhhlgxqhyofkzeqt2eisyijvjvebnu2xyd.onion";
router = "jxx2exuihlls2t6ncs7rvrjh2dssubjmjtclwr2ysvxtr4t7jv55xmqd.onion";
ponyo = "cfamr6artx75qvt7ho3rrbsc7mkucmv5aawebwflsfuorusayacffryd.onion";
s0 = "r3zvf7f2ppaeithzswigma46pajt3hqytmkg3rshgknbl3jbni455fqd.onion";
};
zerotierHosts = {
"s0.zt.neet.dev" = "172.30.145.180";
"ponyo.zt.neet.dev" = "172.30.109.9";
"ray.zt.neet.dev" = "172.30.189.212";
};
in {
# some DNS providers filter local ip results from DNS request
networking.hosts = concatMapAttrs (host: ip: {ip = [host];}) zerotierHosts;
# TODO only add if zerotier is enabled
keepalive-ping.hosts = attrNames zerotierHosts;
programs.ssh.knownHosts = {
liza = {
hostNames = [ "liza" "mail.neet.dev" ];
publicKey = system.liza;
};
liza-unlock = {
hostNames = [ unlock-onion-hosts.liza ];
publicKey = system.liza-unlock;
};
ponyo = {
hostNames = [ "ponyo" "ponyo.neet.dev" "ponyo.zt.neet.dev" "git.neet.dev" ];
publicKey = system.ponyo;
};
ponyo-unlock = {
hostNames = [ unlock-clearnet-hosts.ponyo unlock-onion-hosts.ponyo ];
publicKey = system.ponyo-unlock;
};
router = {
hostNames = [ "router" "192.168.1.228" ];
publicKey = system.router;
};
router-unlock = {
hostNames = [ unlock-onion-hosts.router ];
publicKey = system.router-unlock;
};
ray = {
hostNames = [ "ray" "ray.zt.neet.dev" ];
publicKey = system.ray;
};
s0 = {
hostNames = [ "s0" "s0.zt.neet.dev" ];
publicKey = system.s0;
};
s0-unlock = {
hostNames = [ unlock-onion-hosts.s0 ];
publicKey = system.s0-unlock;
};
n1 = {
hostNames = [ "n1" ];
publicKey = system.n1;
};
n2 = {
hostNames = [ "n2" ];
publicKey = system.n2;
};
n3 = {
hostNames = [ "n3" ];
publicKey = system.n3;
};
n4 = {
hostNames = [ "n4" ];
publicKey = system.n4;
};
n5 = {
hostNames = [ "n5" ];
publicKey = system.n5;
};
n6 = {
hostNames = [ "n6" ];
publicKey = system.n6;
};
n7 = {
hostNames = [ "n7" ];
publicKey = system.n7;
};
};
# prebuilt cmds for easy ssh LUKS unlock
environment.shellAliases =
concatMapAttrs (host: addr: {"unlock-over-tor_${host}" = "torsocks ssh root@${addr}";}) unlock-onion-hosts
//
concatMapAttrs (host: addr: {"unlock_${host}" = "ssh root@${addr}";}) unlock-clearnet-hosts;
}

View File

@@ -108,6 +108,6 @@ in
}; };
}; };
}; };
age.secrets."pia-login.conf".file = ../../secrets/pia-login.conf; age.secrets."pia-login.conf".file = ../../secrets/pia-login.age;
}; };
} }

View File

@@ -6,6 +6,15 @@
# https://github.com/pia-foss/manual-connections # https://github.com/pia-foss/manual-connections
# https://github.com/thrnz/docker-wireguard-pia/blob/master/extra/wg-gen.sh # https://github.com/thrnz/docker-wireguard-pia/blob/master/extra/wg-gen.sh
# TODO handle potential errors (or at least print status, success, and failures to the console)
# TODO parameterize names of systemd services so that multiple wg VPNs could coexist in theory easier
# TODO implement this module such that the wireguard VPN doesn't have to live in a container
# TODO don't add forward rules if the PIA port is the same as cfg.forwardedPort
# TODO verify signatures of PIA responses
with builtins;
with lib;
let let
cfg = config.pia.wireguard; cfg = config.pia.wireguard;
@@ -15,40 +24,118 @@ let
# PIA_TOKEN only lasts 24hrs # PIA_TOKEN only lasts 24hrs
PIA_TOKEN=`curl -s -u "$PIA_USER:$PIA_PASS" https://www.privateinternetaccess.com/gtoken/generateToken | jq -r '.token'` PIA_TOKEN=`curl -s -u "$PIA_USER:$PIA_PASS" https://www.privateinternetaccess.com/gtoken/generateToken | jq -r '.token'`
''; '';
in {
chooseWireguardServer = ''
servers=$(mktemp)
servers_json=$(mktemp)
curl -s "https://serverlist.piaservers.net/vpninfo/servers/v6" > "$servers"
# extract json part only
head -n 1 "$servers" | tr -d '\n' > "$servers_json"
echo "Available location ids:" && jq '.regions | .[] | {name, id, port_forward}' "$servers_json"
# Some locations have multiple servers available. Pick a random one.
totalservers=$(jq -r '.regions | .[] | select(.id=="'${cfg.serverLocation}'") | .servers.wg | length' "$servers_json")
if ! [[ "$totalservers" =~ ^[0-9]+$ ]] || [ "$totalservers" -eq 0 ] 2>/dev/null; then
echo "Location \"${cfg.serverLocation}\" not found."
exit 1
fi
serverindex=$(( RANDOM % totalservers))
WG_HOSTNAME=$(jq -r '.regions | .[] | select(.id=="'${cfg.serverLocation}'") | .servers.wg | .['$serverindex'].cn' "$servers_json")
WG_SERVER_IP=$(jq -r '.regions | .[] | select(.id=="'${cfg.serverLocation}'") | .servers.wg | .['$serverindex'].ip' "$servers_json")
WG_SERVER_PORT=$(jq -r '.groups.wg | .[0] | .ports | .[0]' "$servers_json")
# write chosen server
rm -f /tmp/${cfg.interfaceName}-server.conf
touch /tmp/${cfg.interfaceName}-server.conf
chmod 700 /tmp/${cfg.interfaceName}-server.conf
echo "$WG_HOSTNAME" >> /tmp/${cfg.interfaceName}-server.conf
echo "$WG_SERVER_IP" >> /tmp/${cfg.interfaceName}-server.conf
echo "$WG_SERVER_PORT" >> /tmp/${cfg.interfaceName}-server.conf
rm $servers_json $servers
'';
getChosenWireguardServer = ''
WG_HOSTNAME=`sed '1q;d' /tmp/${cfg.interfaceName}-server.conf`
WG_SERVER_IP=`sed '2q;d' /tmp/${cfg.interfaceName}-server.conf`
WG_SERVER_PORT=`sed '3q;d' /tmp/${cfg.interfaceName}-server.conf`
'';
refreshPIAPort = ''
${getChosenWireguardServer}
signature=`sed '1q;d' /tmp/${cfg.interfaceName}-port-renewal`
payload=`sed '2q;d' /tmp/${cfg.interfaceName}-port-renewal`
bind_port_response=`curl -Gs -m 5 --connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" --cacert "${./ca.rsa.4096.crt}" --data-urlencode "payload=$payload" --data-urlencode "signature=$signature" "https://$WG_HOSTNAME:19999/bindPort"`
'';
portForwarding = cfg.forwardPortForTransmission || cfg.forwardedPort != null;
containerServiceName = "container@${config.vpn-container.containerName}.service";
in
{
options.pia.wireguard = { options.pia.wireguard = {
enable = lib.mkEnableOption "Enable private internet access"; enable = mkEnableOption "Enable private internet access";
serverHostname = lib.mkOption { badPortForwardPorts = mkOption {
type = lib.types.str; type = types.listOf types.port;
default = "zurich406"; description = ''
Ports that will not be accepted from PIA.
If PIA assigns a port from this list, the connection is aborted since we cannot ask for a different port.
This is used to guarantee we are not assigned a port that is used by a service we do not want exposed.
'';
}; };
serverIp = lib.mkOption { wireguardListenPort = mkOption {
type = lib.types.str; type = types.port;
default = "156.146.62.153"; description = "The port wireguard listens on for this VPN connection";
default = 51820;
}; };
interfaceName = lib.mkOption { serverLocation = mkOption {
type = lib.types.str; type = types.str;
default = "swiss";
};
interfaceName = mkOption {
type = types.str;
default = "piaw"; default = "piaw";
}; };
portForwarding = lib.mkEnableOption "Enables PIA port fowarding"; forwardedPort = mkOption {
type = types.nullOr types.port;
# TODO implement this such that the wireguard VPN doesn't have to live in a container description = "The port to redirect port forwarded TCP VPN traffic too";
useInVPNContainer = lib.mkEnableOption "Configures the PIA WG VPN for use in the VPN container"; default = null;
};
forwardPortForTransmission = mkEnableOption "PIA port forwarding for transmission should be performed.";
}; };
config = lib.mkIf cfg.enable { config = mkIf cfg.enable {
vpn-container.mounts = [ "/tmp/${cfg.interfaceName}.conf" "/tmp/${cfg.interfaceName}-address.conf" ]; assertions = [
{
assertion = cfg.forwardPortForTransmission != (cfg.forwardedPort != null);
message = ''
The PIA forwarded port cannot simultaneously be used by transmission and redirected to another port.
'';
}
];
# mounts used to pass the connection parameters to the container
# the container doesn't have internet until it uses these parameters so it cannot fetch them itself
vpn-container.mounts = [
"/tmp/${cfg.interfaceName}.conf"
"/tmp/${cfg.interfaceName}-server.conf"
"/tmp/${cfg.interfaceName}-address.conf"
];
# The container takes ownership of the wireguard interface on its startup
containers.vpn.interfaces = [ cfg.interfaceName ]; containers.vpn.interfaces = [ cfg.interfaceName ];
# TODO: while this is much better than "loose" networking, it seems to have issues with firewall restarts
# allow traffic for wireguard interface to pass since wireguard trips up rpfilter # allow traffic for wireguard interface to pass since wireguard trips up rpfilter
# networking.firewall = { # networking.firewall = {
# extraCommands = '' # extraCommands = ''
# ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --sport 51820 -j RETURN # ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --sport ${toString cfg.wireguardListenPort} -j RETURN
# ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --dport 51820 -j RETURN # ip46tables -t raw -I nixos-fw-rpfilter -p udp -m udp --dport ${toString cfg.wireguardListenPort} -j RETURN
# ''; # '';
# extraStopCommands = '' # extraStopCommands = ''
# ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --sport 51820 -j RETURN || true # ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --sport ${toString cfg.wireguardListenPort} -j RETURN || true
# ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --dport 51820 -j RETURN || true # ip46tables -t raw -D nixos-fw-rpfilter -p udp -m udp --dport ${toString cfg.wireguardListenPort} -j RETURN || true
# ''; # '';
# }; # };
networking.firewall.checkReversePath = "loose"; networking.firewall.checkReversePath = "loose";
@@ -58,8 +145,9 @@ in {
requires = [ "network-online.target" ]; requires = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ]; after = [ "network.target" "network-online.target" ];
before = [ "container@vpn.service" ]; before = [ containerServiceName ];
requiredBy = [ "container@vpn.service" ]; requiredBy = [ containerServiceName ];
partOf = [ containerServiceName ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
path = with pkgs; [ wireguard-tools jq curl iproute ]; path = with pkgs; [ wireguard-tools jq curl iproute ];
@@ -67,6 +155,10 @@ in {
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
# restart once a month; PIA forwarded port expires after two months
# because the container is "PartOf" this unit, it gets restarted too
RuntimeMaxSec = "30d";
}; };
script = '' script = ''
@@ -74,30 +166,34 @@ in {
# cannot do without internet to start with. NAT'ing the host's internet would address this # cannot do without internet to start with. NAT'ing the host's internet would address this
# issue but is not ideal because then leaking network outside of the VPN is more likely. # issue but is not ideal because then leaking network outside of the VPN is more likely.
WG_HOSTNAME=${cfg.serverHostname} ${chooseWireguardServer}
WG_SERVER_IP=${cfg.serverIp}
${getPIAToken} ${getPIAToken}
# generate wireguard keys
privKey=$(wg genkey) privKey=$(wg genkey)
pubKey=$(echo "$privKey" | wg pubkey) pubKey=$(echo "$privKey" | wg pubkey)
wireguard_json=`curl -s -G --connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" --cacert "${./ca.rsa.4096.crt}" --data-urlencode "pt=$PIA_TOKEN" --data-urlencode "pubkey=$pubKey" https://$WG_HOSTNAME:1337/addKey` # authorize our WG keys with the PIA server we are about to connect to
wireguard_json=`curl -s -G --connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" --cacert "${./ca.rsa.4096.crt}" --data-urlencode "pt=$PIA_TOKEN" --data-urlencode "pubkey=$pubKey" https://$WG_HOSTNAME:$WG_SERVER_PORT/addKey`
# create wg-quick config file
rm -f /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf rm -f /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf
touch /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf touch /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf
chmod 700 /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf chmod 700 /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf
echo " echo "
[Interface] [Interface]
# Address = $(echo "$wireguard_json" | jq -r '.peer_ip') # Address = $(echo "$wireguard_json" | jq -r '.peer_ip')
PrivateKey = $privKey PrivateKey = $privKey
ListenPort = 51820 ListenPort = ${toString cfg.wireguardListenPort}
[Peer] [Peer]
PersistentKeepalive = 25 PersistentKeepalive = 25
PublicKey = $(echo "$wireguard_json" | jq -r '.server_key') PublicKey = $(echo "$wireguard_json" | jq -r '.server_key')
AllowedIPs = 0.0.0.0/0 AllowedIPs = 0.0.0.0/0
Endpoint = $WG_SERVER_IP:$(echo "$wireguard_json" | jq -r '.server_port') Endpoint = $WG_SERVER_IP:$(echo "$wireguard_json" | jq -r '.server_port')
" >> /tmp/${cfg.interfaceName}.conf " >> /tmp/${cfg.interfaceName}.conf
# create file storing the VPN ip address PIA assigned to us
echo "$wireguard_json" | jq -r '.peer_ip' >> /tmp/${cfg.interfaceName}-address.conf echo "$wireguard_json" | jq -r '.peer_ip' >> /tmp/${cfg.interfaceName}-address.conf
# Create wg interface now so it inherits from the namespace with internet access # Create wg interface now so it inherits from the namespace with internet access
@@ -109,18 +205,20 @@ in {
''; '';
preStop = '' preStop = ''
# cleanup wireguard interface
ip link del ${cfg.interfaceName} ip link del ${cfg.interfaceName}
rm -f /tmp/${cfg.interfaceName}.conf /tmp/${cfg.interfaceName}-address.conf
''; '';
}; };
vpn-container.config.systemd.services.pia-vpn-wireguard = { vpn-container.config.systemd.services.pia-vpn-wireguard = {
description = "PIA VPN WireGuard Tunnel"; description = "Initializes the PIA VPN WireGuard Tunnel";
requires = [ "network-online.target" ]; requires = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ]; after = [ "network.target" "network-online.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
path = with pkgs; [ wireguard-tools iproute curl jq ]; path = with pkgs; [ wireguard-tools iproute curl jq iptables ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
@@ -129,73 +227,131 @@ in {
script = '' script = ''
# pseudo calls wg-quick # pseudo calls wg-quick
# wg-quick down /tmp/${cfg.interfaceName}.conf # Near equivalent of "wg-quick up /tmp/${cfg.interfaceName}.conf"
# cannot actually call wg-quick because the interface has to be already created at this point # cannot actually call wg-quick because the interface has to be already
# created before the container taken ownership of the interface
# Thus, assumes wg interface was already created:
# ip link add ${cfg.interfaceName} type wireguard
# assumes wg interface was already created ${getChosenWireguardServer}
# ip link add ${cfg.interfaceName} type wireguard
myaddress=`cat /tmp/${cfg.interfaceName}-address.conf` myaddress=`cat /tmp/${cfg.interfaceName}-address.conf`
wg setconf ${cfg.interfaceName} /tmp/${cfg.interfaceName}.conf wg setconf ${cfg.interfaceName} /tmp/${cfg.interfaceName}.conf
ip -4 address add $myaddress dev ${cfg.interfaceName} ip -4 address add $myaddress dev ${cfg.interfaceName}
ip link set mtu 1420 up dev ${cfg.interfaceName} ip link set mtu 1420 up dev ${cfg.interfaceName}
wg set ${cfg.interfaceName} fwmark 51820 wg set ${cfg.interfaceName} fwmark ${toString cfg.wireguardListenPort}
ip -4 route add 0.0.0.0/0 dev ${cfg.interfaceName} table 51820 ip -4 route add 0.0.0.0/0 dev ${cfg.interfaceName} table ${toString cfg.wireguardListenPort}
# TODO is this needed? # TODO is this needed?
ip -4 rule add not fwmark 51820 table 51820 ip -4 rule add not fwmark ${toString cfg.wireguardListenPort} table ${toString cfg.wireguardListenPort}
ip -4 rule add table main suppress_prefixlength 0 ip -4 rule add table main suppress_prefixlength 0
# sysctl -q net.ipv4.conf.all.src_valid_mark=1
# The rest of the script is only for only for port forwarding skip if not needed
if [ ${boolToString portForwarding} == false ]; then exit 0; fi
# Reserve port # Reserve port
${getPIAToken} ${getPIAToken}
payload_and_signature=`curl -s -m 5 --connect-to "${cfg.serverHostname}::${cfg.serverIp}:" --cacert "${./ca.rsa.4096.crt}" -G --data-urlencode "token=$PIA_TOKEN" "https://${cfg.serverHostname}:19999/getSignature"` payload_and_signature=`curl -s -m 5 --connect-to "$WG_HOSTNAME::$WG_SERVER_IP:" --cacert "${./ca.rsa.4096.crt}" -G --data-urlencode "token=$PIA_TOKEN" "https://$WG_HOSTNAME:19999/getSignature"`
signature=$(echo "$payload_and_signature" | jq -r '.signature') signature=$(echo "$payload_and_signature" | jq -r '.signature')
payload=$(echo "$payload_and_signature" | jq -r '.payload') payload=$(echo "$payload_and_signature" | jq -r '.payload')
port=$(echo "$payload" | base64 -d | jq -r '.port') port=$(echo "$payload" | base64 -d | jq -r '.port')
# Check if the port is acceptable
notallowed=(${concatStringsSep " " (map toString cfg.badPortForwardPorts)})
if [[ " ''${notallowed[*]} " =~ " $port " ]]; then
# the port PIA assigned is not allowed, kill the connection
wg-quick down /tmp/${cfg.interfaceName}.conf
exit 1
fi
# write reserved port to file readable for all users # write reserved port to file readable for all users
echo $port > /tmp/${cfg.interfaceName}-port echo $port > /tmp/${cfg.interfaceName}-port
chmod 644 /tmp/${cfg.interfaceName}-port chmod 644 /tmp/${cfg.interfaceName}-port
# write payload and signature info needed to allow refreshing allocated forwarded port
rm -f /tmp/${cfg.interfaceName}-port-renewal rm -f /tmp/${cfg.interfaceName}-port-renewal
touch /tmp/${cfg.interfaceName}-port-renewal touch /tmp/${cfg.interfaceName}-port-renewal
chmod 700 /tmp/${cfg.interfaceName}-port-renewal chmod 700 /tmp/${cfg.interfaceName}-port-renewal
echo $signature >> /tmp/${cfg.interfaceName}-port-renewal echo $signature >> /tmp/${cfg.interfaceName}-port-renewal
echo $payload >> /tmp/${cfg.interfaceName}-port-renewal echo $payload >> /tmp/${cfg.interfaceName}-port-renewal
# Block all traffic from VPN interface except for traffic that is from the forwarded port
iptables -I nixos-fw -p tcp --dport $port -j nixos-fw-accept -i ${cfg.interfaceName}
iptables -I nixos-fw -p udp --dport $port -j nixos-fw-accept -i ${cfg.interfaceName}
# The first port refresh triggers the port to be actually allocated
${refreshPIAPort}
${optionalString (cfg.forwardedPort != null) ''
# redirect the fowarded port
iptables -A INPUT -i ${cfg.interfaceName} -p tcp --dport $port -j ACCEPT
iptables -A INPUT -i ${cfg.interfaceName} -p udp --dport $port -j ACCEPT
iptables -A INPUT -i ${cfg.interfaceName} -p tcp --dport ${toString cfg.forwardedPort} -j ACCEPT
iptables -A INPUT -i ${cfg.interfaceName} -p udp --dport ${toString cfg.forwardedPort} -j ACCEPT
iptables -A PREROUTING -t nat -i ${cfg.interfaceName} -p tcp --dport $port -j REDIRECT --to-port ${toString cfg.forwardedPort}
iptables -A PREROUTING -t nat -i ${cfg.interfaceName} -p udp --dport $port -j REDIRECT --to-port ${toString cfg.forwardedPort}
''}
${optionalString cfg.forwardPortForTransmission ''
# assumes no auth needed for transmission
curlout=$(curl localhost:9091/transmission/rpc 2>/dev/null)
regex='X-Transmission-Session-Id\: (\w*)'
if [[ $curlout =~ $regex ]]; then
sessionId=''${BASH_REMATCH[1]}
else
exit 1
fi
# set the port in transmission
data='{"method": "session-set", "arguments": { "peer-port" :'$port' } }'
curl http://localhost:9091/transmission/rpc -d "$data" -H "X-Transmission-Session-Id: $sessionId"
''}
''; '';
preStop = '' preStop = ''
wg-quick down /tmp/${cfg.interfaceName}.conf wg-quick down /tmp/${cfg.interfaceName}.conf
# The rest of the script is only for only for port forwarding skip if not needed
if [ ${boolToString portForwarding} == false ]; then exit 0; fi
${optionalString (cfg.forwardedPort != null) ''
# stop redirecting the forwarded port
iptables -D INPUT -i ${cfg.interfaceName} -p tcp --dport $port -j ACCEPT
iptables -D INPUT -i ${cfg.interfaceName} -p udp --dport $port -j ACCEPT
iptables -D INPUT -i ${cfg.interfaceName} -p tcp --dport ${toString cfg.forwardedPort} -j ACCEPT
iptables -D INPUT -i ${cfg.interfaceName} -p udp --dport ${toString cfg.forwardedPort} -j ACCEPT
iptables -D PREROUTING -t nat -i ${cfg.interfaceName} -p tcp --dport $port -j REDIRECT --to-port ${toString cfg.forwardedPort}
iptables -D PREROUTING -t nat -i ${cfg.interfaceName} -p udp --dport $port -j REDIRECT --to-port ${toString cfg.forwardedPort}
''}
''; '';
}; };
vpn-container.config.systemd.services.pia-vpn-wireguard-forward-port = { vpn-container.config.systemd.services.pia-vpn-wireguard-forward-port = {
enable = cfg.portForwarding; enable = portForwarding;
description = "PIA VPN WireGuard Tunnel Port Forwarding"; description = "PIA VPN WireGuard Tunnel Port Forwarding";
after = [ "pia-vpn-wireguard.service" ]; after = [ "pia-vpn-wireguard.service" ];
requires = [ "pia-vpn-wireguard.service" ]; requires = [ "pia-vpn-wireguard.service" ];
path = with pkgs; [ curl ]; path = with pkgs; [ curl ];
script = ''
signature=`sed '1q;d' /tmp/${cfg.interfaceName}-port-renewal` serviceConfig = {
payload=`sed '2q;d' /tmp/${cfg.interfaceName}-port-renewal` Type = "oneshot";
bind_port_response=`curl -Gs -m 5 --connect-to "${cfg.serverHostname}::${cfg.serverIp}:" --cacert "${./ca.rsa.4096.crt}" --data-urlencode "payload=$payload" --data-urlencode "signature=$signature" "https://${cfg.serverHostname}:19999/bindPort"` };
'';
script = refreshPIAPort;
}; };
vpn-container.config.systemd.timers.pia-vpn-wireguard-forward-port = { vpn-container.config.systemd.timers.pia-vpn-wireguard-forward-port = {
enable = cfg.portForwarding; enable = portForwarding;
# partOf = [ "pia-vpn-wireguard-forward-port.service" ]; partOf = [ "pia-vpn-wireguard-forward-port.service" ];
wantedBy = [ "timers.target" ]; wantedBy = [ "timers.target" ];
timerConfig.OnCalendar = "*:0/10"; # 10 minutes timerConfig = {
OnCalendar = "*:0/10"; # 10 minutes
RandomizedDelaySec = "1m"; # vary by 1 min to give PIA servers some relief
};
}; };
# TODO enable firewall on the PIA interface age.secrets."pia-login.conf".file = ../../secrets/pia-login.age;
# TODO handle errors
# TODO handle 2 month limit for port
# TODO print status, success, and failures to the console
age.secrets."pia-login.conf".file = ../../secrets/pia-login.conf;
}; };
} }

View File

@@ -10,36 +10,36 @@ with builtins;
let let
cfg = config.keepalive-ping; cfg = config.keepalive-ping;
# keepalive-ping = {
serviceTemplate = host: serviceTemplate = host:
{ {
"keepalive-ping@${host}" = { "keepalive-ping@${host}" = {
description = "Periodic ping keep alive for ${host} connection"; description = "Periodic ping keep alive for ${host} connection";
requires = [ "network-online.target" ]; requires = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ]; after = [ "network.target" "network-online.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig.Restart="always"; serviceConfig.Restart = "always";
path = with pkgs; [ iputils ]; path = with pkgs; [ iputils ];
script = '' script = ''
ping -i ${cfg.delay} ${host} &>/dev/null ping -i ${cfg.delay} ${host} &>/dev/null
''; '';
};
}; };
};
combineAttrs = foldl recursiveUpdate {}; combineAttrs = foldl recursiveUpdate { };
serviceList = map serviceTemplate cfg.hosts; serviceList = map serviceTemplate cfg.hosts;
services = combineAttrs serviceList; services = combineAttrs serviceList;
in { in
{
options.keepalive-ping = { options.keepalive-ping = {
enable = mkEnableOption "Enable keep alive ping task"; enable = mkEnableOption "Enable keep alive ping task";
hosts = mkOption { hosts = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [ ];
description = '' description = ''
Hosts to ping periodically Hosts to ping periodically
''; '';
@@ -56,4 +56,4 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
systemd.services = services; systemd.services = services;
}; };
} }

View File

@@ -8,9 +8,13 @@ in
{ {
options.services.tailscale.exitNode = mkEnableOption "Enable exit node support"; options.services.tailscale.exitNode = mkEnableOption "Enable exit node support";
config.services.tailscale.enable = !config.boot.isContainer; config.services.tailscale.enable = mkDefault (!config.boot.isContainer);
# MagicDNS
config.networking.nameservers = mkIf cfg.enable [ "1.1.1.1" "8.8.8.8" ];
config.networking.search = mkIf cfg.enable [ "koi-bebop.ts.net" ];
# exit node # exit node
config.networking.firewall.checkReversePath = mkIf cfg.exitNode "loose"; config.networking.firewall.checkReversePath = mkIf cfg.exitNode "loose";
config.networking.ip_forward = mkIf cfg.exitNode true; config.networking.ip_forward = mkIf cfg.exitNode true;
} }

View File

@@ -30,7 +30,7 @@ in
config = mkOption { config = mkOption {
type = types.anything; type = types.anything;
default = {}; default = { };
example = '' example = ''
{ {
services.nginx.enable = true; services.nginx.enable = true;
@@ -44,6 +44,7 @@ in
config = mkIf cfg.enable { config = mkIf cfg.enable {
pia.wireguard.enable = !cfg.useOpenVPN; pia.wireguard.enable = !cfg.useOpenVPN;
pia.wireguard.forwardPortForTransmission = !cfg.useOpenVPN;
containers.${cfg.containerName} = { containers.${cfg.containerName} = {
ephemeral = true; ephemeral = true;
@@ -69,12 +70,16 @@ in
localAddress = "172.16.100.2"; localAddress = "172.16.100.2";
config = { config = {
imports = allModules ++ [cfg.config]; imports = allModules ++ [ cfg.config ];
# speeds up evaluation # speeds up evaluation
nixpkgs.pkgs = pkgs; nixpkgs.pkgs = pkgs;
networking.firewall.enable = mkForce false; # networking.firewall.enable = mkForce false;
networking.firewall.trustedInterfaces = [
# completely trust internal interface to host
"eth0"
];
pia.openvpn.enable = cfg.useOpenVPN; pia.openvpn.enable = cfg.useOpenVPN;
pia.openvpn.server = "swiss.privacy.network"; # swiss vpn pia.openvpn.server = "swiss.privacy.network"; # swiss vpn
@@ -101,4 +106,4 @@ in
networking.usePredictableInterfaceNames = false; networking.usePredictableInterfaceNames = false;
networking.nat.externalInterface = "eth0"; networking.nat.externalInterface = "eth0";
}; };
} }

View File

@@ -1,16 +0,0 @@
{ lib, config, ... }:
let
cfg = config.services.zerotierone;
in {
config = lib.mkIf cfg.enable {
keepalive-ping.enable = true;
services.zerotierone.joinNetworks = [
"565799d8f6d654c0"
];
networking.firewall.allowedUDPPorts = [
9993
];
};
}

View File

@@ -2,7 +2,8 @@
let let
cfg = config.de; cfg = config.de;
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
# enable pulseaudio support for packages # enable pulseaudio support for packages
nixpkgs.config.pulseaudio = true; nixpkgs.config.pulseaudio = true;
@@ -16,45 +17,6 @@ in {
alsa.support32Bit = true; alsa.support32Bit = true;
pulse.enable = true; pulse.enable = true;
jack.enable = true; jack.enable = true;
# use the example session manager (no others are packaged yet so this is enabled by default,
# no need to redefine it in your config for now)
#media-session.enable = true;
config.pipewire = {
"context.objects" = [
{
# A default dummy driver. This handles nodes marked with the "node.always-driver"
# properyty when no other driver is currently active. JACK clients need this.
factory = "spa-node-factory";
args = {
"factory.name" = "support.node.driver";
"node.name" = "Dummy-Driver";
"priority.driver" = 8000;
};
}
{
factory = "adapter";
args = {
"factory.name" = "support.null-audio-sink";
"node.name" = "Microphone-Proxy";
"node.description" = "Microphone";
"media.class" = "Audio/Source/Virtual";
"audio.position" = "MONO";
};
}
{
factory = "adapter";
args = {
"factory.name" = "support.null-audio-sink";
"node.name" = "Main-Output-Proxy";
"node.description" = "Main Output";
"media.class" = "Audio/Sink";
"audio.position" = "FL,FR";
};
}
];
};
}; };
users.users.googlebot.extraGroups = [ "audio" ]; users.users.googlebot.extraGroups = [ "audio" ];

View File

@@ -49,7 +49,8 @@ let
]; ];
}; };
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
# chromium with specific extensions + settings # chromium with specific extensions + settings
programs.chromium = { programs.chromium = {
@@ -92,7 +93,7 @@ in {
enable = true; enable = true;
extraPackages = with pkgs; [ extraPackages = with pkgs; [
intel-media-driver # LIBVA_DRIVER_NAME=iHD intel-media-driver # LIBVA_DRIVER_NAME=iHD
vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium) vaapiIntel # LIBVA_DRIVER_NAME=i965 (older but works better for Firefox/Chromium)
# vaapiVdpau # vaapiVdpau
libvdpau-va-gl libvdpau-va-gl
nvidia-vaapi-driver nvidia-vaapi-driver

View File

@@ -2,15 +2,16 @@
let let
cfg = config.de; cfg = config.de;
in { in
{
imports = [ imports = [
./kde.nix ./kde.nix
./xfce.nix ./xfce.nix
./yubikey.nix ./yubikey.nix
./chromium.nix ./chromium.nix
# ./firefox.nix # ./firefox.nix
./audio.nix ./audio.nix
# ./torbrowser.nix # ./torbrowser.nix
./pithos.nix ./pithos.nix
./spotify.nix ./spotify.nix
./vscodium.nix ./vscodium.nix
@@ -50,7 +51,12 @@ in {
arduino arduino
yt-dlp yt-dlp
jellyfin-media-player jellyfin-media-player
joplin-desktop
config.inputs.deploy-rs.packages.${config.currentSystem}.deploy-rs config.inputs.deploy-rs.packages.${config.currentSystem}.deploy-rs
# For Nix IDE
nixpkgs-fmt
rnix-lsp
]; ];
# Networking # Networking

View File

@@ -2,10 +2,11 @@
let let
cfg = config.de; cfg = config.de;
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
users.users.googlebot.packages = [ users.users.googlebot.packages = [
pkgs.discord pkgs.discord
]; ];
}; };
} }

View File

@@ -20,7 +20,7 @@ let
}; };
firefox = pkgs.wrapFirefox somewhatPrivateFF { firefox = pkgs.wrapFirefox somewhatPrivateFF {
desktopName = "Sneed Browser"; desktopName = "Sneed Browser";
nixExtensions = [ nixExtensions = [
(pkgs.fetchFirefoxAddon { (pkgs.fetchFirefoxAddon {
@@ -71,8 +71,8 @@ let
TopSites = false; TopSites = false;
}; };
UserMessaging = { UserMessaging = {
ExtensionRecommendations = false; ExtensionRecommendations = false;
SkipOnboarding = true; SkipOnboarding = true;
}; };
WebsiteFilter = { WebsiteFilter = {
Block = [ Block = [
@@ -92,4 +92,4 @@ in
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
users.users.googlebot.packages = [ firefox ]; users.users.googlebot.packages = [ firefox ];
}; };
} }

View File

@@ -2,7 +2,8 @@
let let
cfg = config.de; cfg = config.de;
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
# kde plasma # kde plasma
services.xserver = { services.xserver = {
@@ -19,5 +20,5 @@ in {
# plasma5Packages.kmail-account-wizard # plasma5Packages.kmail-account-wizard
kate kate
]; ];
}; };
} }

View File

@@ -1,36 +1,48 @@
# mounts the samba share on s0 over zeroteir # mounts the samba share on s0 over tailscale
{ config, lib, ... }: { config, lib, pkgs, ... }:
let let
cfg = config.services.mount-samba; cfg = config.services.mount-samba;
# prevents hanging on network split # prevents hanging on network split and other similar niceties to ensure a stable connection
network_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s,nostrictsync,cache=loose,handlecache,handletimeout=30000,rwpidforward,mapposix,soft,resilienthandles,echo_interval=10,noblocksend"; network_opts = "nostrictsync,cache=strict,handlecache,handletimeout=30000,rwpidforward,mapposix,soft,resilienthandles,echo_interval=10,noblocksend,fsc";
systemd_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";
user_opts = "uid=${toString config.users.users.googlebot.uid},file_mode=0660,dir_mode=0770,user"; user_opts = "uid=${toString config.users.users.googlebot.uid},file_mode=0660,dir_mode=0770,user";
auth_opts = "credentials=/run/agenix/smb-secrets"; auth_opts = "sec=ntlmv2i,credentials=/run/agenix/smb-secrets";
version_opts = "vers=2.1"; version_opts = "vers=3.1.1";
opts = "${network_opts},${user_opts},${version_opts},${auth_opts}"; opts = "${systemd_opts},${network_opts},${user_opts},${version_opts},${auth_opts}";
in { in
{
options.services.mount-samba = { options.services.mount-samba = {
enable = lib.mkEnableOption "enable mounting samba shares"; enable = lib.mkEnableOption "enable mounting samba shares";
}; };
config = lib.mkIf (cfg.enable && config.services.zerotierone.enable) { config = lib.mkIf (cfg.enable && config.services.tailscale.enable) {
fileSystems."/mnt/public" = { fileSystems."/mnt/public" = {
device = "//s0.zt.neet.dev/public"; device = "//s0.koi-bebop.ts.net/public";
fsType = "cifs"; fsType = "cifs";
options = [ opts ]; options = [ opts ];
}; };
fileSystems."/mnt/private" = { fileSystems."/mnt/private" = {
device = "//s0.zt.neet.dev/googlebot"; device = "//s0.koi-bebop.ts.net/googlebot";
fsType = "cifs"; fsType = "cifs";
options = [ opts ]; options = [ opts ];
}; };
age.secrets.smb-secrets.file = ../../secrets/smb-secrets.age; age.secrets.smb-secrets.file = ../../secrets/smb-secrets.age;
environment.shellAliases = {
# remount storage
remount_public = "sudo systemctl restart mnt-public.mount";
remount_private = "sudo systemctl restart mnt-private.mount";
# Encrypted Vault
vault_unlock = "${pkgs.gocryptfs}/bin/gocryptfs /mnt/private/.vault/ /mnt/vault/";
vault_lock = "umount /mnt/vault/";
};
}; };
} }

View File

@@ -2,7 +2,8 @@
let let
cfg = config.de; cfg = config.de;
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
nixpkgs.overlays = [ nixpkgs.overlays = [
(self: super: { (self: super: {
@@ -11,7 +12,7 @@ in {
version = "1.5.1"; version = "1.5.1";
src = super.fetchFromGitHub { src = super.fetchFromGitHub {
owner = pname; owner = pname;
repo = pname; repo = pname;
rev = version; rev = version;
sha256 = "il7OAALpHFZ6wjco9Asp04zWHCD8Ni+iBdiJWcMiQA4="; sha256 = "il7OAALpHFZ6wjco9Asp04zWHCD8Ni+iBdiJWcMiQA4=";
}; };

View File

@@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services.spotifyd; cfg = config.services.spotifyd;
toml = pkgs.formats.toml {}; toml = pkgs.formats.toml { };
spotifydConf = toml.generate "spotify.conf" cfg.settings; spotifydConf = toml.generate "spotify.conf" cfg.settings;
in in
{ {
@@ -17,7 +17,7 @@ in
enable = mkEnableOption "spotifyd, a Spotify playing daemon"; enable = mkEnableOption "spotifyd, a Spotify playing daemon";
settings = mkOption { settings = mkOption {
default = {}; default = { };
type = toml.type; type = toml.type;
example = { global.bitrate = 320; }; example = { global.bitrate = 320; };
description = '' description = ''
@@ -28,7 +28,7 @@ in
users = mkOption { users = mkOption {
type = with types; listOf str; type = with types; listOf str;
default = []; default = [ ];
description = '' description = ''
Usernames to be added to the "spotifyd" group, so that they Usernames to be added to the "spotifyd" group, so that they
can start and interact with the userspace daemon. can start and interact with the userspace daemon.
@@ -83,4 +83,4 @@ in
}; };
}; };
}; };
} }

View File

@@ -2,7 +2,8 @@
let let
cfg = config.de; cfg = config.de;
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
programs.steam.enable = true; programs.steam.enable = true;
hardware.steam-hardware.enable = true; # steam controller hardware.steam-hardware.enable = true; # steam controller
@@ -11,4 +12,4 @@ in {
pkgs.steam pkgs.steam
]; ];
}; };
} }

View File

@@ -2,7 +2,8 @@
let let
cfg = config.de; cfg = config.de;
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
nixpkgs.overlays = [ nixpkgs.overlays = [
(self: super: { (self: super: {

View File

@@ -2,7 +2,8 @@
let let
cfg = config.de.touchpad; cfg = config.de.touchpad;
in { in
{
options.de.touchpad = { options.de.touchpad = {
enable = lib.mkEnableOption "enable touchpad"; enable = lib.mkEnableOption "enable touchpad";
}; };

View File

@@ -4,8 +4,8 @@ let
cfg = config.de; cfg = config.de;
extensions = with pkgs.vscode-extensions; [ extensions = with pkgs.vscode-extensions; [
# bbenoist.Nix # nix syntax support # bbenoist.Nix # nix syntax support
# arrterian.nix-env-selector # nix dev envs # arrterian.nix-env-selector # nix dev envs
]; ];
vscodium-with-extensions = pkgs.vscode-with-extensions.override { vscodium-with-extensions = pkgs.vscode-with-extensions.override {

View File

@@ -2,7 +2,8 @@
let let
cfg = config.de; cfg = config.de;
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.xserver = { services.xserver = {
enable = true; enable = true;

View File

@@ -2,7 +2,8 @@
let let
cfg = config.de; cfg = config.de;
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
# yubikey # yubikey
services.pcscd.enable = true; services.pcscd.enable = true;

View File

@@ -3,13 +3,13 @@
with lib; with lib;
let let
cfg = config.ceph; cfg = config.ceph;
in { in
options.ceph = { {
}; options.ceph = { };
config = mkIf cfg.enable { config = mkIf cfg.enable {
# ceph.enable = true; # ceph.enable = true;
## S3 Object gateway ## S3 Object gateway
#ceph.rgw.enable = true; #ceph.rgw.enable = true;
#ceph.rgw.daemons = [ #ceph.rgw.daemons = [
@@ -40,4 +40,4 @@ in {
ceph.global.fsid = "925773DC-D95F-476C-BBCD-08E01BF0865F"; ceph.global.fsid = "925773DC-D95F-476C-BBCD-08E01BF0865F";
}; };
} }

View File

@@ -10,9 +10,14 @@
./matrix.nix ./matrix.nix
./zerobin.nix ./zerobin.nix
./gitea.nix ./gitea.nix
./gitea-runner.nix
./privatebin/privatebin.nix ./privatebin/privatebin.nix
./radio.nix ./radio.nix
./samba.nix ./samba.nix
./owncast.nix ./owncast.nix
./mailserver.nix
./nextcloud.nix
./iodine.nix
./searx.nix
]; ];
} }

View File

@@ -0,0 +1,98 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.gitea-runner;
in
{
options.services.gitea-runner = {
enable = lib.mkEnableOption "Enables gitea runner";
dataDir = lib.mkOption {
default = "/var/lib/gitea-runner";
type = lib.types.str;
description = lib.mdDoc "gitea runner data directory.";
};
instanceUrl = lib.mkOption {
type = lib.types.str;
};
registrationTokenFile = lib.mkOption {
type = lib.types.path;
};
};
config = lib.mkIf cfg.enable {
virtualisation.docker.enable = true;
users.users.gitea-runner = {
description = "Gitea Runner Service";
home = cfg.dataDir;
useDefaultShell = true;
group = "gitea-runner";
isSystemUser = true;
createHome = true;
extraGroups = [
"docker" # allow creating docker containers
];
};
users.groups.gitea-runner = { };
# registration token
services.gitea-runner.registrationTokenFile = "/run/agenix/gitea-runner-registration-token";
age.secrets.gitea-runner-registration-token = {
file = ../../secrets/gitea-runner-registration-token.age;
owner = "gitea-runner";
};
systemd.services.gitea-runner = {
description = "Gitea Runner";
serviceConfig = {
WorkingDirectory = cfg.dataDir;
User = "gitea-runner";
Group = "gitea-runner";
};
requires = [ "network-online.target" ];
after = [ "network.target" "network-online.target" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ gitea-actions-runner ];
# based on https://gitea.com/gitea/act_runner/src/branch/main/run.sh
script = ''
. ${cfg.registrationTokenFile}
if [[ ! -s .runner ]]; then
try=$((try + 1))
success=0
LOGFILE="$(mktemp)"
# The point of this loop is to make it simple, when running both act_runner and gitea in docker,
# for the act_runner to wait a moment for gitea to become available before erroring out. Within
# the context of a single docker-compose, something similar could be done via healthchecks, but
# this is more flexible.
while [[ $success -eq 0 ]] && [[ $try -lt ''${10:-10} ]]; do
act_runner register \
--instance "${cfg.instanceUrl}" \
--token "$GITEA_RUNNER_REGISTRATION_TOKEN" \
--name "${config.networking.hostName}" \
--no-interactive > $LOGFILE 2>&1
cat $LOGFILE
cat $LOGFILE | grep 'Runner registered successfully' > /dev/null
if [[ $? -eq 0 ]]; then
echo "SUCCESS"
success=1
else
echo "Waiting to retry ..."
sleep 5
fi
done
fi
exec act_runner daemon
'';
};
};
}

View File

@@ -1,8 +1,9 @@
{ lib, config, ... }: { lib, pkgs, config, ... }:
let let
cfg = config.services.gitea; cfg = config.services.gitea;
in { in
{
options.services.gitea = { options.services.gitea = {
hostname = lib.mkOption { hostname = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@@ -14,11 +15,8 @@ in {
domain = cfg.hostname; domain = cfg.hostname;
rootUrl = "https://${cfg.hostname}/"; rootUrl = "https://${cfg.hostname}/";
appName = cfg.hostname; appName = cfg.hostname;
ssh.enable = true;
# lfs.enable = true; # lfs.enable = true;
dump.enable = true; # dump.enable = true;
cookieSecure = true;
disableRegistration = true;
settings = { settings = {
other = { other = {
SHOW_FOOTER_VERSION = false; SHOW_FOOTER_VERSION = false;
@@ -26,8 +24,37 @@ in {
ui = { ui = {
DEFAULT_THEME = "arc-green"; DEFAULT_THEME = "arc-green";
}; };
service = {
DISABLE_REGISTRATION = true;
};
session = {
COOKIE_SECURE = true;
};
mailer = {
ENABLED = true;
MAILER_TYPE = "smtp";
SMTP_ADDR = "mail.neet.dev";
SMTP_PORT = "465";
IS_TLS_ENABLED = true;
USER = "robot@runyan.org";
FROM = "no-reply@neet.dev";
};
actions = {
ENABLED = true;
};
}; };
mailerPasswordFile = "/run/agenix/robots-email-pw";
}; };
age.secrets.robots-email-pw = {
file = ../../secrets/robots-email-pw.age;
owner = config.services.gitea.user;
};
# backups
backup.group."gitea".paths = [
config.services.gitea.stateDir
];
services.nginx.enable = true; services.nginx.enable = true;
services.nginx.virtualHosts.${cfg.hostname} = { services.nginx.virtualHosts.${cfg.hostname} = {
enableACME = true; enableACME = true;
@@ -37,4 +64,4 @@ in {
}; };
}; };
}; };
} }

View File

@@ -20,6 +20,6 @@ in
hydraURL = "https://${domain}"; hydraURL = "https://${domain}";
useSubstitutes = true; useSubstitutes = true;
notificationSender = notifyEmail; notificationSender = notifyEmail;
buildMachinesFiles = []; buildMachinesFiles = [ ];
}; };
} }

View File

@@ -7,7 +7,8 @@
let let
cfg = config.services.icecast; cfg = config.services.icecast;
in { in
{
options.services.icecast = { options.services.icecast = {
mount = lib.mkOption { mount = lib.mkOption {
type = lib.types.str; type = lib.types.str;

21
common/server/iodine.nix Normal file
View File

@@ -0,0 +1,21 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.iodine.server;
in
{
config = lib.mkIf cfg.enable {
# iodine DNS-based vpn
services.iodine.server = {
ip = "192.168.99.1";
domain = "tun.neet.dev";
passwordFile = "/run/agenix/iodine";
};
age.secrets.iodine.file = ../../secrets/iodine.age;
networking.firewall.allowedUDPPorts = [ 53 ];
networking.nat.internalInterfaces = [
"dns0" # iodine
];
};
}

View File

@@ -0,0 +1,100 @@
{ config, pkgs, lib, ... }:
with builtins;
let
cfg = config.mailserver;
domains = [
"neet.space"
"neet.dev"
"neet.cloud"
"runyan.org"
"runyan.rocks"
"thunderhex.com"
"tar.ninja"
"bsd.ninja"
"bsd.rocks"
];
in
{
config = lib.mkIf cfg.enable {
# kresd doesn't work with tailscale MagicDNS
mailserver.localDnsResolver = false;
services.resolved.enable = true;
mailserver = {
fqdn = "mail.neet.dev";
dkimKeyBits = 2048;
indexDir = "/var/lib/mailindex";
enableManageSieve = true;
fullTextSearch.enable = true;
fullTextSearch.indexAttachments = true;
fullTextSearch.memoryLimit = 500;
inherit domains;
loginAccounts = {
"jeremy@runyan.org" = {
hashedPasswordFile = "/run/agenix/hashed-email-pw";
# catchall for all domains
aliases = map (domain: "@${domain}") domains;
};
"robot@runyan.org" = {
aliases = [
"no-reply@neet.dev"
"robot@neet.dev"
];
sendOnly = true;
hashedPasswordFile = "/run/agenix/hashed-robots-email-pw";
};
};
rejectRecipients = [
"george@runyan.org"
"joslyn@runyan.org"
"damon@runyan.org"
"jonas@runyan.org"
];
certificateScheme = 3; # use let's encrypt for certs
};
age.secrets.hashed-email-pw.file = ../../secrets/hashed-email-pw.age;
age.secrets.hashed-robots-email-pw.file = ../../secrets/hashed-robots-email-pw.age;
# sendmail to use xxx@domain instead of xxx@mail.domain
services.postfix.origin = "$mydomain";
# relay sent mail through mailgun
# https://www.howtoforge.com/community/threads/different-smtp-relays-for-different-domains-in-postfix.82711/#post-392620
services.postfix.config = {
smtp_sasl_auth_enable = "yes";
smtp_sasl_security_options = "noanonymous";
smtp_sasl_password_maps = "hash:/var/lib/postfix/conf/sasl_relay_passwd";
smtp_use_tls = "yes";
sender_dependent_relayhost_maps = "hash:/var/lib/postfix/conf/sender_relay";
smtp_sender_dependent_authentication = "yes";
};
services.postfix.mapFiles.sender_relay =
let
relayHost = "[smtp.mailgun.org]:587";
in
pkgs.writeText "sender_relay"
(concatStringsSep "\n" (map (domain: "@${domain} ${relayHost}") domains));
services.postfix.mapFiles.sasl_relay_passwd = "/run/agenix/sasl_relay_passwd";
age.secrets.sasl_relay_passwd.file = ../../secrets/sasl_relay_passwd.age;
# webmail
services.nginx.enable = true;
services.roundcube = {
enable = true;
hostName = config.mailserver.fqdn;
extraConfig = ''
# starttls needed for authentication, so the fqdn required to match the certificate
$config['smtp_server'] = "tls://${config.mailserver.fqdn}";
$config['smtp_user'] = "%u";
$config['smtp_pass'] = "%p";
'';
};
# backups
backup.group."email".paths = [
config.mailserver.mailDirectory
];
};
}

View File

@@ -3,7 +3,8 @@
let let
cfg = config.services.matrix; cfg = config.services.matrix;
certs = config.security.acme.certs; certs = config.security.acme.certs;
in { in
{
options.services.matrix = { options.services.matrix = {
enable = lib.mkEnableOption "enable matrix"; enable = lib.mkEnableOption "enable matrix";
element-web = { element-web = {
@@ -62,15 +63,15 @@ in {
settings = { settings = {
server_name = cfg.host; server_name = cfg.host;
enable_registration = cfg.enable_registration; enable_registration = cfg.enable_registration;
listeners = [ { listeners = [{
bind_addresses = ["127.0.0.1"]; bind_addresses = [ "127.0.0.1" ];
port = cfg.port; port = cfg.port;
tls = false; tls = false;
resources = [ { resources = [{
compress = true; compress = true;
names = [ "client" "federation" ]; names = [ "client" "federation" ];
} ]; }];
} ]; }];
turn_uris = [ turn_uris = [
"turn:${cfg.turn.host}:${toString cfg.turn.port}?transport=udp" "turn:${cfg.turn.host}:${toString cfg.turn.port}?transport=udp"
"turn:${cfg.turn.host}:${toString cfg.turn.port}?transport=tcp" "turn:${cfg.turn.host}:${toString cfg.turn.port}?transport=tcp"
@@ -120,7 +121,7 @@ in {
services.nginx = { services.nginx = {
enable = true; enable = true;
virtualHosts.${cfg.host} = { virtualHosts.${cfg.host} = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
listen = [ listen = [
@@ -137,7 +138,8 @@ in {
]; ];
locations."/".proxyPass = "http://localhost:${toString cfg.port}"; locations."/".proxyPass = "http://localhost:${toString cfg.port}";
}; };
virtualHosts.${cfg.turn.host} = { # get TLS cert for TURN server virtualHosts.${cfg.turn.host} = {
# get TLS cert for TURN server
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
}; };
@@ -214,4 +216,4 @@ in {
openFirewall = true; openFirewall = true;
}; };
}; };
} }

View File

@@ -3,7 +3,8 @@
let let
cfg = config.services.murmur; cfg = config.services.murmur;
certs = config.security.acme.certs; certs = config.security.acme.certs;
in { in
{
options.services.murmur.domain = lib.mkOption { options.services.murmur.domain = lib.mkOption {
type = lib.types.str; type = lib.types.str;
}; };

View File

@@ -0,0 +1,34 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.nextcloud;
in
{
config = lib.mkIf cfg.enable {
services.nextcloud = {
https = true;
package = pkgs.nextcloud25;
hostName = "neet.cloud";
config.dbtype = "sqlite";
config.adminuser = "jeremy";
config.adminpassFile = "/run/agenix/nextcloud-pw";
autoUpdateApps.enable = true;
enableBrokenCiphersForSSE = false;
};
age.secrets.nextcloud-pw = {
file = ../../secrets/nextcloud-pw.age;
owner = "nextcloud";
};
# backups
backup.group."nextcloud".paths = [
config.services.nextcloud.home
];
services.nginx.virtualHosts.${config.services.nextcloud.hostName} = {
enableACME = true;
forceSSL = true;
};
};
}

View File

@@ -5,7 +5,8 @@ let
nginxWithRTMP = pkgs.nginx.override { nginxWithRTMP = pkgs.nginx.override {
modules = [ pkgs.nginxModules.rtmp ]; modules = [ pkgs.nginxModules.rtmp ];
}; };
in { in
{
options.services.nginx.stream = { options.services.nginx.stream = {
enable = lib.mkEnableOption "enable nginx rtmp/hls/dash video streaming"; enable = lib.mkEnableOption "enable nginx rtmp/hls/dash video streaming";
port = lib.mkOption { port = lib.mkOption {
@@ -72,4 +73,4 @@ in {
cfg.port cfg.port
]; ];
}; };
} }

View File

@@ -2,7 +2,8 @@
let let
cfg = config.services.nginx; cfg = config.services.nginx;
in { in
{
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable {
services.nginx = { services.nginx = {
recommendedGzipSettings = true; recommendedGzipSettings = true;
@@ -13,4 +14,4 @@ in {
networking.firewall.allowedTCPPorts = [ 80 443 ]; networking.firewall.allowedTCPPorts = [ 80 443 ];
}; };
} }

View File

@@ -4,7 +4,8 @@ with lib;
let let
cfg = config.services.owncast; cfg = config.services.owncast;
in { in
{
options.services.owncast = { options.services.owncast = {
hostname = lib.mkOption { hostname = lib.mkOption {
type = types.str; type = types.str;
@@ -28,4 +29,4 @@ in {
}; };
}; };
}; };
} }

View File

@@ -14,7 +14,8 @@ let
cp -ar $src $out cp -ar $src $out
''; '';
}; };
in { in
{
options.services.privatebin = { options.services.privatebin = {
enable = lib.mkEnableOption "enable privatebin"; enable = lib.mkEnableOption "enable privatebin";
host = lib.mkOption { host = lib.mkOption {
@@ -30,7 +31,7 @@ in {
group = "privatebin"; group = "privatebin";
isSystemUser = true; isSystemUser = true;
}; };
users.groups.privatebin = {}; users.groups.privatebin = { };
services.nginx.enable = true; services.nginx.enable = true;
services.nginx.virtualHosts.${cfg.host} = { services.nginx.virtualHosts.${cfg.host} = {
@@ -53,7 +54,7 @@ in {
"d '/var/lib/privatebin' 0750 privatebin privatebin - -" "d '/var/lib/privatebin' 0750 privatebin privatebin - -"
]; ];
services.phpfpm.pools.privatebin = { services.phpfpm.pools.privatebin = {
user = "privatebin"; user = "privatebin";
group = "privatebin"; group = "privatebin";
phpEnv = { phpEnv = {

View File

@@ -3,7 +3,8 @@
let let
cfg = config.services.radio; cfg = config.services.radio;
radioPackage = config.inputs.radio.packages.${config.currentSystem}.radio; radioPackage = config.inputs.radio.packages.${config.currentSystem}.radio;
in { in
{
options.services.radio = { options.services.radio = {
enable = lib.mkEnableOption "enable radio"; enable = lib.mkEnableOption "enable radio";
user = lib.mkOption { user = lib.mkOption {
@@ -56,11 +57,11 @@ in {
home = cfg.dataDir; home = cfg.dataDir;
createHome = true; createHome = true;
}; };
users.groups.${cfg.group} = {}; users.groups.${cfg.group} = { };
systemd.services.radio = { systemd.services.radio = {
enable = true; enable = true;
after = ["network.target"]; after = [ "network.target" ];
wantedBy = ["multi-user.target"]; wantedBy = [ "multi-user.target" ];
serviceConfig.ExecStart = "${radioPackage}/bin/radio ${config.services.icecast.listen.address}:${toString config.services.icecast.listen.port} ${config.services.icecast.mount} 5500"; serviceConfig.ExecStart = "${radioPackage}/bin/radio ${config.services.icecast.listen.address}:${toString config.services.icecast.listen.port} ${config.services.icecast.mount} 5500";
serviceConfig.User = cfg.user; serviceConfig.User = cfg.user;
serviceConfig.Group = cfg.group; serviceConfig.Group = cfg.group;
@@ -71,4 +72,4 @@ in {
''; '';
}; };
}; };
} }

View File

@@ -25,9 +25,7 @@
printing = cups printing = cups
printcap name = cups printcap name = cups
# horrible files hide files = /.nobackup/.DS_Store/._.DS_Store/
veto files = /._*/.DS_Store/ /._*/._.DS_Store/
delete veto files = yes
''; '';
shares = { shares = {
@@ -77,6 +75,13 @@
}; };
}; };
# backups
backup.group."samba".paths = [
config.services.samba.shares.googlebot.path
config.services.samba.shares.cris.path
config.services.samba.shares.public.path
];
# Windows discovery of samba server # Windows discovery of samba server
services.samba-wsdd = { services.samba-wsdd = {
enable = true; enable = true;
@@ -110,6 +115,6 @@
# samba user for share # samba user for share
users.users.cris.isSystemUser = true; users.users.cris.isSystemUser = true;
users.users.cris.group = "cris"; users.users.cris.group = "cris";
users.groups.cris = {}; users.groups.cris = { };
}; };
} }

30
common/server/searx.nix Normal file
View File

@@ -0,0 +1,30 @@
{ config, pkgs, lib, ... }:
let
cfg = config.services.searx;
in
{
config = lib.mkIf cfg.enable {
services.searx = {
environmentFile = "/run/agenix/searx";
settings = {
server.port = 43254;
server.secret_key = "@SEARX_SECRET_KEY@";
engines = [{
name = "wolframalpha";
shortcut = "wa";
api_key = "@WOLFRAM_API_KEY@";
engine = "wolframalpha_api";
}];
};
};
services.nginx.virtualHosts."search.neet.space" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.searx.settings.server.port}";
};
};
age.secrets.searx.file = ../../secrets/searx.age;
};
}

View File

@@ -2,7 +2,8 @@
let let
cfg = config.services.thelounge; cfg = config.services.thelounge;
in { in
{
options.services.thelounge = { options.services.thelounge = {
fileUploadBaseUrl = lib.mkOption { fileUploadBaseUrl = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@@ -28,7 +29,7 @@ in {
reverseProxy = true; reverseProxy = true;
maxHistory = -1; maxHistory = -1;
https.enable = false; https.enable = false;
# theme = "thelounge-theme-solarized"; # theme = "thelounge-theme-solarized";
prefetch = false; prefetch = false;
prefetchStorage = false; prefetchStorage = false;
fileUpload = { fileUpload = {
@@ -42,6 +43,10 @@ in {
}; };
}; };
backup.group."thelounge".paths = [
"/var/lib/thelounge/"
];
# the lounge client # the lounge client
services.nginx.virtualHosts.${cfg.host} = { services.nginx.virtualHosts.${cfg.host} = {
enableACME = true; enableACME = true;

View File

@@ -15,14 +15,14 @@ let
in in
{ {
networking.firewall.allowedUDPPorts = [ rtp-port ]; networking.firewall.allowedUDPPorts = [ rtp-port ];
networking.firewall.allowedTCPPortRanges = [ { networking.firewall.allowedTCPPortRanges = [{
from = webrtc-peer-lower-port; from = webrtc-peer-lower-port;
to = webrtc-peer-upper-port; to = webrtc-peer-upper-port;
} ]; }];
networking.firewall.allowedUDPPortRanges = [ { networking.firewall.allowedUDPPortRanges = [{
from = webrtc-peer-lower-port; from = webrtc-peer-lower-port;
to = webrtc-peer-upper-port; to = webrtc-peer-upper-port;
} ]; }];
virtualisation.docker.enable = true; virtualisation.docker.enable = true;
@@ -49,12 +49,12 @@ in
ports = [ ports = [
"${toStr ingest-port}:8084" "${toStr ingest-port}:8084"
]; ];
# imageFile = pkgs.dockerTools.pullImage { # imageFile = pkgs.dockerTools.pullImage {
# imageName = "projectlightspeed/ingest"; # imageName = "projectlightspeed/ingest";
# finalImageTag = "version-0.1.4"; # finalImageTag = "version-0.1.4";
# imageDigest = "sha256:9fc51833b7c27a76d26e40f092b9cec1ac1c4bfebe452e94ad3269f1f73ff2fc"; # imageDigest = "sha256:9fc51833b7c27a76d26e40f092b9cec1ac1c4bfebe452e94ad3269f1f73ff2fc";
# sha256 = "19kxl02x0a3i6hlnsfcm49hl6qxnq2f3hfmyv1v8qdaz58f35kd5"; # sha256 = "19kxl02x0a3i6hlnsfcm49hl6qxnq2f3hfmyv1v8qdaz58f35kd5";
# }; # };
}; };
"lightspeed-react" = { "lightspeed-react" = {
workdir = "/var/lib/lightspeed-react"; workdir = "/var/lib/lightspeed-react";
@@ -62,12 +62,12 @@ in
ports = [ ports = [
"${toStr web-port}:80" "${toStr web-port}:80"
]; ];
# imageFile = pkgs.dockerTools.pullImage { # imageFile = pkgs.dockerTools.pullImage {
# imageName = "projectlightspeed/react"; # imageName = "projectlightspeed/react";
# finalImageTag = "version-0.1.3"; # finalImageTag = "version-0.1.3";
# imageDigest = "sha256:b7c58425f1593f7b4304726b57aa399b6e216e55af9c0962c5c19333fae638b6"; # imageDigest = "sha256:b7c58425f1593f7b4304726b57aa399b6e216e55af9c0962c5c19333fae638b6";
# sha256 = "0d2jh7mr20h7dxgsp7ml7cw2qd4m8ja9rj75dpy59zyb6v0bn7js"; # sha256 = "0d2jh7mr20h7dxgsp7ml7cw2qd4m8ja9rj75dpy59zyb6v0bn7js";
# }; # };
}; };
"lightspeed-webrtc" = { "lightspeed-webrtc" = {
workdir = "/var/lib/lightspeed-webrtc"; workdir = "/var/lib/lightspeed-webrtc";
@@ -79,15 +79,18 @@ in
"${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}:${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}/udp" "${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}:${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}/udp"
]; ];
cmd = [ cmd = [
"lightspeed-webrtc" "--addr=0.0.0.0" "--ip=${domain}" "lightspeed-webrtc"
"--ports=${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}" "run" "--addr=0.0.0.0"
"--ip=${domain}"
"--ports=${toStr webrtc-peer-lower-port}-${toStr webrtc-peer-upper-port}"
"run"
]; ];
# imageFile = pkgs.dockerTools.pullImage { # imageFile = pkgs.dockerTools.pullImage {
# imageName = "projectlightspeed/webrtc"; # imageName = "projectlightspeed/webrtc";
# finalImageTag = "version-0.1.2"; # finalImageTag = "version-0.1.2";
# imageDigest = "sha256:ddf8b3dd294485529ec11d1234a3fc38e365a53c4738998c6bc2c6930be45ecf"; # imageDigest = "sha256:ddf8b3dd294485529ec11d1234a3fc38e365a53c4738998c6bc2c6930be45ecf";
# sha256 = "1bdy4ak99fjdphj5bsk8rp13xxmbqdhfyfab14drbyffivg9ad2i"; # sha256 = "1bdy4ak99fjdphj5bsk8rp13xxmbqdhfyfab14drbyffivg9ad2i";
# }; # };
}; };
}; };
}; };

View File

@@ -1,8 +1,8 @@
import ./module.nix ({ name, description, serviceConfig }: import ./module.nix ({ name, description, serviceConfig }:
{ {
systemd.user.services.${name} = { systemd.user.services.${name} = {
inherit description serviceConfig; inherit description serviceConfig;
wantedBy = [ "default.target" ]; wantedBy = [ "default.target" ];
}; };
}) })

View File

@@ -1,15 +1,15 @@
import ./module.nix ({ name, description, serviceConfig }: import ./module.nix ({ name, description, serviceConfig }:
{ {
systemd.user.services.${name} = { systemd.user.services.${name} = {
Unit = { Unit = {
Description = description; Description = description;
}; };
Service = serviceConfig; Service = serviceConfig;
Install = { Install = {
WantedBy = [ "default.target" ]; WantedBy = [ "default.target" ];
};
}; };
}; })
})

View File

@@ -2,7 +2,8 @@
let let
cfg = config.services.zerobin; cfg = config.services.zerobin;
in { in
{
options.services.zerobin = { options.services.zerobin = {
host = lib.mkOption { host = lib.mkOption {
type = lib.types.str; type = lib.types.str;

View File

@@ -21,6 +21,8 @@
shellInit = '' shellInit = ''
# disable annoying fish shell greeting # disable annoying fish shell greeting
set fish_greeting set fish_greeting
alias sudo="doas"
''; '';
}; };
@@ -28,10 +30,10 @@
myip = "dig +short myip.opendns.com @resolver1.opendns.com"; myip = "dig +short myip.opendns.com @resolver1.opendns.com";
# https://linuxreviews.org/HOWTO_Test_Disk_I/O_Performance # https://linuxreviews.org/HOWTO_Test_Disk_I/O_Performance
io_seq_read = "nix run nixpkgs#fio -- --name TEST --eta-newline=5s --filename=temp.file --rw=read --size=2g --io_size=10g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file"; io_seq_read = "${pkgs.fio}/bin/fio --name TEST --eta-newline=5s --filename=temp.file --rw=read --size=2g --io_size=10g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file";
io_seq_write = "nix run nixpkgs#fio -- --name TEST --eta-newline=5s --filename=temp.file --rw=write --size=2g --io_size=10g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file"; io_seq_write = "${pkgs.fio}/bin/fio --name TEST --eta-newline=5s --filename=temp.file --rw=write --size=2g --io_size=10g --blocksize=1024k --ioengine=libaio --fsync=10000 --iodepth=32 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file";
io_rand_read = "nix run nixpkgs#fio -- --name TEST --eta-newline=5s --filename=temp.file --rw=randread --size=2g --io_size=10g --blocksize=4k --ioengine=libaio --fsync=1 --iodepth=1 --direct=1 --numjobs=32 --runtime=60 --group_reporting; rm temp.file"; io_rand_read = "${pkgs.fio}/bin/fio --name TEST --eta-newline=5s --filename=temp.file --rw=randread --size=2g --io_size=10g --blocksize=4k --ioengine=libaio --fsync=1 --iodepth=1 --direct=1 --numjobs=32 --runtime=60 --group_reporting; rm temp.file";
io_rand_write = "nix run nixpkgs#fio -- --name TEST --eta-newline=5s --filename=temp.file --rw=randrw --size=2g --io_size=10g --blocksize=4k --ioengine=libaio --fsync=1 --iodepth=1 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file"; io_rand_write = "${pkgs.fio}/bin/fio --name TEST --eta-newline=5s --filename=temp.file --rw=randrw --size=2g --io_size=10g --blocksize=4k --ioengine=libaio --fsync=1 --iodepth=1 --direct=1 --numjobs=1 --runtime=60 --group_reporting; rm temp.file";
}; };
nixpkgs.overlays = [ nixpkgs.overlays = [
@@ -47,4 +49,4 @@
}); });
}) })
]; ];
} }

View File

@@ -1,74 +1,38 @@
rec { { config, lib, pkgs, ... }:
users = [
{
programs.ssh.knownHosts = lib.filterAttrs (n: v: v != null) (lib.concatMapAttrs
(host: cfg: {
${host} = {
hostNames = cfg.hostNames;
publicKey = cfg.hostKey;
};
"${host}-remote-unlock" =
if cfg.remoteUnlock != null then {
hostNames = builtins.filter (h: h != null) [ cfg.remoteUnlock.clearnetHost cfg.remoteUnlock.onionHost ];
publicKey = cfg.remoteUnlock.hostKey;
} else null;
})
config.machines.hosts);
# prebuilt cmds for easy ssh LUKS unlock
environment.shellAliases =
let
unlockHosts = unlockType: lib.concatMapAttrs
(host: cfg:
if cfg.remoteUnlock != null && cfg.remoteUnlock.${unlockType} != null then {
${host} = cfg.remoteUnlock.${unlockType};
} else { })
config.machines.hosts;
in
lib.concatMapAttrs (host: addr: { "unlock-over-tor_${host}" = "torsocks ssh root@${addr}"; }) (unlockHosts "onionHost")
//
lib.concatMapAttrs (host: addr: { "unlock_${host}" = "ssh root@${addr}"; }) (unlockHosts "clearnetHost");
# TODO: Old ssh keys I will remove some day...
machines.ssh.userKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMVR/R3ZOsv7TZbICGBCHdjh1NDT8SnswUyINeJOC7QG" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMVR/R3ZOsv7TZbICGBCHdjh1NDT8SnswUyINeJOC7QG"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE0dcqL/FhHmv+a1iz3f9LJ48xubO7MZHy35rW9SZOYM" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE0dcqL/FhHmv+a1iz3f9LJ48xubO7MZHy35rW9SZOYM"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHSkKiRUUmnErOKGx81nyge/9KqjkPh8BfDk0D3oP586" # nat "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHSkKiRUUmnErOKGx81nyge/9KqjkPh8BfDk0D3oP586" # nat
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFeTK1iARlNIKP/DS8/ObBm9yUM/3L1Ub4XI5A2r9OzP" # ray
] ++ higherTrustUserKeys;
system = {
liza = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDY/pNyWedEfU7Tq9ikGbriRuF1ZWkHhegGS17L0Vcdl";
liza-unlock = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ6eMKW7jBNUKm2r9zEoape4s3KVrmLTLC0nkW9t/8JK";
ponyo = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMBBlTAIp38RhErU1wNNV5MBeb+WGH0mhF/dxh5RsAXN";
ponyo-unlock = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9LQuuImgWlkjDhEEIbM1wOd+HqRv1RxvYZuLXPSdRi";
ray = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDQM8hwKRgl8cZj7UVYATSLYu4LhG7I0WFJ9m2iWowiB";
router = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFr2IHmWFlaLaLp5dGoSmFEYKA/eg2SwGXAogaOmLsHL";
router-unlock = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJOw5dTPmtKqiPBH6VKyz5MYBubn8leAh5Eaw7s/O85c";
s0 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwiXcUFtAvZCayhu4+AIcF+Ktrdgv9ee/mXSIhJbp4q";
s0-unlock = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFNiceeFMos5ZXcYem4yFxh8PiZNNnuvhlyLbQLrgIZH";
n1 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPWlhd1Oid5Xf2zdcBrcdrR0TlhObutwcJ8piobRTpRt";
n2 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7bRiRutnI7Bmyt/I238E3Fp5DqiClIXiVibsccipOr";
n3 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+rJEaRrFDGirQC2UoWQkmpzLg4qgTjGJgVqiipWiU5";
n4 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINYm2ROIfCeGz6QtDwqAmcj2DX9tq2CZn0eLhskdvB4Z";
n5 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE5Qhvwq3PiHEKf+2/4w5ZJkSMNzFLhIRrPOR98m7wW4";
n6 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID/P/pa9+qhKAPfvvd8xSO2komJqDW0M1nCK7ZrP6PO7";
n7 = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPtOlOvTlMX2mxPaXDJ6VlMe5rmroUXpKmJVNxgV32xL";
};
higherTrustUserKeys = [
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEaGIwLiUa6wQLlEF+keQOIYy/tCmJvV6eENzUQjSqW2AAAABHNzaDo=" # ray fido
]; ];
}
# groups
systems = with system; [
liza
ponyo
ray
router
s0
n1
n2
n3
n4
n5
n6
n7
];
personal = with system; [
ray
];
servers = with system; [
liza
ponyo
router
s0
n1
n2
n3
n4
n5
n6
n7
];
compute = with system; [
n1
n2
n3
n4
n5
n6
n7
];
storage = with system; [
s0
];
}

61
flake.lock generated
View File

@@ -8,11 +8,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1675176355, "lastModified": 1682101079,
"narHash": "sha256-Qjxh5cmN56siY97mzmBLI1+cdjXSPqmfPVsKxBvHmwI=", "narHash": "sha256-MdAhtjrLKnk2uiqun1FWABbKpLH090oeqCSiWemtuck=",
"owner": "ryantm", "owner": "ryantm",
"repo": "agenix", "repo": "agenix",
"rev": "b7ffcfe77f817d9ee992640ba1f270718d197f28", "rev": "2994d002dcff5353ca1ac48ec584c7f6589fe447",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -117,11 +117,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1674127017, "lastModified": 1682063650,
"narHash": "sha256-QO1xF7stu5ZMDLbHN30LFolMAwY6TVlzYvQoUs1RD68=", "narHash": "sha256-VaDHh2z6xlnTHaONlNVHP7qEMcK5rZ8Js3sT6mKb2XY=",
"owner": "serokell", "owner": "serokell",
"repo": "deploy-rs", "repo": "deploy-rs",
"rev": "8c9ea9605eed20528bf60fae35a2b613b901fd77", "rev": "c2ea4e642dc50fd44b537e9860ec95867af30d39",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -147,12 +147,15 @@
} }
}, },
"flake-utils": { "flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": { "locked": {
"lastModified": 1667395993, "lastModified": 1681202837,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "rev": "cfacdce06f30d2b68473a46042957675eebb3401",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -168,11 +171,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1677382901, "lastModified": 1681591833,
"narHash": "sha256-2idFWlTVG+qUZkU2/W50amGSIxmN56igIkMAXKbv4S4=", "narHash": "sha256-lW+xOELafAs29yw56FG4MzNOFkh8VHC/X/tRs1wsGn8=",
"owner": "Mic92", "owner": "Mic92",
"repo": "nix-index-database", "repo": "nix-index-database",
"rev": "4306fa7c12e098360439faac1a2e6b8e509ec97c", "rev": "68ec961c51f48768f72d2bbdb396ce65a316677e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -183,11 +186,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1677823547, "lastModified": 1682133240,
"narHash": "sha256-xD2qco8Pw8HAXgjf9OSi2H2N20WaTrtvgcl21525kVE=", "narHash": "sha256-s6yRsI/7V+k/+rckp0+/2cs/UXnea3SEfMpy95QiGcc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "78c4d33c16092e535bc4ba1284ba49e3e138483a", "rev": "8dafae7c03d6aa8c2ae0a0612fbcb47e994e3fb8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -212,6 +215,18 @@
"type": "indirect" "type": "indirect"
} }
}, },
"nixpkgs-hostapd-pr": {
"flake": false,
"locked": {
"narHash": "sha256-1rGQKcB1jeRPc1n021ulyOVkA6L6xmNYKmeqQ94+iRc=",
"type": "file",
"url": "https://github.com/NixOS/nixpkgs/pull/222536.patch"
},
"original": {
"type": "file",
"url": "https://github.com/NixOS/nixpkgs/pull/222536.patch"
}
},
"radio": { "radio": {
"inputs": { "inputs": {
"flake-utils": [ "flake-utils": [
@@ -262,6 +277,7 @@
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"nix-index-database": "nix-index-database", "nix-index-database": "nix-index-database",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixpkgs-hostapd-pr": "nixpkgs-hostapd-pr",
"radio": "radio", "radio": "radio",
"radio-web": "radio-web", "radio-web": "radio-web",
"simple-nixos-mailserver": "simple-nixos-mailserver" "simple-nixos-mailserver": "simple-nixos-mailserver"
@@ -291,6 +307,21 @@
"type": "gitlab" "type": "gitlab"
} }
}, },
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": { "utils": {
"locked": { "locked": {
"lastModified": 1605370193, "lastModified": 1605370193,

174
flake.nix
View File

@@ -39,94 +39,108 @@
# prebuilt nix-index database # prebuilt nix-index database
nix-index-database.url = "github:Mic92/nix-index-database"; nix-index-database.url = "github:Mic92/nix-index-database";
nix-index-database.inputs.nixpkgs.follows = "nixpkgs"; nix-index-database.inputs.nixpkgs.follows = "nixpkgs";
nixpkgs-hostapd-pr.url = "https://github.com/NixOS/nixpkgs/pull/222536.patch";
nixpkgs-hostapd-pr.flake = false;
}; };
outputs = { self, nixpkgs, ... }@inputs: { outputs = { self, nixpkgs, ... }@inputs:
nixosConfigurations =
let let
modules = system: with inputs; [ machines = (import ./common/machine-info/moduleless.nix
./common {
simple-nixos-mailserver.nixosModule inherit nixpkgs;
agenix.nixosModules.default assertionsModule = "${nixpkgs}/nixos/modules/misc/assertions.nix";
dailybuild_modules.nixosModule }).machines.hosts;
archivebox.nixosModule
nix-index-database.nixosModules.nix-index
({ lib, ... }: {
config.environment.systemPackages = [
agenix.packages.${system}.agenix
];
# because nixos specialArgs doesn't work for containers... need to pass in inputs a different way
options.inputs = lib.mkOption { default = inputs; };
options.currentSystem = lib.mkOption { default = system; };
})
];
mkSystem = system: nixpkgs: path:
let
allModules = modules system;
# allow patching nixpkgs, remove this hack once this is solved: https://github.com/NixOS/nix/issues/3920
patchedNixpkgsSrc = nixpkgs.legacyPackages.${system}.applyPatches {
name = "nixpkgs-patched";
src = nixpkgs;
patches = [
# inputs.nixpkgs-patch-howdy
];
};
patchedNixpkgs = nixpkgs.lib.fix (self: (import "${patchedNixpkgsSrc}/flake.nix").outputs { self=nixpkgs; });
in patchedNixpkgs.lib.nixosSystem {
inherit system;
modules = allModules ++ [path];
specialArgs = {
inherit allModules;
};
};
in in
{ {
"ray" = mkSystem "x86_64-linux" nixpkgs ./machines/ray/configuration.nix; nixosConfigurations =
# "nat" = mkSystem "aarch64-linux" nixpkgs ./machines/nat/configuration.nix; let
"liza" = mkSystem "x86_64-linux" nixpkgs ./machines/liza/configuration.nix; modules = system: hostname: with inputs; [
"ponyo" = mkSystem "x86_64-linux" nixpkgs ./machines/ponyo/configuration.nix; ./common
"router" = mkSystem "x86_64-linux" nixpkgs ./machines/router/configuration.nix; simple-nixos-mailserver.nixosModule
"s0" = mkSystem "x86_64-linux" nixpkgs ./machines/storage/s0/configuration.nix; agenix.nixosModules.default
}; dailybuild_modules.nixosModule
archivebox.nixosModule
nix-index-database.nixosModules.nix-index
({ lib, ... }: {
config = {
environment.systemPackages = [
agenix.packages.${system}.agenix
];
packages = let networking.hostName = hostname;
mkKexec = system: };
(nixpkgs.lib.nixosSystem {
inherit system;
modules = [ ./machines/ephemeral/kexec.nix ];
}).config.system.build.kexec_tarball;
mkIso = system:
(nixpkgs.lib.nixosSystem {
inherit system;
modules = [ ./machines/ephemeral/iso.nix ];
}).config.system.build.isoImage;
in {
"x86_64-linux"."kexec" = mkKexec "x86_64-linux";
"x86_64-linux"."iso" = mkIso "x86_64-linux";
"aarch64-linux"."kexec" = mkKexec "aarch64-linux";
"aarch64-linux"."iso" = mkIso "aarch64-linux";
};
deploy.nodes = # because nixos specialArgs doesn't work for containers... need to pass in inputs a different way
let options.inputs = lib.mkOption { default = inputs; };
mkDeploy = configName: hostname: { options.currentSystem = lib.mkOption { default = system; };
inherit hostname; })
magicRollback = false; ];
sshUser = "root";
profiles.system.path = inputs.deploy-rs.lib.x86_64-linux.activate.nixos self.nixosConfigurations.${configName}; mkSystem = system: nixpkgs: path: hostname:
let
allModules = modules system hostname;
# allow patching nixpkgs, remove this hack once this is solved: https://github.com/NixOS/nix/issues/3920
patchedNixpkgsSrc = nixpkgs.legacyPackages.${system}.applyPatches {
name = "nixpkgs-patched";
src = nixpkgs;
patches = [
inputs.nixpkgs-hostapd-pr
./patches/kexec-luks.patch
];
};
patchedNixpkgs = nixpkgs.lib.fix (self: (import "${patchedNixpkgsSrc}/flake.nix").outputs { self = nixpkgs; });
in
patchedNixpkgs.lib.nixosSystem {
inherit system;
modules = allModules ++ [ path ];
specialArgs = {
inherit allModules;
};
};
in
nixpkgs.lib.mapAttrs
(hostname: cfg:
mkSystem cfg.arch nixpkgs cfg.configurationPath hostname)
machines;
packages =
let
mkKexec = system:
(nixpkgs.lib.nixosSystem {
inherit system;
modules = [ ./machines/ephemeral/kexec.nix ];
}).config.system.build.kexec_tarball;
mkIso = system:
(nixpkgs.lib.nixosSystem {
inherit system;
modules = [ ./machines/ephemeral/iso.nix ];
}).config.system.build.isoImage;
in
{
"x86_64-linux"."kexec" = mkKexec "x86_64-linux";
"x86_64-linux"."iso" = mkIso "x86_64-linux";
"aarch64-linux"."kexec" = mkKexec "aarch64-linux";
"aarch64-linux"."iso" = mkIso "aarch64-linux";
}; };
in { deploy.nodes =
s0 = mkDeploy "s0" "s0"; let
router = mkDeploy "router" "192.168.1.228"; mkDeploy = configName: arch: hostname: {
}; inherit hostname;
magicRollback = false;
sshUser = "root";
profiles.system.path = inputs.deploy-rs.lib.${arch}.activate.nixos self.nixosConfigurations.${configName};
};
in
nixpkgs.lib.mapAttrs
(hostname: cfg:
mkDeploy hostname cfg.arch (builtins.head cfg.hostNames))
machines;
# checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) inputs.deploy-rs.lib; checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) inputs.deploy-rs.lib;
}; };
} }

View File

@@ -9,4 +9,4 @@
isoImage.makeUsbBootable = true; isoImage.makeUsbBootable = true;
networking.hostName = "iso"; networking.hostName = "iso";
} }

View File

@@ -45,4 +45,4 @@
contents = [ ]; contents = [ ];
}; };
}; };
} }

View File

@@ -1,13 +1,16 @@
{ pkgs, modulesPath, ... }: { config, pkgs, modulesPath, ... }:
{ {
imports = [ imports = [
(modulesPath + "/installer/cd-dvd/channel.nix") (modulesPath + "/installer/cd-dvd/channel.nix")
../../common/machine-info
../../common/ssh.nix
]; ];
boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "e1000" "e1000e" "virtio_pci" "r8169" ]; boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "e1000" "e1000e" "virtio_pci" "r8169" ];
boot.kernelParams = [ boot.kernelParams = [
"panic=30" "boot.panic_on_fail" # reboot the machine upon fatal boot issues "panic=30"
"boot.panic_on_fail" # reboot the machine upon fatal boot issues
"console=ttyS0,115200" # enable serial console "console=ttyS0,115200" # enable serial console
"console=tty1" "console=tty1"
]; ];
@@ -15,13 +18,16 @@
boot.kernelPackages = pkgs.linuxPackages_latest; boot.kernelPackages = pkgs.linuxPackages_latest;
system.stateVersion = "21.11";
# hardware.enableAllFirmware = true; # hardware.enableAllFirmware = true;
# nixpkgs.config.allowUnfree = true; # nixpkgs.config.allowUnfree = true;
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
cryptsetup cryptsetup
btrfs-progs btrfs-progs
git git-lfs git
git-lfs
wget wget
htop htop
dnsutils dnsutils
@@ -36,10 +42,12 @@
services.openssh = { services.openssh = {
enable = true; enable = true;
challengeResponseAuthentication = false; settings = {
passwordAuthentication = false; KbdInteractiveAuthentication = false;
PasswordAuthentication = false;
};
}; };
services.getty.autologinUser = "root"; services.getty.autologinUser = "root";
users.users.root.openssh.authorizedKeys.keys = (import ../../common/ssh.nix).users; users.users.root.openssh.authorizedKeys.keys = config.machines.ssh.userKeys;
} }

View File

@@ -1,97 +0,0 @@
{ config, pkgs, lib, ... }:
{
imports =[
./hardware-configuration.nix
];
system.autoUpgrade.enable = true;
networking.hostName = "liza";
networking.interfaces.enp1s0.useDHCP = true;
mailserver = {
enable = true;
fqdn = "mail.neet.dev";
dkimKeyBits = 2048;
indexDir = "/var/lib/mailindex";
enableManageSieve = true;
fullTextSearch.enable = true;
fullTextSearch.indexAttachments = true;
fullTextSearch.memoryLimit = 500;
domains = [
"neet.space" "neet.dev" "neet.cloud"
"runyan.org" "runyan.rocks"
"thunderhex.com" "tar.ninja"
"bsd.ninja" "bsd.rocks"
];
loginAccounts = {
"jeremy@runyan.org" = {
hashedPasswordFile = "/run/agenix/email-pw";
aliases = [
"@neet.space" "@neet.cloud" "@neet.dev"
"@runyan.org" "@runyan.rocks"
"@thunderhex.com" "@tar.ninja"
"@bsd.ninja" "@bsd.rocks"
];
};
};
rejectRecipients = [
"george@runyan.org"
"joslyn@runyan.org"
"damon@runyan.org"
"jonas@runyan.org"
];
certificateScheme = 3; # use let's encrypt for certs
};
age.secrets.email-pw.file = ../../secrets/email-pw.age;
# sendmail to use xxx@domain instead of xxx@mail.domain
services.postfix.origin = "$mydomain";
# relay sent mail through mailgun
# https://www.howtoforge.com/community/threads/different-smtp-relays-for-different-domains-in-postfix.82711/#post-392620
services.postfix.config = {
smtp_sasl_auth_enable = "yes";
smtp_sasl_security_options = "noanonymous";
smtp_sasl_password_maps = "hash:/var/lib/postfix/conf/sasl_relay_passwd";
smtp_use_tls = "yes";
sender_dependent_relayhost_maps = "hash:/var/lib/postfix/conf/sender_relay";
smtp_sender_dependent_authentication = "yes";
};
services.postfix.mapFiles.sender_relay = let
relayHost = "[smtp.mailgun.org]:587";
in pkgs.writeText "sender_relay" ''
@neet.space ${relayHost}
@neet.cloud ${relayHost}
@neet.dev ${relayHost}
@runyan.org ${relayHost}
@runyan.rocks ${relayHost}
@thunderhex.com ${relayHost}
@tar.ninja ${relayHost}
@bsd.ninja ${relayHost}
@bsd.rocks ${relayHost}
'';
services.postfix.mapFiles.sasl_relay_passwd = "/run/agenix/sasl_relay_passwd";
age.secrets.sasl_relay_passwd.file = ../../secrets/sasl_relay_passwd.age;
services.nextcloud = {
enable = true;
https = true;
package = pkgs.nextcloud22;
hostName = "neet.cloud";
config.dbtype = "sqlite";
config.adminuser = "jeremy";
config.adminpassFile = "/run/agenix/nextcloud-pw";
autoUpdateApps.enable = true;
};
age.secrets.nextcloud-pw = {
file = ../../secrets/nextcloud-pw.age;
owner = "nextcloud";
};
services.nginx.virtualHosts.${config.services.nextcloud.hostName} = {
enableACME = true;
forceSSL = true;
};
}

View File

@@ -1,46 +0,0 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[ (modulesPath + "/profiles/qemu-guest.nix")
];
boot.initrd.availableKernelModules = [ "ata_piix" "virtio_pci" "floppy" "sr_mod" "virtio_blk" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
firmware.x86_64.enable = true;
bios = {
enable = true;
device = "/dev/sda";
};
remoteLuksUnlock.enable = true;
boot.initrd.luks.devices."enc-pv".device = "/dev/disk/by-uuid/2f736fba-8a0c-4fb5-8041-c849fb5e1297";
fileSystems."/" =
{ device = "/dev/disk/by-uuid/b90eaf3c-2f91-499a-a066-861e0f4478df";
fsType = "btrfs";
};
fileSystems."/home" =
{ device = "/dev/disk/by-uuid/b90eaf3c-2f91-499a-a066-861e0f4478df";
fsType = "btrfs";
options = [ "subvol=home" ];
};
fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/2b8f6f6d-9358-4d30-8341-7426574e0819";
fsType = "ext3";
};
swapDevices =
[ { device = "/dev/disk/by-uuid/ef7a83db-4b33-41d1-85fc-cff69e480352"; }
];
}

View File

@@ -10,8 +10,6 @@
networking.hostName = "nat"; networking.hostName = "nat";
networking.interfaces.ens160.useDHCP = true; networking.interfaces.ens160.useDHCP = true;
services.zerotierone.enable = true;
de.enable = true; de.enable = true;
de.touchpad.enable = true; de.touchpad.enable = true;
} }

View File

@@ -12,14 +12,16 @@
boot.extraModulePackages = [ ]; boot.extraModulePackages = [ ];
fileSystems."/" = fileSystems."/" =
{ device = "/dev/disk/by-uuid/02a8c0c7-fd4e-4443-a83c-2d0b63848779"; {
device = "/dev/disk/by-uuid/02a8c0c7-fd4e-4443-a83c-2d0b63848779";
fsType = "btrfs"; fsType = "btrfs";
}; };
fileSystems."/boot" = fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/0C95-1290"; {
device = "/dev/disk/by-uuid/0C95-1290";
fsType = "vfat"; fsType = "vfat";
}; };
swapDevices = [ ]; swapDevices = [ ];
} }

14
machines/phil/default.nix Normal file
View File

@@ -0,0 +1,14 @@
{ config, pkgs, lib, ... }:
{
imports = [
./hardware-configuration.nix
];
services.gitea-runner = {
enable = true;
instanceUrl = "https://git.neet.dev";
};
system.autoUpgrade.enable = true;
}

View File

@@ -0,0 +1,43 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[
(modulesPath + "/profiles/qemu-guest.nix")
];
# because grub just doesn't work for some reason
boot.loader.systemd-boot.enable = true;
remoteLuksUnlock.enable = true;
remoteLuksUnlock.enableTorUnlock = false;
boot.initrd.availableKernelModules = [ "xhci_pci" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
luks.devices = [ "/dev/disk/by-uuid/d26c1820-4c39-4615-98c2-51442504e194" ];
fileSystems."/" =
{
device = "/dev/disk/by-uuid/851bfde6-93cd-439e-9380-de28aa87eda9";
fsType = "btrfs";
};
fileSystems."/boot" =
{
device = "/dev/disk/by-uuid/F185-C4E5";
fsType = "vfat";
};
swapDevices =
[{ device = "/dev/disk/by-uuid/d809e3a1-3915-405a-a200-4429c5efdf87"; }];
networking.interfaces.enp0s6.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
}

View File

@@ -0,0 +1,20 @@
{
hostNames = [
"phil"
"phil.neet.dev"
];
arch = "aarch64-linux";
systemRoles = [
"server"
"gitea-runner"
];
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBlgRPpuUkZqe8/lHugRPm/m2vcN9psYhh5tENHZt9I2";
remoteUnlock = {
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK0RodotOXLMy/w70aa096gaNqPBnfgiXR5ZAH4+wGzd";
clearnetHost = "unlock.phil.neet.dev";
};
}

View File

@@ -1,22 +1,31 @@
{ config, pkgs, lib, ... }: { config, pkgs, lib, ... }:
{ {
imports =[ imports = [
./hardware-configuration.nix ./hardware-configuration.nix
]; ];
networking.hostName = "ponyo";
system.autoUpgrade.enable = true; system.autoUpgrade.enable = true;
# I want to manually trigger kexec updates for now on ponyo
system.autoUpgrade.allowKexec = false;
luks.enableKexec = true;
services.zerotierone.enable = true; # p2p mesh network
services.tailscale.exitNode = true;
# email server
mailserver.enable = true;
# nextcloud
services.nextcloud.enable = true;
# git
services.gitea = { services.gitea = {
enable = true; enable = true;
hostname = "git.neet.dev"; hostname = "git.neet.dev";
disableRegistration = true;
}; };
# IRC
services.thelounge = { services.thelounge = {
enable = true; enable = true;
port = 9000; port = 9000;
@@ -28,12 +37,14 @@
}; };
}; };
# mumble
services.murmur = { services.murmur = {
enable = true; enable = true;
port = 23563; port = 23563;
domain = "voice.neet.space"; domain = "voice.neet.space";
}; };
# IRC bot
services.drastikbot = { services.drastikbot = {
enable = true; enable = true;
wolframAppIdFile = "/run/agenix/wolframalpha"; wolframAppIdFile = "/run/agenix/wolframalpha";
@@ -42,8 +53,11 @@
file = ../../secrets/wolframalpha.age; file = ../../secrets/wolframalpha.age;
owner = config.services.drastikbot.user; owner = config.services.drastikbot.user;
}; };
backup.group."dailybot".paths = [
config.services.drastikbot.dataDir
];
# wrap radio in a VPN # music radio
vpn-container.enable = true; vpn-container.enable = true;
vpn-container.config = { vpn-container.config = {
services.radio = { services.radio = {
@@ -51,11 +65,7 @@
host = "radio.runyan.org"; host = "radio.runyan.org";
}; };
}; };
pia.wireguard.badPortForwardPorts = [ ];
# tailscale
services.tailscale.exitNode = true;
# icecast endpoint + website
services.nginx.virtualHosts."radio.runyan.org" = { services.nginx.virtualHosts."radio.runyan.org" = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
@@ -70,6 +80,7 @@
}; };
}; };
# matrix home server
services.matrix = { services.matrix = {
enable = true; enable = true;
host = "neet.space"; host = "neet.space";
@@ -87,67 +98,36 @@
secret = "a8369a0e96922abf72494bb888c85831b"; secret = "a8369a0e96922abf72494bb888c85831b";
}; };
}; };
# pin postgresql for matrix (will need to migrate eventually)
services.postgresql.package = pkgs.postgresql_11; services.postgresql.package = pkgs.postgresql_11;
services.searx = {
enable = true;
environmentFile = "/run/agenix/searx";
settings = {
server.port = 43254;
server.secret_key = "@SEARX_SECRET_KEY@";
engines = [ {
name = "wolframalpha";
shortcut = "wa";
api_key = "@WOLFRAM_API_KEY@";
engine = "wolframalpha_api";
} ];
};
};
services.nginx.virtualHosts."search.neet.space" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString config.services.searx.settings.server.port}";
};
};
age.secrets.searx.file = ../../secrets/searx.age;
# iodine DNS-based vpn # iodine DNS-based vpn
services.iodine.server = { services.iodine.server.enable = true;
enable = true;
ip = "192.168.99.1";
domain = "tun.neet.dev";
passwordFile = "/run/agenix/iodine";
};
age.secrets.iodine.file = ../../secrets/iodine.age;
networking.firewall.allowedUDPPorts = [ 53 ];
networking.nat.internalInterfaces = [
"dns0" # iodine
];
# proxied web services
services.nginx.enable = true; services.nginx.enable = true;
services.nginx.virtualHosts."jellyfin.neet.cloud" = { services.nginx.virtualHosts."jellyfin.neet.cloud" = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
locations."/" = { locations."/" = {
proxyPass = "http://s0.zt.neet.dev"; proxyPass = "http://s0.koi-bebop.ts.net";
proxyWebsockets = true; proxyWebsockets = true;
}; };
}; };
services.nginx.virtualHosts."navidrome.neet.cloud" = { services.nginx.virtualHosts."navidrome.neet.cloud" = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
locations."/".proxyPass = "http://s0.zt.neet.dev:4533"; locations."/".proxyPass = "http://s0.koi-bebop.ts.net:4533";
}; };
# TODO replace with a proper file hosting service
services.nginx.virtualHosts."tmp.neet.dev" = { services.nginx.virtualHosts."tmp.neet.dev" = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
root = "/var/www/tmp"; root = "/var/www/tmp";
}; };
# redirect to github # redirect runyan.org to github
services.nginx.virtualHosts."runyan.org" = { services.nginx.virtualHosts."runyan.org" = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
@@ -156,6 +136,7 @@
''; '';
}; };
# owncast live streaming
services.owncast.enable = true; services.owncast.enable = true;
services.owncast.hostname = "live.neet.dev"; services.owncast.hostname = "live.neet.dev";
} }

View File

@@ -2,7 +2,8 @@
{ {
imports = imports =
[ (modulesPath + "/profiles/qemu-guest.nix") [
(modulesPath + "/profiles/qemu-guest.nix")
]; ];
boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" ]; boot.initrd.availableKernelModules = [ "ata_piix" "uhci_hcd" "virtio_pci" "virtio_scsi" "sd_mod" ];
@@ -18,16 +19,21 @@
}; };
remoteLuksUnlock.enable = true; remoteLuksUnlock.enable = true;
boot.initrd.luks.devices."enc-pv".device = "/dev/disk/by-uuid/4cc36be4-dbff-4afe-927d-69bf4637bae2";
boot.initrd.luks.devices."enc-pv2".device = "/dev/disk/by-uuid/e52b01b3-81c8-4bb2-ae7e-a3d9c793cb00"; # expanded disk luks.devices = [
"/dev/disk/by-uuid/4cc36be4-dbff-4afe-927d-69bf4637bae2"
"/dev/disk/by-uuid/e52b01b3-81c8-4bb2-ae7e-a3d9c793cb00"
];
fileSystems."/" = fileSystems."/" =
{ device = "/dev/mapper/enc-pv"; {
device = "/dev/mapper/enc-pv1";
fsType = "btrfs"; fsType = "btrfs";
}; };
fileSystems."/boot" = fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/d3a3777d-1e70-47fa-a274-804dc70ee7fd"; {
device = "/dev/disk/by-uuid/d3a3777d-1e70-47fa-a274-804dc70ee7fd";
fsType = "ext4"; fsType = "ext4";
}; };
@@ -39,4 +45,4 @@
]; ];
networking.interfaces.eth0.useDHCP = true; networking.interfaces.eth0.useDHCP = true;
} }

View File

@@ -0,0 +1,28 @@
{
hostNames = [
"ponyo"
"ponyo.neet.dev"
"git.neet.dev"
];
arch = "x86_64-linux";
systemRoles = [
"server"
"email-server"
"iodine"
"pia"
"nextcloud"
"dailybot"
"gitea"
];
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMBBlTAIp38RhErU1wNNV5MBeb+WGH0mhF/dxh5RsAXN";
remoteUnlock = {
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC9LQuuImgWlkjDhEEIbM1wOd+HqRv1RxvYZuLXPSdRi";
clearnetHost = "unlock.ponyo.neet.dev";
onionHost = "cfamr6artx75qvt7ho3rrbsc7mkucmv5aawebwflsfuorusayacffryd.onion";
};
}

View File

@@ -5,8 +5,6 @@
./hardware-configuration.nix ./hardware-configuration.nix
]; ];
networking.hostName = "ray";
# for luks onlock over tor # for luks onlock over tor
services.tor.enable = true; services.tor.enable = true;
services.tor.client.enable = true; services.tor.client.enable = true;
@@ -18,10 +16,20 @@
hardware.openrazer.devicesOffOnScreensaver = false; hardware.openrazer.devicesOffOnScreensaver = false;
users.users.googlebot.packages = [ pkgs.polychromatic ]; users.users.googlebot.packages = [ pkgs.polychromatic ];
# depthai
services.udev.extraRules = '' services.udev.extraRules = ''
# depthai
SUBSYSTEM=="usb", ATTRS{idVendor}=="03e7", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="03e7", MODE="0666"
# Moonlander
# Rules for Oryx web flashing and live training
KERNEL=="hidraw*", ATTRS{idVendor}=="16c0", MODE="0664", GROUP="plugdev"
KERNEL=="hidraw*", ATTRS{idVendor}=="3297", MODE="0664", GROUP="plugdev"
# Wally Flashing rules for the Moonlander and Planck EZ
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE:="0666", SYMLINK+="stm32_dfu"
''; '';
users.groups.plugdev = {
members = [ "googlebot" ];
};
# virt-manager # virt-manager
virtualisation.libvirtd.enable = true; virtualisation.libvirtd.enable = true;
@@ -37,7 +45,8 @@
virtualisation.docker.enable = true; virtualisation.docker.enable = true;
services.zerotierone.enable = true; virtualisation.appvm.enable = true;
virtualisation.appvm.user = "googlebot";
services.mount-samba.enable = true; services.mount-samba.enable = true;

View File

@@ -5,7 +5,8 @@
{ {
imports = imports =
[ (modulesPath + "/installer/scan/not-detected.nix") [
(modulesPath + "/installer/scan/not-detected.nix")
]; ];
# boot # boot
@@ -35,28 +36,24 @@
# disks # disks
remoteLuksUnlock.enable = true; remoteLuksUnlock.enable = true;
boot.initrd.luks.devices."enc-pv" = { luks.devices = [ "/dev/disk/by-uuid/c1822e5f-4137-44e1-885f-954e926583ce" ];
device = "/dev/disk/by-uuid/c1822e5f-4137-44e1-885f-954e926583ce";
allowDiscards = true;
};
fileSystems."/" = fileSystems."/" =
{ device = "/dev/vg/root"; {
device = "/dev/vg/root";
fsType = "btrfs"; fsType = "btrfs";
options = [ "subvol=root" ]; options = [ "subvol=root" ];
}; };
fileSystems."/home" = fileSystems."/home" =
{ device = "/dev/vg/root"; {
device = "/dev/vg/root";
fsType = "btrfs"; fsType = "btrfs";
options = [ "subvol=home" ]; options = [ "subvol=home" ];
}; };
fileSystems."/boot" = fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/2C85-2B59"; {
device = "/dev/disk/by-uuid/2C85-2B59";
fsType = "vfat"; fsType = "vfat";
}; };
swapDevices = swapDevices =
[ { device = "/dev/vg/swap"; } [{ device = "/dev/vg/swap"; }];
];
# high-resolution display
hardware.video.hidpi.enable = lib.mkDefault true;
} }

View File

@@ -0,0 +1,22 @@
{
hostNames = [
"ray"
];
arch = "x86_64-linux";
systemRoles = [
"personal"
"deploy"
];
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDQM8hwKRgl8cZj7UVYATSLYu4LhG7I0WFJ9m2iWowiB";
userKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFeTK1iARlNIKP/DS8/ObBm9yUM/3L1Ub4XI5A2r9OzP"
];
deployKeys = [
"sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAIEaGIwLiUa6wQLlEF+keQOIYy/tCmJvV6eENzUQjSqW2AAAABHNzaDo="
];
}

View File

@@ -1,15 +0,0 @@
{ config, lib, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
];
networking.hostName = "router";
services.zerotierone.enable = true;
system.autoUpgrade.enable = true;
networking.useDHCP = lib.mkForce true;
}

View File

@@ -0,0 +1,38 @@
{ config, lib, pkgs, ... }:
{
imports = [
./hardware-configuration.nix
./router.nix
];
# https://dataswamp.org/~solene/2022-08-03-nixos-with-live-usb-router.html
# https://github.com/mdlayher/homelab/blob/391cfc0de06434e4dee0abe2bec7a2f0637345ac/nixos/routnerr-2/configuration.nix
# https://github.com/skogsbrus/os/blob/master/sys/router.nix
# http://trac.gateworks.com/wiki/wireless/wifi
system.autoUpgrade.enable = true;
services.tailscale.exitNode = true;
router.enable = true;
router.privateSubnet = "192.168.3";
services.iperf3.enable = true;
# networking.useDHCP = lib.mkForce true;
# TODO
# networking.usePredictableInterfaceNames = true;
powerManagement.cpuFreqGovernor = "ondemand";
services.irqbalance.enable = true;
# services.miniupnpd = {
# enable = true;
# externalInterface = "eth0";
# internalIPs = [ "br0" ];
# };
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,223 @@
#!/bin/sh
# TODO allow adding custom parameters to ht_capab, vht_capab
# TODO detect bad channel numbers (preferably not at runtime)
# TODO error if 160mhz is not supported
# TODO 'b' only goes up to 40mhz
# gets the phy number using the input interface
# Ex: get_phy_number("wlan0") -> "1"
get_phy_number() {
local interface=$1
phy=$(iw dev "$interface" info | awk '/phy/ {gsub(/#/,"");print $2}')
if [[ -z "$phy" ]]; then
echo "Error: interface not found" >&2
exit 1
fi
phy=phy$phy
}
get_ht_cap_mask() {
ht_cap_mask=0
for cap in $(iw phy "$phy" info | grep 'Capabilities:' | cut -d: -f2); do
ht_cap_mask="$(($ht_cap_mask | $cap))"
done
local cap_rx_stbc
cap_rx_stbc=$((($ht_cap_mask >> 8) & 3))
ht_cap_mask="$(( ($ht_cap_mask & ~(0x300)) | ($cap_rx_stbc << 8) ))"
}
get_vht_cap_mask() {
vht_cap_mask=0
for cap in $(iw phy "$phy" info | awk -F "[()]" '/VHT Capabilities/ { print $2 }'); do
vht_cap_mask="$(($vht_cap_mask | $cap))"
done
local cap_rx_stbc
cap_rx_stbc=$((($vht_cap_mask >> 8) & 7))
vht_cap_mask="$(( ($vht_cap_mask & ~(0x700)) | ($cap_rx_stbc << 8) ))"
}
mac80211_add_capabilities() {
local __var="$1"; shift
local __mask="$1"; shift
local __out= oifs
oifs="$IFS"
IFS=:
for capab in "$@"; do
set -- $capab
[ "$(($4))" -gt 0 ] || continue
[ "$(($__mask & $2))" -eq "$((${3:-$2}))" ] || continue
__out="$__out[$1]"
done
IFS="$oifs"
export -n -- "$__var=$__out"
}
add_special_ht_capabilities() {
case "$hwmode" in
a)
case "$(( ($channel / 4) % 2 ))" in
1) ht_capab="$ht_capab[HT40+]";;
0) ht_capab="$ht_capab[HT40-]";;
esac
;;
*)
if [ "$channel" -lt 7 ]; then
ht_capab="$ht_capab[HT40+]"
else
ht_capab="$ht_capab[HT40-]"
fi
;;
esac
}
add_special_vht_capabilities() {
local cap_ant
[ "$(($vht_cap_mask & 0x800))" -gt 0 ] && {
cap_ant="$(( ( ($vht_cap_mask >> 16) & 3 ) + 1 ))"
[ "$cap_ant" -gt 1 ] && vht_capab="$vht_capab[SOUNDING-DIMENSION-$cap_ant]"
}
[ "$(($vht_cap_mask & 0x1000))" -gt 0 ] && {
cap_ant="$(( ( ($vht_cap_mask >> 13) & 3 ) + 1 ))"
[ "$cap_ant" -gt 1 ] && vht_capab="$vht_capab[BF-ANTENNA-$cap_ant]"
}
if [ "$(($vht_cap_mask & 12))" -eq 4 ]; then
vht_capab="$vht_capab[VHT160]"
fi
local vht_max_mpdu_hw=3895
[ "$(($vht_cap_mask & 3))" -ge 1 ] && \
vht_max_mpdu_hw=7991
[ "$(($vht_cap_mask & 3))" -ge 2 ] && \
vht_max_mpdu_hw=11454
[ "$vht_max_mpdu_hw" != 3895 ] && \
vht_capab="$vht_capab[MAX-MPDU-$vht_max_mpdu_hw]"
# maximum A-MPDU length exponent
local vht_max_a_mpdu_len_exp_hw=0
[ "$(($vht_cap_mask & 58720256))" -ge 8388608 ] && \
vht_max_a_mpdu_len_exp_hw=1
[ "$(($vht_cap_mask & 58720256))" -ge 16777216 ] && \
vht_max_a_mpdu_len_exp_hw=2
[ "$(($vht_cap_mask & 58720256))" -ge 25165824 ] && \
vht_max_a_mpdu_len_exp_hw=3
[ "$(($vht_cap_mask & 58720256))" -ge 33554432 ] && \
vht_max_a_mpdu_len_exp_hw=4
[ "$(($vht_cap_mask & 58720256))" -ge 41943040 ] && \
vht_max_a_mpdu_len_exp_hw=5
[ "$(($vht_cap_mask & 58720256))" -ge 50331648 ] && \
vht_max_a_mpdu_len_exp_hw=6
[ "$(($vht_cap_mask & 58720256))" -ge 58720256 ] && \
vht_max_a_mpdu_len_exp_hw=7
vht_capab="$vht_capab[MAX-A-MPDU-LEN-EXP$vht_max_a_mpdu_len_exp_hw]"
local vht_link_adapt_hw=0
[ "$(($vht_cap_mask & 201326592))" -ge 134217728 ] && \
vht_link_adapt_hw=2
[ "$(($vht_cap_mask & 201326592))" -ge 201326592 ] && \
vht_link_adapt_hw=3
[ "$vht_link_adapt_hw" != 0 ] && \
vht_capab="$vht_capab[VHT-LINK-ADAPT-$vht_link_adapt_hw]"
}
calculate_channel_offsets() {
vht_oper_chwidth=0
vht_oper_centr_freq_seg0_idx=
local idx="$channel"
case "$channelWidth" in
40)
case "$(( ($channel / 4) % 2 ))" in
1) idx=$(($channel + 2));;
0) idx=$(($channel - 2));;
esac
vht_oper_centr_freq_seg0_idx=$idx
;;
80)
case "$(( ($channel / 4) % 4 ))" in
1) idx=$(($channel + 6));;
2) idx=$(($channel + 2));;
3) idx=$(($channel - 2));;
0) idx=$(($channel - 6));;
esac
vht_oper_chwidth=1
vht_oper_centr_freq_seg0_idx=$idx
;;
160)
case "$channel" in
36|40|44|48|52|56|60|64) idx=50;;
100|104|108|112|116|120|124|128) idx=114;;
esac
vht_oper_chwidth=2
vht_oper_centr_freq_seg0_idx=$idx
;;
esac
he_oper_chwidth=$vht_oper_chwidth
he_oper_centr_freq_seg0_idx=$vht_oper_centr_freq_seg0_idx
}
interface=$1
channel=$2
hwmode=$3
channelWidth=$4
get_phy_number $interface
get_ht_cap_mask
get_vht_cap_mask
mac80211_add_capabilities vht_capab $vht_cap_mask \
RXLDPC:0x10::1 \
SHORT-GI-80:0x20::1 \
SHORT-GI-160:0x40::1 \
TX-STBC-2BY1:0x80::1 \
SU-BEAMFORMER:0x800::1 \
SU-BEAMFORMEE:0x1000::1 \
MU-BEAMFORMER:0x80000::1 \
MU-BEAMFORMEE:0x100000::1 \
VHT-TXOP-PS:0x200000::1 \
HTC-VHT:0x400000::1 \
RX-ANTENNA-PATTERN:0x10000000::1 \
TX-ANTENNA-PATTERN:0x20000000::1 \
RX-STBC-1:0x700:0x100:1 \
RX-STBC-12:0x700:0x200:1 \
RX-STBC-123:0x700:0x300:1 \
RX-STBC-1234:0x700:0x400:1 \
mac80211_add_capabilities ht_capab $ht_cap_mask \
LDPC:0x1::1 \
GF:0x10::1 \
SHORT-GI-20:0x20::1 \
SHORT-GI-40:0x40::1 \
TX-STBC:0x80::1 \
RX-STBC1:0x300::1 \
MAX-AMSDU-7935:0x800::1 \
# TODO this is active when the driver doesn't support it?
# DSSS_CCK-40:0x1000::1 \
# TODO these are active when the driver doesn't support them?
# RX-STBC1:0x300:0x100:1 \
# RX-STBC12:0x300:0x200:1 \
# RX-STBC123:0x300:0x300:1 \
add_special_ht_capabilities
add_special_vht_capabilities
echo ht_capab=$ht_capab
echo vht_capab=$vht_capab
if [ "$channelWidth" != "20" ]; then
calculate_channel_offsets
echo he_oper_chwidth=$he_oper_chwidth
echo vht_oper_chwidth=$vht_oper_chwidth
echo he_oper_centr_freq_seg0_idx=$he_oper_centr_freq_seg0_idx
echo vht_oper_centr_freq_seg0_idx=$vht_oper_centr_freq_seg0_idx
fi

View File

@@ -10,7 +10,8 @@
# Enable serial output # Enable serial output
boot.kernelParams = [ boot.kernelParams = [
"panic=30" "boot.panic_on_fail" # reboot the machine upon fatal boot issues "panic=30"
"boot.panic_on_fail" # reboot the machine upon fatal boot issues
"console=ttyS0,115200n8" # enable serial console "console=ttyS0,115200n8" # enable serial console
]; ];
boot.loader.grub.extraConfig = " boot.loader.grub.extraConfig = "
@@ -21,7 +22,6 @@
# firmware # firmware
firmware.x86_64.enable = true; firmware.x86_64.enable = true;
hardware.enableAllFirmware = true;
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
# boot # boot
@@ -32,18 +32,19 @@
# disks # disks
remoteLuksUnlock.enable = true; remoteLuksUnlock.enable = true;
boot.initrd.luks.devices."enc-pv".device = "/dev/disk/by-uuid/9b090551-f78e-45ca-8570-196ed6a4af0c"; luks.devices = [ "/dev/disk/by-uuid/9b090551-f78e-45ca-8570-196ed6a4af0c" ];
fileSystems."/" = fileSystems."/" =
{ device = "/dev/disk/by-uuid/421c82b9-d67c-4811-8824-8bb57cb10fce"; {
device = "/dev/disk/by-uuid/421c82b9-d67c-4811-8824-8bb57cb10fce";
fsType = "btrfs"; fsType = "btrfs";
}; };
fileSystems."/boot" = fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/d97f324f-3a2e-4b84-ae2a-4b3d1209c689"; {
device = "/dev/disk/by-uuid/d97f324f-3a2e-4b84-ae2a-4b3d1209c689";
fsType = "ext3"; fsType = "ext3";
}; };
swapDevices = swapDevices =
[ { device = "/dev/disk/by-uuid/45bf58dd-67eb-45e4-9a98-246e23fa7abd"; } [{ device = "/dev/disk/by-uuid/45bf58dd-67eb-45e4-9a98-246e23fa7abd"; }];
];
nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.hostPlatform = "x86_64-linux";
} }

View File

@@ -0,0 +1,21 @@
{
hostNames = [
"router"
"192.168.1.228"
];
arch = "x86_64-linux";
systemRoles = [
"server"
"wireless"
"router"
];
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFr2IHmWFlaLaLp5dGoSmFEYKA/eg2SwGXAogaOmLsHL";
remoteUnlock = {
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJOw5dTPmtKqiPBH6VKyz5MYBubn8leAh5Eaw7s/O85c";
onionHost = "jxx2exuihlls2t6ncs7rvrjh2dssubjmjtclwr2ysvxtr4t7jv55xmqd.onion";
};
}

205
machines/router/router.nix Normal file
View File

@@ -0,0 +1,205 @@
{ config, pkgs, lib, ... }:
let
cfg = config.router;
inherit (lib) mapAttrs' genAttrs nameValuePair mkOption types mkIf mkEnableOption;
in
{
options.router = {
enable = mkEnableOption "router";
privateSubnet = mkOption {
type = types.str;
default = "192.168.1";
description = "IP block (/24) to use for the private subnet";
};
};
config = mkIf cfg.enable {
networking.ip_forward = true;
networking.interfaces.enp1s0.useDHCP = true;
networking.nat = {
enable = true;
internalInterfaces = [
"br0"
];
externalInterface = "enp1s0";
};
networking.bridges = {
br0 = {
interfaces = [
"enp2s0"
"wlp4s0"
"wlan1"
];
};
};
networking.interfaces = {
br0 = {
useDHCP = false;
ipv4.addresses = [
{
address = "${cfg.privateSubnet}.1";
prefixLength = 24;
}
];
};
};
networking.firewall = {
enable = true;
trustedInterfaces = [ "br0" "tailscale0" ];
interfaces = {
enp1s0 = {
allowedTCPPorts = [ ];
allowedUDPPorts = [ ];
};
};
};
services.dnsmasq = {
enable = true;
extraConfig = ''
# sensible behaviours
domain-needed
bogus-priv
no-resolv
# upstream name servers
server=1.1.1.1
server=8.8.8.8
# local domains
expand-hosts
domain=home
local=/home/
# Interfaces to use DNS on
interface=br0
# subnet IP blocks to use DHCP on
dhcp-range=${cfg.privateSubnet}.10,${cfg.privateSubnet}.254,24h
'';
};
services.hostapd = {
enable = true;
radios = {
# 2.4GHz
wlp4s0 = {
hwMode = "g";
noScan = true;
channel = 6;
countryCode = "US";
wifi4 = {
capabilities = [ "LDPC" "GF" "SHORT-GI-20" "SHORT-GI-40" "TX-STBC" "RX-STBC1" "MAX-AMSDU-7935" "HT40+" ];
};
wifi5 = {
operatingChannelWidth = "20or40";
capabilities = [ "MAX-A-MPDU-LEN-EXP0" ];
};
wifi6 = {
enable = true;
singleUserBeamformer = true;
singleUserBeamformee = true;
multiUserBeamformer = true;
operatingChannelWidth = "20or40";
};
networks = {
wlp4s0 = {
ssid = "CXNK00BF9176";
authentication.saePasswordsFile = "/run/agenix/hostapd-pw-CXNK00BF9176";
};
# wlp4s0-1 = {
# ssid = "- Experimental 5G Tower by AT&T";
# authentication.saePasswordsFile = "/run/agenix/hostapd-pw-experimental-tower";
# };
# wlp4s0-2 = {
# ssid = "FBI Surveillance Van 2";
# authentication.saePasswordsFile = "/run/agenix/hostapd-pw-experimental-tower";
# };
};
extraConfig = ''
he_oper_centr_freq_seg0_idx=8
vht_oper_centr_freq_seg0_idx=8
'';
};
# 5GHz
wlan1 = {
hwMode = "a";
noScan = true;
channel = 128;
countryCode = "US";
wifi4 = {
capabilities = [ "LDPC" "GF" "SHORT-GI-20" "SHORT-GI-40" "TX-STBC" "RX-STBC1" "MAX-AMSDU-7935" "HT40-" ];
};
wifi5 = {
operatingChannelWidth = "160";
capabilities = [ "RXLDPC" "SHORT-GI-80" "SHORT-GI-160" "TX-STBC-2BY1" "SU-BEAMFORMER" "SU-BEAMFORMEE" "MU-BEAMFORMER" "MU-BEAMFORMEE" "RX-ANTENNA-PATTERN" "TX-ANTENNA-PATTERN" "RX-STBC-1" "SOUNDING-DIMENSION-3" "BF-ANTENNA-3" "VHT160" "MAX-MPDU-11454" "MAX-A-MPDU-LEN-EXP7" ];
};
wifi6 = {
enable = true;
singleUserBeamformer = true;
singleUserBeamformee = true;
multiUserBeamformer = true;
operatingChannelWidth = "160";
};
networks = {
wlan1 = {
ssid = "CXNK00BF9176";
authentication.saePasswordsFile = "/run/agenix/hostapd-pw-CXNK00BF9176";
};
# wlan1-1 = {
# ssid = "- Experimental 5G Tower by AT&T";
# authentication.saePasswordsFile = "/run/agenix/hostapd-pw-experimental-tower";
# };
# wlan1-2 = {
# ssid = "FBI Surveillance Van 5";
# authentication.saePasswordsFile = "/run/agenix/hostapd-pw-experimental-tower";
# };
};
extraConfig = ''
vht_oper_centr_freq_seg0_idx=114
he_oper_centr_freq_seg0_idx=114
'';
};
};
};
age.secrets.hostapd-pw-experimental-tower.file = ../../secrets/hostapd-pw-experimental-tower.age;
age.secrets.hostapd-pw-CXNK00BF9176.file = ../../secrets/hostapd-pw-CXNK00BF9176.age;
hardware.firmware = [
pkgs.mt7916-firmware
];
nixpkgs.overlays = [
(self: super: {
mt7916-firmware = pkgs.stdenvNoCC.mkDerivation {
pname = "mt7916-firmware";
version = "custom-feb-02-23";
src = ./firmware/mediatek; # from here https://github.com/openwrt/mt76/issues/720#issuecomment-1413537674
dontBuild = true;
installPhase = ''
for i in \
mt7916_eeprom.bin \
mt7916_rom_patch.bin \
mt7916_wa.bin \
mt7916_wm.bin;
do
install -D -pm644 $i $out/lib/firmware/mediatek/$i
done
'';
meta = with lib; {
license = licenses.unfreeRedistributableFirmware;
};
};
})
];
};
}

View File

@@ -1,17 +1,17 @@
{ config, pkgs, lib, ... }: { config, pkgs, lib, ... }:
{ {
imports =[ imports = [
./hardware-configuration.nix ./hardware-configuration.nix
]; ];
networking.hostName = "s0";
system.autoUpgrade.enable = true; system.autoUpgrade.enable = true;
# mesh networking services.iperf3.enable = true;
services.iperf3.openFirewall = true;
# p2p mesh network
services.tailscale.exitNode = true; services.tailscale.exitNode = true;
services.zerotierone.enable = true;
# for education purposes only # for education purposes only
services.pykms.enable = true; services.pykms.enable = true;
@@ -114,6 +114,14 @@
uid = 994; uid = 994;
}; };
}; };
pia.wireguard.badPortForwardPorts = [
9696 # prowlarr
8989 # sonarr
6767 # bazarr
7878 # radarr
8686 # lidarr
9091 # transmission web
];
# jellyfin # jellyfin
# jellyfin cannot run in the vpn container and use hardware encoding # jellyfin cannot run in the vpn container and use hardware encoding

View File

@@ -2,11 +2,12 @@
{ {
imports = imports =
[ (modulesPath + "/installer/scan/not-detected.nix") [
(modulesPath + "/installer/scan/not-detected.nix")
]; ];
# boot # boot
efi.enable = true; boot.loader.systemd-boot.enable = true;
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "uas" "sd_mod" "rtsx_pci_sdmmc" ]; boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usb_storage" "uas" "sd_mod" "rtsx_pci_sdmmc" ];
boot.initrd.kernelModules = [ ]; boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ]; boot.kernelModules = [ "kvm-intel" ];
@@ -24,35 +25,48 @@
# luks # luks
remoteLuksUnlock.enable = true; remoteLuksUnlock.enable = true;
boot.initrd.luks.devices."enc-pv1".device = "/dev/disk/by-uuid/d52e99a9-8825-4d0a-afc1-8edbef7e0a86"; luks.devices = [
boot.initrd.luks.devices."enc-pv2".device = "/dev/disk/by-uuid/f7275585-7760-4230-97de-36704b9a2aa3"; "/dev/disk/by-uuid/d52e99a9-8825-4d0a-afc1-8edbef7e0a86"
boot.initrd.luks.devices."enc-pv3".device = "/dev/disk/by-uuid/5d1002b8-a0ed-4a1c-99f5-24b8816d9e38"; "/dev/disk/by-uuid/f7275585-7760-4230-97de-36704b9a2aa3"
boot.initrd.luks.devices."enc-pv4".device = "/dev/disk/by-uuid/e2c7402a-e72c-4c4a-998f-82e4c10187bc"; "/dev/disk/by-uuid/5d1002b8-a0ed-4a1c-99f5-24b8816d9e38"
"/dev/disk/by-uuid/e2c7402a-e72c-4c4a-998f-82e4c10187bc"
];
# mounts # mounts
fileSystems."/" = fileSystems."/" =
{ device = "rpool/nixos/root"; {
fsType = "zfs"; options = [ "zfsutil" "X-mount.mkdir" ]; device = "rpool/nixos/root";
fsType = "zfs";
options = [ "zfsutil" "X-mount.mkdir" ];
}; };
fileSystems."/home" = fileSystems."/home" =
{ device = "rpool/nixos/home"; {
fsType = "zfs"; options = [ "zfsutil" "X-mount.mkdir" ]; device = "rpool/nixos/home";
fsType = "zfs";
options = [ "zfsutil" "X-mount.mkdir" ];
}; };
fileSystems."/var/lib" = fileSystems."/var/lib" =
{ device = "rpool/nixos/var/lib"; {
fsType = "zfs"; options = [ "zfsutil" "X-mount.mkdir" ]; device = "rpool/nixos/var/lib";
fsType = "zfs";
options = [ "zfsutil" "X-mount.mkdir" ];
}; };
fileSystems."/var/log" = fileSystems."/var/log" =
{ device = "rpool/nixos/var/log"; {
fsType = "zfs"; options = [ "zfsutil" "X-mount.mkdir" ]; device = "rpool/nixos/var/log";
fsType = "zfs";
options = [ "zfsutil" "X-mount.mkdir" ];
}; };
fileSystems."/data" = fileSystems."/data" =
{ device = "rpool/nixos/data"; {
fsType = "zfs"; options = [ "zfsutil" "X-mount.mkdir" ]; device = "rpool/nixos/data";
fsType = "zfs";
options = [ "zfsutil" "X-mount.mkdir" ];
}; };
fileSystems."/boot" = fileSystems."/boot" =
{ device = "/dev/disk/by-uuid/4FB4-738E"; {
device = "/dev/disk/by-uuid/4FB4-738E";
fsType = "vfat"; fsType = "vfat";
}; };
swapDevices = [ ]; swapDevices = [ ];

View File

@@ -0,0 +1,20 @@
{
hostNames = [
"s0"
];
arch = "x86_64-linux";
systemRoles = [
"storage"
"server"
"pia"
];
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAwiXcUFtAvZCayhu4+AIcF+Ktrdgv9ee/mXSIhJbp4q";
remoteUnlock = {
hostKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFNiceeFMos5ZXcYem4yFxh8PiZNNnuvhlyLbQLrgIZH";
onionHost = "r3zvf7f2ppaeithzswigma46pajt3hqytmkg3rshgknbl3jbni455fqd.onion";
};
}

View File

@@ -1,27 +1,34 @@
# New Machine Setup
### Prepare Shell If Needed
```sh
nix-shell -p nixFlakes git nix-shell -p nixFlakes git
```
### Disk Setup
```sh
cfdisk cfdisk
mkfs.ext3 boot
cryptsetup luksFormat /dev/vda2 cryptsetup luksFormat /dev/vda2
cryptsetup luksOpen /dev/vda2 enc-pv cryptsetup luksOpen /dev/vda2 enc-pv
pvcreate /dev/mapper/enc-pv pvcreate /dev/mapper/enc-pv
vgcreate vg /dev/mapper/enc-pv
lvcreate -L 4G -n swap vg lvcreate -L 4G -n swap vg
lvcreate -l '100%FREE' -n root vg lvcreate -l '100%FREE' -n root vg
mkswap -L swap /dev/vg/swap mkswap -L swap /dev/vg/swap
swapon /dev/vg/swap swapon /dev/vg/swap
mkfs.btrfs /dev/vg/root mkfs.btrfs /dev/vg/root
mount /dev/vg/root /mnt mount /dev/vg/root /mnt
cd /mnt mkfs.ext3 boot
btrfs subvolume create root
btrfs subvolume create home
cd
mount -o subvol=root /dev/vg/root /mnt
mkdir /mnt/home
mount -o subvol=home /dev/vg/root /mnt/home
mkdir /mnt/boot
mount /dev/vda1 /mnt/boot mount /dev/vda1 /mnt/boot
mkdir /mnt/secret ```
/tmp/tor.rc ### Generate Secrets
```sh
mkdir /mnt/secret
```
In `/tmp/tor.rc`
``` ```
DataDirectory /tmp/my-dummy.tor/ DataDirectory /tmp/my-dummy.tor/
SOCKSPort 127.0.0.1:10050 IsolateDestAddr SOCKSPort 127.0.0.1:10050 IsolateDestAddr
@@ -30,8 +37,23 @@ HiddenServiceDir /mnt/secret/onion
HiddenServicePort 1234 127.0.0.1:1234 HiddenServicePort 1234 127.0.0.1:1234
``` ```
```sh
nix-shell -p tor --run "tor -f /tmp/tor.rc" nix-shell -p tor --run "tor -f /tmp/tor.rc"
ssh-keygen -q -N "" -t rsa -b 4096 -f /mnt/secret/ssh_host_rsa_key ssh-keygen -q -N "" -t rsa -b 4096 -f /mnt/secret/ssh_host_rsa_key
ssh-keygen -q -N "" -t ed25519 -f /mnt/secret/ssh_host_ed25519_key ssh-keygen -q -N "" -t ed25519 -f /mnt/secret/ssh_host_ed25519_key
nixos-generate-config --root /mnt # copy hardware config ```
nixos-install --flake "git+https://git.neet.dev/zuckerberg/nix-config.git#MACHINE_NAME"
### Generate Hardware Config
```sh
nixos-generate-config --root /mnt
```
### Install
```sh
nixos-install --flake "git+https://git.neet.dev/zuckerberg/nix-config.git#MACHINE_NAME"
```
### Post Install Tasks
- Add to DNS
- Add ssh host keys (unlock key + host key)
- Add to tailnet

264
patches/kexec-luks.patch Normal file
View File

@@ -0,0 +1,264 @@
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index b8f36538e70..cc320a04c70 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -146,6 +146,7 @@ let
csopen = "cryptsetup luksOpen ${dev.device} ${dev.name}"
+ optionalString dev.allowDiscards " --allow-discards"
+ optionalString dev.bypassWorkqueues " --perf-no_read_workqueue --perf-no_write_workqueue"
+ + optionalString dev.disableKeyring " --disable-keyring"
+ optionalString (dev.header != null) " --header=${dev.header}";
cschange = "cryptsetup luksChangeKey ${dev.device} ${optionalString (dev.header != null) "--header=${dev.header}"}";
fido2luksCredentials = dev.fido2.credentials ++ optional (dev.fido2.credential != null) dev.fido2.credential;
@@ -243,6 +244,26 @@ let
do_open_passphrase
fi
fi
+ '' else if (dev.masterKeyFile != null) then ''
+ if wait_target "key file" ${dev.masterKeyFile}; then
+ ${csopen} --master-key-file=${dev.masterKeyFile}
+ cs_status=$?
+ if [ $cs_status -ne 0 ]; then
+ echo "Key File ${dev.masterKeyFile} failed!"
+ if ! try_empty_passphrase; then
+ ${if dev.fallbackToPassword then "echo" else "die"} "${dev.masterKeyFile} is unavailable"
+ echo " - failing back to interactive password prompt"
+ do_open_passphrase
+ fi
+ fi
+ else
+ # If the key file never shows up we should also try the empty passphrase
+ if ! try_empty_passphrase; then
+ ${if dev.fallbackToPassword then "echo" else "die"} "${dev.masterKeyFile} is unavailable"
+ echo " - failing back to interactive password prompt"
+ do_open_passphrase
+ fi
+ fi
'' else ''
if ! try_empty_passphrase; then
do_open_passphrase
@@ -625,6 +646,15 @@ in
'';
};
+ masterKeyFile = mkOption {
+ default = null;
+ type = types.nullOr types.str;
+ description = lib.mdDoc ''
+ The name of the file (can be a raw device or a partition) that
+ should be used as the master decryption key for the encrypted device.
+ '';
+ };
+
tryEmptyPassphrase = mkOption {
default = false;
type = types.bool;
@@ -700,6 +730,15 @@ in
'';
};
+ disableKeyring = mkOption {
+ default = false;
+ type = types.bool;
+ description = lib.mdDoc ''
+ Disables using the kernel keyring for LUKS2 disks.
+ This is already the default behavior for LUKS1
+ '';
+ };
+
fallbackToPassword = mkOption {
default = false;
type = types.bool;
diff --git a/nixos/modules/tasks/auto-upgrade.nix b/nixos/modules/tasks/auto-upgrade.nix
index 29e3e313336..050bf09c208 100644
--- a/nixos/modules/tasks/auto-upgrade.nix
+++ b/nixos/modules/tasks/auto-upgrade.nix
@@ -4,7 +4,8 @@ with lib;
let cfg = config.system.autoUpgrade;
-in {
+in
+{
options = {
@@ -22,7 +23,7 @@ in {
};
operation = mkOption {
- type = types.enum ["switch" "boot"];
+ type = types.enum [ "switch" "boot" ];
default = "switch";
example = "boot";
description = lib.mdDoc ''
@@ -86,14 +87,14 @@ in {
'';
};
- allowReboot = mkOption {
+ allowKexec = mkOption {
default = false;
type = types.bool;
description = lib.mdDoc ''
- Reboot the system into the new generation instead of a switch
+ kexec the system into the new generation instead of a switch
if the new generation uses a different kernel, kernel modules
or initrd than the booted system.
- See {option}`rebootWindow` for configuring the times at which a reboot is allowed.
+ See {option}`kexecWindow` for configuring the times at which a kexec is allowed.
'';
};
@@ -109,25 +110,25 @@ in {
'';
};
- rebootWindow = mkOption {
+ kexecWindow = mkOption {
description = lib.mdDoc ''
Define a lower and upper time value (in HH:MM format) which
- constitute a time window during which reboots are allowed after an upgrade.
- This option only has an effect when {option}`allowReboot` is enabled.
- The default value of `null` means that reboots are allowed at any time.
+ constitute a time window during which kexecs are allowed after an upgrade.
+ This option only has an effect when {option}`allowKexec` is enabled.
+ The default value of `null` means that kexecs are allowed at any time.
'';
default = null;
example = { lower = "01:00"; upper = "05:00"; };
type = with types; nullOr (submodule {
options = {
lower = mkOption {
- description = lib.mdDoc "Lower limit of the reboot window";
+ description = lib.mdDoc "Lower limit of the kexec window";
type = types.strMatching "[[:digit:]]{2}:[[:digit:]]{2}";
example = "01:00";
};
upper = mkOption {
- description = lib.mdDoc "Upper limit of the reboot window";
+ description = lib.mdDoc "Upper limit of the kexec window";
type = types.strMatching "[[:digit:]]{2}:[[:digit:]]{2}";
example = "05:00";
};
@@ -165,12 +166,12 @@ in {
}];
system.autoUpgrade.flags = (if cfg.flake == null then
- [ "--no-build-output" ] ++ optionals (cfg.channel != null) [
- "-I"
- "nixpkgs=${cfg.channel}/nixexprs.tar.xz"
- ]
- else
- [ "--flake ${cfg.flake}" ]);
+ [ "--no-build-output" ] ++ optionals (cfg.channel != null) [
+ "-I"
+ "nixpkgs=${cfg.channel}/nixexprs.tar.xz"
+ ]
+ else
+ [ "--flake ${cfg.flake}" ]);
systemd.services.nixos-upgrade = {
description = "NixOS Upgrade";
@@ -195,54 +196,56 @@ in {
config.programs.ssh.package
];
- script = let
- nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
- date = "${pkgs.coreutils}/bin/date";
- readlink = "${pkgs.coreutils}/bin/readlink";
- shutdown = "${config.systemd.package}/bin/shutdown";
- upgradeFlag = optional (cfg.channel == null) "--upgrade";
- in if cfg.allowReboot then ''
- ${nixos-rebuild} boot ${toString (cfg.flags ++ upgradeFlag)}
- booted="$(${readlink} /run/booted-system/{initrd,kernel,kernel-modules})"
- built="$(${readlink} /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
-
- ${optionalString (cfg.rebootWindow != null) ''
- current_time="$(${date} +%H:%M)"
-
- lower="${cfg.rebootWindow.lower}"
- upper="${cfg.rebootWindow.upper}"
-
- if [[ "''${lower}" < "''${upper}" ]]; then
- if [[ "''${current_time}" > "''${lower}" ]] && \
- [[ "''${current_time}" < "''${upper}" ]]; then
- do_reboot="true"
+ script =
+ let
+ nixos-rebuild = "${config.system.build.nixos-rebuild}/bin/nixos-rebuild";
+ date = "${pkgs.coreutils}/bin/date";
+ readlink = "${pkgs.coreutils}/bin/readlink";
+ systemctl_kexec = "${config.systemd.package}/bin/systemctl kexec";
+ upgradeFlag = optional (cfg.channel == null) "--upgrade";
+ in
+ if cfg.allowKexec then ''
+ ${nixos-rebuild} boot ${toString (cfg.flags ++ upgradeFlag)}
+ booted="$(${readlink} /run/booted-system/{initrd,kernel,kernel-modules})"
+ built="$(${readlink} /nix/var/nix/profiles/system/{initrd,kernel,kernel-modules})"
+
+ ${optionalString (cfg.kexecWindow != null) ''
+ current_time="$(${date} +%H:%M)"
+
+ lower="${cfg.kexecWindow.lower}"
+ upper="${cfg.kexecWindow.upper}"
+
+ if [[ "''${lower}" < "''${upper}" ]]; then
+ if [[ "''${current_time}" > "''${lower}" ]] && \
+ [[ "''${current_time}" < "''${upper}" ]]; then
+ do_kexec="true"
+ else
+ do_kexec="false"
+ fi
else
- do_reboot="false"
+ # lower > upper, so we are crossing midnight (e.g. lower=23h, upper=6h)
+ # we want to reboot if cur > 23h or cur < 6h
+ if [[ "''${current_time}" < "''${upper}" ]] || \
+ [[ "''${current_time}" > "''${lower}" ]]; then
+ do_kexec="true"
+ else
+ do_kexec="false"
+ fi
fi
+ ''}
+
+ if [ "''${booted}" = "''${built}" ]; then
+ ${nixos-rebuild} ${cfg.operation} ${toString cfg.flags}
+ ${optionalString (cfg.kexecWindow != null) ''
+ elif [ "''${do_kexec}" != true ]; then
+ echo "Outside of configured kexec window, skipping."
+ ''}
else
- # lower > upper, so we are crossing midnight (e.g. lower=23h, upper=6h)
- # we want to reboot if cur > 23h or cur < 6h
- if [[ "''${current_time}" < "''${upper}" ]] || \
- [[ "''${current_time}" > "''${lower}" ]]; then
- do_reboot="true"
- else
- do_reboot="false"
- fi
+ ${systemctl_kexec}
fi
- ''}
-
- if [ "''${booted}" = "''${built}" ]; then
- ${nixos-rebuild} ${cfg.operation} ${toString cfg.flags}
- ${optionalString (cfg.rebootWindow != null) ''
- elif [ "''${do_reboot}" != true ]; then
- echo "Outside of configured reboot window, skipping."
- ''}
- else
- ${shutdown} -r +1
- fi
- '' else ''
- ${nixos-rebuild} ${cfg.operation} ${toString (cfg.flags ++ upgradeFlag)}
- '';
+ '' else ''
+ ${nixos-rebuild} ${cfg.operation} ${toString (cfg.flags ++ upgradeFlag)}
+ '';
startAt = cfg.dates;

Binary file not shown.

View File

@@ -1,38 +0,0 @@
age-encryption.org/v1
-> ssh-ed25519 xoAm7w H4pJd+lWWypSgHCz6JQ2I2Yqhtabmts6LMpHq/MXwwA
izP31JcNATRlNq6m8FYC7uis+RrZo88ckluXUCqtDkU
-> ssh-ed25519 mbw8xA llqgasmhheEDBVbl0sU4O269iS3DY+ExSpUfockvBmE
4b+10lAkTlsIIIbqafjqgPYVNOs78dqw3mhGNt/Kr+U
-> ssh-ed25519 2a2Yhw qPg4uxTQhi/pBSbiZ1tDjmbuLFqrmVck5s52Seu9WyI
NRJnXzFPdf9m/eL2zoS8yeaz9OqMdHbbX8F9FOVedmM
-> ssh-ed25519 dMQYog l68vM+Hj4TdBVWXG8DH5wz1U2ZEuL9sz04B5ykY0GTE
QKDaeV8CUxf34zS6FJneaP49UCeXondKMPURipqR3AQ
-> ssh-ed25519 G2eSCQ 9Sz80YngHh8GimDhEeMtqrOk3hai+YBUUt71mdmjs20
Sc3Tit/RZ9xlrvVK92RJS9dhyPIJ/mZ1NsZo4dmiRnI
-> ssh-ed25519 6AT2/g aQOztqgJ+lmACELQnewjsKhpwRoAc4Xu6j4L0DI2+n8
TX37F7ZCQfFOddgdllnxqKk85PBY04O48VJZvovpk1k
-> ssh-ed25519 yHDAQw /WlCVe+BEy43M37s89SahbmmLzT1jTGYEliZ5EEW1Vo
FGwQj00KhuyAj5cYJhuiR/uZY03GQwKGm8iOSm7VGNo
-> ssh-ed25519 VyYH/Q AWfQDDmiGyVM+r7uIHE+OqhwMPSclTfSJiYjimIl4TA
2STrZzCDqprYylCIB0b6FJK+ipMZx+08RN6ifsOqIyw
-> ssh-ed25519 hPp1nw QEaJAqCwiz0IZMbZ8AaoHi4LNj3MDGARsC4r8bLQhyY
SxjnCzanVkfyF4moOkYyxYKgY+TOSHdX7PnesmkP42A
-> ssh-ed25519 CRfjsA GTOd0wL35nL91iLdJKzNTQ3+e3gRqEO+y3ppLjWzuQM
QiOdvtSkwrCzdMOTpxQW29J49T6JIU/lwdy/4YoFZ6E
-> ssh-ed25519 vwVIvQ jdSld1nAFuUn14W6gOzg1k32JrZEuKFBV4KAP8/Ilhs
4H7jlaf6wPG6xOLGSz28asD/GirT52LVfELviEFHsiY
-> ssh-ed25519 fBrw3g vu/yjmGNPBUJR/qU/tBTSz4QhTb8NR41lG55NZl+YGg
YqF18PP5zdgRExyszHSs35212EOVLXTtN9oREduuI5Q
-> ssh-ed25519 S5xQfg L3uspJyJKVdChakXgXTcmIzDXeoXK5GVmU+DszgS1z4
0kXsfDav+LEL6aYgpWzrD5cpb0zhjdT2v8/p3Q/Y1rI
-> ssh-ed25519 XPxfUQ 1D6dQ8Pbn5EKtdLzw1VMe28jmd4PjzAaxtoDQJ3qPjY
u6Y3NrdowSWLw0YyRXYYEIQUTcb9NGn0UhSzK4HeGlI
-> ssh-ed25519 SpD5mg M66nCAQ05Iam/GAjfcvBvV1FwSsm0MjUsD6awuidsVY
DZWfw5iqujq/ZU3wVuL1kvC7l5NdPSTi2uq48Oc03zA
-> ssh-ed25519 Kk8sng fsfCIcdBVu3xAreLxvq9zHu0MdL77D2cd6PxJAui804
EqNbLicyIkWjmCABaShEmcxEftmjnvQYFFKd1AFMQs8
-> z;-grease !G/_ ;L|^3hU }
bCokcBX9NxBIh97Cf+qsjRq92nbhFTG1E/ygPscvCeexFIgvBkBATLFlxlFZbfHo
jU1hBN7/lA
--- Gf9q3ird84+YdfWY/WJXiO3nz5T4IHa/geMyfSTmzC0
C+ÖÒSI=5Ñ4c²š>^ ÒI„ÍK+šK<C5A1>hŸÕ6J·É§w†íEà펚üy4iˆ8Ù[ÅšOþûBÅn<C385>;ö%³® ÎÏ)²ïцœa5ßšÛ%Iô[

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