Compare commits

..

1 Commits

Author SHA1 Message Date
42cb2c7239 import and update Tokodon package from <https://github.com/NixOS/nixpkgs/pull/170466>
this hangs and then segfaults at start. i suppose i should `gdb` it.
2022-10-30 18:23:18 -07:00
168 changed files with 1817 additions and 6737 deletions

99
flake.lock generated
View File

@@ -22,11 +22,11 @@
]
},
"locked": {
"lastModified": 1667907331,
"narHash": "sha256-bHkAwkYlBjkupPUFcQjimNS8gxWSWjOTevEuwdnp5m0=",
"lastModified": 1665996265,
"narHash": "sha256-/k9og6LDBQwT+f/tJ5ClcWiUl8kCX5m6ognhsAxOiCY=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "6639e3a837fc5deb6f99554072789724997bc8e5",
"rev": "b81e128fc053ab3159d7b464d9b7dedc9d6a6891",
"type": "github"
},
"original": {
@@ -38,11 +38,11 @@
},
"impermanence": {
"locked": {
"lastModified": 1668668915,
"narHash": "sha256-QjY4ZZbs9shwO4LaLpvlU2bO9J1juYhO9NtV3nrbnYQ=",
"lastModified": 1661933071,
"narHash": "sha256-RFgfzldpbCvS+H2qwH+EvNejvqs+NhPVD5j1I7HQQPY=",
"owner": "nix-community",
"repo": "impermanence",
"rev": "5df9108b346f8a42021bf99e50de89c9caa251c3",
"rev": "def994adbdfc28974e87b0e4c949e776207d5557",
"type": "github"
},
"original": {
@@ -54,11 +54,11 @@
"mobile-nixos": {
"flake": false,
"locked": {
"lastModified": 1670131242,
"narHash": "sha256-T/o1/3gffr010fsqgNshs1NJJjsnUYvQnUZgm6hilsY=",
"lastModified": 1666573922,
"narHash": "sha256-CqB8Y5HajptSFE8Em990dcYZIHJWBiO9zd1us4Mzx8M=",
"owner": "nixos",
"repo": "mobile-nixos",
"rev": "5ee45cc1f8e43f4af14ee17ccef9156b0db8cd77",
"rev": "1351091d2537040454fa232d8b94e745ab0eb5a3",
"type": "github"
},
"original": {
@@ -69,11 +69,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1671722432,
"narHash": "sha256-ojcZUekIQeOZkHHzR81st7qxX99dB1Eaaq6PU5MNeKc=",
"lastModified": 1666447894,
"narHash": "sha256-i9WHX4w/et4qPMzEXd9POmnO0/bthjr7R4cblKNHGms=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "652e92b8064949a11bc193b90b74cb727f2a1405",
"rev": "95aeaf83c247b8f5aa561684317ecd860476fcd6",
"type": "github"
},
"original": {
@@ -82,37 +82,37 @@
"type": "indirect"
}
},
"nixpkgs-stable": {
"nixpkgs-22_05": {
"locked": {
"lastModified": 1671883564,
"narHash": "sha256-C15oAtyupmLB3coZY7qzEHXjhtUx/+77olVdqVMruAg=",
"lastModified": 1666488099,
"narHash": "sha256-DANs2epN5QgvxWzH7xF3dzb4WE0lEuMLrMEu/vPmQxw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "dac57a4eccf1442e8bf4030df6fcbb55883cb682",
"rev": "f9115594149ebcb409a42e303bec4956814a8419",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-22.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1666401273,
"narHash": "sha256-AG3MoIjcWwz1SPjJ2nymWu4NmeVj9P40OpB1lsmxFtg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "3933d8bb9120573c0d8d49dc5e890cb211681490",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-22.11",
"ref": "nixos-22.05",
"type": "indirect"
}
},
"nixpkgs-stable_2": {
"locked": {
"lastModified": 1671923641,
"narHash": "sha256-flPauiL5UrfRJD+1oAcEefpEIUqTqnyKScWe/UUU+lE=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "939c05a176b8485971463c18c44f48e56a7801c9",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-22.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"home-manager": "home-manager",
@@ -120,23 +120,40 @@
"mobile-nixos": "mobile-nixos",
"nixpkgs": "nixpkgs",
"nixpkgs-stable": "nixpkgs-stable",
"rycee": "rycee",
"sops-nix": "sops-nix",
"uninsane": "uninsane"
}
},
"rycee": {
"flake": false,
"locked": {
"lastModified": 1666843362,
"narHash": "sha256-xn2bW9/MT0u8Ptlk+f323p46Q/ktZkzMp7oj5SlYDxU=",
"owner": "rycee",
"repo": "nur-expressions",
"rev": "43d3a363c126968db46585b88b8eb97dd32634ad",
"type": "gitlab"
},
"original": {
"owner": "rycee",
"repo": "nur-expressions",
"type": "gitlab"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable_2"
"nixpkgs-22_05": "nixpkgs-22_05"
},
"locked": {
"lastModified": 1671937829,
"narHash": "sha256-YtaNB+mLw0d67JFYNjRWM+/AL3JCXuD/DGlnTlyX1tY=",
"lastModified": 1666499473,
"narHash": "sha256-q1eFnBFL0kHgcnUPeKagw3BfbE/5sMJNGL2E2AR+a2M=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "855b8d51fc3991bd817978f0f093aa6ae0fae738",
"rev": "1b5f9512a265f0c9687dbff47893180f777f4809",
"type": "github"
},
"original": {
@@ -153,11 +170,11 @@
]
},
"locked": {
"lastModified": 1666870107,
"narHash": "sha256-b9eXZxSwhzdJI5uQgfrMhu4SY2POrPkinUg7F5gQVYo=",
"lastModified": 1665758541,
"narHash": "sha256-ibR8bPwHlDjavri5cNVnoo5FmFk1IfNMmQXxat5biqs=",
"ref": "refs/heads/master",
"rev": "80c6ec95bd430e29d231cf745f19279bb76fb382",
"revCount": 164,
"rev": "4ad1801f6cecd678bbeae5dfe5933448dd7b3360",
"revCount": 163,
"type": "git",
"url": "https://git.uninsane.org/colin/uninsane"
},

View File

@@ -4,7 +4,7 @@
{
inputs = {
nixpkgs-stable.url = "nixpkgs/nixos-22.11";
nixpkgs-stable.url = "nixpkgs/nixos-22.05";
nixpkgs.url = "nixpkgs/nixos-unstable";
mobile-nixos = {
url = "github:nixos/mobile-nixos";
@@ -14,6 +14,10 @@
url = "github:nix-community/home-manager/release-22.05";
inputs.nixpkgs.follows = "nixpkgs";
};
rycee = {
url = "gitlab:rycee/nur-expressions";
flake = false;
};
sops-nix = {
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
@@ -31,6 +35,7 @@
nixpkgs-stable,
mobile-nixos,
home-manager,
rycee,
sops-nix,
impermanence,
uninsane
@@ -38,17 +43,14 @@
patchedPkgs = system: nixpkgs.legacyPackages.${system}.applyPatches {
name = "nixpkgs-patched-uninsane";
src = nixpkgs;
patches = import ./nixpatches/list.nix {
inherit (nixpkgs.legacyPackages.${system}) fetchpatch;
inherit (nixpkgs.lib) fakeHash;
};
patches = import ./nixpatches/list.nix nixpkgs.legacyPackages.${system}.fetchpatch;
};
# return something which behaves like `pkgs`, for the provided system
# `local` = architecture of builder. `target` = architecture of the system beying deployed to
nixpkgsFor = local: target: import (patchedPkgs target) { crossSystem = target; localSystem = local; };
# evaluate ONLY our overlay, for the provided system
customPackagesFor = local: target: import ./pkgs/overlay.nix (nixpkgsFor local target) (nixpkgsFor local target);
decl-host = { name, local, target }:
decl-machine = { name, local, target }:
let
nixosSystem = import ((patchedPkgs target) + "/nixos/lib/eval-config.nix");
in (nixosSystem {
@@ -57,13 +59,14 @@
specialArgs = { inherit mobile-nixos home-manager impermanence; };
modules = [
./modules
(import ./hosts/instantiate.nix name)
(import ./machines/instantiate.nix name)
home-manager.nixosModule
impermanence.nixosModule
sops-nix.nixosModules.sops
{
nixpkgs.overlays = [
(import "${mobile-nixos}/overlay/overlay.nix")
(import "${rycee}/overlay.nix")
uninsane.overlay
(import ./pkgs/overlay.nix)
(next: prev: rec {
@@ -72,23 +75,17 @@
# the config can explicitly pull such packages from `pkgs.cross` to do more efficient cross-compilation.
cross = (nixpkgsFor local target) // (customPackagesFor local target);
stable = import nixpkgs-stable { system = target; };
# cross-compatible packages
# gocryptfs = cross.gocryptfs;
# pinned packages:
# 2022/12/13: grpc does not build on aarch64-linux. https://github.com/NixOS/nixpkgs/issues/205887
grpc = stable.grpc;
# depends on grpc, so pinned.
duplicity = stable.duplicity;
electrum = stable.electrum; # 2022-10-10: build break
sequoia = stable.sequoia; # 2022-10-13: build break
})
];
}
];
});
decl-bootable-host = { name, local, target }: rec {
nixosConfiguration = decl-host { inherit name local target; };
decl-bootable-machine = { name, local, target }: rec {
nixosConfiguration = decl-machine { inherit name local target; };
# this produces a EFI-bootable .img file (GPT with a /boot partition and a system (/ or /nix) partition).
# after building this:
# - flash it to a bootable medium (SD card, flash drive, HDD)
@@ -101,41 +98,32 @@
# - boot
# - if fs wasn't resized automatically, then `sudo btrfs filesystem resize max /`
# - checkout this flake into /etc/nixos AND UPDATE THE FS UUIDS.
# - `nixos-rebuild --flake './#<host>' switch`
# - `nixos-rebuild --flake './#<machine>' switch`
img = nixosConfiguration.config.system.build.img;
};
hosts.servo = decl-bootable-host { name = "servo"; local = "x86_64-linux"; target = "x86_64-linux"; };
hosts.desko = decl-bootable-host { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
hosts.lappy = decl-bootable-host { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
hosts.moby = decl-bootable-host { name = "moby"; local = "aarch64-linux"; target = "aarch64-linux"; };
machines.servo = decl-bootable-machine { name = "servo"; local = "aarch64-linux"; target = "aarch64-linux"; };
machines.desko = decl-bootable-machine { name = "desko"; local = "x86_64-linux"; target = "x86_64-linux"; };
machines.lappy = decl-bootable-machine { name = "lappy"; local = "x86_64-linux"; target = "x86_64-linux"; };
machines.moby = decl-bootable-machine { name = "moby"; local = "aarch64-linux"; target = "aarch64-linux"; };
# special cross-compiled variant, to speed up deploys from an x86 box to the arm target
# note that these *do* produce different store paths, because the closure for the tools used to cross compile
# v.s. emulate differ.
# so deploying foo-cross and then foo incurs some rebuilding.
hosts.moby-cross = decl-bootable-host { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
hosts.rescue = decl-bootable-host { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
# so deploying moby-cross and then moby incurs some rebuilding.
machines.moby-cross = decl-bootable-machine { name = "moby"; local = "x86_64-linux"; target = "aarch64-linux"; };
machines.rescue = decl-bootable-machine { name = "rescue"; local = "x86_64-linux"; target = "x86_64-linux"; };
in {
nixosConfigurations = builtins.mapAttrs (name: value: value.nixosConfiguration) hosts;
imgs = builtins.mapAttrs (name: value: value.img) hosts;
nixosConfigurations = builtins.mapAttrs (name: value: value.nixosConfiguration) machines;
imgs = builtins.mapAttrs (name: value: value.img) machines;
packages = let
allPkgsFor = sys: (customPackagesFor sys sys) // {
nixpkgs = nixpkgsFor sys sys;
uninsane = uninsane.packages."${sys}";
rycee = (import "${rycee}/default.nix" { pkgs = nixpkgsFor sys sys; });
};
in {
x86_64-linux = allPkgsFor "x86_64-linux";
aarch64-linux = allPkgsFor "aarch64-linux";
};
templates = {
python-data = {
# initialize with:
# - `nix flake init -t '/home/colin/dev/nixos/#python-data'`
# then enter with:
# - `nix develop`
path = ./templates/python-data;
description = "python environment for data processing";
};
};
};
}

View File

@@ -1,18 +0,0 @@
{ lib, pkgs, ... }:
{
# TODO: don't need to depend on binsh if we were to use a nix-style shebang
system.activationScripts.linkBluetoothKeys = let
unwrapped = ../../scripts/install-bluetooth;
install-bluetooth = pkgs.writeShellApplication {
name = "install-bluetooth";
runtimeInputs = with pkgs; [ coreutils gnused ];
text = ''${unwrapped} "$@"'';
};
in (lib.stringAfter
[ "setupSecrets" "binsh" ]
''
${install-bluetooth}/bin/install-bluetooth /run/secrets/bt
''
);
}

View File

@@ -1,4 +0,0 @@
{ ... }:
{
services.i2p.enable = true;
}

View File

@@ -1,16 +0,0 @@
{ ... }:
{
# /etc/machine-id is a globally unique identifier used for:
# - systemd-networkd: DHCP lease renewal (instead of keying by the MAC address)
# - systemd-journald: to filter logs by host
# - chromium (potentially to track re-installations)
# - gdbus; system services that might upgrade to AF_LOCAL if both services can confirm they're on the same machine
# because of e.g. the chromium use, we *don't want* to persist this.
# however, `journalctl` won't show logs from previous boots unless the machine-ids match.
# so for now, generate something unique from the host ssh key.
# TODO: move this into modules?
system.activationScripts.machine-id = {
deps = [ "etc" ];
text = "sha256sum /etc/ssh/host_keys/ssh_host_ed25519_key | cut -c 1-32 > /etc/machine-id";
};
}

View File

@@ -1,124 +0,0 @@
{ config, ... }:
{
# SOPS configuration:
# docs: https://github.com/Mic92/sops-nix
#
# for each new user you want to edit sops files:
# create a private age key from ssh key:
# $ mkdir -p ~/.config/sops/age; ssh-to-age -private-key -i ~/.ssh/id_ed25519 > ~/.config/sops/age/keys.txt; chmod 600 ~/.config/sops/age/keys.txt
# if the private key was password protected, then first decrypt it:
# $ cp ~/.ssh/id_ed25519 /tmp/id_ed25519
# $ ssh-keygen -p -N "" -f /tmp/id_ed25519
#
# for each user you want to decrypt secrets:
# $ cat ~/.ssh/id_ed25519.pub | ssh-to-age
# add the result to .sops.yaml
# since we specify ssh pubkeys in the nix config, you can just grep for `ssh-ed25519` here and use those instead
#
# for each host you want to decrypt secrets:
# $ cat /etc/ssh/ssh_host_ed25519_key.pub | ssh-to-age
# add the result to .sops.yaml
# $ sops updatekeys secrets/example.yaml
#
# to create a new secret:
# $ sops secrets/example.yaml
# control access below (sops.secret.<x>.owner = ...)
#
# to read a secret:
# $ cat /run/secrets/example_key
# sops.age.sshKeyPaths = [ "/home/colin/.ssh/id_ed25519_dec" ];
# This will add secrets.yaml to the nix store
# You can avoid this by adding a string to the full path instead, i.e.
# sops.defaultSopsFile = "/root/.sops/secrets/example.yaml";
sops.defaultSopsFile = ../../secrets/universal.yaml;
sops.gnupg.sshKeyPaths = []; # disable RSA key import
# This is using an age key that is expected to already be in the filesystem
# sops.age.keyFile = "/home/colin/.ssh/age.pub";
# sops.age.keyFile = "/var/lib/sops-nix/key.txt";
# This will generate a new key if the key specified above does not exist
# sops.age.generateKey = true;
# This is the actual specification of the secrets.
# sops.secrets.example_key = {
# owner = config.users.users.colin.name;
# };
# sops.secrets."myservice/my_subdir/my_secret" = {};
## universal secrets
# TODO: glob these?
sops.secrets."jackett_apikey" = {
sopsFile = ../../secrets/universal.yaml;
owner = config.users.users.colin.name;
};
sops.secrets."router_passwd" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."wg_ovpnd_us_privkey" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."wg_ovpnd_us-atl_privkey" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."wg_ovpnd_us-mi_privkey" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."wg_ovpnd_ukr_privkey" = {
sopsFile = ../../secrets/universal.yaml;
};
sops.secrets."snippets" = {
sopsFile = ../../secrets/universal/snippets.bin;
format = "binary";
owner = config.users.users.colin.name;
};
sops.secrets."bt/car" = {
sopsFile = ../../secrets/universal/bt/car.bin;
format = "binary";
};
sops.secrets."bt/earbuds" = {
sopsFile = ../../secrets/universal/bt/earbuds.bin;
format = "binary";
};
sops.secrets."bt/portable-speaker" = {
sopsFile = ../../secrets/universal/bt/portable-speaker.bin;
format = "binary";
};
sops.secrets."iwd/community-university.psk" = {
sopsFile = ../../secrets/universal/net/community-university.psk.bin;
format = "binary";
};
sops.secrets."iwd/friend-libertarian-dod.psk" = {
sopsFile = ../../secrets/universal/net/friend-libertarian-dod.psk.bin;
format = "binary";
};
sops.secrets."iwd/friend-rationalist-empathist.psk" = {
sopsFile = ../../secrets/universal/net/friend-rationalist-empathist.psk.bin;
format = "binary";
};
sops.secrets."iwd/home-bedroom.psk" = {
sopsFile = ../../secrets/universal/net/home-bedroom.psk.bin;
format = "binary";
};
sops.secrets."iwd/home-shared-24G.psk" = {
sopsFile = ../../secrets/universal/net/home-shared-24G.psk.bin;
format = "binary";
};
sops.secrets."iwd/home-shared.psk" = {
sopsFile = ../../secrets/universal/net/home-shared.psk.bin;
format = "binary";
};
sops.secrets."iwd/iphone" = {
sopsFile = ../../secrets/universal/net/iphone.psk.bin;
format = "binary";
};
sops.secrets."iwd/parents" = {
sopsFile = ../../secrets/universal/net/parents.psk.bin;
format = "binary";
};
}

View File

@@ -1,10 +0,0 @@
{ config, lib, ... }:
{
environment.etc."ssh/host_keys".source = "/nix/persist/etc/ssh/host_keys";
services.openssh.hostKeys = [
{ type = "rsa"; bits = 4096; path = "/etc/ssh/host_keys/ssh_host_rsa_key"; }
{ type = "ed25519"; path = "/etc/ssh/host_keys/ssh_host_ed25519_key"; }
];
}

View File

@@ -1,66 +0,0 @@
{ config, lib, ... }:
# to add a new OVPN VPN:
# - generate a privkey `wg genkey`
# - add this key to `sops secrets/universal.yaml`
# - upload pubkey to OVPN.com
# - generate config @ OVPN.com
# - copy the Address, PublicKey, Endpoint from OVPN's config
# N.B.: maximum interface name in Linux is 15 characters.
let
def-ovpn = name: { endpoint, publicKey, address }: {
networking.wg-quick.interfaces."ovpnd-${name}" = {
inherit address;
privateKeyFile = config.sops.secrets."wg_ovpnd_${name}_privkey".path;
dns = [
"46.227.67.134"
"192.165.9.158"
];
peers = [
{
allowedIPs = [
"0.0.0.0/0"
"::/0"
];
inherit endpoint publicKey;
}
];
# to start: `systemctl start wg-quick-ovpnd-${name}`
autostart = false;
};
};
in lib.mkMerge [
(def-ovpn "us" {
endpoint = "vpn31.prd.losangeles.ovpn.com:9929";
publicKey = "VW6bEWMOlOneta1bf6YFE25N/oMGh1E1UFBCfyggd0k=";
address = [
"172.27.237.218/32"
"fd00:0000:1337:cafe:1111:1111:ab00:4c8f/128"
];
})
# NB: us-* share the same wg key and link-local addrs, but distinct public addresses
(def-ovpn "us-atl" {
endpoint = "vpn18.prd.atlanta.ovpn.com:9929";
publicKey = "Dpg/4v5s9u0YbrXukfrMpkA+XQqKIFpf8ZFgyw0IkE0=";
address = [
"172.21.182.178/32"
"fd00:0000:1337:cafe:1111:1111:cfcb:27e3/128"
];
})
(def-ovpn "us-mi" {
endpoint = "vpn34.prd.miami.ovpn.com:9929";
publicKey = "VtJz2irbu8mdkIQvzlsYhU+k9d55or9mx4A2a14t0V0=";
address = [
"172.21.182.178/32"
"fd00:0000:1337:cafe:1111:1111:cfcb:27e3/128"
];
})
(def-ovpn "ukr" {
endpoint = "vpn96.prd.kyiv.ovpn.com:9929";
publicKey = "CjZcXDxaaKpW8b5As1EcNbI6+42A6BjWahwXDCwfVFg=";
address = [
"172.18.180.159/32"
"fd00:0000:1337:cafe:1111:1111:ec5c:add3/128"
];
})
]

View File

@@ -1,10 +0,0 @@
# trampoline from flake.nix into the specific host definition, while doing a tiny bit of common setup
hostName: { ... }: {
imports = [
./${hostName}
./common
];
networking.hostName = hostName;
}

View File

@@ -1,89 +0,0 @@
{ ... }:
{
sane.impermanence.root-on-tmpfs = true;
# we need a /tmp for building large nix things
fileSystems."/tmp" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=777"
"defaults"
];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/cc81cca0-3cc7-4d82-a00c-6243af3e7776";
fsType = "btrfs";
options = [
"compress=zstd"
"defaults"
];
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/6EE3-4171";
fsType = "vfat";
};
# slow, external storage (for archiving, etc)
fileSystems."/nix/persist/ext" = {
device = "/dev/disk/by-uuid/aa272cff-0fcc-498e-a4cb-0d95fb60631b";
fsType = "btrfs";
options = [
"compress=zstd"
"defaults"
];
};
sane.impermanence.dirs = [
# TODO: this is overly broad; only need media and share directories to be persisted
{ user = "colin"; group = "users"; directory = "/var/lib/uninsane"; }
];
# direct these media directories to external storage
environment.persistence."/nix/persist/ext/persist" = {
directories = [
({
user = "colin";
group = "users";
mode = "0777";
directory = "/var/lib/uninsane/media/Videos";
})
({
user = "colin";
group = "users";
mode = "0777";
directory = "/var/lib/uninsane/media/freeleech";
})
];
};
# in-memory compressed RAM (seems to be dynamically sized)
# zramSwap = {
# enable = true;
# };
# btrfs doesn't easily support swapfiles
# swapDevices = [
# { device = "/nix/persist/swapfile"; size = 4096; }
# ];
# this can be a partition. create with:
# fdisk <dev>
# n
# <default partno>
# <start>
# <end>
# t
# <partno>
# 19 # set part type to Linux swap
# w # write changes
# mkswap -L swap <part>
# swapDevices = [
# {
# label = "swap";
# # TODO: randomEncryption.enable = true;
# }
# ];
}

View File

@@ -1,212 +0,0 @@
{ config, pkgs, ... }:
{
networking.domain = "uninsane.org";
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
# Per-interface useDHCP will be mandatory in the future, so this generated config
# replicates the default behaviour.
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
# XXX colin: probably don't need this. wlan0 won't be populated unless i touch a value in networking.interfaces.wlan0
networking.wireless.enable = false;
# networking.firewall.enable = false;
networking.firewall.enable = true;
# this is needed to forward packets from the VPN to the host
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
# unless we add interface-specific settings for each VPN, we have to define nameservers globally.
# networking.nameservers = [
# "1.1.1.1"
# "9.9.9.9"
# ];
# use systemd's stub resolver.
# /etc/resolv.conf isn't sophisticated enough to use different servers per net namespace (or link).
# instead, running the stub resolver on a known address in the root ns lets us rewrite packets
# in the ovnps namespace to use the provider's DNS resolvers.
# a weakness is we can only query 1 NS at a time (unless we were to clone the packets?)
# there also seems to be some cache somewhere that's shared between the two namespaces.
# i think this is a libc thing. might need to leverage proper cgroups to _really_ kill it.
# - getent ahostsv4 www.google.com
# - try fix: <https://serverfault.com/questions/765989/connect-to-3rd-party-vpn-server-but-dont-use-it-as-the-default-route/766290#766290>
services.resolved.enable = true;
networking.nameservers = [
# use systemd-resolved resolver
# full resolver (which understands /etc/hosts) lives on 127.0.0.53
# stub resolver (just forwards upstream) lives on 127.0.0.54
"127.0.0.53"
];
# nscd -- the Name Service Caching Daemon -- caches DNS query responses
# in a way that's unaware of my VPN routing, so routes are frequently poor against
# services which advertise different IPs based on geolocation.
# nscd claims to be usable without a cache, but in practice i can't get it to not cache!
# nsncd is the Name Service NON-Caching Daemon. it's a drop-in that doesn't cache;
# this is OK on the host -- because systemd-resolved caches. it's probably sub-optimal
# in the netns and we query upstream DNS more often than needed. hm.
# TODO: run a separate recursive resolver in each namespace.
services.nscd.enableNsncd = true;
# services.resolved.extraConfig = ''
# # docs: `man resolved.conf`
# # DNS servers to use via the `wg0` interface.
# # i hope that from the root ns, these aren't visible.
# DNS=46.227.67.134%wg0 192.165.9.158%wg0
# FallbackDNS=1.1.1.1 9.9.9.9
# '';
# OVPN CONFIG (https://www.ovpn.com):
# DOCS: https://nixos.wiki/wiki/WireGuard
# if you `systemctl restart wireguard-wg0`, make sure to also restart any other services in `NetworkNamespacePath = .../ovpns`.
# TODO: why not create the namespace as a seperate operation (nix config for that?)
networking.wireguard.enable = true;
networking.wireguard.interfaces.wg0 = let
ip = "${pkgs.iproute2}/bin/ip";
in-ns = "${ip} netns exec ovpns";
iptables = "${pkgs.iptables}/bin/iptables";
veth-host-ip = "10.0.1.5";
veth-local-ip = "10.0.1.6";
vpn-ip = "185.157.162.178";
# DNS = 46.227.67.134, 192.165.9.158, 2a07:a880:4601:10f0:cd45::1, 2001:67c:750:1:cafe:cd45::1
vpn-dns = "46.227.67.134";
in {
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
# wg is active only in this namespace.
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
# sudo ip netns exec ovpns ping www.google.com
interfaceNamespace = "ovpns";
ips = [
"185.157.162.178/32"
];
peers = [
{
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
endpoint = "185.157.162.10:9930";
# alternatively: use hostname, but that presents bootstrapping issues (e.g. if host net flakes)
# endpoint = "vpn36.prd.amsterdam.ovpn.com:9930";
allowedIPs = [ "0.0.0.0/0" ];
# nixOS says this is important for keeping NATs active
persistentKeepalive = 25;
# re-executes wg this often. docs hint that this might help wg notice DNS/hostname changes.
# so, maybe that helps if we specify endpoint as a domain name
# dynamicEndpointRefreshSeconds = 30;
# when refresh fails, try it again after this period instead.
# TODO: not avail until nixpkgs upgrade
# dynamicEndpointRefreshRestartSeconds = 5;
}
];
preSetup = "" + ''
${ip} netns add ovpns || echo "ovpns already exists"
'';
postShutdown = "" + ''
${in-ns} ip link del ovpns-veth-b || echo "couldn't delete ovpns-veth-b"
${ip} link del ovpns-veth-a || echo "couldn't delete ovpns-veth-a"
${ip} netns delete ovpns || echo "couldn't delete ovpns"
# restore rules/routes
${ip} rule del from ${veth-host-ip} lookup ovpns pref 50 || echo "couldn't delete init -> ovpns rule"
${ip} route del default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns || echo "couldn't delete init -> ovpns route"
${ip} rule add from all lookup local pref 0
${ip} rule del from all lookup local pref 100
'';
postSetup = "" + ''
# DOCS:
# - some of this approach is described here: <https://josephmuia.ca/2018-05-16-net-namespaces-veth-nat/>
# - iptables primer: <https://danielmiessler.com/study/iptables/>
# create veth pair
${ip} link add ovpns-veth-a type veth peer name ovpns-veth-b
${ip} addr add ${veth-host-ip}/24 dev ovpns-veth-a
${ip} link set ovpns-veth-a up
# mv veth-b into the ovpns namespace
${ip} link set ovpns-veth-b netns ovpns
${in-ns} ip addr add ${veth-local-ip}/24 dev ovpns-veth-b
${in-ns} ip link set ovpns-veth-b up
# make it so traffic originating from the host side of the veth
# is sent over the veth no matter its destination.
${ip} rule add from ${veth-host-ip} lookup ovpns pref 50
# for traffic originating at the host veth to the WAN, use the veth as our gateway
# not sure if the metric 1002 matters.
${ip} route add default via ${veth-local-ip} dev ovpns-veth-a proto kernel src ${veth-host-ip} metric 1002 table ovpns
# give the default route lower priority
${ip} rule add from all lookup local pref 100
${ip} rule del from all lookup local pref 0
# bridge HTTP traffic:
# any external port-80 request sent to the VPN addr will be forwarded to the rootns.
# this exists so LetsEncrypt can procure a cert for the MX over http.
# TODO: we could use _acme_challence.mx.uninsane.org CNAME to avoid this forwarding
# - <https://community.letsencrypt.org/t/where-does-letsencrypt-resolve-dns-from/37607/8>
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 80 -m iprange --dst-range ${vpn-ip} \
-j DNAT --to-destination ${veth-host-ip}:80
# we also bridge DNS traffic
${in-ns} ${iptables} -A PREROUTING -t nat -p udp --dport 53 -m iprange --dst-range ${vpn-ip} \
-j DNAT --to-destination ${veth-host-ip}:53
${in-ns} ${iptables} -A PREROUTING -t nat -p tcp --dport 53 -m iprange --dst-range ${vpn-ip} \
-j DNAT --to-destination ${veth-host-ip}:53
# in order to access DNS in this netns, we need to route it to the VPN's nameservers
# - alternatively, we could fix DNS servers like 1.1.1.1.
${in-ns} ${iptables} -A OUTPUT -t nat -p udp --dport 53 -m iprange --dst-range 127.0.0.53 \
-j DNAT --to-destination ${vpn-dns}:53
'';
};
# create a new routing table that we can use to proxy traffic out of the root namespace
# through the ovpns namespace, and to the WAN via VPN.
networking.iproute2.rttablesExtraConfig = ''
5 ovpns
'';
networking.iproute2.enable = true;
sops.secrets."wg_ovpns_privkey" = {
sopsFile = ../../secrets/servo.yaml;
};
# HURRICANE ELECTRIC CONFIG:
# networking.sits = {
# hurricane = {
# remote = "216.218.226.238";
# local = "192.168.0.5";
# # local = "10.0.0.5";
# # remote = "10.0.0.1";
# # local = "10.0.0.22";
# dev = "eth0";
# ttl = 255;
# };
# };
# networking.interfaces."hurricane".ipv6 = {
# addresses = [
# # mx.uninsane.org (publically routed /64)
# {
# address = "2001:470:b:465::1";
# prefixLength = 128;
# }
# # client addr
# # {
# # address = "2001:470:a:466::2";
# # prefixLength = 64;
# # }
# ];
# routes = [
# {
# address = "::";
# prefixLength = 0;
# # via = "2001:470:a:466::1";
# }
# ];
# };
# # after configuration, we want the hurricane device to look like this:
# # hurricane: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1480
# # inet6 2001:470:a:450::2 prefixlen 64 scopeid 0x0<global>
# # inet6 fe80::c0a8:16 prefixlen 64 scopeid 0x20<link>
# # sit txqueuelen 1000 (IPv6-in-IPv4)
# # test with:
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
# # ping 2607:f8b0:400a:80b::2004
}

View File

@@ -1,31 +0,0 @@
{ config, lib, pkgs, ... }:
# using manual ddns now
lib.mkIf false
{
systemd.services.ddns-afraid = {
description = "update dynamic DNS entries for freedns.afraid.org";
serviceConfig = {
EnvironmentFile = config.sops.secrets.ddns_afraid.path;
# TODO: ProtectSystem = "strict";
# TODO: ProtectHome = "full";
# TODO: PrivateTmp = true;
};
script = let
curl = "${pkgs.curl}/bin/curl -4";
in ''
${curl} "https://freedns.afraid.org/dynamic/update.php?$AFRAID_KEY"
'';
};
systemd.timers.ddns-afraid = {
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnStartupSec = "2min";
OnUnitActiveSec = "10min";
};
};
sops.secrets."ddns_afraid" = {
sopsFile = ../../../secrets/servo.yaml;
};
}

View File

@@ -1,393 +0,0 @@
# docs:
# - <https://docs.ejabberd.im/admin/configuration/basic>
# example configs:
# - <https://github.com/vkleen/machines/blob/138a2586ce185d7cf201d4e1fe898c83c4af52eb/hosts/europium/ejabberd.nix>
# - <https://github.com/Mic92/stockholm/blob/675ef0088624c9de1cb531f318446316884a9d3d/tv/3modules/ejabberd/default.nix>
# - <https://github.com/buffet/tararice/blob/master/programs/ejabberd.nix>
# - enables STUN and TURN
# - only over UDP 3478, not firewall-forwarding any TURN port range
# - uses stun_disco module (but with no options)
# - <https://github.com/leo60228/dotfiles/blob/39b3abba3009bdc31413d4757ca2f882a33eec8b/files/ejabberd.yml>
# - <https://github.com/Mic92/dotfiles/blob/ddf0f4821f554f7667fc803344657367c55fb9e6/nixos/eve/modules/ejabberd.nix>
# - <nixpkgs:nixos/tests/xmpp/ejabberd.nix>
# - 2013: <https://github.com/processone/ejabberd/blob/master/ejabberd.yml.example>
#
# compliance tests:
# - <https://compliance.conversations.im/server/uninsane.org/#xep0352>
{ config, lib, pkgs, ... }:
# XXX: avatar support works in MUCs but not DMs
# lib.mkIf false
{
sane.impermanence.dirs = [
{ user = "ejabberd"; group = "ejabberd"; directory = "/var/lib/ejabberd"; }
];
networking.firewall.allowedTCPPorts = [
3478 # STUN/TURN
5222 # XMPP client -> server
5223 # XMPPS client -> server (XMPP over TLS)
5269 # XMPP server -> server
5270 # XMPPS server -> server (XMPP over TLS)
5280 # bosh
5281 # bosh (https) ??
5349 # STUN/TURN (TLS)
5443 # web services (file uploads, websockets, admin)
];
networking.firewall.allowedUDPPorts = [
3478 # STUN/TURN
];
networking.firewall.allowedTCPPortRanges = [{
from = 49152; # TURN
to = 65535;
}];
networking.firewall.allowedUDPPortRanges = [{
from = 49152; # TURN
to = 65535;
}];
# provide access to certs
users.users.ejabberd.extraGroups = [ "nginx" ];
security.acme.certs."uninsane.org".extraDomainNames = [
"xmpp.uninsane.org"
"muc.xmpp.uninsane.org"
"pubsub.xmpp.uninsane.org"
"upload.xmpp.uninsane.org"
"vjid.xmpp.uninsane.org"
];
# exists so the XMPP server's cert can obtain altNames for all its resources
services.nginx.virtualHosts."xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
services.nginx.virtualHosts."muc.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
services.nginx.virtualHosts."pubsub.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
services.nginx.virtualHosts."upload.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
services.nginx.virtualHosts."vjid.xmpp.uninsane.org" = {
useACMEHost = "uninsane.org";
};
sane.services.trust-dns.zones."uninsane.org".inet = {
# XXX: SRV records have to point to something with a A/AAAA record; no CNAMEs
A."xmpp" = [ "%NATIVE%" ];
CNAME."muc.xmpp" = [ "xmpp" ];
CNAME."pubsub.xmpp" = [ "xmpp" ];
CNAME."upload.xmpp" = [ "xmpp" ];
CNAME."vjid.xmpp" = [ "xmpp" ];
# _Service._Proto.Name TTL Class SRV Priority Weight Port Target
# - <https://xmpp.org/extensions/xep-0368.html>
# something's requesting the SRV records for muc.xmpp, so let's include it
# nothing seems to request XMPP SRVs for the other records (except @)
# lower numerical priority field tells clients to prefer this method
SRV."_xmpps-client._tcp.muc.xmpp" = [ "3 50 5223 xmpp" ];
SRV."_xmpps-server._tcp.muc.xmpp" = [ "3 50 5270 xmpp" ];
SRV."_xmpp-client._tcp.muc.xmpp" = [ "5 50 5222 xmpp" ];
SRV."_xmpp-server._tcp.muc.xmpp" = [ "5 50 5269 xmpp" ];
SRV."_xmpps-client._tcp" = [ "3 50 5223 xmpp" ];
SRV."_xmpps-server._tcp" = [ "3 50 5270 xmpp" ];
SRV."_xmpp-client._tcp" = [ "5 50 5222 xmpp" ];
SRV."_xmpp-server._tcp" = [ "5 50 5269 xmpp" ];
SRV."_stun._udp" = [ "5 50 3478 xmpp" ];
SRV."_stun._tcp" = [ "5 50 3478 xmpp" ];
SRV."_stuns._tcp" = [ "5 50 5349 xmpp" ];
SRV."_turn._udp" = [ "5 50 3478 xmpp" ];
SRV."_turn._tcp" = [ "5 50 3478 xmpp" ];
SRV."_turns._tcp" = [ "5 50 5349 xmpp" ];
};
# TODO: allocate UIDs/GIDs ?
services.ejabberd.enable = true;
services.ejabberd.configFile = "/var/lib/ejabberd/ejabberd.yaml";
systemd.services.ejabberd.preStart = let
config-in = pkgs.writeTextFile {
name = "ejabberd.yaml.in";
text = ''
hosts:
- uninsane.org
# none | emergency | alert | critical | error | warning | notice | info | debug
loglevel: debug
# loglevel: info
# loglevel: notice
acme:
auto: false
certfiles:
- /var/lib/acme/uninsane.org/full.pem
# ca_file: ${pkgs.cacert.unbundled}/etc/ssl/certs/
# ca_file: ${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
pam_userinfotype: jid
acl:
admin:
user:
- "colin@uninsane.org"
local:
user_regexp: ""
loopback:
ip:
- 127.0.0.0/8
- ::1/128
access_rules:
local:
allow: local
c2s_access:
allow: all
announce:
allow: admin
configure:
allow: admin
muc_create:
allow: local
pubsub_createnode_access:
allow: all
trusted_network:
allow: loopback
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shaper-rules>
shaper_rules:
# setting this to above 1 may break outgoing messages
# - maybe some servers rate limit? or just don't understand simultaneous connections?
max_s2s_connections: 1
max_user_sessions: 10
max_user_offline_messages: 5000
c2s_shaper:
fast: all
s2s_shaper:
med: all
# docs: <https://docs.ejabberd.im/admin/configuration/basic/#shapers>
# this limits the bytes/sec.
# for example, burst: 3_000_000 and rate: 100_000 means:
# - each client has a BW budget that accumulates 100kB/sec and is capped at 3 MB
shaper:
fast: 1000000
med: 500000
# fast:
# - rate: 1000000
# - burst_size: 10000000
# med:
# - rate: 500000
# - burst_size: 5000000
# see: <https://docs.ejabberd.im/admin/configuration/listen/>
# s2s_use_starttls: true
s2s_use_starttls: optional
# lessens 504: remote-server-timeout errors
# see: <https://github.com/processone/ejabberd/issues/3105#issuecomment-562182967>
negotiation_timeout: 60
listen:
-
port: 5222
module: ejabberd_c2s
shaper: c2s_shaper
starttls: true
access: c2s_access
-
port: 5223
module: ejabberd_c2s
shaper: c2s_shaper
tls: true
access: c2s_access
-
port: 5269
module: ejabberd_s2s_in
shaper: s2s_shaper
-
port: 5270
module: ejabberd_s2s_in
shaper: s2s_shaper
tls: true
-
port: 5443
module: ejabberd_http
tls: true
request_handlers:
/admin: ejabberd_web_admin # TODO: ensure this actually works
/api: mod_http_api # ejabberd API endpoint (to control server)
/bosh: mod_bosh
/upload: mod_http_upload
/ws: ejabberd_http_ws
# /.well-known/host-meta: mod_host_meta
# /.well-known/host-meta.json: mod_host_meta
-
# STUN+TURN TCP
# note that the full port range should be forwarded ("not NAT'd")
# `use_turn=true` enables both TURN *and* STUN
port: 3478
module: ejabberd_stun
transport: tcp
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
turn_ipv4_address: %NATIVE%
-
# STUN+TURN UDP
port: 3478
module: ejabberd_stun
transport: udp
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
turn_ipv4_address: %NATIVE%
-
# STUN+TURN TLS over TCP
port: 5349
module: ejabberd_stun
transport: tcp
tls: true
certfile: /var/lib/acme/uninsane.org/full.pem
use_turn: true
turn_min_port: 49152
turn_max_port: 65535
turn_ipv4_address: %NATIVE%
# TODO: enable mod_fail2ban
# TODO(low): look into mod_http_fileserver for serving macros?
modules:
# mod_adhoc: {}
# mod_announce:
# access: admin
# allows users to set avatars in vCard
# - <https://docs.ejabberd.im/admin/configuration/modules/#mod-avatar>
mod_avatar: {}
mod_caps: {} # for mod_pubsub
mod_carboncopy: {} # allows multiple clients to receive a user's message
# queues messages when recipient is offline, including PEP and presence messages.
# compliance test suggests this be enabled
mod_client_state: {}
# mod_conversejs: TODO: enable once on 21.12
# allows clients like Dino to discover where to upload files
mod_disco:
server_info:
-
modules: all
name: abuse-addresses
urls:
- "mailto:admin.xmpp@uninsane.org"
- "xmpp:colin@uninsane.org"
-
modules: all
name: admin-addresses
urls:
- "mailto:admin.xmpp@uninsane.org"
- "xmpp:colin@uninsane.org"
mod_http_upload:
host: upload.xmpp.uninsane.org
hosts:
- upload.xmpp.uninsane.org
put_url: "https://@HOST@:5443/upload"
dir_mode: "0750"
file_mode: "0750"
rm_on_unregister: false
# allow discoverability of BOSH and websocket endpoints
# TODO: enable once on ejabberd 22.05 (presently 21.04)
# mod_host_meta: {}
mod_jidprep: {} # probably not needed: lets clients normalize jids
mod_last: {} # allow other users to know when i was last online
mod_mam:
# Mnesia is limited to 2GB, better to use an SQL backend
# For small servers SQLite is a good fit and is very easy
# to configure. Uncomment this when you have SQL configured:
# db_type: sql
assume_mam_usage: true
default: always
mod_muc:
access:
- allow
access_admin:
- allow: admin
access_create: muc_create
access_persistent: muc_create
access_mam:
- allow
history_size: 100 # messages to show new participants
host: muc.xmpp.uninsane.org
hosts:
- muc.xmpp.uninsane.org
default_room_options:
anonymous: false
lang: en
persistent: true
mam: true
mod_muc_admin: {}
mod_offline: # store messages for a user when they're offline (TODO: understand multi-client workflow?)
access_max_user_messages: max_user_offline_messages
store_groupchat: true
mod_ping: {}
mod_privacy: {} # deprecated, but required for `ejabberctl export_piefxis`
mod_private: {} # allow local clients to persist arbitrary data on my server
# push notifications to services integrated with e.g. Apple/Android.
# default is for a maximum amount of PII to be withheld, since these push notifs
# generally traverse 3rd party services. can opt to include message body, etc, though.
mod_push: {}
# i don't fully understand what this does, but it seems aimed at making push notifs more reliable.
mod_push_keepalive: {}
mod_roster:
versioning: true
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-s2s-dialback>
# s2s dialback to verify inbound messages
# unclear to what degree the XMPP network requires this
mod_s2s_dialback: {}
mod_shared_roster: {} # creates groups for @all, @online, and anything manually administered?
mod_stream_mgmt:
resend_on_timeout: if_offline # resend undelivered messages if the origin client is offline
# fallback for when DNS-based STUN discovery is unsupported.
# - see: <https://xmpp.org/extensions/xep-0215.html>
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-stun-disco>
# people say to just keep this defaulted (i guess ejabberd knows to return its `host` option of uninsane.org?)
mod_stun_disco: {}
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-vcard>
mod_vcard:
allow_return_all: true # all users are discoverable (?)
host: vjid.xmpp.uninsane.org
hosts:
- vjid.xmpp.uninsane.org
search: true
mod_vcard_xupdate: {} # needed for avatars
# docs: <https://docs.ejabberd.im/admin/configuration/modules/#mod-pubsub>
mod_pubsub: # needed for avatars
access_createnode: pubsub_createnode_access
host: pubsub.xmpp.uninsane.org
hosts:
- pubsub.xmpp.uninsane.org
ignore_pep_from_offline: false
last_item_cache: true
plugins:
- pep
- flat
force_node_config:
# ensure client bookmarks are private
storage:bookmarks:
access_model: whitelist
urn:xmpp:avatar:data:
access_model: open
urn:xmpp:avatar:metadata:
access_model: open
mod_version: {}
'';
};
sed = "${pkgs.gnused}/bin/sed";
in ''
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
# config is 444 (not 644), so we want to write out-of-place and then atomically move
# TODO: factor this out into `sane-woop` helper?
rm -f /var/lib/ejabberd/ejabberd.yaml.new
${sed} "s/%NATIVE%/$ip/" ${config-in} > /var/lib/ejabberd/ejabberd.yaml.new
mv /var/lib/ejabberd/ejabberd.yaml{.new,}
'';
sane.services.dyn-dns.restartOnChange = [ "ejabberd.service" ];
}

View File

@@ -1,69 +0,0 @@
{ config, lib, ... }:
# TODO: re-enable after migrating media dir to /var/lib/uninsane/media
# else it's too spammy
lib.mkIf false
{
networking.firewall.allowedUDPPorts = [
1900 7359 # DLNA: https://jellyfin.org/docs/general/networking/index.html
];
sane.impermanence.dirs = [
# TODO: mode? could be more granular
{ user = "jellyfin"; group = "jellyfin"; directory = "/var/lib/jellyfin"; }
];
# Jellyfin multimedia server
# this is mostly taken from the official jellfin.org docs
services.nginx.virtualHosts."jelly.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
proxy_buffering off;
'';
};
# locations."/web/" = {
# proxyPass = "http://127.0.0.1:8096/web/index.html";
# extraConfig = ''
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Forwarded-Protocol $scheme;
# proxy_set_header X-Forwarded-Host $http_host;
# '';
# };
locations."/socket" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
'';
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jelly" = [ "native" ];
# users.users.jellyfin.uid = config.sane.allocations.jellyfin-uid;
# users.groups.jellyfin.gid = config.sane.allocations.jellyfin-gid;
services.jellyfin.enable = true;
}

View File

@@ -1,17 +0,0 @@
{ ... }:
{
sane.services.kiwix-serve = {
enable = true;
port = 8013;
zimPaths = [ "/var/lib/uninsane/www-archive/wikipedia_en_all_maxi_2022-05.zim" ];
};
services.nginx.virtualHosts."w.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/".proxyPass = "http://127.0.0.1:8013";
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."w" = [ "native" ];
}

View File

@@ -1,168 +0,0 @@
# docs: https://nixos.wiki/wiki/Nginx
{ config, pkgs, ... }:
let
# make the logs for this host "public" so that they show up in e.g. metrics
publog = vhost: vhost // {
extraConfig = (vhost.extraConfig or "") + ''
access_log /var/log/nginx/public.log vcombined;
'';
};
# kTLS = true; # in-kernel TLS for better perf
in
{
networking.firewall.allowedTCPPorts = [ 80 443 ];
services.nginx.enable = true;
services.nginx.appendConfig = ''
# use 1 process per core.
# may want to increase worker_connections too, but `ulimit -n` must be increased first.
worker_processes auto;
'';
# this is the standard `combined` log format, with the addition of $host
# so that we have the virtualHost in the log.
# KEEP IN SYNC WITH GOACCESS
# goaccess calls this VCOMBINED:
# - <https://gist.github.com/jyap808/10570005>
services.nginx.commonHttpConfig = ''
log_format vcombined '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referrer" "$http_user_agent"';
access_log /var/log/nginx/private.log vcombined;
'';
# sets gzip_comp_level = 5
services.nginx.recommendedGzipSettings = true;
# enables OCSP stapling (so clients don't need contact the OCSP server -- i do instead)
# - doesn't seem to, actually: <https://www.ssllabs.com/ssltest/analyze.html?d=uninsane.org>
# caches TLS sessions for 10m
services.nginx.recommendedTlsSettings = true;
# enables sendfile, tcp_nopush, tcp_nodelay, keepalive_timeout 65
services.nginx.recommendedOptimisation = true;
# web blog/personal site
services.nginx.virtualHosts."uninsane.org" = publog {
root = "${pkgs.uninsane-dot-org}/share/uninsane-dot-org";
# a lot of places hardcode https://uninsane.org,
# and then when we mix http + non-https, we get CORS violations
# and things don't look right. so force SSL.
forceSSL = true;
enableACME = true;
# inherit kTLS;
# for OCSP stapling
sslTrustedCertificate = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
# uninsane.org/share/foo => /var/lib/uninsane/root/share/foo.
# yes, nginx does not strip the prefix when evaluating against the root.
locations."/share".root = "/var/lib/uninsane/root";
# allow matrix users to discover that @user:uninsane.org is reachable via matrix.uninsane.org
locations."= /.well-known/matrix/server".extraConfig =
let
# use 443 instead of the default 8448 port to unite
# the client-server and server-server port for simplicity
server = { "m.server" = "matrix.uninsane.org:443"; };
in ''
add_header Content-Type application/json;
return 200 '${builtins.toJSON server}';
'';
locations."= /.well-known/matrix/client".extraConfig =
let
client = {
"m.homeserver" = { "base_url" = "https://matrix.uninsane.org"; };
"m.identity_server" = { "base_url" = "https://vector.im"; };
};
# ACAO required to allow element-web on any URL to request this json file
in ''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '${builtins.toJSON client}';
'';
# static URLs might not be aware of .well-known (e.g. registration confirmation URLs),
# so hack around that.
locations."/_matrix" = {
proxyPass = "http://127.0.0.1:8008";
};
locations."/_synapse" = {
proxyPass = "http://127.0.0.1:8008";
};
# allow ActivityPub clients to discover how to reach @user@uninsane.org
# TODO: waiting on https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3361/
# locations."/.well-known/nodeinfo" = {
# proxyPass = "http://127.0.0.1:4000";
# extraConfig = pleromaExtraConfig;
# };
};
# serve any site not listed above, if it's static.
# because we define it dynamically, SSL isn't trivial. support only http
# documented <https://nginx.org/en/docs/http/ngx_http_core_module.html#server_name>
services.nginx.virtualHosts."~^(?<domain>.+)$" = {
default = true;
addSSL = true;
enableACME = false;
sslCertificate = "/var/www/certs/wildcard/cert.pem";
sslCertificateKey = "/var/www/certs/wildcard/key.pem";
# sslCertificate = "/var/lib/acme/.minica/cert.pem";
# sslCertificateKey = "/var/lib/acme/.minica/key.pem";
# serverName = null;
locations."/" = {
# somehow this doesn't escape -- i get error 400 if i:
# curl 'http://..' --resolve '..:80:127.0.0.1'
root = "/var/www/sites/$domain";
# tryFiles = "$domain/$uri $domain/$uri/ =404";
};
};
security.acme.acceptTerms = true;
security.acme.defaults.email = "admin.acme@uninsane.org";
users.users.acme.uid = config.sane.allocations.acme-uid;
users.groups.acme.gid = config.sane.allocations.acme-gid;
sane.impermanence.dirs = [
# TODO: mode?
{ user = "acme"; group = "acme"; directory = "/var/lib/acme"; }
{ user = "colin"; group = "users"; directory = "/var/www/sites"; }
];
# let's encrypt default chain looks like:
# - End-entity certificate ← R3 ← ISRG Root X1 ← DST Root CA X3
# - <https://community.letsencrypt.org/t/production-chain-changes/150739>
# DST Root CA X3 expired in 2021 (?)
# the alternative chain is:
# - End-entity certificate ← R3 ← ISRG Root X1 (self-signed)
# using this alternative chain grants more compatibility for services like ejabberd
# but might decrease compatibility with very old clients that don't get updates (e.g. old android, iphone <= 4).
# security.acme.defaults.extraLegoFlags = [
security.acme.certs."uninsane.org" = rec {
# ISRG Root X1 results in lets encrypt sending the same chain as default,
# just without the final ISRG Root X1 ← DST Root CA X3 link.
# i.e. we could alternative clip the last item and achieve the exact same thing.
extraLegoRunFlags = [
"--preferred-chain" "ISRG Root X1"
];
extraLegoRenewFlags = extraLegoRunFlags;
};
# TODO: alternatively, we could clip the last cert IF it's expired,
# optionally outputting that to a new cert file.
# security.acme.defaults.postRun = "";
# create a self-signed SSL certificate for use with literally any domain.
# browsers will reject this, but proxies and local testing tools can be configured
# to accept it.
system.activationScripts.generate-x509-self-signed.text = ''
mkdir -p /var/www/certs/wildcard
test -f /var/www/certs/wildcard/key.pem || ${pkgs.openssl}/bin/openssl \
req -x509 -newkey rsa:4096 \
-keyout /var/www/certs/wildcard/key.pem \
-out /var/www/certs/wildcard/cert.pem \
-sha256 -nodes -days 3650 \
-addext 'subjectAltName=DNS:*' \
-subj '/CN=self-signed'
chmod 640 /var/www/certs/wildcard/{key,cert}.pem
chown root:nginx /var/www/certs/wildcard /var/www/certs/wildcard/{key,cert}.pem
'';
}

View File

@@ -1,21 +0,0 @@
{ config, ... }:
{
services.nginx.virtualHosts."nixcache.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
# serverAliases = [ "nixcache" ];
locations."/".extraConfig = ''
proxy_pass http://localhost:${toString config.services.nix-serve.port};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."nixcache" = [ "native" ];
sane.services.nixserve.enable = true;
sane.services.nixserve.sopsFile = ../../../secrets/servo.yaml;
}

View File

@@ -1,64 +0,0 @@
# example configs:
# - <https://github.com/kittywitch/nixfiles/blob/main/services/prosody.nix>
# create users with:
# - `sudo -u prosody prosodyctl adduser colin@uninsane.org`
{ lib, ... }:
# XXX disabled: doesn't send messages to nixnet.social (only receives them).
# nixnet runs ejabberd, so revisiting that.
lib.mkIf false
{
sane.impermanence.dirs = [
{ user = "prosody"; group = "prosody"; directory = "/var/lib/prosody"; }
];
networking.firewall.allowedTCPPorts = [
5222 # XMPP client -> server
5269 # XMPP server -> server
5280 # bosh
5281 # Prosody HTTPS port (necessary?)
];
# provide access to certs
users.users.prosody.extraGroups = [ "nginx" ];
security.acme.certs."uninsane.org".extraDomainNames = [
"conference.xmpp.uninsane.org"
"upload.xmpp.uninsane.org"
];
services.prosody = {
enable = true;
admins = [ "colin@uninsane.org" ];
# allowRegistration = false;
# extraConfig = ''
# s2s_require_encryption = true
# c2s_require_encryption = true
# '';
extraModules = [ "private" "vcard" "privacy" "compression" "component" "muc" "pep" "adhoc" "lastactivity" "admin_adhoc" "blocklist"];
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
ssl.key = "/var/lib/acme/uninsane.org/key.pem";
muc = [
{
domain = "conference.xmpp.uninsane.org";
}
];
uploadHttp.domain = "upload.xmpp.uninsane.org";
virtualHosts = {
localhost = {
domain = "localhost";
enabled = true;
};
"xmpp.uninsane.org" = {
domain = "uninsane.org";
enabled = true;
ssl.cert = "/var/lib/acme/uninsane.org/fullchain.pem";
ssl.key = "/var/lib/acme/uninsane.org/key.pem";
};
};
};
}

View File

@@ -1,66 +0,0 @@
{ config, pkgs, ... }:
{
sane.services.trust-dns.enable = true;
sane.services.trust-dns.listenAddrsIPv4 = [
# specify each address explicitly, instead of using "*".
# this ensures responses are sent from the address at which the request was received.
"192.168.0.5"
"10.0.1.5"
];
sane.services.trust-dns.zones."uninsane.org".TTL = 900;
# SOA record structure: <https://en.wikipedia.org/wiki/SOA_record#Structure>
# SOA MNAME RNAME (... rest)
# MNAME = Master name server for this zone. this is where update requests should be sent.
# RNAME = admin contact (encoded email address)
# Serial = YYYYMMDDNN, where N is incremented every time this file changes, to trigger secondary NS to re-fetch it.
# Refresh = how frequently secondary NS should query master
# Retry = how long secondary NS should wait until re-querying master after a failure (must be < Refresh)
# Expire = how long secondary NS should continue to reply to queries after master fails (> Refresh + Retry)
sane.services.trust-dns.zones."uninsane.org".inet = {
SOA."@" = [''
ns1.uninsane.org. admin-dns.uninsane.org. (
2022122101 ; Serial
4h ; Refresh
30m ; Retry
7d ; Expire
5m) ; Negative response TTL
''];
TXT."rev" = [ "2022122101" ];
# XXX NS records must also not be CNAME
# it's best that we keep this identical, or a superset of, what org. lists as our NS.
# so, org. can specify ns2/ns3 as being to the VPN, with no mention of ns1. we provide ns1 here.
A."ns1" = [ "%NATIVE%" ];
A."ns2" = [ "185.157.162.178" ];
A."ns3" = [ "185.157.162.178" ];
A."ovpns" = [ "185.157.162.178" ];
A."native" = [ "%NATIVE%" ];
A."@" = [ "%NATIVE%" ];
NS."@" = [
"ns1.uninsane.org."
"ns2.uninsane.org."
"ns3.uninsane.org."
];
};
sane.services.trust-dns.zones."uninsane.org".file =
"/var/lib/trust-dns/uninsane.org.zone";
systemd.services.trust-dns.preStart = let
sed = "${pkgs.gnused}/bin/sed";
zone-dir = "/var/lib/trust-dns";
zone-out = "${zone-dir}/uninsane.org.zone";
zone-template = pkgs.writeText "uninsane.org.zone.in" config.sane.services.trust-dns.generatedZones."uninsane.org";
in ''
# make WAN records available to trust-dns
mkdir -p ${zone-dir}
ip=$(cat '${config.sane.services.dyn-dns.ipPath}')
${sed} s/%NATIVE%/$ip/ ${zone-template} > ${zone-out}
'';
sane.services.dyn-dns.restartOnChange = [ "trust-dns.service" ];
}

View File

@@ -1,33 +0,0 @@
# docs: <https://nixos.wiki/wiki/MediaWiki>
{ config, lib, ... }:
# XXX: working to host wikipedia with kiwix instead of mediawiki
# mediawiki does more than i need and isn't obviously superior in any way
# except that the dumps are more frequent/up-to-date.
lib.mkIf false
{
sops.secrets."mediawiki_pw" = {
owner = config.users.users.mediawiki.name;
sopsFile = ../../../secrets/servo.yaml;
};
users.users.mediawiki.uid = config.sane.allocations.mediawiki-uid;
services.mediawiki.enable = true;
services.mediawiki.name = "Uninsane Wiki";
services.mediawiki.passwordFile = config.sops.secrets.mediawiki_pw.path;
services.mediawiki.extraConfig = ''
# Disable anonymous editing
$wgGroupPermissions['*']['edit'] = false;
'';
services.mediawiki.virtualHost.listen = [
{
ip = "127.0.0.1";
port = 8013;
ssl = false;
}
];
services.mediawiki.virtualHost.hostName = "w.uninsane.org";
services.mediawiki.virtualHost.adminAddr = "admin+mediawiki@uninsane.org";
# services.mediawiki.extensions = TODO: wikipedia sync extension?
}

View File

@@ -4,8 +4,6 @@
./fs.nix
];
# sane.packages.enableDevPkgs = true;
sane.gui.sway.enable = true;
sane.services.duplicity.enable = true;
sane.services.nixserve.enable = true;
@@ -25,9 +23,6 @@
neededForUsers = true;
};
# don't enable wifi by default: it messes with connectivity.
systemd.services.iwd.enable = false;
# default config: https://man.archlinux.org/man/snapper-configs.5
# defaults to something like:
# - hourly snapshots

View File

@@ -1,7 +1,16 @@
{ ... }:
{
sane.impermanence.root-on-tmpfs = true;
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=755"
"size=1G"
"defaults"
];
};
# we need a /tmp for building large nix things.
# a cross-compiled kernel, particularly, will easily use 30+GB of tmp
fileSystems."/tmp" = {

11
machines/instantiate.nix Normal file
View File

@@ -0,0 +1,11 @@
# trampoline from flake.nix into the specific machine definition, while doing a tiny bit of common setup
hostName: { ... }: {
imports = [
./${hostName}
];
networking.hostName = hostName;
nixpkgs.config.allowUnfree = true;
}

View File

@@ -4,8 +4,6 @@
./fs.nix
];
# sane.packages.enableDevPkgs = true;
# sane.users.guest.enable = true;
sane.gui.sway.enable = true;
sane.impermanence.enable = true;

View File

@@ -1,7 +1,16 @@
{ ... }:
{
sane.impermanence.root-on-tmpfs = true;
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=755"
"size=1G"
"defaults"
];
};
# we need a /tmp of default size (half RAM) for building large nix things
fileSystems."/tmp" = {
device = "none";

View File

@@ -25,12 +25,11 @@
# usability compromises
sane.impermanence.home-dirs = [
config.sane.web-browser.dotDir
".config/pulse" # persist pulseaudio volume
".librewolf"
];
# sane.packages.enableGuiPkgs = false; # XXX faster builds/imaging for debugging
sane.packages.extraUserPkgs = [
# sane.home-packages.enableGuiPkgs = false; # XXX faster builds/imaging for debugging
sane.home-manager.extraPackages = [
pkgs.plasma5Packages.konsole # terminal
];
@@ -40,9 +39,7 @@
boot.loader.efi.canTouchEfiVariables = false;
# /boot space is at a premium. default was 20.
# even 10 can be too much
# TODO: compress moby kernels!
boot.loader.generic-extlinux-compatible.configurationLimit = 8;
boot.loader.generic-extlinux-compatible.configurationLimit = 10;
# mobile.bootloader.enable = false;
# mobile.boot.stage-1.enable = false;
# boot.initrd.systemd.enable = false;

View File

@@ -1,7 +1,17 @@
{ ... }:
{
sane.impermanence.root-on-tmpfs = true;
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=755"
"size=1G"
"defaults"
];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/1f1271f8-53ce-4081-8a29-60a4a6b5d6f9";
fsType = "btrfs";

View File

@@ -3,22 +3,27 @@
{
imports = [
./fs.nix
./hardware.nix
./net.nix
./users.nix
./services
];
sane.packages.extraUserPkgs = [
sane.home-manager.extraPackages = [
# for administering services
pkgs.matrix-synapse
pkgs.freshrss
pkgs.goaccess
];
sane.impermanence.enable = true;
sane.services.dyn-dns.enable = true;
# sane.services.duplicity.enable = true; # TODO: re-enable after HW upgrade
sane.services.duplicity.enable = true;
sane.services.nixserve.enable = true;
# TODO: look into the EFI stuff
boot.loader.grub.enable = false;
boot.loader.generic-extlinux-compatible.enable = true;
boot.loader.efi.canTouchEfiVariables = false;
sane.image.extraBootFiles = [ pkgs.bootpart-uefi-x86_64 ];
sane.image.extraBootFiles = [ pkgs.bootpart-u-boot-rpi-aarch64 ];
sops.secrets.duplicity_passphrase = {
sopsFile = ../../secrets/servo.yaml;
@@ -27,7 +32,7 @@
# both transmission and ipfs try to set different net defaults.
# we just use the most aggressive of the two here:
boot.kernel.sysctl = {
"net.core.rmem_max" = 4194304; # 4MB
"net.core.rmem_max" = "4194304"; # 4MB
};
# This value determines the NixOS release from which the default
@@ -36,6 +41,6 @@
# this value at the release version of the first install of this system.
# Before changing this value read the documentation for this option
# (e.g. man configuration.nix or on https://nixos.org/nixos/options.html).
system.stateVersion = "21.11";
system.stateVersion = "21.11"; # Did you read the comment?
}

69
machines/servo/fs.nix Normal file
View File

@@ -0,0 +1,69 @@
{ ... }:
{
# root is a tmpfs so that we have an ephemeral system ("impermanence" handles the state)
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=755"
"size=1G"
"defaults"
];
};
# we need a /tmp for building large nix things
fileSystems."/tmp" = {
device = "none";
fsType = "tmpfs";
options = [
"size=40G"
"mode=777"
"defaults"
];
};
fileSystems."/nix" = {
device = "/dev/disk/by-uuid/aa272cff-0fcc-498e-a4cb-0d95fb60631b";
fsType = "btrfs";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/31D3-40CB";
fsType = "vfat";
};
# fileSystems."/var/lib/pleroma" = {
# device = "/opt/pleroma";
# options = [ "bind" ];
# };
# in-memory compressed RAM (seems to be dynamically sized)
zramSwap = {
enable = true;
};
# btrfs doesn't easily support swapfiles
# swapDevices = [
# { device = "/nix/persist/swapfile"; size = 4096; }
# ];
# this can be a partition. create with:
# fdisk <dev>
# n
# <default partno>
# <start>
# <end>
# t
# <partno>
# 19 # set part type to Linux swap
# w # write changes
# mkswap -L swap <part>
swapDevices = [
{
label = "swap";
# TODO: randomEncryption.enable = true;
}
];
}

View File

@@ -0,0 +1,75 @@
# this file originates from nixos-generate-config
# but has been heavily modified
{ pkgs, ... }:
{
# i changed this becuse linux 5.10 didn't have rpi-400 device tree blob.
# nixos-22.05 linux 5.15 DOES have these now.
# it should be possible to remove this if desired, but i'm not sure how the rpi-specific kernel differs.
# see: https://github.com/raspberrypi/linux
boot.kernelPackages = pkgs.linuxPackages_rpi4;
# raspberryPi boot loader creates extlinux.conf.
# otherwise, enable the generic-extlinux-compatible loader below.
# note: THESE ARE MUTUALLY EXCLUSIVE. generic-extlinux-compatible causes uboot to not be built
boot.initrd.availableKernelModules = [
"bcm2711_thermal"
"bcm_phy_lib"
"brcmfmac"
"brcmutil"
"broadcom"
"clk_raspberrypi"
"drm" # Direct Render Manager
"enclosure" # SCSI ?
"fuse"
"mdio_bcm_unimac"
"pcie_brcmstb"
"raspberrypi_cpufreq"
"raspberrypi_hwmon"
"ses" # SCSI Enclosure Services
"uas" # USB attached storage
"uio" # userspace IO
"uio_pdrv_genirq"
"xhci_pci"
"xhci_pci_renesas"
];
# boot.initrd.compressor = "gzip"; # defaults to zstd
# ondemand power scaling keeps the cpu at low frequency when idle, and sets to max frequency
# when load is detected. (v.s. the "performance" default, which always uses the max frequency)
powerManagement.cpuFreqGovernor = "ondemand";
# XXX colin: this allows one to `systemctl halt` and then not remove power until the HDD has spun down.
# however, it doesn't work with reboot because systemd will spin the drive up again to read its reboot bin.
# a better solution would be to put the drive behind a powered USB hub (or get a SSD).
# systemd.services.diskguard = {
# description = "Safely power off spinning media";
# before = [ "shutdown.target" ];
# wantedBy = [ "sysinit.target" ];
# # old (creates dep loop, but works)
# # before = [ "systemd-remount-fs.service" "shutdown.target" ];
# # wantedBy = [ "systemd-remount-fs.service" ];
# serviceConfig = {
# Type = "oneshot";
# RemainAfterExit = true;
# ExecStart = "${pkgs.coreutils}/bin/true";
# ExecStop = with pkgs; writeScript "diskguard" ''
# #!${bash}/bin/bash
# if ${procps}/bin/pgrep nixos-rebuild ;
# then
# exit 0 # don't halt drives unless we're actually shutting down. maybe better way to do this (check script args?)
# fi
# # ${coreutils}/bin/sync
# # ${util-linux}/bin/mount -o remount,ro /nix/store
# # ${util-linux}/bin/mount -o remount,ro /
# # -S 1 retracts the spindle after 5 seconds of idle
# # -B 1 spins down the drive after <vendor specific duration>
# ${hdparm}/sbin/hdparm -S 1 -B 1 /dev/sda
# # TODO: monitor smartmonctl until disk is idle? or try hdparm -Y
# # ${coreutils}/bin/sleep 20
# # exec ${util-linux}/bin/umount --all -t ext4,vfat,ext2
# '';
# };
# };
}

139
machines/servo/net.nix Normal file
View File

@@ -0,0 +1,139 @@
{ config, pkgs, ... }:
{
networking.domain = "uninsane.org";
# The global useDHCP flag is deprecated, therefore explicitly set to false here.
# Per-interface useDHCP will be mandatory in the future, so this generated config
# replicates the default behaviour.
networking.useDHCP = false;
networking.interfaces.eth0.useDHCP = true;
# XXX colin: probably don't need this. wlan0 won't be populated unless i touch a value in networking.interfaces.wlan0
networking.wireless.enable = false;
# networking.firewall.enable = false;
networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [
25 # SMTP
80 # HTTP
143 # IMAP
443 # HTTPS
465 # SMTPS
587 # SMTPS/submission
993 # IMAPS
4001 # IPFS
];
networking.firewall.allowedUDPPorts = [
1900 7359 # DLNA: https://jellyfin.org/docs/general/networking/index.html
4001 # IPFS
];
# we need to use externally-visible nameservers in order for VPNs to be able to resolve hosts.
networking.nameservers = [
"1.1.1.1"
"9.9.9.9"
];
# OVPN CONFIG (https://www.ovpn.com):
# DOCS: https://nixos.wiki/wiki/WireGuard
networking.wireguard.enable = true;
networking.wireguard.interfaces.wg0 = {
privateKeyFile = config.sops.secrets.wg_ovpns_privkey.path;
# wg is active only in this namespace.
# run e.g. ip netns exec ovpns <some command like ping/curl/etc, it'll go through wg>
# sudo ip netns exec ovpns ping www.google.com
# note: without the namespace, you'll need to add a specific route through eth0 for the peer (185.157.162.178/32)
interfaceNamespace = "ovpns";
preSetup = "${pkgs.iproute2}/bin/ip netns add ovpns || true";
postShutdown = "${pkgs.iproute2}/bin/ip netns delete ovpns";
ips = [
"185.157.162.178/32"
];
peers = [
{
publicKey = "SkkEZDCBde22KTs/Hc7FWvDBfdOCQA4YtBEuC3n5KGs=";
endpoint = "vpn36.prd.amsterdam.ovpn.com:9930";
allowedIPs = [ "0.0.0.0/0" ];
# nixOS says this is important for keeping NATs active
persistentKeepalive = 25;
}
];
};
systemd.services.wg0veth = {
description = "veth pair to allow communication between host and wg0 netns";
after = [ "wireguard-wg0.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = with pkgs; writeScript "wg0veth-start" ''
#!${bash}/bin/bash
# create veth pair
${iproute2}/bin/ip link add ovpns-veth-a type veth peer name ovpns-veth-b
${iproute2}/bin/ip addr add 10.0.1.5/24 dev ovpns-veth-a
${iproute2}/bin/ip link set ovpns-veth-a up
# mv veth-b into the ovpns namespace
${iproute2}/bin/ip link set ovpns-veth-b netns ovpns
${iproute2}/bin/ip -n ovpns addr add 10.0.1.6/24 dev ovpns-veth-b
${iproute2}/bin/ip -n ovpns link set ovpns-veth-b up
# forward HTTP traffic, which we need for letsencrypt to work
${iproute2}/bin/ip netns exec ovpns ${socat}/bin/socat TCP4-LISTEN:80,reuseaddr,fork,su=nobody TCP4:10.0.1.5:80 &
'';
ExecStop = with pkgs; writeScript "wg0veth-stop" ''
#!${bash}/bin/bash
${iproute2}/bin/ip -n wg0 link del ovpns-veth-b
${iproute2}/bin/ip link del ovpns-veth-a
'';
};
};
sops.secrets."wg_ovpns_privkey" = {
sopsFile = ../../secrets/servo.yaml;
};
# HURRICANE ELECTRIC CONFIG:
# networking.sits = {
# hurricane = {
# remote = "216.218.226.238";
# local = "192.168.0.5";
# # local = "10.0.0.5";
# # remote = "10.0.0.1";
# # local = "10.0.0.22";
# dev = "eth0";
# ttl = 255;
# };
# };
# networking.interfaces."hurricane".ipv6 = {
# addresses = [
# # mx.uninsane.org (publically routed /64)
# {
# address = "2001:470:b:465::1";
# prefixLength = 128;
# }
# # client addr
# # {
# # address = "2001:470:a:466::2";
# # prefixLength = 64;
# # }
# ];
# routes = [
# {
# address = "::";
# prefixLength = 0;
# # via = "2001:470:a:466::1";
# }
# ];
# };
# # after configuration, we want the hurricane device to look like this:
# # hurricane: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1480
# # inet6 2001:470:a:450::2 prefixlen 64 scopeid 0x0<global>
# # inet6 fe80::c0a8:16 prefixlen 64 scopeid 0x20<link>
# # sit txqueuelen 1000 (IPv6-in-IPv4)
# # test with:
# # curl --interface hurricane http://[2607:f8b0:400a:80b::2004]
# # ping 2607:f8b0:400a:80b::2004
}

View File

@@ -1,7 +1,5 @@
{ config, lib, pkgs, ... }:
{ config, pkgs, ... }:
# we use manual DDNS now
lib.mkIf false
{
systemd.services.ddns-he = {
description = "update dynamic DNS entries for HurricaneElectric";

View File

@@ -1,26 +1,19 @@
{ ... }:
{
imports = [
./ddns-afraid.nix
./ddns-he.nix
./ejabberd.nix
./freshrss.nix
./gitea.nix
./goaccess.nix
./ipfs.nix
./jackett.nix
./jellyfin.nix
./kiwix-serve.nix
./matrix
./navidrome.nix
./nixserve.nix
./nginx.nix
./pleroma.nix
./postfix.nix
./postgres.nix
./prosody.nix
./transmission.nix
./trust-dns.nix
./wikipedia.nix
];
}

View File

@@ -16,7 +16,7 @@
owner = config.users.users.freshrss.name;
mode = "400";
};
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
{ user = "freshrss"; group = "freshrss"; directory = "/var/lib/freshrss"; }
];
@@ -30,7 +30,7 @@
systemd.services.freshrss-import-feeds =
let
fresh = config.systemd.services.freshrss-config;
feeds = import ../../../modules/home-manager/feeds.nix { inherit lib; };
feeds = import ../../../modules/universal/home-manager/feeds.nix { inherit lib; };
opml = pkgs.writeText "sane-freshrss.opml" (feeds.feedsToOpml feeds.all);
in {
inherit (fresh) wantedBy environment;
@@ -45,17 +45,4 @@
${pkgs.freshrss}/cli/import-for-user.php --user admin --filename ${opml}
'';
};
# the default ("*:0/5") is to run every 5 minutes.
# `systemctl list-timers` to show
systemd.services.freshrss-updater.startAt = lib.mkForce "*:3/30";
services.nginx.virtualHosts."rss.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
# the routing is handled by services.freshrss.virtualHost
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."rss" = [ "native" ];
}

View File

@@ -1,7 +1,7 @@
{ config, pkgs, lib, ... }:
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "git"; group = "gitea"; directory = "/var/lib/gitea"; }
];
@@ -72,18 +72,4 @@
"/var/lib/gitea"
];
};
# hosted git (web view and for `git <cmd>` use
# TODO: enable publog?
services.nginx.virtualHosts."git.uninsane.org" = {
forceSSL = true; # gitea complains if served over a different protocol than its config file says
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."git" = [ "native" ];
}

