Merge branch 'main' of git.pub.solar:b12f/os

This commit is contained in:
Benjamin Yule Bädorf 2024-08-30 14:58:47 +02:00
commit 4fb46398d3
Signed by: b12f
GPG key ID: 729956E1124F8F26
96 changed files with 2121 additions and 1653 deletions

View file

@ -6,7 +6,8 @@ end_of_line = lf
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
charset = utf-8 charset = utf-8
indent_style = tab indent_style = space
indent_size = 2
# Ignore diffs/patches # Ignore diffs/patches
[*.{diff,patch}] [*.{diff,patch}]
@ -19,8 +20,8 @@ indent_style = unset
indent_size = unset indent_size = unset
[{.*,secrets}/**] [{.*,secrets}/**]
end_of_line = unset end_of_line = false
insert_final_newline = unset insert_final_newline = false
trim_trailing_whitespace = unset trim_trailing_whitespace = unset
charset = unset charset = unset
indent_style = unset indent_style = unset

View file

@ -169,6 +169,27 @@
"type": "github" "type": "github"
} }
}, },
"devshell_2": {
"inputs": {
"nixpkgs": [
"mezza-biz",
"nixpkgs"
]
},
"locked": {
"lastModified": 1722113426,
"narHash": "sha256-Yo/3loq572A8Su6aY5GP56knpuKYRvM2a1meP9oJZCw=",
"owner": "numtide",
"repo": "devshell",
"rev": "67cce7359e4cd3c45296fb4aaf6a19e2a9c757ae",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"flake-compat": { "flake-compat": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -241,6 +262,24 @@
"inputs": { "inputs": {
"nixpkgs-lib": "nixpkgs-lib_3" "nixpkgs-lib": "nixpkgs-lib_3"
}, },
"locked": {
"lastModified": 1722555600,
"narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_4": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib_4"
},
"locked": { "locked": {
"lastModified": 1714606777, "lastModified": 1714606777,
"narHash": "sha256-bMkNmAXLj8iyTvxaaD/StcLSadbj1chPcJOjtuVnLmA=", "narHash": "sha256-bMkNmAXLj8iyTvxaaD/StcLSadbj1chPcJOjtuVnLmA=",
@ -379,6 +418,28 @@
"type": "github" "type": "github"
} }
}, },
"mezza-biz": {
"inputs": {
"devshell": "devshell_2",
"flake-parts": "flake-parts_3",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1724541053,
"narHash": "sha256-bQiwF08H8GEi7lxNiJKc4Gu42K7zYeDPPqRCNYVnp7U=",
"ref": "refs/heads/main",
"rev": "0ee615488dec2685cee6ed558cbfcf9840e92b94",
"revCount": 10,
"type": "git",
"url": "https://git.pub.solar/b12f/mezza.biz.git"
},
"original": {
"type": "git",
"url": "https://git.pub.solar/b12f/mezza.biz.git"
}
},
"mobile-nixos": { "mobile-nixos": {
"flake": false, "flake": false,
"locked": { "locked": {
@ -415,7 +476,7 @@
}, },
"nixd": { "nixd": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_3", "flake-parts": "flake-parts_4",
"flake-root": "flake-root", "flake-root": "flake-root",
"nixpkgs": "nixpkgs_3" "nixpkgs": "nixpkgs_3"
}, },
@ -505,6 +566,18 @@
} }
}, },
"nixpkgs-lib_3": { "nixpkgs-lib_3": {
"locked": {
"lastModified": 1722555339,
"narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
}
},
"nixpkgs-lib_4": {
"locked": { "locked": {
"dir": "lib", "dir": "lib",
"lastModified": 1714253743, "lastModified": 1714253743,
@ -524,11 +597,11 @@
}, },
"nixpkgs-master": { "nixpkgs-master": {
"locked": { "locked": {
"lastModified": 1723210907, "lastModified": 1724505469,
"narHash": "sha256-bDREVGFdYVZDhkipgFSJSTxZMzsNl1wg1EM6Li5eWlk=", "narHash": "sha256-U0KAINJreo0RbZ2QbA4Y5EhWO7XERFRlkJdrRIncjn8=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d6037c7090df9b7fb9f8d421570d25553713e264", "rev": "59fbe04a3baa1011fe9f6eb00a1afb7db5179933",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -540,11 +613,11 @@
}, },
"nixpkgs-unstable": { "nixpkgs-unstable": {
"locked": { "locked": {
"lastModified": 1722813957, "lastModified": 1724224976,
"narHash": "sha256-IAoYyYnED7P8zrBFMnmp7ydaJfwTnwcnqxUElC1I26Y=", "narHash": "sha256-Z/ELQhrSd7bMzTO8r7NZgi9g5emh+aRKoCdaAv5fiO0=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "cb9a96f23c491c081b38eab96d22fa958043c9fa", "rev": "c374d94f1536013ca8e92341b540eba4c22f9c62",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -588,11 +661,11 @@
}, },
"nixpkgs_4": { "nixpkgs_4": {
"locked": { "locked": {
"lastModified": 1722987190, "lastModified": 1724316499,
"narHash": "sha256-68hmex5efCiM2aZlAAEcQgmFI4ZwWt8a80vOeB/5w3A=", "narHash": "sha256-Qb9MhKBUTCfWg/wqqaxt89Xfi6qTD3XpTzQ9eXi3JmE=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "21cc704b5e918c5fbf4f9fff22b4ac2681706d90", "rev": "797f7dc49e0bc7fab4b57c021cdf68f595e47841",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -634,6 +707,7 @@
"home-manager": "home-manager_2", "home-manager": "home-manager_2",
"impermanence": "impermanence", "impermanence": "impermanence",
"invoiceplane-template": "invoiceplane-template", "invoiceplane-template": "invoiceplane-template",
"mezza-biz": "mezza-biz",
"mobile-nixos": "mobile-nixos", "mobile-nixos": "mobile-nixos",
"musnix": "musnix", "musnix": "musnix",
"nixd": "nixd", "nixd": "nixd",

View file

@ -46,10 +46,13 @@
invoiceplane-template.url = "git+https://git.pub.solar/b12f/invoiceplane-templates.git"; invoiceplane-template.url = "git+https://git.pub.solar/b12f/invoiceplane-templates.git";
invoiceplane-template.inputs.nixpkgs.follows = "nixpkgs"; invoiceplane-template.inputs.nixpkgs.follows = "nixpkgs";
mezza-biz.url = "git+https://git.pub.solar/b12f/mezza.biz.git";
mezza-biz.inputs.nixpkgs.follows = "nixpkgs";
}; };
outputs = inputs@{ self, ... }: outputs = inputs @ {self, ...}:
inputs.flake-parts.lib.mkFlake { inherit inputs; } { inputs.flake-parts.lib.mkFlake {inherit inputs;} {
systems = [ systems = [
"x86_64-linux" "x86_64-linux"
"aarch64-linux" "aarch64-linux"
@ -65,7 +68,13 @@
./overlays ./overlays
]; ];
perSystem = args@{ system, pkgs, lib, config, ... }: { perSystem = args @ {
system,
pkgs,
lib,
config,
...
}: {
_module.args = { _module.args = {
inherit inputs; inherit inputs;
pkgs = import inputs.nixpkgs { pkgs = import inputs.nixpkgs {
@ -81,6 +90,7 @@
devShells.default = pkgs.mkShell { devShells.default = pkgs.mkShell {
packages = with pkgs; [ packages = with pkgs; [
nix nix
nixd
agenix agenix
age-plugin-yubikey age-plugin-yubikey
cachix cachix
@ -97,6 +107,7 @@
deploy-rs deploy-rs
terraform-ls
opentofu opentofu
terraform-backend-git terraform-backend-git

View file

@ -1,4 +1,4 @@
{ ... }: { {...}: {
imports = [ imports = [
./configuration.nix ./configuration.nix
./hardware-configuration.nix ./hardware-configuration.nix

View file

@ -39,6 +39,6 @@ in {
]; ];
}; };
networking.firewall.allowedUDPPorts = [ 34197 ]; networking.firewall.allowedUDPPorts = [34197];
networking.firewall.allowedTCPPorts = [ 34197 ]; networking.firewall.allowedTCPPorts = [34197];
} }

View file

@ -1,5 +1,9 @@
{ withSystem, self, inputs, ...}:
{ {
withSystem,
self,
inputs,
...
}: {
flake = { flake = {
nixosConfigurations = { nixosConfigurations = {
stroopwafel = self.nixos-flake.lib.mkLinuxSystem { stroopwafel = self.nixos-flake.lib.mkLinuxSystem {
@ -137,7 +141,7 @@
self.nixosModules.graphical self.nixosModules.graphical
self.nixosModules.audio self.nixosModules.audio
self.nixosModules.bluetooth self.nixosModules.bluetooth
({ ... }: { pub-solar.graphical.wayland.software-renderer.enable = true; }) ({...}: {pub-solar.graphical.wayland.software-renderer.enable = true;})
]; ];
}; };
}; };

View file

@ -23,7 +23,7 @@ in {
# Hack so that network is considered up by boot.initrd.network and postCommands gets executed. # Hack so that network is considered up by boot.initrd.network and postCommands gets executed.
"ip=127.0.0.1:::::lo:none" "ip=127.0.0.1:::::lo:none"
]; ];
boot.initrd.availableKernelModules = [ "tg3" ]; boot.initrd.availableKernelModules = ["tg3"];
boot.initrd.network = { boot.initrd.network = {
enable = true; enable = true;
ssh = { ssh = {

View file

@ -1,17 +1,21 @@
# Do not modify this file! It was generated by nixos-generate-config # Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes # and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead. # to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{ {
imports = config,
[ (modulesPath + "/installer/scan/not-detected.nix") lib,
]; pkgs,
modulesPath,
...
}: {
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "xhci_pci" "ahci" "ehci_pci" "usbhid" "usb_storage" "uas" "sd_mod" ]; boot.initrd.availableKernelModules = ["xhci_pci" "ahci" "ehci_pci" "usbhid" "usb_storage" "uas" "sd_mod"];
boot.initrd.kernelModules = [ "dm-snapshot" "amdgpu" ]; boot.initrd.kernelModules = ["dm-snapshot" "amdgpu"];
boot.kernelModules = [ "kvm-amd" ]; boot.kernelModules = ["kvm-amd"];
boot.extraModulePackages = [ ]; boot.extraModulePackages = [];
boot.initrd.luks.devices = { boot.initrd.luks.devices = {
"cryptroot" = { "cryptroot" = {
@ -21,42 +25,42 @@
"cryptdata".device = "/dev/sda1"; "cryptdata".device = "/dev/sda1";
}; };
fileSystems."/" = fileSystems."/" = {
{ device = "none"; device = "none";
fsType = "tmpfs"; fsType = "tmpfs";
}; };
fileSystems."/nix" = fileSystems."/nix" = {
{ device = "/dev/disk/by-uuid/837cc93f-6d9a-4bfd-b089-29ac6d68127c"; device = "/dev/disk/by-uuid/837cc93f-6d9a-4bfd-b089-29ac6d68127c";
fsType = "ext4"; fsType = "ext4";
neededForBoot = true; neededForBoot = true;
}; };
fileSystems."/persist" = fileSystems."/persist" = {
{ device = "/dev/disk/by-uuid/a7711118-51b0-4d84-8f18-ef2e06084e05"; device = "/dev/disk/by-uuid/a7711118-51b0-4d84-8f18-ef2e06084e05";
fsType = "ext4"; fsType = "ext4";
neededForBoot = true; neededForBoot = true;
}; };
fileSystems."/home" = fileSystems."/home" = {
{ device = "/dev/disk/by-uuid/0965d496-ffad-4a8d-9de7-28af903baf16"; device = "/dev/disk/by-uuid/0965d496-ffad-4a8d-9de7-28af903baf16";
fsType = "ext4"; fsType = "ext4";
}; };
fileSystems."/boot" = fileSystems."/boot" = {
{ device = "/dev/disk/by-uuid/991E-79C1"; device = "/dev/disk/by-uuid/991E-79C1";
fsType = "vfat"; fsType = "vfat";
neededForBoot = true; neededForBoot = true;
}; };
fileSystems."/data" = fileSystems."/data" = {
{ device = "/dev/disk/by-uuid/5fc34ef4-207b-45fb-b846-dbb01080d9fe"; device = "/dev/disk/by-uuid/5fc34ef4-207b-45fb-b846-dbb01080d9fe";
fsType = "ext4"; fsType = "ext4";
}; };
swapDevices = swapDevices = [
[ { device = "/dev/disk/by-uuid/0ef8dbbd-2832-4fb2-8a52-86682822f769"; } {device = "/dev/disk/by-uuid/0ef8dbbd-2832-4fb2-8a52-86682822f769";}
]; ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware; hardware.cpu.amd.updateMicrocode = lib.mkDefault config.hardware.enableRedistributableFirmware;

View file

@ -17,7 +17,7 @@
}; };
# from https://jellyfin.org/docs/general/networking/index.html # from https://jellyfin.org/docs/general/networking/index.html
networking.firewall.allowedUDPPorts = [ 1900 7359 ]; networking.firewall.allowedUDPPorts = [1900 7359];
security.acme.certs = { security.acme.certs = {
"media.b12f.io" = {}; "media.b12f.io" = {};

View file

@ -10,7 +10,12 @@
networking.interfaces.enp2s0f1.useDHCP = true; networking.interfaces.enp2s0f1.useDHCP = true;
networking.interfaces.enp2s0f0 = { networking.interfaces.enp2s0f0 = {
ipv6.addresses = [ { address = "2a02:908:5b1:e3c0:3::"; prefixLength = 64; } ]; ipv6.addresses = [
{
address = "2a02:908:5b1:e3c0:3::";
prefixLength = 64;
}
];
}; };
# Allow pub.solar restic backups # Allow pub.solar restic backups

View file

@ -9,7 +9,6 @@ with lib; let
psCfg = config.pub-solar; psCfg = config.pub-solar;
xdg = config.home-manager.users."${psCfg.user.name}".xdg; xdg = config.home-manager.users."${psCfg.user.name}".xdg;
in { in {
# Use the systemd-boot EFI boot loader. # Use the systemd-boot EFI boot loader.
boot.loader.systemd-boot.enable = true; boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true; boot.loader.efi.canTouchEfiVariables = true;
@ -20,13 +19,13 @@ in {
"ip=127.0.0.1:::::lo:none" "ip=127.0.0.1:::::lo:none"
]; ];
boot.initrd.availableKernelModules = [ "virtio_pci" "virtio_net" ]; boot.initrd.availableKernelModules = ["virtio_pci" "virtio_net"];
boot.initrd.network = { boot.initrd.network = {
enable = true; enable = true;
ssh = { ssh = {
enable = true; enable = true;
port = 2222; port = 2222;
hostKeys = [ /boot/initrd-ssh-key ]; hostKeys = [/boot/initrd-ssh-key];
authorizedKeys = flake.self.publicKeys; authorizedKeys = flake.self.publicKeys;
shell = "/bin/cryptsetup-askpass"; shell = "/bin/cryptsetup-askpass";
}; };
@ -44,7 +43,7 @@ in {
''; '';
}; };
boot.supportedFilesystems = [ "zfs" ]; boot.supportedFilesystems = ["zfs"];
# Copy the NixOS configuration file and link it from the resulting system # Copy the NixOS configuration file and link it from the resulting system
# (/run/current-system/configuration.nix). This is useful in case you # (/run/current-system/configuration.nix). This is useful in case you

View file

@ -5,9 +5,15 @@
lib, lib,
... ...
}: let }: let
# hzDomain = lib.concatStrings [ "hw" "dz" "z." "net" ]; hzDomain = lib.concatStrings ["hw" "dz" "z." "net"];
dkimDNSb12fio = '' dkimDNSb12fio = ''
default._domainkey IN TXT ( "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyla9hW3TvoXvZQxwzaJ4SZ9ict1HU3E6+FWlwNIgE6tIpTCyRJtiSIUDqB8TLTIBoxIs+QQBXZi+QUi3Agu6OSY2RiV0EwO8+oOOqOD9pERftc/aqe51cXuv4kPqwvpXEBwrXFWVM+VxivEubUJ7eKkFyXJpelv0LslXv/MmYbUyed6dF+reOGZCsvnbiRv74qdxbAL/25j62E8WrnxzJwhUtx/JhdBOjsHBvuw9hy6rZsVJL9eXayWyGRV6qmsLRzsRSBs+mDrgmKk4dugADd11+A03ics3i8hplRoWDkqnNKz1qy4f5TsV6v9283IANrAzRfHwX8EvNiFsBz+ZCQIDAQAB" ) ; default._domainkey IN TXT ( "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyla9hW3TvoXvZQxwzaJ4SZ9ict1HU3E6+FWlwNIgE6tIpTCyRJtiSIUDqB8TLTIBoxIs+QQBXZi+QUi3Agu6OSY2RiV0EwO8+oOOqOD9pERftc/aqe51cXuv4kPqwvpXEBwrXFWVM+VxivEubUJ7eKkFyXJpelv0LslXv/MmYbUyed6dF+reOGZCsvnbiRv74qdxbAL/25j62E8WrnxzJwhUtx/JhdBOjsHBvuw9hy6rZsVJL9eXayWyGRV6qmsLRzsRSBs+mDrgmKk4dugADd11+A03ics3i8hplRoWDkqnNKz1qy4f5TsV6v9283IANrAzRfHwX8EvNiFsBz+ZCQIDAQAB" ) ;
'';
dkimDNSmezzabiz = ''
default._domainkey IN TXT ( "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG8iuDq0eon2k7QlBJWGxwDiEv53iJQu2uqxOjr7Ul/nfQjuR6kVKs6oOVopnyFTGRpffrpSHHW1YUN5nF76p0fJphk4l+QmJP36/xweajsNU27PAkb88xG6yRKl28MCfPdMR96+Jobpei8S0UhqcskYs1aZybm7ci9ZuAMidziwIDAQAB" ) ;
'';
dkimDNShzDomain = ''
default._domainkey IN TXT ( "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDvVA2XZno6g6qBdmxoLgX2Qmd883M6yV4YkE/VaNH6xcR0AcTo4hEYoAOPryfKn4FE/TYvyk/k2cyBKpMBn2qbVhwUavYQh/e9bweS2FKQvdzCUUoqXk04o2MqSXb2ZFwkUCtfrPcckBgpF754PDL4HMZGPnkMSdDX7bmYe37CWQIDAQAB") ;
''; '';
in { in {
age.secrets."b12f.io-dkim-private-rsa" = { age.secrets."b12f.io-dkim-private-rsa" = {
@ -23,19 +29,49 @@ in {
owner = "maddy"; owner = "maddy";
}; };
users.users.maddy.extraGroups = [ "nginx" ]; age.secrets."mezza.biz-dkim-private-rsa" = {
file = "${flake.self}/secrets/mezza.biz-dkim-private-rsa.age";
security.acme.certs = { path = "/var/lib/maddy/dkim_keys/mezza.biz_default.key";
"mail.b12f.io" = { mode = "400";
reloadServices = [ "maddy" ]; owner = "maddy";
};
"b12f.io" = {
reloadServices = [ "maddy" ];
};
"mta-sts.b12f.io" = {};
}; };
services.nginx.virtualHosts = builtins.foldl' (hosts: hostName: hosts // { age.secrets."mail@mezza.biz-password" = {
file = "${flake.self}/secrets/mail@mezza.biz-password.age";
mode = "400";
owner = "maddy";
};
age.secrets."hzdomain-dkim-private-rsa" = {
file = "${flake.self}/secrets/hzdomain-dkim-private-rsa.age";
path = "/var/lib/maddy/dkim_keys/hzdomain_default.key";
mode = "400";
owner = "maddy";
};
age.secrets."mail@hzdomain-password" = {
file = "${flake.self}/secrets/mail@hzdomain-password.age";
mode = "400";
owner = "maddy";
};
users.users.maddy.extraGroups = ["nginx"];
security.acme.certs = {
"mail.b12f.io".reloadServices = ["maddy"];
"b12f.io".reloadServices = ["maddy"];
"mta-sts.b12f.io" = {};
"mail.mezza.biz".reloadServices = ["maddy"];
"mezza.biz".reloadServices = ["maddy"];
"mta-sts.mezza.biz" = {};
"mail.${hzDomain}".reloadServices = ["maddy"];
"${hzDomain}".reloadServices = ["maddy"];
"mta-sts.${hzDomain}" = {};
};
services.nginx.virtualHosts = builtins.foldl' (hosts: hostName:
hosts
// {
"mta-sts.${hostName}" = { "mta-sts.${hostName}" = {
forceSSL = true; forceSSL = true;
useACMEHost = "mta-sts.${hostName}"; useACMEHost = "mta-sts.${hostName}";
@ -52,21 +88,23 @@ in {
tryFiles = "$uri $uri/ =404"; tryFiles = "$uri $uri/ =404";
}; };
}; };
}) {} [ "b12f.io" ]; }) {} ["b12f.io" "mezza.biz" hzDomain];
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d '/run/maddy' 0750 maddy maddy - -" "d '/run/maddy' 0750 maddy maddy - -"
]; ];
system.activationScripts.makeMaddyDKIMDNS = lib.stringAfter [ "var" ] '' system.activationScripts.makeMaddyDKIMDNS = lib.stringAfter ["var"] ''
mkdir -p /var/lib/maddy/dkim_keys mkdir -p /var/lib/maddy/dkim_keys
echo '${dkimDNSb12fio}' >> /var/lib/maddy/dkim_keys/b12f.io_default.dns echo '${dkimDNSb12fio}' >> /var/lib/maddy/dkim_keys/b12f.io_default.dns
echo '${dkimDNSmezzabiz}' >> /var/lib/maddy/dkim_keys/mezza.biz_default.dns
echo '${dkimDNShzDomain}' >> /var/lib/maddy/dkim_keys/${hzDomain}_default.dns
chown -R maddy:maddy /var/lib/maddy chown -R maddy:maddy /var/lib/maddy
''; '';
networking.firewall.allowedTCPPorts = [ 25 ]; networking.firewall.allowedTCPPorts = [25];
networking.firewall.interfaces.wg-private.allowedTCPPorts = [ 465 587 993 ]; networking.firewall.interfaces.wg-private.allowedTCPPorts = [465 587 993];
services.maddy = { services.maddy = {
enable = true; enable = true;
@ -76,14 +114,22 @@ in {
localDomains = [ localDomains = [
"b12f.io" "b12f.io"
"mail.b12f.io" "mail.b12f.io"
"mezza.biz"
"mail.mezza.biz"
hzDomain
"mail.${hzDomain}"
]; ];
ensureAccounts = [ ensureAccounts = [
"mail@b12f.io" "mail@b12f.io"
"mail@mezza.biz"
"mail@${hzDomain}"
]; ];
ensureCredentials = { ensureCredentials = {
# Do not use this in production. This will make passwords world-readable # Do not use this in production. This will make passwords world-readable
# in the Nix store # in the Nix store
"mail@b12f.io".passwordFile = config.age.secrets."mail@b12f.io-password".path; "mail@b12f.io".passwordFile = config.age.secrets."mail@b12f.io-password".path;
"mail@mezza.biz".passwordFile = config.age.secrets."mail@mezza.biz-password".path;
"mail@${hzDomain}".passwordFile = config.age.secrets."mail@hzdomain-password".path;
}; };
tls = { tls = {
loader = "file"; loader = "file";
@ -96,6 +142,22 @@ in {
keyPath = "${config.security.acme.certs."b12f.io".directory}/key.pem"; keyPath = "${config.security.acme.certs."b12f.io".directory}/key.pem";
certPath = "${config.security.acme.certs."b12f.io".directory}/cert.pem"; certPath = "${config.security.acme.certs."b12f.io".directory}/cert.pem";
} }
{
keyPath = "${config.security.acme.certs."mail.mezza.biz".directory}/key.pem";
certPath = "${config.security.acme.certs."mail.mezza.biz".directory}/cert.pem";
}
{
keyPath = "${config.security.acme.certs."mezza.biz".directory}/key.pem";
certPath = "${config.security.acme.certs."mezza.biz".directory}/cert.pem";
}
{
keyPath = "${config.security.acme.certs."mail.${hzDomain}".directory}/key.pem";
certPath = "${config.security.acme.certs."mail.${hzDomain}".directory}/cert.pem";
}
{
keyPath = "${config.security.acme.certs."${hzDomain}".directory}/key.pem";
certPath = "${config.security.acme.certs."${hzDomain}".directory}/cert.pem";
}
]; ];
}; };
config = '' config = ''
@ -145,7 +207,7 @@ in {
# replace rcpt to catchall and deliver it there # replace rcpt to catchall and deliver it there
destination $(local_domains) { destination $(local_domains) {
modify { modify {
replace_rcpt regexp ".*" "mail@$(primary_domain)" replace_rcpt regexp "(.+)@(.+)" "mail@$2"
} }
deliver_to &local_mailboxes deliver_to &local_mailboxes
} }
@ -251,7 +313,7 @@ in {
''; '';
}; };
systemd.services.rspamd.serviceConfig.SupplementaryGroups = [ "maddy" ]; systemd.services.rspamd.serviceConfig.SupplementaryGroups = ["maddy"];
age.secrets."rclone-pubsolar.conf" = { age.secrets."rclone-pubsolar.conf" = {
file = "${flake.self}/secrets/rclone-pubsolar.conf.age"; file = "${flake.self}/secrets/rclone-pubsolar.conf.age";
@ -265,7 +327,7 @@ in {
services.restic.backups = { services.restic.backups = {
maddy = { maddy = {
paths = [ "/var/lib/maddy" ]; paths = ["/var/lib/maddy"];
initialize = true; initialize = true;
passwordFile = config.age.secrets."restic-password".path; passwordFile = config.age.secrets."restic-password".path;
# See https://www.hosting.de/blog/verschluesselte-backups-mit-rclone-und-restic-in-nextcloud/ # See https://www.hosting.de/blog/verschluesselte-backups-mit-rclone-und-restic-in-nextcloud/

View file

@ -1,16 +1,21 @@
# Do not modify this file! It was generated by nixos-generate-config # Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes # and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead. # to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{ {
config,
lib,
pkgs,
modulesPath,
...
}: {
imports = [ imports = [
(modulesPath + "/profiles/qemu-guest.nix") (modulesPath + "/profiles/qemu-guest.nix")
]; ];
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ]; boot.initrd.availableKernelModules = ["ahci" "xhci_pci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod"];
boot.initrd.kernelModules = [ ]; boot.initrd.kernelModules = [];
boot.kernelModules = [ ]; boot.kernelModules = [];
boot.extraModulePackages = [ ]; boot.extraModulePackages = [];
boot.initrd.luks.devices = { boot.initrd.luks.devices = {
cryptroot = { cryptroot = {
@ -19,19 +24,19 @@
}; };
}; };
fileSystems."/" = fileSystems."/" = {
{ device = "zroot/root"; device = "zroot/root";
fsType = "zfs"; fsType = "zfs";
}; };
fileSystems."/boot" = fileSystems."/boot" = {
{ device = "/dev/disk/by-uuid/684A-5884"; device = "/dev/disk/by-uuid/684A-5884";
fsType = "vfat"; fsType = "vfat";
}; };
swapDevices = swapDevices = [
[ { device = "/dev/disk/by-uuid/a7d1cbb8-7c9e-4c3d-841a-add867f47389"; } {device = "/dev/disk/by-uuid/a7d1cbb8-7c9e-4c3d-841a-add867f47389";}
]; ];
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux"; nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
} }

View file

@ -17,8 +17,18 @@
# Network configuration (Hetzner uses static IP assignments, and we don't use DHCP here) # Network configuration (Hetzner uses static IP assignments, and we don't use DHCP here)
networking.useDHCP = false; networking.useDHCP = false;
networking.interfaces.enp1s0 = { networking.interfaces.enp1s0 = {
ipv4.addresses = [{ address = "128.140.109.213"; prefixLength = 32; }]; ipv4.addresses = [
ipv6.addresses = [{ address = "2a01:4f8:c2c:b60::"; prefixLength = 64; }]; {
address = "128.140.109.213";
prefixLength = 32;
}
];
ipv6.addresses = [
{
address = "2a01:4f8:c2c:b60::";
prefixLength = 64;
}
];
}; };
networking.defaultGateway = { networking.defaultGateway = {
address = "172.31.1.1"; address = "172.31.1.1";
@ -29,5 +39,5 @@
interface = "enp1s0"; interface = "enp1s0";
}; };
networking.firewall.allowedTCPPorts = [ 80 443 ]; networking.firewall.allowedTCPPorts = [80 443];
} }

View file

@ -29,8 +29,8 @@
owner = "unbound"; owner = "unbound";
}; };
networking.firewall.interfaces.wg-private.allowedUDPPorts = [ 53 ]; networking.firewall.interfaces.wg-private.allowedUDPPorts = [53];
networking.firewall.interfaces.wg-private.allowedTCPPorts = [ 53 ]; networking.firewall.interfaces.wg-private.allowedTCPPorts = [53];
services.resolved.enable = false; services.resolved.enable = false;
services.unbound = { services.unbound = {
@ -96,6 +96,16 @@
"\"b12f.io. 10800 IN AAAA fd00:b12f:acab:1312:acab:7::\"" "\"b12f.io. 10800 IN AAAA fd00:b12f:acab:1312:acab:7::\""
"\"mail.b12f.io. 10800 IN A 10.13.12.7\"" "\"mail.b12f.io. 10800 IN A 10.13.12.7\""
"\"mail.b12f.io. 10800 IN AAAA fd00:b12f:acab:1312:acab:7::\"" "\"mail.b12f.io. 10800 IN AAAA fd00:b12f:acab:1312:acab:7::\""
"\"mezza.biz. 10800 IN A 10.13.12.7\""
"\"mezza.biz. 10800 IN AAAA fd00:b12f:acab:1312:acab:7::\""
"\"mail.mezza.biz. 10800 IN A 10.13.12.7\""
"\"mail.mezza.biz. 10800 IN AAAA fd00:b12f:acab:1312:acab:7::\""
"\"h${"w" + "dz" + "z.n"}et. 10800 IN A 10.13.12.7\""
"\"h${"w" + "dz" + "z.n"}et. 10800 IN AAAA fd00:b12f:acab:1312:acab:7::\""
"\"mail.h${"w" + "dz" + "z.n"}et. 10800 IN A 10.13.12.7\""
"\"mail.h${"w" + "dz" + "z.n"}et. 10800 IN AAAA fd00:b12f:acab:1312:acab:7::\""
]; ];
tls-cert-bundle = "/etc/ssl/certs/ca-certificates.crt"; tls-cert-bundle = "/etc/ssl/certs/ca-certificates.crt";
@ -123,5 +133,4 @@
}; };
}; };
}; };
} }

View file

@ -6,6 +6,7 @@
security.acme.certs = { security.acme.certs = {
"benjaminbaedorf.eu" = {}; "benjaminbaedorf.eu" = {};
"b12f.io" = {}; "b12f.io" = {};
"mezza.biz" = {};
}; };
services.nginx.virtualHosts = { services.nginx.virtualHosts = {
@ -25,5 +26,16 @@
tryFiles = "$uri $uri/ =404"; tryFiles = "$uri $uri/ =404";
}; };
}; };
"mezza.biz" = {
forceSSL = true;
useACMEHost = "mezza.biz";
locations."/" = {
root = pkgs.mezza-biz;
index = "index.html";
tryFiles = "$uri $uri/ =404";
};
};
}; };
} }

View file

@ -4,7 +4,8 @@
pkgs, pkgs,
lib, lib,
... ...
}: with lib; { }:
with lib; {
boot.kernel.sysctl = { boot.kernel.sysctl = {
"net.ipv4.ip_forward" = 1; "net.ipv4.ip_forward" = 1;
"net.ipv6.conf.wg-private.forwarding" = 1; "net.ipv6.conf.wg-private.forwarding" = 1;
@ -16,10 +17,10 @@
enable = true; enable = true;
enableIPv6 = true; enableIPv6 = true;
externalInterface = "enp1s0"; externalInterface = "enp1s0";
internalInterfaces = [ "wg-private" ]; internalInterfaces = ["wg-private"];
}; };
networking.firewall.allowedUDPPorts = [ 51899 ]; networking.firewall.allowedUDPPorts = [51899];
networking.firewall.extraForwardRules = [ networking.firewall.extraForwardRules = [
"iifname { != wg-private } reject" "iifname { != wg-private } reject"
@ -57,7 +58,8 @@
]; ];
privateKeyFile = config.age.secrets.wg-private-key.path; privateKeyFile = config.age.secrets.wg-private-key.path;
peers = [ peers = [
{ # pie {
# pie
publicKey = "hPTXEqQ2GYEywdPNdZBacwB9KKcoFZ/heClxnqmizyw="; publicKey = "hPTXEqQ2GYEywdPNdZBacwB9KKcoFZ/heClxnqmizyw=";
allowedIPs = [ allowedIPs = [
"10.13.12.2/32" "10.13.12.2/32"
@ -66,7 +68,8 @@
persistentKeepalive = 30; persistentKeepalive = 30;
dynamicEndpointRefreshSeconds = 30; dynamicEndpointRefreshSeconds = 30;
} }
{ # droppie {
# droppie
publicKey = "qsnBMoj9Z16D8PJ5ummRtIfT5AiMpoF3SoOCo4sbyiw="; publicKey = "qsnBMoj9Z16D8PJ5ummRtIfT5AiMpoF3SoOCo4sbyiw=";
allowedIPs = [ allowedIPs = [
"10.13.12.3/32" "10.13.12.3/32"
@ -75,7 +78,8 @@
persistentKeepalive = 30; persistentKeepalive = 30;
dynamicEndpointRefreshSeconds = 30; dynamicEndpointRefreshSeconds = 30;
} }
{ # chocolatebar {
# chocolatebar
publicKey = "nk8EtGE/QsnSEm1lhLS3/w83nOBD2OGYhODIf92G91A="; publicKey = "nk8EtGE/QsnSEm1lhLS3/w83nOBD2OGYhODIf92G91A=";
allowedIPs = [ allowedIPs = [
"10.13.12.5/32" "10.13.12.5/32"
@ -84,7 +88,8 @@
persistentKeepalive = 30; persistentKeepalive = 30;
dynamicEndpointRefreshSeconds = 30; dynamicEndpointRefreshSeconds = 30;
} }
{ # biolimo {
# biolimo
publicKey = "4ymN7wwBuhF+h+5fFN0TqXmVyOe1AsWiTqRL0jJ3CDc="; publicKey = "4ymN7wwBuhF+h+5fFN0TqXmVyOe1AsWiTqRL0jJ3CDc=";
allowedIPs = [ allowedIPs = [
"10.13.12.6/32" "10.13.12.6/32"
@ -93,7 +98,8 @@
persistentKeepalive = 30; persistentKeepalive = 30;
dynamicEndpointRefreshSeconds = 30; dynamicEndpointRefreshSeconds = 30;
} }
{ # stroopwafel {
# stroopwafel
publicKey = "5iNRg13utOJ30pX2Z8SjwPNUFwfH2zonlbeYW2mKFkU="; publicKey = "5iNRg13utOJ30pX2Z8SjwPNUFwfH2zonlbeYW2mKFkU=";
allowedIPs = [ allowedIPs = [
"10.13.12.8/32" "10.13.12.8/32"
@ -102,7 +108,8 @@
persistentKeepalive = 30; persistentKeepalive = 30;
dynamicEndpointRefreshSeconds = 30; dynamicEndpointRefreshSeconds = 30;
} }
{ # fp3 {
# fp3
publicKey = "wQJXFibxhWkyUbRPrPt5y/YfDnH3gDQ5a/PWoyxDfDI="; publicKey = "wQJXFibxhWkyUbRPrPt5y/YfDnH3gDQ5a/PWoyxDfDI=";
allowedIPs = [ allowedIPs = [
"10.13.12.9/32" "10.13.12.9/32"

View file

@ -4,7 +4,7 @@
... ...
}: { }: {
isoImage.squashfsCompression = "gzip -Xcompression-level 1"; isoImage.squashfsCompression = "gzip -Xcompression-level 1";
systemd.services.sshd.wantedBy = lib.mkForce [ "multi-user.target" ]; systemd.services.sshd.wantedBy = lib.mkForce ["multi-user.target"];
networking.networkmanager.enable = false; networking.networkmanager.enable = false;
services.openssh.openFirewall = lib.mkForce true; services.openssh.openFirewall = lib.mkForce true;
} }

View file

@ -1,8 +1,12 @@
{ flake, pkgs, ... }: { {
flake,
pkgs,
...
}: {
imports = [ imports = [
./configuration.nix ./configuration.nix
./hardware-configuration.nix ./hardware-configuration.nix
((import "${flake.inputs.mobile-nixos}/lib/configuration.nix") { device = "pine64-pinephone"; }) ((import "${flake.inputs.mobile-nixos}/lib/configuration.nix") {device = "pine64-pinephone";})
"${flake.inputs.mobile-nixos}/examples/phosh/phosh.nix" "${flake.inputs.mobile-nixos}/examples/phosh/phosh.nix"
]; ];
} }

View file

@ -1,6 +1,10 @@
# NOTE: this file was generated by the Mobile NixOS installer. # NOTE: this file was generated by the Mobile NixOS installer.
{ config, lib, pkgs, ... }:
{ {
config,
lib,
pkgs,
...
}: {
fileSystems = { fileSystems = {
"/" = { "/" = {
device = "/dev/disk/by-uuid/51a668b8-fa2e-4d3e-ac3f-73ca002d0004"; device = "/dev/disk/by-uuid/51a668b8-fa2e-4d3e-ac3f-73ca002d0004";

View file

@ -81,7 +81,7 @@ in {
}; };
authentication_backend = { authentication_backend = {
refresh_interval = "disable"; refresh_interval = "disable";
password_reset = { disable = true; }; password_reset = {disable = true;};
file = { file = {
path = config.age.secrets."authelia-users-file".path; path = config.age.secrets."authelia-users-file".path;
watch = false; watch = false;
@ -93,7 +93,10 @@ in {
storage.local.path = "/var/lib/authelia-b12f/db.sqlite3"; storage.local.path = "/var/lib/authelia-b12f/db.sqlite3";
access_control.default_policy = "two_factor"; access_control.default_policy = "two_factor";
session.cookies = [ session.cookies = [
{ domain = "b12f.io"; authelia_url = "https://auth.b12f.io"; } {
domain = "b12f.io";
authelia_url = "https://auth.b12f.io";
}
]; ];
notifier.smtp = { notifier.smtp = {
host = "mail.b12f.io"; host = "mail.b12f.io";
@ -110,7 +113,7 @@ in {
services.restic.backups = { services.restic.backups = {
authelia = { authelia = {
paths = [ "/var/lib/authelia-b12f" ]; paths = ["/var/lib/authelia-b12f"];
initialize = true; initialize = true;
passwordFile = config.age.secrets."restic-password".path; passwordFile = config.age.secrets."restic-password".path;
# See https://www.hosting.de/blog/verschluesselte-backups-mit-rclone-und-restic-in-nextcloud/ # See https://www.hosting.de/blog/verschluesselte-backups-mit-rclone-und-restic-in-nextcloud/

View file

@ -20,7 +20,7 @@ in {
boot.loader.systemd-boot.enable = false; boot.loader.systemd-boot.enable = false;
boot.loader.generic-extlinux-compatible.enable = false; boot.loader.generic-extlinux-compatible.enable = false;
boot.supportedFilesystems = [ "zfs" ]; boot.supportedFilesystems = ["zfs"];
boot.kernelParams = [ boot.kernelParams = [
"boot.shell_on_fail=1" "boot.shell_on_fail=1"
@ -28,7 +28,7 @@ in {
"ip=127.0.0.1:::::lo:none" "ip=127.0.0.1:::::lo:none"
]; ];
# See https://discourse.nixos.org/t/ssh-and-network-in-initrd-on-raspberry-pi-4/6289/3 # See https://discourse.nixos.org/t/ssh-and-network-in-initrd-on-raspberry-pi-4/6289/3
boot.initrd.availableKernelModules = [ "genet" ]; boot.initrd.availableKernelModules = ["genet"];
boot.initrd.network = { boot.initrd.network = {
enable = true; enable = true;
ssh = { ssh = {

View file

@ -1,6 +1,9 @@
{ pkgs, adblock-unbound, ... }:
{ {
networking.firewall.allowedUDPPorts = [ 67 547 ]; pkgs,
adblock-unbound,
...
}: {
networking.firewall.allowedUDPPorts = [67 547];
networking.firewall.extraInputRules = '' networking.firewall.extraInputRules = ''
ip6 daddr ff02::1:2/128 udp dport 547 accept comment "DHCPv6 server" ip6 daddr ff02::1:2/128 udp dport 547 accept comment "DHCPv6 server"
''; '';
@ -30,7 +33,7 @@
{ {
subnet = "192.168.178.0/24"; subnet = "192.168.178.0/24";
pools = [ pools = [
{ pool = "192.168.178.2 - 192.168.178.255"; } {pool = "192.168.178.2 - 192.168.178.255";}
]; ];
option-data = [ option-data = [
@ -100,19 +103,19 @@
subnet = "2a02:908:5b1:e3c0::/64"; subnet = "2a02:908:5b1:e3c0::/64";
pools = [ pools = [
{ pool = "2a02:908:5b1:e3c0::/72"; } {pool = "2a02:908:5b1:e3c0::/72";}
]; ];
ddns-qualifying-suffix = "local."; ddns-qualifying-suffix = "local.";
option-data = [ option-data = [
{ {
name = "dns-servers"; name = "dns-servers";
data = "2a02:908:5b1:e3c0:2::"; data = "2a02:908:5b1:e3c0:2::";
} }
{ {
name = "domain-search"; name = "domain-search";
data = "local"; data = "local";
} }
]; ];

View file

@ -80,14 +80,14 @@ in {
volumes = [ volumes = [
"/var/lib/firefly/upload:/var/www/html/storage/upload" "/var/lib/firefly/upload:/var/www/html/storage/upload"
]; ];
extraOptions = [ "--network=firefly" ]; extraOptions = ["--network=firefly"];
environmentFiles = [ environmentFiles = [
./.env.firefly ./.env.firefly
config.age.secrets."firefly-secrets.env".path config.age.secrets."firefly-secrets.env".path
config.age.secrets."firefly-cron-secrets.env".path config.age.secrets."firefly-cron-secrets.env".path
]; ];
ports = [ "127.0.0.1:8080:8080" ]; ports = ["127.0.0.1:8080:8080"];
dependsOn = [ "firefly-db" ]; dependsOn = ["firefly-db"];
}; };
containers."firefly-db" = { containers."firefly-db" = {
@ -96,7 +96,7 @@ in {
volumes = [ volumes = [
"/var/lib/firefly/db:/var/lib/postgresql/data" "/var/lib/firefly/db:/var/lib/postgresql/data"
]; ];
extraOptions = [ "--network=firefly" ]; extraOptions = ["--network=firefly"];
environmentFiles = [ environmentFiles = [
config.age.secrets."firefly-db-secrets.env".path config.age.secrets."firefly-db-secrets.env".path
]; ];
@ -105,8 +105,8 @@ in {
containers."firefly-importer" = { containers."firefly-importer" = {
image = "fireflyiii/data-importer:latest"; image = "fireflyiii/data-importer:latest";
autoStart = true; autoStart = true;
extraOptions = [ "--network=firefly" ]; extraOptions = ["--network=firefly"];
ports = [ "127.0.0.1:8081:8080" ]; ports = ["127.0.0.1:8081:8080"];
environment = { environment = {
FIREFLY_III_URL = "https://firefly.b12f.io"; FIREFLY_III_URL = "https://firefly.b12f.io";
}; };
@ -114,7 +114,7 @@ in {
./.env.firefly-importer ./.env.firefly-importer
config.age.secrets."firefly-importer-secrets.env".path config.age.secrets."firefly-importer-secrets.env".path
]; ];
dependsOn = [ "firefly" ]; dependsOn = ["firefly"];
}; };
containers."firefly-cron" = { containers."firefly-cron" = {
@ -128,7 +128,7 @@ in {
environmentFiles = [ environmentFiles = [
config.age.secrets."firefly-cron-secrets.env".path config.age.secrets."firefly-cron-secrets.env".path
]; ];
extraOptions = [ "--network=firefly" ]; extraOptions = ["--network=firefly"];
}; };
}; };
}; };

View file

@ -1,18 +1,22 @@
# Do not modify this file! It was generated by nixos-generate-config # Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes # and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead. # to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{ {
config,
lib,
pkgs,
modulesPath,
...
}: {
imports = [ imports = [
(modulesPath + "/installer/scan/not-detected.nix") (modulesPath + "/installer/scan/not-detected.nix")
]; ];
boot.initrd.availableKernelModules = [ "xhci_pci" "usbhid" "uas" "usb_storage" ]; boot.initrd.availableKernelModules = ["xhci_pci" "usbhid" "uas" "usb_storage"];
boot.initrd.kernelModules = [ ]; boot.initrd.kernelModules = [];
boot.kernelModules = [ ]; boot.kernelModules = [];
boot.extraModulePackages = [ ]; boot.extraModulePackages = [];
boot.supportedFilesystems = [ "zfs" ]; boot.supportedFilesystems = ["zfs"];
boot.initrd.luks.devices = { boot.initrd.luks.devices = {
cryptroot = { cryptroot = {
@ -21,20 +25,19 @@
}; };
}; };
fileSystems."/" = fileSystems."/" = {
{ device = "zroot/root"; device = "zroot/root";
fsType = "zfs"; fsType = "zfs";
}; };
fileSystems."/boot" = fileSystems."/boot" = {
{ device = "/dev/disk/by-uuid/0D5D-B809"; device = "/dev/disk/by-uuid/0D5D-B809";
fsType = "vfat"; fsType = "vfat";
}; };
swapDevices =
[ { device = "/dev/disk/by-uuid/af71e930-42ce-4174-a098-4ea5753b1ea9"; }
];
swapDevices = [
{device = "/dev/disk/by-uuid/af71e930-42ce-4174-a098-4ea5753b1ea9";}
];
nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux"; nixpkgs.hostPlatform = lib.mkDefault "aarch64-linux";
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand"; powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";

View file

@ -44,7 +44,7 @@ in {
createLocally = false; createLocally = false;
}; };
invoiceTemplates = [ pkgs.invoiceplane-template ]; invoiceTemplates = [pkgs.invoiceplane-template];
extraConfig = '' extraConfig = ''
SETUP_COMPLETED=true SETUP_COMPLETED=true
@ -72,7 +72,7 @@ in {
containers."invoiceplane-db" = { containers."invoiceplane-db" = {
image = "mariadb:11"; image = "mariadb:11";
autoStart = true; autoStart = true;
ports = [ "127.0.0.1:3306:3306" ]; ports = ["127.0.0.1:3306:3306"];
volumes = [ volumes = [
"/var/lib/invoiceplane/db:/var/lib/mysql" "/var/lib/invoiceplane/db:/var/lib/mysql"
]; ];

View file

@ -15,16 +15,25 @@
networking.interfaces.enabcm6e4ei0 = { networking.interfaces.enabcm6e4ei0 = {
ipv4.addresses = [ ipv4.addresses = [
{ address = "192.168.178.2"; prefixLength = 32; } {
address = "192.168.178.2";
prefixLength = 32;
}
]; ];
ipv6.addresses = [ ipv6.addresses = [
{ address = "2a02:908:5b1:e3c0:2::"; prefixLength = 128; } {
{ address = "fe80:b12f:acab:1312:acab:2::"; prefixLength = 128; } address = "2a02:908:5b1:e3c0:2::";
prefixLength = 128;
}
{
address = "fe80:b12f:acab:1312:acab:2::";
prefixLength = 128;
}
]; ];
}; };
networking.hosts = { networking.hosts = {
"192.168.178.3" = [ "droppie-initrd.b12f.io" ]; "192.168.178.3" = ["droppie-initrd.b12f.io"];
}; };
services.openssh.allowSFTP = true; services.openssh.allowSFTP = true;

View file

@ -13,33 +13,34 @@ with lib; let
backupDir = "/var/lib/PaperlessBackup"; backupDir = "/var/lib/PaperlessBackup";
consumptionDir = "/var/lib/scandir"; consumptionDir = "/var/lib/scandir";
scan2paperless = with pkgs; writeShellScriptBin "scan2paperless" '' scan2paperless = with pkgs;
DEVICE=$1 writeShellScriptBin "scan2paperless" ''
NUM_PAGES=$2 DEVICE=$1
NAME=$3 NUM_PAGES=$2
NAME=$3
if [ -z "''${DEVICE}" ] || [ -z "''${NUM_PAGES}" ] || [ -z "''${NAME}" ]; then if [ -z "''${DEVICE}" ] || [ -z "''${NUM_PAGES}" ] || [ -z "''${NAME}" ]; then
echo "Usage: scan2paperless <device> <num_pages> <name>" echo "Usage: scan2paperless <device> <num_pages> <name>"
exit 1 exit 1
fi fi
tmpDir=$(${coreutils}/bin/mktemp -d) tmpDir=$(${coreutils}/bin/mktemp -d)
files=() files=()
for i in $(seq 1 $NUM_PAGES); do for i in $(seq 1 $NUM_PAGES); do
fileName=$(${openssl}/bin/openssl rand -hex 12) fileName=$(${openssl}/bin/openssl rand -hex 12)
file="$tmpDir/$fileName.jpg" file="$tmpDir/$fileName.jpg"
echo "Start scanning page $i/$NUM_PAGES"; echo "Start scanning page $i/$NUM_PAGES";
${sane-backends}/bin/scanimage -d $DEVICE --format=jpeg --resolution 300 --progress -o $file ${sane-backends}/bin/scanimage -d $DEVICE --format=jpeg --resolution 300 --progress -o $file
echo "Finished scanning page $i"; echo "Finished scanning page $i";
files+=($file) files+=($file)
done done
pdf="${consumptionDir}/$NAME.pdf" pdf="${consumptionDir}/$NAME.pdf"
${python3Packages.img2pdf}/bin/img2pdf --output $pdf ''${files[@]} ${python3Packages.img2pdf}/bin/img2pdf --output $pdf ''${files[@]}
echo "PDF written to $pdf" echo "PDF written to $pdf"
''; '';
in { in {
age.secrets."paperless.env" = { age.secrets."paperless.env" = {
file = "${flake.self}/secrets/paperless.env.age"; file = "${flake.self}/secrets/paperless.env.age";
@ -82,7 +83,7 @@ in {
}; };
}; };
systemd.services.paperless-web.serviceConfig.EnvironmentFile = [ config.age.secrets."paperless.env".path ]; systemd.services.paperless-web.serviceConfig.EnvironmentFile = [config.age.secrets."paperless.env".path];
################################# #################################
# Scanning # Scanning
@ -147,7 +148,7 @@ in {
services.restic.backups = { services.restic.backups = {
paperless = { paperless = {
paths = [ backupDir ]; paths = [backupDir];
initialize = true; initialize = true;
passwordFile = config.age.secrets."restic-password".path; passwordFile = config.age.secrets."restic-password".path;
# See https://www.hosting.de/blog/verschluesselte-backups-mit-rclone-und-restic-in-nextcloud/ # See https://www.hosting.de/blog/verschluesselte-backups-mit-rclone-und-restic-in-nextcloud/

View file

@ -29,8 +29,8 @@
owner = "unbound"; owner = "unbound";
}; };
networking.firewall.allowedUDPPorts = [ 53 ]; networking.firewall.allowedUDPPorts = [53];
networking.firewall.allowedTCPPorts = [ 53 ]; networking.firewall.allowedTCPPorts = [53];
services.resolved.enable = false; services.resolved.enable = false;
services.unbound = { services.unbound = {
@ -94,5 +94,4 @@
}; };
}; };
}; };
} }

View file

@ -1,5 +1,4 @@
{ pkgs, ... }: {pkgs, ...}: {
{
services.cron = { services.cron = {
enable = true; enable = true;
systemCronJobs = [ systemCronJobs = [

View file

@ -17,9 +17,9 @@ in {
boot.initrd.preLVMCommands = "udevadm trigger --settle"; boot.initrd.preLVMCommands = "udevadm trigger --settle";
boot.swraid.enable = true; boot.swraid.enable = true;
boot.swraid.mdadmConf = '' boot.swraid.mdadmConf = ''
DEVICE /dev/nvme0n1p2 /dev/nvme1n1p2 DEVICE /dev/nvme0n1p2 /dev/nvme1n1p2
ARRAY /dev/md/nixos:root metadata=1.2 name=nixos:root UUID=67d1aa81:1b348887:c17a75e8:f2edf2bd ARRAY /dev/md/nixos:root metadata=1.2 name=nixos:root UUID=67d1aa81:1b348887:c17a75e8:f2edf2bd
MAILADDR ${psCfg.user.email} MAILADDR ${psCfg.user.email}
''; '';
pub-solar.core.hibernation.enable = true; pub-solar.core.hibernation.enable = true;

View file

@ -1,4 +1,4 @@
{ ... }: { {...}: {
imports = [ imports = [
./configuration.nix ./configuration.nix
./hardware-configuration.nix ./hardware-configuration.nix

View file

@ -1,55 +1,59 @@
# Do not modify this file! It was generated by nixos-generate-config # Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes # and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead. # to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{ {
imports = config,
[ (modulesPath + "/installer/scan/not-detected.nix") lib,
]; pkgs,
modulesPath,
...
}: {
imports = [
(modulesPath + "/installer/scan/not-detected.nix")
];
boot.initrd.availableKernelModules = [ "nvme" "xhci_pci" "usbhid" "usb_storage" "sd_mod" ]; boot.initrd.availableKernelModules = ["nvme" "xhci_pci" "usbhid" "usb_storage" "sd_mod"];
boot.initrd.kernelModules = [ "dm-snapshot" ]; boot.initrd.kernelModules = ["dm-snapshot"];
boot.kernelModules = [ "kvm-amd" ]; boot.kernelModules = ["kvm-amd"];
boot.extraModulePackages = [ ]; boot.extraModulePackages = [];
boot.initrd.luks.devices."cryptroot" = { boot.initrd.luks.devices."cryptroot" = {
device = "/dev/disk/by-id/md-name-nixos:root"; device = "/dev/disk/by-id/md-name-nixos:root";
allowDiscards = true; allowDiscards = true;
}; };
fileSystems."/" = fileSystems."/" = {
{ device = "none"; device = "none";
fsType = "tmpfs"; fsType = "tmpfs";
}; };
fileSystems."/boot" = fileSystems."/boot" = {
{ device = "/dev/disk/by-uuid/EC82-67F4"; device = "/dev/disk/by-uuid/EC82-67F4";
fsType = "vfat"; fsType = "vfat";
}; };
fileSystems."/home" = fileSystems."/home" = {
{ device = "/dev/disk/by-uuid/0cc568f0-402d-4535-980a-ed3a1dc697b9"; device = "/dev/disk/by-uuid/0cc568f0-402d-4535-980a-ed3a1dc697b9";
fsType = "ext4"; fsType = "ext4";
# https://github.com/ryantm/agenix/issues/45#issuecomment-957865406 # https://github.com/ryantm/agenix/issues/45#issuecomment-957865406
neededForBoot = true; neededForBoot = true;
}; };
fileSystems."/nix" = fileSystems."/nix" = {
{ device = "/dev/disk/by-uuid/e203d629-4d34-4147-bee6-919f0bfa25de"; device = "/dev/disk/by-uuid/e203d629-4d34-4147-bee6-919f0bfa25de";
fsType = "ext4"; fsType = "ext4";
}; };
fileSystems."/persist" = fileSystems."/persist" = {
{ device = "/dev/disk/by-uuid/a0855aaa-76bf-445e-b0d1-ab1552e5496f"; device = "/dev/disk/by-uuid/a0855aaa-76bf-445e-b0d1-ab1552e5496f";
fsType = "ext4"; fsType = "ext4";
# https://github.com/ryantm/agenix/issues/45#issuecomment-957865406 # https://github.com/ryantm/agenix/issues/45#issuecomment-957865406
neededForBoot = true; neededForBoot = true;
}; };
swapDevices = swapDevices = [
[ { device = "/dev/disk/by-uuid/761507ab-479d-414b-ac3e-2149564ca470"; } {device = "/dev/disk/by-uuid/761507ab-479d-414b-ac3e-2149564ca470";}
]; ];
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking # 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 # (the default) this is the recommended approach. When using systemd-networkd it's

View file

@ -46,7 +46,7 @@
pub-solar.wireguard.ehex = { pub-solar.wireguard.ehex = {
ownIPs = [ ownIPs = [
"10.42.0.135/22" "10.42.0.135/22"
]; ];
privateKeyFile = config.age.secrets.wg-ehex-key.path; privateKeyFile = config.age.secrets.wg-ehex-key.path;
}; };

View file

@ -1,5 +1,4 @@
{ lib }: {lib}: hostnames: {
hostnames: {
"127.0.0.1" = hostnames; "127.0.0.1" = hostnames;
"::1" = hostnames; "::1" = hostnames;
} }

View file

@ -1,4 +1,8 @@
{ lib, inputs, ... }: { {
lib,
inputs,
...
}: {
# Configuration common to all Linux systems # Configuration common to all Linux systems
flake = { flake = {
lib = let lib = let
@ -10,7 +14,7 @@
#foo = callLibs ./foo.nix; #foo = callLibs ./foo.nix;
## In configs, they can be used under "lib.our" ## In configs, they can be used under "lib.our"
deploy = import ./deploy.nix { inherit inputs lib; }; deploy = import ./deploy.nix {inherit inputs lib;};
addLocalHostname = callLibs ./add-local-hostname.nix; addLocalHostname = callLibs ./add-local-hostname.nix;
recursiveMerge = callLibs ./recursive-merge.nix; recursiveMerge = callLibs ./recursive-merge.nix;
mkEmailAddress = account: domain: account + "@" + domain; mkEmailAddress = account: domain: account + "@" + domain;

View file

@ -1,11 +1,13 @@
/* /*
* The contents of this file are adapted from digga * The contents of this file are adapted from digga
* https://github.com/divnix/digga * https://github.com/divnix/digga
* *
* Licensed under the MIT license * Licensed under the MIT license
*/ */
{
{ lib, inputs }: let lib,
inputs,
}: let
getFqdn = c: let getFqdn = c: let
net = c.config.networking; net = c.config.networking;
fqdn = fqdn =
@ -17,48 +19,53 @@
in { in {
mkDeployNodes = systemConfigurations: extraConfig: mkDeployNodes = systemConfigurations: extraConfig:
/* /*
* *
Synopsis: mkNodes _systemConfigurations_ _extraConfig_ Synopsis: mkNodes _systemConfigurations_ _extraConfig_
Generate the `nodes` attribute expected by deploy-rs Generate the `nodes` attribute expected by deploy-rs
where _systemConfigurations_ are `nodes`. where _systemConfigurations_ are `nodes`.
_systemConfigurations_ should take the form of a flake's _systemConfigurations_ should take the form of a flake's
_nixosConfigurations_. Note that deploy-rs does not currently support _nixosConfigurations_. Note that deploy-rs does not currently support
deploying to darwin hosts. deploying to darwin hosts.
_extraConfig_, if specified, will be merged into each of the _extraConfig_, if specified, will be merged into each of the
nodes' configurations. nodes' configurations.
Example _systemConfigurations_ input: Example _systemConfigurations_ input:
``` ```
{ {
hostname-1 = { hostname-1 = {
fastConnection = true; fastConnection = true;
sshOpts = [ "-p" "25" ]; sshOpts = [ "-p" "25" ];
}; };
hostname-2 = { hostname-2 = {
sshOpts = [ "-p" "19999" ]; sshOpts = [ "-p" "19999" ];
sshUser = "root"; sshUser = "root";
}; };
} }
``` ```
* *
*/ */
lib.recursiveUpdate lib.recursiveUpdate
(lib.mapAttrs (lib.mapAttrs
( (
_: c: let _: c: let
system = c.pkgs.stdenv.hostPlatform.system; system = c.pkgs.stdenv.hostPlatform.system;
# Unmodified nixpkgs # Unmodified nixpkgs
pkgs = import inputs.nixpkgs { inherit system; }; pkgs = import inputs.nixpkgs {inherit system;};
# nixpkgs with deploy-rs overlay but force the nixpkgs package # nixpkgs with deploy-rs overlay but force the nixpkgs package
deployPkgs = import inputs.nixpkgs { deployPkgs = import inputs.nixpkgs {
inherit system; inherit system;
overlays = [ overlays = [
inputs.deploy-rs.overlay # or deploy-rs.overlays.default inputs.deploy-rs.overlay # or deploy-rs.overlays.default
(self: super: { deploy-rs = { inherit (pkgs) deploy-rs; lib = super.deploy-rs.lib; }; }) (self: super: {
deploy-rs = {
inherit (pkgs) deploy-rs;
lib = super.deploy-rs.lib;
};
})
]; ];
}; };
in { in {

View file

@ -1,6 +1,4 @@
{ lib }: {lib}: attrList: let
attrList:
let
f = attrPath: f = attrPath:
zipAttrsWith ( zipAttrsWith (
n: values: n: values:
@ -13,4 +11,4 @@ let
else last values else last values
); );
in in
f [] attrList; f [] attrList

View file

@ -38,7 +38,7 @@
}; };
# Don't expose SSH via public interfaces # Don't expose SSH via public interfaces
networking.firewall.interfaces.wg-private.allowedTCPPorts = [ 22 ]; networking.firewall.interfaces.wg-private.allowedTCPPorts = [22];
# For rage encryption, all hosts need a ssh key pair # For rage encryption, all hosts need a ssh key pair
services.openssh = { services.openssh = {

View file

@ -29,14 +29,6 @@ in {
element-desktop element-desktop
element-b12f element-b12f
element-mezza element-mezza
# Nix specific utilities
alejandra
manix
nix-index
nix-tree
nix-inspect
nvd
]; ];
fonts = { fonts = {

View file

@ -159,8 +159,8 @@
# Base16 Burn 256 - alacritty color config # Base16 Burn 256 - alacritty color config
# Benjamin Bädorf # Benjamin Bädorf
colors = { colors = {
# When true, bold text is drawn using the bright variant of colors. # When true, bold text is drawn using the bright variant of colors.
draw_bold_text_with_bright_colors = true; draw_bold_text_with_bright_colors = true;
# Default colors # Default colors
primary = { primary = {

View file

@ -45,6 +45,7 @@ in {
glib glib
xdg-utils xdg-utils
xorg.xbacklight
]; ];
etc = { etc = {
@ -105,7 +106,7 @@ in {
toggle-kbd-layout toggle-kbd-layout
vlc vlc
wcwd wcwd
wl-mirror wl-mirror
]; ];
home-manager.users."${psCfg.user.name}" = { home-manager.users."${psCfg.user.name}" = {

View file

@ -2,39 +2,40 @@
pkgs, pkgs,
psCfg, psCfg,
... ...
}: with pkgs; }:
'' with pkgs;
# Set shut down, restart and locking features
''
+ (
if psCfg.core.hibernation.enable
then ''
set $mode_system (e)xit, (l)ock, (h)ibernate, (r)eboot, (Shift+s)hutdown
'' ''
else '' # Set shut down, restart and locking features
set $mode_system (e)xit, (l)ock, (r)eboot, (Shift+s)hutdown
'' ''
) + (
+ '' if psCfg.core.hibernation.enable
bindsym $mod+0 mode "$mode_system" then ''
set $mode_system (e)xit, (l)ock, (h)ibernate, (r)eboot, (Shift+s)hutdown
''
else ''
set $mode_system (e)xit, (l)ock, (r)eboot, (Shift+s)hutdown
''
)
+ ''
bindsym $mod+0 mode "$mode_system"
mode "$mode_system" { mode "$mode_system" {
bindsym e exec ${sway}/bin/swaymsg exit, mode "default" bindsym e exec ${sway}/bin/swaymsg exit, mode "default"
bindsym l exec ${swaylock-bg}/bin/swaylock-bg, mode "default" bindsym l exec ${swaylock-bg}/bin/swaylock-bg, mode "default"
''
+ (
if psCfg.core.hibernation.enable
then ''
bindsym h exec ${systemd}/bin/systemctl hibernate, mode "default"
'' ''
else "" + (
) if psCfg.core.hibernation.enable
+ '' then ''
bindsym r exec ${systemd}/bin/systemctl reboot, mode "default" bindsym h exec ${systemd}/bin/systemctl hibernate, mode "default"
bindsym Shift+s exec ${systemd}/bin/systemctl poweroff, mode "default" ''
else ""
)
+ ''
bindsym r exec ${systemd}/bin/systemctl reboot, mode "default"
bindsym Shift+s exec ${systemd}/bin/systemctl poweroff, mode "default"
# exit system mode: "Enter" or "Escape" # exit system mode: "Enter" or "Escape"
bindsym Return mode "default" bindsym Return mode "default"
bindsym Escape mode "default" bindsym Escape mode "default"
} }
'' ''

View file

@ -96,16 +96,16 @@ in {
]; ];
timeouts = [ timeouts = [
{ {
timeout = 120; timeout = 300;
command = "${swaylock-bg}/bin/swaylock-bg"; command = "${swaylock-bg}/bin/swaylock-bg";
} }
{ {
timeout = 130; timeout = 180;
command = "${sway}/bin/swaymsg \"output * dpms off\""; command = "${sway}/bin/swaymsg \"output * dpms off\"";
resumeCommand = "${sway}/bin/swaymsg \"output * dpms on\""; resumeCommand = "${sway}/bin/swaymsg \"output * dpms on\"";
} }
{ {
timeout = 300; timeout = 600;
command = "${systemd}/bin/systemctl hibernate"; command = "${systemd}/bin/systemctl hibernate";
} }
]; ];

View file

@ -46,10 +46,10 @@ in {
on-scroll = "-1"; on-scroll = "-1";
on-click-right = "mode"; on-click-right = "mode";
format = { format = {
months = "<span color='#ffead3'><b>{}</b></span>"; months = "<span color='#ffead3'><b>{}</b></span>";
days = "<span color='#ecc6d9'><b>{}</b></span>"; days = "<span color='#ecc6d9'><b>{}</b></span>";
weekdays = "<span color='#ffcc66'><b>{}</b></span>"; weekdays = "<span color='#ffcc66'><b>{}</b></span>";
today = "<span color='#ff6699'><b><u>{}</u></b></span>"; today = "<span color='#ff6699'><b><u>{}</u></b></span>";
}; };
}; };
}; };

View file

@ -1,215 +1,221 @@
{ config, pkgs, lib, ... }: {
config,
with lib; pkgs,
lib,
let ...
}:
with lib; let
cfg = config.services.invoiceplane; cfg = config.services.invoiceplane;
eachSite = cfg.sites; eachSite = cfg.sites;
user = "invoiceplane"; user = "invoiceplane";
webserver = config.services.${cfg.webserver}; webserver = config.services.${cfg.webserver};
invoiceplane-config = hostName: cfg: pkgs.writeText "ipconfig.php" '' invoiceplane-config = hostName: cfg:
IP_URL=http://${hostName} pkgs.writeText "ipconfig.php" ''
ENABLE_DEBUG=false IP_URL=http://${hostName}
DISABLE_SETUP=false ENABLE_DEBUG=false
REMOVE_INDEXPHP=false DISABLE_SETUP=false
DB_HOSTNAME=${cfg.database.host} REMOVE_INDEXPHP=false
DB_USERNAME=${cfg.database.user} DB_HOSTNAME=${cfg.database.host}
# NOTE: file_get_contents adds newline at the end of returned string DB_USERNAME=${cfg.database.user}
DB_PASSWORD=${if cfg.database.passwordFile == null then "" else "trim(file_get_contents('${cfg.database.passwordFile}'),\"\\r\\n\")"} # NOTE: file_get_contents adds newline at the end of returned string
DB_DATABASE=${cfg.database.name} DB_PASSWORD=${
DB_PORT=${toString cfg.database.port} if cfg.database.passwordFile == null
SESS_EXPIRATION=864000 then ""
ENABLE_INVOICE_DELETION=false else "trim(file_get_contents('${cfg.database.passwordFile}'),\"\\r\\n\")"
DISABLE_READ_ONLY=false }
ENCRYPTION_KEY= DB_DATABASE=${cfg.database.name}
ENCRYPTION_CIPHER=AES-256 DB_PORT=${toString cfg.database.port}
SETUP_COMPLETED=false SESS_EXPIRATION=864000
REMOVE_INDEXPHP=true ENABLE_INVOICE_DELETION=false
''; DISABLE_READ_ONLY=false
ENCRYPTION_KEY=
extraConfig = hostName: cfg: pkgs.writeText "extraConfig.php" '' ENCRYPTION_CIPHER=AES-256
${toString cfg.extraConfig} SETUP_COMPLETED=false
''; REMOVE_INDEXPHP=true
pkg = hostName: cfg: pkgs.stdenv.mkDerivation rec {
pname = "invoiceplane-${hostName}";
version = src.version;
src = pkgs.invoiceplane;
postPhase = ''
# Patch index.php file to load additional config file
substituteInPlace index.php \
--replace "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();";
''; '';
installPhase = '' extraConfig = hostName: cfg:
mkdir -p $out pkgs.writeText "extraConfig.php" ''
cp -r * $out/ ${toString cfg.extraConfig}
# symlink uploads and log directories
rm -r $out/uploads $out/application/logs $out/vendor/mpdf/mpdf/tmp
ln -sf ${cfg.stateDir}/uploads $out/
ln -sf ${cfg.stateDir}/logs $out/application/
ln -sf ${cfg.stateDir}/tmp $out/vendor/mpdf/mpdf/
# symlink the InvoicePlane config
ln -s ${cfg.stateDir}/ipconfig.php $out/ipconfig.php
# symlink the extraConfig file
ln -s ${extraConfig hostName cfg} $out/extraConfig.php
# symlink additional templates
${concatMapStringsSep "\n" (template: "cp -r ${template}/. $out/application/views/invoice_templates/pdf/") cfg.invoiceTemplates}
''; '';
};
siteOpts = { lib, name, ... }: pkg = hostName: cfg:
{ pkgs.stdenv.mkDerivation rec {
options = { pname = "invoiceplane-${hostName}";
version = src.version;
src = pkgs.invoiceplane;
enable = mkEnableOption (lib.mdDoc "InvoicePlane web application"); postPhase = ''
# Patch index.php file to load additional config file
substituteInPlace index.php \
--replace "require('vendor/autoload.php');" "require('vendor/autoload.php'); \$dotenv = Dotenv\Dotenv::createImmutable(__DIR__, 'extraConfig.php'); \$dotenv->load();";
'';
stateDir = mkOption { installPhase = ''
type = types.path; mkdir -p $out
default = "/var/lib/invoiceplane/${name}"; cp -r * $out/
description = lib.mdDoc ''
This directory is used for uploads of attachments and cache.
The directory passed here is automatically created and permissions
adjusted as required.
'';
};
database = { # symlink uploads and log directories
host = mkOption { rm -r $out/uploads $out/application/logs $out/vendor/mpdf/mpdf/tmp
type = types.str; ln -sf ${cfg.stateDir}/uploads $out/
default = "localhost"; ln -sf ${cfg.stateDir}/logs $out/application/
description = lib.mdDoc "Database host address."; ln -sf ${cfg.stateDir}/tmp $out/vendor/mpdf/mpdf/
};
port = mkOption { # symlink the InvoicePlane config
type = types.port; ln -s ${cfg.stateDir}/ipconfig.php $out/ipconfig.php
default = 3306;
description = lib.mdDoc "Database host port.";
};
name = mkOption { # symlink the extraConfig file
type = types.str; ln -s ${extraConfig hostName cfg} $out/extraConfig.php
default = "invoiceplane";
description = lib.mdDoc "Database name.";
};
user = mkOption { # symlink additional templates
type = types.str; ${concatMapStringsSep "\n" (template: "cp -r ${template}/. $out/application/views/invoice_templates/pdf/") cfg.invoiceTemplates}
default = "invoiceplane"; '';
description = lib.mdDoc "Database user."; };
};
passwordFile = mkOption { siteOpts = {
type = types.nullOr types.path; lib,
default = null; name,
example = "/run/keys/invoiceplane-dbpassword"; ...
description = lib.mdDoc '' }: {
A file containing the password corresponding to options = {
{option}`database.user`. enable = mkEnableOption (lib.mdDoc "InvoicePlane web application");
'';
};
createLocally = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Create the database and database user locally.";
};
};
invoiceTemplates = mkOption {
type = types.listOf types.path;
default = [];
description = lib.mdDoc ''
List of path(s) to respective template(s) which are copied from the 'invoice_templates/pdf' directory.
::: {.note}
These templates need to be packaged before use, see example.
:::
'';
example = literalExpression ''
let
# Let's package an example template
template-vtdirektmarketing = pkgs.stdenv.mkDerivation {
name = "vtdirektmarketing";
# Download the template from a public repository
src = pkgs.fetchgit {
url = "https://git.project-insanity.org/onny/invoiceplane-vtdirektmarketing.git";
sha256 = "1hh0q7wzsh8v8x03i82p6qrgbxr4v5fb05xylyrpp975l8axyg2z";
};
sourceRoot = ".";
# Installing simply means copying template php file to the output directory
installPhase = ""
mkdir -p $out
cp invoiceplane-vtdirektmarketing/vtdirektmarketing.php $out/
"";
};
# And then pass this package to the template list like this:
in [ template-vtdirektmarketing ]
'';
};
poolConfig = mkOption {
type = with types; attrsOf (oneOf [ str int bool ]);
default = {
"pm" = "dynamic";
"pm.max_children" = 32;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 2;
"pm.max_spare_servers" = 4;
"pm.max_requests" = 500;
};
description = lib.mdDoc ''
Options for the InvoicePlane PHP pool. See the documentation on `php-fpm.conf`
for details on configuration directives.
'';
};
extraConfig = mkOption {
type = types.nullOr types.lines;
default = null;
example = ''
SETUP_COMPLETED=true
DISABLE_SETUP=true
IP_URL=https://invoice.example.com
'';
description = lib.mdDoc ''
InvoicePlane configuration. Refer to
<https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example>
for details on supported values.
'';
};
cron = {
enable = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Enable cron service which periodically runs Invoiceplane tasks.
Requires key taken from the administration page. Refer to
<https://wiki.invoiceplane.com/en/1.0/modules/recurring-invoices>
on how to configure it.
'';
};
key = mkOption {
type = types.str;
description = lib.mdDoc "Cron key taken from the administration page.";
};
};
stateDir = mkOption {
type = types.path;
default = "/var/lib/invoiceplane/${name}";
description = lib.mdDoc ''
This directory is used for uploads of attachments and cache.
The directory passed here is automatically created and permissions
adjusted as required.
'';
}; };
database = {
host = mkOption {
type = types.str;
default = "localhost";
description = lib.mdDoc "Database host address.";
};
port = mkOption {
type = types.port;
default = 3306;
description = lib.mdDoc "Database host port.";
};
name = mkOption {
type = types.str;
default = "invoiceplane";
description = lib.mdDoc "Database name.";
};
user = mkOption {
type = types.str;
default = "invoiceplane";
description = lib.mdDoc "Database user.";
};
passwordFile = mkOption {
type = types.nullOr types.path;
default = null;
example = "/run/keys/invoiceplane-dbpassword";
description = lib.mdDoc ''
A file containing the password corresponding to
{option}`database.user`.
'';
};
createLocally = mkOption {
type = types.bool;
default = true;
description = lib.mdDoc "Create the database and database user locally.";
};
};
invoiceTemplates = mkOption {
type = types.listOf types.path;
default = [];
description = lib.mdDoc ''
List of path(s) to respective template(s) which are copied from the 'invoice_templates/pdf' directory.
::: {.note}
These templates need to be packaged before use, see example.
:::
'';
example = literalExpression ''
let
# Let's package an example template
template-vtdirektmarketing = pkgs.stdenv.mkDerivation {
name = "vtdirektmarketing";
# Download the template from a public repository
src = pkgs.fetchgit {
url = "https://git.project-insanity.org/onny/invoiceplane-vtdirektmarketing.git";
sha256 = "1hh0q7wzsh8v8x03i82p6qrgbxr4v5fb05xylyrpp975l8axyg2z";
};
sourceRoot = ".";
# Installing simply means copying template php file to the output directory
installPhase = ""
mkdir -p $out
cp invoiceplane-vtdirektmarketing/vtdirektmarketing.php $out/
"";
};
# And then pass this package to the template list like this:
in [ template-vtdirektmarketing ]
'';
};
poolConfig = mkOption {
type = with types; attrsOf (oneOf [str int bool]);
default = {
"pm" = "dynamic";
"pm.max_children" = 32;
"pm.start_servers" = 2;
"pm.min_spare_servers" = 2;
"pm.max_spare_servers" = 4;
"pm.max_requests" = 500;
};
description = lib.mdDoc ''
Options for the InvoicePlane PHP pool. See the documentation on `php-fpm.conf`
for details on configuration directives.
'';
};
extraConfig = mkOption {
type = types.nullOr types.lines;
default = null;
example = ''
SETUP_COMPLETED=true
DISABLE_SETUP=true
IP_URL=https://invoice.example.com
'';
description = lib.mdDoc ''
InvoicePlane configuration. Refer to
<https://github.com/InvoicePlane/InvoicePlane/blob/master/ipconfig.php.example>
for details on supported values.
'';
};
cron = {
enable = mkOption {
type = types.bool;
default = false;
description = lib.mdDoc ''
Enable cron service which periodically runs Invoiceplane tasks.
Requires key taken from the administration page. Refer to
<https://wiki.invoiceplane.com/en/1.0/modules/recurring-invoices>
on how to configure it.
'';
};
key = mkOption {
type = types.str;
description = lib.mdDoc "Cron key taken from the administration page.";
};
};
}; };
in };
{ in {
disabledModules = [ disabledModules = [
"services/web-apps/invoiceplane.nix" "services/web-apps/invoiceplane.nix"
]; ];
@ -218,7 +224,6 @@ in
options = { options = {
services.invoiceplane = mkOption { services.invoiceplane = mkOption {
type = types.submodule { type = types.submodule {
options.sites = mkOption { options.sites = mkOption {
type = types.attrsOf (types.submodule siteOpts); type = types.attrsOf (types.submodule siteOpts);
default = {}; default = {};
@ -226,7 +231,7 @@ in
}; };
options.webserver = mkOption { options.webserver = mkOption {
type = types.enum [ "caddy" "nginx" ]; type = types.enum ["caddy" "nginx"];
default = "caddy"; default = "caddy";
description = lib.mdDoc '' description = lib.mdDoc ''
Which webserver to use for virtual host management. Currently only Which webserver to use for virtual host management. Currently only
@ -237,160 +242,172 @@ in
default = {}; default = {};
description = lib.mdDoc "InvoicePlane configuration."; description = lib.mdDoc "InvoicePlane configuration.";
}; };
}; };
# implementation # implementation
config = mkIf (eachSite != {}) (mkMerge [{ config = mkIf (eachSite != {}) (mkMerge [
{
assertions = flatten (mapAttrsToList (hostName: cfg: [
{
assertion = cfg.database.createLocally -> cfg.database.user == user;
message = ''services.invoiceplane.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned'';
}
{
assertion = cfg.database.createLocally -> cfg.database.passwordFile == null;
message = ''services.invoiceplane.sites."${hostName}".database.passwordFile cannot be specified if services.invoiceplane.sites."${hostName}".database.createLocally is set to true.'';
}
{
assertion = cfg.cron.enable -> cfg.cron.key != null;
message = ''services.invoiceplane.sites."${hostName}".cron.key must be set in order to use cron service.'';
}
])
eachSite);
assertions = flatten (mapAttrsToList (hostName: cfg: services.mysql = mkIf (any (v: v.database.createLocally) (attrValues eachSite)) {
[{ assertion = cfg.database.createLocally -> cfg.database.user == user; enable = true;
message = ''services.invoiceplane.sites."${hostName}".database.user must be ${user} if the database is to be automatically provisioned''; package = mkDefault pkgs.mariadb;
} ensureDatabases = mapAttrsToList (hostName: cfg: cfg.database.name) eachSite;
{ assertion = cfg.database.createLocally -> cfg.database.passwordFile == null; ensureUsers =
message = ''services.invoiceplane.sites."${hostName}".database.passwordFile cannot be specified if services.invoiceplane.sites."${hostName}".database.createLocally is set to true.''; mapAttrsToList (
} hostName: cfg: {
{ assertion = cfg.cron.enable -> cfg.cron.key != null; name = cfg.database.user;
message = ''services.invoiceplane.sites."${hostName}".cron.key must be set in order to use cron service.''; ensurePermissions = {"${cfg.database.name}.*" = "ALL PRIVILEGES";};
}
]) eachSite);
services.mysql = mkIf (any (v: v.database.createLocally) (attrValues eachSite)) {
enable = true;
package = mkDefault pkgs.mariadb;
ensureDatabases = mapAttrsToList (hostName: cfg: cfg.database.name) eachSite;
ensureUsers = mapAttrsToList (hostName: cfg:
{ name = cfg.database.user;
ensurePermissions = { "${cfg.database.name}.*" = "ALL PRIVILEGES"; };
}
) eachSite;
};
services.phpfpm = {
phpPackage = pkgs.php81;
pools = mapAttrs' (hostName: cfg: (
nameValuePair "invoiceplane-${hostName}" {
inherit user;
group = webserver.group;
settings = {
"listen.owner" = webserver.user;
"listen.group" = webserver.group;
} // cfg.poolConfig;
}
)) eachSite;
};
}
{
systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
"d ${cfg.stateDir} 0750 ${user} ${webserver.group} - -"
"f ${cfg.stateDir}/ipconfig.php 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/logs 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/uploads 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/uploads/archive 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/uploads/customer_files 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/uploads/temp 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/uploads/temp/mpdf 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/tmp 0750 ${user} ${webserver.group} - -"
]) eachSite);
systemd.services.invoiceplane-config = {
serviceConfig.Type = "oneshot";
script = concatStrings (mapAttrsToList (hostName: cfg:
''
mkdir -p ${cfg.stateDir}/logs \
${cfg.stateDir}/uploads
if ! grep -q IP_URL "${cfg.stateDir}/ipconfig.php"; then
cp "${invoiceplane-config hostName cfg}" "${cfg.stateDir}/ipconfig.php"
fi
'') eachSite);
wantedBy = [ "multi-user.target" ];
};
users.users.${user} = {
group = webserver.group;
isSystemUser = true;
};
}
{
# Cron service implementation
systemd.timers = mapAttrs' (hostName: cfg: (
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "5m";
OnUnitActiveSec = "5m";
Unit = "invoiceplane-cron-${hostName}.service";
};
})
)) eachSite;
systemd.services =
mapAttrs' (hostName: cfg: (
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
serviceConfig = {
Type = "oneshot";
User = user;
ExecStart = "${pkgs.curl}/bin/curl --header 'Host: ${hostName}' http://localhost/invoices/cron/recur/${cfg.cron.key}";
};
})
)) eachSite;
}
(mkIf (cfg.webserver == "caddy") {
services.caddy = {
enable = true;
virtualHosts = mapAttrs' (hostName: cfg: (
nameValuePair "http://${hostName}" {
extraConfig = ''
root * ${pkg hostName cfg}
file_server
php_fastcgi unix/${config.services.phpfpm.pools."invoiceplane-${hostName}".socket}
'';
}
)) eachSite;
};
})
(mkIf (cfg.webserver == "nginx") {
services.nginx = {
enable = true;
virtualHosts = mapAttrs' (hostName: cfg: (
nameValuePair "${hostName}" {
root = "${pkg hostName cfg}";
extraConfig = ''
index index.php index.html index.htm;
if (!-e $request_filename){
rewrite ^(.*)$ /index.php break;
} }
''; )
eachSite;
};
locations = { services.phpfpm = {
"/setup".extraConfig = '' phpPackage = pkgs.php81;
rewrite ^(.*)$ http://${hostName}/ redirect; pools =
''; mapAttrs' (hostName: cfg: (
nameValuePair "invoiceplane-${hostName}" {
inherit user;
group = webserver.group;
settings =
{
"listen.owner" = webserver.user;
"listen.group" = webserver.group;
}
// cfg.poolConfig;
}
))
eachSite;
};
}
"~ .php$" = { {
extraConfig = '' systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
fastcgi_split_path_info ^(.+\.php)(/.+)$; "d ${cfg.stateDir} 0750 ${user} ${webserver.group} - -"
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; "f ${cfg.stateDir}/ipconfig.php 0750 ${user} ${webserver.group} - -"
fastcgi_pass unix:${config.services.phpfpm.pools."invoiceplane-${hostName}".socket}; "d ${cfg.stateDir}/logs 0750 ${user} ${webserver.group} - -"
include ${pkgs.nginx}/conf/fastcgi_params; "d ${cfg.stateDir}/uploads 0750 ${user} ${webserver.group} - -"
include ${pkgs.nginx}/conf/fastcgi.conf; "d ${cfg.stateDir}/uploads/archive 0750 ${user} ${webserver.group} - -"
''; "d ${cfg.stateDir}/uploads/customer_files 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/uploads/temp 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/uploads/temp/mpdf 0750 ${user} ${webserver.group} - -"
"d ${cfg.stateDir}/tmp 0750 ${user} ${webserver.group} - -"
])
eachSite);
systemd.services.invoiceplane-config = {
serviceConfig.Type = "oneshot";
script = concatStrings (mapAttrsToList (hostName: cfg: ''
mkdir -p ${cfg.stateDir}/logs \
${cfg.stateDir}/uploads
if ! grep -q IP_URL "${cfg.stateDir}/ipconfig.php"; then
cp "${invoiceplane-config hostName cfg}" "${cfg.stateDir}/ipconfig.php"
fi
'')
eachSite);
wantedBy = ["multi-user.target"];
};
users.users.${user} = {
group = webserver.group;
isSystemUser = true;
};
}
{
# Cron service implementation
systemd.timers =
mapAttrs' (hostName: cfg: (
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
wantedBy = ["timers.target"];
timerConfig = {
OnBootSec = "5m";
OnUnitActiveSec = "5m";
Unit = "invoiceplane-cron-${hostName}.service";
}; };
}; })
} ))
)) eachSite; eachSite;
};
})
systemd.services =
mapAttrs' (hostName: cfg: (
nameValuePair "invoiceplane-cron-${hostName}" (mkIf cfg.cron.enable {
serviceConfig = {
Type = "oneshot";
User = user;
ExecStart = "${pkgs.curl}/bin/curl --header 'Host: ${hostName}' http://localhost/invoices/cron/recur/${cfg.cron.key}";
};
})
))
eachSite;
}
(mkIf (cfg.webserver == "caddy") {
services.caddy = {
enable = true;
virtualHosts =
mapAttrs' (hostName: cfg: (
nameValuePair "http://${hostName}" {
extraConfig = ''
root * ${pkg hostName cfg}
file_server
php_fastcgi unix/${config.services.phpfpm.pools."invoiceplane-${hostName}".socket}
'';
}
))
eachSite;
};
})
(mkIf (cfg.webserver == "nginx") {
services.nginx = {
enable = true;
virtualHosts =
mapAttrs' (hostName: cfg: (
nameValuePair "${hostName}" {
root = "${pkg hostName cfg}";
extraConfig = ''
index index.php index.html index.htm;
if (!-e $request_filename){
rewrite ^(.*)$ /index.php break;
}
'';
locations = {
"/setup".extraConfig = ''
rewrite ^(.*)$ http://${hostName}/ redirect;
'';
"~ .php$" = {
extraConfig = ''
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:${config.services.phpfpm.pools."invoiceplane-${hostName}".socket};
include ${pkgs.nginx}/conf/fastcgi_params;
include ${pkgs.nginx}/conf/fastcgi.conf;
'';
};
};
}
))
eachSite;
};
})
]); ]);
} }

