Compare commits

..

1 Commits

Author SHA1 Message Date
88ad6cd610 be.lt: show battery level in tmux
All checks were successful
flake / flake (push) Successful in 1m13s
2024-07-17 16:45:46 +01:00
135 changed files with 988 additions and 3008 deletions

View File

@ -11,9 +11,12 @@ jobs:
flake:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: DeterminateSystems/nix-installer-action@b92f66560d6f97d6576405a7bae901ab57e72b6a # v15
- uses: DeterminateSystems/magic-nix-cache-action@87b14cf437d03d37989d87f0fa5ce4f5dc1a330b # v8
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
- name: Prepare for Nix installation
run: |
apt-get update
apt-get install -y sudo
- uses: cachix/install-nix-action@ba0dd844c9180cbf77aa72a116d6fbc515d0e87b # v27
- name: lint
run: |
nix fmt

View File

@ -1,27 +0,0 @@
{ config, pkgs, ... }:
{
config = {
system.stateVersion = 4;
networking.hostName = "jakehillion-mba-m2-15";
nix = {
useDaemon = true;
};
programs.zsh.enable = true;
security.pam.enableSudoTouchIdAuth = true;
environment.systemPackages = with pkgs; [
fd
htop
mosh
neovim
nix
ripgrep
sapling
];
};
}

View File

@ -2,9 +2,7 @@
"nodes": {
"agenix": {
"inputs": {
"darwin": [
"darwin"
],
"darwin": "darwin",
"home-manager": [
"home-manager"
],
@ -14,11 +12,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1723293904,
"narHash": "sha256-b+uqzj+Wa6xgMS9aNbX4I+sXeb5biPDi39VgvSFqFvU=",
"lastModified": 1720546205,
"narHash": "sha256-boCXsjYVxDviyzoEyAk624600f3ZBo/DKtUdvMTpbGY=",
"owner": "ryantm",
"repo": "agenix",
"rev": "f6291c5935fdc4e0bef208cfc0dcab7e3f7a1c41",
"rev": "de96bd907d5fbc3b14fc33ad37d1b9a3cb15edc6",
"type": "github"
},
"original": {
@ -30,53 +28,35 @@
"darwin": {
"inputs": {
"nixpkgs": [
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1731153869,
"narHash": "sha256-3Ftf9oqOypcEyyrWJ0baVkRpvQqroK/SVBFLvU3nPuc=",
"lastModified": 1700795494,
"narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "5c74ab862c8070cbf6400128a1b56abb213656da",
"rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1731060864,
"narHash": "sha256-aYE7oAYZ+gPU1mPNhM0JwLAQNgjf0/JK1BF1ln2KBgk=",
"owner": "nix-community",
"repo": "disko",
"rev": "5e40e02978e3bd63c2a6a9fa6fa8ba0e310e747f",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
@ -92,11 +72,11 @@
]
},
"locked": {
"lastModified": 1726989464,
"narHash": "sha256-Vl+WVTJwutXkimwGprnEtXc/s/s8sMuXzqXaspIGlwM=",
"lastModified": 1720042825,
"narHash": "sha256-A0vrUB6x82/jvf17qPCpxaM+ulJnD8YZwH9Ci0BsAzE=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "2f23fa308a7c067e52dfcc30a0758f47043ec176",
"rev": "e1391fb22e18a36f57e6999c7a9f966dc80ac073",
"type": "github"
},
"original": {
@ -113,11 +93,11 @@
]
},
"locked": {
"lastModified": 1730837930,
"narHash": "sha256-0kZL4m+bKBJUBQse0HanewWO0g8hDdCvBhudzxgehqc=",
"lastModified": 1721135958,
"narHash": "sha256-H548rpPMsn25LDKn1PCFmPxmWlClJJGnvdzImHkqjuY=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "2f607e07f3ac7e53541120536708e824acccfaa8",
"rev": "afd2021bedff2de92dfce0e257a3d03ae65c603d",
"type": "github"
},
"original": {
@ -128,11 +108,11 @@
},
"impermanence": {
"locked": {
"lastModified": 1730403150,
"narHash": "sha256-W1FH5aJ/GpRCOA7DXT/sJHFpa5r8sq2qAUncWwRZ3Gg=",
"lastModified": 1719091691,
"narHash": "sha256-AxaLX5cBEcGtE02PeGsfscSb/fWMnyS7zMWBXQWDKbE=",
"owner": "nix-community",
"repo": "impermanence",
"rev": "0d09341beeaa2367bac5d718df1404bf2ce45e6f",
"rev": "23c1f06316b67cb5dabdfe2973da3785cfe9c34a",
"type": "github"
},
"original": {
@ -144,11 +124,11 @@
},
"nixos-hardware": {
"locked": {
"lastModified": 1730919458,
"narHash": "sha256-yMO0T0QJlmT/x4HEyvrCyigGrdYfIXX3e5gWqB64wLg=",
"lastModified": 1720737798,
"narHash": "sha256-G/OtEAts7ZUvW5lrGMXSb8HqRp2Jr9I7reBuvCOL54w=",
"owner": "nixos",
"repo": "nixos-hardware",
"rev": "e1cc1f6483393634aee94514186d21a4871e78d7",
"rev": "c5013aa7ce2c7ec90acee5d965d950c8348db751",
"type": "github"
},
"original": {
@ -159,11 +139,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1730963269,
"narHash": "sha256-rz30HrFYCHiWEBCKHMffHbMdWJ35hEkcRVU0h7ms3x0=",
"lastModified": 1720954236,
"narHash": "sha256-1mEKHp4m9brvfQ0rjCca8P1WHpymK3TOr3v34ydv9bs=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "83fb6c028368e465cd19bb127b86f971a5e41ebc",
"rev": "53e81e790209e41f0c1efa9ff26ff2fd7ab35e27",
"type": "github"
},
"original": {
@ -175,22 +155,23 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1730867498,
"narHash": "sha256-Ce3a1w7Qf+UEPjVJcXxeSiWyPMngqf1M2EIsmqiluQw=",
"rev": "9240e11a83307a6e8cf2254340782cba4aa782fd",
"type": "tarball",
"url": "https://gitea.hillion.co.uk/api/v1/repos/JakeHillion/nixpkgs/archive/9240e11a83307a6e8cf2254340782cba4aa782fd.tar.gz"
"lastModified": 1720957393,
"narHash": "sha256-oedh2RwpjEa+TNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "693bc46d169f5af9c992095736e82c3488bf7dbb",
"type": "github"
},
"original": {
"type": "tarball",
"url": "https://gitea.hillion.co.uk/JakeHillion/nixpkgs/archive/nixos-unstable.tar.gz"
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"agenix": "agenix",
"darwin": "darwin",
"disko": "disko",
"flake-utils": "flake-utils",
"home-manager": "home-manager",
"home-manager-unstable": "home-manager-unstable",

124
flake.nix
View File

@ -1,18 +1,14 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
nixpkgs-unstable.url = "https://gitea.hillion.co.uk/JakeHillion/nixpkgs/archive/nixos-unstable.tar.gz";
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
nixos-hardware.url = "github:nixos/nixos-hardware";
flake-utils.url = "github:numtide/flake-utils";
darwin.url = "github:lnl7/nix-darwin";
darwin.inputs.nixpkgs.follows = "nixpkgs";
agenix.url = "github:ryantm/agenix";
agenix.inputs.nixpkgs.follows = "nixpkgs";
agenix.inputs.darwin.follows = "darwin";
agenix.inputs.home-manager.follows = "home-manager";
home-manager.url = "github:nix-community/home-manager/release-24.05";
@ -21,89 +17,51 @@
home-manager-unstable.inputs.nixpkgs.follows = "nixpkgs-unstable";
impermanence.url = "github:nix-community/impermanence/master";
disko.url = "github:nix-community/disko";
disko.inputs.nixpkgs.follows = "nixpkgs";
};
description = "Hillion Nix flake";
outputs =
{ self
, agenix
, darwin
, disko
, flake-utils
, home-manager
, home-manager-unstable
, impermanence
, nixos-hardware
, nixpkgs
, nixpkgs-unstable
, ...
}@inputs:
let
getSystemOverlays = system: nixpkgsConfig: [
(final: prev: {
unstable = nixpkgs-unstable.legacyPackages.${prev.system};
outputs = { self, nixpkgs, nixpkgs-unstable, nixos-hardware, flake-utils, agenix, home-manager, home-manager-unstable, impermanence, ... }@inputs: {
nixosConfigurations =
let
fqdns = builtins.attrNames (builtins.readDir ./hosts);
getSystemOverlays = system: nixpkgsConfig: [
(final: prev: {
"storj" = final.callPackage ./pkgs/storj.nix { };
})
];
mkHost = fqdn:
let
system = builtins.readFile ./hosts/${fqdn}/system;
func = if builtins.pathExists ./hosts/${fqdn}/unstable then nixpkgs-unstable.lib.nixosSystem else nixpkgs.lib.nixosSystem;
home-manager-pick = if builtins.pathExists ./hosts/${fqdn}/unstable then home-manager-unstable else home-manager;
in
func {
inherit system;
specialArgs = inputs;
modules = [
./hosts/${fqdn}/default.nix
./modules/default.nix
"storj" = final.callPackage ./pkgs/storj.nix { };
})
];
in
{
nixosConfigurations =
let
fqdns = builtins.attrNames (builtins.readDir ./hosts);
mkHost = fqdn:
let
system = builtins.readFile ./hosts/${fqdn}/system;
func = if builtins.pathExists ./hosts/${fqdn}/unstable then nixpkgs-unstable.lib.nixosSystem else nixpkgs.lib.nixosSystem;
home-manager-pick = if builtins.pathExists ./hosts/${fqdn}/unstable then home-manager-unstable else home-manager;
in
func {
inherit system;
specialArgs = inputs;
modules = [
./hosts/${fqdn}/default.nix
./modules/default.nix
agenix.nixosModules.default
impermanence.nixosModules.impermanence
agenix.nixosModules.default
impermanence.nixosModules.impermanence
disko.nixosModules.disko
home-manager-pick.nixosModules.default
{
home-manager.sharedModules = [
impermanence.nixosModules.home-manager.impermanence
];
}
home-manager-pick.nixosModules.default
{
home-manager.sharedModules = [
impermanence.nixosModules.home-manager.impermanence
];
}
({ config, ... }: {
system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;
nixpkgs.overlays = getSystemOverlays config.nixpkgs.hostPlatform.system config.nixpkgs.config;
})
];
};
in
nixpkgs.lib.genAttrs fqdns mkHost;
darwinConfigurations = {
jakehillion-mba-m2-15 = darwin.lib.darwinSystem {
system = "aarch64-darwin";
specialArgs = inputs;
modules = [
./darwin/jakehillion-mba-m2-15/configuration.nix
({ config, ... }: {
nixpkgs.overlays = getSystemOverlays "aarch64-darwin" config.nixpkgs.config;
})
];
};
};
} // flake-utils.lib.eachDefaultSystem (system: {
formatter = nixpkgs.legacyPackages.${system}.nixpkgs-fmt;
});
({ config, ... }: {
system.configurationRevision = nixpkgs.lib.mkIf (self ? rev) self.rev;
nixpkgs.overlays = getSystemOverlays config.nixpkgs.hostPlatform.system config.nixpkgs.config;
})
];
};
in
nixpkgs.lib.genAttrs fqdns mkHost;
} // flake-utils.lib.eachDefaultSystem (system: {
formatter = nixpkgs.legacyPackages.${system}.nixpkgs-fmt;
});
}

View File

@ -15,6 +15,7 @@
boot.loader.efi.canTouchEfiVariables = true;
custom.defaults = true;
custom.laptop = true;
## Impermanence
custom.impermanence = {
@ -24,17 +25,6 @@
];
};
## WiFi
age.secrets."wifi/be.lt.ts.hillion.co.uk".file = ../../secrets/wifi/be.lt.ts.hillion.co.uk.age;
networking.wireless = {
enable = true;
environmentFile = config.age.secrets."wifi/be.lt.ts.hillion.co.uk".path;
networks = {
"Hillion WPA3 Network".psk = "@HILLION_WPA3_NETWORK_PSK@";
};
};
## Desktop
custom.users.jake.password = true;
custom.desktop.awesome.enable = true;

View File

@ -34,27 +34,14 @@
### Explicitly use the latest kernel at time of writing because the LTS
### kernels available in NixOS do not seem to support this server's very
### modern hardware.
### custom.sched_ext.enable implies >=6.12, if this is removed the kernel may need to be pinned again. >=6.10 seems good.
custom.sched_ext.enable = true;
boot.kernelPackages = pkgs.linuxPackages_6_9;
## Enable btrfs compression
fileSystems."/data".options = [ "compress=zstd" ];
fileSystems."/nix".options = [ "compress=zstd" ];
## Impermanence
custom.impermanence = {
enable = true;
cache.enable = true;
userExtraFiles.jake = [
".ssh/id_ecdsa"
".ssh/id_rsa"
];
};
boot.initrd.postDeviceCommands = lib.mkAfter ''
btrfs subvolume delete /cache/system
btrfs subvolume snapshot /cache/empty_snapshot /cache/system
'';
custom.impermanence.enable = true;
## Custom Services
custom = {
@ -88,42 +75,6 @@
fileSystems = [ "/data" ];
};
## Resilio
custom.resilio = {
enable = true;
folders =
let
folderNames = [
"dad"
"joseph"
"projects"
"resources"
"sync"
];
mkFolder = name: {
name = name;
secret = {
name = "resilio/plain/${name}";
file = ../../secrets/resilio/plain/${name}.age;
};
};
in
builtins.map (mkFolder) folderNames;
};
services.resilio.directoryRoot = "/data/sync";
## General usability
### Make podman available for dev tools such as act
virtualisation = {
containers.enable = true;
podman = {
enable = true;
dockerCompat = true;
dockerSocket.enable = true;
};
};
users.users.jake.extraGroups = [ "podman" ];
## Networking
boot.kernel.sysctl = {
"net.ipv4.ip_forward" = true;

View File

@ -18,7 +18,7 @@
{
device = "tmpfs";
fsType = "tmpfs";
options = [ "mode=0755" "size=100%" ];
options = [ "mode=0755" ];
};
fileSystems."/boot" =
@ -35,13 +35,6 @@
options = [ "subvol=data" ];
};
fileSystems."/cache" =
{
device = "/dev/disk/by-uuid/9aebe351-156a-4aa0-9a97-f09b01ac23ad";
fsType = "btrfs";
options = [ "subvol=cache" ];
};
fileSystems."/nix" =
{
device = "/dev/disk/by-uuid/9aebe351-156a-4aa0-9a97-f09b01ac23ad";

View File

@ -1,7 +0,0 @@
# gendry.jakehillion-terminals.ts.hillion.co.uk
Additional installation step for Clevis/Tang:
$ echo -n $DISK_ENCRYPTION_PASSWORD | clevis encrypt sss "$(cat /etc/nixos/hosts/gendry.jakehillion-terminals.ts.hillion.co.uk/clevis_config.json)" >/mnt/data/disk_encryption.jwe
$ sudo chown root:root /mnt/data/disk_encryption.jwe
$ sudo chmod 0400 /mnt/data/disk_encryption.jwe

View File

@ -15,24 +15,8 @@
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
];
boot.initrd = {
availableKernelModules = [ "r8169" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices."root".secretFile = "/data/disk_encryption.jwe";
};
};
custom.defaults = true;
## Custom scheduler
custom.sched_ext.enable = true;
## Impermanence
custom.impermanence = {
enable = true;
@ -45,13 +29,6 @@
];
};
## Enable ZRAM swap to help with root on tmpfs
zramSwap = {
enable = true;
memoryPercent = 200;
algorithm = "zstd";
};
## Desktop
custom.users.jake.password = true;
custom.desktop.awesome.enable = true;
@ -59,7 +36,9 @@
## Resilio
custom.resilio.enable = true;
services.resilio.deviceName = "gendry.jakehillion-terminals";
services.resilio.directoryRoot = "/data/sync";
services.resilio.storagePath = "/data/sync/.sync";
custom.resilio.folders =
let

View File

@ -1,75 +0,0 @@
{ config, pkgs, lib, ... }:
{
imports = [
./disko.nix
./hardware-configuration.nix
];
config = {
system.stateVersion = "24.05";
networking.hostName = "merlin";
networking.domain = "rig.ts.hillion.co.uk";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
# zswap
"zswap.enabled=1"
"zswap.compressor=zstd"
"zswap.max_pool_percent=20"
];
boot.initrd = {
availableKernelModules = [ "igc" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices = {
"disk0-crypt".secretFile = "/data/disk_encryption.jwe";
};
};
};
boot.kernelPackages = pkgs.linuxPackages_latest;
custom.defaults = true;
custom.locations.autoServe = true;
custom.impermanence.enable = true;
custom.users.jake.password = true;
security.sudo.wheelNeedsPassword = lib.mkForce true;
# Networking
networking = {
interfaces.enp171s0.name = "eth0";
interfaces.enp172s0.name = "eth1";
};
networking.nameservers = lib.mkForce [ ]; # Trust the DHCP nameservers
networking.firewall = {
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = lib.mkForce [
22 # SSH
];
allowedUDPPorts = lib.mkForce [ ];
interfaces = {
eth0 = {
allowedTCPPorts = lib.mkForce [ ];
allowedUDPPorts = lib.mkForce [ ];
};
};
};
## Tailscale
age.secrets."tailscale/merlin.rig.ts.hillion.co.uk".file = ../../secrets/tailscale/merlin.rig.ts.hillion.co.uk.age;
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/merlin.rig.ts.hillion.co.uk".path;
};
};
}

