feat: deployed frontend, postgres, rev-proxy, tailscale config, initialisation scripts

This commit is contained in:
Luka Dekanozishvili 2026-01-31 14:28:52 +01:00
parent f48059e37e
commit a24fd5bd93
20 changed files with 413 additions and 262 deletions

View file

@ -9,6 +9,7 @@
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
hexname-backend hexname-backend
deno
]; ];
users.users = { users.users = {
@ -21,6 +22,7 @@
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM/4F45h/xkq+MIRDzhHqDm5uWM4KTpYi3Tv/DtSo28t luka@gram" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM/4F45h/xkq+MIRDzhHqDm5uWM4KTpYi3Tv/DtSo28t luka@gram"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF7OvW6MffYFshZyarEaWvWjEmhodn/P+NLcnqbbMpma luka@conway" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF7OvW6MffYFshZyarEaWvWjEmhodn/P+NLcnqbbMpma luka@conway"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIT+vMzh2ngUeqnVJS8Zl1m1HQMBkDOqoGdoARPyJgDM u0_a380@localhost" # s "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIT+vMzh2ngUeqnVJS8Zl1m1HQMBkDOqoGdoARPyJgDM u0_a380@localhost" # s
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAmMXGO8HuFS9SRbIDRDGMVgyiR0hy7uL9VkL5BOcgkw luka@thinkway" # w
]; ];
}; };
root = { root = {

132
flake.lock generated
View file

@ -1,5 +1,24 @@
{ {
"nodes": { "nodes": {
"deno2nix": {
"inputs": {
"flake-utils": "flake-utils_2",
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1767824708,
"narHash": "sha256-UL8Jdh4UoBc25nyq9xkAMnqT1886DzymPbhI4/+/KUQ=",
"owner": "Makesesama",
"repo": "deno2nix",
"rev": "e1b0a16c6148991f3d9e902b68aa1cb532173b8a",
"type": "github"
},
"original": {
"owner": "Makesesama",
"repo": "deno2nix",
"type": "github"
}
},
"fenix": { "fenix": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -41,6 +60,24 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"hexname-backend": { "hexname-backend": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
@ -48,11 +85,11 @@
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1769444495, "lastModified": 1769526729,
"narHash": "sha256-8dC6d0XedjH+3YIxUb9PMzr1WhHzwxi5tsFLTkM0bss=", "narHash": "sha256-/ew2cd+lz9xOkTLsYZLqYKfnOoxqlAhpRR7jAqOUljc=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "5be98605494183210469cf5ad3be211dd0e3f18e", "rev": "59180721d3b95f28c17eff9e3a18e54b0a1fa588",
"revCount": 20, "revCount": 23,
"type": "git", "type": "git",
"url": "ssh://forgejo@git.lukadeka.com:6968/LukaDeka/HexName-Backend.git" "url": "ssh://forgejo@git.lukadeka.com:6968/LukaDeka/HexName-Backend.git"
}, },
@ -61,6 +98,25 @@
"url": "ssh://forgejo@git.lukadeka.com:6968/LukaDeka/HexName-Backend.git" "url": "ssh://forgejo@git.lukadeka.com:6968/LukaDeka/HexName-Backend.git"
} }
}, },
"hexname-frontend": {
"inputs": {
"deno2nix": "deno2nix",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1769709099,
"narHash": "sha256-Y5uALgdWMSRp8rqFFiv+iQCcs/HLoYvl0KxFaqNqlGE=",
"ref": "refs/heads/main",
"rev": "bbbb5e9f5ba8f5c18415b4ca5c03d57e45937095",
"revCount": 18,
"type": "git",
"url": "ssh://forgejo@git.lukadeka.com:6968/LukaDeka/HexName-Frontend.git"
},
"original": {
"type": "git",
"url": "ssh://forgejo@git.lukadeka.com:6968/LukaDeka/HexName-Frontend.git"
}
},
"naersk": { "naersk": {
"inputs": { "inputs": {
"fenix": "fenix", "fenix": "fenix",
@ -85,11 +141,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1769330179, "lastModified": 1769527094,
"narHash": "sha256-yxgb4AmkVHY5OOBrC79Vv6EVd4QZEotqv+6jcvA212M=", "narHash": "sha256-xV20Alb7ZGN7qujnsi5lG1NckSUmpIb05H2Xar73TDc=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "48698d12cc10555a4f3e3222d9c669b884a49dfe", "rev": "afce96367b2e37fc29afb5543573cd49db3357b7",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -101,11 +157,11 @@
}, },
"nixpkgs-stable": { "nixpkgs-stable": {
"locked": { "locked": {
"lastModified": 1769318308, "lastModified": 1769598131,
"narHash": "sha256-Mjx6p96Pkefks3+aA+72lu1xVehb6mv2yTUUqmSet6Q=", "narHash": "sha256-e7VO/kGLgRMbWtpBqdWl0uFg8Y2XWFMdz0uUJvlML8o=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "1cd347bf3355fce6c64ab37d3967b4a2cb4b878c", "rev": "fa83fd837f3098e3e678e6cf017b2b36102c7211",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -117,11 +173,43 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1769170682, "lastModified": 1767767207,
"narHash": "sha256-oMmN1lVQU0F0W2k6OI3bgdzp2YOHWYUAw79qzDSjenU=", "narHash": "sha256-Mj3d3PfwltLmukFal5i3fFt27L6NiKXdBezC1EBuZs4=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "c5296fdd05cfa2c187990dd909864da9658df755", "rev": "5912c1772a44e31bf1c63c0390b90501e5026886",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1769461804,
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1769461804,
"narHash": "sha256-msG8SU5WsBUfVVa/9RPLaymvi5bI8edTavbIq3vRlhI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -134,7 +222,8 @@
"root": { "root": {
"inputs": { "inputs": {
"hexname-backend": "hexname-backend", "hexname-backend": "hexname-backend",
"nixpkgs": "nixpkgs_2", "hexname-frontend": "hexname-frontend",
"nixpkgs": "nixpkgs_4",
"nixpkgs-stable": "nixpkgs-stable" "nixpkgs-stable": "nixpkgs-stable"
} }
}, },
@ -169,6 +258,21 @@
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View file

@ -6,13 +6,14 @@
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.11"; nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-25.11";
hexname-backend.url = "git+ssh://forgejo@git.lukadeka.com:6968/LukaDeka/HexName-Backend.git"; hexname-backend.url = "git+ssh://forgejo@git.lukadeka.com:6968/LukaDeka/HexName-Backend.git";
hexname-frontend.url = "git+ssh://forgejo@git.lukadeka.com:6968/LukaDeka/HexName-Frontend.git";
}; };
outputs = { nixpkgs, nixpkgs-stable, hexname-backend, ... } @ inputs: { outputs = { nixpkgs, nixpkgs-stable, hexname-backend, hexname-frontend, ... } @ inputs: {
nixosConfigurations = { nixosConfigurations = {
hexname-ns1 = nixpkgs.lib.nixosSystem { hexname-ns1 = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; system = "x86_64-linux";
specialArgs = { inherit inputs; inherit hexname-backend; }; specialArgs = { inherit inputs hexname-backend hexname-frontend; };
modules = [ modules = [
######## Boilerplate ######## ######## Boilerplate ########
./configuration.nix ./configuration.nix
@ -21,15 +22,18 @@
./pkgs/extra.nix ./pkgs/extra.nix
######## HexName configuration ######## ######## HexName configuration ########
./pkgs/powerdns.nix # ./pkgs/reverse-proxy.nix
./pkgs/nginx.nix
./pkgs/backend.nix ./pkgs/backend.nix
# ./pkgs/postgres.nix
./pkgs/powerdns.nix
./pkgs/postgres.nix
######## Networking ######## ######## Networking ########
./pkgs/server-ssh.nix ./pkgs/ssh.nix
./pkgs/nginx.nix ./pkgs/tailscale.nix
./pkgs/virtualisation.nix ./pkgs/virtualisation.nix
# ./pkgs/stalwart.nix ./pkgs/stalwart.nix
######## Sysadmin ######## ######## Sysadmin ########
./pkgs/neovim.nix ./pkgs/neovim.nix
@ -39,8 +43,8 @@
######## etc. ######## ######## etc. ########
######## Scripts ######## ######## Scripts ########
#./scripts/virtualisation/update-containers.nix # Runs podman pull weekly ./services/update-containers.nix # Runs podman pull weekly
#./scripts/virtualisation/restart-pihole.nix ./services/restart-powerdns.nix
]; ];
}; };
}; };

View file

@ -27,6 +27,7 @@
User = "hexname-backend"; User = "hexname-backend";
Group = "hexname"; Group = "hexname";
Type = "simple"; Type = "simple";
Restart = "always";
ExecStart = "${pkgs.hexname-backend}/bin/dns-backend"; ExecStart = "${pkgs.hexname-backend}/bin/dns-backend";
}; };
}; };

View file

@ -44,7 +44,7 @@
gc = { gc = {
automatic = true; automatic = true;
dates = "Mon *-*-* 16:00:00"; dates = "Sat *-*-* 10:00:00";
options = "--delete-older-than 90d"; options = "--delete-older-than 90d";
}; };
}; };

42
pkgs/frontend.nix Normal file
View file

@ -0,0 +1,42 @@
{ pkgs, hexname-frontend, ... }:
{
nixpkgs.overlays = [
(self: super: {
hexname-frontend = hexname-frontend.packages.${super.stdenv.hostPlatform.system}.default;
})
];
users.groups.hexname = {};
users.users = {
hexname-frontend = {
group = "hexname";
isSystemUser = true;
createHome = true;
home = "/var/lib/hexname/frontend";
};
};
systemd.services.hexname-frontend = {
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
environment = {
ENV_PATH = "/etc/env/hexname/frontend.env";
NODE_ENV = "production";
};
serviceConfig = {
User = "hexname-backend";
Group = "hexname";
Type = "simple";
Restart = "always";
# ExecStart = "${hexname-frontend.packages.${pkgs.system}.default}/bin/hexname-frontend";
ExecStart = "env $(cat $ENV_PATH | xargs) ${pkgs.hexname-frontend}/bin/hexname-frontend";
};
# script = ''
# source /etc/env/hexname/frontend.env
# ${hexname-frontend.packages.${pkgs.system}.default}/bin/hexname-frontend
# '';
};
}

View file

@ -3,30 +3,49 @@
{ {
services.postgresql = { services.postgresql = {
enable = true; enable = true;
settings = {
# Set hashing algorithm
password_encryption = "scram-sha-256";
# settings = { # Replication conf
# listen_addresses = lib.mkForce "127.0.0.1,10.89.0.10"; wal_level = "logical";
# }; max_wal_senders = "5";
max_replication_slots = "5";
};
# Allow root to log in as postgres in the DB (for the PowerDNS container) # Allow root to log in as postgres in the DB (for the PowerDNS container)
identMap = '' # identMap = ''
postgres root postgres # postgres root postgres
''; # '';
authentication = lib.mkForce '' # authentication = lib.mkForce ''
# TYPE DATABASE USER ADDRESS AUTH-METHOD [auth-options] # # TYPE DATABASE USER ADDRESS AUTH-METHOD [auth-options]
host hexname-backend hexname-backend 127.0.0.1/24 scram-sha-256 # # host hexname-backend hexname-backend 127.0.0.1/24 scram-sha-256
# host all powerdns-user 127.0.0.1/24 scram-sha-256 # # host postgres postgres 127.0.0.1/24 scram-sha-256
# local all root trust # host all all 127.0.0.1/24 scram-sha-256
''; # # host all powerdns-user 127.0.0.1/24 scram-sha-256
# # local all root trust
ensureUsers = [ { name = "hexname-backend"; } ]; # '';
# No need to define the DB since `diesel` creates everything
# This password is only the initial one - don't get too excited # This password is only the initial one - don't get too excited
initialScript = pkgs.writeText "set-initial-password-script" '' initialScript = "/etc/env/hexname/init-db-dns.sql";
alter user hexname-backend with password 'shuaze-gagyof'; # initialScript = pkgs.writeText "set-initial-password-script" ''
''; # CREATE USER powerdns WITH PASSWORD 'shuaze-gagyof';
# CREATE USER hexname_backend WITH PASSWORD 'shuaze-gagyof2' CREATEDB;
# CREATE USER replication_user SUPERUSER WITH PASSWORD 'shuaze-gagyof3';
# CREATE DATABASE powerdns OWNER "powerdns";
# CREATE DATABASE hexname_backend OWNER "hexname-backend";
# CREATE DATABASE hexname_backend OWNER "hexname-backend";
# \c powerdns;
# CREATE PUBLICATION powerdns_pub FOR ALL TABLES;
# CREATE SUBSCRIPTION hexname_ns2
# CONNECTION 'host=publisher_ip_address port=5432 user=replication_user password=' ' dbname=powerdns'
# PUBLICATION powerdns_pub;
# '';
# ALTER USER "postgres" WITH PASSWORD 'shuaze-gagyof';
}; };
} }

View file

@ -2,39 +2,17 @@
let let
domain = "hexname.com"; domain = "hexname.com";
dbIp = "10.89.0.25"; # pdnsIp = "10.89.0.53";
pdnsIp = "10.89.0.53";
in in
{ {
virtualisation.oci-containers.containers = { virtualisation.oci-containers.containers = {
hexname-postgres = {
hostname = "pgsql";
image = "postgres:18-alpine";
ports = [
"127.0.0.1:5432:5432"
];
volumes = [
"pgsql:/var/lib/postgresql/data:Z"
"/etc/localtime:/etc/localtime:ro"
];
environment = {
POSTGRES_USER = "hexname-backend";
POSTGRES_PASSWORD = "EZQVObWjoEM7bldX2wu5oyJkgBIMfoU8OZZf";
};
networks = [ "hexname-net" ];
extraOptions = [
"--dns=10.89.0.1"
"--ip=${dbIp}"
];
};
hexname-powerdns = { hexname-powerdns = {
image = "pschiffe/pdns-pgsql:latest"; image = "pschiffe/pdns-pgsql:latest";
hostname = "ns1.${domain}"; hostname = "ns1.${domain}";
ports = [ # ports = [
"127.0.0.1:8081:8081/tcp" # "127.0.0.1:8081:8081/tcp"
]; # ];
networks = [ "hexname-net" ]; # networks = [ "hexname-net" ];
volumes = [ volumes = [
"/etc/localtime:/etc/localtime:ro" "/etc/localtime:/etc/localtime:ro"
@ -46,62 +24,60 @@ in
PDNS_api = "yes"; PDNS_api = "yes";
PDNS_disable_axfr = "yes"; PDNS_disable_axfr = "yes";
#PDNS_webserver = "yes"; #PDNS_webserver = "yes";
PDNS_webserver_address = "0.0.0.0"; PDNS_webserver_address = "127.0.0.1";
PDNS_webserver_port = "8081"; PDNS_webserver_port = "8081";
PDNS_local_address = "${pdnsIp}:53"; PDNS_local_address = "0.0.0.0:53";
PDNS_webserver_allow_from = "10.89.0.0/24"; PDNS_webserver_allow_from = "127.0.0.1/24";
PDNS_version_string = "anonymous"; PDNS_version_string = "anonymous";
PDNS_default_ttl = "3600"; PDNS_default_ttl = "3600";
# PDNS_gpgsql_password=... # PDNS_gpgsql_password=...
# PDNS_api_key=... # PDNS_api_key=...
# PDNS_gpgsql_host = "127.0.0.1"; PDNS_gpgsql_host = "127.0.0.1";
# PDNS_gpgsql_port = "5432"; PDNS_gpgsql_port = "5432";
# PDNS_gpgsql_dbname = "powerdns"; PDNS_gpgsql_dbname = "powerdns";
# PDNS_gpgsql_user = "postgres"; PDNS_gpgsql_user = "powerdns";
# PDNS_gpgsql_password = "powerdns"; PDNS_gpgsql_dnssec = "yes";
}; };
extraOptions = [ extraOptions = [
# "--network=host" "--network=host"
"--ip=${pdnsIp}"
"--add-host=pgsql:${dbIp}"
]; ];
dependsOn = [ "hexname-postgres" ]; # dependsOn = [ "hexname-postgres" ];
}; };
}; };
systemd.services.podman-network-hexname = { # systemd.services.podman-network-hexname = {
description = "Podman network for HexName/PowerDNS"; # description = "Podman network for HexName/PowerDNS";
after = [ "podman.service" ]; # after = [ "podman.service" ];
wantedBy = [ "multi-user.target" "podman-hexname-postgres.target" "podman-hexname-powerdns.target" ]; # wantedBy = [ "multi-user.target" "podman-hexname-postgres.target" "podman-hexname-powerdns.target" ];
serviceConfig.Type = "oneshot"; # serviceConfig.Type = "oneshot";
path = [ pkgs.podman ] ; # path = [ pkgs.podman ] ;
script = '' # script = ''
podman network inspect hexname-net >/dev/null 2>&1 || \ # podman network inspect hexname-net >/dev/null 2>&1 || \
podman network create hexname-net --subnet 10.89.0.0/24 # podman network create hexname-net --subnet 10.89.0.0/24
''; # '';
}; # };
# Bind port 53 and send all requests to the container # Bind port 53 and send all requests to the container
networking.nftables.enable = true; # networking.nftables.enable = true;
networking.nftables.tables.dns = { # networking.nftables.tables.dns = {
family = "inet"; # family = "inet";
content = '' # content = ''
chain prerouting { # chain prerouting {
type nat hook prerouting priority -100; # type nat hook prerouting priority -100;
udp dport 53 dnat ip to ${pdnsIp}:53 # udp dport 53 dnat ip to ${pdnsIp}:53
tcp dport 53 dnat ip to ${pdnsIp}:53 # tcp dport 53 dnat ip to ${pdnsIp}:53
} # }
chain postrouting { # chain postrouting {
type nat hook postrouting priority 100; # type nat hook postrouting priority 100;
ip saddr 10.89.0.0/24 masquerade # ip saddr 10.89.0.0/24 masquerade
} # }
''; # '';
}; # };
networking.firewall.allowedTCPPorts = [ 53 ]; networking.firewall.allowedTCPPorts = [ 53 ];
networking.firewall.allowedUDPPorts = [ 53 ]; networking.firewall.allowedUDPPorts = [ 53 ];

33
pkgs/reverse-proxy.nix Normal file
View file

@ -0,0 +1,33 @@
{ config, pkgs, lib, ... }:
let
domain = "hexname.com";
in
{
services.nginx.virtualHosts = {
"${domain}" = {
forceSSL = true;
enableACME = true;
locations."/api" = {
proxyPass = "http://127.0.0.1:8080";
proxyWebsockets = true;
extraConfig = ''
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# add_header Strict-Transport-Security 'max-age=300; includeSubDomains; preload; always;'
'';
};
locations."/" = {
root = "/var/www/hexname/build";
tryFiles = "$uri $uri.html $uri/ /200.html";
};
};
"www.${domain}" = { # Redirect www to root
forceSSL = true;
enableACME = true;
globalRedirect = domain;
};
};
}

View file

@ -8,7 +8,7 @@
settings = { settings = {
PasswordAuthentication = false; PasswordAuthentication = false;
AllowUsers = [ "luka" ]; AllowUsers = [ "luka" ];
UseDns = false; # Disable checking of rDNS records to speed up login UseDns = false; # Disable checking of rDNS records
X11Forwarding = false; X11Forwarding = false;
PermitRootLogin = "prohibit-password"; PermitRootLogin = "prohibit-password";
}; };
@ -20,11 +20,6 @@
# ignoreIP = [ ]; # ignoreIP = [ ];
}; };
networking.firewall = { networking.firewall.enable = true;
enable = true;
# allowedTCPPorts = [ ];
# allowedUDPPorts = [];
};
} }