View file

@ -5,15 +5,16 @@
flake, flake,
... ...
}: { }: {
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ nixpkgs.config.allowUnfreePredicate = pkg:
"steam" builtins.elem (lib.getName pkg) [
"steam-original" "steam"
"steam-run" "steam-original"
"hplip" "steam-run"
"cups-brother-hl3140cw" "hplip"
"uhk-agent" "cups-brother-hl3140cw"
"uhk-udev-rules" "uhk-agent"
]; "uhk-udev-rules"
];
nix = { nix = {
# Use default version alias for nix package # Use default version alias for nix package

View file

@ -1,5 +1,8 @@
{ lib, config, ... }:
{ {
lib,
config,
...
}: {
environment.persistence."/persist" = { environment.persistence."/persist" = {
hideMounts = true; hideMounts = true;
directories = [ directories = [
@ -17,7 +20,7 @@
fileSystems."/etc/nixos" = { fileSystems."/etc/nixos" = {
device = "/home/${config.pub-solar.user.name}/Workspace/os"; device = "/home/${config.pub-solar.user.name}/Workspace/os";
options = [ "bind" ]; options = ["bind"];
}; };
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [

View file

@ -1,5 +1,4 @@
{ pkgs, ... }: {pkgs, ...}: {
{
services.cron = { services.cron = {
enable = true; enable = true;
systemCronJobs = [ systemCronJobs = [

View file

@ -16,15 +16,19 @@
services.printing.listenAddresses = ["localhost:631"]; services.printing.listenAddresses = ["localhost:631"];
services.printing.defaultShared = lib.mkDefault false; services.printing.defaultShared = lib.mkDefault false;
services.printing.drivers = [ services.printing.drivers =
pkgs.gutenprint [
] ++ (if (pkgs.system == "x86_64-linux") pkgs.gutenprint
then [ pkgs.cups-brother-hl3140cw ] ]
else []); ++ (
if (pkgs.system == "x86_64-linux")
then [pkgs.cups-brother-hl3140cw]
else []
);
environment.persistence."/persist" = { # environment.persistence."/persist" = {
directories = [ # directories = [
"/var/lib/cups" # "/etc/lib/cups"
]; # ];
}; # };
} }

View file

@ -5,7 +5,7 @@
lib, lib,
... ...
}: { }: {
networking.firewall.allowedTCPPorts = [ 80 443 ]; networking.firewall.allowedTCPPorts = [80 443];
services.nginx = { services.nginx = {
enable = true; enable = true;

View file

@ -6,27 +6,27 @@
user = config.pub-solar.user; user = config.pub-solar.user;
xdg = config.home-manager.users."${user.name}".xdg; xdg = config.home-manager.users."${user.name}".xdg;
in '' in ''
# What happened? # What happened?
# #
# fix feat build chore ci docs style refactor perf test # fix feat build chore ci docs style refactor perf test
# #
# type!(optional scope): <summary> --------------# # type!(optional scope): <summary> --------------#
# #
# ^\n # ^\n
# What exactly was done and why? --------------------------------------# # What exactly was done and why? --------------------------------------#
# #
# ^\n # ^\n
# #
# Any issue numbers or links? # Any issue numbers or links?
# #
# Ref: #123 # Ref: #123
# ^\n # ^\n
# #
# Co-authored-by: Example Name <email@example.com> # Co-authored-by: Example Name <email@example.com>
'' ''

View file

@ -21,27 +21,40 @@ in {
config = { config = {
programs.command-not-found.enable = false; programs.command-not-found.enable = false;
users.users."${psCfg.user.name}".packages = with pkgs; [ users.users."${psCfg.user.name}".packages = with pkgs;
ack [
asciinema asciinema
bat bat
blesh blesh
eza eza
fd fd
jump ripgrep
(nnn.overrideAttrs (o: { (nnn.overrideAttrs (o: {
patches = patches =
(o.patches or []) (o.patches or [])
++ [ ++ [
./nnn/0001-feat-use-wasd-keybindings-for-jkli.patch ./nnn/0001-feat-use-wasd-keybindings-for-jkli.patch
]; ];
})) }))
p p
powerline powerline
screen screen
silver-searcher watson
watson jump
]; ]
++ (
if cfg.full
then [
# Nix specific utilities
alejandra
manix
nix-index
nix-tree
nix-inspect
nvd
]
else []
);
home-manager.users."${psCfg.user.name}" = { home-manager.users."${psCfg.user.name}" = {
xdg.dataFile."scripts/base16.sh".source = .local/share/scripts/base16.sh; xdg.dataFile."scripts/base16.sh".source = .local/share/scripts/base16.sh;
@ -79,13 +92,11 @@ in {
inherit pkgs; inherit pkgs;
inherit lib; inherit lib;
}; };
# Ensure nvim backup directory gets created # Ensure nvim backup directory gets created
# Workaround for E510: Can't make backup file (add ! to override) # Workaround for E510: Can't make backup file (add ! to override)
xdg.dataFile."nvim/backup/.keep".text = ""; xdg.dataFile."nvim/backup/.keep".text = "";
xdg.dataFile."nvim/json-schemas/.keep".text = ""; xdg.dataFile."nvim/json-schemas/.keep".text = "";
# Generated with:
# docker run -it --name caddy-json-schema registry.greenbaum.cloud/gc/caddy-l4:2.5.2 caddy json-schema -output /srv/caddy_schema.json
xdg.dataFile."nvim/json-schemas/caddy_schema.json".source = .local/share/nvim/json-schemas/caddy_schema.json;
xdg.dataFile."nvim/templates/.keep".text = ""; xdg.dataFile."nvim/templates/.keep".text = "";
programs.git = import ./git {}; programs.git = import ./git {};

View file

@ -1,5 +1,4 @@
{ ... }: {...}: {
{
enable = true; enable = true;
nix-direnv = { nix-direnv = {
enable = true; enable = true;

View file

@ -1,5 +1,4 @@
{ ... }: {...}: {
{
enable = true; enable = true;
extraConfig = { extraConfig = {

View file

@ -0,0 +1,48 @@
lua <<EOF
local luasnip = require 'luasnip'
local cmp = require 'cmp'
cmp.setup {
snippet = {
expand = function(args)
require('luasnip').lsp_expand(args.body)
end,
},
mapping = {
['<C-p>'] = cmp.mapping.select_prev_item(),
['<C-n>'] = cmp.mapping.select_next_item(),
['<C-d>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.close(),
['<CR>'] = cmp.mapping.confirm {
behavior = cmp.ConfirmBehavior.Replace,
select = true,
},
['<Tab>'] = function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end,
['<S-Tab>'] = function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end,
},
sources = {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
},
}
EOF

View file

@ -7,8 +7,6 @@
psCfg = config.pub-solar; psCfg = config.pub-solar;
cfg = config.pub-solar.terminal-life; cfg = config.pub-solar.terminal-life;
xdg = config.home-manager.users."${psCfg.user.name}".xdg; xdg = config.home-manager.users."${psCfg.user.name}".xdg;
preview-file = pkgs.writeShellScriptBin "preview-file" (import ./preview-file.nix pkgs);
in { in {
enable = true; enable = true;
@ -20,129 +18,131 @@ in {
withRuby = true; withRuby = true;
withPython3 = true; withPython3 = true;
extraPackages = with pkgs; extraPackages = with pkgs; [
lib.mkIf (cfg.full) [ ripgrep
ansible-language-server nixd
ccls universal-ctags
gopls # ansible-language-server
nixd # clang-tools
nodejs # gopls
nodePackages.bash-language-server # nodePackages.bash-language-server
nodePackages.dockerfile-language-server-nodejs # nodePackages.svelte-language-server
nodePackages.svelte-language-server # nodePackages.typescript
nodePackages.typescript # nodePackages.typescript-language-server
nodePackages.typescript-language-server # nodePackages.vue-language-server
nodePackages.vim-language-server # nodePackages.vscode-langservers-extracted
nodePackages.vue-language-server # nginx-language-server
nodePackages.vscode-langservers-extracted # lua-language-server
nodePackages.yaml-language-server # cmake-language-server
python3Packages.python-lsp-server # vim-language-server
python3Full # yaml-language-server
rust-analyzer # python3Packages.python-lsp-server
solargraph # nodePackages.dockerfile-language-server-nodejs
terraform-ls # docker-compose-language-service
universal-ctags # rust-analyzer
]; # cargo
# solargraph
plugins = with pkgs.vimPlugins; lib.mkIf cfg.full [ # terraform-ls
pkgs.vimPlugins.nvim-treesitter.withAllGrammars # python3Full
# Dependencies for nvim-lspconfig
nvim-cmp
cmp-nvim-lsp
cmp_luasnip
luasnip
# Quickstart configs for neovim LSP
lsp_extensions-nvim
nvim-lspconfig
# Collaborative editing in Neovim using built-in capabilities
instant-nvim-nvfetcher
# Search functionality behind :Ack
ack-vim
# The status bar in the bottom of the screen with the mode indication and file location
vim-airline
# Automatically load editorconfig files in repos to configure nvim settings
editorconfig-vim
# File browser. Use <leader>n to access
nnn-vim
# Highlight characters when using f, F, t, and T
quick-scope
# Get sudo in vim; :SudaWrite <optional filename>
vim-suda
# Undo history etc. per project
vim-workspace-nvfetcher
# JSON schemas
SchemaStore-nvim
# Work with tags files
vim-gutentags
# Neovim colorschemes / themes
sonokai
vim-hybrid-material
vim-airline-themes
vim-apprentice-nvfetcher
# Git integrations
# A Git wrapper so awesome, it should be illegal
fugitive
# Shows git diff markers in the sign column
vim-gitgutter
# GitHub extension for fugitive
vim-rhubarb
# Ease your git workflow within Vim
vimagit-nvfetcher
# FZF fuzzy finder
fzf-vim
fzfWrapper
# Make the yanked region apparent
vim-highlightedyank
# :Beautify Code beautifier
vim-beautify-nvfetcher
# Unload, delete or wipe a buffer without closing the window
vim-bufkill
# Defaults everyone can agree on
vim-sensible
# emmet for vim: http://emmet.io/
emmet-vim
# Caddyfile syntax support for Vim
vim-caddyfile-nvfetcher
]; ];
extraConfig = builtins.concatStringsSep "\n" [ plugins = with pkgs.vimPlugins;
'' [
" Persistent undo # The status bar in the bottom of the screen with the mode indication and file location
set undofile vim-airline
set undodir=${xdg.cacheHome}/nvim/undo
set backupdir=${xdg.dataHome}/nvim/backup # Automatically load editorconfig files in repos to configure nvim settings
set directory=${xdg.dataHome}/nvim/swap/ editorconfig-vim
''
(builtins.readFile ./init.vim) # File browser. Use <leader>n to access
(builtins.readFile ./plugins.vim) nnn-vim
(builtins.readFile ./clipboard.vim)
(builtins.readFile ./ui.vim) # Highlight characters when using f, F, t, and T
(builtins.readFile ./quickfixopenall.vim) quick-scope
(builtins.readFile ./lsp.vim)
(builtins.readFile ./filetypes.vim) # Undo history etc. per project
'' vim-workspace-nvfetcher
" fzf with file preview
command! -bang -nargs=? -complete=dir Files # Neovim colorschemes / themes
\ call fzf#vim#files(<q-args>, { 'options': ['--keep-right', '--cycle', '--layout', 'reverse', '--preview', '${preview-file}/bin/preview-file {}'] }, <bang>0) sonokai
'' vim-hybrid-material
]; vim-airline-themes
vim-apprentice-nvfetcher
# Git integrations
# A Git wrapper so awesome, it should be illegal
fugitive
# Shows git diff markers in the sign column
vim-gitgutter
# GitHub extension for fugitive
vim-rhubarb
# Ease your git workflow within Vim
vimagit-nvfetcher
# Telescope fuzzy finder
telescope-nvim
telescope-fzf-native-nvim
# Make the yanked region apparent
vim-highlightedyank
# :Beautify Code beautifier
vim-beautify-nvfetcher
# Unload, delete or wipe a buffer without closing the window
vim-bufkill
# Defaults everyone can agree on
vim-sensible
# Work with tags files
vim-gutentags
]
++ (
if cfg.full
then [
nvim-treesitter.withAllGrammars
# Dependencies for nvim-lspconfig
nvim-cmp
cmp-nvim-lsp
cmp_luasnip
luasnip
# Quickstart configs for neovim LSP
lsp_extensions-nvim
nvim-lspconfig
# Collaborative editing in Neovim using built-in capabilities
instant-nvim-nvfetcher
# JSON schemas
SchemaStore-nvim
]
else []
);
extraConfig = builtins.concatStringsSep "\n" ([
''
" Persistent undo
set undofile
set undodir=${xdg.cacheHome}/nvim/undo
set backupdir=${xdg.dataHome}/nvim/backup
set directory=${xdg.dataHome}/nvim/swap/
''
(builtins.readFile ./init.vim)
(builtins.readFile ./plugins.vim)
(builtins.readFile ./clipboard.vim)
(builtins.readFile ./ui.vim)
(builtins.readFile ./filetypes.vim)
]
++ (
if cfg.full
then [
(builtins.readFile ./lsp.vim)
(builtins.readFile ./cmp.vim)
]
else []
));
} }

View file

@ -1,3 +1,10 @@
au BufRead,BufNewFile *.html.twig set filetype=html au BufRead,BufNewFile *.html.twig set filetype=html
au BufRead,BufNewFile *.vto set filetype=html au BufRead,BufNewFile *.vto set filetype=html
au BufRead,BufNewFile *.njk set filetype=html au BufRead,BufNewFile *.njk set filetype=html
au BufRead,BufNewFile *.age set filetype=age
autocmd FileType age setlocal noeol nofixeol
au! BufNewFile,BufReadPost *.{yaml,yml} set filetype=yaml
autocmd FileType yaml setlocal ts=2 sts=2 sw=2 expandtab

View file

@ -58,10 +58,6 @@ map <leader>wJ :wincmd H<CR>
map <leader>wK :wincmd J<CR> map <leader>wK :wincmd J<CR>
map <leader>wL :wincmd L<CR> map <leader>wL :wincmd L<CR>
map <leader>tj :tabprevious<CR>
map <leader>tl :tabnext<CR>
map <leader>tq :tabclose<CR>
" replay macro for each line of a visual selection " replay macro for each line of a visual selection
xnoremap @q :normal @q<CR> xnoremap @q :normal @q<CR>
xnoremap @@ :normal @@<CR> xnoremap @@ :normal @@<CR>
@ -73,34 +69,13 @@ xnoremap p pgvy
inoremap jj <Esc> inoremap jj <Esc>
" Open new buffer " Open new buffer
nmap <leader>T :enew<cr> nmap <leader>bn :enew<cr>
" Move to the next buffer
nmap <leader>l :bnext<CR> nmap <leader>l :bnext<CR>
nmap <leader>bn :bnext<CR>
" Move to the previous buffer
nmap <leader>j :bprevious<CR> nmap <leader>j :bprevious<CR>
nmap <leader>bp :bprevious<CR>
" Close the current buffer and move to the previous one
" This replicates the idea of closing a tab
nmap <leader>q :bp <BAR> bd #<CR> nmap <leader>q :bp <BAR> bd #<CR>
nmap <leader>bq :bp <BAR> bd #<CR>
" Show all open buffers and their status
nmap <leader>bl :ls<CR>
" Mapping selecting mappings
nmap <leader><tab> <plug>(fzf-maps-n)
xmap <leader><tab> <plug>(fzf-maps-x)
omap <leader><tab> <plug>(fzf-maps-o)
nmap <c-p> :Files<CR>
imap <c-p> <ESC>:Files<CR>
" Insert mode completion
imap <c-x><c-k> <plug>(fzf-complete-word)
imap <c-x><c-f> <plug>(fzf-complete-path)
imap <c-x><c-j> <plug>(fzf-complete-file)
imap <c-x><c-l> <plug>(fzf-complete-line)
" Clear quickfix shortcut " Clear quickfix shortcut
nmap <Leader>c :ccl<CR> nmap <Leader>c :ccl<CR>
@ -110,7 +85,3 @@ nmap <Leader>c :ccl<CR>
if has("autocmd") if has("autocmd")
au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
endif endif
nmap - :NnnPicker %<CR>
nmap <leader>n :NnnPicker %<CR>
nmap <leader>N :NnnPicker<CR>

View file

@ -1,258 +1,149 @@
" Set completeopt to have a better completion experience
" :help completeopt
" menuone: popup even when there's only one match
" noinsert: Do not insert text until a selection is made
" noselect: Do not select, force user to select one from the menu
set completeopt=menuone,noinsert,noselect
" Avoid showing extra messages when using completion
set shortmess+=c
function AddTemplate(tmpl_file)
exe "0read " . a:tmpl_file
set nomodified
6
endfunction
autocmd BufNewFile shell.nix call AddTemplate("$XDG_DATA_HOME/nvim/templates/shell.nix.tmpl")
" Configure neovim 0.6+ experimental LSPs " Configure neovim 0.6+ experimental LSPs
" https://github.com/neovim/nvim-lspconfig " https://github.com/neovim/nvim-lspconfig
" https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md " https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
" https://github.com/neovim/nvim-lspconfig/wiki/UI-Customization " https://github.com/neovim/nvim-lspconfig/wiki/UI-Customization
" https://gitlab.com/Iron_E/dotfiles/-/blob/master/.config/nvim/lua/_config/plugin/nvim_lsp.lua " https://gitlab.com/Iron_E/dotfiles/-/blob/master/.config/nvim/lua/_config/plugin/nvim_lsp.lua
lua <<EOF lua <<EOF
local nvim_lsp = require('lspconfig')
-- Mappings (global) -- Set completeopt to have a better completion experience
-- See `:help vim.diagnostic.*` for documentation on any of the below functions vim.o.completeopt = 'menuone,noselect,noinsert'
local opts = { noremap=true, silent=true } vim.o.shortmess = vim.o.shortmess .. 'c'
vim.api.nvim_set_keymap('n', '<leader>e', '<cmd>lua vim.diagnostic.open_float()<CR>', opts) vim.o.signcolumn = 'yes:2'
vim.api.nvim_set_keymap('n', 'g[', '<cmd>lua vim.diagnostic.goto_prev()<CR>', opts)
vim.api.nvim_set_keymap('n', 'g]', '<cmd>lua vim.diagnostic.goto_next()<CR>', opts)
vim.api.nvim_set_keymap('n', '<leader>dq', '<cmd>lua vim.diagnostic.setloclist()<CR>', opts)
vim.api.nvim_set_keymap('n', '<leader>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts)
-- Use an on_attach function to only map the following keys local lspconfig = require('lspconfig')
-- after the language server attaches to the current buffer
local on_attach = function(client, bufnr)
-- Enable completion triggered by <c-x><c-o>
vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
-- Mappings (available if LSP is configured and attached to buffer) -- Mappings (global)
-- See `:help vim.lsp.*` for documentation on any of the below functions -- See `:help vim.diagnostic.*` for documentation on any of the below functions
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts) local opts = { noremap=true, silent=true }
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', opts) vim.api.nvim_set_keymap('n', 'g[', '<cmd>lua vim.diagnostic.goto_prev()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', opts) vim.api.nvim_set_keymap('n', 'g]', '<cmd>lua vim.diagnostic.goto_next()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts) vim.api.nvim_set_keymap('n', '<leader>dq', '<cmd>lua vim.diagnostic.setloclist()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts) vim.api.nvim_set_keymap('n', '<leader>do', '<cmd>lua vim.diagnostic.open_float()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts) vim.api.nvim_set_keymap('n', '<leader>bf', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
-- Show diagnostic popup on cursor hold local on_attach = function(client, bufnr)
vim.api.nvim_create_autocmd("CursorHold", { -- Enable completion triggered by <c-x><c-o>
buffer = bufnr, vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
callback = function()
local opts = {
focusable = false,
close_events = { "BufLeave", "CursorMoved", "InsertEnter", "FocusLost" },
border = 'rounded',
source = 'always',
prefix = ' ',
scope = 'cursor',
}
vim.diagnostic.open_float(nil, opts)
end
})
end -- Mappings (available if LSP is configured and attached to buffer)
-- See `:help vim.lsp.*` for documentation on any of the below functions
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gD', '<cmd>lua vim.lsp.buf.declaration()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gT', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<leader>ca', '<cmd>lua vim.lsp.buf.code_action()<CR>', opts)
-- Add additional capabilities supported by nvim-cmp -- Show diagnostic popup on cursor hold
local capabilities = require('cmp_nvim_lsp').default_capabilities() vim.api.nvim_create_autocmd("CursorHold", {
-- vscode HTML lsp needs this https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#html buffer = bufnr,
capabilities.textDocument.completion.completionItem.snippetSupport = true callback = function()
local opts = {
-- vscode HTML lsp needs this https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#html focusable = false,
capabilities.textDocument.completion.completionItem.snippetSupport = true close_events = { "BufLeave", "CursorMoved", "InsertEnter", "FocusLost" },
border = 'rounded',
local use_denols_for_typescript = not(os.getenv('NVIM_USE_DENOLS') == nil) source = 'always',
prefix = ' ',
for lsp_key, lsp_settings in pairs({ scope = 'cursor',
'ansiblels', ---------------------------- Ansible
'bashls', ------------------------------- Bash
'ccls', --------------------------------- C / C++ / Objective-C
'cssls', -------------------------------- CSS / SCSS / LESS
'dockerls', ----------------------------- Docker
['gopls'] = { --------------------------- Go
['settings'] = {
['gopls'] = {
['analyses'] = {
['unusedparams'] = true,
},
['staticcheck'] = true
},
},
},
'html', --------------------------------- HTML
['jdtls'] = { --------------------------- Java
['root_dir'] = nvim_lsp.util.root_pattern('.git', 'pom.xml', 'build.xml'),
['init_options'] = {
['jvm_args'] = {['java.format.settings.url'] = vim.fn.stdpath('config')..'/eclipse-formatter.xml'},
['workspace'] = vim.fn.stdpath('cache')..'/java-workspaces'
}
},
['jsonls'] = { -------------------------- JSON
['settings'] = {
['json'] = {
['schemas' ] = vim.list_extend(
{
{
['description'] = 'JSON schema for Caddy v2',
['fileMatch'] = { '*caddy*.json' },
['name'] = 'caddy_schema.json',
['url'] = vim.fn.stdpath('data')..'/json-schemas/caddy_schema.json',
},
},
require('schemastore').json.schemas()
),
['validate'] = { ['enable'] = true }
}
}
},
'nixd', --------------------------------- Nix
'phpactor', ----------------------------- PHP
'pylsp', -------------------------------- Python
'solargraph', --------------------------- Ruby
'rust_analyzer', ------------------------ Rust
['sqlls'] = {
['cmd'] = {vim.fn.stdpath('data')..'/nvm/versions/node/v12.19.0/bin/sql-language-server', 'up', '--method', 'stdio'}
},
['terraformls'] = { --------------------- Terraform
['filetypes'] = { 'terraform', 'hcl', 'tf' }
},
-- The TS/JS server is chosen depending on an environment variable,
-- since denols is nicer for Deno based projects
------------------------ Deno TS/JS
------------------------------------ Typescript / JavaScript
(use_denols_for_typescript and 'denols' or 'tsserver'),
'vuels', -------------------------------- Vue
'svelte', ------------------------------- Svelte
['yamlls'] = { -------------------------- YAML
['settings'] = {
['yaml'] = {
['schemas'] = {
['https://json.schemastore.org/github-workflow'] = '.github/workflows/*.{yml,yaml}',
['https://json.schemastore.org/github-action'] = '.github/action.{yml,yaml}',
['https://json.schemastore.org/drone'] = '*.drone.{yml,yaml}',
['https://json.schemastore.org/swagger-2.0'] = 'swagger.{yml,yaml}',
}
}
}
}
}) do -- Setup all of the language servers. †
if type(lsp_key) == 'number' then -- Enable the LSP with defaults.
-- The `lsp` is an index in this case.
nvim_lsp[lsp_settings].setup{
on_attach = on_attach,
flags = {
debounce_text_changes = 150,
},
capabilities = capabilities,
} }
else -- Use the LSP's configuration. vim.diagnostic.open_float(nil, opts)
lsp_settings.on_attach = on_attach
lsp_settings.capabilities = capabilities
nvim_lsp[lsp_key].setup(lsp_settings)
end end
end --
-- configure floating diagnostics appearance, symbols
local signs = { Error = " ", Warn = " ", Hint = " ", Info = " " }
for type, icon in pairs(signs) do
local hl = "DiagnosticSign" .. type
vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = hl })
end
-- Set completeopt to have a better completion experience
vim.o.completeopt = 'menuone,noselect'
-- luasnip setup
local luasnip = require 'luasnip'
-- nvim-cmp setup
local cmp = require 'cmp'
cmp.setup {
snippet = {
expand = function(args)
require('luasnip').lsp_expand(args.body)
end,
},
mapping = {
['<C-p>'] = cmp.mapping.select_prev_item(),
['<C-n>'] = cmp.mapping.select_next_item(),
['<C-d>'] = cmp.mapping.scroll_docs(-4),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-Space>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.close(),
['<CR>'] = cmp.mapping.confirm {
behavior = cmp.ConfirmBehavior.Replace,
select = true,
},
['<Tab>'] = function(fallback)
if cmp.visible() then
cmp.select_next_item()
elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
else
fallback()
end
end,
['<S-Tab>'] = function(fallback)
if cmp.visible() then
cmp.select_prev_item()
elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
else
fallback()
end
end,
},
sources = {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
},
}
-- https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#denols
vim.g.markdown_fenced_languages = {
"ts=typescript"
}
-- Configure diagnostics
vim.diagnostic.config({
virtual_text = false,
signs = true,
underline = true,
update_in_insert = false,
severity_sort = false,
}) })
-- Change diagnostic symbols in the sign column (gutter) end
local signs = { Error = " ", Warn = " ", Hint = " ", Info = " " }
for type, icon in pairs(signs) do
local hl = "DiagnosticSign" .. type
vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = hl })
end
EOF
" have a fixed column for the diagnostics to appear in local lspconfig = require 'lspconfig'
" this removes the jitter when warnings/errors flow in -- Add additional capabilities supported by nvim-cmp
set signcolumn=yes:2 local CAPABILITIES = require('cmp_nvim_lsp').default_capabilities()
--- Event handlers
local HANDLERS = {
-- TODO: replace with vim.lsp.protocol.Methods
["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, FLOAT_CONFIG),
["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, FLOAT_CONFIG),
}
-- vscode HTML lsp needs this https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#html
CAPABILITIES.textDocument.completion.completionItem.snippetSupport = true
local function setup(lsp, config)
if config == nil then
config = {}
end
config.capabilities = CAPABILITIES
config.handlers = HANDLERS
config.on_attach = on_attach
lspconfig[lsp].setup(config)
end
setup('nixd')
setup('bashls')
setup('clangd')
setup('cssls')
setup('eslint')
setup('tsserver')
setup('denols')
setup('vuels')
setup('svelte')
setup('html')
setup('yamlls')
setup('jsonls', {
json = {
schemas = require('schemastore').json.schemas(),
validate = {
enable = true
}
}
})
setup('gopls', {
settings = {
gopls = { semanticTokens = true }
}
})
setup('phpactor')
setup('pylsp')
setup('solargraph') -- ruby
setup('rust_analyzer', {
settings = {
['rust-analyzer'] = {
checkOnSave = { extraArgs = { "--target-dir", "/tmp/rust-analyzer-check" } },
diagnostics = { disabled = { 'inactive-code' } },
},
}
})
setup('sqlls')
setup('salt_ls')
setup('ansiblels')
setup('dockerls')
setup('docker_compose_language_service')
setup('terraformls')
-- https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#denols
vim.g.markdown_fenced_languages = {
"ts=typescript"
}
-- Configure diagnostics
vim.diagnostic.config({
virtual_text = false,
signs = true,
underline = true,
update_in_insert = false,
severity_sort = false,
})
-- Change diagnostic symbols in the sign column (gutter)
local signs = { Error = "x ", Warn = "! ", Hint = "? ", Info = "i " }
for type, icon in pairs(signs) do
local hl = "DiagnosticSign" .. type
vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = hl })
end
EOF

View file

@ -1,32 +1,8 @@
" Happy yaml configuration
au! BufNewFile,BufReadPost *.{yaml,yml} set filetype=yaml
autocmd FileType yaml setlocal ts=2 sts=2 sw=2 expandtab
let g:gutentags_file_list_command = 'git ls-files' let g:gutentags_file_list_command = 'git ls-files'
" quick-scope
" https://github.com/unblevable/quick-scope " https://github.com/unblevable/quick-scope
let g:qs_highlight_on_keys = ['f', 'F', 't', 'T'] let g:qs_highlight_on_keys = ['f', 'F', 't', 'T']
" Golang
" Go test, Def, Decls shortcut
nmap <Leader>got :GoTest<CR>:botright copen<CR>
autocmd FileType go nmap gd :GoDef<CR>
autocmd FileType go nmap gD :GoDecls<CR>
" Go formatting
autocmd FileType go setlocal noexpandtab shiftwidth=4 tabstop=4 softtabstop=4 nolist
" Caddyfile indentation
autocmd FileType caddyfile setlocal noexpandtab shiftwidth=8 tabstop=8 softtabstop=8 nolist
" vim-go disable text-objects
let g:go_textobj_enabled = 0
" disable vim-go :GoDef short cut (gd)
" this is handled by LanguageClient [LC]
let g:go_def_mapping_enabled = 0
" GitGutter and vim Magit " GitGutter and vim Magit
" inspired by: https://jakobgm.com/posts/vim/git-integration/ " inspired by: https://jakobgm.com/posts/vim/git-integration/
" Don't map gitgutter keys automatically, set them ourselves " Don't map gitgutter keys automatically, set them ourselves
@ -43,17 +19,11 @@ nmap <Leader>gu <Plug>(GitGutterUndoHunk) " git undo (chunk)
" Open vimagit pane " Open vimagit pane
nnoremap <leader>gs :Magit<CR> " git status nnoremap <leader>gs :Magit<CR> " git status
" Push to remote
nnoremap <leader>gP :! git push<CR> " git Push
" Quick conflict resolution in git mergetool nvim " Quick conflict resolution in git mergetool nvim
" http://vimcasts.org/episodes/fugitive-vim-resolving-merge-conflicts-with-vimdiff/ " http://vimcasts.org/episodes/fugitive-vim-resolving-merge-conflicts-with-vimdiff/
nmap <Leader>[ :diffget //2<CR> nmap <Leader>[ :diffget //2<CR>
nmap <Leader>] :diffget //3<CR> nmap <Leader>] :diffget //3<CR>
" netrw
let g:netrw_fastbrowse=0
" Auto-FMT rust code on save " Auto-FMT rust code on save
let g:rustfmt_autosave = 1 let g:rustfmt_autosave = 1
@ -66,22 +36,51 @@ let g:highlightedyank_highlight_duration = 200
" Markdown options " Markdown options
let g:vim_markdown_folding_disabled = 1 let g:vim_markdown_folding_disabled = 1
" Haskell options
let g:haskell_enable_quantification = 1 " to enable highlighting of `forall`
let g:haskell_enable_recursivedo = 1 " to enable highlighting of `mdo` and `rec`
let g:haskell_enable_arrowsyntax = 1 " to enable highlighting of `proc`
let g:haskell_enable_pattern_synonyms = 1 " to enable highlighting of `pattern`
let g:haskell_enable_typeroles = 1 " to enable highlighting of type roles
let g:haskell_enable_static_pointers = 1 " to enable highlighting of `static`
let g:haskell_backpack = 1 " to enable highlighting of backpack keywords
" Emmet
let g:user_emmet_leader_key='<c-n>'
" Ack
if executable('ag')
let g:ackprg = 'ag --hidden --vimgrep'
endif
" nnn " nnn
let g:nnn#command = 'nnn -d -e -H -r' let g:nnn#command = 'nnn -d -e -H -r'
nmap - :NnnPicker %<CR>
nmap <leader>n :NnnPicker %<CR>
nmap <leader>N :NnnPicker<CR>
lua <<EOF
local actions = require("telescope.actions")
local telescope = require("telescope")
telescope.setup{
defaults = {
mappings = {
n = {
["k"] = actions.move_selection_next,
["i"] = actions.move_selection_previous,
["I"] = actions.move_to_top,
["K"] = actions.move_to_bottom,
["<C-c>"] = actions.close,
},
},
},
pickers = {
find_files = {
-- `hidden = true` will still show the inside of `.git/` as it's not `.gitignore`d.
find_command = { "rg", "--files", "--hidden", "--glob", "!**/.git/*" },
},
},
extensions = {
fzf = {
fuzzy = true, -- false will only do exact matching
override_generic_sorter = true, -- override the generic sorter
override_file_sorter = true, -- override the file sorter
case_mode = "smart_case", -- or "ignore_case" or "respect_case"
}
}
}
telescope.load_extension('fzf')
local builtin = require('telescope.builtin')
vim.keymap.set('n', '<leader>ff', builtin.find_files, {})
vim.keymap.set('n', '<leader>f/', builtin.live_grep, {})
vim.keymap.set('n', '<leader>f?', builtin.builtin, {})
vim.keymap.set('n', '<leader>fr', builtin.command_history, {})
vim.keymap.set('n', '<leader>fc', builtin.commands, {})
vim.keymap.set('n', '<leader>ft', builtin.treesitter, {})
EOF

View file

@ -1,36 +0,0 @@
self:
with self; ''
IFS=':' read -r -a INPUT <<< "$1"
FILE=''${INPUT[0]}
CENTER=''${INPUT[1]}
if [[ "$1" =~ ^[A-Za-z]:\\ ]]; then
FILE=$FILE:''${INPUT[1]}
CENTER=''${INPUT[2]}
fi
if [[ -n "$CENTER" && ! "$CENTER" =~ ^[0-9] ]]; then
exit 1
fi
CENTER=''${CENTER/[^0-9]*/}
FILE="''${FILE/#\~\//$HOME/}"
if [ ! -r "$FILE" ]; then
echo "File not found ''${FILE}"
exit 1
fi
if [ -z "$CENTER" ]; then
CENTER=0
fi
exec cat "$FILE" \
| sed -e '/[#|\/\/ ?]-- copyright/,/[#\/\/]++/c\\' \
| ${pkgs.coreutils}/bin/tr -s '\n' \
| ${pkgs.bat}/bin/bat \
--style="''${BAT_STYLE:-numbers}" \
--color=always \
--pager=never \
--file-name="''$FILE" \
--highlight-line=$CENTER
''

View file

@ -1,20 +0,0 @@
"Usage:
" 1. Perform a vimgrep search
" :vimgrep /def/ *.rb
" 2. Issue QuickFixOpenAll command
" :QuickFixOpenAll
function! QuickFixOpenAll()
if empty(getqflist())
return
endif
let s:prev_val = ""
for d in getqflist()
let s:curr_val = bufname(d.bufnr)
if (s:curr_val != s:prev_val)
exec "edit " . s:curr_val
endif
let s:prev_val = s:curr_val
endfor
endfunction
command! QuickFixOpenAll call QuickFixOpenAll()

View file

@ -13,20 +13,3 @@ let g:airline#extensions#tabline#fnamemod = ':t' " Show just the filename
let g:airline#extensions#tabline#formatter = 'unique_tail_improved' let g:airline#extensions#tabline#formatter = 'unique_tail_improved'
let g:airline_powerline_fonts = 1 " Use powerline fonts let g:airline_powerline_fonts = 1 " Use powerline fonts
let g:airline_theme = 'sonokai' let g:airline_theme = 'sonokai'
" Customize fzf colors to match your color scheme
" - fzf#wrap translates this to a set of `--color` options
let g:fzf_colors =
\ { 'fg': ['fg', 'Normal'],
\ 'bg': ['bg', 'Normal'],
\ 'hl': ['fg', 'Comment'],
\ 'fg+': ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
\ 'bg+': ['bg', 'CursorLine', 'CursorColumn'],
\ 'hl+': ['fg', 'Statement'],
\ 'info': ['fg', 'PreProc'],
\ 'border': ['fg', 'Ignore'],
\ 'prompt': ['fg', 'Conditional'],
\ 'pointer': ['fg', 'Exception'],
\ 'marker': ['fg', 'Keyword'],
\ 'spinner': ['fg', 'Label'],
\ 'header': ['fg', 'Comment'] }

View file

@ -7,98 +7,100 @@
}: let }: let
psCfg = config.pub-solar; psCfg = config.pub-solar;
in in
with lib; { with lib; {
imports = [ imports = [
./home.nix ./home.nix
];
options.pub-solar = {
user = {
name = mkOption {
description = "User login name";
type = types.nullOr types.str;
default = "nixos";
};
description = mkOption {
description = "User description";
type = types.nullOr types.str;
default = "The main PubSolarOS user";
};
password = mkOption {
description = "User password";
type = types.nullOr types.str;
default = null;
};
passwordlessSudo = mkOption {
description = "Whether this user can use sudo without entering a password";
type = types.bool;
default = false;
};
publicKeys = mkOption {
description = "User SSH public keys";
type = types.listOf types.str;
default = [];
};
fullName = mkOption {
description = "User full name";
type = types.nullOr types.str;
default = null;
};
email = mkOption {
description = "User email address";
type = types.nullOr types.str;
default = null;
};
gpgKeyId = mkOption {
description = "GPG Key ID";
type = types.nullOr types.str;
default = null;
};
};
};
config = {
users = {
mutableUsers = false;
users."${psCfg.user.name}" = {
# Indicates whether this is an account for a “real” user.
# This automatically sets group to users, createHome to true,
# home to /home/username, useDefaultShell to true, and isSystemUser to false.
isNormalUser = true;
description = psCfg.user.description;
extraGroups = [
"input"
"lp"
"networkmanager"
"scanner"
"video"
"dialout"
"wheel"
];
shell = pkgs.bash;
initialHashedPassword =
if psCfg.user.password != null
then psCfg.user.password
else "";
openssh.authorizedKeys.keys =
flake.self.publicKeys ++
(if psCfg.user.publicKeys != null
then psCfg.user.publicKeys
else []);
};
};
security.sudo.extraRules = mkIf psCfg.user.passwordlessSudo [
{
users = ["${psCfg.user.name}"];
commands = [
{
command = "ALL";
options = ["NOPASSWD"];
}
];
}
]; ];
};
} options.pub-solar = {
user = {
name = mkOption {
description = "User login name";
type = types.nullOr types.str;
default = "nixos";
};
description = mkOption {
description = "User description";
type = types.nullOr types.str;
default = "The main PubSolarOS user";
};
password = mkOption {
description = "User password";
type = types.nullOr types.str;
default = null;
};
passwordlessSudo = mkOption {
description = "Whether this user can use sudo without entering a password";
type = types.bool;
default = false;
};
publicKeys = mkOption {
description = "User SSH public keys";
type = types.listOf types.str;
default = [];
};
fullName = mkOption {
description = "User full name";
type = types.nullOr types.str;
default = null;
};
email = mkOption {
description = "User email address";
type = types.nullOr types.str;
default = null;
};
gpgKeyId = mkOption {
description = "GPG Key ID";
type = types.nullOr types.str;
default = null;
};
};
};
config = {
users = {
mutableUsers = false;
users."${psCfg.user.name}" = {
# Indicates whether this is an account for a “real” user.
# This automatically sets group to users, createHome to true,
# home to /home/username, useDefaultShell to true, and isSystemUser to false.
isNormalUser = true;
description = psCfg.user.description;
extraGroups = [
"input"
"lp"
"networkmanager"
"scanner"
"video"
"dialout"
"wheel"
];
shell = pkgs.bash;
initialHashedPassword =
if psCfg.user.password != null
then psCfg.user.password
else "";
openssh.authorizedKeys.keys =
flake.self.publicKeys
++ (
if psCfg.user.publicKeys != null
then psCfg.user.publicKeys
else []
);
};
};
security.sudo.extraRules = mkIf psCfg.user.passwordlessSudo [
{
users = ["${psCfg.user.name}"];
commands = [
{
command = "ALL";
options = ["NOPASSWD"];
}
];
}
];
};
}

View file

@ -1,4 +1,4 @@
{ ... }: { {...}: {
imports = [ imports = [
./private.nix ./private.nix
./tunnel.nix ./tunnel.nix

View file

@ -2,8 +2,7 @@
lib, lib,
config, config,
... ...
}: }: let
let
cfg = config.pub-solar.wireguard.ehex; cfg = config.pub-solar.wireguard.ehex;
in { in {
options.pub-solar.wireguard.ehex = { options.pub-solar.wireguard.ehex = {
@ -19,10 +18,10 @@ in {
}; };
}; };
config = lib.mkIf (lib.length cfg.ownIPs != 0){ config = lib.mkIf (lib.length cfg.ownIPs != 0) {
networking.firewall.allowedUDPPorts = [51822]; networking.firewall.allowedUDPPorts = [51822];
systemd.network.wait-online.ignoredInterfaces = [ "wg-ehex" ]; systemd.network.wait-online.ignoredInterfaces = ["wg-ehex"];
systemd.services.wireguard-wg-ehex = import ./service-override.nix lib; systemd.services.wireguard-wg-ehex = import ./service-override.nix lib;
@ -42,8 +41,8 @@ in {
{ {
endpoint = "vpn-gateway.ehex.de:4242"; endpoint = "vpn-gateway.ehex.de:4242";
publicKey = "Fsg4KEyDEvQEt/1cVWU9xa/k9x/3UhONDj61aXZ7tys="; publicKey = "Fsg4KEyDEvQEt/1cVWU9xa/k9x/3UhONDj61aXZ7tys=";
presharedKey = "tQy7B5R3wOgWwIKFDcEr4WZIqCrwG+9UgPRIQx/5xso="; presharedKey = "tQy7B5R3wOgWwIKFDcEr4WZIqCrwG+9UgPRIQx/5xso=";
allowedIPs = [ "10.42.0.0/22" "10.0.66.0/24" ]; allowedIPs = ["10.42.0.0/22" "10.0.66.0/24"];
persistentKeepalive = 15; persistentKeepalive = 15;
dynamicEndpointRefreshSeconds = 30; dynamicEndpointRefreshSeconds = 30;
} }

View file

@ -3,8 +3,7 @@
config, config,
pkgs, pkgs,
... ...
}: }: let
let
cfg = config.pub-solar.wireguard.private; cfg = config.pub-solar.wireguard.private;
in { in {
options.pub-solar.wireguard.private = { options.pub-solar.wireguard.private = {
@ -43,7 +42,7 @@ in {
config = lib.mkIf (builtins.length cfg.ownIPs != 0) { config = lib.mkIf (builtins.length cfg.ownIPs != 0) {
networking.firewall.allowedUDPPorts = [51899]; networking.firewall.allowedUDPPorts = [51899];
systemd.network.wait-online.ignoredInterfaces = [ "wg-private" ]; systemd.network.wait-online.ignoredInterfaces = ["wg-private"];
systemd.services.wireguard-wg-private = import ./service-override.nix lib; systemd.services.wireguard-wg-private = import ./service-override.nix lib;
@ -53,21 +52,30 @@ in {
mtu = 1300; mtu = 1300;
ips = cfg.ownIPs; ips = cfg.ownIPs;
privateKeyFile = cfg.privateKeyFile; privateKeyFile = cfg.privateKeyFile;
postSetup = "" postSetup =
+ (if cfg.useDNS then '' ""
printf "nameserver 10.13.12.7\nnameserver fd00:b12f:acab:1312:acab:7::" | resolvconf -a wg-private -m 0 -x + (
'' else "") if cfg.useDNS
+ (if cfg.fullTunnel then '' then ''
defaultRoute=$(${pkgs.iproute2}/bin/ip r | ${pkgs.gnugrep}/bin/grep "default via" | head -n 1 | ${pkgs.gawk}/bin/awk '{ print $3 " " $4 " " $5 }') printf "nameserver 10.13.12.7\nnameserver fd00:b12f:acab:1312:acab:7::" | resolvconf -a wg-private -m 0 -x
ipv4=$(${pkgs.dnsutils}/bin/dig +short A vpn.b12f.io) ''
${pkgs.iproute2}/bin/ip route add $ipv4 metric 256 via $defaultRoute else ""
ipv6=$(${pkgs.dnsutils}/bin/dig +short AAAA vpn.b12f.io) )
${pkgs.iproute2}/bin/ip route add $ipv6 metric 256 via $defaultRoute + (
ip -4 route delete default dev wg-private || true if cfg.fullTunnel
ip -4 route replace default dev wg-private metric 512 then ''
ip -6 route delete default dev wg-private || true defaultRoute=$(${pkgs.iproute2}/bin/ip r | ${pkgs.gnugrep}/bin/grep "default via" | head -n 1 | ${pkgs.gawk}/bin/awk '{ print $3 " " $4 " " $5 }')
ip -6 route replace default dev wg-private metric 512 ipv4=$(${pkgs.dnsutils}/bin/dig +short A vpn.b12f.io)
'' else ""); ${pkgs.iproute2}/bin/ip route add $ipv4 metric 256 via $defaultRoute
ipv6=$(${pkgs.dnsutils}/bin/dig +short AAAA vpn.b12f.io)
${pkgs.iproute2}/bin/ip route add $ipv6 metric 256 via $defaultRoute
ip -4 route delete default dev wg-private || true
ip -4 route replace default dev wg-private metric 512
ip -6 route delete default dev wg-private || true
ip -6 route replace default dev wg-private metric 512
''
else ""
);
postShutdown = lib.mkIf cfg.useDNS '' postShutdown = lib.mkIf cfg.useDNS ''
resolvconf -d wg-private -f resolvconf -d wg-private -f
''; '';
@ -75,13 +83,19 @@ in {
{ {
# frikandel # frikandel
publicKey = "p6YKNYBlySKfhTN+wbSsKdoNjzko/XSAiTAlCJzP1jA="; publicKey = "p6YKNYBlySKfhTN+wbSsKdoNjzko/XSAiTAlCJzP1jA=";
allowedIPs = [ allowedIPs =
"10.13.12.0/24" [
"fd00:b12f:acab:1312::/64" "10.13.12.0/24"
] ++ (if cfg.fullTunnel then [ "fd00:b12f:acab:1312::/64"
"0.0.0.0/0" ]
"::/0" ++ (
] else []); if cfg.fullTunnel
then [
"0.0.0.0/0"
"::/0"
]
else []
);
endpoint = "vpn.b12f.io:51899"; endpoint = "vpn.b12f.io:51899";
dynamicEndpointRefreshSeconds = 30; dynamicEndpointRefreshSeconds = 30;
} }

View file

@ -2,8 +2,7 @@
lib, lib,
config, config,
... ...
}: }: let
let
cfg = config.pub-solar.wireguard.pub-solar; cfg = config.pub-solar.wireguard.pub-solar;
in { in {
options.pub-solar.wireguard.pub-solar = { options.pub-solar.wireguard.pub-solar = {
@ -19,10 +18,10 @@ in {
}; };
}; };
config = lib.mkIf (lib.length cfg.ownIPs != 0){ config = lib.mkIf (lib.length cfg.ownIPs != 0) {
networking.firewall.allowedUDPPorts = [51821]; networking.firewall.allowedUDPPorts = [51821];
systemd.network.wait-online.ignoredInterfaces = [ "wg-pub-solar" ]; systemd.network.wait-online.ignoredInterfaces = ["wg-pub-solar"];
systemd.services.wireguard-wg-pub-solar = import ./service-override.nix lib; systemd.services.wireguard-wg-pub-solar = import ./service-override.nix lib;
@ -33,28 +32,78 @@ in {
ips = cfg.ownIPs; ips = cfg.ownIPs;
privateKeyFile = cfg.privateKeyFile; privateKeyFile = cfg.privateKeyFile;
peers = [ peers = [
{
endpoint = "flora-6.pub.solar:51820";
publicKey = "jtSR5G2P/nm9s8WrVc26Xc/SQLupRxyXE+5eIeqlsTU=";
allowedIPs = [ "10.7.6.2/32" "fd00:fae:fae:fae:fae:2::/96" ];
persistentKeepalive = 15;
dynamicEndpointRefreshSeconds = 30;
}
{ {
endpoint = "nachtigall.pub.solar:51820"; endpoint = "nachtigall.pub.solar:51820";
publicKey = "qzNywKY9RvqTnDO8eLik75/SHveaSk9OObilDzv+xkk="; publicKey = "qzNywKY9RvqTnDO8eLik75/SHveaSk9OObilDzv+xkk=";
allowedIPs = [ "10.7.6.1/32" "fd00:fae:fae:fae:fae:1::/96" ]; allowedIPs = [
"10.7.6.1/32"
"fd00:fae:fae:fae:fae:1::/96"
];
persistentKeepalive = 15; persistentKeepalive = 15;
dynamicEndpointRefreshSeconds = 30; dynamicEndpointRefreshSeconds = 30;
} }
{ {
endpoint = "tankstelle.pub.solar:51820"; endpoint = "flora-6.pub.solar:51820";
publicKey = "jtSR5G2P/nm9s8WrVc26Xc/SQLupRxyXE+5eIeqlsTU=";
allowedIPs = [
"10.7.6.2/32"
"fd00:fae:fae:fae:fae:2::/96"
];
persistentKeepalive = 15;
dynamicEndpointRefreshSeconds = 30;
}
{
# metronom.pub.solar
endpoint = "49.13.236.167:51820";
publicKey = "zOSYGO7MfnOOUnzaTcWiKRQM0qqxR3JQrwx/gtEtHmo=";
allowedIPs = [
"10.7.6.3/32"
"fd00:fae:fae:fae:fae:3::/96"
];
persistentKeepalive = 15;
}
{
# tankstelle.pub.solar
endpoint = "80.244.242.5:51820";
publicKey = "iRTlY1lB7nPXf2eXzX8ZZDkfMmXyGjff5/joccbP8Cg="; publicKey = "iRTlY1lB7nPXf2eXzX8ZZDkfMmXyGjff5/joccbP8Cg=";
allowedIPs = [ allowedIPs = [
"10.7.6.4/32" "10.7.6.4/32"
"fd00:fae:fae:fae:fae:4::/96" "fd00:fae:fae:fae:fae:4::/96"
]; ];
} }
{
# trinkgenossin.pub.solar
publicKey = "QWgHovHxtqiQhnHLouSWiT6GIoQDmuvnThYL5c/rvU4=";
allowedIPs = [
"10.7.6.5/32"
"fd00:fae:fae:fae:fae:5::/96"
];
#endpoint = "80.244.242.5:51820";
endpoint = "[2a01:239:35d:f500::1]:51820";
persistentKeepalive = 15;
}
{
# delite.pub.solar
publicKey = "ZT2qGWgMPwHRUOZmTQHWCRX4m14YwOsiszjsA5bpc2k=";
allowedIPs = [
"10.7.6.6/32"
"fd00:fae:fae:fae:fae:6::/96"
];
#endpoint = "80.244.242.5:51820";
endpoint = "[2a04:52c0:124:9d8c::2]:51820";
persistentKeepalive = 15;
}
{
# blue-shell.pub.solar
publicKey = "bcrIpWrKc1M+Hq4ds3aN1lTaKE26f2rvXhd+93QrzR8=";
allowedIPs = [
"10.7.6.7/32"
"fd00:fae:fae:fae:fae:7::/96"
];
#endpoint = "80.244.242.5:51820";
endpoint = "[2a03:4000:43:24e::1]:51820";
persistentKeepalive = 15;
}
]; ];
}; };
}; };

View file

@ -1,17 +1,17 @@
lib: { lib: {
wantedBy = [ wantedBy = [
"network.target" "network.target"
"network-online.target" "network-online.target"
"nss-lookup.target" "nss-lookup.target"
]; ];
serviceConfig = { serviceConfig = {
Type = lib.mkForce "simple"; Type = lib.mkForce "simple";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "15"; RestartSec = "15";
}; };
environment = { environment = {
WG_ENDPOINT_RESOLUTION_RETRIES = "infinity"; WG_ENDPOINT_RESOLUTION_RETRIES = "infinity";
}; };
} }

View file

@ -3,8 +3,7 @@
config, config,
pkgs, pkgs,
... ...
}: }: let
let
cfg = config.pub-solar.wireguard.tunnel; cfg = config.pub-solar.wireguard.tunnel;
in { in {
options.pub-solar.wireguard.tunnel = { options.pub-solar.wireguard.tunnel = {
@ -37,10 +36,10 @@ in {
}; };
}; };
config = lib.mkIf (lib.length cfg.ownIPs != 0){ config = lib.mkIf (lib.length cfg.ownIPs != 0) {
networking.firewall.allowedUDPPorts = [51820]; networking.firewall.allowedUDPPorts = [51820];
systemd.network.wait-online.ignoredInterfaces = [ "wg-tunnel" ]; systemd.network.wait-online.ignoredInterfaces = ["wg-tunnel"];
systemd.targets.wireguard-wg-tunnel = { systemd.targets.wireguard-wg-tunnel = {
wantedBy = lib.mkForce []; wantedBy = lib.mkForce [];
@ -73,11 +72,22 @@ in {
}; };
networking.wireguard.interfaces = let networking.wireguard.interfaces = let
splitEndpoint = (lib.strings.splitString ":" cfg.peer.endpoint); splitEndpoint = lib.strings.splitString ":" cfg.peer.endpoint;
joinIPV6 = p: ip: p + (if (lib.stringLength ip > 0) then ":" else "") + ip; joinIPV6 = p: ip:
p
+ (
if (lib.stringLength ip > 0)
then ":"
else ""
)
+ ip;
isIPV4 = lib.length splitEndpoint < 3; isIPV4 = lib.length splitEndpoint < 3;
ipFlag = if isIPV4 then "-4" else "-6"; ipFlag =
endpointIP = (if isIPV4 if isIPV4
then "-4"
else "-6";
endpointIP = (
if isIPV4
then lib.elemAt splitEndpoint 0 then lib.elemAt splitEndpoint 0
else lib.lists.fold joinIPV6 "" ((lib.lists.take ((lib.length splitEndpoint) - 1)) splitEndpoint) else lib.lists.fold joinIPV6 "" ((lib.lists.take ((lib.length splitEndpoint) - 1)) splitEndpoint)
); );
@ -87,24 +97,36 @@ in {
listenPort = 51820; listenPort = 51820;
ips = cfg.ownIPs; ips = cfg.ownIPs;
privateKeyFile = cfg.privateKeyFile; privateKeyFile = cfg.privateKeyFile;
postSetup = '' postSetup =
defaultRoute=$(${pkgs.iproute2}/bin/ip ${ipFlag} r | ${pkgs.gnugrep}/bin/grep "default via" | head -n 1 | ${pkgs.gawk}/bin/awk '{ print $3 " " $4 " " $5 }') ''
${pkgs.iproute2}/bin/ip ${ipFlag} route add "${endpointIPStripped}${if isIPV4 then "/32" else "/128"}" metric 256 via $defaultRoute defaultRoute=$(${pkgs.iproute2}/bin/ip ${ipFlag} r | ${pkgs.gnugrep}/bin/grep "default via" | head -n 1 | ${pkgs.gawk}/bin/awk '{ print $3 " " $4 " " $5 }')
ip -4 route delete default dev wg-tunnel || true ${pkgs.iproute2}/bin/ip ${ipFlag} route add "${endpointIPStripped}${
ip -4 route add default dev wg-tunnel metric 512 if isIPV4
ip -6 route delete default dev wg-tunnel || true then "/32"
ip -6 route add default dev wg-tunnel metric 512 else "/128"
'' + (if cfg.useDNS }" metric 256 via $defaultRoute
then ''printf "nameserver 10.64.0.1" | resolvconf -a wg-tunnel -m 0 -x'' ip -4 route delete default dev wg-tunnel || true
else ""); ip -4 route add default dev wg-tunnel metric 512
postShutdown = '' ip -6 route delete default dev wg-tunnel || true
addedRoute=$(${pkgs.iproute2}/bin/ip ${ipFlag} r | ${pkgs.gnugrep}/bin/grep "${endpointIPStripped}" | head -n 1 | ${pkgs.gawk}/bin/awk '{ print $1 " " $2 " " $3 " " $4 " " $5 }') ip -6 route add default dev wg-tunnel metric 512
if [ -n "$addedRoute" ]; then ''
${pkgs.iproute2}/bin/ip ${ipFlag} route delete $addedRoute + (
fi if cfg.useDNS
'' + (if cfg.useDNS then ''printf "nameserver 10.64.0.1" | resolvconf -a wg-tunnel -m 0 -x''
then ''resolvconf -d wg-tunnel -f'' else ""
else ""); );
postShutdown =
''
addedRoute=$(${pkgs.iproute2}/bin/ip ${ipFlag} r | ${pkgs.gnugrep}/bin/grep "${endpointIPStripped}" | head -n 1 | ${pkgs.gawk}/bin/awk '{ print $1 " " $2 " " $3 " " $4 " " $5 }')
if [ -n "$addedRoute" ]; then
${pkgs.iproute2}/bin/ip ${ipFlag} route delete $addedRoute
fi
''
+ (
if cfg.useDNS
then ''resolvconf -d wg-tunnel -f''
else ""
);
peers = [ peers = [
{ {
publicKey = cfg.peer.publicKey; publicKey = cfg.peer.publicKey;

View file

@ -12,6 +12,6 @@ in {
users.users."${psCfg.user.name}" = { users.users."${psCfg.user.name}" = {
extraGroups = ["wireshark"]; extraGroups = ["wireshark"];
packages = [ pkgs.wireshark ]; packages = [pkgs.wireshark];
}; };
} }

View file

@ -4,22 +4,24 @@
... ...
}: { }: {
flake = { flake = {
nixosModules = rec { nixosModules = {
overlays = ({ ... }: { overlays = {...}: {
nixpkgs.overlays = [ nixpkgs.overlays = [
inputs.deno2nix.overlays.default inputs.deno2nix.overlays.default
inputs.nixd.overlays.default inputs.nixd.overlays.default
inputs.invoiceplane-template.overlays.default inputs.invoiceplane-template.overlays.default
inputs.mezza-biz.overlays.default
(final: prev: let (final: prev: let
unstable = import inputs.nixpkgs-unstable { system = prev.system; }; unstable = import inputs.nixpkgs-unstable {system = prev.system;};
master = import inputs.nixpkgs-master { system = prev.system; }; master = import inputs.nixpkgs-master {system = prev.system;};
authelia-438 = import inputs.authelia-438 { system = prev.system; }; authelia-438 = import inputs.authelia-438 {system = prev.system;};
in { in {
factorio-headless = master.factorio-headless; factorio-headless = master.factorio-headless;
paperless-ngx = unstable.paperless-ngx; paperless-ngx = unstable.paperless-ngx;
waybar = master.waybar; waybar = master.waybar;
nix-inspect = unstable.nix-inspect; nix-inspect = unstable.nix-inspect;
nix = unstable.lix;
authelia = authelia-438.authelia; authelia = authelia-438.authelia;
adlist = inputs.adblock-unbound.packages.${prev.system}; adlist = inputs.adblock-unbound.packages.${prev.system};
@ -43,7 +45,7 @@
(import ./element-desktop.nix) (import ./element-desktop.nix)
(import ../pkgs) (import ../pkgs)
(import ./blesh.nix) # (import ./blesh.nix)
(import ./rnix-lsp.nix) (import ./rnix-lsp.nix)
(import ./neovim-plugins.nix) (import ./neovim-plugins.nix)
@ -59,7 +61,7 @@
}; };
}) })
]; ];
}); };
}; };
}; };
} }

View file

@ -1,3 +1,3 @@
final: prev: { final: prev: {
element-desktop = prev.element-desktop.override { electron = prev.electron_28; }; element-desktop = prev.element-desktop.override {electron = prev.electron_28;};
} }

View file

@ -20,7 +20,7 @@
}, },
"blesh-nvfetcher": { "blesh-nvfetcher": {
"cargoLocks": null, "cargoLocks": null,
"date": "2024-03-11", "date": "2024-08-15",
"extract": null, "extract": null,
"name": "blesh-nvfetcher", "name": "blesh-nvfetcher",
"passthru": null, "passthru": null,
@ -32,11 +32,11 @@
"name": null, "name": null,
"owner": "akinomyoga", "owner": "akinomyoga",
"repo": "ble.sh", "repo": "ble.sh",
"rev": "b6344b3be1978695889371de83ac4d15352e4fee", "rev": "75c4a8483e506ec130054dd61273b4ef72aabd4d",
"sha256": "sha256-mKqvbwLW71NBeuP5Cqsp/dmrbodzAmFI3HYN5v07cNg=", "sha256": "sha256-wQRDnc8EM6M7gSaVy/YpjElVn2RLJFTiIbg6itefciU=",
"type": "github" "type": "github"
}, },
"version": "b6344b3be1978695889371de83ac4d15352e4fee" "version": "75c4a8483e506ec130054dd61273b4ef72aabd4d"
}, },
"instant-nvim-nvfetcher": { "instant-nvim-nvfetcher": {
"cargoLocks": null, "cargoLocks": null,
@ -80,7 +80,7 @@
}, },
"vim-apprentice-nvfetcher": { "vim-apprentice-nvfetcher": {
"cargoLocks": null, "cargoLocks": null,
"date": "2023-02-15", "date": "2024-07-30",
"extract": null, "extract": null,
"name": "vim-apprentice-nvfetcher", "name": "vim-apprentice-nvfetcher",
"passthru": null, "passthru": null,
@ -92,11 +92,11 @@
"name": null, "name": null,
"owner": "romainl", "owner": "romainl",
"repo": "Apprentice", "repo": "Apprentice",
"rev": "59ad13661fa15edaf72c62218903c7817b5a3691", "rev": "cb051ec95d12fa34169bb384142b8e50e328cd97",
"sha256": "sha256-03B9tmU9+6t2hxhOgZxBqJr9r41CAqhHLUkHYvFdcks=", "sha256": "sha256-rMaF8jeMA5DDTDAAUuCXTQrQcb9Ce8wMiFxUyQZMz6Q=",
"type": "github" "type": "github"
}, },
"version": "59ad13661fa15edaf72c62218903c7817b5a3691" "version": "cb051ec95d12fa34169bb384142b8e50e328cd97"
}, },
"vim-beautify-nvfetcher": { "vim-beautify-nvfetcher": {
"cargoLocks": null, "cargoLocks": null,
@ -160,7 +160,7 @@
}, },
"vimagit-nvfetcher": { "vimagit-nvfetcher": {
"cargoLocks": null, "cargoLocks": null,
"date": "2024-01-04", "date": "2024-03-28",
"extract": null, "extract": null,
"name": "vimagit-nvfetcher", "name": "vimagit-nvfetcher",
"passthru": null, "passthru": null,
@ -172,10 +172,10 @@
"name": null, "name": null,
"owner": "jreybert", "owner": "jreybert",
"repo": "vimagit", "repo": "vimagit",
"rev": "06afe48439d0118a77d622ef06eff0f7cd7d62ab", "rev": "fc7eda97da4f8182c8abbe6ea7befbd789b8b935",
"sha256": "sha256-2kugFr32lZINgpmDyfTyBp5lNa2/dculKmcFGa2q/io=", "sha256": "sha256-HievBzyVZke4AyCWAL9MlOw65X460cEEeOhwAL2brzs=",
"type": "github" "type": "github"
}, },
"version": "06afe48439d0118a77d622ef06eff0f7cd7d62ab" "version": "fc7eda97da4f8182c8abbe6ea7befbd789b8b935"
} }
} }

View file

@ -1,6 +1,10 @@
# This file was generated by nvfetcher, please do not modify it manually. # This file was generated by nvfetcher, please do not modify it manually.
{ fetchgit, fetchurl, fetchFromGitHub, dockerTools }:
{ {
fetchgit,
fetchurl,
fetchFromGitHub,
dockerTools,
}: {
b12f-io-nvfetcher = { b12f-io-nvfetcher = {
pname = "b12f-io-nvfetcher"; pname = "b12f-io-nvfetcher";
version = "38adb94ce69d8807ea2e36f57abe08091192b31c"; version = "38adb94ce69d8807ea2e36f57abe08091192b31c";
@ -16,17 +20,17 @@
}; };
blesh-nvfetcher = { blesh-nvfetcher = {
pname = "blesh-nvfetcher"; pname = "blesh-nvfetcher";
version = "b6344b3be1978695889371de83ac4d15352e4fee"; version = "75c4a8483e506ec130054dd61273b4ef72aabd4d";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "akinomyoga"; owner = "akinomyoga";
repo = "ble.sh"; repo = "ble.sh";
rev = "b6344b3be1978695889371de83ac4d15352e4fee"; rev = "75c4a8483e506ec130054dd61273b4ef72aabd4d";
fetchSubmodules = true; fetchSubmodules = true;
deepClone = false; deepClone = false;
leaveDotGit = true; leaveDotGit = true;
sha256 = "sha256-mKqvbwLW71NBeuP5Cqsp/dmrbodzAmFI3HYN5v07cNg="; sha256 = "sha256-wQRDnc8EM6M7gSaVy/YpjElVn2RLJFTiIbg6itefciU=";
}; };
date = "2024-03-11"; date = "2024-08-15";
}; };
instant-nvim-nvfetcher = { instant-nvim-nvfetcher = {
pname = "instant-nvim-nvfetcher"; pname = "instant-nvim-nvfetcher";
@ -54,15 +58,15 @@
}; };
vim-apprentice-nvfetcher = { vim-apprentice-nvfetcher = {
pname = "vim-apprentice-nvfetcher"; pname = "vim-apprentice-nvfetcher";
version = "59ad13661fa15edaf72c62218903c7817b5a3691"; version = "cb051ec95d12fa34169bb384142b8e50e328cd97";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "romainl"; owner = "romainl";
repo = "Apprentice"; repo = "Apprentice";
rev = "59ad13661fa15edaf72c62218903c7817b5a3691"; rev = "cb051ec95d12fa34169bb384142b8e50e328cd97";
fetchSubmodules = false; fetchSubmodules = false;
sha256 = "sha256-03B9tmU9+6t2hxhOgZxBqJr9r41CAqhHLUkHYvFdcks="; sha256 = "sha256-rMaF8jeMA5DDTDAAUuCXTQrQcb9Ce8wMiFxUyQZMz6Q=";
}; };
date = "2023-02-15"; date = "2024-07-30";
}; };
vim-beautify-nvfetcher = { vim-beautify-nvfetcher = {
pname = "vim-beautify-nvfetcher"; pname = "vim-beautify-nvfetcher";
@ -102,14 +106,14 @@
}; };
vimagit-nvfetcher = { vimagit-nvfetcher = {
pname = "vimagit-nvfetcher"; pname = "vimagit-nvfetcher";
version = "06afe48439d0118a77d622ef06eff0f7cd7d62ab"; version = "fc7eda97da4f8182c8abbe6ea7befbd789b8b935";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "jreybert"; owner = "jreybert";
repo = "vimagit"; repo = "vimagit";
rev = "06afe48439d0118a77d622ef06eff0f7cd7d62ab"; rev = "fc7eda97da4f8182c8abbe6ea7befbd789b8b935";
fetchSubmodules = false; fetchSubmodules = false;
sha256 = "sha256-2kugFr32lZINgpmDyfTyBp5lNa2/dculKmcFGa2q/io="; sha256 = "sha256-HievBzyVZke4AyCWAL9MlOw65X460cEEeOhwAL2brzs=";
}; };
date = "2024-01-04"; date = "2024-03-28";
}; };
} }

View file

@ -1,13 +1,13 @@
self: self:
with self; '' with self; ''
status=$(cat /sys/class/power_supply/BAT0/status) status=$(cat /sys/class/power_supply/BAT0/status)
if [ $status != "Discharging" ]; then if [ $status != "Discharging" ]; then
exit 0 exit 0
fi fi
capacity=$(cat /sys/class/power_supply/BAT0/capacity) capacity=$(cat /sys/class/power_supply/BAT0/capacity)
if [ $capacity -lt 20 ]; then if [ $capacity -lt 20 ]; then
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus
${libnotify}/bin/notify-send -u critical "Battery ''${capacity}%" ${libnotify}/bin/notify-send -u critical "Battery ''${capacity}%"
fi fi
'' ''

View file

@ -5,8 +5,14 @@ with prev; {
# then, call packages with `final.callPackage` # then, call packages with `final.callPackage`
check-battery = writeShellScriptBin "check-battery" (import ./check-battery.nix final); check-battery = writeShellScriptBin "check-battery" (import ./check-battery.nix final);
concourse = import ./concourse.nix final; concourse = import ./concourse.nix final;
element-b12f = writeShellScriptBin "element-b12f" (import ./element-desktop.nix { inherit final; profile = "b12f"; }); element-b12f = writeShellScriptBin "element-b12f" (import ./element-desktop.nix {
element-mezza = writeShellScriptBin "element-mezza" (import ./element-desktop.nix { inherit final; profile = "mezza"; }); inherit final;
profile = "b12f";
});
element-mezza = writeShellScriptBin "element-mezza" (import ./element-desktop.nix {
inherit final;
profile = "mezza";
});
fetch-hostingde-invoices = import ./fetch-hostingde-invoices final; fetch-hostingde-invoices = import ./fetch-hostingde-invoices final;
import-gtk-settings = writeShellScriptBin "import-gtk-settings" (import ./import-gtk-settings.nix final); import-gtk-settings = writeShellScriptBin "import-gtk-settings" (import ./import-gtk-settings.nix final);
lgcl = writeShellScriptBin "lgcl" (import ./lgcl.nix final); lgcl = writeShellScriptBin "lgcl" (import ./lgcl.nix final);

View file

@ -1,4 +1,7 @@
{ final, profile }: {
final,
profile,
}:
with final; '' with final; ''
${element-desktop}/bin/element-desktop --profile=${profile} ${element-desktop}/bin/element-desktop --profile=${profile}
'' ''

View file

@ -1,4 +1,5 @@
self: self.deno2nix.mkExecutable { self:
self.deno2nix.mkExecutable {
pname = "fetch-hostingde-invoices"; pname = "fetch-hostingde-invoices";
version = "0.1.0"; version = "0.1.0";

View file

@ -1,5 +1,4 @@
{ lib, ... }: {lib, ...}: {
{
flake = { flake = {
publicKeys = [ publicKeys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCmXpOU6vzQiVSSYCoxHYv7wDxC63Qg3dxlAMR6AOzwIABCU5PFFNcO0NWYms/YR7MOViorl+19LCLRABar9JgHU1n+uqxKV6eGph3OPeMp5sN8LAh7C9N+TZj8iJzBxQ3ch+Z/LdmLRwYNJ7KSUI+gwGK6xRS3+z1022Y4P0G0sx7IeCBl4lealQEIIF10ZOfjUdBcLQar7XTc5AxyGKnHCerXHRtccCoadLQujk0AvPXbv3Ma4JwX9X++AnCWRWakqS5UInu2tGuZ/6Hrjd2a9AKWjTaBVDcbYqCvY4XVuMj2/A2bCceFBaoi41apybSk26FSFTU4qiEUNQ6lxeOwG4+1NCXyHe2bGI4VyoxinDYa8vLLzXIRfTRA0qoGfCweXNeWPf0jMqASkUKaSOH5Ot7O5ps34r0j9pWzavDid8QeKJPyhxKuF1a5G4iBEZ0O9vuti60dPSjJPci9oTxbune2/jb7Sa0yO06DtLFJ2ncr5f70s/BDxKk4XIwQLy+KsvzlQEGdY8yA6xv28bOGxL3sQ0HE2pDTsvIbAisVOKzdJeolStL9MM5W8Hg0r/KkGj2bg0TfoRp1xHV9hjKkvJrsQ6okaPvNFeZq0HXzPhWMOVQ+/46z80uaQ1ByRLr3FTwuWJ7F/73ndfxiq6bDE4z2Ji0vOjeWJm6HCxTdGw== id_bbcom" "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCmXpOU6vzQiVSSYCoxHYv7wDxC63Qg3dxlAMR6AOzwIABCU5PFFNcO0NWYms/YR7MOViorl+19LCLRABar9JgHU1n+uqxKV6eGph3OPeMp5sN8LAh7C9N+TZj8iJzBxQ3ch+Z/LdmLRwYNJ7KSUI+gwGK6xRS3+z1022Y4P0G0sx7IeCBl4lealQEIIF10ZOfjUdBcLQar7XTc5AxyGKnHCerXHRtccCoadLQujk0AvPXbv3Ma4JwX9X++AnCWRWakqS5UInu2tGuZ/6Hrjd2a9AKWjTaBVDcbYqCvY4XVuMj2/A2bCceFBaoi41apybSk26FSFTU4qiEUNQ6lxeOwG4+1NCXyHe2bGI4VyoxinDYa8vLLzXIRfTRA0qoGfCweXNeWPf0jMqASkUKaSOH5Ot7O5ps34r0j9pWzavDid8QeKJPyhxKuF1a5G4iBEZ0O9vuti60dPSjJPci9oTxbune2/jb7Sa0yO06DtLFJ2ncr5f70s/BDxKk4XIwQLy+KsvzlQEGdY8yA6xv28bOGxL3sQ0HE2pDTsvIbAisVOKzdJeolStL9MM5W8Hg0r/KkGj2bg0TfoRp1xHV9hjKkvJrsQ6okaPvNFeZq0HXzPhWMOVQ+/46z80uaQ1ByRLr3FTwuWJ7F/73ndfxiq6bDE4z2Ji0vOjeWJm6HCxTdGw== id_bbcom"

View file

@ -1,7 +1 @@
# Serial: 25473464, Slot: 1
# Name: age identity bd1ccf37
# Created: Fri, 02 Feb 2024 19:26:49 +0000
# PIN policy: Once (A PIN is required once per session, if set)
# Touch policy: Cached (A physical touch is required for decryption, and is cached for 15 seconds)
# Recipient: age1yubikey1qd7szmr9ux2znl4x4hzykkwaru60nr4ufu6kdd88sm7657gjz4x5w0jy4y7
AGE-PLUGIN-YUBIKEY-1HZCCGQVZH5WV7DCL6V837 AGE-PLUGIN-YUBIKEY-1HZCCGQVZH5WV7DCL6V837

View file

@ -1,7 +1 @@
# Serial: 25473485, Slot: 1
# Name: age identity ceaabf8b
# Created: Fri, 02 Feb 2024 19:28:33 +0000
# PIN policy: Once (A PIN is required once per session, if set)
# Touch policy: Cached (A physical touch is required for decryption, and is cached for 15 seconds)
# Recipient: age1yubikey1qgxuu2x3uzw7k5pg5sp2dv43edhwdz3xuhj7kjqrnw0p8t0l67c5yz9nm6q
AGE-PLUGIN-YUBIKEY-1EKCCGQVZE64TLZCKYUCW7 AGE-PLUGIN-YUBIKEY-1EKCCGQVZE64TLZCKYUCW7

Binary file not shown.

View file

@ -0,0 +1,23 @@
age-encryption.org/v1
-> ssh-ed25519 8bHz7g B8CppVVWblUzZYe4KLZZQg1+Z9HtOZE2riG5rrj7lDc
BBNd3OpQz+QoPp6mv+P2+eYTMwKt8+ty4ERdO5+2Xtk
-> ssh-ed25519 n71/yQ 4cDMfD1yorzkNgdqrbmcI6FCDEWlFlZmdedD5O5x/3k
gvmvNFiPVGZdcIb6PacTn3IKEBEk0TnSaWv30XWX2rY
-> ssh-rsa kFDS0A
D/Wxbu8XMyCpYi3b58FKYrYlSog0yCTDV0+cKQssOPyc/NNQ39FviB6HcqahmZfi
HpXAXdgDBNwHBN+Gmcu4gSFSgogKG3U8UxGmY9kNUUbJ8mKnljGO2rdPPIEbMLEn
ZmUAK86RYOW4ctRceZ5APR24uLN5DpTnq5phLJgWjh9pvUXrI4SPawkMOq7CxylB
h2AOYXPso0Iz9SVHl/KRLV+w32US8ISlLzJSUSAMYBY/2uQd2TRDJGdw5Jz/Ih+q
f/G463YV6opFmYO9odxWPQzuEPmEBKSO7zThXnlCvsW6LDZlJ1IY0SZviPIhO4M8
RX4jsganUDti19RmiHytDXwKkM4XPCPh5wpE/a6qTVneFhnlXUNiF0Y938dAAMNx
S1rjS2v5ezHHtofpZqspl1s3WiAmsPzb7+E10ymoyT3elvWehWkTTk8a+HP4SoM+
QKiig8HaevLWS5Ea/8wO8h8lzEDtda65GBvlARQGTCCPyijwHBAfiivU6Xp2EJQr
YP3+hxbLO1wmV8QMxUfMrAfbJVhua+o5oDPZSImNwGfEQo4yztL2jit0bOuA3qDF
6S3Pfvg6YpLcJwKdBCI4t0sBeFCm/Wxk4JT/eh0tdnBHUaviQ0Gj+Bzz1A7J+mek
Ko/jR43KTFbIz46n/mCeYrtn2MTFl/AOsW+T/XoaOTI
-> piv-p256 zqq/iw A71bIRILKAlGedebswRMWObcmTf4o0VGarNPs0HwF7pU
EUfi118cd2/bfnwTXuYAiqx14FawWUf36n66hmpQuIM
-> piv-p256 vRzPNw Atd637HL03L8GedzPSanEXZt9V85DgGnriZnXngfKRFz
UiIUX1ADioDqckf0iT04NN5kOhmyRwf+/CG2+THAsrc
--- uajThUB7bCOg/ahzarVYOMb1c3XR0qrphQ/ehGBQztM
˜ehCMÅríbIÕ Îcì@sýFAS29Ÿ®îÀùœ] þØsýip]…ãV͇©<E280A1>5<EFBFBD>‡£Œ$IÙGkœ)ãúü¥¹\ IWNÔo3õÉy©„! :AS!

Binary file not shown.

Binary file not shown.

View file

@ -99,9 +99,14 @@ in {
"invoiceplane-db-secrets.env.age".publicKeys = pieKeys ++ baseKeys; "invoiceplane-db-secrets.env.age".publicKeys = pieKeys ++ baseKeys;
"mail@b12f.io-password.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys; "mail@b12f.io-password.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys;
"b12f.io-dkim-private-rsa.age".publicKeys = frikandelKeys ++ baseKeys; "b12f.io-dkim-private-rsa.age".publicKeys = frikandelKeys ++ baseKeys;
"mail@mezza.biz-password.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys;
"mezza.biz-dkim-private-rsa.age".publicKeys = frikandelKeys ++ baseKeys;
"mail@hzdomain-password.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys;
"hzdomain-dkim-private-rsa.age".publicKeys = frikandelKeys ++ baseKeys;
"unbound_control.key.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys; "unbound_control.key.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys;
"unbound_control.pem.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys; "unbound_control.pem.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys;
"unbound_server.key.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys; "unbound_server.key.age".publicKeys = pieKeys ++ frikandelKeys ++ baseKeys;

View file

@ -63,3 +63,27 @@ resource "hostingde_record" "hz-mta-sts" {
content = local.domain content = local.domain
ttl = 300 ttl = 300
} }
resource "hostingde_record" "hz-spf" {
zone_id = hostingde_zone.hz.id
name = local.domain
type = "TXT"
content = "v=spf1 a:mail.${local.domain} -all"
ttl = 300
}
resource "hostingde_record" "hz-dkim" {
zone_id = hostingde_zone.hz.id
name = "default._domainkey.${local.domain}"
type = "TXT"
content = "v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyla9hW3TvoXvZQxwzaJ4SZ9ict1HU3E6+FWlwNIgE6tIpTCyRJtiSIUDqB8TLTIBoxIs+QQBXZi+QUi3Agu6OSY2RiV0EwO8+oOOqOD9pERftc/aqe51cXuv4kPqwvpXEBwrXFWVM+VxivEubUJ7eKkFyXJpelv0LslXv/MmYbUyed6dF+reOGZCsvnbiRv74qdxbAL/25j62E8WrnxzJwhUtx/JhdBOjsHBvuw9hy6rZsVJL9eXayWyGRV6qmsLRzsRSBs+mDrgmKk4dugADd11+A03ics3i8hplRoWDkqnNKz1qy4f5TsV6v9283IANrAzRfHwX8EvNiFsBz+ZCQIDAQAB"
ttl = 300
}
resource "hostingde_record" "hz-dmarc" {
zone_id = hostingde_zone.hz.id
name = "_dmarc.${local.domain}"
type = "TXT"
content = "v=DMARC1;p=none;"
ttl = 300
}

View file

@ -26,3 +26,68 @@ resource "hostingde_record" "mezza-www" {
content = "mezza.biz" content = "mezza.biz"
ttl = 300 ttl = 300
} }
resource "hostingde_record" "mezza-mail" {
zone_id = hostingde_zone.mezza.id
name = "mail.mezza.biz"
type = "CNAME"
content = "mezza.biz"
ttl = 300
}
resource "hostingde_record" "mezza-autoconfig" {
zone_id = hostingde_zone.mezza.id
name = "autoconfig.mezza.biz"
type = "CNAME"
content = "mail.mezza.biz"
ttl = 300
}
resource "hostingde_record" "mezza-autodiscover" {
zone_id = hostingde_zone.mezza.id
name = "autodiscover.mezza.biz"
type = "CNAME"
content = "mail.mezza.biz"
ttl = 300
}
resource "hostingde_record" "mezza-mx" {
zone_id = hostingde_zone.mezza.id
name = "mezza.biz"
type = "MX"
content = "mail.mezza.biz"
priority = 10
ttl = 300
}
resource "hostingde_record" "mezza-mta-sts" {
zone_id = hostingde_zone.mezza.id
name = "mta-sts.mezza.biz"
type = "CNAME"
content = "mezza.biz"
ttl = 300
}
resource "hostingde_record" "mezza-spf" {
zone_id = hostingde_zone.mezza.id
name = "mezza.biz"
type = "TXT"
content = "v=spf1 a:mail.mezza.biz -all"
ttl = 300
}
resource "hostingde_record" "mezza-dkim" {
zone_id = hostingde_zone.mezza.id
name = "default._domainkey.mezza.biz"
type = "TXT"
content = "v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDG8iuDq0eon2k7QlBJWGxwDiEv53iJQu2uqxOjr7Ul/nfQjuR6kVKs6oOVopnyFTGRpffrpSHHW1YUN5nF76p0fJphk4l+QmJP36/xweajsNU27PAkb88xG6yRKl28MCfPdMR96+Jobpei8S0UhqcskYs1aZybm7ci9ZuAMidziwIDAQAB"
ttl = 300
}
resource "hostingde_record" "mezza-dmarc" {
zone_id = hostingde_zone.mezza.id
name = "_dmarc.mezza.biz"
type = "TXT"
content = "v=DMARC1;p=none;"
ttl = 300
}

View file

@ -1,7 +1,8 @@
[formatter.nix] [formatter.nix]
command = "alejandra" command = "nix"
options = ["fmt"]
includes = ["*.nix"] includes = ["*.nix"]
excludes = ["pkgs/_sources/generated.nix"] excludes = []
[formatter.prettier] [formatter.prettier]
command = "prettier" command = "prettier"

View file

@ -34,46 +34,55 @@ in {
"salt.base.test" = { "salt.base.test" = {
hostname = "10.0.0.2"; hostname = "10.0.0.2";
user = "root"; user = "root";
proxyJump = "gateway.base.test";
}; };
"gateway.base.test" = { "gateway.base.test" = {
hostname = "10.0.0.3"; # hostname = "10.0.0.3";
hostname = "gateway.base.cate-infra-test.de";
user = "root"; user = "root";
}; };
"monitor.base.test" = { "monitor.base.test" = {
hostname = "10.0.0.4"; hostname = "10.0.0.4";
user = "root"; user = "root";
proxyJump = "gateway.base.test";
}; };
"proxy1.prod.test" = { "proxy1.prod.test" = {
hostname = "10.0.0.6"; hostname = "10.0.0.6";
user = "root"; user = "root";
proxyJump = "gateway.base.test";
}; };
"proxy2.prod.test" = { "proxy2.prod.test" = {
hostname = "10.0.0.7"; hostname = "10.0.0.7";
user = "root"; user = "root";
proxyJump = "gateway.base.test";
}; };
"demo1.prod.test" = { "demo1.prod.test" = {
hostname = "10.0.0.5"; hostname = "10.0.0.5";
user = "root"; user = "root";
proxyJump = "gateway.base.test";
}; };
"demo2.prod.test" = { "demo2.prod.test" = {
hostname = "10.0.0.5"; hostname = "10.0.0.5";
user = "root"; user = "root";
proxyJump = "gateway.base.test";
}; };
"proxy1.test.test" = { "proxy1.test.test" = {
hostname = "10.0.0.8"; hostname = "10.0.0.8";
user = "root"; user = "root";
proxyJump = "gateway.base.test";
}; };
"proxy2.test.test" = { "proxy2.test.test" = {
hostname = "10.0.0.9"; hostname = "10.0.0.9";
user = "root"; user = "root";
proxyJump = "gateway.base.test";
}; };
}; };
}; };

View file

@ -12,59 +12,71 @@ with lib; let
cacheHome = xdg.cacheHome; cacheHome = xdg.cacheHome;
maildirBasePath = "/home/${psCfg.user.name}/Mail"; maildirBasePath = "/home/${psCfg.user.name}/Mail";
generateMailAccount = args@{ address, ... }: rec { generateMailAccount = args @ {address, ...}:
inherit address; rec {
realName = psCfg.user.fullName; inherit address;
signature = { realName =
showSignature = "append"; if (args ? "fullName")
text = builtins.readFile (./.config/neomutt + "/${builtins.replaceStrings ["@"] ["_"] address}.signature"); then args.fullName
}; else psCfg.user.fullName;
signature = {
showSignature = "append";
text =
if (args ? "emptysignature")
then ""
else builtins.readFile (./.config/neomutt + "/${builtins.replaceStrings ["@"] ["_"] address}.signature");
};
folders = { folders = {
inbox = "INBOX"; inbox = "INBOX";
drafts = "Drafts"; drafts = "Drafts";
sent = "Sent"; sent = "Sent";
trash = "Trash"; trash = "Trash";
}; };
gpg.key = psCfg.user.gpgKeyId; gpg.key = psCfg.user.gpgKeyId;
userName = address; userName = address;
passwordCommand = "secret-tool lookup email ${address}"; passwordCommand = "secret-tool lookup email ${address}";
imap = { imap = {
host = mkIf (args ? "host") args.host; host = mkIf (args ? "host") args.host;
port = 993; port = 993;
}; };
smtp = { smtp = {
host = mkIf (args ? "host") args.host; host = mkIf (args ? "host") args.host;
port = 587; port = 587;
tls.useStartTls = true; tls.useStartTls = true;
}; };
offlineimap.enable = true; offlineimap.enable = true;
imapnotify = { imapnotify = {
enable = true; enable = true;
boxes = [ "INBOX" ]; boxes = ["INBOX"];
onNotify = "${pkgs.offlineimap}/bin/offlineimap -a ${address}"; onNotify = "${pkgs.offlineimap}/bin/offlineimap -a ${address}";
onNotifyPost = "${pkgs.libnotify}/bin/notify-send '${address} has new mail'"; onNotifyPost = "${pkgs.libnotify}/bin/notify-send '${address} has new mail'";
}; };
msmtp.enable = true; msmtp.enable = true;
neomutt = { neomutt = {
enable = true; enable = true;
sendMailCommand = "msmtp -a ${address}"; sendMailCommand = "msmtp -a ${address}";
extraConfig = '' extraConfig = ''
set pgp_default_key="${gpg.key}" set pgp_default_key="${gpg.key}"
unmailboxes * unmailboxes *
mailboxes +INBOX \ mailboxes +INBOX \
+Drafts \ +Drafts \
+Sent \ +Sent \
+Archive \ +Archive \
+Trash +Trash
''; '';
}; };
} // (if args ? "config" then args.config else {}); }
// (
if args ? "config"
then args.config
else {}
);
in { in {
users.users."${psCfg.user.name}".packages = with pkgs; [ users.users."${psCfg.user.name}".packages = with pkgs; [
w3m w3m
@ -86,57 +98,66 @@ in {
accounts.email = { accounts.email = {
inherit maildirBasePath; inherit maildirBasePath;
accounts = with flake.self.lib; lib.lists.foldr (item: set: (set // { "${item.address}" = generateMailAccount item; })) {} [ accounts = with flake.self.lib;
{ lib.lists.foldr (item: set: (set // {"${item.address}" = generateMailAccount item;})) {} [
address = mkEmailAddress "hello" "benjaminbaedorf.eu"; {
host = "mail.hosting.de"; address = mkEmailAddress "hello" "benjaminbaedorf.eu";
config.primary = true; host = "mail.hosting.de";
} config.primary = true;
{ }
address = "mail@b12f.io"; {
host = "mail.b12f.io"; address = mkEmailAddress "mail" "b12f.io";
} host = "mail.b12f.io";
{ }
address = mkEmailAddress "benjamin.baedorf" "rwth-aachen.de"; {
host = "mail.rwth-aachen.de"; address = mkEmailAddress "benjamin.baedorf" "rwth-aachen.de";
config.userName = mkEmailAddress "bb564306" "rwth-aachen.de"; host = "mail.rwth-aachen.de";
} config.userName = mkEmailAddress "bb564306" "rwth-aachen.de";
{ }
address = mkEmailAddress "byb" "miom.space"; {
host = "mail.hosting.de"; address = mkEmailAddress "byb" "miom.space";
} host = "mail.hosting.de";
{ }
address = mkEmailAddress "contact" "miom.space"; {
host = "mail.hosting.de"; address = mkEmailAddress "contact" "miom.space";
} host = "mail.hosting.de";
{ }
address = mkEmailAddress "admins" "pub.solar"; {
host = "mail.pub.solar"; address = mkEmailAddress "admins" "pub.solar";
} host = "mail.pub.solar";
{ }
address = mkEmailAddress "crew" "pub.solar"; {
host = "mail.pub.solar"; address = mkEmailAddress "crew" "pub.solar";
} host = "mail.pub.solar";
# { }
# address = mkEmailAddress "benjamin.yule.baedorf" "verkstedt.com"; # {
# flavor = "gmail"; # address = mkEmailAddress "benjamin.yule.baedorf" "verkstedt.com";
# config = { # flavor = "gmail";
# smtp.host = "smtp.gmail.com"; # config = {
# imap.host = "imap.gmail.com"; # smtp.host = "smtp.gmail.com";
# }; # imap.host = "imap.gmail.com";
# } # };
{ # }
address = mkEmailAddress "benjamin-yule.baedorf" "ext.ehealthexperts.de"; {
config = { address = mkEmailAddress "benjamin-yule.baedorf" "ext.ehealthexperts.de";
smtp.host = "smtp.mailbox.org"; config = {
imap.host = "imap.mailbox.org"; smtp.host = "smtp.mailbox.org";
}; imap.host = "imap.mailbox.org";
} };
{ }
address = mkEmailAddress "hetzner" "benjaminbaedorf.eu"; {
host = "mail.hosting.de"; fullName = "mezza";
} address = mkEmailAddress "mail" "mezza.biz";
]; host = "mail.mezza.biz";
emptysignature = true;
}
{
fullname = "hwd" + "zz";
address = mkEmailAddress "mail" "h" + "w" + "dz" + "z.net";
host = "mail.h" + "w" + "dz" + "z.net";
emptysignature = true;
}
];
}; };
services.imapnotify.enable = true; services.imapnotify.enable = true;
@ -146,69 +167,167 @@ in {
enable = true; enable = true;
binds = [ binds = [
# Moving around # Moving around
{ map = ["generic"]; key = "g"; action = "noop"; } {
{ map = ["generic"]; key = "gg"; action = "first-entry"; } map = ["generic"];
{ map = ["generic" "index"]; key = "G"; action = "last-entry"; } key = "g";
{ map = ["generic" "index"]; key = "i"; action = "previous-entry"; } action = "noop";
{ map = ["generic" "index"]; key = "k"; action = "next-entry"; } }
{ map = ["pager" "index"]; key = "d"; action = "noop"; } {
{ map = ["pager" "index"]; key = "dd"; action = "delete-message"; } map = ["generic"];
{ map = ["pager"]; key = "i"; action = "previous-line"; } key = "gg";
{ map = ["pager"]; key = "k"; action = "next-line"; } action = "first-entry";
{ map = ["pager"]; key = "I"; action = "previous-entry"; } }
{ map = ["pager"]; key = "K"; action = "next-entry"; } {
map = ["generic" "index"];
key = "G";
action = "last-entry";
}
{
map = ["generic" "index"];
key = "i";
action = "previous-entry";
}
{
map = ["generic" "index"];
key = "k";
action = "next-entry";
}
{
map = ["pager" "index"];
key = "d";
action = "noop";
}
{
map = ["pager" "index"];
key = "dd";
action = "delete-message";
}
{
map = ["pager"];
key = "i";
action = "previous-line";
}
{
map = ["pager"];
key = "k";
action = "next-line";
}
{
map = ["pager"];
key = "I";
action = "previous-entry";
}
{
map = ["pager"];
key = "K";
action = "next-entry";
}
{ map = ["pager"]; key = "r"; action = "noop"; } {
{ map = ["pager"]; key = "rr"; action = "reply"; } map = ["pager"];
{ map = ["pager"]; key = "ra"; action = "group-reply"; } key = "r";
{ map = ["pager"]; key = "rn"; action = "group-chat-reply"; } action = "noop";
{ map = ["pager"]; key = "rl"; action = "list-reply"; } }
{
map = ["pager"];
key = "rr";
action = "reply";
}
{
map = ["pager"];
key = "ra";
action = "group-reply";
}
{
map = ["pager"];
key = "rn";
action = "group-chat-reply";
}
{
map = ["pager"];
key = "rl";
action = "list-reply";
}
# Threads # Threads
{ map = ["browser" "pager" "index"]; key = "N"; action = "search-opposite"; }
{ map = ["pager" "index"]; key = "dT"; action = "delete-thread"; }
{ map = ["pager" "index"]; key = "dt"; action = "delete-subthread"; }
{ map = ["pager" "index"]; key = "g"; action = "noop"; }
{ map = ["pager" "index"]; key = "gt"; action = "next-thread"; }
{ map = ["pager" "index"]; key = "gT"; action = "previous-thread"; }
{ map = ["index"]; key = "za"; action = "collapse-thread"; }
{ map = ["index"]; key = "zA"; action = "collapse-all"; }
];
macros = [
# Enable URL opening
{ {
map = ["index" "pager"]; map = ["browser" "pager" "index"];
key = "\\Cb"; key = "N";
action = ''<pipe-message> ${pkgs.urlscan}/bin/urlscan<Enter>" "call urlscan to extract URLs out of a message''; action = "search-opposite";
} }
{ {
map = ["attach" "compose"]; map = ["pager" "index"];
key = "\\Cb"; key = "dT";
action = ''<pipe-entry> ${pkgs.urlscan}/bin/urlscan<Enter>" "call urlscan to extract URLs out of a message''; action = "delete-thread";
} }
# Translate markdown to html
{ {
map = ["compose"]; map = ["pager" "index"];
key = "\\Cm"; key = "dt";
action = ''F ${pkgs.pandoc}/bin/pandoc -s -f markdown -t html \ny^T^Utext/html; charset=utf-8\n" "Convert to HTML''; action = "delete-subthread";
} }
# Save to ~/Downloads by default
{ {
map = ["attach"]; map = ["pager" "index"];
key = "S"; key = "g";
action = ''<save-entry><bol>~/Downloads/<eol>" "Save to Downloads''; action = "noop";
}
{
map = ["pager" "index"];
key = "gt";
action = "next-thread";
}
{
map = ["pager" "index"];
key = "gT";
action = "previous-thread";
} }
{ {
map = ["index"]; map = ["index"];
key = "\\Ca"; key = "za";
action = ''<tag-pattern>~N<enter><tag-prefix><clear-flag>N<untag-pattern>.<enter>" "Mark all as read''; action = "collapse-thread";
} }
] ++ lib.lists.imap1 (i: address: { {
map = ["index" "pager"]; map = ["index"];
key = "<F${builtins.toString i}>"; key = "zA";
action = ''<sync-mailbox><enter-command>source ${configHome}/neomutt/${address}<enter><change-folder>!<enter>''; action = "collapse-all";
}) (builtins.attrNames accounts.email.accounts); }
];
macros =
[
# Enable URL opening
{
map = ["index" "pager"];
key = "\\Cb";
action = ''<pipe-message> ${pkgs.urlscan}/bin/urlscan<Enter>" "call urlscan to extract URLs out of a message'';
}
{
map = ["attach" "compose"];
key = "\\Cb";
action = ''<pipe-entry> ${pkgs.urlscan}/bin/urlscan<Enter>" "call urlscan to extract URLs out of a message'';
}
# Translate markdown to html
{
map = ["compose"];
key = "\\Cm";
action = ''F ${pkgs.pandoc}/bin/pandoc -s -f markdown -t html \ny^T^Utext/html; charset=utf-8\n" "Convert to HTML'';
}
# Save to ~/Downloads by default
{
map = ["attach"];
key = "S";
action = ''<save-entry><bol>~/Downloads/<eol>" "Save to Downloads'';
}
{
map = ["index"];
key = "\\Ca";
action = ''<tag-pattern>~N<enter><tag-prefix><clear-flag>N<untag-pattern>.<enter>" "Mark all as read'';
}
]
++ lib.lists.imap1 (i: address: {
map = ["index" "pager"];
key = "<F${builtins.toString i}>";
action = ''<sync-mailbox><enter-command>source ${configHome}/neomutt/${address}<enter><change-folder>!<enter>'';
}) (builtins.attrNames accounts.email.accounts);
# unmailboxes = true; # unmailboxes = true;
extraConfig = '' extraConfig = ''
@ -226,7 +345,7 @@ in {
imap_check_subscribed = "yes"; imap_check_subscribed = "yes";
date_format = ''"!%d.%m.%Y %H:%M"''; date_format = ''"!%d.%m.%Y %H:%M"'';
index_format = ''"%4C %Z %D %4c %20.20L %s"''; index_format = ''"%4C %Z %D %4c %20.20L %s"'';
mailcap_path = "${configHome}/neomutt/mailcap"; mailcap_path = "${configHome}/neomutt/mailcap";
}; };
}; };
}; };
@ -243,7 +362,7 @@ in {
services.restic.backups = { services.restic.backups = {
email = { email = {
paths = [ maildirBasePath ]; paths = [maildirBasePath];
initialize = true; initialize = true;
passwordFile = config.age.secrets."restic-password".path; passwordFile = config.age.secrets."restic-password".path;
# See https://www.hosting.de/blog/verschluesselte-backups-mit-rclone-und-restic-in-nextcloud/ # See https://www.hosting.de/blog/verschluesselte-backups-mit-rclone-und-restic-in-nextcloud/

View file

@ -127,6 +127,31 @@ in {
user = "barkeeper"; user = "barkeeper";
hostname = "10.7.6.2"; hostname = "10.7.6.2";
}; };
"metronom.pub.solar" = {
user = "barkeeper";
hostname = "10.7.6.3";
};
"tankstelle.pub.solar" = {
user = "barkeeper";
hostname = "10.7.6.4";
};
"trinkgenossin.pub.solar" = {
user = "barkeeper";
hostname = "10.7.6.5";
};
"delite.pub.solar" = {
user = "barkeeper";
hostname = "10.7.6.6";
};
"blue-shell.pub.solar" = {
user = "barkeeper";
hostname = "10.7.6.7";
};
}; };
}; };
}; };

View file

@ -1,5 +1,4 @@
{ self, ... }: {self, ...}: {
{
flake = { flake = {
nixosModules = rec { nixosModules = rec {
root = import ./root; root = import ./root;

View file

@ -1,4 +1,3 @@
{...}: {...}: {
{
users.users.root.hashedPassword = "$y$j9T$HihsChALx5fotahvDVhdC/$iQCGUr35quGDDEFg0SGjDBxWzU/kokgOVDX.weRvL80"; users.users.root.hashedPassword = "$y$j9T$HihsChALx5fotahvDVhdC/$iQCGUr35quGDDEFg0SGjDBxWzU/kokgOVDX.weRvL80";
} }