View File

@ -1,70 +0,0 @@
{
disko.devices = {
disk = {
disk0 = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
disk0-crypt = {
size = "100%";
content = {
type = "luks";
name = "disk0-crypt";
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
subvolumes = {
"/data" = {
mountpoint = "/data";
mountOptions = [ "compress=zstd" "ssd" ];
};
"/nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "ssd" ];
};
};
};
};
};
swap = {
size = "64G";
content = {
type = "swap";
randomEncryption = true;
discardPolicy = "both";
};
};
};
};
};
};
nodev = {
"/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"size=100%"
];
};
};
};
}

View File

@ -1,28 +0,0 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "thunderbolt" "nvme" "usbhid" "usb_storage" "sd_mod" "rtsx_pci_sdmmc" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp171s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp172s0.useDHCP = lib.mkDefault true;
# networking.interfaces.wlp173s0f0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@ -23,6 +23,12 @@
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/microserver.home.ts.hillion.co.uk".path;
useRoutingFeatures = "server";
extraUpFlags = [
"--advertise-routes"
"10.64.50.0/24,10.239.19.0/24"
"--advertise-exit-node"
];
};
## Enable IoT VLAN
@ -37,6 +43,11 @@
bluetooth.enable = true;
};
## Enable IP forwarding for Tailscale
boot.kernel.sysctl = {
"net.ipv4.ip_forward" = true;
};
## Run a persistent iperf3 server
services.iperf3.enable = true;
services.iperf3.openFirewall = true;
@ -45,9 +56,11 @@
networking.firewall.interfaces = {
"eth0" = {
allowedUDPPorts = [
5353 # HomeKit
];
allowedTCPPorts = [
7654 # Tang
21063 # HomeKit
];
};
};

View File

@ -1,7 +0,0 @@
# phoenix.st.ts.hillion.co.uk
Additional installation step for Clevis/Tang:
$ echo -n $DISK_ENCRYPTION_PASSWORD | clevis encrypt sss "$(cat /etc/nixos/hosts/phoenix.st.ts.hillion.co.uk/clevis_config.json)" >/mnt/data/disk_encryption.jwe
$ sudo chown root:root /mnt/data/disk_encryption.jwe
$ sudo chmod 0400 /mnt/data/disk_encryption.jwe

View File

@ -1,14 +0,0 @@
{
"t": 1,
"pins": {
"tang": [
{
"url": "http://10.64.50.21:7654"
},
{
"url": "http://10.64.50.25:7654"
}
]
}
}

View File

@ -1,161 +0,0 @@
{ config, pkgs, lib, ... }:
let
zpool_name = "practical-defiant-coffee";
in
{
imports = [
./disko.nix
./hardware-configuration.nix
];
config = {
system.stateVersion = "24.05";
networking.hostName = "phoenix";
networking.domain = "st.ts.hillion.co.uk";
networking.hostId = "4d7241e9";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
"zfs.zfs_arc_max=34359738368"
# zswap
"zswap.enabled=1"
"zswap.compressor=zstd"
"zswap.max_pool_percent=20"
];
boot.initrd = {
availableKernelModules = [ "igc" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices = {
"disk0-crypt".secretFile = "/data/disk_encryption.jwe";
"disk1-crypt".secretFile = "/data/disk_encryption.jwe";
};
};
};
custom.defaults = true;
custom.locations.autoServe = true;
custom.impermanence.enable = true;
custom.users.jake.password = true; # TODO: remove me once booting has stabilised
## Filesystems
boot.supportedFilesystems = [ "zfs" ];
boot.zfs = {
forceImportRoot = false;
extraPools = [ zpool_name ];
};
services.btrfs.autoScrub = {
enable = true;
interval = "Tue, 02:00";
# All filesystems includes the BTRFS parts of all the hard drives. This
# would take forever and is redundant as they get fully read regularly.
fileSystems = [ "/data" ];
};
services.zfs.autoScrub = {
enable = true;
interval = "Wed, 02:00";
};
## Resilio
custom.resilio = {
enable = true;
backups.enable = true;
folders =
let
folderNames = [
"dad"
"joseph"
"projects"
"resources"
"sync"
];
mkFolder = name: {
name = name;
secret = {
name = "resilio/plain/${name}";
file = ../../secrets/resilio/plain/${name}.age;
};
};
in
builtins.map (mkFolder) folderNames;
};
services.resilio.directoryRoot = "/${zpool_name}/sync";
## Chia
age.secrets."chia/farmer.key" = {
file = ../../secrets/chia/farmer.key.age;
owner = "chia";
group = "chia";
};
custom.chia = {
enable = true;
keyFile = config.age.secrets."chia/farmer.key".path;
plotDirectories = builtins.genList (i: "/mnt/d${toString i}/plots/contract-k32") 8;
};
## Restic
custom.services.restic.path = "/${zpool_name}/backups/restic";
## Backups
### Git
custom.backups.git = {
enable = true;
extraRepos = [ "https://gitea.hillion.co.uk/JakeHillion/nixos.git" ];
};
## Downloads
custom.services.downloads = {
metadataPath = "/${zpool_name}/downloads/metadata";
downloadCachePath = "/${zpool_name}/downloads/torrents";
filmsPath = "/${zpool_name}/media/films";
tvPath = "/${zpool_name}/media/tv";
};
## Plex
users.users.plex.extraGroups = [ "mediaaccess" ];
services.plex.enable = true;
## Networking
networking = {
interfaces.enp4s0.name = "eth0";
interfaces.enp5s0.name = "eth1";
interfaces.enp6s0.name = "eth2";
interfaces.enp8s0.name = "eth3";
};
networking.nameservers = lib.mkForce [ ]; # Trust the DHCP nameservers
networking.firewall = {
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = lib.mkForce [
22 # SSH
];
allowedUDPPorts = lib.mkForce [ ];
interfaces = {
eth0 = {
allowedTCPPorts = lib.mkForce [
32400 # Plex
];
allowedUDPPorts = lib.mkForce [ ];
};
};
};
## Tailscale
age.secrets."tailscale/phoenix.st.ts.hillion.co.uk".file = ../../secrets/tailscale/phoenix.st.ts.hillion.co.uk.age;
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/phoenix.st.ts.hillion.co.uk".path;
};
};
}

View File

@ -1,103 +0,0 @@
{
disko.devices = {
disk = {
disk0 = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
disk0-crypt = {
size = "100%";
content = {
type = "luks";
name = "disk0-crypt";
settings = {
allowDiscards = true;
};
};
};
swap = {
size = "64G";
content = {
type = "swap";
randomEncryption = true;
discardPolicy = "both";
};
};
};
};
};
disk1 = {
type = "disk";
device = "/dev/nvme1n1";
content = {
type = "gpt";
partitions = {
disk1-crypt = {
size = "100%";
content = {
type = "luks";
name = "disk1-crypt";
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
extraArgs = [
"-d raid1"
"/dev/mapper/disk0-crypt"
];
subvolumes = {
"/data" = {
mountpoint = "/data";
mountOptions = [ "compress=zstd" "ssd" ];
};
"/nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "ssd" ];
};
};
};
};
};
swap = {
size = "64G";
content = {
type = "swap";
randomEncryption = true;
discardPolicy = "both";
};
};
};
};
};
};
nodev = {
"/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"size=100%"
];
};
};
};
}

View File

@ -1 +0,0 @@
x86_64-linux

View File

@ -32,14 +32,6 @@
nat.enable = lib.mkForce false;
useDHCP = false;
vlans = {
cameras = {
id = 3;
interface = "eth2";
};
};
interfaces = {
enp1s0 = {
name = "eth0";
@ -64,14 +56,6 @@
}
];
};
cameras /* cameras@eth2 */ = {
ipv4.addresses = [
{
address = "10.133.145.1";
prefixLength = 24;
}
];
};
enp4s0 = { name = "eth3"; };
enp5s0 = { name = "eth4"; };
enp6s0 = { name = "eth5"; };
@ -98,10 +82,8 @@
ip protocol icmp counter accept comment "accept all ICMP types"
iifname "eth0" tcp dport 22 counter accept comment "SSH"
iifname { "eth0", "cameras" } ct state { established, related } counter accept
iifname { "eth0", "cameras" } drop
iifname "eth0" ct state { established, related } counter accept
iifname "eth0" drop
}
chain forward {
@ -110,7 +92,6 @@
iifname {
"eth1",
"eth2",
"tailscale0",
} oifname {
"eth0",
} counter accept comment "Allow trusted LAN to WAN"
@ -120,13 +101,12 @@
} oifname {
"eth1",
"eth2",
"tailscale0",
} ct state { established,related } counter accept comment "Allow established back to LANs"
} ct state established,related counter accept comment "Allow established back to LANs"
iifname "tailscale0" oifname { "eth1", "eth2" } counter accept comment "Allow LAN access from Tailscale"
iifname { "eth1", "eth2" } oifname "tailscale0" ct state { established,related } counter accept comment "Allow established back to Tailscale"
ip daddr 10.64.50.20 tcp dport 32400 counter accept comment "Plex"
ip daddr 10.64.50.20 tcp dport 8444 counter accept comment "Chia"
ip daddr 10.64.50.27 tcp dport 32400 counter accept comment "Plex"
ip daddr 10.64.50.21 tcp dport 7654 counter accept comment "Tang"
}
}
@ -135,17 +115,16 @@
chain prerouting {
type nat hook prerouting priority filter; policy accept;
iifname eth0 tcp dport 32400 counter dnat to 10.64.50.27
iifname eth0 tcp dport 32400 counter dnat to 10.64.50.20
iifname eth0 tcp dport 8444 counter dnat to 10.64.50.20
iifname eth0 tcp dport 7654 counter dnat to 10.64.50.21
}
chain postrouting {
type nat hook postrouting priority filter; policy accept;
oifname "eth0" masquerade
iifname tailscale0 oifname eth1 snat to 10.64.50.1
iifname tailscale0 oifname eth2 snat to 10.239.19.1
}
}
'';
@ -159,7 +138,7 @@
settings = {
interfaces-config = {
interfaces = [ "eth1" "eth2" "cameras" ];
interfaces = [ "eth1" "eth2" ];
};
lease-database = {
type = "memfile";
@ -216,22 +195,18 @@
data = "10.64.50.1, 1.1.1.1, 8.8.8.8";
}
];
reservations = lib.lists.remove null (lib.lists.imap0
(i: el: if el == null then null else {
reservations = lib.lists.imap0
(i: el: {
ip-address = "10.64.50.${toString (20 + i)}";
inherit (el) hw-address hostname;
}) [
null
{ hostname = "tywin"; hw-address = "c8:7f:54:6d:e1:03"; }
{ hostname = "microserver"; hw-address = "e4:5f:01:b4:58:95"; }
{ hostname = "theon"; hw-address = "00:1e:06:49:06:1e"; }
{ hostname = "server-switch"; hw-address = "84:d8:1b:9d:0d:85"; }
{ hostname = "apc-ap7921"; hw-address = "00:c0:b7:6b:f4:34"; }
{ hostname = "sodium"; hw-address = "d8:3a:dd:c3:d6:2b"; }
{ hostname = "gendry"; hw-address = "18:c0:4d:35:60:1e"; }
{ hostname = "phoenix"; hw-address = "a8:b8:e0:04:17:a5"; }
{ hostname = "merlin"; hw-address = "b0:41:6f:13:20:14"; }
{ hostname = "stinger"; hw-address = "7c:83:34:be:30:dd"; }
]);
];
}
{
subnet = "10.239.19.0/24";
@ -255,58 +230,17 @@
];
reservations = [
{
# bedroom-everything-presence-one
hw-address = "40:22:d8:e0:1d:50";
ip-address = "10.239.19.2";
hostname = "bedroom-everything-presence-one";
}
{
# living-room-everything-presence-one
hw-address = "40:22:d8:e0:0f:78";
ip-address = "10.239.19.3";
hostname = "living-room-everything-presence-one";
}
{
hw-address = "a0:7d:9c:b0:f0:14";
ip-address = "10.239.19.4";
hostname = "hallway-wall-tablet";
}
{
hw-address = "d8:3a:dd:c3:d6:2b";
ip-address = "10.239.19.5";
hostname = "sodium";
}
{
hw-address = "48:da:35:6f:f2:4b";
ip-address = "10.239.19.6";
hostname = "hammer";
}
{
hw-address = "48:da:35:6f:83:b8";
ip-address = "10.239.19.7";
hostname = "charlie";
}
];
}
{
subnet = "10.133.145.0/24";
interface = "cameras";
pools = [{
pool = "10.133.145.64 - 10.133.145.254";
}];
option-data = [
{
name = "routers";
data = "10.133.145.1";
}
{
name = "broadcast-address";
data = "10.133.145.255";
}
{
name = "domain-name-servers";
data = "1.1.1.1, 8.8.8.8";
}
];
reservations = [
];
}
];
@ -350,13 +284,6 @@
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/router.home.ts.hillion.co.uk".path;
useRoutingFeatures = "server";
extraSetFlags = [
"--advertise-routes"
"10.64.50.0/24,10.239.19.0/24,10.133.145.0/24"
"--advertise-exit-node"
"--netfilter-mode=off"
];
};
## Enable btrfs compression
@ -380,34 +307,9 @@
};
services.caddy = {
enable = true;
virtualHosts = {
"graphs.router.home.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy unix///run/netdata/netdata.sock
'';
};
"hammer.kvm.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy http://10.239.19.6
'';
};
"charlie.kvm.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy http://10.239.19.7
'';
};
virtualHosts."http://graphs.router.home.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = "reverse_proxy unix///run/netdata/netdata.sock";
};
};
users.users.caddy.extraGroups = [ "netdata" ];

View File

@ -1,4 +1,4 @@
{ config, pkgs, lib, nixos-hardware, ... }:
{ config, pkgs, nixpkgs-unstable, lib, nixos-hardware, ... }:
{
imports = [
@ -22,19 +22,14 @@
fileSystems."/nix".options = [ "compress=zstd" ];
## Impermanence
custom.impermanence = {
enable = true;
cache.enable = true;
};
custom.impermanence.enable = true;
boot.initrd.postDeviceCommands = lib.mkAfter ''
btrfs subvolume delete /cache/tmp
btrfs subvolume snapshot /cache/empty_snapshot /cache/tmp
chmod 1777 /cache/tmp
chmod 0777 /cache/tmp
chmod +t /cache/tmp
'';
## CA server
custom.ca.service.enable = true;
### nix only supports build-dir from 2.22. bind mount /tmp to something persistent instead.
fileSystems."/tmp" = {
device = "/cache/tmp";
@ -48,17 +43,14 @@
## Custom Services
custom.locations.autoServe = true;
custom.www.home.enable = true;
custom.www.iot.enable = true;
custom.services.isponsorblocktv.enable = true;
# Networking
networking = {
interfaces.end0.name = "eth0";
vlans = {
iot = {
id = 2;
interface = "eth0";
useDHCP = false;
interfaces = {
end0 = {
name = "eth0";
useDHCP = true;
};
};
};
@ -67,27 +59,14 @@
networking.firewall = {
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = lib.mkForce [
22 # SSH
];
allowedUDPPorts = lib.mkForce [ ];
interfaces = {
eth0 = {
allowedTCPPorts = lib.mkForce [
80 # HTTP 1-2
443 # HTTPS 1-2
7654 # Tang
];
allowedUDPPorts = lib.mkForce [
443 # HTTP 3
];
};
iot = {
allowedTCPPorts = lib.mkForce [
80 # HTTP 1-2
443 # HTTPS 1-2
];
allowedUDPPorts = lib.mkForce [
443 # HTTP 3
];
};
};

View File

@ -1,84 +0,0 @@
{ config, pkgs, lib, ... }:
{
imports = [
./disko.nix
./hardware-configuration.nix
];
config = {
system.stateVersion = "24.05";
networking.hostName = "stinger";
networking.domain = "pop.ts.hillion.co.uk";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
# zswap
"zswap.enabled=1"
"zswap.compressor=zstd"
"zswap.max_pool_percent=20"
];
boot.initrd = {
availableKernelModules = [ "r8169" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices = {
"disk0-crypt".secretFile = "/data/disk_encryption.jwe";
};
};
};
custom.defaults = true;
custom.locations.autoServe = true;
custom.impermanence.enable = true;
hardware = {
bluetooth.enable = true;
};
# Networking
networking = {
interfaces.enp1s0.name = "eth0";
vlans = {
iot = {
id = 2;
interface = "eth0";
};
};
};
networking.nameservers = lib.mkForce [ ]; # Trust the DHCP nameservers
networking.firewall = {
trustedInterfaces = [ "tailscale0" ];
allowedTCPPorts = lib.mkForce [
22 # SSH
];
allowedUDPPorts = lib.mkForce [ ];
interfaces = {
eth0 = {
allowedTCPPorts = lib.mkForce [
1400 # HA Sonos
21063 # HomeKit
];
allowedUDPPorts = lib.mkForce [
5353 # HomeKit
];
};
};
};
## Tailscale
age.secrets."tailscale/stinger.pop.ts.hillion.co.uk".file = ../../secrets/tailscale/stinger.pop.ts.hillion.co.uk.age;
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/stinger.pop.ts.hillion.co.uk".path;
};
};
}

View File

@ -1,70 +0,0 @@
{
disko.devices = {
disk = {
disk0 = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
size = "1G";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
disk0-crypt = {
size = "100%";
content = {
type = "luks";
name = "disk0-crypt";
settings = {
allowDiscards = true;
};
content = {
type = "btrfs";
subvolumes = {
"/data" = {
mountpoint = "/data";
mountOptions = [ "compress=zstd" "ssd" ];
};
"/nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "ssd" ];
};
};
};
};
};
swap = {
size = "64G";
content = {
type = "swap";
randomEncryption = true;
discardPolicy = "both";
};
};
};
};
};
};
nodev = {
"/" = {
fsType = "tmpfs";
mountOptions = [
"mode=755"
"size=100%"
];
};
};
};
}

