init commit
This commit is contained in:
commit
181e6f681e
22 changed files with 940 additions and 0 deletions
46
configuration.nix
Normal file
46
configuration.nix
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
{ pkgs, config, modulesPath, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
(modulesPath + "/profiles/qemu-guest.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.hostName = "hexname-ns1";
|
||||||
|
|
||||||
|
users.users = {
|
||||||
|
luka = {
|
||||||
|
isNormalUser = true;
|
||||||
|
linger = true; # Keep user services running
|
||||||
|
extraGroups = [ "networkmanager" "wheel" "podman" ];
|
||||||
|
hashedPassword = "$y$j9T$6bEHYFO.AGCC2bnKxC3xB/$6/1zmuzaSvDSHID6ZTgnrHiWRS8ayEXhNBp48ugR4z7";
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM/4F45h/xkq+MIRDzhHqDm5uWM4KTpYi3Tv/DtSo28t luka@gram"
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF7OvW6MffYFshZyarEaWvWjEmhodn/P+NLcnqbbMpma luka@conway"
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIT+vMzh2ngUeqnVJS8Zl1m1HQMBkDOqoGdoARPyJgDM u0_a380@localhost" # s
|
||||||
|
];
|
||||||
|
};
|
||||||
|
root = {
|
||||||
|
isSystemUser = true;
|
||||||
|
hashedPassword = "$y$j9T$6bEHYFO.AGCC2bnKxC3xB/$6/1zmuzaSvDSHID6ZTgnrHiWRS8ayEXhNBp48ugR4z7";
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM/4F45h/xkq+MIRDzhHqDm5uWM4KTpYi3Tv/DtSo28t luka@gram"
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF7OvW6MffYFshZyarEaWvWjEmhodn/P+NLcnqbbMpma luka@conway"
|
||||||
|
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIT+vMzh2ngUeqnVJS8Zl1m1HQMBkDOqoGdoARPyJgDM u0_a380@localhost" # s
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nix.settings.trusted-users = [ "luka" ]; # TODO: remove this line
|
||||||
|
|
||||||
|
boot.loader.grub = {
|
||||||
|
enable = true;
|
||||||
|
efiSupport = true;
|
||||||
|
efiInstallAsRemovable = true;
|
||||||
|
device = "nodev";
|
||||||
|
};
|
||||||
|
boot.supportedFilesystems = [ "zfs" ];
|
||||||
|
networking.hostId = "11111111"; # $ head -c 8 /etc/machine-id
|
||||||
|
|
||||||
|
system.stateVersion = "25.11";
|
||||||
|
}
|
||||||
|
|
||||||
44
flake.lock
generated
Executable file
44
flake.lock
generated
Executable file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1768564909,
|
||||||
|
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-stable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1768621446,
|
||||||
|
"narHash": "sha256-6YwHV1cjv6arXdF/PQc365h1j+Qje3Pydk501Rm4Q+4=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "72ac591e737060deab2b86d6952babd1f896d7c5",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-25.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"nixpkgs-stable": "nixpkgs-stable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
45
flake.nix
Executable file
45
flake.nix
Executable file
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
description = "Epic";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.11";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { nixpkgs, nixpkgs-stable, ... } @ inputs: {
|
||||||
|
nixosConfigurations = {
|
||||||
|
hexname-ns1 = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs = { inherit inputs; };
|
||||||
|
modules = [
|
||||||
|
######## Boilerplate ########
|
||||||
|
./configuration.nix
|
||||||
|
./hardware-configuration.nix
|
||||||
|
|
||||||
|
./pkgs/extra.nix
|
||||||
|
|
||||||
|
######## HexName configuration ########
|
||||||
|
#./pkgs/hexname/powerdns-podman.nix
|
||||||
|
|
||||||
|
######## Networking ########
|
||||||
|
./pkgs/server-ssh.nix
|
||||||
|
./pkgs/nginx.nix
|
||||||
|
./pkgs/virtualisation.nix
|
||||||
|
# ./pkgs/stalwart.nix
|
||||||
|
|
||||||
|
######## Sysadmin ########
|
||||||
|
./pkgs/neovim.nix
|
||||||
|
./pkgs/common-packages.nix
|
||||||
|
./pkgs/aliases.nix
|
||||||
|
|
||||||
|
######## etc. ########
|
||||||
|
|
||||||
|
######## Scripts ########
|
||||||
|
#./scripts/virtualisation/update-containers.nix # Runs podman pull weekly
|
||||||
|
#./scripts/virtualisation/restart-pihole.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
39
hardware-configuration.nix
Normal file
39
hardware-configuration.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Do not modify this file! It was generated by ‘nixos-generate-config’
|
||||||
|
# and may be overwritten by future invocations. Please make changes
|
||||||
|
# to /etc/nixos/configuration.nix instead.
|
||||||
|
{ config, lib, pkgs, modulesPath, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports =
|
||||||
|
[ (modulesPath + "/profiles/qemu-guest.nix")
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.initrd.availableKernelModules = [ "ahci" "xhci_pci" "virtio_pci" "virtio_scsi" "sd_mod" "sr_mod" ];
|
||||||
|
boot.initrd.kernelModules = [ ];
|
||||||
|
boot.kernelModules = [ ];
|
||||||
|
boot.extraModulePackages = [ ];
|
||||||
|
|
||||||
|
fileSystems."/" =
|
||||||
|
{ device = "zroot/nixos";
|
||||||
|
fsType = "zfs";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/boot" =
|
||||||
|
{ device = "/dev/disk/by-uuid/3979-106D";
|
||||||
|
fsType = "vfat";
|
||||||
|
options = [ "fmask=0022" "dmask=0022" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
swapDevices =
|
||||||
|
[ { device = "/dev/disk/by-uuid/e383cf2c-0d45-4e13-8bc8-7151023fb881"; }
|
||||||
|
];
|
||||||
|
|
||||||
|
# Enables DHCP on each ethernet and wireless interface. In case of scripted networking
|
||||||
|
# (the default) this is the recommended approach. When using systemd-networkd it's
|
||||||
|
# still possible to use this option, but it's recommended to use it in conjunction
|
||||||
|
# with explicit per-interface declarations with `networking.interfaces.<interface>.useDHCP`.
|
||||||
|
networking.useDHCP = lib.mkDefault true;
|
||||||
|
# networking.interfaces.enp1s0.useDHCP = lib.mkDefault true;
|
||||||
|
|
||||||
|
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
|
||||||
|
}
|
||||||
34
pkgs/aliases.nix
Normal file
34
pkgs/aliases.nix
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
programs.bash = {
|
||||||
|
shellAliases = {
|
||||||
|
ll = "ls -Ahlv --time-style=iso --group-directories-first";
|
||||||
|
l = "ls -hgov --time-style=iso --group-directories-first";
|
||||||
|
|
||||||
|
gs = "git status";
|
||||||
|
ga = "git add .";
|
||||||
|
gd = "git diff";
|
||||||
|
gp = "git push";
|
||||||
|
gr = "git restore";
|
||||||
|
|
||||||
|
du = "du -sh";
|
||||||
|
|
||||||
|
".." = "cd ..";
|
||||||
|
|
||||||
|
n = "cd /etc/nixos";
|
||||||
|
f = "vim /etc/nixos/flake.nix";
|
||||||
|
c = "vim /etc/nixos/configuration.nix";
|
||||||
|
};
|
||||||
|
shellInit = ''
|
||||||
|
export COLORTERM=truecolor;
|
||||||
|
s() {
|
||||||
|
cd /etc/nixos;
|
||||||
|
git add . &&
|
||||||
|
sudo nixos-rebuild switch --flake /etc/nixos;
|
||||||
|
cd - &> /dev/null;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
3
pkgs/backend.nix
Normal file
3
pkgs/backend.nix
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{}:
|
||||||
|
|
||||||
|
|
||||||
24
pkgs/common-packages.nix
Normal file
24
pkgs/common-packages.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
vim
|
||||||
|
|
||||||
|
tmux
|
||||||
|
|
||||||
|
fastfetch
|
||||||
|
btop
|
||||||
|
iotop
|
||||||
|
dool
|
||||||
|
ncdu
|
||||||
|
dig
|
||||||
|
wget
|
||||||
|
unzip
|
||||||
|
nettools
|
||||||
|
pciutils
|
||||||
|
|
||||||
|
iptables
|
||||||
|
openssl # Generate secure passwords with: $ openssl rand -base64 48
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
50
pkgs/extra.nix
Normal file
50
pkgs/extra.nix
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
programs.git = {
|
||||||
|
enable = true;
|
||||||
|
config = {
|
||||||
|
user.name = "Luka Dekanozishvili";
|
||||||
|
user.email = "me@lukadeka.com";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
|
||||||
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
|
# Select internationalisation properties.
|
||||||
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
i18n.extraLocaleSettings = let
|
||||||
|
locale = "en_US.UTF-8";
|
||||||
|
in {
|
||||||
|
LC_ADDRESS = locale;
|
||||||
|
LC_IDENTIFICATION = locale;
|
||||||
|
LC_MEASUREMENT = locale;
|
||||||
|
LC_MONETARY = locale;
|
||||||
|
LC_NAME = locale;
|
||||||
|
LC_NUMERIC = locale;
|
||||||
|
LC_PAPER = locale;
|
||||||
|
LC_TELEPHONE = locale;
|
||||||
|
LC_TIME = locale;
|
||||||
|
};
|
||||||
|
|
||||||
|
nix = {
|
||||||
|
settings.experimental-features = [ "nix-command" "flakes" ];
|
||||||
|
optimise.automatic = true;
|
||||||
|
|
||||||
|
package = pkgs.nixVersions.latest;
|
||||||
|
|
||||||
|
# Remove warning each rebuild that files aren't commited to git
|
||||||
|
extraOptions = ''
|
||||||
|
warn-dirty = false
|
||||||
|
'';
|
||||||
|
|
||||||
|
gc = {
|
||||||
|
automatic = true;
|
||||||
|
dates = "Mon *-*-* 16:00:00";
|
||||||
|
options = "--delete-older-than 90d";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
36
pkgs/neovim.nix
Normal file
36
pkgs/neovim.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{ config, pkgs, programs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
programs.neovim = {
|
||||||
|
enable = true;
|
||||||
|
defaultEditor = true;
|
||||||
|
|
||||||
|
# viAlias = true;
|
||||||
|
vimAlias = true;
|
||||||
|
|
||||||
|
configure = {
|
||||||
|
customRC = ''
|
||||||
|
set cc=80
|
||||||
|
set list
|
||||||
|
set listchars=tab:→\ ,trail:•,precedes:«,extends:»
|
||||||
|
colorscheme habamax
|
||||||
|
'';
|
||||||
|
packages.myVimPackage = with pkgs.vimPlugins; {
|
||||||
|
start = [
|
||||||
|
(nvim-treesitter.withPlugins (
|
||||||
|
plugins: with plugins; [
|
||||||
|
nix
|
||||||
|
python
|
||||||
|
bash
|
||||||
|
c
|
||||||
|
]
|
||||||
|
))
|
||||||
|
telescope-nvim
|
||||||
|
vim-commentary # gcc
|
||||||
|
vim-startify
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
41
pkgs/nginx.nix
Normal file
41
pkgs/nginx.nix
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
recommendedGzipSettings = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
recommendedTlsSettings = true;
|
||||||
|
# recommendedProxySettings = true;
|
||||||
|
|
||||||
|
# Only allow PFS-enabled ciphers with AES256
|
||||||
|
sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL";
|
||||||
|
|
||||||
|
appendHttpConfig = ''
|
||||||
|
# Minimize information leaked to other domains
|
||||||
|
add_header 'Referrer-Policy' 'origin-when-cross-origin';
|
||||||
|
|
||||||
|
# Disable embedding as a frame
|
||||||
|
add_header X-Frame-Options SAMEORIGIN;
|
||||||
|
|
||||||
|
# Prevent injection of code in other mime types (XSS Attacks)
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
|
||||||
|
# error_log /var/log/nginx/error.log debug;
|
||||||
|
# error_log stderr;
|
||||||
|
# access_log syslog:server=unix:/dev/log combined;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
security.acme = {
|
||||||
|
acceptTerms = true;
|
||||||
|
defaults.email = "me@lukadeka.com";
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall = {
|
||||||
|
enable = true;
|
||||||
|
allowedTCPPorts = [ 80 443 ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
71
pkgs/powerdns-podman.nix
Normal file
71
pkgs/powerdns-podman.nix
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
domain = "hexname.com";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
virtualisation.oci-containers.containers = {
|
||||||
|
hexname-powerdns-postgres = {
|
||||||
|
hostname = "pgsql";
|
||||||
|
image = "postgres:18-alpine";
|
||||||
|
# ports = [
|
||||||
|
# "127.0.0.1:5432:5432"
|
||||||
|
# ];
|
||||||
|
volumes = [
|
||||||
|
"/etc/localtime:/etc/localtime:ro"
|
||||||
|
"pgsql:/var/lib/postgresql/data:Z"
|
||||||
|
];
|
||||||
|
networks = [ "hexname-powerdns-net" ];
|
||||||
|
environmentFiles = [ "/etc/env/hexname/postgres.env" ]; # POSTGRES_PASSWORD=...
|
||||||
|
};
|
||||||
|
|
||||||
|
hexname-powerdns = {
|
||||||
|
image = "pschiffe/pdns-pgsql:latest";
|
||||||
|
hostname = "ns1.${domain}";
|
||||||
|
ports = [
|
||||||
|
"127.0.0.2:53:53/tcp" # TODO: remove localhost
|
||||||
|
"127.0.0.2:53:53/udp"
|
||||||
|
"127.0.0.2:8081:8081/tcp"
|
||||||
|
];
|
||||||
|
networks = [ "hexname-powerdns-net" ];
|
||||||
|
|
||||||
|
volumes = [
|
||||||
|
"/etc/localtime:/etc/localtime:ro"
|
||||||
|
];
|
||||||
|
|
||||||
|
environmentFiles = [ "/etc/env/hexname/powerdns.env" ];
|
||||||
|
environment = {
|
||||||
|
PDNS_primary = "yes";
|
||||||
|
PDNS_api = "yes";
|
||||||
|
#PDNS_webserver = "yes";
|
||||||
|
PDNS_webserver_address = "0.0.0.0";
|
||||||
|
PDNS_webserver_port = "8081";
|
||||||
|
PDNS_local_address = "0.0.0.0:53";
|
||||||
|
PDNS_webserver_allow_from = "10.0.0.0/8";
|
||||||
|
PDNS_version_string = "anonymous";
|
||||||
|
PDNS_default_ttl = "1500";
|
||||||
|
# PDNS_allow_axfr_ips = "172.5.0.21";
|
||||||
|
|
||||||
|
# PDNS_gpgsql_password=...
|
||||||
|
# PDNS_api_key=...
|
||||||
|
};
|
||||||
|
dependsOn = [ "hexname-powerdns-postgres" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.podman-network-hexname = {
|
||||||
|
description = "Podman network for HexName/PowerDNS";
|
||||||
|
after = [ "podman.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" "podman-hexname-powerdns.target" "podman-hexname-powerdns-postgres.target" ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
path = [ pkgs.podman] ;
|
||||||
|
script = ''
|
||||||
|
podman network inspect hexname-powerdns-net >/dev/null 2>&1 || \
|
||||||
|
podman network create hexname-powerdns-net
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 53 ];
|
||||||
|
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||||
|
}
|
||||||
|
|
||||||
25
pkgs/powerdns.nix
Normal file
25
pkgs/powerdns.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
domain = "hexname.com";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
services.powerdns = {
|
||||||
|
enable = true
|
||||||
|
|
||||||
|
# To hash the api-key, use:
|
||||||
|
# $ pdnsutil hash-password
|
||||||
|
extraConfig = ''
|
||||||
|
api=true
|
||||||
|
api-key=
|
||||||
|
primary=yes
|
||||||
|
webserver-address=127.0.0.1
|
||||||
|
webserver-port=8081
|
||||||
|
local-address=0.0.0.0:53
|
||||||
|
webserver-allow-from=127.0.0.1/32
|
||||||
|
version-string=anonymous
|
||||||
|
default-ttl=1500
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
30
pkgs/server-ssh.nix
Normal file
30
pkgs/server-ssh.nix
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{ pkgs, config, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
ports = [ 6968 ];
|
||||||
|
openFirewall = true;
|
||||||
|
settings = {
|
||||||
|
PasswordAuthentication = false;
|
||||||
|
AllowUsers = [ "luka" ];
|
||||||
|
UseDns = false; # Disable checking of rDNS records to speed up login
|
||||||
|
X11Forwarding = false;
|
||||||
|
PermitRootLogin = "prohibit-password";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.fail2ban = {
|
||||||
|
enable = false;
|
||||||
|
bantime = "24h"; # Ban IPs for one day on the first ban
|
||||||
|
# ignoreIP = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# allowedTCPPorts = [ ];
|
||||||
|
# allowedUDPPorts = [];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
272
pkgs/stalwart.nix
Normal file
272
pkgs/stalwart.nix
Normal file
|
|
@ -0,0 +1,272 @@
|
||||||
|
{ config, inputs, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
domain = "hexname.com";
|
||||||
|
stalwartDomain = "mail.${domain}";
|
||||||
|
roundcubeDomain = "email.${domain}";
|
||||||
|
|
||||||
|
dataDir = "/var/lib/stalwart-mail";
|
||||||
|
credPath = "/run/credentials/stalwart-mail.service";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
services.stalwart-mail = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.stalwart-mail;
|
||||||
|
openFirewall = true;
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
server = {
|
||||||
|
hostname = stalwartDomain;
|
||||||
|
tls = {
|
||||||
|
enable = true;
|
||||||
|
implicit = true;
|
||||||
|
};
|
||||||
|
listener = {
|
||||||
|
smtp = {
|
||||||
|
bind = [ "[::]:25" ];
|
||||||
|
protocol = "smtp";
|
||||||
|
};
|
||||||
|
submission = {
|
||||||
|
bind = [ "[::]:587" ];
|
||||||
|
protocol = "smtp";
|
||||||
|
};
|
||||||
|
submissions = {
|
||||||
|
bind = [ "[::]:465" ];
|
||||||
|
protocol = "smtp";
|
||||||
|
tls.implicit = true;
|
||||||
|
};
|
||||||
|
imap = {
|
||||||
|
bind = [ "[::]:143" ];
|
||||||
|
protocol = "imap";
|
||||||
|
};
|
||||||
|
imaps = {
|
||||||
|
bind = [ "[::]:993" ];
|
||||||
|
protocol = "imap";
|
||||||
|
tls.implicit = true;
|
||||||
|
};
|
||||||
|
http = {
|
||||||
|
bind = [ "[::]:51020" ];
|
||||||
|
protocol = "http";
|
||||||
|
url = "https://${stalwartDomain}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
directory."in-memory" = {
|
||||||
|
type = "memory";
|
||||||
|
# Generate hashes with:
|
||||||
|
# $ openssl passwd -6
|
||||||
|
principals = [
|
||||||
|
{
|
||||||
|
name = "contact-us@${domain}";
|
||||||
|
email = [ "contact-us@${domain}" "@${domain}" ];
|
||||||
|
secret = "$6$E8AhTdIdgl2ag6/x$reYvoPByjvkPK/Uwm3/481BBBkuBKQxd3rgSgQw3PawJ4G8TOt0jlIXdOo5xuDv1DQAdn52lUAgx0U9GSVoc7/";
|
||||||
|
class = "individual";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "no-reply@${domain}";
|
||||||
|
email = [ "no-reply@${domain}" ];
|
||||||
|
secret = "$6$V/u1LImVZAyynuLO$l/mMaLWQ5t0jWz6XWNlHcha8nuTQbjQfES.Nj73mNS0xQjv3vu5z03fLMAt3hxAb5BwE3jgtfmh.PknBjM1M//";
|
||||||
|
class = "individual";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
authentication.fallback-admin = {
|
||||||
|
user = "superdupermegaadmin";
|
||||||
|
secret = "$6$LPDx0LFqtpAVJO2s$GPR/4Rguhmspy8OLLKI2oZxVgvWrlHRckd6WN2RZNMxkSN9YMiPJ/pfq.XD/VTKsqCu2GCnzerQOv5bivBCph.";
|
||||||
|
};
|
||||||
|
|
||||||
|
email.folders = let
|
||||||
|
mkFolder = name: {
|
||||||
|
inherit name;
|
||||||
|
create = true;
|
||||||
|
subscribe = true;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
inbox = mkFolder "Inbox";
|
||||||
|
sent = mkFolder "Sent";
|
||||||
|
drafts = mkFolder "Drafts";
|
||||||
|
archive = mkFolder "Archive";
|
||||||
|
junk = mkFolder "Spam";
|
||||||
|
trash = mkFolder "Trash";
|
||||||
|
};
|
||||||
|
|
||||||
|
session.rcpt = {
|
||||||
|
catch-all = true;
|
||||||
|
script = "'reject-addresses'";
|
||||||
|
};
|
||||||
|
|
||||||
|
sieve.trusted.scripts.reject-addresses.contents = ''
|
||||||
|
require ["envelope", "reject"];
|
||||||
|
|
||||||
|
if anyof (
|
||||||
|
envelope :is "to" "no-reply@${domain}"
|
||||||
|
envelope :is "to" "info@${domain}",
|
||||||
|
envelope :is "to" "contact@${domain}",
|
||||||
|
envelope :is "to" "support@${domain}"
|
||||||
|
envelope :is "to" "marketing@${domain}",
|
||||||
|
envelope :is "to" "sales@${domain}"
|
||||||
|
) {
|
||||||
|
reject "403 This address does not accept incoming mails.";
|
||||||
|
}
|
||||||
|
|
||||||
|
redirect "contact-us@${domain}";
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Change the DNS records manually to these addresses to
|
||||||
|
# keep postmaster free for non-automated emails
|
||||||
|
# https://github.com/stalwartlabs/mail-server/discussions/877
|
||||||
|
report.analysis = {
|
||||||
|
addresses = [
|
||||||
|
"dmarc-reports@*"
|
||||||
|
"tls-reports@*"
|
||||||
|
"spf-reports@*"
|
||||||
|
];
|
||||||
|
forward = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Stop warnings about what's managed where
|
||||||
|
config.local-keys = [
|
||||||
|
"authentication.fallback-admin.*"
|
||||||
|
"certificate.*"
|
||||||
|
"cluster.node-id"
|
||||||
|
"directory.*"
|
||||||
|
"email.folders.*"
|
||||||
|
"lookup.default.domain"
|
||||||
|
"lookup.default.hostname"
|
||||||
|
"report.analysis.*"
|
||||||
|
"resolver.*"
|
||||||
|
"server.*"
|
||||||
|
"!server.blocked-ip.*"
|
||||||
|
"session.mta-sts.*"
|
||||||
|
"session.rcpt.catch-all"
|
||||||
|
"session.rcpt.script"
|
||||||
|
"sieve.trusted.scripts.*"
|
||||||
|
"spam-filter.resource"
|
||||||
|
"storage.blob"
|
||||||
|
"storage.data"
|
||||||
|
"storage.directory"
|
||||||
|
"storage.fts"
|
||||||
|
"storage.lookup"
|
||||||
|
"store.*"
|
||||||
|
"tracer.*"
|
||||||
|
"webadmin.*"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Store blobs in the file system for easier backups.
|
||||||
|
# Since the database is backed up to /tmp, it would not fit in RAM
|
||||||
|
# with all the blobs.
|
||||||
|
store.fs = {
|
||||||
|
type = "fs";
|
||||||
|
path = "${dataDir}/blobs";
|
||||||
|
};
|
||||||
|
storage.blob = "fs";
|
||||||
|
|
||||||
|
# We have DANE and don't want a certificate for each domain
|
||||||
|
# session.mta-sts.mode = "none";
|
||||||
|
|
||||||
|
certificate.default = {
|
||||||
|
cert = "%{file:${credPath}/cert.pem}%";
|
||||||
|
private-key = "%{file:${credPath}/key.pem}%";
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
lookup.default = {
|
||||||
|
inherit domain;
|
||||||
|
hostname = stalwartDomain;
|
||||||
|
};
|
||||||
|
|
||||||
|
tracer.stdout.level = "info";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [ 25 143 465 587 993 ];
|
||||||
|
|
||||||
|
systemd.services.stalwart-mail = {
|
||||||
|
wants = [ "acme-${stalwartDomain}.service" ];
|
||||||
|
after = [ "acme-${stalwartDomain}.service" ];
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${dataDir}/db
|
||||||
|
'';
|
||||||
|
serviceConfig = {
|
||||||
|
LogsDirectory = "stalwart-mail";
|
||||||
|
LoadCredential = [
|
||||||
|
"cert.pem:${config.security.acme.certs.${stalwartDomain}.directory}/cert.pem"
|
||||||
|
"key.pem:${config.security.acme.certs.${stalwartDomain}.directory}/key.pem"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.roundcube = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.roundcube;
|
||||||
|
dicts = with pkgs.aspellDicts; [ en de ];
|
||||||
|
hostName = roundcubeDomain;
|
||||||
|
plugins = [
|
||||||
|
"archive"
|
||||||
|
"zipdownload"
|
||||||
|
"acl"
|
||||||
|
];
|
||||||
|
extraConfig = ''
|
||||||
|
$config['imap_host'] = 'ssl://${stalwartDomain}:993';
|
||||||
|
$config['smtp_host'] = 'ssl://%h:465';
|
||||||
|
$config['mail_domain'] = '%z';
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx.virtualHosts = let
|
||||||
|
proxy = "http://localhost:51020";
|
||||||
|
in {
|
||||||
|
${stalwartDomain} = {
|
||||||
|
enableACME = true;
|
||||||
|
forceSSL = true;
|
||||||
|
locations."/".proxyPass = proxy;
|
||||||
|
};
|
||||||
|
${roundcubeDomain}.locations."/".extraConfig = ''
|
||||||
|
add_header Cache-Control "public, max-age=604800, must-revalidate" always;
|
||||||
|
add_header Referrer-Policy "origin-when-cross-origin" always;
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
|
||||||
|
'';
|
||||||
|
"mta-sts.${domain}" = {
|
||||||
|
enableACME = true;
|
||||||
|
forceSSL = true;
|
||||||
|
locations."/".root = pkgs.writeTextFile {
|
||||||
|
name = "mta-sts.txt";
|
||||||
|
text = ''
|
||||||
|
version: STSv1
|
||||||
|
mode: enforce
|
||||||
|
mx: ${stalwartDomain}
|
||||||
|
max_age: 86400
|
||||||
|
'';
|
||||||
|
destination = "/.well-known/mta-sts.txt";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
security.acme.certs.${stalwartDomain} = {
|
||||||
|
# Keep a stable private key for TLSA records (DANE)
|
||||||
|
# https://community.letsencrypt.org/t/please-avoid-3-0-1-and-3-0-2-dane-tlsa-records-with-le-certificates/7022/14
|
||||||
|
# extraLegoRenewFlags = [ "--reuse-key" ];
|
||||||
|
# Restart Stalwart to apply new certificates
|
||||||
|
reloadServices = [ "stalwart-mail.service" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# services.restic = {
|
||||||
|
# backupPrepareCommand = ''
|
||||||
|
# ${pkgs.coreutils}/bin/install -b -m 700 -d /tmp/stalwart-db-secondary /tmp/stalwart-db-backup
|
||||||
|
# ${lib.getExe' rocksdb.tools "ldb"} --db=${dataDir}/db --secondary_path=/tmp/stalwart-db-secondary backup --backup_dir=/tmp/stalwart-db-backup
|
||||||
|
# '';
|
||||||
|
# backupCleanupCommand = ''
|
||||||
|
# rm -rf /tmp/stalwart-db-secondary
|
||||||
|
# rm -rf /tmp/stalwart-db-backup
|
||||||
|
# '';
|
||||||
|
# paths = [
|
||||||
|
# "/tmp/stalwart-db-backup"
|
||||||
|
# "${dataDir}/blobs"
|
||||||
|
# ];
|
||||||
|
# };
|
||||||
|
}
|
||||||
|
|
||||||
25
pkgs/tailscale.nix
Normal file
25
pkgs/tailscale.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
environment.systemPackages = with pkgs; [ tailscale ];
|
||||||
|
|
||||||
|
services.tailscale = {
|
||||||
|
enable = true;
|
||||||
|
openFirewall = true;
|
||||||
|
useRoutingFeatures = "both"; # Act as a client and a server (exit node)
|
||||||
|
|
||||||
|
# extraSetFlags = [
|
||||||
|
# "--advertise-exit-node"
|
||||||
|
# ];
|
||||||
|
|
||||||
|
disableUpstreamLogging = true;
|
||||||
|
disableTaildrop = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Enable IP forwarding for subnet routers
|
||||||
|
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||||
|
|
||||||
|
# TODO: this fixes MagicDNS but breaks DNS resolution on LAN (Pihole)
|
||||||
|
# services.resolved.enable = true;
|
||||||
|
}
|
||||||
|
|
||||||
15
pkgs/uptime-kuma.nix
Normal file
15
pkgs/uptime-kuma.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
services.uptime-kuma = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
# NODE_EXTRA_CA_CERTS = "/etc/env/ssl/${domain}.pem";
|
||||||
|
PORT = "4000";
|
||||||
|
HOST = "127.0.0.1";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# networking.firewall.allowedTCPPorts = [ 4000 ];
|
||||||
|
}
|
||||||
|
|
||||||
13
pkgs/virtualisation.nix
Normal file
13
pkgs/virtualisation.nix
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# Auto-prune old containers
|
||||||
|
virtualisation.podman = {
|
||||||
|
enable = true;
|
||||||
|
autoPrune = {
|
||||||
|
enable = true;
|
||||||
|
flags = [ "--all" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
18
scripts/virtualisation/restart-netbird-relay.nix
Normal file
18
scripts/virtualisation/restart-netbird-relay.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
systemd.timers.restart-netbird-relay = {
|
||||||
|
timerConfig = {
|
||||||
|
Unit = "update-containers.service";
|
||||||
|
OnCalendar = "Tue 02:40"; # 10 mins after podman pull
|
||||||
|
};
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
};
|
||||||
|
systemd.services.restart-netbird-relay = {
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${pkgs.systemd}/bin/systemctl try-restart podman-netbird-relay.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
18
scripts/virtualisation/restart-pihole.nix
Normal file
18
scripts/virtualisation/restart-pihole.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
systemd.timers.restart-pihole = {
|
||||||
|
timerConfig = {
|
||||||
|
Unit = "update-containers.service";
|
||||||
|
OnCalendar = "Tue 02:40"; # 10 mins after podman pull
|
||||||
|
};
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
};
|
||||||
|
systemd.services.restart-pihole = {
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${pkgs.systemd}/bin/systemctl try-restart podman-pihole.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
24
scripts/virtualisation/update-containers.nix
Normal file
24
scripts/virtualisation/update-containers.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
systemd.timers.update-containers = {
|
||||||
|
timerConfig = {
|
||||||
|
Unit = "update-containers.service";
|
||||||
|
OnCalendar = "Mon 02:30";
|
||||||
|
};
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
};
|
||||||
|
systemd.services.update-containers = {
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = lib.getExe (pkgs.writeShellScriptBin "update-containers" ''
|
||||||
|
images=$(${pkgs.podman}/bin/podman ps -a --format="{{.Image}}" | sort -u)
|
||||||
|
|
||||||
|
for image in $images; do
|
||||||
|
${pkgs.podman}/bin/podman pull "$image"
|
||||||
|
done
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
38
scripts/zfs-healthcheck/service.nix
Executable file
38
scripts/zfs-healthcheck/service.nix
Executable file
|
|
@ -0,0 +1,38 @@
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
scriptPath = "${config.vars.homeDir}/nixos/scripts";
|
||||||
|
after = [ "network.target" "NetworkManager.service" "uptime-kuma.service" ];
|
||||||
|
environment = {
|
||||||
|
VAR_IP = config.vars.privateIp;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
systemd.services = {
|
||||||
|
"zfs-uptime-kuma" = {
|
||||||
|
inherit environment after;
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = "root";
|
||||||
|
};
|
||||||
|
path = with pkgs; [ bash curl zfs jq ];
|
||||||
|
script = ''
|
||||||
|
bash ${scriptPath}/zfs-healthcheck/uptime-kuma.sh
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.timers = {
|
||||||
|
"zfs-uptime-kuma" = {
|
||||||
|
wantedBy = [ "timers.target" ];
|
||||||
|
partOf = [ "zfs-uptime-kuma.service" ];
|
||||||
|
timerConfig = {
|
||||||
|
Persistent = true; # Execute immediately if missed
|
||||||
|
OnUnitActiveSec = "7m"; # Run every x minutes
|
||||||
|
Unit = "zfs-uptime-kuma.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
29
scripts/zfs-healthcheck/uptime-kuma.sh
Executable file
29
scripts/zfs-healthcheck/uptime-kuma.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#! /bin/sh
|
||||||
|
set -euo pipefail
|
||||||
|
set -x
|
||||||
|
|
||||||
|
push_token=$(< /etc/env/zfs/push-token);
|
||||||
|
|
||||||
|
start_time=$(date -u +%s%3N)
|
||||||
|
health=$(zpool list -H -o health)
|
||||||
|
|
||||||
|
status="up"
|
||||||
|
|
||||||
|
echo "$health" | while IFS= read -r line; do
|
||||||
|
if [ "$line" != "ONLINE" ]; then
|
||||||
|
status="down"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
end_time=$(date -u +%s%3N)
|
||||||
|
duration=$(("$end_time" - "$start_time"))
|
||||||
|
|
||||||
|
msg=$(printf '%s' "$health" | tr '\n' ',' | tr -d "'" | jq -sRr @uri)
|
||||||
|
url="http://$VAR_IP:4000/api/push/$push_token?ping=$duration&status=$status&msg='$msg'"
|
||||||
|
|
||||||
|
output=$(curl --fail --no-progress-meter --retry 1 $url 2>&1)
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Ping failed: $output" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue