init commit

This commit is contained in:
Luka Dekanozishvili 2026-01-19 21:47:18 +00:00
commit 181e6f681e
22 changed files with 940 additions and 0 deletions

34
pkgs/aliases.nix Normal file
View 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
View file

@ -0,0 +1,3 @@
{}:

24
pkgs/common-packages.nix Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,13 @@
{ ... }:
{
# Auto-prune old containers
virtualisation.podman = {
enable = true;
autoPrune = {
enable = true;
flags = [ "--all" ];
};
};
}