View File

@ -1,28 +0,0 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports =
[
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp0s20f0u2.useDHCP = lib.mkDefault true;
# networking.interfaces.enp1s0.useDHCP = lib.mkDefault true;
# networking.interfaces.wlo1.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.intel.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@ -1 +0,0 @@
x86_64-linux

View File

@ -0,0 +1,7 @@
# tywin.storage.ts.hillion.co.uk
Additional installation step for Clevis/Tang:
$ echo -n $DISK_ENCRYPTION_PASSWORD | clevis encrypt sss "$(cat /etc/nixos/hosts/tywin.storage.ts.hillion.co.uk/clevis_config.json)" >/mnt/disk_encryption.jwe
$ sudo chown root:root /mnt/disk_encryption.jwe
$ sudo chmod 0400 /mnt/disk_encryption.jwe

View File

@ -0,0 +1,245 @@
{ config, pkgs, lib, ... }:
{
imports = [
./hardware-configuration.nix
];
config = {
system.stateVersion = "22.11";
networking.hostName = "tywin";
networking.domain = "storage.ts.hillion.co.uk";
networking.hostId = "2a9b6df5";
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"ip=dhcp"
"zfs.zfs_arc_max=25769803776"
];
boot.initrd = {
availableKernelModules = [ "r8169" ];
network.enable = true;
clevis = {
enable = true;
useTang = true;
devices."root".secretFile = "/disk_encryption.jwe";
};
};
custom.locations.autoServe = true;
custom.defaults = true;
# zram swap: used in the hope it will give the ZFS ARC more room to back off
zramSwap = {
enable = true;
memoryPercent = 200;
algorithm = "zstd";
};
## Tailscale
age.secrets."tailscale/tywin.storage.ts.hillion.co.uk".file = ../../secrets/tailscale/tywin.storage.ts.hillion.co.uk.age;
services.tailscale = {
enable = true;
authKeyFile = config.age.secrets."tailscale/tywin.storage.ts.hillion.co.uk".path;
};
## Filesystems
fileSystems."/".options = [ "compress=zstd" ];
boot.supportedFilesystems = [ "zfs" ];
boot.zfs = {
forceImportRoot = false;
extraPools = [ "data" ];
};
services.btrfs.autoScrub = {
enable = true;
interval = "Tue, 02:00";
# All filesystems includes the BTRFS parts of all the hard drives. This
# would take forever and is redundant as they get fully read regularly.
fileSystems = [ "/" ];
};
services.zfs.autoScrub = {
enable = true;
interval = "Wed, 02:00";
};
## Backups
### Git
age.secrets."git/git_backups_ecdsa".file = ../../secrets/git/git_backups_ecdsa.age;
age.secrets."git/git_backups_remotes".file = ../../secrets/git/git_backups_remotes.age;
custom.backups.git = {
enable = true;
sshKey = config.age.secrets."git/git_backups_ecdsa".path;
reposFile = config.age.secrets."git/git_backups_remotes".path;
repos = [ "https://gitea.hillion.co.uk/JakeHillion/nixos.git" ];
};
## Resilio
custom.resilio.enable = true;
services.resilio.deviceName = "tywin.storage";
services.resilio.directoryRoot = "/data/users/jake/sync";
services.resilio.storagePath = "/data/users/jake/sync/.sync";
custom.resilio.folders =
let
folderNames = [
"dad"
"joseph"
"projects"
"resources"
"sync"
];
mkFolder = name: {
name = name;
secret = {
name = "resilio/plain/${name}";
file = ../../secrets/resilio/plain/${name}.age;
};
};
in
builtins.map (mkFolder) folderNames;
age.secrets."resilio/restic/128G.key" = {
file = ../../secrets/restic/128G.age;
owner = "rslsync";
group = "rslsync";
};
services.restic.backups."sync" = {
repository = "rest:http://restic.tywin.storage.ts.hillion.co.uk/128G";
user = "rslsync";
passwordFile = config.age.secrets."resilio/restic/128G.key".path;
timerConfig = {
Persistent = true;
OnUnitInactiveSec = "15m";
RandomizedDelaySec = "5m";
};
paths = [ "/data/users/jake/sync" ];
exclude = [
"/data/users/jake/sync/.sync"
"/data/users/jake/sync/*/.sync"
"/data/users/jake/sync/resources/media/films"
"/data/users/jake/sync/resources/media/iso"
"/data/users/jake/sync/resources/media/tv"
"/data/users/jake/sync/dad/media"
];
};
## Restic
age.secrets."restic/128G.key" = {
file = ../../secrets/restic/128G.age;
owner = "restic";
group = "restic";
};
age.secrets."restic/1.6T.key" = {
file = ../../secrets/restic/1.6T.age;
owner = "restic";
group = "restic";
};
services.restic.server = {
enable = true;
appendOnly = true;
extraFlags = [ "--no-auth" ];
dataDir = "/data/backups/restic";
listenAddress = "127.0.0.1:8000"; # TODO: can this be a Unix socket?
};
services.caddy = {
enable = true;
virtualHosts."http://restic.tywin.storage.ts.hillion.co.uk".extraConfig = ''
bind ${config.custom.dns.tailscale.ipv4} ${config.custom.dns.tailscale.ipv6}
reverse_proxy http://localhost:8000
'';
};
### HACK: Allow Caddy to restart if it fails. This happens because Tailscale
### is too late at starting. Upstream nixos caddy does restart on failure
### but it's prevented on exit code 1. Set the exit code to 0 (non-failure)
### to override this.
systemd.services.caddy = {
requires = [ "tailscaled.service" ];
after = [ "tailscaled.service" ];
serviceConfig = {
RestartPreventExitStatus = lib.mkForce 0;
};
};
services.restic.backups."prune-128G" = {
repository = "/data/backups/restic/128G";
user = "restic";
passwordFile = config.age.secrets."restic/128G.key".path;
timerConfig = {
Persistent = true;
OnCalendar = "02:30";
RandomizedDelaySec = "1h";
};
pruneOpts = [
"--keep-last 48"
"--keep-within-hourly 7d"
"--keep-within-daily 1m"
"--keep-within-weekly 6m"
"--keep-within-monthly 24m"
];
};
services.restic.backups."prune-1.6T" = {
repository = "/data/backups/restic/1.6T";
user = "restic";
passwordFile = config.age.secrets."restic/1.6T.key".path;
timerConfig = {
Persistent = true;
OnCalendar = "Wed, 02:30";
RandomizedDelaySec = "4h";
};
pruneOpts = [
"--keep-within-daily 14d"
"--keep-within-weekly 2m"
"--keep-within-monthly 18m"
];
};
## Chia
age.secrets."chia/farmer.key" = {
file = ../../secrets/chia/farmer.key.age;
owner = "chia";
group = "chia";
};
custom.chia = {
enable = true;
openFirewall = true;
keyFile = config.age.secrets."chia/farmer.key".path;
plotDirectories = builtins.genList (i: "/mnt/d${toString i}/plots/contract-k32") 7;
};
## Downloads
custom.services.downloads = {
metadataPath = "/data/downloads/metadata";
downloadCachePath = "/data/downloads/torrents";
filmsPath = "/data/media/films";
tvPath = "/data/media/tv";
};
## Plex
users.users.plex.extraGroups = [ "mediaaccess" ];
services.plex = {
enable = true;
openFirewall = true;
};
## Networking
networking.nameservers = lib.mkForce [ ]; # Trust the DHCP nameservers
networking.firewall.interfaces."tailscale0".allowedTCPPorts = [
80 # Caddy (restic.tywin.storage.ts.)
];
};
}

View File

@ -9,11 +9,28 @@
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "nvme" "ahci" "xhci_pci" "thunderbolt" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ "kvm-amd" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{
device = "/dev/disk/by-uuid/cb48d4ed-d268-490c-9977-2b5d31ce2c1b";
fsType = "btrfs";
};
boot.initrd.luks.devices."root" = {
device = "/dev/disk/by-uuid/32837730-5e15-4917-9939-cbb58bb0aabf";
allowDiscards = true;
};
fileSystems."/boot" =
{
device = "/dev/disk/by-uuid/BC57-0AF6";
fsType = "vfat";
};
fileSystems."/mnt/d0" =
{
device = "/dev/disk/by-uuid/9136434d-d883-4118-bd01-903f720e5ce1";
@ -50,28 +67,14 @@
fsType = "btrfs";
};
fileSystems."/mnt/d6" =
{
device = "/dev/disk/by-uuid/b461e07d-39ab-46b4-b1d1-14c2e0791915";
fsType = "btrfs";
};
fileSystems."/mnt/d7" =
{
device = "/dev/disk/by-uuid/eb8d32d0-e506-449b-8dbc-585ba05c4252";
fsType = "btrfs";
};
swapDevices = [ ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
# (the default) this is the recommended approach. When using systemd-networkd it's
# still possible to use this option, but it's recommended to use it in conjunction
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
networking.useDHCP = lib.mkDefault true;
# networking.interfaces.enp4s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp5s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp6s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp8s0.useDHCP = lib.mkDefault true;
# networking.interfaces.enp7s0.useDHCP = lib.mkDefault true;
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;
}

View File

@ -2,7 +2,7 @@
{
imports = [
./git/default.nix
./git.nix
./homeassistant.nix
./matrix.nix
];

View File

@ -7,17 +7,25 @@ in
options.custom.backups.git = {
enable = lib.mkEnableOption "git";
extraRepos = lib.mkOption {
repos = lib.mkOption {
description = "A list of remotes to clone.";
type = with lib.types; listOf str;
default = [ ];
};
reposFile = lib.mkOption {
description = "A file containing the remotes to clone, one per line.";
type = with lib.types; nullOr str;
default = null;
};
sshKey = lib.mkOption {
description = "SSH private key to use when cloning repositories over SSH.";
type = with lib.types; nullOr str;
default = null;
};
};
config = lib.mkIf cfg.enable {
age.secrets."git/git_backups_ecdsa".file = ../../../secrets/git/git_backups_ecdsa.age;
age.secrets."git/git_backups_remotes".file = ../../../secrets/git/git_backups_remotes.age;
age.secrets."git-backups/restic/128G".file = ../../../secrets/restic/128G.age;
age.secrets."git-backups/restic/128G".file = ../../secrets/restic/128G.age;
systemd.services.backup-git = {
description = "Git repo backup service.";
@ -29,10 +37,9 @@ in
WorkingDirectory = "%C/backup-git";
LoadCredential = [
"id_ecdsa:${config.age.secrets."git/git_backups_ecdsa".path}"
"repos_file:${config.age.secrets."git/git_backups_remotes".path}"
"restic_password:${config.age.secrets."git-backups/restic/128G".path}"
];
] ++ (if cfg.sshKey == null then [ ] else [ "id_ecdsa:${cfg.sshKey}" ])
++ (if cfg.reposFile == null then [ ] else [ "repos_file:${cfg.reposFile}" ]);
};
environment = {
@ -41,12 +48,11 @@ in
};
script = ''
set -x
shopt -s nullglob
# Read and deduplicate repos
readarray -t raw_repos < $CREDENTIALS_DIRECTORY/repos_file
declare -A repos=(${builtins.concatStringsSep " " (builtins.map (x : "[${x}]=1") cfg.extraRepos)})
${if cfg.reposFile == null then "" else "readarray -t raw_repos < $CREDENTIALS_DIRECTORY/repos_file"}
declare -A repos=(${builtins.concatStringsSep " " (builtins.map (x : "[${x}]=1") cfg.repos)})
for repo in ''${raw_repos[@]}; do repos[$repo]=1; done
# Clean up existing repos
@ -73,7 +79,7 @@ in
# Backup to Restic
${pkgs.restic}/bin/restic \
-r rest:https://restic.ts.hillion.co.uk/128G \
-r rest:http://restic.tywin.storage.ts.hillion.co.uk/128G \
--cache-dir .restic --exclude .restic \
backup .
@ -87,9 +93,9 @@ in
wantedBy = [ "timers.target" ];
timerConfig = {
Persistent = true;
OnBootSec = "10m";
OnUnitInactiveSec = "15m";
RandomizedDelaySec = "5m";
Unit = "backup-git.service";
};
};
};

View File

@ -1 +0,0 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIc3WVROMCifYtqHRWf5gZAOQFdpbcSYOC0JckKzUVM5sGdXtw3VXNiVqY3npdMizS4e1V8Hh77UecD3q9CLkMA= backups-git@nixos

View File