View File

@@ -14,7 +14,6 @@
-f /var/log/nginx/public.log \
--log-format=VCOMBINED \
--real-time-html \
--html-refresh=30 \
--no-query-string \
--anonymize-ip \
--ignore-panel=HOSTS \
@@ -25,7 +24,6 @@
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
Type = "simple";
Restart = "on-failure";
RestartSec = "10s";
# hardening
WorkingDirectory = "/tmp";
@@ -43,26 +41,4 @@
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
};
# server statistics
services.nginx.virtualHosts."sink.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
root = "/var/lib/uninsane/sink";
locations."/ws" = {
proxyPass = "http://127.0.0.1:7890";
# XXX not sure how much of this is necessary
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 7d;
'';
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."sink" = [ "native" ];
}

View File

@@ -6,50 +6,26 @@
# - number of open peer connections:
# - sudo -u ipfs -g ipfs ipfs -c /var/lib/ipfs/ swarm peers | wc -l
{ lib, ... }:
lib.mkIf false # i don't actively use ipfs anymore
{ ... }:
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "261"; group = "261"; directory = "/var/lib/ipfs"; }
];
networking.firewall.allowedTCPPorts = [ 4001 ];
networking.firewall.allowedUDPPorts = [ 4001 ];
services.nginx.virtualHosts."ipfs.uninsane.org" = {
# don't default to ssl upgrades, since this may be dnslink'd from a different domain.
# ideally we'd disable ssl entirely, but some places assume it?
addSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:8080";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Ipfs-Gateway-Prefix "";
'';
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."ipfs" = [ "native" ];
# services.ipfs.enable = true;
services.kubo.localDiscovery = true;
services.kubo.settings = {
services.kubo.swarmAddress = [
# "/dns4/ipfs.uninsane.org/tcp/4001"
# "/ip4/0.0.0.0/tcp/4001"
"/dns4/ipfs.uninsane.org/udp/4001/quic"
"/ip4/0.0.0.0/udp/4001/quic"
];
services.kubo.extraConfig = {
Addresses = {
Announce = [
# "/dns4/ipfs.uninsane.org/tcp/4001"
"/dns4/ipfs.uninsane.org/udp/4001/quic"
];
Swarm = [
# "/dns4/ipfs.uninsane.org/tcp/4001"
# "/ip4/0.0.0.0/tcp/4001"
"/dns4/ipfs.uninsane.org/udp/4001/quic"
"/ip4/0.0.0.0/udp/4001/quic"
];
};
Gateway = {
# the gateway can only be used to serve content already replicated on this host

View File

@@ -1,32 +1,18 @@
{ ... }:
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
# TODO: mode? we only need this to save Indexer creds ==> migrate to config?
{ user = "root"; group = "root"; directory = "/var/lib/jackett"; }
];
services.jackett.enable = true;
systemd.services.jackett.after = [ "wireguard-wg0.service" ];
systemd.services.jackett.partOf = [ "wireguard-wg0.service" ];
systemd.services.jackett.after = ["wg0veth.service"];
systemd.services.jackett.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
# patch jackett to listen on the public interfaces
# ExecStart = lib.mkForce "${pkgs.jackett}/bin/Jackett --NoUpdates --DataFolder /var/lib/jackett/.config/Jackett --ListenPublic";
};
# jackett torrent search
services.nginx.virtualHosts."jackett.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9117";
proxyPass = "http://10.0.1.6:9117";
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."jackett" = [ "native" ];
}

View File

@@ -0,0 +1,14 @@
{ config, ... }:
{
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "jellyfin"; group = "jellyfin"; directory = "/var/lib/jellyfin"; }
];
# users.users.jellyfin.uid = config.sane.allocations.jellyfin-uid;
# users.groups.jellyfin.gid = config.sane.allocations.jellyfin-gid;
# TODO: re-enable after migrating media dir to /var/lib/uninsane/media
# else it's too spammy
# services.jellyfin.enable = true;
}

View File

@@ -1,6 +1,6 @@
# docs: https://nixos.wiki/wiki/Matrix
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-synapse
{ config, lib, pkgs, ... }:
{ config, lib, ... }:
{
imports = [
@@ -8,7 +8,7 @@
# ./irc.nix
];
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/matrix-synapse"; }
];
services.matrix-synapse.enable = true;
@@ -77,55 +77,6 @@
# create a token with limited uses:
# curl -d '{ "uses_allowed": 1 }' --header "Authorization: Bearer <my_token>" localhost:8008/_synapse/admin/v1/registration_tokens/new
# matrix chat server
# TODO: was `publog`
services.nginx.virtualHosts."matrix.uninsane.org" = {
addSSL = true;
enableACME = true;
# inherit kTLS;
# TODO colin: replace this with something helpful to the viewer
# locations."/".extraConfig = ''
# return 404;
# '';
locations."/" = {
proxyPass = "http://127.0.0.1:8008";
};
# redirect browsers to the web client.
# i don't think native matrix clients ever fetch the root.
# ideally this would be put behind some user-agent test though.
locations."= /" = {
return = "301 https://web.matrix.uninsane.org";
};
# locations."/_matrix" = {
# proxyPass = "http://127.0.0.1:8008";
# };
};
# matrix web client
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-element-web
services.nginx.virtualHosts."web.matrix.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
root = pkgs.element-web.override {
conf = {
default_server_config."m.homeserver" = {
"base_url" = "https://matrix.uninsane.org";
"server_name" = "uninsane.org";
};
};
};
};
sane.services.trust-dns.zones."uninsane.org".inet = {
CNAME."matrix" = [ "native" ];
CNAME."web.matrix" = [ "native" ];
};
sops.secrets.matrix_synapse_secrets = {
sopsFile = ../../../../secrets/servo.yaml;

View File

@@ -1,6 +1,6 @@
{ lib, ... }:
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
{ user = "matrix-synapse"; group = "matrix-synapse"; directory = "/var/lib/mx-puppet-discord"; }
];