View file

@ -2,7 +2,7 @@
let let
domain = "hexname.com"; domain = "hexname.com";
stalwartDomain = "mail.${domain}"; stalwartDomain = "mx.${domain}";
roundcubeDomain = "email.${domain}"; roundcubeDomain = "email.${domain}";
dataDir = "/var/lib/stalwart-mail"; dataDir = "/var/lib/stalwart-mail";
@ -59,22 +59,22 @@ in
principals = [ principals = [
{ {
name = "contact-us@${domain}"; name = "contact-us@${domain}";
email = [ "contact-us@${domain}" "@${domain}" ]; email = [ "contact-us@${domain}" "privacy@${domain}" ];
secret = "$6$E8AhTdIdgl2ag6/x$reYvoPByjvkPK/Uwm3/481BBBkuBKQxd3rgSgQw3PawJ4G8TOt0jlIXdOo5xuDv1DQAdn52lUAgx0U9GSVoc7/"; secret = "$6$iyUwAnKuGTz31jeu$QPfoaUQPccVDWjCWs4PY43dBI6oG4eNb7buNlGBlnNJrvQOePYKyF8RXN8FI5H6y2x191kOa4U8aDD4K/ssKn/";
class = "individual"; class = "individual";
} }
{ {
name = "no-reply@${domain}"; name = "no-reply@${domain}";
email = [ "no-reply@${domain}" ]; email = [ "no-reply@${domain}" ];
secret = "$6$V/u1LImVZAyynuLO$l/mMaLWQ5t0jWz6XWNlHcha8nuTQbjQfES.Nj73mNS0xQjv3vu5z03fLMAt3hxAb5BwE3jgtfmh.PknBjM1M//"; secret = "$6$FpTIF6mjoBRXyZAO$9lqf/u3NyJNHYNutFY0WmPkbfkq8J.SIkhzya3izl7AbCRE72TlyKeGx/OOyPuI1QTMV10NgOEGzL8jboOWhZ1";
class = "individual"; class = "individual";
} }
]; ];
}; };
authentication.fallback-admin = { authentication.fallback-admin = {
user = "superdupermegaadmin"; user = "unguessable-username";
secret = "$6$LPDx0LFqtpAVJO2s$GPR/4Rguhmspy8OLLKI2oZxVgvWrlHRckd6WN2RZNMxkSN9YMiPJ/pfq.XD/VTKsqCu2GCnzerQOv5bivBCph."; secret = "$6$1sRTqTbiXuGNE3zt$oLcXi.kPsy72W5SDMwWSitpJyKlZSKSzhr1QO3DBn6Q9LSE.YpWUbT2Thu5Kbs0bmTMvqAPFI7x/qa1wm9Bj91";
}; };
email.folders = let email.folders = let
@ -92,28 +92,6 @@ in
trash = mkFolder "Trash"; 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 # Change the DNS records manually to these addresses to
# keep postmaster free for non-automated emails # keep postmaster free for non-automated emails
# https://github.com/stalwartlabs/mail-server/discussions/877 # https://github.com/stalwartlabs/mail-server/discussions/877
@ -164,7 +142,7 @@ in
storage.blob = "fs"; storage.blob = "fs";
# We have DANE and don't want a certificate for each domain # We have DANE and don't want a certificate for each domain
# session.mta-sts.mode = "none"; session.mta-sts.mode = "none";
certificate.default = { certificate.default = {
cert = "%{file:${credPath}/cert.pem}%"; cert = "%{file:${credPath}/cert.pem}%";
@ -249,24 +227,9 @@ in
security.acme.certs.${stalwartDomain} = { security.acme.certs.${stalwartDomain} = {
# Keep a stable private key for TLSA records (DANE) # 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 # 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" ]; extraLegoRenewFlags = [ "--reuse-key" ];
# Restart Stalwart to apply new certificates # Restart Stalwart to apply new certificates
reloadServices = [ "stalwart-mail.service" ]; 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"
# ];
# };
} }

View file

@ -1,23 +1,19 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
environment.systemPackages = with pkgs; [ tailscale ];
services.tailscale = { services.tailscale = {
enable = true; enable = true;
openFirewall = true; openFirewall = true;
useRoutingFeatures = "both"; # Act as a client and a server (exit node) useRoutingFeatures = "client";
# extraSetFlags = [
# "--advertise-exit-node"
# ];
disableUpstreamLogging = true; disableUpstreamLogging = true;
disableTaildrop = true; disableTaildrop = true;
}; };
environment.systemPackages = with pkgs; [ tailscale ];
# Enable IP forwarding for subnet routers # Enable IP forwarding for subnet routers
boot.kernel.sysctl."net.ipv4.ip_forward" = 1; # boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
# TODO: this fixes MagicDNS but breaks DNS resolution on LAN (Pihole) # TODO: this fixes MagicDNS but breaks DNS resolution on LAN (Pihole)
# services.resolved.enable = true; # services.resolved.enable = true;

View file

@ -4,11 +4,6 @@
virtualisation.podman = { virtualisation.podman = {
enable = true; enable = true;
# Disable netavark
# defaultNetwork.settings = {
# dns_enabled = false;
# };
# Auto-prune old containers # Auto-prune old containers
autoPrune = { autoPrune = {
enable = true; enable = true;

10
scripts/deploy-frontend.sh Executable file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
cd /home/luka/HexName-Frontend
git pull
deno task build
sudo rm -rf /var/www/hexname/build
sudo cp -r build/ /var/www/hexname/

96
scripts/pdns-init-config-dns.sh Executable file
View file

@ -0,0 +1,96 @@
#!/usr/bin/env bash
set -euo pipefail
API_URL="http://127.0.0.1:8081/api/v1/servers/localhost/zones/hexname.com."
API_KEY="${API_KEY:?API_KEY not set}"
declare -A RRSETS
add_record() {
local type="$1"
local ttl="$2"
local name="$3"
local content="$4"
local key="${name}|${type}|${ttl}"
RRSETS["$key"]+="${content}"$'\n'
}
flush_rrsets() {
local rrsets_json="[]"
for key in "${!RRSETS[@]}"; do
IFS='|' read -r name type ttl <<<"$key"
local records_json
records_json=$(printf '%s' "${RRSETS[$key]}" \
| sed '/^$/d' \
| jq -R '{content: ., disabled: false}' \
| jq -s '.')
rrsets_json=$(jq \
--arg name "$name" \
--arg type "$type" \
--argjson ttl "$ttl" \
--argjson records "$records_json" \
'. + [{
name: $name,
type: $type,
ttl: $ttl,
changetype: "REPLACE",
records: $records
}]' <<<"$rrsets_json")
done
jq -n --argjson rrsets "$rrsets_json" '{ rrsets: $rrsets }' \
| curl -sS -X PATCH \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- \
"$API_URL"
}
add_record "NS" 604800 "hexname.com." "ns1.hexname.com."
add_record "NS" 604800 "hexname.com." "ns2.hexname.com."
add_record "A" 604800 "hexname.com." "188.245.239.209"
add_record "A" 604800 "ns1.hexname.com." "188.245.239.209"
add_record "A" 604800 "ns2.hexname.com." "91.99.69.65"
# Mailserver records
add_record "A" 3600 "mx.hexname.com." "188.245.239.209"
add_record "A" 3600 "email.hexname.com." "188.245.239.209"
add_record "A" 3600 "mta-sts.hexname.com." "188.245.239.209"
add_record "CNAME" 3600 "mail.hexname.com." "mx.hexname.com."
add_record "MX" 3600 "hexname.com." "10 mx.hexname.com."
add_record "TXT" 3600 "_mta-sts.hexname.com." "\"v=STSv1; id=20260127182600Z;\""
add_record "TXT" 3600 "202601e._domainkey.hexname.com." "\"v=DKIM1; k=ed25519; h=sha256; p=C30gZd1CbkUpIGInw/wZgZQD0pmUEnwTp+svCLm1oCk=\""
add_record "TXT" 3600 "202601r._domainkey.hexname.com." "\"v=DKIM1; k=rsa; h=sha256; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnyQRHisJtMpgRCAKAE5mfq63n1hvguiNheRrGWcLjEziA9r3M8oaxM71gDNeDEZj19yXlYBWlZZiPsdkMNsumFaElTt3E810JjZxvWslvRgCQ9qMK6lE4ytJZHXJD1a+g82/j4Pfu3C0iz0GfMvngXf03pDl5jWeScwfSFgvKx/0tRdzCAzwkSZfZaSKCh5bcvVwoxzXqHjz3zxwxDJGlUPoERymd18/7NkdRRfJZoqAo/aHdsh5JsYa8APtNIHjSjp2vUBPQnNrtx9+lI0qRnwdyrim8v8CRKin+QUW0sstWGuyqZxgxOGXO2Ek2fqTrpzVu2fu6pzGqJdbTVf5BQIDAQAB\""
add_record "TXT" 3600 "mx.hexname.com." "\"v=spf1 a ra=spf-reports -all\""
add_record "TXT" 3600 "hexname.com." "\"v=spf1 mx ra=spf-reports -all\""
add_record "TXT" 3600 "_dmarc.hexname.com." "\"v=DMARC1; p=reject; rua=mailto:dmarc-reports@hexname.com; ruf=mailto:dmarc-reports@hexname.com\""
add_record "TXT" 3600 "_smtp._tls.hexname.com." "\"v=TLSRPTv1; rua=mailto:tls-reports@hexname.com\""
add_record "SRV" 3600 "_imaps._tcp.hexname.com." "0 1 993 mx.hexname.com."
add_record "SRV" 3600 "_imap._tcp.hexname.com." "0 1 143 mx.hexname.com."
add_record "SRV" 3600 "_submissions._tcp.hexname.com." "0 1 465 mx.hexname.com."
add_record "SRV" 3600 "_submission._tcp.hexname.com." "0 1 587 mx.hexname.com."
add_record "TLSA" 3600 "_25._tcp.mx.hexname.com." "3 1 1 88151fff33b6c5b820d83b3030d55376f57085a154652a27089d9d9a71fe6a7e"
# Create the zone
curl -d '{
"name": "hexname.com.",
"kind": "Native",
"masters": [],
"nameservers": [
"ns1.hexname.com.",
"ns2.hexname.com."
]
}' -X POST -H "X-API-Key: $API_KEY" http://127.0.0.1:8081/api/v1/servers/localhost/zones
flush_rrsets
# Secure/update DDNSEC
sudo podman exec -it hexname-powerdns pdnsutil zone secure hexname.com

View file

@ -1,18 +0,0 @@
{ 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";
};
};
}

View file

@ -1,38 +0,0 @@
{ 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";
};
};
};
}

View file

@ -1,29 +0,0 @@
#! /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

View file

@ -1,17 +1,17 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
systemd.timers.restart-pihole = { systemd.timers.restart-powerdns = {
timerConfig = { timerConfig = {
Unit = "update-containers.service"; Unit = "update-containers.service";
OnCalendar = "Tue 02:40"; # 10 mins after podman pull OnCalendar = "Sat 10:51"; # 10 mins after podman pull
}; };
wantedBy = [ "timers.target" ]; wantedBy = [ "timers.target" ];
}; };
systemd.services.restart-pihole = { systemd.services.restart-powerdns = {
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
ExecStart = "${pkgs.systemd}/bin/systemctl try-restart podman-pihole.service"; ExecStart = "${pkgs.systemd}/bin/systemctl try-restart podman-powerdns.service";
}; };
}; };
} }

View file

@ -4,7 +4,7 @@
systemd.timers.update-containers = { systemd.timers.update-containers = {
timerConfig = { timerConfig = {
Unit = "update-containers.service"; Unit = "update-containers.service";
OnCalendar = "Mon 02:30"; OnCalendar = "Sat 10:41";
}; };
wantedBy = [ "timers.target" ]; wantedBy = [ "timers.target" ];
}; };