@ -14,44 +14,19 @@ in
owner = "hass";
group = "hass";
};
age.secrets."backups/homeassistant/restic/1.6T" = {
file = ../../secrets/restic/1.6T.age;
owner = "postgres";
group = "postgres";
};
services = {
postgresqlBackup = {
enable = true;
compression = "none"; # for better diffing
databases = [ "homeassistant" ];
};
restic.backups = {
"homeassistant-config" = {
user = "hass";
timerConfig = {
OnCalendar = "03:00";
RandomizedDelaySec = "60m";
};
repository = "rest:https://restic.ts.hillion.co.uk/128G";
passwordFile = config.age.secrets."backups/homeassistant/restic/128G".path;
paths = [
config.services.home-assistant.configDir
];
};
"homeassistant-database" = {
user = "postgres";
timerConfig = {
OnCalendar = "03:00";
RandomizedDelaySec = "60m";
};
repository = "rest:https://restic.ts.hillion.co.uk/1.6T";
passwordFile = config.age.secrets."backups/homeassistant/restic/1.6T".path;
paths = [
"${config.services.postgresqlBackup.location}/homeassistant.sql"
];
restic.backups."homeassistant" = {
user = "hass";
timerConfig = {
OnCalendar = "03:00";
RandomizedDelaySec = "60m";
};
repository = "rest:http://restic.tywin.storage.ts.hillion.co.uk/128G";
passwordFile = config.age.secrets."backups/homeassistant/restic/128G".path;
paths = [
config.services.home-assistant.configDir
];
};
};
};

View File

@ -24,7 +24,7 @@ in
OnCalendar = "03:00";
RandomizedDelaySec = "60m";
};
repository = "rest:https://restic.ts.hillion.co.uk/128G";
repository = "rest:http://restic.tywin.storage.ts.hillion.co.uk/128G";
passwordFile = config.age.secrets."backups/matrix/restic/128G".path;
paths = [
"${config.services.postgresqlBackup.location}/matrix-synapse.sql"

View File

@ -1,11 +0,0 @@
# ca
Getting the certificates in the right place is a manual process (for now, at least). This is to keep the most control over the root certificate's key and allow manual cycling. The manual commands should be run on a trusted machine.
Creating a 10 year root certificate:
nix run nixpkgs#step-cli -- certificate create 'Hillion ACME' cert.pem key.pem --kty=EC --curve=P-521 --profile=root-ca --not-after=87600h
Creating the intermediate key:
nix run nixpkgs#step-cli -- certificate create 'Hillion ACME (sodium.pop.ts.hillion.co.uk)' intermediate_cert.pem intermediate_key.pem --kty=EC --curve=P-521 --profile=intermediate-ca --not-after=8760h --ca=$NIXOS_ROOT/modules/ca/cert.pem --ca-key=DOWNLOADED_KEY.pem

View File

@ -1,13 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIB+TCCAVqgAwIBAgIQIZdaIUsuJdjnu7DQP1N8oTAKBggqhkjOPQQDBDAXMRUw
EwYDVQQDEwxIaWxsaW9uIEFDTUUwHhcNMjQwODAxMjIyMjEwWhcNMzQwNzMwMjIy
MjEwWjAXMRUwEwYDVQQDEwxIaWxsaW9uIEFDTUUwgZswEAYHKoZIzj0CAQYFK4EE
ACMDgYYABAAJI3z1PrV97EFc1xaENcr6ML1z6xdXTy+ReHtf42nWsw+c3WDKzJ45
+xHJ/p2BTOR5+NQ7RGQQ68zmFJnEYTYDogAw6U9YzxxDGlG1HlgnZ9PPmXoF+PFl
Zy2WZCiDPx5KDJcjTPzLV3ITt4fl3PMA12BREVeonvrvRLcpVrMfS2b7wKNFMEMw
DgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFFBT
fMT0uUbS+lVUbGKK8/SZHPISMAoGCCqGSM49BAMEA4GMADCBiAJCAPNIwrQztPrN
MaHB3J0lNVODIGwQWblt99vnjqIWOKJhgckBxaElyInsyt8dlnmTCpOCJdY4BA+K
Nr87AfwIWdAaAkIBV5i4zXPXVKblGKnmM0FomFSbq2cYE3pmi5BO1StakH1kEHlf
vbkdwFgkw2MlARp0Ka3zbWivBG9zjPoZtsL/8tk=
-----END CERTIFICATE-----

View File

@ -1,14 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.ca.consumer;
in
{
options.custom.ca.consumer = {
enable = lib.mkEnableOption "ca.service";
};
config = lib.mkIf cfg.enable {
security.pki.certificates = [ (builtins.readFile ./cert.pem) ];
};
}

View File

@ -1,8 +0,0 @@
{ ... }:
{
imports = [
./consumer.nix
./service.nix
];
}

View File

@ -1,48 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.ca.service;
in
{
options.custom.ca.service = {
enable = lib.mkEnableOption "ca.service";
};
config = lib.mkIf cfg.enable {
users.users.step-ca.uid = config.ids.uids.step-ca;
users.groups.step-ca.gid = config.ids.gids.step-ca;
services.step-ca = {
enable = true;
address = config.custom.dns.tailscale.ipv4;
port = 8443;
intermediatePasswordFile = "/data/system/ca/intermediate.psk";
settings = {
root = ./cert.pem;
crt = "/data/system/ca/intermediate.crt";
key = "/data/system/ca/intermediate.pem";
dnsNames = [ "ca.ts.hillion.co.uk" ];
logger = { format = "text"; };
db = {
type = "badgerv2";
dataSource = "/var/lib/step-ca/db";
};
authority = {
provisioners = [
{
type = "ACME";
name = "acme";
}
];
};
};
};
};
}

View File

@ -22,8 +22,8 @@ in
default = null;
};
plotDirectories = lib.mkOption {
type = with lib.types; listOf str;
default = [ ];
type = with lib.types; nullOr (listOf str);
default = null;
};
openFirewall = lib.mkOption {
type = lib.types.bool;
@ -46,7 +46,7 @@ in
};
virtualisation.oci-containers.containers.chia = {
image = "ghcr.io/chia-network/chia:2.4.3";
image = "ghcr.io/chia-network/chia:2.4.1";
ports = [ "8444" ];
extraOptions = [
"--uidmap=0:${toString config.users.users.chia.uid}:1"
@ -62,11 +62,6 @@ in
};
};
systemd.tmpfiles.rules = [
"d ${cfg.path} 0700 chia chia - -"
"d ${cfg.path}/.chia 0700 chia chia - -"
];
networking.firewall = lib.mkIf cfg.openFirewall {
allowedTCPPorts = [ 8444 ];
};

View File

@ -3,7 +3,6 @@
{
imports = [
./backups/default.nix
./ca/default.nix
./chia.nix
./defaults.nix
./desktop/awesome/default.nix
@ -12,16 +11,16 @@
./hostinfo.nix
./ids.nix
./impermanence.nix
./laptop.nix
./locations.nix
./prometheus/default.nix
./resilio.nix
./sched_ext.nix
./services/default.nix
./shell/default.nix
./ssh/default.nix
./storj.nix
./users.nix
./www/default.nix
./www/global.nix
./www/www-repo.nix
];
options.custom = {

View File

@ -1,10 +1,9 @@
{ pkgs, nixpkgs-unstable, lib, config, agenix, ... }:
{ pkgs, lib, config, agenix, ... }:
{
options.custom.defaults = lib.mkEnableOption "defaults";
config = lib.mkIf config.custom.defaults {
hardware.enableAllFirmware = true;
nix = {
settings.experimental-features = [ "nix-command" "flakes" ];
settings = {
@ -39,6 +38,7 @@
git
htop
nix
sapling
vim
];
variables.EDITOR = "vim";
@ -53,17 +53,10 @@
};
networking.firewall.enable = true;
nix.registry.nixpkgs-unstable.to = {
type = "path";
path = nixpkgs-unstable;
};
# Delegation
custom.ca.consumer.enable = true;
custom.dns.enable = true;
custom.home.defaults = true;
custom.hostinfo.enable = true;
custom.prometheus.client.enable = true;
custom.shell.enable = true;
custom.ssh.enable = true;
};

View File

@ -50,16 +50,10 @@ in
pop = {
li = "100.106.87.35";
sodium = "100.87.188.4";
stinger = "100.117.89.126";
};
rig = {
merlin = "100.69.181.56";
};
st = {
phoenix = "100.92.37.106";
};
storage = {
theon = "100.104.142.22";
tywin = "100.115.31.91";
};
};
};
@ -83,16 +77,10 @@ in
pop = {
li = "fd7a:115c:a1e0::e701:5723";
sodium = "fd7a:115c:a1e0::3701:bc04";
stinger = "fd7a:115c:a1e0::8401:597e";
};
rig = {
merlin = "fd7a:115c:a1e0::8d01:b538";
};
st = {
phoenix = "fd7a:115c:a1e0::6901:256a";
};
storage = {
theon = "fd7a:115c:a1e0::4aa8:8e16";
tywin = "fd7a:115c:a1e0:ab12:4843:cd96:6273:1f5b";
};
};
};

View File