View File

@@ -1,7 +1,7 @@
{ config, lib, ... }:
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
# TODO: mode?
# user and group are both "matrix-appservice-irc"
{ user = "993"; group = "992"; directory = "/var/lib/matrix-appservice-irc"; }

View File

@@ -1,7 +1,7 @@
{ ... }:
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
{ user = "navidrome"; group = "navidrome"; directory = "/var/lib/private/navidrome"; }
];
services.navidrome.enable = true;
@@ -14,13 +14,4 @@
AutoImportPlaylists = false;
ScanSchedule = "@every 1h";
};
services.nginx.virtualHosts."music.uninsane.org" = {
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/".proxyPass = "http://127.0.0.1:4533";
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."music" = [ "native" ];
}

View File

@@ -0,0 +1,320 @@
# docs: https://nixos.wiki/wiki/Nginx
{ config, pkgs, ... }:
let
# make the logs for this host "public" so that they show up in e.g. metrics
publog = vhost: vhost // {
extraConfig = (vhost.extraConfig or "") + ''
access_log /var/log/nginx/public.log vcombined;
'';
};
in
{
services.nginx.enable = true;
# this is the standard `combined` log format, with the addition of $host
# so that we have the virtualHost in the log.
# KEEP IN SYNC WITH GOACCESS
# goaccess calls this VCOMBINED:
# - <https://gist.github.com/jyap808/10570005>
services.nginx.commonHttpConfig = ''
log_format vcombined '$host:$server_port $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referrer" "$http_user_agent"';
access_log /var/log/nginx/private.log vcombined;
'';
# web blog/personal site
services.nginx.virtualHosts."uninsane.org" = publog {
root = "${pkgs.uninsane-dot-org}/share/uninsane-dot-org";
# a lot of places hardcode https://uninsane.org,
# and then when we mix http + non-https, we get CORS violations
# and things don't look right. so force SSL.
forceSSL = true;
enableACME = true;
# uninsane.org/share/foo => /var/lib/uninsane/root/share/foo.
# yes, nginx does not strip the prefix when evaluating against the root.
locations."/share".root = "/var/lib/uninsane/root";
# allow matrix users to discover that @user:uninsane.org is reachable via matrix.uninsane.org
locations."= /.well-known/matrix/server".extraConfig =
let
# use 443 instead of the default 8448 port to unite
# the client-server and server-server port for simplicity
server = { "m.server" = "matrix.uninsane.org:443"; };
in ''
add_header Content-Type application/json;
return 200 '${builtins.toJSON server}';
'';
locations."= /.well-known/matrix/client".extraConfig =
let
client = {
"m.homeserver" = { "base_url" = "https://matrix.uninsane.org"; };
"m.identity_server" = { "base_url" = "https://vector.im"; };
};
# ACAO required to allow element-web on any URL to request this json file
in ''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '${builtins.toJSON client}';
'';
# static URLs might not be aware of .well-known (e.g. registration confirmation URLs),
# so hack around that.
locations."/_matrix" = {
proxyPass = "http://127.0.0.1:8008";
};
locations."/_synapse" = {
proxyPass = "http://127.0.0.1:8008";
};
# allow ActivityPub clients to discover how to reach @user@uninsane.org
# TODO: waiting on https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3361/
# locations."/.well-known/nodeinfo" = {
# proxyPass = "http://127.0.0.1:4000";
# extraConfig = pleromaExtraConfig;
# };
};
# server statistics
services.nginx.virtualHosts."sink.uninsane.org" = {
addSSL = true;
enableACME = true;
root = "/var/lib/uninsane/sink";
locations."/ws" = {
proxyPass = "http://127.0.0.1:7890";
# XXX not sure how much of this is necessary
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_buffering off;
proxy_read_timeout 7d;
'';
};
};
# Pleroma server and web interface
services.nginx.virtualHosts."fed.uninsane.org" = publog {
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:4000";
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
extraConfig = ''
# XXX colin: this block is in the nixos examples: i don't understand all of it
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
if ($request_method = OPTIONS) {
return 204;
}
add_header X-XSS-Protection "1; mode=block";
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
add_header X-Download-Options noopen;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# proxy_set_header Host $http_host;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# colin: added this due to Pleroma complaining in its logs
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 16m;
'';
};
};
# transmission web client
services.nginx.virtualHosts."bt.uninsane.org" = {
# basicAuth is literally cleartext user/pw, so FORCE this to happen over SSL
forceSSL = true;
enableACME = true;
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9091";
proxyPass = "http://10.0.1.6:9091";
};
};
# jackett torrent search
services.nginx.virtualHosts."jackett.uninsane.org" = {
forceSSL = true;
enableACME = true;
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9117";
proxyPass = "http://10.0.1.6:9117";
};
};
# matrix chat server
services.nginx.virtualHosts."matrix.uninsane.org" = publog {
addSSL = true;
enableACME = true;
# TODO colin: replace this with something helpful to the viewer
# locations."/".extraConfig = ''
# return 404;
# '';
locations."/" = {
proxyPass = "http://127.0.0.1:8008";
};
# redirect browsers to the web client.
# i don't think native matrix clients ever fetch the root.
# ideally this would be put behind some user-agent test though.
locations."= /" = {
return = "301 https://web.matrix.uninsane.org";
};
# locations."/_matrix" = {
# proxyPass = "http://127.0.0.1:8008";
# };
};
# matrix web client
# docs: https://nixos.org/manual/nixos/stable/index.html#module-services-matrix-element-web
services.nginx.virtualHosts."web.matrix.uninsane.org" = {
forceSSL = true;
enableACME = true;
root = pkgs.element-web.override {
conf = {
default_server_config."m.homeserver" = {
"base_url" = "https://matrix.uninsane.org";
"server_name" = "uninsane.org";
};
};
};
};
# hosted git (web view and for `git <cmd>` use
services.nginx.virtualHosts."git.uninsane.org" = publog {
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
};
};
# Jellyfin multimedia server
# this is mostly taken from the official jellfin.org docs
services.nginx.virtualHosts."jelly.uninsane.org" = {
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
# Disable buffering when the nginx proxy gets very resource heavy upon streaming
proxy_buffering off;
'';
};
# locations."/web/" = {
# proxyPass = "http://127.0.0.1:8096/web/index.html";
# extraConfig = ''
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_set_header X-Forwarded-Protocol $scheme;
# proxy_set_header X-Forwarded-Host $http_host;
# '';
# };
locations."/socket" = {
proxyPass = "http://127.0.0.1:8096";
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-Host $http_host;
'';
};
};
services.nginx.virtualHosts."music.uninsane.org" = {
forceSSL = true;
enableACME = true;
locations."/".proxyPass = "http://127.0.0.1:4533";
};
services.nginx.virtualHosts."rss.uninsane.org" = {
addSSL = true;
enableACME = true;
# the routing is handled by freshrss.nix
};
services.nginx.virtualHosts."ipfs.uninsane.org" = {
# don't default to ssl upgrades, since this may be dnslink'd from a different domain.
# ideally we'd disable ssl entirely, but some places assume it?
addSSL = true;
enableACME = true;
default = true;
locations."/" = {
proxyPass = "http://127.0.0.1:8080";
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Ipfs-Gateway-Prefix "";
'';
};
};
# exists only to manage certs for dovecot
services.nginx.virtualHosts."imap.uninsane.org" = {
forceSSL = true;
enableACME = true;
};
# exists only to manage certs for Postfix
services.nginx.virtualHosts."mx.uninsane.org" = {
forceSSL = true;
enableACME = true;
};
services.nginx.virtualHosts."nixcache.uninsane.org" = {
addSSL = true;
enableACME = true;
# serverAliases = [ "nixcache" ];
locations."/".extraConfig = ''
proxy_pass http://localhost:${toString config.services.nix-serve.port};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
'';
};
security.acme.acceptTerms = true;
security.acme.defaults.email = "admin.acme@uninsane.org";
users.users.acme.uid = config.sane.allocations.acme-uid;
users.groups.acme.gid = config.sane.allocations.acme-gid;
sane.impermanence.service-dirs = [
# TODO: mode?
{ user = "acme"; group = "acme"; directory = "/var/lib/acme"; }
# TODO: this is overly broad; only need media and share directories to be persisted
{ user = "colin"; group = "users"; directory = "/var/lib/uninsane"; }
];
}

View File