@ -3,47 +3,30 @@
{
imports = [
./git.nix
./neovim.nix
./tmux/default.nix
];
options.custom.home.defaults = lib.mkEnableOption "home";
config = lib.mkIf config.custom.home.defaults {
home-manager =
let
stateVersion = if (builtins.compareVersions config.system.stateVersion "24.05") > 0 then config.system.stateVersion else "22.11";
in
{
users.root.home = {
inherit stateVersion;
home-manager = {
users.root.home = {
stateVersion = "22.11";
## Set an empty ZSH config and defer to the global one
file.".zshrc".text = "";
};
users."${config.custom.user}" = {
home = {
inherit stateVersion;
};
services = {
ssh-agent.enable = true;
};
programs = {
zoxide = {
enable = true;
options = [ "--cmd cd" ];
};
zsh.enable = true;
};
};
## Set an empty ZSH config and defer to the global one
file.".zshrc".text = "";
};
users."${config.custom.user}".home = {
stateVersion = "22.11";
## Set an empty ZSH config and defer to the global one
file.".zshrc".text = "";
};
};
# Delegation
custom.home.git.enable = true;
custom.home.neovim.enable = true;
custom.home.tmux.enable = true;
};
}

View File

@ -9,34 +9,21 @@ in
};
config = lib.mkIf cfg.enable {
home-manager.users.jake.programs = {
sapling = lib.mkIf (config.custom.user == "jake") {
enable = true;
userName = "Jake Hillion";
userEmail = "jake@hillion.co.uk";
extraConfig = {
ui = {
"merge:interactive" = ":merge3";
};
home-manager.users.jake.programs.git = lib.mkIf (config.custom.user == "jake") {
enable = true;
extraConfig = {
user = {
email = "jake@hillion.co.uk";
name = "Jake Hillion";
};
};
git = lib.mkIf (config.custom.user == "jake") {
enable = true;
userName = "Jake Hillion";
userEmail = "jake@hillion.co.uk";
extraConfig = {
pull = {
rebase = true;
};
merge = {
conflictstyle = "diff3";
};
init = {
defaultBranch = "main";
};
pull = {
rebase = true;
};
merge = {
conflictstyle = "diff3";
};
init = {
defaultBranch = "main";
};
};
};

View File

@ -1,82 +0,0 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.home.neovim;
in
{
options.custom.home.neovim = {
enable = lib.mkEnableOption "neovim";
};
config = lib.mkIf config.custom.home.neovim.enable {
home-manager.users."${config.custom.user}".programs.neovim = {
enable = true;
viAlias = true;
vimAlias = true;
plugins = with pkgs.vimPlugins; [
a-vim
dracula-nvim
telescope-nvim
];
extraLuaConfig = ''
-- Logical options
vim.opt.splitright = true
vim.opt.splitbelow = true
vim.opt.ignorecase = true
vim.opt.smartcase = true
vim.opt.expandtab = true
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
-- Appearance
vim.cmd[[colorscheme dracula-soft]]
vim.opt.number = true
vim.opt.relativenumber = true
-- Telescope
require('telescope').setup({
pickers = {
find_files = {
find_command = {
"${pkgs.fd}/bin/fd",
"--type=f",
"--strip-cwd-prefix",
"--no-require-git",
"--hidden",
"--exclude=.sl",
},
},
},
defaults = {
vimgrep_arguments = {
"${pkgs.ripgrep}/bin/rg",
"--color=never",
"--no-heading",
"--with-filename",
"--line-number",
"--column",
"--smart-case",
"--no-require-git",
"--hidden",
"--glob=!.sl",
},
},
})
-- Key bindings
vim.g.mapleader = ","
--- Key bindings: Telescope
local telescope_builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', telescope_builtin.find_files, {})
vim.keymap.set('n', '<leader>fg', telescope_builtin.live_grep, {})
vim.keymap.set('n', '<leader>fb', telescope_builtin.buffers, {})
vim.keymap.set('n', '<leader>fh', telescope_builtin.help_tags, {})
'';
};
};
}

View File

@ -1,16 +1,9 @@
setw -g mouse on
# Large history
set -g history-limit 500000
# Bindings
bind C-Y set-window-option synchronize-panes
bind -n C-k clear-history
# Status pane
set -g status-right-length 100
set -g status-right "#(uname -r) • #(hostname -f | sed 's/\.ts\.hillion\.co\.uk//g') • %d-%b-%y %H:%M"
# New panes in the same directory
bind '"' split-window -c "#{pane_current_path}"
bind % split-window -h -c "#{pane_current_path}"

View File

@ -11,7 +11,14 @@ in
config = lib.mkIf cfg.enable {
home-manager.users.jake.programs.tmux = {
enable = true;
extraConfig = lib.readFile ./.tmux.conf;
extraConfig = lib.strings.concatLines [
(lib.readFile ./.tmux.conf)
''set -g status-right "${lib.strings.optionalString config.custom.laptop "#{battery_icon} #{battery_percentage} #{battery_remain} | "}\"#{=21:pane_title}\" %H:%M %d-%b-%y"''
];
plugins = with pkgs; lib.lists.optional config.custom.laptop tmuxPlugins.battery;
};
};
}

View File

@ -7,9 +7,6 @@
unifi = 183;
chia = 185;
gitea = 186;
node-exporter = 188;
step-ca = 198;
isponsorblocktv = 199;
## Consistent People
jake = 1000;
@ -20,9 +17,6 @@
unifi = 183;
chia = 185;
gitea = 186;
node-exporter = 188;
step-ca = 198;
isponsorblocktv = 199;
## Consistent Groups
mediaaccess = 1200;

View File

@ -2,6 +2,7 @@
let
cfg = config.custom.impermanence;
listIf = (enable: x: if enable then x else [ ]);
in
{
options.custom.impermanence = {
@ -11,13 +12,6 @@ in
type = lib.types.str;
default = "/data";
};
cache = {
enable = lib.mkEnableOption "impermanence.cache";
path = lib.mkOption {
type = lib.types.str;
default = "/cache";
};
};
users = lib.mkOption {
type = with lib.types; listOf str;
@ -46,76 +40,38 @@ in
gitea.stateDir = "${cfg.base}/system/var/lib/gitea";
};
custom.chia = lib.mkIf config.custom.chia.enable {
path = lib.mkOverride 999 "/data/chia";
environment.persistence."${cfg.base}/system" = {
hideMounts = true;
directories = [
"/etc/nixos"
] ++ (listIf config.services.tailscale.enable [ "/var/lib/tailscale" ]) ++
(listIf config.services.zigbee2mqtt.enable [ config.services.zigbee2mqtt.dataDir ]) ++
(listIf config.services.postgresql.enable [ config.services.postgresql.dataDir ]) ++
(listIf config.hardware.bluetooth.enable [ "/var/lib/bluetooth" ]) ++
(listIf config.custom.services.unifi.enable [ "/var/lib/unifi" ]) ++
(listIf (config.virtualisation.oci-containers.containers != { }) [ "/var/lib/containers" ]) ++
(listIf config.services.tang.enable [ "/var/lib/private/tang" ]);
};
services.resilio = lib.mkIf config.services.resilio.enable {
directoryRoot = lib.mkOverride 999 "${cfg.base}/sync";
};
services.plex = lib.mkIf config.services.plex.enable {
dataDir = lib.mkOverride 999 "/data/plex";
};
services.home-assistant = lib.mkIf config.services.home-assistant.enable {
configDir = lib.mkOverride 999 "/data/home-assistant";
};
environment.persistence = lib.mkMerge [
{
"${cfg.base}/system" = {
hideMounts = true;
directories = [
"/etc/nixos"
] ++ (lib.lists.optional config.services.tailscale.enable "/var/lib/tailscale") ++
(lib.lists.optional config.services.zigbee2mqtt.enable config.services.zigbee2mqtt.dataDir) ++
(lib.lists.optional config.services.postgresql.enable config.services.postgresql.dataDir) ++
(lib.lists.optional config.hardware.bluetooth.enable "/var/lib/bluetooth") ++
(lib.lists.optional config.custom.services.unifi.enable "/var/lib/unifi") ++
(lib.lists.optional (config.virtualisation.oci-containers.containers != { }) "/var/lib/containers") ++
(lib.lists.optional config.services.tang.enable "/var/lib/private/tang") ++
(lib.lists.optional config.services.caddy.enable "/var/lib/caddy") ++
(lib.lists.optional config.services.prometheus.enable "/var/lib/${config.services.prometheus.stateDir}") ++
(lib.lists.optional config.custom.services.isponsorblocktv.enable "${config.custom.services.isponsorblocktv.dataDir}") ++
(lib.lists.optional config.services.step-ca.enable "/var/lib/step-ca/db");
};
}
(lib.mkIf cfg.cache.enable {
"${cfg.cache.path}/system" = {
hideMounts = true;
directories = (lib.lists.optional config.services.postgresqlBackup.enable config.services.postgresqlBackup.location);
};
})
];
home-manager.users =
let
mkUser = (x:
let
homeCfg = config.home-manager.users."${x}";
in
{
name = x;
value = {
home = {
persistence."/data/users/${x}" = {
allowOther = false;
mkUser = (x: {
name = x;
value = {
home = {
persistence."/data/users/${x}" = {
allowOther = false;
files = cfg.userExtraFiles.${x} or [ ];
directories = cfg.userExtraDirs.${x} or [ ];
};
sessionVariables = lib.attrsets.optionalAttrs homeCfg.programs.zoxide.enable { _ZO_DATA_DIR = "/data/users/${x}/.local/share/zoxide"; };
};
programs = {
zsh.history.path = lib.mkOverride 999 "/data/users/${x}/.zsh_history";
files = cfg.userExtraFiles.${x} or [ ];
directories = cfg.userExtraDirs.${x} or [ ];
};
file.".zshrc".text = lib.mkForce ''
HISTFILE=/data/users/${x}/.zsh_history
'';
};
});
};
});
in
builtins.listToAttrs (builtins.map mkUser cfg.users);

5
modules/laptop.nix Normal file
View File

@ -0,0 +1,5 @@
{ pkgs, lib, config, ... }:
{
options.custom.laptop = lib.mkEnableOption "laptop";
}

View File

@ -20,13 +20,11 @@ in
custom.locations.locations = {
services = {
authoritative_dns = [ "boron.cx.ts.hillion.co.uk" ];
downloads = "phoenix.st.ts.hillion.co.uk";
downloads = "tywin.storage.ts.hillion.co.uk";
gitea = "boron.cx.ts.hillion.co.uk";
homeassistant = "stinger.pop.ts.hillion.co.uk";
homeassistant = "microserver.home.ts.hillion.co.uk";
mastodon = "";
matrix = "boron.cx.ts.hillion.co.uk";
prometheus = "boron.cx.ts.hillion.co.uk";
restic = "phoenix.st.ts.hillion.co.uk";
tang = [
"li.pop.ts.hillion.co.uk"
"microserver.home.ts.hillion.co.uk"

View File

@ -1,24 +0,0 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.prometheus.client;
in
{
options.custom.prometheus.client = {
enable = lib.mkEnableOption "prometheus-client";
};
config = lib.mkIf cfg.enable {
users.users.node-exporter.uid = config.ids.uids.node-exporter;
users.groups.node-exporter.gid = config.ids.gids.node-exporter;
services.prometheus.exporters.node = {
enable = true;
port = 9000;
enabledCollectors = [
"systemd"
];
};
};
}

View File

@ -1,8 +0,0 @@
{ ... }:
{
imports = [
./client.nix
./service.nix
];
}

View File

@ -1,67 +0,0 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.services.prometheus;
in
{
options.custom.services.prometheus = {
enable = lib.mkEnableOption "prometheus-client";
};
config = lib.mkIf cfg.enable {
services.prometheus = {
enable = true;
globalConfig = {
scrape_interval = "15s";
};
retentionTime = "1y";
scrapeConfigs = [{
job_name = "node";
static_configs = [{
targets = builtins.map (x: "${x}:9000") (builtins.attrNames (builtins.readDir ../../hosts));
}];
}];
rules = [
''
groups:
- name: service alerting
rules:
- alert: ResilioSyncDown
expr: node_systemd_unit_state{ name = 'resilio.service', state != 'active' } > 0
for: 10m
annotations:
summary: "Resilio Sync systemd service is down"
description: "The Resilio Sync systemd service is not active on instance {{ $labels.instance }}."
''
];
};
services.caddy = {
enable = true;
virtualHosts."prometheus.ts.hillion.co.uk" = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
reverse_proxy http://localhost:9090
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
'';
};
};
### HACK: Allow Caddy to restart if it fails. This happens because Tailscale
### is too late at starting. Upstream nixos caddy does restart on failure
### but it's prevented on exit code 1. Set the exit code to 0 (non-failure)
### to override this.
systemd.services.caddy = {
requires = [ "tailscaled.service" ];
after = [ "tailscaled.service" ];
serviceConfig = {
RestartPreventExitStatus = lib.mkForce 0;
};
};
};
}

View File

@ -4,9 +4,6 @@ let
cfg = config.custom.resilio;
in
{
imports = [ "${nixpkgs-unstable}/nixos/modules/services/networking/resilio.nix" ];
disabledModules = [ "services/networking/resilio.nix" ];
options.custom.resilio = {
enable = lib.mkEnableOption "resilio";
@ -19,93 +16,50 @@ in
type = with lib.types; uniq (listOf attrs);
default = [ ];
};
backups = {
enable = lib.mkEnableOption "resilio.backups";
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
users.users =
let
mkUser =
(user: {
name = user;
value = {
extraGroups = [ "rslsync" ];
};
});
in
builtins.listToAttrs (builtins.map mkUser cfg.extraUsers);
age.secrets =
let
mkSecret = (secret: {
name = secret.name;
config = lib.mkIf cfg.enable {
users.users =
let
mkUser =
(user: {
name = user;
value = {
file = secret.file;
owner = "rslsync";
group = "rslsync";
extraGroups = [ "rslsync" ];
};
});
in
builtins.listToAttrs (builtins.map mkUser cfg.extraUsers);
age.secrets =
let
mkSecret = (secret: {
name = secret.name;
value = {
file = secret.file;
owner = "rslsync";
group = "rslsync";
};
});
in
builtins.listToAttrs (builtins.map (folder: mkSecret folder.secret) cfg.folders);
services.resilio = {
enable = true;
sharedFolders =
let
mkFolder = name: secret: {
directory = "${config.services.resilio.directoryRoot}/${name}";
secretFile = "${config.age.secrets."${secret.name}".path}";
knownHosts = [ ];
searchLAN = true;
useDHT = true;
useRelayServer = true;
useSyncTrash = false;
useTracker = true;
};
in
builtins.listToAttrs (builtins.map (folder: mkSecret folder.secret) cfg.folders);
services.resilio = {
enable = true;
deviceName = lib.mkOverride 999 (lib.strings.concatStringsSep "." (lib.lists.take 2 (lib.strings.splitString "." config.networking.fqdnOrHostName)));
storagePath = lib.mkOverride 999 "${config.services.resilio.directoryRoot}/.sync";
sharedFolders =
let
mkFolder = name: secret: {
directory = "${config.services.resilio.directoryRoot}/${name}";
secretFile = "${config.age.secrets."${secret.name}".path}";
knownHosts = [ ];
searchLAN = true;
useDHT = true;
useRelayServer = true;
useSyncTrash = false;
useTracker = true;
};
in
builtins.map (folder: mkFolder folder.name folder.secret) cfg.folders;
};
systemd.services.resilio.unitConfig.RequiresMountsFor = builtins.map (folder: "${config.services.resilio.directoryRoot}/${folder.name}") cfg.folders;
}
(lib.mkIf cfg.backups.enable {
age.secrets."resilio/restic/128G.key" = {
file = ../secrets/restic/128G.age;
owner = "rslsync";
group = "rslsync";
};
services.restic.backups."resilio" = {
repository = "rest:https://restic.ts.hillion.co.uk/128G";
user = "rslsync";
passwordFile = config.age.secrets."resilio/restic/128G.key".path;
timerConfig = {
OnBootSec = "10m";
OnUnitInactiveSec = "15m";
RandomizedDelaySec = "5m";
};
paths = [ config.services.resilio.directoryRoot ];
exclude = [
"${config.services.resilio.directoryRoot}/.sync"
"${config.services.resilio.directoryRoot}/*/.sync"
"${config.services.resilio.directoryRoot}/resources/media/films"
"${config.services.resilio.directoryRoot}/resources/media/iso"
"${config.services.resilio.directoryRoot}/resources/media/tv"
"${config.services.resilio.directoryRoot}/dad/media"
];
};
})
]);
builtins.map (folder: mkFolder folder.name folder.secret) cfg.folders;
};
};
}

View File

@ -1,22 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.sched_ext;
in
{
options.custom.sched_ext = {
enable = lib.mkEnableOption "sched_ext";
};
config = lib.mkIf cfg.enable {
assertions = [{
assertion = config.boot.kernelPackages.kernelAtLeast "6.12";
message = "sched_ext requires a kernel >=6.12";
}];
boot.kernelPackages = if pkgs.linuxPackages.kernelAtLeast "6.12" then pkgs.linuxPackages else (if pkgs.linuxPackages_latest.kernelAtLeast "6.12" then pkgs.linuxPackages_latest else pkgs.unstable.linuxPackages_testing);
environment.systemPackages = with pkgs; [ unstable.scx.layered unstable.scx.lavd ];
};
}

View File

@ -32,21 +32,14 @@ in
86400 NS ns1.hillion.co.uk.
ca 21600 CNAME sodium.pop.ts.hillion.co.uk.
restic 21600 CNAME ${config.custom.locations.locations.services.restic}.
prometheus 21600 CNAME ${config.custom.locations.locations.services.prometheus}.
deluge.downloads 21600 CNAME ${config.custom.locations.locations.services.downloads}.
prowlarr.downloads 21600 CNAME ${config.custom.locations.locations.services.downloads}.
radarr.downloads 21600 CNAME ${config.custom.locations.locations.services.downloads}.
sonarr.downloads 21600 CNAME ${config.custom.locations.locations.services.downloads}.
deluge.downloads 21600 CNAME tywin.storage.ts.hillion.co.uk.
graphs.router.home 21600 CNAME router.home.ts.hillion.co.uk.
prowlarr.downloads 21600 CNAME tywin.storage.ts.hillion.co.uk.
radarr.downloads 21600 CNAME tywin.storage.ts.hillion.co.uk.
restic.tywin.storage 21600 CNAME tywin.storage.ts.hillion.co.uk.
sonarr.downloads 21600 CNAME tywin.storage.ts.hillion.co.uk.
zigbee2mqtt.home 21600 CNAME router.home.ts.hillion.co.uk.
charlie.kvm 21600 CNAME router.home.ts.hillion.co.uk.
hammer.kvm 21600 CNAME router.home.ts.hillion.co.uk.
'' + (makeRecords "A" config.custom.dns.authoritative.ipv4.uk.co.hillion.ts) + "\n\n" + (makeRecords "AAAA" config.custom.dns.authoritative.ipv6.uk.co.hillion.ts);
};
};

View File

@ -6,10 +6,8 @@
./downloads.nix
./gitea/default.nix
./homeassistant.nix
./isponsorblocktv.nix
./mastodon/default.nix
./matrix.nix
./restic.nix
./tang.nix
./unifi.nix
./version_tracker.nix

View File

@ -24,33 +24,27 @@ in
};
config = lib.mkIf cfg.enable {
age.secrets."wireguard/downloads".file = ../../secrets/wireguard/downloads.age;
age.secrets."deluge/auth" = {
file = ../../secrets/deluge/auth.age;
owner = "deluge";
};
services.caddy = {
enable = true;
virtualHosts = builtins.listToAttrs (builtins.map
(x: {
name = "${x}.downloads.ts.hillion.co.uk";
name = "http://${x}.downloads.ts.hillion.co.uk";
value = {
listenAddresses = [ config.custom.dns.tailscale.ipv4 config.custom.dns.tailscale.ipv6 ];
extraConfig = ''
reverse_proxy unix//${cfg.metadataPath}/caddy/caddy.sock
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
'';
extraConfig = "reverse_proxy unix//${cfg.metadataPath}/caddy/caddy.sock";
};
}) [ "prowlarr" "sonarr" "radarr" "deluge" ]);
};
## Wireguard
age.secrets."wireguard/downloads".file = ../../secrets/wireguard/downloads.age;
age.secrets."deluge/auth" = {
file = ../../secrets/deluge/auth.age;
owner = "deluge";
};
networking.wireguard.interfaces."downloads" = {
privateKeyFile = config.age.secrets."wireguard/downloads".path;
ips = [ "10.2.0.2/32" ];

View File

@ -63,11 +63,6 @@ in
runner = {
capacity = 3;
};
cache = {
enabled = true;
host = "10.108.27.2";
port = 41919;
};
};
};
@ -81,8 +76,6 @@ in
chain output {
type filter hook output priority 100; policy accept;
ct state { established, related } counter accept
ip daddr 10.0.0.0/8 drop
ip daddr 100.64.0.0/10 drop
ip daddr 172.16.0.0/12 drop

View File

@ -1,4 +1,4 @@
{ config, pkgs, lib, ... }:
{ config, pkgs, lib, nixpkgs-unstable, ... }:
let
cfg = config.custom.services.gitea;
@ -55,7 +55,7 @@ in
services.gitea = {
enable = true;
package = pkgs.unstable.gitea;
package = nixpkgs-unstable.legacyPackages.x86_64-linux.gitea;
mailerPasswordFile = config.age.secrets."gitea/mailer_password".path;
appName = "Hillion Gitea";

View File

@ -44,40 +44,20 @@ in
"bluetooth"
"default_config"
"esphome"
"fully_kiosk"
"flux"
"google_assistant"
"homekit"
"met"
"mobile_app"
"mqtt"
"otp"
"smartthings"
"sonos"
"sun"
"switchbot"
"waze_travel_time"
];
customComponents = with pkgs.home-assistant-custom-components; [
adaptive_lighting
];
config = {
default_config = { };
homeassistant = {
auth_providers = [
{ type = "homeassistant"; }
{
type = "trusted_networks";
trusted_networks = [ "10.239.19.4/32" ];
trusted_users = {
"10.239.19.4" = "fb4979873ecb480d9e3bb336250fa344";
};
allow_bypass_login = true;
}
];
};
recorder = {
db_url = "postgresql://@/homeassistant";
};
@ -87,8 +67,6 @@ in
trusted_proxies = with config.custom.dns.authoritative; [
ipv4.uk.co.hillion.ts.cx.boron
ipv6.uk.co.hillion.ts.cx.boron
ipv4.uk.co.hillion.ts.pop.sodium
ipv6.uk.co.hillion.ts.pop.sodium
];
};
@ -101,9 +79,6 @@ in
report_state = true;
expose_by_default = true;
exposed_domains = [ "light" ];
entity_config = {
"input_boolean.sleep_mode" = { };
};
};
homekit = [{
filter = {
@ -113,19 +88,25 @@ in
bluetooth = { };
adaptive_lighting = {
lights = [
"light.bedroom_lamp"
"light.bedroom_light"
"light.cubby_light"
"light.desk_lamp"
"light.hallway_light"
"light.living_room_lamp"
"light.living_room_light"
"light.wardrobe_light"
];
min_sunset_time = "21:00";
};
switch = [
{
platform = "flux";
start_time = "07:00";
stop_time = "23:59";
mode = "mired";
disable_brightness_adjust = true;
lights = [
"light.bedroom_lamp"
"light.bedroom_light"
"light.cubby_light"
"light.desk_lamp"
"light.hallway_light"
"light.living_room_lamp"
"light.living_room_light"
"light.wardrobe_light"
];
}
];
light = [
{
@ -133,7 +114,7 @@ in
lights = {
bathroom_light = {
unique_id = "87a4cbb5-e5a7-44fd-9f28-fec2d6a62538";
value_template = "{{ false if state_attr('script.bathroom_light_switch_if_on', 'last_triggered') > states.sensor.bathroom_motion_sensor_illuminance_lux.last_reported else states('sensor.bathroom_motion_sensor_illuminance_lux') | int > 500 }}";
value_template = "{{ states('sensor.bathroom_motion_sensor_illuminance_lux') | int > 500 }}";
turn_on = { service = "script.noop"; };
turn_off = { service = "script.bathroom_light_switch_if_on"; };
};
@ -164,13 +145,6 @@ in
}
];
input_boolean = {
sleep_mode = {
name = "Set house to sleep mode";
icon = "mdi:sleep";
};
};
# UI managed expansions
automation = "!include automations.yaml";
script = "!include scripts.yaml";

View File

@ -1,62 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.services.isponsorblocktv;
ver = "v2.2.1";
ctl = pkgs.writeScriptBin "isponsorblocktv-config" ''
#! ${pkgs.runtimeShell}
set -e
sudo systemctl stop podman-isponsorblocktv
sudo ${pkgs.podman}/bin/podman run \
--rm -it \
--uidmap=0:${toString config.users.users.isponsorblocktv.uid}:1 \
--gidmap=0:${toString config.users.groups.isponsorblocktv.gid}:1 \
-v ${cfg.dataDir}:/app/data \
ghcr.io/dmunozv04/isponsorblocktv:${ver} \
--setup-cli
sudo systemctl start podman-isponsorblocktv
'';
in
{
options.custom.services.isponsorblocktv = {
enable = lib.mkEnableOption "isponsorblocktv";
dataDir = lib.mkOption {
type = lib.types.str;
default = "/var/lib/isponsorblocktv";
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ ctl ];
users.groups.isponsorblocktv = {
gid = config.ids.gids.isponsorblocktv;
};
users.users.isponsorblocktv = {
home = cfg.dataDir;
createHome = true;
isSystemUser = true;
group = "isponsorblocktv";
uid = config.ids.uids.isponsorblocktv;
};
virtualisation.oci-containers.containers.isponsorblocktv = {
image = "ghcr.io/dmunozv04/isponsorblocktv:${ver}";
extraOptions = [
"--uidmap=0:${toString config.users.users.isponsorblocktv.uid}:1"
"--gidmap=0:${toString config.users.groups.isponsorblocktv.gid}:1"
];
volumes = [ "${cfg.dataDir}:/app/data" ];
};
systemd.tmpfiles.rules = [
"d ${cfg.dataDir} 0700 isponsorblocktv isponsorblocktv - -"
];
};
}

View File

@ -1,306 +0,0 @@
{ config, pkgs, lib, ... }:
let
cfg = config.custom.services.restic;
in
{
options.custom.services.restic = {
enable = lib.mkEnableOption "restic http server";
path = lib.mkOption {
type = lib.types.path;
default = "/var/lib/restic";
};
repos = lib.mkOption {
readOnly = true;
type = with lib.types; attrsOf (submodule {
options = {
path = lib.mkOption {
default = null;
type = nullOr str;
};
passwordFile = lib.mkOption {
default = null;
type = nullOr str;
};
environmentFile = lib.mkOption {
default = null;
type = nullOr str;
};
forgetConfig = lib.mkOption {
default = null;
type = nullOr (submodule {
options = {
timerConfig = lib.mkOption {
type = attrs;
};
opts = lib.mkOption {
type = listOf str;
};
};
});
};
clones = lib.mkOption {
default = [ ];
type = listOf (submodule {
options = {
timerConfig = lib.mkOption {
type = attrs;
};
repo = lib.mkOption {
type = str;
};
};
});
};
};
});
default = {
"128G" = {
path = "${cfg.path}/128G";
passwordFile = config.age.secrets."restic/128G.key".path;
forgetConfig = {
timerConfig = {
OnCalendar = "02:30";
RandomizedDelaySec = "1h";
};
opts = [
"--keep-last 48"
"--keep-within-hourly 7d"
"--keep-within-daily 1m"
"--keep-within-weekly 6m"
"--keep-within-monthly 24m"
];
};
clones = [
{
repo = "128G-wasabi";
timerConfig = {
OnBootSec = "30m";
OnUnitInactiveSec = "60m";
RandomizedDelaySec = "20m";
};
}
{
repo = "128G-backblaze";
timerConfig = {
OnBootSec = "30m";
OnUnitInactiveSec = "60m";
RandomizedDelaySec = "20m";
};
}
];
};
"1.6T" = {
path = "${cfg.path}/1.6T";
passwordFile = config.age.secrets."restic/1.6T.key".path;
forgetConfig = {
timerConfig = {
OnCalendar = "Wed, 02:30";
RandomizedDelaySec = "4h";
};
opts = [
"--keep-within-daily 14d"
"--keep-within-weekly 2m"
"--keep-within-monthly 18m"
];
};
clones = [
{
repo = "1.6T-wasabi";
timerConfig = {
OnBootSec = "30m";
OnUnitInactiveSec = "60m";
RandomizedDelaySec = "20m";
};
}
{
repo = "1.6T-backblaze";
timerConfig = {
OnBootSec = "30m";
OnUnitInactiveSec = "60m";
RandomizedDelaySec = "20m";
};
}
];
};
"128G-wasabi" = {
environmentFile = config.age.secrets."restic/128G-wasabi.env".path;
};
"1.6T-wasabi" = {
environmentFile = config.age.secrets."restic/1.6T-wasabi.env".path;
};
"128G-backblaze" = {
environmentFile = config.age.secrets."restic/128G-backblaze.env".path;
};
"1.6T-backblaze" = {
environmentFile = config.age.secrets."restic/1.6T-backblaze.env".path;
};
};
};
};
config = lib.mkIf cfg.enable {
age.secrets = {
"restic/128G.key" = {
file = ../../secrets/restic/128G.age;
owner = "restic";
group = "restic";
};
"restic/128G-wasabi.env".file = ../../secrets/restic/128G-wasabi.env.age;
"restic/128G-backblaze.env".file = ../../secrets/restic/128G-backblaze.env.age;
"restic/1.6T.key" = {
file = ../../secrets/restic/1.6T.age;
owner = "restic";
group = "restic";
};
"restic/1.6T-wasabi.env".file = ../../secrets/restic/1.6T-wasabi.env.age;
"restic/1.6T-backblaze.env".file = ../../secrets/restic/1.6T-backblaze.env.age;
};
services.restic.server = {
enable = true;
appendOnly = true;
extraFlags = [ "--no-auth" ];
dataDir = cfg.path;
listenAddress = "127.0.0.1:8000"; # TODO: can this be a Unix socket?
};
services.caddy = {
enable = true;
virtualHosts."restic.ts.hillion.co.uk".extraConfig = ''
bind ${config.custom.dns.tailscale.ipv4} ${config.custom.dns.tailscale.ipv6}
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy http://localhost:8000
'';
};
systemd =
let
mkRepoInfo = repo_cfg: (if (repo_cfg.passwordFile != null) then {
serviceConfig.LoadCredential = [
"password_file:${repo_cfg.passwordFile}"
];
environment = {
RESTIC_REPOSITORY = repo_cfg.path;
RESTIC_PASSWORD_FILE = "%d/password_file";
};
} else {
serviceConfig.EnvironmentFile = repo_cfg.environmentFile;
});
mkForgetService = name: repo_cfg:
if (repo_cfg.forgetConfig != null) then
({
description = "Restic forget service for ${name}";
serviceConfig = {
User = "restic";
Group = "restic";
};
script = ''
set -xe
${pkgs.restic}/bin/restic forget ${lib.strings.concatStringsSep " " repo_cfg.forgetConfig.opts} \
--prune \
--retry-lock 30m
'';
} // (mkRepoInfo repo_cfg)) else { };
mkForgetTimer = repo_cfg:
if (repo_cfg.forgetConfig != null) then {
wantedBy = [ "timers.target" ];
timerConfig = repo_cfg.forgetConfig.timerConfig;
} else { };
mkCloneService = from_repo: clone_cfg: to_repo: {
name = "restic-clone-${from_repo.name}-${to_repo.name}";
value = lib.mkMerge [
{
description = "Restic copy from ${from_repo.name} to ${to_repo.name}";
serviceConfig = {
User = "restic";
Group = "restic";
LoadCredential = [
"from_password_file:${from_repo.cfg.passwordFile}"
];
};
environment = {
RESTIC_FROM_PASSWORD_FILE = "%d/from_password_file";
};
script = ''
set -xe
${pkgs.restic}/bin/restic copy \
--from-repo ${from_repo.cfg.path} \
--retry-lock 30m
'';
}
(mkRepoInfo to_repo.cfg)
];
};
mkCloneTimer = from_repo: clone_cfg: to_repo: {
name = "restic-clone-${from_repo.name}-${to_repo.name}";
value = {
wantedBy = [ "timers.target" ];
timerConfig = clone_cfg.timerConfig;
};
};
mapClones = fn: builtins.listToAttrs (lib.lists.flatten (lib.mapAttrsToList
(
from_repo_name: from_repo_cfg: (builtins.map
(
clone_cfg: (fn
{ name = from_repo_name; cfg = from_repo_cfg; }
clone_cfg
{ name = clone_cfg.repo; cfg = cfg.repos."${clone_cfg.repo}"; }
)
)
from_repo_cfg.clones)
)
cfg.repos));
in
{
services = {
caddy = {
### HACK: Allow Caddy to restart if it fails. This happens because Tailscale
### is too late at starting. Upstream nixos caddy does restart on failure
### but it's prevented on exit code 1. Set the exit code to 0 (non-failure)
### to override this.
requires = [ "tailscaled.service" ];
after = [ "tailscaled.service" ];
serviceConfig = {
RestartPreventExitStatus = lib.mkForce 0;
};
};
}
// lib.mapAttrs' (name: value: lib.attrsets.nameValuePair ("restic-forget-" + name) (mkForgetService name value)) cfg.repos
// mapClones mkCloneService;
timers = lib.mapAttrs' (name: value: lib.attrsets.nameValuePair ("restic-forget-" + name) (mkForgetTimer value)) cfg.repos
// mapClones mkCloneTimer;
};
};
}

View File

@ -13,10 +13,7 @@ in
enable = true;
ipAddressAllow = [
"138.201.252.214/32"
"10.64.50.26/32"
"10.64.50.27/32"
"10.64.50.28/32"
"10.64.50.29/32"
"10.64.50.20/32"
];
};
};

View File

@ -10,14 +10,20 @@ in
dataDir = lib.mkOption {
type = lib.types.str;
default = "/var/lib/unifi";
readOnly = true; # NixOS module only supports this directory
};
};
config = lib.mkIf cfg.enable {
# Fix dynamically allocated user and group ids
users.users.unifi.uid = config.ids.uids.unifi;
users.groups.unifi.gid = config.ids.gids.unifi;
users.users.unifi = {
uid = config.ids.uids.unifi;
isSystemUser = true;
group = "unifi";
description = "UniFi controller daemon user";
home = "${cfg.dataDir}";
};
users.groups.unifi = {
gid = config.ids.gids.unifi;
};
services.caddy = {
enable = true;
@ -32,9 +38,21 @@ in
};
};
services.unifi = {
enable = true;
unifiPackage = pkgs.unifi8;
virtualisation.oci-containers.containers = {
"unifi" = {
image = "lscr.io/linuxserver/unifi-controller:8.0.24-ls221";
environment = {
PUID = toString config.ids.uids.unifi;
PGID = toString config.ids.gids.unifi;
TZ = "Etc/UTC";
};
volumes = [ "${cfg.dataDir}:/config" ];
ports = [
"8080:8080"
"8443:8443"
"3478:3478/udp"
];
};
};
};
}

View File

@ -75,7 +75,7 @@ in
};
services.restic.backups."zigbee2mqtt" = lib.mkIf cfg.backup {
repository = "rest:https://restic.ts.hillion.co.uk/1.6T";
repository = "rest:http://restic.tywin.storage.ts.hillion.co.uk/1.6T";
user = "zigbee2mqtt";
passwordFile = config.age.secrets."resilio/zigbee2mqtt/1.6T.key".path;

View File

@ -14,8 +14,6 @@ in
"jake".openssh.authorizedKeys.keys = [
"sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBBwJH4udKNvi9TjOBgkxpBBy7hzWqmP0lT5zE9neusCpQLIiDhr6KXYMPXWXdZDc18wH1OLi2+639dXOvp8V/wgAAAAEc3NoOg== jake@beryllium-keys"
"sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBPPJtW19jOaUsjmxc0+QibaLJ3J3yxPXSXZXwKT0Ean6VeaH5G8zG+zjt1Y6sg2d52lHgrRfeVl1xrG/UGX8qWoAAAAEc3NoOg== jakehillion@jakehillion-mbp"
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOt74U+rL+BMtAEjfu/Optg1D7Ly7U+TupRxd5u9kfN7oJnW4dJA25WRSr4dgQNq7MiMveoduBY/ky2s0c9gvIA= jake@jake-gentoo"
"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBC0uKIvvvkzrOcS7AcamsQRFId+bqPwUC9IiUIsiH5oWX1ReiITOuEo+TL9YMII5RyyfJFeu2ZP9moNuZYlE7Bs= jake@jake-mbp"
@ -50,6 +48,7 @@ in
"router.home.ts.hillion.co.uk".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAlCj/i2xprN6h0Ik2tthOJQy6Qwq3Ony73+yfbHYTFu";
"sodium.pop.ts.hillion.co.uk".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDQmG7v/XrinPmkTU2eIoISuU3+hoV4h60Bmbwd+xDjr";
"theon.storage.ts.hillion.co.uk".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN59psLVu3/sQORA4x3p8H3ei8MCQlcwX5T+k3kBeBMf";
"tywin.storage.ts.hillion.co.uk".publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGATsjWO0qZNFp2BhfgDuWi+e/ScMkFxp79N2OZoed1k";
};
programs.ssh.knownHostsFiles = [ ./github_known_hosts ];
};

View File

@ -1,10 +0,0 @@
{ config, lib, ... }:
{
imports = [
./global.nix
./home.nix
./iot.nix
./www-repo.nix
];
}

View File

@ -33,11 +33,6 @@ in
services.caddy = {
enable = true;
package = pkgs.unstable.caddy;
globalConfig = ''
email acme@hillion.co.uk
'';
virtualHosts = {
"hillion.co.uk".extraConfig = ''

View File

@ -1,27 +0,0 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.www.home;
locations = config.custom.locations.locations;
in
{
options.custom.www.home = {
enable = lib.mkEnableOption "home";
};
config = lib.mkIf cfg.enable {
services.caddy = {
enable = true;
virtualHosts = {
"homeassistant.home.hillion.co.uk".extraConfig = ''
bind 10.64.50.25
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
reverse_proxy http://${locations.services.homeassistant}:8123
'';
};
};
};
}

View File

@ -1,32 +0,0 @@
{ pkgs, lib, config, ... }:
let
cfg = config.custom.www.iot;
locations = config.custom.locations.locations;
in
{
options.custom.www.iot = {
enable = lib.mkEnableOption "iot";
};
config = lib.mkIf cfg.enable {
services.caddy = {
enable = true;
package = pkgs.unstable.caddy;
virtualHosts = {
"homeassistant.iot.hillion.co.uk".extraConfig = ''
bind 10.239.19.5
tls {
ca https://ca.ts.hillion.co.uk:8443/acme/acme/directory
}
@blocked not remote_ip 10.239.19.4
respond @blocked "<h1>Access Denied</h1>" 403
reverse_proxy http://${locations.services.homeassistant}:8123
'';
};
};
};
}

View File

@ -57,7 +57,7 @@ in
${pkgs.git}/bin/git clone ${cfg.remote} ${cfg.location}
else
cd ${cfg.location}
${pkgs.git}/bin/git remote set-url origin ${cfg.remote}
${pkgs.git} remote set-url origin ${cfg.remote}
${pkgs.git}/bin/git fetch
${pkgs.git}/bin/git reset --hard origin/${cfg.branch}
fi

View File

@ -1,15 +0,0 @@
#!/usr/bin/env nix-shell
#!nix-shell -i bash -p restic rsync
set -e
HOST="restic.ts.hillion.co.uk"
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd "$DIR"
rsync -ar --no-perms --delete-after --rsync-path='sudo -u restic rsync' --progress $HOST:/practical-defiant-coffee/backups/restic/128G/ restic/128G
echo 'checking 128G'
restic -r restic/128G check --read-data-subset=25%
touch last_synced

View File

@ -2,12 +2,12 @@
#!nix-shell -i bash -p restic rsync
set -e
HOST="restic.ts.hillion.co.uk"
HOST="tywin.storage.ts.hillion.co.uk"
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd "$DIR"
rsync -ar --no-perms --delete-after --rsync-path='sudo -u restic rsync' --progress $HOST:/practical-defiant-coffee/backups/restic/ restic
rsync -ar --no-perms --delete-after --rsync-path='sudo -u restic rsync' --progress $HOST:/data/backups/restic/ restic
echo 'checking 128G'
restic -r restic/128G check --read-data
@ -15,3 +15,4 @@ echo 'checking 1.6T'
restic -r restic/1.6T check --read-data
touch last_synced

Binary file not shown.

Binary file not shown.

View File

@ -1,31 +1,23 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
AiswwF+XOeVnSZ7QbCIG+4D+o5JxVYlw6d4R1GDj3orsEGPy8a83QtjiUiNDUwFO
/3GD3I6Hv5/H16s5kLVjxachAKKequNMtZ3R9y8pJaCa3i2Ypf0itlryoPT5MywA
vlgLzPHmc/eYdyOGWJ+Lfe5ZYq8cGSyWn7E2g5KzWxj3+Pn4onNwTCQBClPtebrw
E3mvTywpIvWQ5QyrJHIkFQERoKO1kprL9G5ikvgvGqC87SLPsOhvCX/5a83znKPo
8HMEUDIUbcyjaTusdXpXdRtFEtjDoXR2lDsbJB73LG7u9mBFwJNDPeI5ulWL/7kl
whb9xs87Z6JaEJFZOB1jwfsIUGQKVSoHAWNWfM6WT9tNkYfh+mulQOuE99rEwGVn
+/Mo5nYMfmb0hUeQEEVAdfko4w4NvWvUIq0keZ4M9IjfggOdDPM7wG41xKcqXJkz
d92e4Qj7fl8ldMcha7EJkXKTuP48zBeEmnWwMRY0UAbnHsIb0ko03Wi5fO8tKiHj
sl/asjk074jextEkhlzGQrDtJm2rK9GXuZVvtuIy+wKRWNs0sS7NqArlZj5jlUbB
sPYf5JeWBC3pEcdN73T+O4CIssOT5k7D7k8P2h8zw5Se2iMAjB50sFnCOe6Iiyfq
RHL/686aDDCJVqQ2tk0N25oT4h1Xk7dJZSNFaFh47zs
-> ssh-rsa GxPFJQ
in/cSQ8GJaS5GpEdJFXBC8no9M3BRcB+F98R1Xm+MSlyS153MVuNcGUAfEQ9RbOd
8nKFnJ0ZjvgWnE342cJPnOZJ9yrMi7pcRy8RXslMIIBXLnt5Tv32kJJSdWYfOyHc
aT3ZYQN3j+rqokLb2Axe95pWCXKj/wUyoBCYgC5evb8/ek6XRVA647bnET5DsD0J
HCTXanw3Nm6l6Uy/AJ87FM3tBaIzA/EIktZiS1ujY7yKOU5R0VPvmwZEFC8Y4dSE
k3FbG6aUcz9JP+BSA2bO3p3z89fyun7MFgPZt0xW7EVN+YQaamqdQOFtXZZAdDzq
jDVgoCbFm/g8bJzvFAFq+w
F1+/Im3cdSzHDBn2W5LAV+ezO23nrjzaeVLQ0pFNYMt6v8XTA/WHHTPsImg/Nebi
noLNcxgjPu3fSnubXJuhsL5kQ6qHWvjQlQ0cRVSnMvV4UvrrPyp0066q1iMBL3PA
rh4hQ7sF4sHz3pm9jtxfdnWamGxqsowE793AjtNEvSU/i9/ZkFrKRj0LLAhHeIHJ
dWdYpnlZIrRjXFo6y+jdYpK0gyGQopy4SwoRwvDmQ/5paBRhpiXYUPtwRckiz/KE
iHA0WJhDO9a5OHLN9WPnKVRIutH/YHviy4twyPDjhHCDeJBQE02Lykd6wvJg9cWT
BkCeYhVdGp7VpWZEbLCtnw
-> ssh-rsa K9mW1w
sSMw9YdkrWxDctoj4jEEd7d3Pzaar13X/AlYmaUjl4JFG5en372xl1TUvqX8GN0k
ZwtLyUdSZCubpu+2Hd17hMK06KYz0jyV8qobsepgifgEvsFCeGq356pd6wDWaoog
hARFKOgHgHV6ElHlwkeWjIeFkz5BunTUn3NTZGlbDFnhKnEw1dexy98ZRmrZ7vlO
6+55q81hL1eRsBuenq8/hF+5zsnJK6pNnfL34NlPU8uQ5EmHZoDxpsk0AhJAOijK
Jh18WlnsHdLganJpa7vx8fn/rPUJq+M5ieNCu+BFvta08UA7vmlW/OYkaFIn4weS
OfzizypwKK4Y1HV+PMdtKg
-> ssh-ed25519 RR/L5A iVk2PxSSxsqJ6TKq2kXOATgdBf3WXVikjuXTonkXnwQ
cbeSnGk8CAwz20mJUa2tkaJMNCbcU2tvzH+aRdJ1aPE
--- CaEXrA9MPkvFWguILIVe2FhmFbgeHMNTHXgvDBf3Hbk
Baüm'm™£ÏAOðOU!ýzÊ(ÿü#Ô°¾ÿ_?!g÷q" ù§žŽZƒ<5A>è|æ·šî÷aOó|HªüÐûSq&ÆÆn+w<>$áZ^ù Gg|à(HR,<06>àm1VÁÈÄû<C384>3]ÁE~püÃ/¥e÷²
Pm7esUbyDOoLdzxxrRkOXYxodhfKdK+X41nAT2AOR0bBcNHqGt8BSvL+HOK1vbOW
AquzA7+S91aNF7noV3JPIDrWZxDquqBp4z89YYyWNjpH9EIOYa2Sbp5xNFdgyAJw
C0uS3B/XKJcQi2np39szNzuR2h7uTKkKW5wMan+YUK1ODxmTJVibo0c0JUybMjXA
VS31BrkDb0LKRgvp/kNfn9aEaI7d78oca+6X0BDHk2zYRPwL1Q/MGw4tkx3pqI+x
i6frCSHlo715tHaYvxRQyimWthmUft/1etMasFINhLidx4ufdawLR6sbR09sCrMj
5NMeB1wc1XrGxwMumNDWyg
-> ssh-ed25519 nWv9MA CxCjLfbIfEn1voAr3jX+dRGnHmdAyylNuPn/2QfuphM
PS2cpu13uyPlo9PJIT+dybq4P2juNpiZY61sJq7DepQ
-> )ei-grease ,jk Bw
IucfaHwBWhzdMcTDXxOUyTy1kCUnSlsfAwVw8YKimsxHyvJS+wkyPXUzepHMZYId
SvebFA
--- q53LwYLjQ6p0yHStpDma4DYMpfFTpv7+MAzaHjM7Kaw
¹§—?0šîÕº9S>íƒÊìhàÛ(Æ Qå>8;þÓz°Øø¿L‰úPYhJÞsÛ@¥dÕÒœC׌<C397>9Ú¿{r|­±t¶¡aNv3'o•
ôÖ±¹d= í¦àZ<C3A0>`T62(€÷$ÓH°ŸZ:œQhµú<C2B5>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,31 +1,19 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
uM89I2iF5jhpx3ndhtroIDxXWMSZew8mSLLBiSisYM+CfjtGKOVCDvmOFNSgPz1g
96D93T9esUo+KSwWyhhEqUwEHyrdnHVPkTAQ73ie/ZoSvdCPmdSA0RgKbyYHSTp1
Z1hS/SN0xLSshz8aur/dd6FC01Ju0dqjDgCMN6edNhJfMhZXc2xidcj7Ir2T77B9
kFnLqAADHL8yyHfQzU7EIzWp857mcn4sbtPV5CuMfSvOzAdQWMxtFQndJUTnokTz
e3Gg57gkNOa50jYbr0fxN7fUBWu7J/MyOErwbRq5TzF/ZfbHkrH5iXvMBaNjSQy1
RVQozDXJ+j5oc14TQDbi59kOi4ZJmXJTk+eCFMTalYrHqdtTqV9c7KsOdiceeajp
wec3Iowgben4uqEgBluJtn5lRoDSdmKWbYSYxL3E/wX+0df8P9aZpTTEGGfhH+RY
yxhalaRYmLHD8kJ8yBlPdcKx9y6hkkE5Me+IB9Soh4pqtTbk3k/7pHLRYD2sq1T2
gxipaSumoj5trfPnp75qOTubnJ+a3l5FPiv3JOe0nOxY8zFuv7IcOsvcVipo1z+X
miuOVgbZeZrKb7//zVb0Dn1IJSZITY0SsWbNh8MVB7N5xN9iGWAmE9adrnbCKFIZ
PWteIw7/tlZHdUEr0QJwim1Br639Ah41mMspltsKA2c
-> ssh-rsa GxPFJQ
sGxzilQ4r6X/D1MXKuXVI4p8v6KGnqN+4WUqda8OknlK79PTQY58fL/Mtmep+syZ
D11IuciWOSbaH21ZH3fuUNuNz0CRq+JWgkLnI0HwcnXH/Ni2MvXTiX9ihHzLawDE
ApkC+UbpUcZ5qsOQp2Kh0hbtLj+vegsQeIiMej4g13Z0QS6P7ElX5lYkdv/TBjXS
b1AwwmSuhl/IPzfnvMqnwpa/QeyweC7aSPuuy4WQQwjJa7pAPvuyZl7Bi2KfSa/c
p3SJsGNMJxeNUrD0FC/k/Jn0qm1B0Hf7qHi1/0ax58j1uNQCfqv6/YG0uC+SGHjY
LcFKAvBPn3HvH672Ed6ytQ
bIDYJMyhT4Tt0oTNWGwahKnw7ewWky+O8J7ExiDqIRNskA99SJ45u4n03mc6uQl0
56gTQ53HEQIH7u4DfITmKTf5iSn6O7QDW0c15dJ7P1hz3JaPu4p/Lyuzy9B1RcUY
XjNhmRyIAJdUWla/+k+o/dLRp46CtK3nIoxLd8all7vPys8i2gBQIr4HmmkZ4Nd1
CFQCXbb58asmAcYta1nTTY5yvFq6IHDbWPZDGOYCWceFoPCjlkvVH4VJjTQ7uyLX
bopz3gKxUogk0RTuF0o8tfqeZzJhcXOB0tvMfpPmMnQh08u8t4j8kVlnv+Ru5ZsE
H95rbwVArprojzFBy1fcHQ
-> ssh-rsa K9mW1w
FmNlXwGIN5E/7btwU2TBzyEJq99RNWLE8+P1VSqXI3ZRvbq4sGMW8wpEYSlkYrLk
A9wsYA3x0U7oEt6pnJzuZkyVrc08k/7Jr8Oboscti7wPjbNyMCsPNHJU0CvpuVfh
lRVyy8Ir793jNkqvtWpJS203YyPyV9WwYzH8Q6QlKVs8VLi8KLv4truVbM4MfBQd
BiGYakuK+ckkoS3GBaSm8NNDV1+Ocf3M9RFJnqke7dQIS5tRyY2HCYzqZXbzDtSl
9CRaHnu/H46O3R9Fqq3314Uj94HoUe0SuayXBD7WCzkn9ZevsHZPhk9BHulHaRBT
ExOUcA6hCIDopz+xqErcRQ
-> ssh-ed25519 iWiFbA lsC4GR0vzRjpq20voTzHrzRuoU/aamdM3sIIHYpUQWc
KA5kjHA1lxdKM6Y2yK0rvtlUaB8EI8q5jMZPs/VywCg
--- q/m5+KxRTtf5vgXnAXd8HLGCCE9BfsDs/uJuGSGbw/Y
€@âºv<Y&4•[ýæð![U•<55>oÂ8³Œ`.Mt¤ÝSg¹.S5Tñg²˜»¼cp:g$¥f{eânñAdt¦Þò‰R@lŒ?
s/y+2ROtxC5AY/1KxqAYuFUJgV4QtrnhOwi+fF+61TKet/8ni1oEjvAiUwg40b9h
eNPGr/ohywXgAj77j8y/J3uWA6uXt1zhpoadZFw63Gdy236AAK6mqU+pf6tEFGYf
ZiD25Z+rhorfYsMS6CkWEVIrM8dgMAGri1pdhXTuoC7LYn9f7GRmcyujbwRUOT9Q
DXmdb6V1j5Q5I6f6TYljGdhp6I8KrAnHMBEcZBECfgTnHE3Hot51p/aoS1KeNcsb
Y9gjpQgNIcccx3CANNJ8y8iZpPzd6Qp5eFk0/IrqqMlGfYrVJ6hX37mV1pqPp4Z+
KTuVZ47PIByw6yabdJdmsg
-> ssh-ed25519 iWiFbA SMZ9gRd/HVCXD7q7W+D5ooyS+k3Nr/hoU2bbwmv/3mI
AZEH5d3rS9B27j52SYNt7wy2IanpmrYJor5OjZAzaVI
--- xWHau8xcErvYJZNB/HkjjfzWWvLrOqdI4ccGSmg/xjo
Ý׍vÄď˝J/XçM<C3A7>Mkě@Î9ämÎ"vĹ «äĆŻî—ÉŤç¨ H÷ń-×<>cŽuĹ…€Z†3nv\°ĘMĽ5đXşQ6

View File

@ -1,31 +1,19 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
HWrOsyGtCBR++xduIQUiaVMo8UplDwkbb6WnO6zOLdDD+ZRjje0X+jKbvpSD/k8m
d9p/0AnneewAPmP3xToEk9hRjhGwUcrYURH3ed7/1a+ef7wIGfeNfJfAe5muAUWE
OLNxu9s6OIRUK5f6EtHKto5IEhr/GoGWCCCxDJheXQ5e4DLzhrH3FxfWDXz7PqiH
trXWMploZArJ2f1RGr0wOJIhtHLcv5PxXpHz4ASx1m6pL+q3OdmIV7J+JQaDKVWE
LDa4tQnwcvFodB+g9U8a/KetsCyCdlQnpDqjVAYelMDz+IaMqz8htO/nTL8uS9bg
2AHdB7c1NiPzbI1M4G563jQ7q7n5eQB+nFSTiyXYfvz3tz47GlDvO3DiVtoRnfiY
Yp+K02/emsa6wcBVEPcLlWqACp3+DwL48BVll5XfPadXeR2jsv4JFuDfBSzhr7P8
Xb+PfXbcK3fbXykMrVlfuMfNgnnyFoyTSXqVByolU9lu+FHewgVkyFOhpOse/5eo
azm2Bg7DICsXiSXhlJj1iQmacjxdav771qqs71W68Vu3BgImKrRdgKJ1/DY/yZs+
UhyGN316EcMen+DhV1ZSD7FfO6A/HFB/GwoOgth+HAFCmL8XrDwWeSYHIqHmK9LI
z8rfksUGQPV1p1SL3UGBZZYO86ufXY9h0vdrKTUxGh0
-> ssh-rsa GxPFJQ
AaJUbzqcQ1JuhUECy6HAlijuUFCldS7YuLYnm6iv31IFK3W5X4k38CO5lHQGnzvy
GJKEhqETzgNk05MKu5kVWqPC/DwwO99JCmy6v3G88cTi1ekAox7OIwmeqnyT0NQX
Un8eiBECIT3WXfHUZ263n0b96s8YW/MDVlBi+EKjhj701qsjpl2b1aFiPUfETGZd
eyE0pi68mI3Mnp/WX6hR9DODAysJE0nubU/d50A+HtOSCNrrjguDuoEMhNgAPEjP
UzTMQ/+lrKJfIYRf+zvXnFbeUvQZu4Aswo+ghgwnNRYoYiEvbQyK+dhqQunHO6tI
e+A5GMBycd4IRz0AvCeEaA
EpPN7UHHRuytMr2vXy9CHzjVkH1iCt9LiTLhFsqXbL03Rk+X82q233Lm12f0sQvz
Hqjukkh9bU90TLKcEOFpKrU5FQwKUjzEy85A+4UoovWdJ8VwACOzoJf29Ys1bX3i
Xp4gUT7ne5+4afNwKXVFDS1YCPjIoQPu2cGw6iTNIYwVY5fNxz5y5ZHLkI9lOqJD
mT7jCgLLdkK8vJiDcu4Ofr21GaziQh93YXK69i3gyAt6pqSRyQdAfhMGkykOWdrO
FXMtpzT82UCvfbFbbRCRuSFga0uq5zx2cwvBD4Xagw8Dfg9RQO0rX9NAbbgcoyfG
qnk+0bYAk3pWfdXW9Z/psQ
-> ssh-rsa K9mW1w
e/qVub+2c0GRMuVkaGwyQ1I7GodKg0njuLCLhgWcGfqvov2aqbBy5QuTEIwPm/CM
Wp+LcPU+m0wyw/oAtiANeb1TNXRwX/k2oSbbbhN6912Sf/7QRJukgSWaT/l/TDYz
H9RW4EJ3ir2aGS+/rZRchhrbTgMdluH132Pkg9hy0Wybj+PnqUFf2RXk3bC6CfMB
uM1S6LdFPE09ublSk80U6M8Ihu8j2MSIQ+LSfHJJY3aoRZSr1DWz991+0WyC9fOQ
U885VoYh8tmw7rs0aw8gHLZ23Rx/Kf8S/ZQIM9ZVQnQrCEsGCmPXOZLbPM0hawXh
gQUICXoa4AXslSdBn+FG4g
-> ssh-ed25519 iWiFbA 0xrDzh2/OoYkwZck8r6VYNBsITWDuDBe+8AYYxvCiTM
jSlH57h8pxoeyTbUQQUoTTK0KwTvvg2LUggAJ8xUL9s
--- QGRYDjRpaQ369Yej6tuziZ/TJOjjDc7FGX9LRNuHYLU
~”Ubp§ÜƒÖ³Š"0·2çw´“KdŠ†K.÷Ïmк Þ¾4š¥NbÊôºÅŒGßçnb+AD-cDÙùÛ¡Ž?8fbãö“GÚ© <0C> ß‰<C39F>&
NaR245c+88dGflT9cG73bQOBxQsVi5x8JkMTrqjabzwzHpRiBUdtP+Ou1w+klOI4
cv1RLngEZH9jsSiEdvpvRkzE2ILOR/abgABXZi/4vl7iXiC8T23QSOPXnMxrAgpH
RV9B3GcSClb70+Lf3pJtPBVHVENhFVFvj5JgxQ2Zi6eMpcMuL18r/Szn4erk8zXQ
330oEau80X6WoPtRaSqSxVRrMGecGHdIE9chLosCf1x8CgIcYBTtviky+fDQMkKZ
iwueW1luuBj1AuP33jUqjeyyMaJ6SqSmaxGqGHGXA/ayxF8HnHU9AJlhPH+tEEbs
84Xu2vwg9ikUz7B1tTBYeA
-> ssh-ed25519 iWiFbA CuUeGNUBc5K+AkXBRvp7SUTJNoMDW0bWRnYs3ZhFSGM
UwwyxNA2L9q6yYK+BqYcqOq6F5CF+iCUpuceWsEj7ck
--- 3XKIweSg0UFqbadbOP0APwaLyquaEdoanlvndvxcQkk
ßüu _śnTx#H-7<>‰yó—Îç<>¨—B„ô»uµŐÉîj<C3AE>ZHĐ'&÷_đś#ľĽ3ŕHÍ˝0f  šBęą×Ö™ýčÂLĽîz

Binary file not shown.

View File

@ -1,29 +1,18 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
BZKLDxLAZZ/1TfFlNANKnS0NEzsrS45xVuppc21R8wx9OwzTGZIPJcEigYjbWCE1
XCvkvpw6oTk0RNXx6VdAdZ0dfvfOcU3PfR30/IeiEZyKioyO3gSPp1L2z4Qacz88
MZDsdFMKhivexSRcNl89Xa1LEDf30XYaLKMxhcgZ1b5Z8TEG5trIs2NKek0eHY5k
jBgfvAoKqILVeot8cRANoIhC7aVI6lvVxNVhKzshv/ZAADUN7AZgYyvD09i2qFRX
AP1Y8ld5Csi9pp1KQHn1qzu2ofGjkERUHDipfV7oL3C9s1/sJP2u1oBthqYhPi2R
YUv7k9c8jILWzYw0ryO5bvQJkUWW+SS4WuQAZA127+aq4XECAUiNS4R/aNKwzDK9
8N1k5vkzJLXdcmP8jL3D+03O7igV//CbxzbyLzJXP2dAQI85VW4oxsFiPBHLYqoo
Nq79K6iZll7FUvyPxiFEzll7N6wzM2nPIrDSJeeDxbfZiZzAW0FN7bWl4qS/olp6
sA62SyRPtLd1WugaULIeCa1hopJGUJM4kQpnXPu8uUIOpnHUbc+iGXMWqxTqmyZD
bH7oNIAtTQKXzZiO7M30rt85x1eiADZqWirH26yYufxg6KndUKe+SAsBZUAkoziO
0Lotv232mcwbyWrc3/+sk0rtEP8GYRlCFhP72EQW9dA
-> ssh-rsa GxPFJQ
EBc6e5bYK8E+adyWM+l4nKIHbun1S2nTh8sLJiZYd9EYdKG/2S/+Pe7vREB5I3Ab
5femntN0JeCTwzjjlbBZKyULAN85sSm0pGibT/RUneOBzmENp1hQSbvGTNK0a/l1
ELRxmlDvziU5DO0WK574lXdTdXTkk8SGCpvNBtwnTov3TqlcO36BoCE5Apw62b1g
7l5/P9hMnIQovAHorh96kqj/unSCTc4Y0ZsNX9IqUqWLLxYNJovUEORFUnPTSvWh
Lbxp7wVXRxccY2bnr2BzmCObzujH7o4K1x/7zxVjxXc4TqGsPT/pzemZ6vzY0LPA
FCCV6Mk41j0O6ez0OyerHw
kYf/CWnC4WoFnELMnW4b3k1df6r1sZn7JeLPqIvQ7haKwA0SUrbMGiyWui9ewn6s
VSZb7T7zCPFAgs546wiq9O3JzeQPtWdTFCA5F/U48ftbZsAL1YjINqm3ySUbg0O0
eh8wZnO4ludFG0bi0/7B6eDmDpfqn/TKLjDJkWiintYg1PMLMR0iDaF8D5oy0zCi
Yiz+sAWyYBVvUSD9PEdCPFu/q8ySj9upBxaObf/Z5pDC94I2Ex7NZm8lGoXyMmnk
vrhVuHc8eRcqqN+ID48JakdPopA/X7IvFIIJVh40yw4Qldzr7H+b7M98Wg0/UEiC
mZldTPzpKznZntXXx6O89Q
-> ssh-rsa K9mW1w
FHEQuY/JPZLSrSuHj8HTc+jNrxidPpM6qKAkZTl/0vYgWExdJtGi/IukNibjF47q
176i56gjkJZPts8FRgBFXUimBGUBiMCvEOV/Xc/ZK/Foz7JQuTpiNmxhPyVSrGhW
GgyXqLycs2KxWbIz7fPLghGXXEeq86z0yTKiQOx5IkkwZOdvUDmzpHrqnCNi4uG5
LfgzbAcZxhZEKEfOsqq46Gzww96uLmcLypnppUFfelioceMcMnzyXbwXYMDODoL4
c7rXqWqHA9Bu7P8uiPFeiGXuISQGsRUJVbK2GsT7lr6+WfSdTxJcOG/63nKWStbg
VkHYEs6vACX4gHb9BHvpLA
--- nPAuzxbz5u9828pB+pGjc8p63zEYMSyHjHdZZepEs1g
¢(dsÜç´WæâÆó3þo&<26>$÷îùIuÜßBF `PìíØHº¬}d4(H<>@&`kô«'öh«Õzqªâ2Š´[?0Ìt„<74>¾æU½ÊÈ
M89fPs6WcToz9Jr2YXrRGM26lHU/8Xk72OExtArEsqKpHcRw27EeOQpcXkQ1baro
0MFQTKRsMc2PV0S85BPlUIBrvIiTTHNr/eusKTpAlDwnwllGk26xdaqPO3SYXzMX
/zo65i6837vQZr1aRPg1LFzRMWfrj7tLGQPuuE9F68Q/BYaCBbEo8t1vVVVNibjt
1rGuaMnjJd7scHkJ+wEJURQQuwrIY4oH8obmrzjQrFbBiWPH/lIUH1OUv5SDbYul
/Oq0QEHws/o68EQxOKWrPWRu9P1A3xYu3I19YYZgrKr1G9c0+ZEtZsvfeqCE9w79
OOz3GtRRHRCLBDDzv8nfrA
--- e0EGzCgM5FbTE91OLwUaICK+yruDi8EAIBMGxFJXWLE
ªœ£®AòÂÙî{3·…óÚûí€ñ=Õ89ð9<C3B0>es<65>Gïj
!”Áü;ætr0¼î&Øgd;ü‚¢&Ã¥Í1£^§ 'ø6û<36>Wh^qœ

View File

@ -1,31 +1,19 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
YmBjkjwn3FjgeFaxQ9G5h3ex2ZGe1TStge4BZsHOPizJnaU0aGmN/e2hjgRRWBgc
Js+SrukIFZjrQ/z9qgEQhhktBkpn/y34a9jSeqYA7IUjBEb3puWFqREbkEtC91Wa
DGlJFnGQiFCZ5Z6cIj+CkylQme2UeFD8Mbzrnhslh3w3Y3M6oA0zbBmwsJZ3WtQv
1QUjkPoVEd7t5RDcvm7au+ezUFUm06TsVz77vYkm9P82/fq+u684kwsPTJY0AL6x
RXtAjN8y324nrwij/x1EHl1J9h4hYL8QT8S/9hnVQ5YuANAO3W39wRPedOyESIXt
MXssIp7mMdDzaWBBKsfGkV0SinN9LQ1m9lTEFthmhX76lrLUvlVCKIHMfZWUWf3i
fXf3AREcLCVcEqeWJw4Js8Phet7MsnWReDV/jrTFSISP7BJj5oSLtuLCRwa/imGk
rcZaCG14oo4Tnf4VJDV5UdjJQMo67/rROezv6wDKUW/NkNOKtN+fcErCLjiu2tGV
M9oe/G4sE6gCthy00BpeAJTiK8XDObnGAokBddS/SbhHyh3kZ6Zu49HWMBVW5dOV
RWmvKy++8nDft6oRVITOdxHoyurRUqSzN0p9K7HS6GDP78ThlDT9nigR1HAB1BMD
Ouh1N8tXdP4Ap5osPaOzcN5UWkdnFOPJ8+Fma8gQLZ4
-> ssh-rsa GxPFJQ
E9g838j5piMvfHhBSdMC5wDSkncDCP1GP2a5hVEL34Krn4+mWE9yRoYGTdcz5AK/
heVfg8h+NteAUYLcBw8B3TSxKKt1BBiQj3gGwX/mrMmOmOZD508JEQAVb6Bmvi5u
3DgxN9gci7iUF6BpaHcHi3U4Sl6bJK2oH6QmRFd/oM3c0f+0vDsTM1f0Yyt+Gab4
/DiGdSmvvTO9tq4sEd5Y1Y96HSBmRoCQU9VWv48/yx2HCiDioZH7vdgOdOkBWDX9
Ba1Qa0pev2PJFiCujCLLvb+6YWzlBlUKc6Awv7tEicExFUXbfiKVGxZmI50xY0Wl
KXRzoyUmAodsdp/HkwaoEA
oP+PAtaEFPKwY/p94ofdBlLtCJDzaOkE7jblE4COKCp6NcfjUot7a7G2rSzG3Z9A
cF3HcuKsMrG2Tth4ElBB0nwfaXUJZOBO3nrZaU0RGBBkxqooDVV2LzA+NG0HpIZ6
8Rd/Ch621gaBDYbSNFKLx5pAotqsARt13BMVY9nuifNGmWAamZ5UsJwZ/OhxKC5i
bkFZGeHZm4tilpsBEnh99PxofQmFy52AQhpx2UZETaD7yXvyEQjN9yBVKGhwA8Xs
xIRZLVgCbMv5lroYChpj/SiuoNhuaLo+05+3r5JnL/ODEl2dYZHkr2fAo31QUPnr
kmir2Nwoq4MlNELjQvSfdQ
-> ssh-rsa K9mW1w
GLZFcv1WXomvnn05f2db9MIAmDQXbD1dCpcNJNFK3Ax/efvgZxHNrppHZXDYwWn3
EkNDZVd9iMb+YZ8Zqa58TtTOhiNH8yTsZeq6jF3bM8Lc2ZoS+v/DqOA/pNALcS3b
LlIkIH4cZ/4cvrVc32+uRFhByLR5vgKuf+F/p41zzFUeA50rCFUR9/gzCByrxSRT
zW6z+PbgNARqbburCp8Jh7yJo74Phki8pizgDrfvWH7hoSYxtf/hbo2DobRntsnn
irlmXjXrXy3P41VoE+4PjbJpDBygAYy6uy0MwkSVXMc+y6i+txeMOw74amvOrF+f
gCaexeMDPmyGm3N2j7qDFg
-> ssh-ed25519 iWiFbA +U3hUeNDk/1M0AGubIlLJeJRiVG6yrLFeSxi5Q7rPBk
BVVyNpQ+BJOhl3/6gy96NXGGGA4nNHoUePDkIVj0xGU
--- 5udXYFRKZut6o3QTDjezohRg3uwaLSBo0IxC88yercs
`"íq·(9~úš‹:€S"ò±ê´1%ÉæNüÅ@â‹=†p„j?|Í_ñùeÈÊâdP)8`ÕEÀã¨FµAàæhÚÚJ<$ðò¢ÃtÌgüáP/z¶mÿHàu7Æja³|Ôsn÷Ð c ªz<àå5·¼>
GVNgJY5cse7GnU0UpgfBT2a8Ev0KeFC+Tfvj8Jd7Wgu7pYv/DlwIumJN2NcmU76S
zW1Z6we+Fs4DO83v+4Bug4d+m7oUbKxbUfgIDE4MDEkc4B/XUKv0Ex1VaO4lGh6h
0lRF4PO22OjyO4TT4tkLZgTAStq/vS20GhluEdVPp2ovSsn7KYLwmx81iBnfNDbm
0uEKAE2dj01BHSiRZ1rVj9OnTacrRpzp6mbVJxZqkUMJ+A/tMp1B7eTjWiGhUUGs
+de52Ba/ww1jM0BmbdemWS5SA1Kch1ttgnSFKIh24tYyRxX4AVsuYqPkIuH8+W4O
Jh0387AJaR+3+Tvweo88Tw
-> ssh-ed25519 iWiFbA xx3oYrX6/Z1srbxmAztZV5AJgYZn20UMvRnn4qrSoAU
98oe26dqhSE3eET7hwdV/jJTVu2ldBiZ6ysabwK97Co
--- qrw4HhyaHrpDmws4EoABLFF3HU30AaZFCt4qHKo6gUM
OnÄCfIÚìQÿlµç@‡?:5¸<09>Ö m*63ÿgtjÁÃïÖA8 ^;TÓx%;O»·»×ß׶YG¢€,mïÞÜ â¥ ®Éîs8Pƒ òR«\nòXA÷·w;—ÚXRMÛù2´òFÏ

View File

@ -1,32 +1,20 @@
age-encryption.org/v1
-> ssh-rsa JSzstA
D5yNwwuWnJ0EUY0GvE7/ZMVDOGJaqv212LZhLLoUEd50l6fgXhk9bhycbLCJcchN
Sw9qr+uryVvuWjemKH5YTSx9iTpFegXcB4ekCGopUV5UwMbXFh0mdSHu8ZWm8dXi
7FldNgEe57JTink8macAON+jcZya3sP5QDa3/MITLv0FZHvkUTCDGIZY9u0utkc1
TZlOivvY7vx2CP2y+2Jh3NTOqbncVzDpOnhE1l0Wc+Lcwz8yWWatigwcC2O53lk4
kDdBNGOPppx8AjHbDxPe22hN7XW4axCAgek8IobTfo9ROto+Mwb5DZXM2afTQGjj
rX49hTjksoi0vXXssX/fhp8PPHnHLU8MwURaqJKBwGBFjiIms67rPKc99hFHsJP9
BLF/ajs9yOZDDl1zwspP0koIBYTtrhxXF8mJ3azDFvVcFWBJcNdWd4PldW/71yEE
iA5Q1qDqhixRLkTcihYikekNZ410hmigPnotkxsRdHxKRjnNi1t7kLrWmA21VyCy
93FasIUZOCKpm7KjzJkFhfijTJCxaBu6e1qBfCJdCC0xUT+eTNWSOpF69ymOZ8n3
uDFqEUi2U0nw7BbVMjv4ny8Qi3dMNKASw3PcuHLxxeuHgqxrqkGcvpJZffCEefZt
C9bicJ7OcESTDyFp+t4Nu/+PJyTiOClVCDMi1IXBTy0
-> ssh-rsa GxPFJQ
JoNUBVV9WIA0Zx/+EjA47T5uRLEC9/wHsRR6aN51Ldd0Q5BzcM+bGCKM3yVLiUlC
ATiMcs3Z1PnpD5pgWlAbWRlvFO/HEPxofhXO6KK61KvhyIfwVfRxDf46WUERL9XM
28+YziZtobuhyYzf4knQumjkp3K7qOC64BqU2rXSwwHVqI/JdgMCf1H5w0bJi0rH
62RGy+D9vEnqwNPvnvwTMkOCvmFVcqCCJdpbOjZ9i3imsREO81aGdGz0daTm9FDU
iWrdpRcAnLFbxOkGn6tb5daTKut6hzpiV55yzN4LK8eo+gbVLUsrmCe8oz8EnSni
Cw0pDV2Y1YuNrbg0xWPiJw
XtQerUqo59DoCzMSSbYWK1fZR5uPzA20NJcpZ8YOCD2cPJvGiSN4ePnPLSSnyUAV
ei45c6sdRRRZEKcq/uabTWPqk1xymKv4CrLn4FjS9VviKNYW0p+oo0J9ummwE6sa
/REKRIQRrsP1HwSO0gaq6CQEyGr9NouybuDDS5AIGpvaRV9+F1Htc/Um5JmsGaeC
aUBSJdZGnUajg/NN+QBlhhndT1PevFNKaviOJB4ghZUv8V89KZUhZMLuWQHVSr6D
fPQLXCI7mDjv82rydG5NWzE3OiMycLlt3AXWE97G4dhrtQGMPb3KDeGaY2mXBQ5G
Qq+myMZJqx9LbQWPyBgLgQ
-> ssh-rsa K9mW1w
VuOJ56O13sQx72WUoSSPcjiFx5soxvzhr7RsmLibpoPKq859nXPObhsSqLm89zrp
1kI0AHcOKzuOV8DPI0e67b5NSaORUQMLCcbWhCI5Bgs486BEcwMMtPWDDq5X6z3/
V8mgHoh8iOIH65vxsU7TC9SG7CzYEQWOkuayrfYBi1eoiC8Zo7eiWoyskFa2MjLx
/QM0j4f6iLGIGi60dOgbaseZKLgKX+jYKWQCB8poRyLpCyWrf1o/ldBrJvDpT3s6
J+8tg/y4wJnZ8Pvdnmbeq+L6oz4+w7mC1phsvzQQg6sfSL1ZS80NKexgLnqR07Bq
O2lOHX6pciENCRDVY7YKPw
-> ssh-ed25519 iWiFbA rsOEDax/kHkNdNdNe+dMwJUKoWGcEchbqRTBz4OlUlY
McVrDhF2MuCtbBd7ibOWmJmXRzwDLIthhEv0FLlFi0Y
--- kCiH49m3rceEryHxukITIRQ4cmCUC0kHya+GYoBAeuk
’¡¦™÷>Á¼€Pßb?bñÊ,_
°!1#±Ü=ð¼â䈔® R׳ ]AcHíÛdTú8Ϥ‘
QjWGmW/CSl1+xidbzQ7rBjPI9gphRAqC9j4dTsQmK48WlnlG9fAX1w7s8zJI287c
VwtOndfP/yYe8zj14IB0bn/efJ4s9DXS1toSVHhHMXGRYNx4LZJtGrHPEe7bmBDR
g3m6i3dFB48o4niZwLFmsTWD8KpYFPRmGnD9AX7cXtA1KzjD61ebb7dsjQ0J7/04
QLUmPUJ4BMgvMhv5zHfjq/LjasrOVY5QkKi95xYVREacy9hp/5COs9d0d40+qLFh
QJ2CuiOba5+aYnVWtbq63F9YJsAwknPOI2nV64l1BflQGJjec+wS4LPi4+l6pT+0
wPStv+iVu5vYdVNuq7KtnQ
-> ssh-ed25519 iWiFbA Qrs30RtQXXEglsSKQbmeLhZvQB9yR0EbKeba8IV1/wo
o89IKeFH91zdqfRJrIge0Sod6k+66BbN3DcUfnqSgqg
--- zhvOvBCbfuhWDejcJcXxtdypACwgPb1KrqpsNYWiKNc
å<}š‡: ¸Ã2bzªüÙÄq¬SYîÌõr掄Ý}¯â<C2AF> u—»ÌòbŸ\;

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