@@ -1,12 +1,10 @@
# docs:
# - https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/pleroma.nix
# - https://docs.pleroma.social/backend/configuration/cheatsheet/
# docs: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/pleroma.nix
#
# to run it in a oci-container: https://github.com/barrucadu/nixfiles/blob/master/services/pleroma.nix
{ config, pkgs, ... }:
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "pleroma"; group = "pleroma"; directory = "/var/lib/pleroma"; }
];
@@ -50,19 +48,16 @@
redirect_on_failure: true
#base_url: "https://cache.pleroma.social"
# see for reference:
# - `force_custom_plan`: <https://docs.pleroma.social/backend/configuration/postgresql/#disable-generic-query-plans>
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
username: "pleroma",
database: "pleroma",
hostname: "localhost",
pool_size: 10,
prepare: :named,
parameters: [
plan_cache_mode: "force_custom_plan"
]
# XXX: prepare: :named is needed only for PG <= 12
# prepare: :named,
# password: "{secrets.pleroma.db_password}",
# Configure web push notifications
@@ -115,9 +110,9 @@
systemd.services.pleroma.path = [
# something inside pleroma invokes `sh` w/o specifying it by path, so this is needed to allow pleroma to start
pkgs.bash
pkgs.bash
# used by Pleroma to strip geo tags from uploads
pkgs.exiftool
pkgs.exiftool
# i saw some errors when pleroma was shutting down about it not being able to find `awk`. probably not critical
pkgs.gawk
# needed for email operations like password reset
@@ -127,7 +122,6 @@
systemd.services.pleroma.serviceConfig = {
# postgres can be slow to service early requests, preventing pleroma from starting on the first try
Restart = "on-failure";
RestartSec = "10s";
};
# systemd.services.pleroma.serviceConfig = {
@@ -137,50 +131,6 @@
# CapabilityBoundingSet = lib.mkForce "~";
# };
# Pleroma server and web interface
# TODO: enable publog?
services.nginx.virtualHosts."fed.uninsane.org" = {
forceSSL = true; # pleroma redirects to https anyway
enableACME = true;
# inherit kTLS;
locations."/" = {
proxyPass = "http://127.0.0.1:4000";
# documented: https://git.pleroma.social/pleroma/pleroma/-/blob/develop/installation/pleroma.nginx
extraConfig = ''
# XXX colin: this block is in the nixos examples: i don't understand all of it
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'POST, PUT, DELETE, GET, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Idempotency-Key' always;
add_header 'Access-Control-Expose-Headers' 'Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id' always;
if ($request_method = OPTIONS) {
return 204;
}
add_header X-XSS-Protection "1; mode=block";
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
add_header X-Download-Options noopen;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# proxy_set_header Host $http_host;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# colin: added this due to Pleroma complaining in its logs
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 16m;
'';
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."fed" = [ "native" ];
sops.secrets.pleroma_secrets = {
sopsFile = ../../../secrets/servo.yaml;
owner = config.users.users.pleroma.name;

View File

@@ -16,7 +16,7 @@ let
};
in
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
# TODO: mode? could be more granular
{ user = "opendkim"; group = "opendkim"; directory = "/var/lib/opendkim"; }
{ user = "root"; group = "root"; directory = "/var/lib/postfix"; }
@@ -25,61 +25,6 @@ in
# "/var/lib/dhparams" # https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/dhparams.nix
# "/var/lib/dovecot"
];
networking.firewall.allowedTCPPorts = [
25 # SMTP
143 # IMAP
465 # SMTPS
587 # SMTPS/submission
993 # IMAPS
];
# exists only to manage certs for dovecot
services.nginx.virtualHosts."imap.uninsane.org" = {
enableACME = true;
};
# exists only to manage certs for Postfix
services.nginx.virtualHosts."mx.uninsane.org" = {
enableACME = true;
};
sane.services.trust-dns.zones."uninsane.org".inet = {
MX."@" = [ "10 mx.uninsane.org." ];
# XXX: RFC's specify that the MX record CANNOT BE A CNAME
A."mx" = [ "185.157.162.178" ];
CNAME."imap" = [ "native" ];
# Sender Policy Framework:
# +mx => mail passes if it originated from the MX
# +a => mail passes if it originated from the A address of this domain
# +ip4:.. => mail passes if it originated from this IP
# -all => mail fails if none of these conditions were met
TXT."@" = [ "v=spf1 a mx -all" ];
# DKIM public key:
TXT."mx._domainkey" = [
"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkSyMufc2KrRx3j17e/LyB+3eYSBRuEFT8PUka8EDX04QzCwDPdkwgnj3GNDvnB5Ktb05Cf2SJ/S1OLqNsINxJRWtkVfZd/C339KNh9wrukMKRKNELL9HLUw0bczOI4gKKFqyrRE9qm+4csCMAR79Te9FCjGV/jVnrkLdPT0GtFwIDAQAB"
];
# DMARC fields <https://datatracker.ietf.org/doc/html/rfc7489>:
# p=none|quarantine|reject: what to do with failures
# sp = p but for subdomains
# rua = where to send aggregrate reports
# ruf = where to send individual failure reports
# fo=0|1|d|s controls WHEN to send failure reports
# (1=on bad alignment; d=on DKIM failure; s=on SPF failure);
# Additionally:
# adkim=r|s (is DKIM relaxed [default] or strict)
# aspf=r|s (is SPF relaxed [default] or strict)
# pct = sampling ratio for punishing failures (default 100 for 100%)
# rf = report format
# ri = report interval
TXT."_dmarc" = [
"v=DMARC1;p=quarantine;sp=reject;rua=mailto:admin+mail@uninsane.org;ruf=mailto:admin+mail@uninsane.org;fo=1:d:s"
];
};
services.postfix.enable = true;
services.postfix.hostname = "mx.uninsane.org";
services.postfix.origin = "uninsane.org";
@@ -110,8 +55,7 @@ in
services.postfix.enableSubmissions = true;
services.postfix.submissionsOptions = submissionOptions;
systemd.services.postfix.after = [ "wireguard-wg0.service" ];
systemd.services.postfix.partOf = [ "wireguard-wg0.service" ];
systemd.services.postfix.after = [ "wg0veth.service" ];
systemd.services.postfix.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
@@ -132,8 +76,7 @@ in
# keeping this the same as the hostname seems simplest
services.opendkim.selector = "mx";
systemd.services.opendkim.after = [ "wireguard-wg0.service" ];
systemd.services.opendkim.partOf = [ "wireguard-wg0.service" ];
systemd.services.opendkim.after = [ "wg0veth.service" ];
systemd.services.opendkim.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";

View File

@@ -1,7 +1,7 @@
{ ... }:
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
# TODO: mode?
{ user = "postgres"; group = "postgres"; directory = "/var/lib/postgresql"; }
];
@@ -17,11 +17,6 @@
# LC_CTYPE = "C";
# '';
# TODO: perf tuning
# - for recommended values see: <https://pgtune.leopard.in.ua/>
# - for official docs (sparse), see: <https://www.postgresql.org/docs/11/config-setting.html#CONFIG-SETTING-CONFIGURATION-FILE>
# services.postgresql.settings = { ... }
# daily backups to /var/backup
services.postgresqlBackup.enable = true;

View File

@@ -1,7 +1,7 @@
{ pkgs, ... }:
{ ... }:
{
sane.impermanence.dirs = [
sane.impermanence.service-dirs = [
# TODO: mode? we need this specifically for the stats tracking in .config/
{ user = "transmission"; group = "transmission"; directory = "/var/lib/transmission"; }
];
@@ -40,41 +40,11 @@
# transmission will by default not allow the world to read its files.
services.transmission.downloadDirPermissions = "775";
systemd.services.transmission.after = [ "wireguard-wg0.service" ];
systemd.services.transmission.partOf = [ "wireguard-wg0.service" ];
systemd.services.transmission.after = ["wg0veth.service"];
systemd.services.transmission.serviceConfig = {
# run this behind the OVPN static VPN
NetworkNamespacePath = "/run/netns/ovpns";
LogLevelMax = "warning";
};
# service to automatically backup torrents i add to transmission
systemd.services.backup-torrents = {
description = "archive torrents to storage not owned by transmission";
script = ''
${pkgs.rsync}/bin/rsync -arv /var/lib/transmission/.config/transmission-daemon/torrents/ /var/backup/torrents/
'';
};
systemd.timers.backup-torrents = {
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnStartupSec = "11min";
OnUnitActiveSec = "240min";
};
};
# transmission web client
services.nginx.virtualHosts."bt.uninsane.org" = {
# basicAuth is literally cleartext user/pw, so FORCE this to happen over SSL
forceSSL = true;
enableACME = true;
# inherit kTLS;
locations."/" = {
# proxyPass = "http://ovpns.uninsane.org:9091";
proxyPass = "http://10.0.1.6:9091";
};
};
sane.services.trust-dns.zones."uninsane.org".inet.CNAME."bt" = ["native"];
}

View File

@@ -2,13 +2,12 @@
{
imports = [
./allocations.nix
./gui
./home-manager
./packages.nix
./hardware
./image.nix
./impermanence
./impermanence.nix
./nixcache.nix
./services
./universal
];
}

View File

@@ -8,7 +8,6 @@ in
imports = [
./gnome.nix
./phosh.nix
./plasma.nix
./plasma-mobile.nix
./sway.nix
];
@@ -22,7 +21,7 @@ in
};
config = lib.mkIf cfg.enable {
sane.packages.enableGuiPkgs = lib.mkDefault true;
sane.home-packages.enableGuiPkgs = lib.mkDefault true;
# all GUIs use network manager?
users.users.nm-iodine.uid = config.sane.allocations.nm-iodine-uid;
};

View File

@@ -69,7 +69,7 @@ in
NIXOS_OZONE_WL = "1";
};
sane.packages.extraUserPkgs = with pkgs; [
sane.home-manager.extraPackages = with pkgs; [
phosh-mobile-settings
# TODO: see about removing this if the in-built gnome-settings bluetooth manager can work
@@ -89,16 +89,19 @@ in
services.xserver.displayManager.lightdm.extraSeatDefaults = ''
user-session = phosh
'';
# services.xserver.displayManager.lightdm.greeters.gtk.enable = false; # gtk greeter overrides our own?
# services.xserver.displayManager.lightdm.greeter = {
# enable = true;
# package = pkgs.lightdm-mobile-greeter.xgreeters;
# name = "lightdm-mobile-greeter";
# };
# # services.xserver.displayManager.lightdm.enable = true;
services.xserver.displayManager.lightdm.enable = true;
services.xserver.displayManager.lightdm.greeters.mobile.enable = true;
services.xserver.displayManager.lightdm.greeters.gtk.enable = false; # gtk greeter overrides our own?
services.xserver.displayManager.lightdm.greeter = {
enable = true;
package = pkgs.lightdm-mobile-greeter.xgreeters;
name = "lightdm-mobile-greeter";
};
# services.xserver.displayManager.lightdm.enable = true;
# # services.xserver.displayManager.lightdm.greeters.enso.enable = true; # tried (with reboot); got a mouse then died. next time was black
# # services.xserver.displayManager.lightdm.greeters.gtk.enable = true; # tried (with reboot); unusable without OSK
# # services.xserver.displayManager.lightdm.greeters.mini.enable = true; # tried (with reboot); unusable without OSK
# # services.xserver.displayManager.lightdm.greeters.pantheon.enable = true; # tried (no reboot); unusable without OSK
# services.xserver.displayManager.lightdm.greeters.slick.enable = true; # tried; unusable without OSK (a11y -> OSK doesn't work)
# # services.xserver.displayManager.lightdm.greeters.tiny.enable = true; # tried; block screen
systemd.services.phosh.wantedBy = lib.mkForce []; # disable auto-start
})

View File

@@ -1,28 +0,0 @@
{ lib, config, ... }:
with lib;
let
cfg = config.sane.gui.plasma;
in
{
options = {
sane.gui.plasma.enable = mkOption {
default = false;
type = types.bool;
};
};
config = mkIf cfg.enable {
sane.gui.enable = true;
# start plasma on boot
services.xserver.enable = true;
services.xserver.desktopManager.plasma5.enable = true;
services.xserver.displayManager.sddm.enable = true;
# gnome does networking stuff with networkmanager
networking.useDHCP = false;
networking.networkmanager.enable = true;
networking.wireless.enable = lib.mkForce false;
};
}

View File

@@ -1,11 +0,0 @@
https://search.nixos.org/options?channel=unstable&query=
https://search.nixos.org/packages?channel=unstable&query=
https://nixos.wiki/index.php?go=Go&search=
https://github.com/nixos/nixpkgs/pulls?q=
https://nix-community.github.io/home-manager/options.html
https://w.uninsane.org/viewer#search?books.name=wikipedia_en_all_maxi_2022-05&pattern=
https://jackett.uninsane.org/UI/Dashboard#search=
https://fed.uninsane.org
https://bt.uninsane.org
https://sci-hub.se
https://news.ycombinator.com

View File

@@ -81,29 +81,8 @@ in
sane.home-manager.windowManager.sway = {
enable = true;
wrapperFeatures.gtk = true;
config = let
fuzzel = "${pkgs.fuzzel}/bin/fuzzel";
sed = "${pkgs.gnused}/bin/sed";
wtype = "${pkgs.wtype}/bin/wtype";
kitty = "${pkgs.kitty}/bin/kitty";
lock-cmd = "${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
vol-up-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5";
vol-down-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5";
mute-cmd = "${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute";
brightness-up-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set +2%";
brightness-down-cmd = "${pkgs.brightnessctl}/bin/brightnessctl set 2%-";
screenshot-cmd = "${pkgs.sway-contrib.grimshot}/bin/grimshot copy area";
# "bookmarking"/snippets inspired by Luke Smith:
# - <https://www.youtube.com/watch?v=d_11QaTlf1I>
snip-file = ./snippets.txt;
# TODO: querying sops here breaks encapsulation
list-snips = "cat ${snip-file} ${config.sops.secrets.snippets.path}";
strip-comments = "${sed} 's/ #.*$//'";
snip-cmd = "${wtype} $(${list-snips} | ${fuzzel} -d -i -w 60 | ${strip-comments})";
# TODO: next splatmoji release should allow `-s none` to disable skin tones
emoji-cmd = "${pkgs.splatmoji}/bin/splatmoji -s medium-light type";
in rec {
terminal = kitty;
config = rec {
terminal = "${pkgs.kitty}/bin/kitty";
window = {
border = 3; # pixel boundary between windows
hideEdgeBorders = "smart"; # don't show border if only window on workspace
@@ -124,7 +103,7 @@ in
modifier = "Mod1";
# list of launchers: https://www.reddit.com/r/swaywm/comments/v39hxa/your_favorite_launcher/
# menu = "${pkgs.dmenu}/bin/dmenu_path";
menu = fuzzel;
menu = "${pkgs.fuzzel}/bin/fuzzel";
# menu = "${pkgs.albert}/bin/albert";
left = "h";
down = "j";
@@ -135,9 +114,7 @@ in
"${modifier}+Return" = "exec ${terminal}";
"${modifier}+Shift+q" = "kill";
"${modifier}+d" = "exec ${menu}";
"${modifier}+s" = "exec ${snip-cmd}";
"${modifier}+l" = "exec ${lock-cmd}";
"${modifier}+slash" = "exec ${emoji-cmd}";
"${modifier}+l" = "exec ${pkgs.swaylock}/bin/swaylock --indicator-idle-visible --indicator-radius 100 --indicator-thickness 30";
# "${modifier}+${left}" = "focus left";
# "${modifier}+${down}" = "focus down";
@@ -164,7 +141,7 @@ in
"${modifier}+f" = "fullscreen toggle";
"${modifier}+a" = "focus parent";
# "${modifier}+s" = "layout stacking";
"${modifier}+s" = "layout stacking";
"${modifier}+w" = "layout tabbed";
"${modifier}+e" = "layout toggle split";
@@ -208,20 +185,19 @@ in
"exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -b 'Yes, exit sway' 'swaymsg exit'";
"${modifier}+r" = "mode resize";
} // {
# media keys
XF86MonBrightnessDown = "exec ${brightness-down-cmd}";
XF86MonBrightnessUp = "exec ${brightness-up-cmd}";
XF86MonBrightnessDown = ''exec "${pkgs.brightnessctl}/bin/brightnessctl set 2%-"'';
XF86MonBrightnessUp = ''exec "${pkgs.brightnessctl}/bin/brightnessctl set +2%"'';
# TODO: hook into a visual prompt to display volume?
XF86AudioRaiseVolume = "exec ${vol-up-cmd}";
XF86AudioLowerVolume = "exec ${vol-down-cmd}";
XF86AudioMute = "exec ${mute-cmd}";
XF86AudioRaiseVolume = "exec '${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5'";
XF86AudioLowerVolume = "exec '${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5'";
XF86AudioMute = "exec '${pkgs.pulsemixer}/bin/pulsemixer --toggle-mute'";
"${modifier}+Page_Up" = "exec ${vol-up-cmd}";
"${modifier}+Page_Down" = "exec ${vol-down-cmd}";
"${modifier}+Page_Up" = "exec '${pkgs.pulsemixer}/bin/pulsemixer --change-volume +5'";
"${modifier}+Page_Down" = "exec '${pkgs.pulsemixer}/bin/pulsemixer --change-volume -5'";
"${modifier}+Print" = "exec ${screenshot-cmd}";
"${modifier}+Print" = "exec '${pkgs.sway-contrib.grimshot}/bin/grimshot copy area'";
};
# mostly defaults:
@@ -621,7 +597,7 @@ in
# }
# '';
};
sane.packages.extraUserPkgs = with pkgs; [
sane.home-manager.extraPackages = with pkgs; [
swaylock
swayidle # (unused)
wl-clipboard

View File

@@ -2,7 +2,7 @@
with lib;
{
config = mkIf (pkgs.system == "x86_64-linux") {
config = mkIf (pkgs.system == "x86_64-linux") {
boot.initrd.availableKernelModules = [
"xhci_pci" "ahci" "sd_mod" "sdhci_pci" # nixos-generate-config defaults
"usb_storage" # rpi needed this to boot from usb storage, i think.

View File

@@ -1,145 +0,0 @@
# common settings to toggle (at runtime, in about:config):
# > security.ssl.require_safe_negotiation
# librewolf is a forked firefox which patches firefox to allow more things
# (like default search engines) to be configurable at runtime.
# many of the settings below won't have effect without those patches.
# see: https://gitlab.com/librewolf-community/settings/-/blob/master/distribution/policies.json
{ config, lib, pkgs, ...}:
with lib;
let
cfg = config.sane.web-browser;
# allow easy switching between firefox and librewolf with `defaultSettings`, below
librewolfSettings = {
browser = pkgs.librewolf-unwrapped;
# browser = pkgs.librewolf-unwrapped.overrideAttrs (drv: {
# # this allows side-loading unsigned addons
# MOZ_REQUIRE_SIGNING = false;
# });
libName = "librewolf";
dotDir = ".librewolf";
desktop = "librewolf.desktop";
};
firefoxSettings = {
browser = pkgs.firefox-esr-unwrapped;
libName = "firefox";
dotDir = ".mozilla/firefox";
desktop = "firefox.desktop";
};
defaultSettings = firefoxSettings;
# defaultSettings = librewolfSettings;
package = pkgs.wrapFirefox cfg.browser {
# inherit the default librewolf.cfg
# it can be further customized via ~/.librewolf/librewolf.overrides.cfg
inherit (pkgs.librewolf-unwrapped) extraPrefsFiles;
inherit (cfg) libName;
extraNativeMessagingHosts = [ pkgs.browserpass ];
# extraNativeMessagingHosts = [ pkgs.gopass-native-messaging-host ];
nixExtensions = let
addon = name: extid: hash: pkgs.fetchFirefoxAddon {
inherit name hash;
url = "https://addons.mozilla.org/firefox/downloads/latest/${name}/latest.xpi";
# extid can be found by unar'ing the above xpi, and copying browser_specific_settings.gecko.id field
fixedExtid = extid;
};
localAddon = pkg: pkgs.fetchFirefoxAddon {
inherit (pkg) name;
src = "${pkg}/share/mozilla/extensions/\\{ec8030f7-c20a-464f-9b0e-13a3a9e97384\\}/${pkg.extid}.xpi";
fixedExtid = pkg.extid;
};
in [
# get names from:
# - ~/ref/nix-community/nur-combined/repos/rycee/pkgs/firefox-addons/generated-firefox-addons.nix
# `wget ...xpi`; `unar ...xpi`; `cat */manifest.json | jq '.browser_specific_settings.gecko.id'`
(addon "ublock-origin" "uBlock0@raymondhill.net" "sha256-+xc4lcdsOwXxMsr4enFsdePbIb6GHq0bFLpqvH5xXos=")
(addon "sponsorblock" "sponsorBlocker@ajay.app" "sha256-30F8oDIgshXVY7YKgnfoc1tUTHfgeFbzXISJuVJs0AM=")
(addon "bypass-paywalls-clean" "{d133e097-46d9-4ecc-9903-fa6a722a6e0e}" "sha256-7ZDkG8O1rEYdh/La0PLi9tp92JxYeQvaOFt/BmnDv3U=")
(addon "sidebery" "{3c078156-979c-498b-8990-85f7987dd929}" "sha256-YONfK/rIjlsrTgRHIt3km07Q7KnpIW89Z9r92ZSCc6w=")
(addon "ether-metamask" "webextension@metamask.io" "sha256-G+MwJDOcsaxYSUXjahHJmkWnjLeQ0Wven8DU/lGeMzA=")
(addon "ublacklist" "@ublacklist" "sha256-vHe/7EYOzcKeAbTElmt0Rb4E2rX0f3JgXThJaUmaz+M=")
(addon "i2p-in-private-browsing" "i2ppb@eyedeekay.github.io" "sha256-dJcJ3jxeAeAkRvhODeIVrCflvX+S4E0wT/PyYzQBQWs=")
# (addon "browserpass-ce" "browserpass@maximbaz.com" "sha256-sXgUBbRvMnRpeIW1MTkmTcoqtW/8RDXAkxAq1evFkpc=")
(localAddon pkgs.browserpass-extension)
];
extraPolicies = {
NoDefaultBookmarks = true;
SearchEngines = {
Default = "DuckDuckGo";
};
AppUpdateURL = "https://localhost";
DisableAppUpdate = true;
OverrideFirstRunPage = "";
OverridePostUpdatePage = "";
DisableSystemAddonUpdate = true;
DisableFirefoxStudies = true;
DisableTelemetry = true;
DisableFeedbackCommands = true;
DisablePocket = true;
DisableSetDesktopBackground = false;
# remove many default search providers
# XXX this seems to prevent the `nixExtensions` from taking effect
# Extensions.Uninstall = [
# "google@search.mozilla.org"
# "bing@search.mozilla.org"
# "amazondotcom@search.mozilla.org"
# "ebay@search.mozilla.org"
# "twitter@search.mozilla.org"
# ];
# XXX doesn't seem to have any effect...
# docs: https://github.com/mozilla/policy-templates#homepage
# Homepage = {
# HomepageURL = "https://uninsane.org/";
# StartPage = "homepage";
# };
# NewTabPage = true;
};
};
in
{
options = {
sane.web-browser = mkOption {
default = defaultSettings;
type = types.attrs;
};
};
config = lib.mkIf config.sane.home-manager.enable {
# XXX: although home-manager calls this option `firefox`, we can use other browsers and it still mostly works.
home-manager.users.colin = lib.mkIf (config.sane.gui.enable) {
programs.firefox = {
enable = true;
inherit package;
};
# uBlock filter list configuration.
# specifically, enable the GDPR cookie prompt blocker.
# data.toOverwrite.filterLists is additive (i.e. it supplements the default filters)
# this configuration method is documented here:
# - <https://github.com/gorhill/uBlock/issues/2986#issuecomment-364035002>
# the specific attribute path is found via scraping ublock code here:
# - <https://github.com/gorhill/uBlock/blob/master/src/js/storage.js>
# - <https://github.com/gorhill/uBlock/blob/master/assets/assets.json>
home.file."${cfg.dotDir}/managed-storage/uBlock0@raymondhill.net.json".text = ''
{
"name": "uBlock0@raymondhill.net",
"description": "ignored",
"type": "storage",
"data": {
"toOverwrite": "{\"filterLists\": [\"fanboy-cookiemonster\"]}"
}
}
'';
home.file."${cfg.dotDir}/${cfg.libName}.overrides.cfg".text = ''
// if we can't query the revocation status of a SSL cert because the issuer is offline,
// treat it as unrevoked.
// see: <https://librewolf.net/docs/faq/#im-getting-sec_error_ocsp_server_error-what-can-i-do>
defaultPref("security.OCSP.require", false);
'';
};
};
}

View File

@@ -1,20 +0,0 @@
# borrows from:
# - default config: <https://github.com/cspeterson/splatmoji/blob/master/splatmoji.config>
# - wayland: <https://github.com/cspeterson/splatmoji/issues/32#issuecomment-830862566>
{ pkgs, ... }:
{
home-manager.users.colin = {
xdg.configFile."splatmoji/splatmoji.config".text = ''
history_file=/home/colin/.local/state/splatmoji/history
history_length=5
# TODO: wayland equiv
paste_command=xdotool key ctrl+v
# rofi_command=${pkgs.wofi}/bin/wofi --dmenu --insensitive --cache-file /dev/null
rofi_command=${pkgs.fuzzel}/bin/fuzzel -d -i -w 60
xdotool_command=${pkgs.wtype}/bin/wtype
# TODO: wayland equiv
xsel_command=xsel -b -i
'';
};
}

View File

@@ -1,108 +0,0 @@
{ config, lib, ... }:
lib.mkIf config.sane.home-manager.enable
{
sane.impermanence.home-dirs = [
# we don't need to full zsh dir -- just the history file --
# but zsh will sometimes backup the history file and we get fewer errors if we do proper mounts instead of symlinks.
# TODO: should be private?
".local/share/zsh"
# cache gitstatus otherwise p10k fetched it from the net EVERY BOOT
".cache/gitstatus"
];
home-manager.users.colin.programs.zsh = {
enable = true;
enableSyntaxHighlighting = true;
enableVteIntegration = true;
history.ignorePatterns = [ "rm *" ];
dotDir = ".config/zsh";
history.path = "/home/colin/.local/share/zsh/history";
# defaultKeymap = "vicmd"; # vim normal mode (cmd mode)
# powerlevel10k prompt config
# p10k.zsh is the auto-generated config, and i overwrite those defaults here, below.
initExtraBeforeCompInit = (builtins.readFile ./p10k.zsh) + ''
# powerlevel10k launches a gitstatusd daemon to accelerate git prompt queries.
# this keeps open file handles for any git repo i touch for 60 minutes (by default).
# that prevents unmounting whatever device the git repo is on -- particularly problematic for ~/private.
# i can disable gitstatusd and get slower fallback git queries:
# - either universally
# - or selectively by path
# see: <https://github.com/romkatv/powerlevel10k/issues/246>
typeset -g POWERLEVEL9K_VCS_DISABLED_DIR_PATTERN='(/home/colin/private/*|/home/colin/knowledge/*)'
# typeset -g POWERLEVEL9K_DISABLE_GITSTATUS=true
# show user@host also when logged into the current machine.
# default behavior is to show it only over ssh.
typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_CONTENT_EXPANSION='$P9K_CONTENT'
'';
initExtra = ''
# zmv is a way to do rich moves/renames, with pattern matching/substitution.
# see for an example: <https://filipe.kiss.ink/zmv-zsh-rename/>
autoload -Uz zmv
# disable `rm *` confirmations
setopt rmstarsilent
function nd() {
mkdir -p "$1";
pushd "$1";
}
'';
# prezto = oh-my-zsh fork; controls prompt, auto-completion, etc.
# see: https://github.com/sorin-ionescu/prezto
prezto = {
enable = true;
pmodules = [
# configures jobs to persist after shell exit; other basic niceties
"environment"
# auto-titles terminal (e.g. based on cwd)
"terminal"
# configures shortcuts like Ctrl+U=undo, Ctrl+L=clear
"editor"
# adds `history-stat` alias, setopts for good history defaults
"history"
# sets AUTO_CD, adds `d` alias to list directory stack, and `1`-`9` to cd that far back the stack
"directory"
# helpers for term colors and styling. used by prompts? might be unnecessary
"spectrum"
# configures aliases like `ll`, `la`, disables globbing for things like rsync
# adds aliases like `get` to fetch a file. also adds `http-serve` alias??
"utility"
# tab completion. requires `utility` module prior to loading
# TODO: enable AUTO_PARAM_SLASH
"completion"
"prompt"
# TODO: enable syntax-highlighting ?
];
prompt.theme = "powerlevel10k";
utility.safeOps = false; # disable `mv` confirmation (and supposedly `rm`, too)
# editor.keymap = "vi";
};
dirHashes = {
# convenient `cd`-isms
"3rd" = "/home/colin/dev/3rd";
"dev" = "/home/colin/dev";
"knowledge" = "/home/colin/knowledge";
"nixos" = "/home/colin/nixos";
"nixpkgs" = "/home/colin/dev/3rd/nixpkgs";
"ref" = "/home/colin/ref";
"secrets" = "/home/colin/knowledge/secrets";
"tmp" = "/home/colin/tmp";
"uninsane" = "/home/colin/dev/uninsane";
"Videos" = "/home/colin/Videos";
};
};
home-manager.users.colin.home.shellAliases = {
":q" = "exit";
# common typos
"cd.." = "cd ..";
"cd../" = "cd ../";
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,11 +6,6 @@ let
in
{
options = {
sane.image.enable = mkOption {
default = true;
type = types.bool;
description = "whether to enable image targets. this doesn't mean they'll be built unless you specifically reference the target.";
};
# packages whose contents should be copied directly into the /boot partition.
# e.g. EFI loaders, u-boot bootloader, etc.
sane.image.extraBootFiles = mkOption {

98
modules/impermanence.nix Normal file
View File

@@ -0,0 +1,98 @@
# borrows from:
# https://xeiaso.net/blog/paranoid-nixos-2021-07-18
# https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/
# https://github.com/nix-community/impermanence
{ lib, config, impermanence, ... }:
with lib;
let
cfg = config.sane.impermanence;
# taken from sops-nix code: checks if any secrets are needed to create /etc/shadow
secretsForUsers = (lib.filterAttrs (_: v: v.neededForUsers) config.sops.secrets) != {};
in
{
options = {
sane.impermanence.enable = mkOption {
default = false;
type = types.bool;
};
sane.impermanence.home-dirs = mkOption {
default = [];
type = types.listOf (types.either types.str (types.attrsOf types.str));
};
sane.impermanence.service-dirs = mkOption {
default = [];
type = types.listOf (types.either types.str (types.attrsOf types.str));
};
};
config = let
map-dir = defaults: dir: if isString dir then
map-dir defaults { directory = "${defaults.directory}${dir}"; }
else
defaults // dir
;
map-dirs = defaults: dirs: builtins.map (map-dir defaults) dirs;
map-home-dirs = map-dirs { user = "colin"; group = "users"; mode = "0755"; directory = "/home/colin/"; };
map-sys-dirs = map-dirs { user = "root"; group = "root"; mode = "0755"; directory = ""; };
in mkIf cfg.enable {
sane.image.extraDirectories = [ "/nix/persist/var/log" ];
environment.persistence."/nix/persist" = {
directories = (map-home-dirs cfg.home-dirs) ++ (map-sys-dirs [
# TODO: this `0700` here clobbers the perms for /persist/etc, breaking boot on freshly-deployed devices
# { mode = "0700"; directory = "/etc/NetworkManager/system-connections"; }
# "/etc/nixos"
# "/etc/ssh" # persist only the specific files we want, instead
"/var/log"
"/var/backup" # for e.g. postgres dumps
# "/var/lib/AccountsService" # not sure what this is, but it's empty
"/var/lib/alsa" # preserve output levels, default devices
# "/var/lib/blueman" # files aren't human readable
"/var/lib/bluetooth" # preserve bluetooth handshakes
"/var/lib/colord" # preserve color calibrations (?)
# "/var/lib/dhclient" # empty on lappy; dunno about desko
# "/var/lib/fwupd" # not sure why this would need persistent state
# "/var/lib/geoclue" # empty on lappy
# "/var/lib/lockdown" # empty on desko; might store secrets after iOS handshake?
# "/var/lib/logrotate.status" # seems redundant with what's in /var/log?
"/var/lib/machines" # maybe not needed, but would be painful to add a VM and forget.
# "/var/lib/misc" # empty on lappy
# "/var/lib/NetworkManager" # looks to be mostly impermanent state?
# "/var/lib/NetworkManager-fortisslvpn" # empty on lappy
# "/var/lib/nixos" # has some uid/gid maps, but we enforce these to be deterministic.
# "/var/lib/PackageKit" # wtf is this?
# "/var/lib/power-profiles-daemon" # redundant with nixos declarations
# "/var/lib/private" # empty on lappy
# "/var/lib/systemd" # nothing obviously necessary
# "/var/lib/udisks2" # empty on lappy
# "/var/lib/upower" # historic charge data. unnecessary, but maybe used somewhere?
#
# servo additions:
] ++ cfg.service-dirs);
files = [ "/etc/machine-id" ];
};
# secret decoding depends on /etc/ssh keys, which are persisted
system.activationScripts.setupSecrets.deps = [ "persist-files" ];
# `setupSecretsForUsers` should depend on `persist-files`,
# but `persist-files` itself depends on `users`, to this would be circular.
# we work around that by manually mounting the ssh host key.
# strictly speaking, this makes the `setupSecrets -> persist-files` dep extraneous,
# but it's a decent safety net in case something goes wrong.
# system.activationScripts.setupSecretsForUsers.deps = [ "persist-files" ];
system.activationScripts.setupSecretsForUsers= lib.mkIf secretsForUsers {
deps = [ "persist-ssh-host-keys" ];
};
system.activationScripts.persist-ssh-host-keys = lib.mkIf secretsForUsers (
let
key_dir = "/etc/ssh/host_keys";
in ''
mkdir -p ${key_dir}
mount -o bind /nix/persist${key_dir} ${key_dir}
''
);
};
}

View File

@@ -1,326 +0,0 @@
# borrows from:
# https://xeiaso.net/blog/paranoid-nixos-2021-07-18
# https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/
# https://github.com/nix-community/impermanence
{ config, lib, pkgs, utils, ... }:
with lib;
let
cfg = config.sane.impermanence;
# taken from sops-nix code: checks if any secrets are needed to create /etc/shadow
secrets-for-users = (lib.filterAttrs (_: v: v.neededForUsers) config.sops.secrets) != {};
getStore = { encryptedClearOnBoot, ... }: (
if encryptedClearOnBoot then {
device = "/mnt/impermanence/crypt/clearedonboot";
underlying = {
path = "/nix/persist/crypt/clearedonboot";
# TODO: consider moving this to /tmp, but that requires tmp be mounted first?
type = "gocryptfs";
key = "/mnt/impermanence/crypt/clearedonboot.key";
};
} else {
device = "/nix/persist";
# device = "/mnt/impermenanence/persist/plain";
# underlying = {
# path = "/nix/persist";
# type = "bind";
# };
}
);
home-dir-defaults = {
user = "colin";
group = "users";
mode = "0755";
relativeTo = "/home/colin";
};
sys-dir-defaults = {
user = "root";
group = "root";
mode = "0755";
relativeTo = "";
};
# turn a path into a name suitable for systemd
cleanName = utils.escapeSystemdPath;
# split the string path into a list of string components.
# root directory "/" becomes the empty list [].
# implicitly performs normalization so that:
# splitPath "a//b/" => ["a" "b"]
# splitPath "/a/b" => ["a" "b"]
splitPath = str: builtins.filter (seg: (builtins.isString seg) && seg != "" ) (builtins.split "/" str);
# return a string path, with leading slash but no trailing slash
joinPathAbs = comps: "/" + (builtins.concatStringsSep "/" comps);
concatPaths = paths: joinPathAbs (builtins.concatLists (builtins.map (p: splitPath p) paths));
# normalize the given path
normPath = str: joinPathAbs (splitPath str);
# return the parent directory. doesn't care about leading/trailing slashes.
parentDir = str: normPath (builtins.dirOf (normPath str));
dirOptions = defaults: types.submodule {
options = {
encryptedClearOnBoot = mkOption {
default = false;
type = types.bool;
};
directory = mkOption {
type = types.str;
};
user = mkOption {
type = types.str;
default = defaults.user;
};
group = mkOption {
type = types.str;
default = defaults.group;
};
mode = mkOption {
type = types.str;
default = defaults.mode;
};
};
};
mkDirsOption = defaults: mkOption {
default = [];
type = types.listOf (types.coercedTo types.str (d: { directory = d; }) (dirOptions defaults));
# apply = map (d: if isString d then { directory = d; } else d);
};
# expand user options with more context
ingestDirOption = defaults: opt: {
inherit (opt) user group mode;
directory = concatPaths [ defaults.relativeTo opt.directory ];
## helpful context
store = builtins.addErrorContext ''while ingestDirOption on ${opt.directory} with attrs ${builtins.concatStringsSep " " (attrNames opt)}''
(getStore opt);
};
ingestDirOptions = defaults: opts: builtins.map (ingestDirOption defaults) opts;
ingested-home-dirs = ingestDirOptions home-dir-defaults cfg.home-dirs;
ingested-sys-dirs = ingestDirOptions sys-dir-defaults cfg.dirs;
ingested-dirs = ingested-home-dirs ++ ingested-sys-dirs;
# include these anchor points as "virtual" nodes in below fs tree.
home-dir = {
inherit (home-dir-defaults) user group mode;
directory = normPath home-dir-defaults.relativeTo;
};
root-dir = {
inherit (sys-dir-defaults) user group mode;
directory = normPath sys-dir-defaults.relativeTo;
};
unexpanded-tree = builtins.listToAttrs (builtins.map
(dir: {
name = dir.directory;
value = dir;
})
(ingested-dirs ++ [ home-dir root-dir ])
);
# ensures the provided node and all parent nodes exist
ensureNode = tree: path: (
let
parent-path = parentDir path;
tree-with-parent = if parent-path == "/"
then tree
else ensureNode tree parent-path;
parent = tree-with-parent."${parent-path}";
# how to initialize this node if it doesn't exist explicitly.
default-node = parent // { directory = path; };
in
{ "${path}" = default-node; } // tree-with-parent
);
# finally, this tree has no orphan nodes
expanded-tree = foldl' ensureNode unexpanded-tree (builtins.attrNames unexpanded-tree);
in
{
options = {
sane.impermanence.enable = mkOption {
default = false;
type = types.bool;
};
sane.impermanence.root-on-tmpfs = mkOption {
default = false;
type = types.bool;
description = "define / to be a tmpfs. make sure to mount some other device to /nix";
};
sane.impermanence.home-dirs = mkDirsOption home-dir-defaults;
sane.impermanence.dirs = mkDirsOption sys-dir-defaults;
};
config = mkIf cfg.enable (lib.mkMerge [
(lib.mkIf cfg.root-on-tmpfs {
fileSystems."/" = {
device = "none";
fsType = "tmpfs";
options = [
"mode=755"
"size=1G"
"defaults"
];
};
})
{
# without this, we get `fusermount: fuse device not found, try 'modprobe fuse' first`.
# - that only happens after a activation-via-boot -- not activation-after-rebuild-switch.
# it seems likely that systemd loads `fuse` by default. see:
# - </etc/systemd/system/sysinit.target.wants/sys-fs-fuse-connections.mount>
# - triggers: /etc/systemd/system/modprobe@.service
# - calls `modprobe`
# note: even `boot.kernelModules = ...` isn't enough: that option creates /etc/modules-load.d/, which is ingested only by systemd.
# note: `boot.initrd.availableKernelModules` ALSO isn't enough: idk why.
# TODO: might not be necessary now we're using fileSystems and systemd
boot.initrd.kernelModules = [ "fuse" ];
# TODO: convert this to a systemd unit file?
system.activationScripts.prepareEncryptedClearedOnBoot =
let
script = pkgs.writeShellApplication {
name = "prepareEncryptedClearedOnBoot";
runtimeInputs = with pkgs; [ gocryptfs ];
text = ''
backing="$1"
passfile="$2"
if ! test -e "$passfile"
then
tmpdir=$(dirname "$passfile")
mkdir -p "$backing" "$tmpdir"
# if the key doesn't exist, it's probably not mounted => delete the backing dir
rm -rf "''${backing:?}"/*
# generate key. we can "safely" keep it around for the lifetime of this boot
dd if=/dev/random bs=128 count=1 | base64 --wrap=0 > "$passfile"
# initialize the crypt store
gocryptfs -quiet -passfile "$passfile" -init "$backing"
fi
'';
};
store = getStore { encryptedClearOnBoot = true; };
in {
text = ''${script}/bin/prepareEncryptedClearedOnBoot ${store.underlying.path} ${store.underlying.key}'';
};
fileSystems = let
store = getStore { encryptedClearOnBoot = true; };
in {
"${store.device}" = {
device = store.underlying.path;
fsType = "fuse.gocryptfs";
options = [
"nodev"
"nosuid"
"allow_other"
"passfile=${store.underlying.key}"
"defaults"
];
noCheck = true;
};
};
environment.systemPackages = [ pkgs.gocryptfs ]; # fuse needs to find gocryptfs
}
(
let cfgFor = opt:
let
# systemd creates <path>.mount services for every fileSystems entry.
# <path> gets escaped as part of that: this code tries to guess that escaped name here.
backing-mount = cleanName opt.store.device;
mount-service = cleanName opt.directory;
perms-service = "impermanence-perms-${mount-service}";
parent-mount-service = cleanName (parentDir opt.directory);
parent-perms-service = "impermanence-perms-${parent-mount-service}";
is-mount = opt ? store;
backing-path = if is-mount then
concatPaths [ opt.store.device opt.directory ]
else
opt.directory;
in {
fileSystems."${opt.directory}" = lib.mkIf is-mount {
device = concatPaths [ opt.store.device opt.directory ];
options = [
"bind"
# "x-systemd.requires=${backing-mount}.mount" # this should be implicit
"x-systemd.after=${perms-service}.service"
# `wants` doesn't seem to make it to the service file here :-(
"x-systemd.wants=${perms-service}.service"
];
# fsType = "bind";
noCheck = true;
};
# create services which ensure the source directories exist and have correct ownership/perms before mounting
systemd.services."${perms-service}" = let
perms-script = pkgs.writeShellScript "impermanence-prepare-perms" ''
backing="$1"
path="$2"
user="$3"
group="$4"
mode="$5"
mkdir "$path" || test -d "$path"
chmod "$mode" "$path"
chown "$user:$group" "$path"
# XXX: fix up the permissions of the origin, otherwise it overwrites the mountpoint with defaults.
# TODO: apply to the full $backing path? like, construct it entirely in parallel?
if [ "$backing" != "$path" ]
then
mkdir -p "$backing"
chmod "$mode" "$backing"
chown "$user:$group" "$backing"
fi
'';
in {
description = "prepare permissions for ${opt.directory}";
serviceConfig = {
ExecStart = ''${perms-script} ${backing-path} ${opt.directory} ${opt.user} ${opt.group} ${opt.mode}'';
Type = "oneshot";
};
unitConfig = {
# prevent systemd making this unit implicitly dependent on sysinit.target.
# see: <https://www.freedesktop.org/software/systemd/man/systemd.special.html>
DefaultDependencies = "no";
};
wantedBy = lib.mkIf is-mount [ "${mount-service}.mount" ];
after = lib.mkIf (opt.directory != "/") [ "${parent-perms-service}.service" ];
wants = lib.mkIf (opt.directory != "/") [ "${parent-perms-service}.service" ];
};
};
cfgs = builtins.map cfgFor (builtins.attrValues expanded-tree);
# cfgs = builtins.map cfgFor ingested-dirs;
# cfgs = [ (cfgFor (ingestDirOption home-dir-defaults ".cache")) ];
# myMerge = items: builtins.foldl' (acc: new: acc // new) {} items;
in {
# fileSystems = myMerge (catAttrs "fileSystems" cfgs);
fileSystems = lib.mkMerge (builtins.catAttrs "fileSystems" cfgs);
systemd = lib.mkMerge (catAttrs "systemd" cfgs);
}
)
(lib.mkIf secrets-for-users {
# secret decoding depends on /etc/ssh keys, so make sure those are present.
system.activationScripts.setupSecretsForUsers = lib.mkIf secrets-for-users {
deps = [ "etc" ];
};
system.activationScripts.etc.deps = lib.mkForce [];
assertions = builtins.concatLists (builtins.attrValues (
builtins.mapAttrs
(path: value: [
{
assertion = (builtins.substring 0 1 value.user) == "+";
message = "non-numeric user for /etc/${path}: ${value.user} prevents early /etc linking";
}
{
assertion = (builtins.substring 0 1 value.group) == "+";
message = "non-numeric group for /etc/${path}: ${value.group} prevents early /etc linking";
}
])
config.environment.etc
));
})
]);
}

View File

@@ -20,28 +20,22 @@ in
default = false;
type = types.bool;
};
sane.nixcache.enable-trusted-keys = mkOption {
default = config.sane.nixcache.enable;
type = types.bool;
};
};
config = {
config = mkIf cfg.enable {
# use our own binary cache
# to explicitly build from a specific cache (in case others are down):
# - `nixos-rebuild ... --option substituters https://cache.nixos.org`
# - `nix build ... --substituters http://desko:5000`
nix.settings.substituters = mkIf cfg.enable [
"https://nixcache.uninsane.org"
"http://desko:5000"
"https://nix-community.cachix.org"
"https://cache.nixos.org/"
];
# always trust our keys (so one can explicitly use a substituter even if it's not the default
nix.settings.trusted-public-keys = mkIf cfg.enable-trusted-keys [
"nixcache.uninsane.org:r3WILM6+QrkmsLgqVQcEdibFD7Q/4gyzD9dGT33GP70="
"desko:Q7mjjqoBMgNQ5P0e63sLur65A+D4f3Sv4QiycDIKxiI="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
nix.settings = {
substituters = [
"https://nixcache.uninsane.org"
"http://desko:5000"
"https://nix-community.cachix.org"
"https://cache.nixos.org/"
];
trusted-public-keys = [
"nixcache.uninsane.org:r3WILM6+QrkmsLgqVQcEdibFD7Q/4gyzD9dGT33GP70="
"desko:Q7mjjqoBMgNQ5P0e63sLur65A+D4f3Sv4QiycDIKxiI="
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
};
};
}

View File

@@ -1,313 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
with pkgs;
let
cfg = config.sane.packages;
imagemagick = pkgs.imagemagick.override {
ghostscriptSupport = true;
};
consolePkgs = [
backblaze-b2
cdrtools
dmidecode
duplicity
efivar
flashrom
fwupd
ghostscript # TODO: imagemagick wrapper should add gs to PATH
gnupg
gocryptfs
gopass
gopass-jsonapi
ifuse
imagemagick
ipfs
libimobiledevice
libsecret # for managing user keyrings
lm_sensors # for sensors-detect
lshw
ffmpeg
memtester
networkmanager
nixpkgs-review
# nixos-generators
# nettools
nmon
oathToolkit # for oathtool
# ponymix
pulsemixer
python3
rsync
# python3Packages.eyeD3 # music tagging
sane-scripts
sequoia
snapper
sops
speedtest-cli
sqlite # to debug sqlite3 databases
ssh-to-age
sudo
# tageditor # music tagging
unar
visidata
w3m
wireguard-tools
# youtube-dl
yt-dlp
];
guiPkgs = [
# GUI only
aerc # email client
audacity
celluloid # mpv frontend
chromium
clinfo
{ pkg = dino; private = [ ".local/share/dino" ]; }
electrum
# creds/session keys, etc
{ pkg = element-desktop; private = [ ".config/Element" ]; }
# `emote` will show a first-run dialog based on what's in this directory.
# mostly, it just keeps a LRU of previously-used emotes to optimize display order.
# TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
{ pkg = emote; dir = [ ".local/share/Emote" ]; }
evince # works on phosh
# { pkg = fluffychat-moby; dir = [ ".local/share/chat.fluffy.fluffychat" ]; } # TODO: ship normal fluffychat on non-moby?
foliate
font-manager
# XXX by default fractal stores its state in ~/.local/share/<UUID>.
# after logging in, manually change ~/.local/share/keyrings/... to point it to some predictable subdir.
# then reboot (so that libsecret daemon re-loads the keyring...?)
{ pkg = fractal-latest; private = [ ".local/share/fractal" ]; }
# { pkg = fractal-next; private = [ ".local/share/fractal" ]; }
gajim # XMPP client
gimp # broken on phosh
gnome.cheese
gnome.dconf-editor
gnome-feeds # RSS reader (with claimed mobile support)
gnome.file-roller
gnome.gnome-disk-utility
gnome.gnome-maps # works on phosh
gnome.nautilus
# gnome-podcasts
gnome.gnome-system-monitor
gnome.gnome-terminal # works on phosh
gnome.gnome-weather
{ pkg = gpodder-configured; dir = [ "gPodder/Downloads" ]; }
gthumb
handbrake
inkscape
kdenlive
kid3 # audio tagging
krita
libreoffice-fresh # XXX colin: maybe don't want this on mobile
lollypop
mesa-demos
{ pkg = mpv; dir = [ ".config/mpv/watch_later" ]; }
networkmanagerapplet
# not strictly necessary, but allows caching articles; offline use, etc.
{ pkg = newsflash; dir = [ ".local/share/news-flash" ]; }
{ pkg = nheko; private = [
".config/nheko" # config file (including client token)
".cache/nheko" # media cache
".local/share/nheko" # per-account state database
]; }
# settings (electron app). TODO: can i manage these settings with home-manager?
{ pkg = obsidian; dir = [ ".config/obsidian" ]; }
pavucontrol
# picard # music tagging
playerctl
libsForQt5.plasmatube # Youtube player
soundconverter
# sublime music persists any downloaded albums here.
# it doesn't obey a conventional ~/Music/{Artist}/{Album}/{Track} notation, so no symlinking
# config (e.g. server connection details) is persisted in ~/.config/sublime-music/config.json
# possible to pass config as a CLI arg (sublime-music -c config.json)
# { pkg = sublime-music; dir = [ ".local/share/sublime-music" ]; }
{ pkg = sublime-music-mobile; dir = [ ".local/share/sublime-music" ]; }
tdesktop # broken on phosh
{ pkg = tokodon; private = [ ".cache/KDE/tokodon" ]; }
# vlc remembers play position in ~/.config/vlc/vlc-qt-interface.conf
{ pkg = vlc; dir = [ ".config/vlc" ]; }
whalebird # pleroma client. input is broken on phosh
xdg-utils # for xdg-open
xterm # broken on phosh
]
++ (if pkgs.system == "x86_64-linux" then
[
# x86_64 only
# creds, but also 200 MB of node modules, etc
(let discord = (pkgs.discord.override {
# XXX 2022-07-31: fix to allow links to open in default web-browser:
# https://github.com/NixOS/nixpkgs/issues/78961
nss = pkgs.nss_latest;
}); in { pkg = discord; private = [ ".config/discord" ]; })
# kaiteki # Pleroma client
# gnome.zenity # for kaiteki (it will use qarma, kdialog, or zenity)
# gpt2tc # XXX: unreliable mirror
logseq
losslesscut-bin
makemkv
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
{ pkg = monero-gui; dir = [ ".bitmonero" ]; }
# creds, media
{ pkg = signal-desktop; private = [ ".config/Signal" ]; }
# creds. TODO: can i manage this with home-manager?
{ pkg = spotify; dir = [ ".config/spotify" ]; }
# hardenedMalloc solves a crash at startup
(tor-browser-bundle-bin.override { useHardenedMalloc = false; })
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
{ pkg = zecwallet-lite; private = [ ".zcash" ]; }
] else []);
# general-purpose utilities that we want any user to be able to access
# (specifically: root, in case of rescue)
systemPkgs = [
btrfs-progs
cacert.unbundled # some services require unbundled /etc/ssl/certs
cryptsetup
dig
efibootmgr
fatresize
fd
file
gawk
gptfdisk
hdparm
htop
iftop
inetutils # for telnet
iotop
iptables
jq
killall
lsof
netcat
nethogs
nmap
openssl
parted
pciutils
powertop
pstree
ripgrep
screen
smartmontools
socat
strace
tcpdump
tree
usbutils
wget
];
# useful devtools:
devPkgs = [
bison
dtc
flex
gcc
gdb
# gcc-arm-embedded
# gcc_multi
gnumake
mercurial
mix2nix
rustup
swig
];
pkgSpec = types.submodule {
options = {
pkg = mkOption {
type = types.package;
};
dir = mkOption {
type = types.listOf types.str;
default = [];
description = "list of home-relative paths to persist for this package";
};
private = mkOption {
type = types.listOf types.str;
default = [];
description = "list of home-relative paths to persist (in encrypted format) for this package";
};
};
};
in
{
options = {
# packages to deploy to the user's home
sane.packages.extraUserPkgs = mkOption {
default = [ ];
type = types.listOf (types.either types.package pkgSpec);
};
sane.packages.enableConsolePkgs = mkOption {
default = false;
type = types.bool;
};
sane.packages.enableGuiPkgs = mkOption {
default = false;
type = types.bool;
};
sane.packages.enableDevPkgs = mkOption {
description = ''
enable packages that are useful for building other software by hand.
you should prefer to keep this disabled except when prototyping, e.g. packaging new software.
'';
default = false;
type = types.bool;
};
sane.packages.enableSystemPkgs = mkOption {
default = false;
type = types.bool;
description = "enable system-wide packages";
};
sane.packages.enabledUserPkgs = mkOption {
default = cfg.extraUserPkgs
++ (if cfg.enableConsolePkgs then consolePkgs else [])
++ (if cfg.enableGuiPkgs then guiPkgs else [])
++ (if cfg.enableDevPkgs then devPkgs else [])
;
type = types.listOf (types.either types.package types.attrs);
description = "generated from other config options";
};
};
config = {
environment.systemPackages = mkIf cfg.enableSystemPkgs systemPkgs;
# XXX: this might not be necessary. try removing this and cacert.unbundled?
environment.etc."ssl/certs".source = mkIf cfg.enableSystemPkgs "${pkgs.cacert.unbundled}/etc/ssl/certs/*";
};
}

View File

@@ -2,9 +2,6 @@
{
imports = [
./duplicity.nix
./dyn-dns.nix
./kiwix-serve.nix
./nixserve.nix
./trust-dns.nix
];
}

View File

@@ -1,5 +1,5 @@
# docs: https://search.nixos.org/options?channel=21.11&query=duplicity
{ config, lib, pkgs, ... }:
{ config, lib, ... }:
with lib;
let
@@ -15,10 +15,11 @@ in
config = mkIf cfg.enable {
# we need this mostly because of the size of duplicity's cache
sane.impermanence.dirs = [ "/var/lib/duplicity" ];
sane.impermanence.service-dirs = [ "/var/lib/duplicity" ];
services.duplicity.enable = true;
services.duplicity.targetUrl = "$DUPLICITY_URL";
services.duplicity.targetUrl = ''"$DUPLICITY_URL"'';
services.duplicity.escapeUrl = false;
# format: PASSPHRASE=<cleartext> \n DUPLICITY_URL=b2://...
# two sisters
# PASSPHRASE: remote backups will be encrypted using this passphrase (using gpg)
@@ -31,28 +32,29 @@ in
services.duplicity.secretFile = config.sops.secrets.duplicity_passphrase.path;
# NB: manually trigger with `systemctl start duplicity`
services.duplicity.frequency = "daily";
# TODO: this needs updating to handle impermanence changes
services.duplicity.exclude = [
# impermanent/inconsequential data:
"/dev"
"/proc"
"/run"
"/sys"
"/tmp"
# bind mounted (dupes):
"/var/lib"
# other mounts
"/mnt"
# data that's not worth the cost to backup:
"/nix/persist/var/lib/uninsane/media"
"/nix/persist/home/colin/tmp"
"/nix/persist/home/colin/Videos"
"/home/colin/tmp"
"/home/colin/Videos"
];
services.duplicity.extraFlags = [
# without --allow-source-mismatch, duplicity will abort if you change the hostname between backups
"--allow-source-mismatch"
# includes/exclude ordering matters, so we explicitly control it here.
# the first match decides a file's treatment. so here:
# - /nix/persist/home/colin/tmp is excluded
# - *other* /nix/persist/ files are included by default
# - anything else under `/` are excluded by default
"--exclude" "/nix/persist/home/colin/dev/home-logic/coremem/out" # this can reach > 1 TB
"--exclude" "/nix/persist/home/colin/use/iso" # might want to re-enable... but not critical
"--exclude" "/nix/persist/home/colin/.local/share/sublime-music" # music cache. better to just keep the HQ sources
"--exclude" "/nix/persist/home/colin/.local/share/Steam" # can just re-download games
"--exclude" "/nix/persist/home/colin/.bitmonero/lmdb" # monero blockchain
"--exclude" "/nix/persist/home/colin/.rustup"
"--exclude" "/nix/persist/home/colin/ref" # publicly available data: no point in duplicating it
"--exclude" "/nix/persist/home/colin/tmp"
"--exclude" "/nix/persist/home/colin/Videos"
"--exclude" "/nix/persist/var/lib/duplicity" # don't back up our own backup state!
"--include" "/nix/persist"
"--exclude" "/"
];
# set this for the FIRST backup, then remove it to enable incremental backups
@@ -68,26 +70,5 @@ in
"/dev/mmc0 5M"
];
};
# based on <nixpkgs:nixos/modules/services/backup/duplicity.nix> with changes:
# - remove the cleanup step: API key doesn't have delete perms
# - don't escape the targetUrl: it comes from an env var set in the secret file
systemd.services.duplicity.script = let
cfg = config.services.duplicity;
target = cfg.targetUrl;
extra = escapeShellArgs ([ "--archive-dir" "/var/lib/duplicity" ] ++ cfg.extraFlags);
dup = "${pkgs.duplicity}/bin/duplicity";
in lib.mkForce ''
set -x
# ${dup} cleanup ${target} --force ${extra}
# ${lib.optionalString (cfg.cleanup.maxAge != null) "${dup} remove-older-than ${lib.escapeShellArg cfg.cleanup.maxAge} ${target} --force ${extra}"}
# ${lib.optionalString (cfg.cleanup.maxFull != null) "${dup} remove-all-but-n-full ${builtins.toString cfg.cleanup.maxFull} ${target} --force ${extra}"}
# ${lib.optionalString (cfg.cleanup.maxIncr != null) "${dup} remove-all-inc-of-but-n-full ${toString cfg.cleanup.maxIncr} ${target} --force ${extra}"}
exec ${dup} ${if cfg.fullIfOlderThan == "always" then "full" else "incr"} ${lib.escapeShellArg cfg.root} ${target} ${lib.escapeShellArgs ([]
++ concatMap (p: [ "--include" p ]) cfg.include
++ concatMap (p: [ "--exclude" p ]) cfg.exclude
++ (lib.optionals (cfg.fullIfOlderThan != "never" && cfg.fullIfOlderThan != "always") [ "--full-if-older-than" cfg.fullIfOlderThan ])
)} ${extra}
'';
};
}

View File

@@ -1,91 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.sane.services.dyn-dns;
in
{
options = {
sane.services.dyn-dns = {
enable = mkOption {
default = false;
type = types.bool;
};
ipPath = mkOption {
default = "/var/lib/uninsane/wan.txt";
type = types.str;
description = "where to store the latest WAN IPv4 address";
};
ipCmd = mkOption {
default = "${pkgs.sane-scripts}/bin/sane-ip-check-router-wan";
type = types.path;
description = "command to run to query the current WAN IP";
};
interval = mkOption {
type = types.str;
default = "10min";
description = "systemd time string for how frequently to re-check the IP";
};
restartOnChange = mkOption {
type = types.listOf types.str;
default = [];
description = "list of systemd unit files to restart when the IP changes";
};
};
};
config = mkIf cfg.enable {
systemd.services.dyn-dns = {
description = "update this host's record of its WAN IP";
serviceConfig.Type = "oneshot";
restartTriggers = [(builtins.toJSON cfg)];
after = [ "network.target" ];
wantedBy = cfg.restartOnChange;
before = cfg.restartOnChange;
script = ''
mkdir -p "$(dirname '${cfg.ipPath}')"
newIp=$(${cfg.ipCmd})
oldIp=$(cat '${cfg.ipPath}' || true)
# systemd path units are triggered on any file write action,
# regardless of content change. so only update the file if our IP *actually* changed
if [ "$newIp" != "$oldIp" ]
then
echo "$newIp" > '${cfg.ipPath}'
echo "WAN ip changed $oldIp -> $newIp"
fi
exit $(test -f '${cfg.ipPath}')
'';
};
systemd.timers.dyn-dns = {
# if anything wants dyn-dns.service, they surely want the timer too.
wantedBy = [ "dyn-dns.service" ];
timerConfig = {
OnUnitActiveSec = cfg.interval;
};
};
systemd.paths.dyn-dns-watcher = {
before = [ "dyn-dns.timer" ];
wantedBy = [ "dyn-dns.timer" ];
pathConfig = {
Unit = "dyn-dns-reactor.service";
PathChanged = [ cfg.ipPath ];
};
};
systemd.services.dyn-dns-reactor = {
description = "react to the system's WAN IP changing";
serviceConfig.Type = "oneshot";
script = if cfg.restartOnChange != [] then ''
${pkgs.systemd}/bin/systemctl restart ${toString cfg.restartOnChange}
'' else "${pkgs.coreutils}/bin/true";
};
};
}

View File

@@ -1,55 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.sane.services.kiwix-serve;
in
{
options = {
sane.services.kiwix-serve = {
enable = mkOption {
default = false;
type = types.bool;
};
package = mkOption {
type = types.package;
default = pkgs.kiwix-tools;
defaultText = literalExpression "pkgs.kiwix-tools";
description = lib.mdDoc ''
The package that provides `bin/kiwix-serve`.
'';
};
port = mkOption {
type = types.port;
default = 80;
description = lib.mdDoc "Port number to listen on.";
};
listenAddress = mkOption {
type = types.nullOr types.str;
default = null;
description = lib.mdDoc ''
IP address to listen on. Listens on all available addresses if unspecified.
'';
};
zimPaths = mkOption {
type = types.nonEmptyListOf (types.either types.str types.path);
description = lib.mdDoc "ZIM file path(s)";
};
};
};
config = mkIf cfg.enable {
systemd.services.kiwix-serve = {
description = "Deliver ZIM file(s) articles via HTTP";
serviceConfig = let
maybeListenAddress = lib.optionals (cfg.listenAddress != null) ["-l" cfg.listenAddress];
args = maybeListenAddress ++ ["-p" cfg.port] ++ cfg.zimPaths;
in {
ExecStart = "${cfg.package}/bin/kiwix-serve ${lib.escapeShellArgs args}";
Type = "simple";
};
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
};
};
}

View File

@@ -14,8 +14,8 @@ in
type = types.bool;
};
sane.services.nixserve.sopsFile = mkOption {
default = ../../secrets/servo.yaml;
type = types.path;
description = "path to file that contains the nix_serv_privkey secret (can be in VCS)";
};
};

View File

@@ -1,167 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.sane.services.trust-dns;
toml = pkgs.formats.toml { };
fmtRecord = proto: rrtype: name: value: "${name}\t${proto}\t${rrtype}\t${value}";
fmtRecordList = proto: rrtype: name: values: concatStringsSep
"\n"
(map (fmtRecord proto rrtype name) values)
;
fmtRecordAttrs = proto: rrtype: rrAttrs:
concatStringsSep
"\n"
(
attrValues (
mapAttrs
(name: fmtRecordList proto rrtype name)
rrAttrs
)
);
fmtIncludes = paths: concatStringsSep
"\n"
(map (path: "$INCLUDE ${path}") paths);
genZone = zcfg: ''
$TTL ${toString zcfg.TTL}
${fmtRecordAttrs "IN" "SOA" zcfg.inet.SOA}
${fmtRecordAttrs "IN" "A" zcfg.inet.A}
${fmtRecordAttrs "IN" "CNAME" zcfg.inet.CNAME}
${fmtRecordAttrs "IN" "MX" zcfg.inet.MX}
${fmtRecordAttrs "IN" "NS" zcfg.inet.NS}
${fmtRecordAttrs "IN" "SRV" zcfg.inet.SRV}
${fmtRecordAttrs "IN" "TXT" zcfg.inet.TXT}
${fmtIncludes zcfg.include}
${zcfg.extraConfig}
'';
configFile = toml.generate "trust-dns.toml" {
listen_addrs_ipv4 = cfg.listenAddrsIPv4;
zones = attrValues (
mapAttrs (zname: zcfg: rec {
zone = if zcfg.name == null then zname else zcfg.name;
zone_type = "Primary";
file = if zcfg.file == null then
pkgs.writeText "${zone}.zone" (genZone zcfg)
else
zcfg.file;
}) cfg.zones
);
};
in
{
options = {
sane.services.trust-dns = {
enable = mkOption {
default = false;
type = types.bool;
};
listenAddrsIPv4 = mkOption {
type = types.listOf types.str;
default = [];
description = "array of ipv4 addresses on which to listen for DNS queries";
};
# reference <nixpkgs:nixos/modules/services/web-servers/nginx/vhost-options.nix>
zones = mkOption {
type = types.attrsOf (types.submodule {
options = {
name = mkOption {
type = types.nullOr types.str;
description = "zone name. defaults to the attribute name in zones";
default = null;
};
TTL = mkOption {
type = types.int;
description = "default TTL";
default = 3600;
};
include = mkOption {
type = types.listOf types.str;
description = "paths of other zone files to $INCLUDE into this one";
default = [];
};
extraConfig = mkOption {
type = types.lines;
description = "extra lines to append to the zone file";
default = "";
};
inet = {
SOA = mkOption {
type = types.attrsOf (types.listOf types.str);
description = "Start of Authority record(s)";
default = {};
};
A = mkOption {
type = types.attrsOf (types.listOf types.str);
description = "IPv4 address record(s)";
default = {};
};
CNAME = mkOption {
type = types.attrsOf (types.listOf types.str);
description = "canonical name record(s)";
default = {};
};
MX = mkOption {
type = types.attrsOf (types.listOf types.str);
description = "mail exchanger record(s)";
default = {};
};
NS = mkOption {
type = types.attrsOf (types.listOf types.str);
description = "name server record(s)";
default = {};
};
SRV = mkOption {
type = types.attrsOf (types.listOf types.str);
description = "service record(s)";
default = {};
};
TXT = mkOption {
type = types.attrsOf (types.listOf types.str);
description = "text record(s)";
default = {};
};
};
file = mkOption {
type = types.nullOr types.str;
default = null;
description = "instead of using the generated zone file, use the specified path";
};
};
});
default = {};
description = "Declarative zone config";
};
generatedZones = mkOption {
type = types.attrsOf types.str;
description = "generated zone text for each zone";
};
};
};
config = mkIf cfg.enable {
sane.services.trust-dns.generatedZones = mapAttrs (zone: zcfg: genZone zcfg) cfg.zones;
networking.firewall.allowedTCPPorts = [ 53 ];
networking.firewall.allowedUDPPorts = [ 53 ];
systemd.services.trust-dns = {
description = "trust-dns DNS server";
serviceConfig = {
ExecStart = ''
${pkgs.trust-dns}/bin/named \
--config ${configFile} \
--zonedir /
'';
Type = "simple";
Restart = "on-failure";
RestartSec = "10s";
# TODO: hardening (like, don't run as root!)
};
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
};
};
}

View File

@@ -23,15 +23,13 @@ in
sane.allocations.greeter-uid = mkId 999;
sane.allocations.greeter-gid = mkId 999;
# new servo users
sane.allocations.freshrss-uid = mkId 2401;
sane.allocations.freshrss-gid = mkId 2401;
sane.allocations.mediawiki-uid = mkId 2402;
sane.allocations.colin-uid = mkId 1000;
sane.allocations.guest-uid = mkId 1100;
# found on all hosts
# found on all machines
sane.allocations.sshd-uid = mkId 2001; # 997
sane.allocations.sshd-gid = mkId 2001; # 997
sane.allocations.polkituser-gid = mkId 2002; # 998
@@ -41,15 +39,15 @@ in
sane.allocations.systemd-oom-uid = mkId 2005;
sane.allocations.systemd-oom-gid = mkId 2005;
# found on graphical hosts
# found on graphical machines
sane.allocations.nm-iodine-uid = mkId 2101; # desko/moby/lappy
# found on desko host
# found on desko machine
sane.allocations.usbmux-uid = mkId 2204;
sane.allocations.usbmux-gid = mkId 2204;
# originally found on moby host
# originally found on moby machine
sane.allocations.avahi-uid = mkId 2304;
sane.allocations.avahi-gid = mkId 2304;
sane.allocations.colord-uid = mkId 2305;

View File

@@ -1,37 +1,20 @@
{ pkgs, ... }:
{
imports = [
./bluetooth.nix
./allocations.nix
./fs.nix
./hardware
./i2p.nix
./machine-id.nix
./home-manager
./home-packages.nix
./net.nix
./secrets.nix
./ssh.nix
./system-packages.nix
./users.nix
./vpn.nix
];
sane.home-manager.enable = true;
sane.nixcache.enable-trusted-keys = true;
sane.packages.enableConsolePkgs = true;
sane.packages.enableSystemPkgs = true;
sane.impermanence.dirs = [
"/var/log"
"/var/backup" # for e.g. postgres dumps
# TODO: move elsewhere
"/var/lib/alsa" # preserve output levels, default devices
"/var/lib/bluetooth" # preserve bluetooth handshakes
"/var/lib/colord" # preserve color calibrations (?)
"/var/lib/machines" # maybe not needed, but would be painful to add a VM and forget.
];
nixpkgs.config.allowUnfree = true;
# time.timeZone = "America/Los_Angeles";
time.timeZone = "Etc/UTC"; # DST is too confusing for me => use a stable timezone
time.timeZone = "America/Los_Angeles";
# allow `nix flake ...` command
nix.extraOptions = ''
@@ -51,9 +34,6 @@
};
};
# disable non-required packages like nano, perl, rsync, strace
environment.defaultPackages = [];
# programs.vim.defaultEditor = true;
environment.variables = {
EDITOR = "vim";
@@ -68,8 +48,16 @@
};
# enable zsh completions
environment.pathsToLink = [ "/share/zsh" ];
environment.systemPackages = with pkgs; [
# required for pam_mount
gocryptfs
];
# link debug symbols into /run/current-system/sw/lib/debug
# hopefully picked up by gdb automatically?
environment.enableDebugInfo = true;
security.pam.mount.enable = true;
# security.pam.mount.debugLevel = 1;
# security.pam.enableSSHAgentAuth = true; # ??
# needed for `allow_other` in e.g. gocryptfs mounts
# or i guess going through mount.fuse sets suid so that's not necessary?
# programs.fuse.userAllowOther = true;
}

View File

@@ -19,17 +19,11 @@ let sshOpts = rec {
optionsRoot = optionsBase ++ [
# we don't transform_symlinks because that breaks the validity of remote /nix stores
"sftp_server=/run/wrappers/bin/sudo\\040/run/current-system/sw/libexec/sftp-server"
"sftp_server=/run/wrappers/bin/sudo\\040${pkgs.openssh}/libexec/sftp-server"
];
};
in
{
environment.pathsToLink = [
# needed to achieve superuser access for user-mounted filesystems (see optionsRoot above)
# we can only link whole directories here, even though we're only interested in pkgs.openssh
"/libexec"
];
fileSystems."/mnt/servo-media-wan" = {
device = "colin@uninsane.org:/var/lib/uninsane/media";
inherit (sshOpts) fsType;

View File

@@ -1,11 +1,9 @@
# Terminal UI mail client
{ config, lib, ... }:
lib.mkIf config.sane.home-manager.enable
{ config, ... }:
{
sops.secrets."aerc_accounts" = {
owner = config.users.users.colin.name;
sopsFile = ../../secrets/universal/aerc_accounts.conf;
sopsFile = ../../../secrets/universal/aerc_accounts.conf;
format = "binary";
};
home-manager.users.colin = let sysconfig = config; in { config, ... }: {

View File

@@ -9,34 +9,38 @@
with lib;
let
cfg = config.sane.home-manager;
# extract package from `sane.packages.enabledUserPkgs`
# extract package from `extraPackages`
pkg-list = pkgspec: builtins.map (e: e.pkg or e) pkgspec;
# extract `dir` from `sane.packages.enabledUserPkgs`
dir-list = pkgspec: builtins.concatLists (builtins.map (e: e.dir or []) pkgspec);
private-list = pkgspec: builtins.concatLists (builtins.map (e: e.private or []) pkgspec);
# extract `dir` from `extraPackages`
dir-list = pkgspec: builtins.concatLists (builtins.map (e: if e ? "dir" then [ e.dir ] else []) pkgspec);
private-list = pkgspec: builtins.concatLists (builtins.map (e: if e ? "private" then [ e.private ] else []) pkgspec);
feeds = import ./feeds.nix { inherit lib; };
in
{
imports = [
./aerc.nix
./firefox.nix
./discord.nix
./git.nix
./kitty.nix
./librewolf.nix
./mpv.nix
./nb.nix
./neovim.nix
./splatmoji.nix
./ssh.nix
./sublime-music.nix
./vlc.nix
./zsh
./zsh.nix
];
options = {
sane.home-manager.enable = mkOption {
default = false;
type = types.bool;
# packages to deploy to the user's home
sane.home-manager.extraPackages = mkOption {
default = [ ];
# each entry can be either a package, or attrs:
# { pkg = package; dir = optional string;
type = types.listOf (types.either types.package types.attrs);
};
# attributes to copy directly to home-manager's `wayland.windowManager` option
sane.home-manager.windowManager = mkOption {
default = {};
@@ -50,7 +54,7 @@ in
};
};
config = lib.mkIf cfg.enable {
config = {
sane.impermanence.home-dirs = [
"archive"
"dev"
@@ -61,7 +65,7 @@ in
"Music"
"Pictures"
"Videos"
] ++ (dir-list config.sane.packages.enabledUserPkgs);
] ++ (dir-list cfg.extraPackages);
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
@@ -75,7 +79,7 @@ in
manual.html.enable = false; # TODO: set to true later (build failure)
manual.manpages.enable = false; # TODO: enable after https://github.com/nix-community/home-manager/issues/3344
home.packages = pkg-list sysconfig.sane.packages.enabledUserPkgs;
home.packages = pkg-list cfg.extraPackages;
wayland.windowManager = cfg.windowManager;
home.stateVersion = "21.11";
@@ -86,7 +90,7 @@ in
initKeyring = {
after = ["writeBoundary"];
before = [];
data = "${../../scripts/init-keyring}";
data = "${../../../scripts/init-keyring}";
};
};
@@ -97,18 +101,18 @@ in
name = path;
value = { source = config.lib.file.mkOutOfStoreSymlink "/home/colin/private/${path}"; };
})
(private-list sysconfig.sane.packages.enabledUserPkgs)
(private-list cfg.extraPackages)
);
in {
# convenience
"knowledge".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/private/knowledge";
"knowledge".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/knowledge";
"nixos".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/nixos";
"Videos/servo".source = config.lib.file.mkOutOfStoreSymlink "/mnt/servo-media/Videos";
"Videos/servo-incomplete".source = config.lib.file.mkOutOfStoreSymlink "/mnt/servo-media/incomplete";
"Music/servo".source = config.lib.file.mkOutOfStoreSymlink "/mnt/servo-media/Music";
# used by password managers, e.g. unix `pass`
".password-store".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/knowledge/secrets/accounts";
".password-store".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/knowledge/secrets/accounts";
} // privates;
# XDG defines things like ~/Desktop, ~/Downloads, etc.
@@ -130,7 +134,7 @@ in
# - `xdg-mime query filetype path/to/thing.ext`
xdg.mimeApps.enable = true;
xdg.mimeApps.defaultApplications = let
www = sysconfig.sane.web-browser.desktop;
www = "librewolf.desktop";
pdf = "org.gnome.Evince.desktop";
md = "obsidian.desktop";
thumb = "org.gnome.gThumb.desktop";
@@ -161,18 +165,6 @@ in
"audio/x-vorbis+ogg" = [ audio ];
};
# libreoffice: disable first-run stuff
xdg.configFile."libreoffice/4/user/registrymodifications.xcu".text = ''
<?xml version="1.0" encoding="UTF-8"?>
<oor:items xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="FirstRun" oor:op="fuse"><value>false</value></prop></item>
<item oor:path="/org.openoffice.Office.Common/Misc"><prop oor:name="ShowTipOfTheDay" oor:op="fuse"><value>false</value></prop></item>
</oor:items>
'';
# <item oor:path="/org.openoffice.Setup/Product"><prop oor:name="LastTimeDonateShown" oor:op="fuse"><value>1667693880</value></prop></item>
# <item oor:path="/org.openoffice.Setup/Product"><prop oor:name="LastTimeGetInvolvedShown" oor:op="fuse"><value>1667693880</value></prop></item>
xdg.configFile."gpodderFeeds.opml".text = with feeds;
feedsToOpml feeds.podcasts;

View File

@@ -0,0 +1,10 @@
{ ... }:
{
# TODO: this should only be enabled on gui devices
# make Discord usable even when client is "outdated"
home-manager.users.colin.xdg.configFile."discord/settings.json".text = ''
{
"SKIP_HOST_UPDATE": true
}
'';
}

View File

@@ -61,12 +61,6 @@ in rec {
(mkPod "https://feeds.feedburner.com/dancarlin/history?format=xml" // rat // infrequent)
## 60 minutes (NB: this features more than *just* audio?)
(mkPod "https://www.cbsnews.com/latest/rss/60-minutes" // pol // infrequent)
## The Verge - Decoder
(mkPod "https://feeds.megaphone.fm/recodedecode" // tech // weekly)
## Matrix (chat) Live
(mkPod "https://feed.podbean.com/matrixlive/feed.xml" // tech // weekly)
## Michael Malice - Your Welcome
(mkPod "https://www.podcastone.com/podcast?categoryID2=2232" // pol // weekly)
];
texts = [
@@ -100,8 +94,6 @@ in rec {
(mkText "https://bitbashing.io/feed.xml" // tech // infrequent)
(mkText "https://idiomdrottning.org/feed.xml" // uncat // daily)
(mkText "https://anish.lakhwara.com/home.html" // tech // weekly)
(mkText "https://www.jefftk.com/news.rss" // tech // daily)
(mkText "https://pomeroyb.com/feed.xml" // tech // infrequent)
# (TECH; POL) COMMENTATORS
(mkSubstack "edwardsnowden" // pol // infrequent)
@@ -119,7 +111,6 @@ in rec {
(mkText "https://blog.dshr.org/rss.xml" // pol // weekly)
## Matt Levine
(mkText "https://www.bloomberg.com/opinion/authors/ARbTQlRLRjE/matthew-s-levine.rss" // pol // weekly)
(mkText "https://stpeter.im/atom.xml" // pol // weekly)
# RATIONALITY/PHILOSOPHY/ETC
(mkSubstack "samkriss" // humor // infrequent)
@@ -139,11 +130,8 @@ in rec {
## Sean Carroll
(mkText "https://www.preposterousuniverse.com/rss" // rat // infrequent)
## mostly dating topics. not advice, or humor, but looking through a social lens
(mkText "https://putanumonit.com/feed" // rat // infrequent)
# CODE
# (mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
(mkText "https://github.com/Kaiteki-Fedi/Kaiteki/commits/master.atom" // tech // infrequent)
];
images = [

View File

@@ -1,6 +1,4 @@
{ config, lib, pkgs, ... }:
lib.mkIf config.sane.home-manager.enable
{ pkgs, ... }:
{
home-manager.users.colin.programs.git = {
enable = true;

View File

@@ -1,6 +1,4 @@
{ config, lib, ... }:
lib.mkIf config.sane.home-manager.enable
{ ... }:
{
home-manager.users.colin.programs.kitty = {
enable = true;

View File

@@ -0,0 +1,102 @@
# common settings to toggle (at runtime, in about:config):
# > security.ssl.require_safe_negotiation
# librewolf is a forked firefox which patches firefox to allow more things
# (like default search engines) to be configurable at runtime.
# many of the settings below won't have effect without those patches.
# see: https://gitlab.com/librewolf-community/settings/-/blob/master/distribution/policies.json
{ config, lib, pkgs, ...}:
let
package = pkgs.wrapFirefox pkgs.librewolf-unwrapped {
# inherit the default librewolf.cfg
# it can be further customized via ~/.librewolf/librewolf.overrides.cfg
inherit (pkgs.librewolf-unwrapped) extraPrefsFiles;
libName = "librewolf";
extraNativeMessagingHosts = [ pkgs.browserpass ];
# extraNativeMessagingHosts = [ pkgs.gopass-native-messaging-host ];
extraPolicies = {
NoDefaultBookmarks = true;
SearchEngines = {
Default = "DuckDuckGo";
};
AppUpdateURL = "https://localhost";
DisableAppUpdate = true;
OverrideFirstRunPage = "";
OverridePostUpdatePage = "";
DisableSystemAddonUpdate = true;
DisableFirefoxStudies = true;
DisableTelemetry = true;
DisableFeedbackCommands = true;
DisablePocket = true;
DisableSetDesktopBackground = false;
Extensions = {
Install = let
addon = pkg: addonId: "${pkg}/share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}/${addonId}.xpi";
in with pkgs.firefox-addons; [
# the extension key is found by building and checking the output: `nix build '.#rycee.firefox-addons.<foo>'`
# or by taking the `addonId` input to `buildFirefoxXpiAddon` in rycee's firefox-addons repo
(addon ublock-origin "uBlock0@raymondhill.net")
(addon sponsorblock "sponsorBlocker@ajay.app")
(addon bypass-paywalls-clean "{d133e097-46d9-4ecc-9903-fa6a722a6e0e}")
(addon sidebery "{3c078156-979c-498b-8990-85f7987dd929}")
(addon browserpass "browserpass@maximbaz.com")
(addon metamask "webextension@metamask.io")
# extensions can alternatively be installed by URL, in which case they are fetched (and cached) on first run.
# "https://addons.mozilla.org/firefox/downloads/latest/gopass-bridge/latest.xpi"
];
# remove many default search providers
Uninstall = [
"google@search.mozilla.org"
"bing@search.mozilla.org"
"amazondotcom@search.mozilla.org"
"ebay@search.mozilla.org"
"twitter@search.mozilla.org"
];
};
# XXX doesn't seem to have any effect...
# docs: https://github.com/mozilla/policy-templates#homepage
# Homepage = {
# HomepageURL = "https://uninsane.org/";
# StartPage = "homepage";
# };
# NewTabPage = true;
};
};
in
{
# XXX: although home-manager calls this option `firefox`, we can use other browsers and it still mostly works.
home-manager.users.colin = lib.mkIf (config.sane.gui.enable) {
programs.firefox = {
enable = true;
inherit package;
};
# uBlock filter list configuration.
# specifically, enable the GDPR cookie prompt blocker.
# data.toOverwrite.filterLists is additive (i.e. it supplements the default filters)
# this configuration method is documented here:
# - <https://github.com/gorhill/uBlock/issues/2986#issuecomment-364035002>
# the specific attribute path is found via scraping ublock code here:
# - <https://github.com/gorhill/uBlock/blob/master/src/js/storage.js>
# - <https://github.com/gorhill/uBlock/blob/master/assets/assets.json>
home.file.".librewolf/managed-storage/uBlock0@raymondhill.net.json".text = ''
{
"name": "uBlock0@raymondhill.net",
"description": "ignored",
"type": "storage",
"data": {
"toOverwrite": "{\"filterLists\": [\"fanboy-cookiemonster\"]}"
}
}
'';
home.file.".librewolf/librewolf.overrides.cfg".text = ''
// if we can't query the revocation status of a SSL cert because the issuer is offline,
// treat it as unrevoked.
// see: <https://librewolf.net/docs/faq/#im-getting-sec_error_ocsp_server_error-what-can-i-do>
defaultPref("security.OCSP.require", false);
'';
};
}

View File

@@ -1,6 +1,4 @@
{ config, lib, ... }:
lib.mkIf config.sane.home-manager.enable
{ ... }:
{
home-manager.users.colin.programs.mpv = {
enable = true;

View File

@@ -8,16 +8,13 @@
# it offers a primitive web-server
# and it offers some CLI query tools
{ config, lib, pkgs, ... }:
# lib.mkIf config.sane.home-manager.enable
lib.mkIf false # XXX disabled!
{ lib, pkgs, ... }: lib.mkIf false # XXX disabled!
{
sane.packages.extraUserPkgs = [ pkgs.nb ];
sane.home-manager.extraPackages = [ pkgs.nb ];
home-manager.users.colin = { config, ... }: {
# nb markdown/personal knowledge manager
home.file.".nb/knowledge".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/knowledge";
home.file.".nb/knowledge".source = config.lib.file.mkOutOfStoreSymlink "/home/colin/dev/knowledge";
home.file.".nb/.current".text = "knowledge";
home.file.".nbrc".text = ''
# manage with `nb settings`

View File

@@ -1,6 +1,4 @@
{ config, lib, pkgs, ... }:
lib.mkIf config.sane.home-manager.enable
{ pkgs, ... }:
{
sane.impermanence.home-dirs = [ ".cache/vim-swap" ];
@@ -33,12 +31,21 @@ lib.mkIf config.sane.home-manager.enable
" autocmd Syntax tex set conceallevel=2
'';
})
# nabla renders inline math in any document, but it's buggy.
# https://github.com/jbyuki/nabla.nvim
# ({
# plugin = pkgs.nabla;
# type = "lua";
# config = ''
# require'nabla'.enable_virt()
# '';
# })
# treesitter syntax highlighting: https://nixos.wiki/wiki/Tree_sitters
# docs: https://github.com/nvim-treesitter/nvim-treesitter
# config taken from: https://github.com/i077/system/blob/master/modules/home/neovim/default.nix
# this is required for tree-sitter to even highlight
({
plugin = nvim-treesitter.withAllGrammars;
plugin = (nvim-treesitter.withPlugins (_: pkgs.tree-sitter.allGrammars));
type = "lua";
config = ''
require'nvim-treesitter.configs'.setup {

View File

@@ -1,6 +1,4 @@
{ config, lib, pkgs, ... }:
lib.mkIf config.sane.home-manager.enable
{ config, pkgs, ... }:
{
home-manager.users.colin = let
host = config.networking.hostName;

View File

@@ -1,11 +1,9 @@
{ config, lib, ... }:
lib.mkIf config.sane.home-manager.enable
{ config, ... }:
{
# TODO: this should only be shipped on gui platforms
sops.secrets."sublime_music_config" = {
owner = config.users.users.colin.name;
sopsFile = ../../secrets/universal/sublime_music_config.json.bin;
sopsFile = ../../../secrets/universal/sublime_music_config.json.bin;
format = "binary";
};
home-manager.users.colin = let sysconfig = config; in { config, ... }: {

View File

@@ -1,6 +1,4 @@
{ config, lib, ... }:
lib.mkIf config.sane.home-manager.enable
{ lib, ... }:
{
home-manager.users.colin.xdg.configFile."vlc/vlcrc".text =
let

View File

@@ -0,0 +1,61 @@
{ ... }:
{
# we don't need to full zsh dir -- just the history file --
# but zsh will sometimes backup the history file and we get fewer errors if we do proper mounts instead of symlinks.
sane.impermanence.home-dirs = [ ".local/share/zsh" ];
home-manager.users.colin.programs.zsh = {
enable = true;
enableSyntaxHighlighting = true;
enableVteIntegration = true;
history.ignorePatterns = [ "rm *" ];
dotDir = ".config/zsh";
history.path = "/home/colin/.local/share/zsh/history";
initExtraBeforeCompInit = ''
# p10k instant prompt
# run p10k configure to configure, but it can't write out its file :-(
POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true
'';
initExtra = ''
# zmv is a way to do rich moves/renames, with pattern matching/substitution.
# see for an example: <https://filipe.kiss.ink/zmv-zsh-rename/>
autoload -Uz zmv
# disable `rm *` confirmations
setopt rmstarsilent
function nd() {
mkdir -p "$1";
pushd "$1";
}
'';
# prezto = oh-my-zsh fork; controls prompt, auto-completion, etc.
# see: https://github.com/sorin-ionescu/prezto
prezto = {
enable = true;
pmodules = [
"environment"
"terminal"
"editor"
"history"
"directory"
"spectrum"
"utility"
"completion"
"prompt"
"git"
];
prompt.theme = "powerlevel10k";
utility.safeOps = false; # disable `mv` confirmation (and supposedly `rm`, too)
};
};
home-manager.users.colin.home.shellAliases = {
":q" = "exit";
# common typos
"cd.." = "cd ..";
"cd../" = "cd ../";
};
}

View File

@@ -0,0 +1,198 @@
{ config, lib, pkgs, ... }:
with lib;
with pkgs;
let
cfg = config.sane.home-packages;
universalPkgs = [
backblaze-b2
cdrtools
duplicity
gnupg
gocryptfs
gopass
gopass-jsonapi
ifuse
ipfs
libimobiledevice
libsecret # for managing user keyrings
lm_sensors # for sensors-detect
lshw
ffmpeg
networkmanager
nixpkgs-review
# nixos-generators
# nettools
nmon
oathToolkit # for oathtool
# ponymix
pulsemixer
python3
# python3Packages.eyeD3 # music tagging
sane-scripts
sequoia
snapper
sops
speedtest-cli
sqlite # to debug sqlite3 databases
ssh-to-age
sudo
# tageditor # music tagging
unar
visidata
w3m
wireguard-tools
# youtube-dl
yt-dlp
];
guiPkgs = [
# GUI only
aerc # email client
audacity
celluloid # mpv frontend
chromium
clinfo
electrum
# creds/session keys, etc
{ pkg = element-desktop; private = ".config/Element"; }
emote # TODO: package [smile](https://github.com/mijorus/smile) for probably a better mobile experience.
evince # works on phosh
# { pkg = fluffychat-moby; dir = ".local/share/chat.fluffy.fluffychat"; } # TODO: ship normal fluffychat on non-moby?
foliate
font-manager
# XXX by default fractal stores its state in ~/.local/share/<UUID>.
# after logging in, manually change ~/.local/share/keyrings/... to point it to some predictable subdir.
# then reboot (so that libsecret daemon re-loads the keyring...?)
{ pkg = fractal-next; private = ".local/share/fractal"; }
gimp # broken on phosh
gnome.cheese
gnome.dconf-editor
gnome-feeds # RSS reader (with claimed mobile support)
gnome.file-roller
gnome.gnome-disk-utility
gnome.gnome-maps # works on phosh
gnome.nautilus
# gnome-podcasts
gnome.gnome-system-monitor
gnome.gnome-terminal # works on phosh
gnome.gnome-weather
{ pkg = gpodder-configured; dir = "gPodder/Downloads"; }
gthumb
handbrake
inkscape
kid3 # audio tagging
krita
libreoffice-fresh # XXX colin: maybe don't want this on mobile
lollypop
mesa-demos
{ pkg = mpv; dir = ".config/mpv/watch_later"; }
networkmanagerapplet
# not strictly necessary, but allows caching articles; offline use, etc.
{ pkg = newsflash; dir = ".local/share/news-flash"; }
# settings (electron app). TODO: can i manage these settings with home-manager?
{ pkg = obsidian; dir = ".config/obsidian"; }
pavucontrol
picard # music tagging
playerctl
soundconverter
# sublime music persists any downloaded albums here.
# it doesn't obey a conventional ~/Music/{Artist}/{Album}/{Track} notation, so no symlinking
# config (e.g. server connection details) is persisted in ~/.config/sublime-music/config.json
# possible to pass config as a CLI arg (sublime-music -c config.json)
{ pkg = sublime-music; dir = ".local/share/sublime-music"; }
tdesktop # broken on phosh
tokodon
# vlc remembers play position in ~/.config/vlc/vlc-qt-interface.conf
{ pkg = vlc; dir = ".config/vlc"; }
whalebird # pleroma client. input is broken on phosh
xdg-utils # for xdg-open
xterm # broken on phosh
]
++ (if pkgs.system == "x86_64-linux" then
[
# x86_64 only
# creds, but also 200 MB of node modules, etc
(let discord = (pkgs.discord.override {
# XXX 2022-07-31: fix to allow links to open in default web-browser:
# https://github.com/NixOS/nixpkgs/issues/78961
nss = pkgs.nss_latest;
}); in { pkg = discord; dir = ".config/discord"; })
# kaiteki # Pleroma client
# gnome.zenity # for kaiteki (it will use qarma, kdialog, or zenity)
logseq
losslesscut-bin
makemkv
# actual monero blockchain (not wallet/etc; safe to delete, just slow to regenerate)
{ pkg = monero-gui; dir = ".bitmonero"; }
# creds, media
{ pkg = signal-desktop; dir = ".config/Signal"; }
# creds. TODO: can i manage this with home-manager?
{ pkg = spotify; dir = ".config/spotify"; }
# hardenedMalloc solves a crash at startup
(tor-browser-bundle-bin.override { useHardenedMalloc = false; })
# zcash coins. safe to delete, just slow to regenerate (10-60 minutes)
{ pkg = zecwallet-lite; dir = ".zcash"; }
] else []);
# useful devtools:
devPkgs = [
bison
dtc
flex
gcc
gdb
# gcc-arm-embedded
# gcc_multi
gnumake
mix2nix
rustup
swig
];
in
{
options = {
sane.home-packages.enableGuiPkgs = mkOption {
default = false;
type = types.bool;
};
sane.home-packages.enableDevPkgs = mkOption {
description = ''
enable packages that are useful for building other software by hand.
you should prefer to keep this disabled except when prototyping, e.g. packaging new software.
'';
default = false;
type = types.bool;
};
};
config = {
sane.home-manager.extraPackages = universalPkgs
++ (if cfg.enableGuiPkgs then guiPkgs else [])
++ (if cfg.enableDevPkgs then devPkgs else []);
};
}

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