Goodbye containers.

This commit is contained in:
Dmitry Voronin 2024-12-02 21:12:45 +03:00
parent f1ddca8b18
commit 07cfed44d3
Signed by: voronind
SSH key fingerprint: SHA256:3kBb4iV2ahufEBNq+vFbUe4QYfHt98DHQjN7QaptY9k
112 changed files with 1716 additions and 3614 deletions

View file

@ -1,52 +0,0 @@
{
config,
container,
lib,
...
}: let
cfg = config.container.module.change;
in {
options.container.module.change = {
enable = lib.mkEnableOption "the change detection service";
address = lib.mkOption {
default = "10.1.0.41";
type = lib.types.str;
};
port = lib.mkOption {
default = 5000;
type = lib.types.int;
};
domain = lib.mkOption {
default = "change.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/change";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.change = container.mkContainer cfg {
bindMounts = {
"/var/lib/changedetection-io" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
services.changedetection-io = {
enable = true;
baseURL = cfg.domain;
behindProxy = true;
listenAddress = cfg.address;
};
};
};
};
}

View file

@ -1,72 +0,0 @@
{
config,
container,
lib,
pkgs,
...
}: let
cfg = config.container.module.chat;
db = config.container.module.postgres;
in {
options.container.module.chat = {
enable = lib.mkEnableOption "the chat container.";
address = lib.mkOption {
default = "10.1.0.20";
type = lib.types.str;
};
port = lib.mkOption {
default = 8065;
type = lib.types.int;
};
domain = lib.mkOption {
default = "chat.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/chat";
type = lib.types.str;
};
};
# WIP: https://search.nixos.org/options?channel=24.05&from=0&size=50&sort=relevance&type=packages&query=mattermost
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.chat = container.mkContainer cfg {
bindMounts = {
"/var/lib/mattermost" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
services.mattermost = {
enable = true;
listenAddress = ":${toString cfg.port}";
localDatabaseCreate = false;
mutableConfig = false;
package = pkgs.mattermost;
siteName = "Chat";
siteUrl = "https://${cfg.domain}";
statePath = "/var/lib/mattermost";
plugins = [
(pkgs.fetchurl rec {
hash = "sha256-yQGBpBPgXxC+Pm6dHlbwlNEdvn6wg9neSpNNTC4YYAA=";
url = "https://github.com/mattermost/mattermost-plugin-calls/releases/download/v${version}/mattermost-plugin-calls-v${version}.tar.gz";
version = "1.2.0";
})
];
extraConfig = {
SqlSettings = {
DataSource = "postgres://mattermost:any@${db.address}:${toString db.port}/mattermost?sslmode=disable&connect_timeout=10";
DriverName = "postgres";
};
};
};
};
};
};
}

View file

@ -1,104 +0,0 @@
{
config,
container,
lib,
pkgs,
...
}: let
cfg = config.container.module.cloud;
postgres = config.container.module.postgres;
proxy = config.container.module.proxy;
in {
options.container.module.cloud = {
enable = lib.mkEnableOption "the file cloud service.";
address = lib.mkOption {
default = "10.1.0.13";
type = lib.types.str;
};
port = lib.mkOption {
default = 80;
type = lib.types.int;
};
domain = lib.mkOption {
default = "cloud.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/cloud";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.cloud = container.mkContainer cfg {
bindMounts = {
"/var/lib/nextcloud" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { config, ... }: container.mkContainerConfig cfg {
services.nextcloud = {
enable = true;
hostName = cfg.domain;
# package = pkgs.nextcloud29;
# phpOptions = {
# memory_limit = lib.mkForce "20G";
# };
config = {
adminpassFile = "${pkgs.writeText "NextcloudPassword" "root"}";
adminuser = "root";
dbhost = postgres.address;
dbname = "nextcloud";
dbpassFile = "${pkgs.writeText "NextcloudDbPassword" "nextcloud"}";
dbtype = "pgsql";
dbuser = "nextcloud";
};
extraApps = {
inherit (config.services.nextcloud.package.packages.apps)
contacts calendar onlyoffice;
};
extraAppsEnable = true;
settings = {
allow_local_remote_servers = true;
trusted_domains = [
cfg.address
cfg.domain
];
trusted_proxies = [
proxy.address
];
};
};
# HACK: This is required for TCP postgres connection.
systemd = {
services = {
nextcloud-setup = {
serviceConfig.PrivateNetwork = lib.mkForce false;
wantedBy = lib.mkForce [ ];
};
nextcloud-update-db = {
serviceConfig.PrivateNetwork = lib.mkForce false;
wantedBy = lib.mkForce [ ];
};
};
timers.fixsystemd = {
timerConfig = {
OnBootSec = 5;
Unit = "nextcloud-setup.service";
};
wantedBy = [
"timers.target"
];
};
};
};
};
};
}

View file

@ -1,127 +0,0 @@
{
config,
container,
lib,
pkgs,
...
}: let
cfg = config.container.module.dns;
in {
options.container.module.dns = {
enable = lib.mkEnableOption "the DNS server.";
address = lib.mkOption {
default = "10.1.0.6";
type = lib.types.str;
};
port = lib.mkOption {
default = 53;
type = lib.types.int;
};
};
config = lib.mkIf cfg.enable {
containers.dns = container.mkContainer cfg {
config = { ... }: container.mkContainerConfig cfg {
services.blocky = {
enable = true;
# REF: https://0xerr0r.github.io/blocky/main/configuration/
settings = {
bootstrapDns = "tcp+udp:1.1.1.1";
connectIPVersion = "v4";
ports.dns = cfg.port;
# httpPort = "80";
upstreams.groups = {
default = [
"https://dns.quad9.net/dns-query"
];
};
caching = {
maxItemsCount = 100000;
maxTime = "30m";
minTime = "5m";
prefetchExpires = "2h";
prefetchMaxItemsCount = 100000;
prefetchThreshold = 5;
prefetching = true;
};
blocking = {
blockTTL = "1m";
blockType = "zeroIP";
loading = {
refreshPeriod = "24h";
strategy = "blocking";
downloads = {
attempts = 3;
cooldown = "10s";
timeout = "5m";
};
};
# SRC: https://oisd.nl
# SRC: https://v.firebog.net
denylists = {
suspicious = [
"https://raw.githubusercontent.com/PolishFiltersTeam/KADhosts/master/KADhosts.txt"
"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" # https://github.com/StevenBlack/hosts
"https://v.firebog.net/hosts/static/w3kbl.txt"
];
ads = [
"https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext"
"https://raw.githubusercontent.com/bigdargon/hostsVN/master/hosts"
"https://v.firebog.net/hosts/AdguardDNS.txt"
"https://v.firebog.net/hosts/Admiral.txt"
"https://v.firebog.net/hosts/Easylist.txt"
];
tracking = [
"https://hostfiles.frogeye.fr/firstparty-trackers-hosts.txt"
"https://v.firebog.net/hosts/Easyprivacy.txt"
"https://v.firebog.net/hosts/Prigent-Ads.txt"
];
malicious = [
"https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-malware.txt"
"https://osint.digitalside.it/Threat-Intel/lists/latestdomains.txt"
"https://phishing.army/download/phishing_army_blocklist_extended.txt"
"https://raw.githubusercontent.com/AssoEchap/stalkerware-indicators/master/generated/hosts"
"https://raw.githubusercontent.com/Spam404/lists/master/main-blacklist.txt"
"https://urlhaus.abuse.ch/downloads/hostfile/"
"https://v.firebog.net/hosts/Prigent-Crypto.txt"
"https://v.firebog.net/hosts/Prigent-Malware.txt"
];
other = [
"https://big.oisd.nl/domainswild"
"https://zerodot1.gitlab.io/CoinBlockerLists/hosts_browser"
];
};
# allowlists = {
# other = [
# "/.*.vk.com/"
# ];
# };
clientGroupsBlock = {
default = [
"ads"
"malicious"
"other"
"suspicious"
"tracking"
];
};
};
customDNS = {
mapping = let
block = host: { ${host} = "0.0.0.0"; };
in {
# All subdomains to current host.
# ${config.container.domain} = config.container.host;
"voronind.com" = "10.0.0.1,fd09:8d46:b26::1";
}
// block "gosuslugi.ru"
// block "rutube.ru"
// block "vk.com"
;
};
};
};
};
};
};
}

View file

@ -1,63 +0,0 @@
{
config,
container,
lib,
...
}: let
cfg = config.container.module.download;
in {
options.container.module.download = {
enable = lib.mkEnableOption "the bit-torrent downloader.";
address = lib.mkOption {
default = "10.1.0.12";
type = lib.types.str;
};
port = lib.mkOption {
default = 8112;
type = lib.types.int;
};
domain = lib.mkOption {
default = "download.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/download";
type = lib.types.str;
};
memLimit = lib.mkOption {
default = "4G";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.download = container.mkContainer cfg {
enableTun = true;
bindMounts = {
"/var/lib/deluge/.config/deluge" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
}
// container.attachMedia "download" false
;
config = { ... }: container.mkContainerConfig cfg {
services.deluge = {
enable = true;
dataDir = "/var/lib/deluge";
web.enable = true;
};
systemd.services.deluged.serviceConfig = {
MemoryLimit = cfg.memLimit;
Restart = lib.mkForce "always";
RuntimeMaxSec = "3h";
};
};
};
};
}

View file

@ -1,124 +0,0 @@
{
__findFile,
config,
container,
inputs,
lib,
pkgs,
pkgsMaster,
util,
...
} @args: let
cfg = config.container.module.frkn;
in {
options.container.module.frkn = {
enable = lib.mkEnableOption "the Allmighty FRKN service.";
address = lib.mkOption {
default = "10.1.0.69";
type = lib.types.str;
};
port = lib.mkOption {
default = 1080;
type = lib.types.int;
};
torport = lib.mkOption {
default = 9150;
type = lib.types.int;
};
xrayport = lib.mkOption {
default = 1081;
type = lib.types.int;
};
storage = lib.mkOption {
default = "${config.container.storage}/frkn";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.frkn = container.mkContainer cfg {
bindMounts = {
"/data" = {
hostPath = "${cfg.storage}/data";
isReadOnly = true;
};
};
config = { ... }: container.mkContainerConfig cfg {
disabledModules = [ "services/networking/zapret.nix" ];
imports = [ "${inputs.nixpkgsMaster}/nixos/modules/services/networking/zapret.nix" ];
boot.kernel.sysctl = {
"net.ipv4.conf.all.src_valid_mark" = 1;
"net.ipv4.ip_forward" = 1;
};
services.zapret = {
inherit (config.services.zapret) params;
enable = true;
package = pkgsMaster.zapret;
};
services = {
microsocks = {
enable = true;
disableLogging = true;
ip = cfg.address;
port = cfg.port;
};
tor = {
enable = true;
openFirewall = true;
settings = let
exclude = "{RU},{UA},{BY},{KZ},{CN},{??}";
in {
# ExcludeExitNodes = exclude;
# ExcludeNodes = exclude;
# DNSPort = dnsport;
UseBridges = true;
ClientTransportPlugin = "obfs4 exec ${pkgs.obfs4}/bin/lyrebird";
Bridge = [
"obfs4 121.45.140.249:12123 0922E212E33B04F0B7C1E398161E8EDE06734F26 cert=3AQ4iJFAzxzt7a/zgXIiFEs6fvrXInXt1Dtr09DgnpvUzG/iiyRTdXYZKSYpI124Zt3ZUA iat-mode=0"
"obfs4 145.239.31.71:10161 882125D15B59BB82BE66F999056CB676D3F061F8 cert=AnD+EvcBMuQDVM7PwW7NgFAzW1M5jDm7DjQtIIcBSjoyAf1FJ2p535rrYL2Kk8POAd0+aw iat-mode=0"
"obfs4 79.137.11.45:45072 ECA3197D49A29DDECD4ACBF9BCF15E4987B78137 cert=2FKyLWkPgMNCWxBD3cNOTRxJH3XP+HdStPGKMjJfw2YbvVjihIp3X2BCrtxQya9m5II5XA iat-mode=0"
"obfs4 94.103.89.153:4443 5617848964FD6546968B5BF3FFA6C11BCCABE58B cert=tYsmuuTe9phJS0Gh8NKIpkVZP/XKs7gJCqi31o8LClwYetxzFz0fQZgsMwhNcIlZ0HG5LA iat-mode=0"
];
};
client = {
enable = true;
# dns.enable = true;
socksListenAddress = {
IsolateDestAddr = true;
addr = cfg.address;
port = cfg.torport;
};
};
};
xray = {
enable = true;
settingsFile = "/data/Client.json";
};
};
systemd = {
services.tor.wantedBy = lib.mkForce [ ];
timers.tor = {
timerConfig = {
OnBootSec = 5;
Unit = "tor.service";
};
wantedBy = [ "timers.target" ];
};
};
};
};
};
}

View file

@ -1,129 +0,0 @@
{
config,
container,
lib,
pkgs,
...
}: let
cfg = config.container.module.git;
in {
options.container.module.git = {
enable = lib.mkEnableOption "the git server.";
address = lib.mkOption {
default = "10.1.0.8";
type = lib.types.str;
};
port = lib.mkOption {
default = 3000;
type = lib.types.int;
};
portSsh = lib.mkOption {
default = 22144;
type = lib.types.int;
};
domain = lib.mkOption {
default = "git.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/git";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.git = container.mkContainer cfg {
bindMounts = {
"/var/lib/forgejo" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
environment.systemPackages = with pkgs; [
forgejo
];
services.forgejo = {
enable = true;
stateDir = "/var/lib/forgejo";
database = let
postgre = config.container.module.postgres;
in {
createDatabase = false;
host = postgre.address;
name = "forgejo";
port = postgre.port;
type = "postgres";
user = "forgejo";
};
settings = let
gcArgs = "--aggressive --no-cruft --prune=now";
gcTimeout = 600;
in {
"cron.cleanup_actions".ENABLED = true;
"cron.update_mirrors".SCHEDULE = "@midnight";
"git".GC_ARGS = gcArgs;
"git.timeout".GC = gcTimeout;
"log".LEVEL = "Error";
"repo-archive".ENABLED = false;
"repository.issue".MAX_PINNED = 99999;
"repository.pull-request".DEFAULT_MERGE_STYLE = "rebase";
"service".DISABLE_REGISTRATION = true;
"server" = {
DOMAIN = cfg.domain;
HTTP_ADDR = cfg.address;
ROOT_URL = "https://${cfg.domain}";
BUILTIN_SSH_SERVER_USER = "git";
DISABLE_SSH = false;
SSH_PORT = cfg.portSsh;
START_SSH_SERVER = true;
};
"ui" = {
AMBIGUOUS_UNICODE_DETECTION = false;
};
"repository" = {
DEFAULT_PRIVATE = "private";
DEFAULT_PUSH_CREATE_PRIVATE = true;
};
"cron" = {
ENABLED = true;
RUN_AT_START = true;
};
"cron.git_gc_repos" = {
ENABLED = true;
ARGS = gcArgs;
SCHEDULE = "@midnight";
TIMEOUT = gcTimeout;
};
};
};
systemd = {
services = {
forgejo = {
serviceConfig.PrivateNetwork = lib.mkForce false;
wantedBy = lib.mkForce [ ];
};
};
timers.fixsystemd = {
timerConfig = {
OnBootSec = 5;
Unit = "forgejo.service";
};
wantedBy = [
"timers.target"
];
};
};
};
};
};
}

View file

@ -1,54 +0,0 @@
{
__findFile,
config,
container,
lib,
pkgs,
util,
...
} @args: let
cfg = config.container.module.home;
package = (pkgs.callPackage <package/homer> args);
in {
options.container.module.home = {
enable = lib.mkEnableOption "the dashboard.";
address = lib.mkOption {
default = "10.1.0.18";
type = lib.types.str;
};
port = lib.mkOption {
default = 80;
type = lib.types.int;
};
domain = lib.mkOption {
default = "home.${config.container.domain}";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
containers.home = container.mkContainer cfg {
config = { ... }: container.mkContainerConfig cfg {
environment.systemPackages = [
package
];
systemd.packages = [
package
];
services.nginx = {
enable = true;
virtualHosts.${cfg.domain} = container.mkServer {
default = true;
root = "${package}";
locations = {
"/".extraConfig = util.trimTabs ''
try_files $uri $uri/index.html;
'';
};
};
};
};
};
};
}

View file

@ -1,119 +0,0 @@
{
config,
container,
lib,
...
}: let
cfg = config.container.module.iot;
in {
options.container.module.iot = {
enable = lib.mkEnableOption "IoT service.";
address = lib.mkOption {
default = "10.1.0.27";
type = lib.types.str;
};
port = lib.mkOption {
default = 8123;
type = lib.types.int;
};
domain = lib.mkOption {
default = "iot.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/iot";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.iot = container.mkContainer cfg {
bindMounts = {
"/var/lib/hass" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
"/dev/ttyACM0" = {
hostPath = "/dev/ttyACM0";
isReadOnly = false;
};
"/dev/serial/by-id" = {
hostPath = "/dev/serial/by-id";
isReadOnly = false;
};
}
// container.attachMedia "photo" true
;
allowedDevices = [
{
modifier = "rwm";
node = "/dev/ttyACM0";
}
];
config = { ... }: container.mkContainerConfig cfg {
# Allow Hass to talk to Zigbee dongle.
users.users.hass.extraGroups = [
"dialout"
"tty"
];
services.home-assistant = {
# NOTE: Missing: hacs. Inside hacs: `card-mod`, `Clock Weather Card`, `WallPanel` and `Yandex.Station`.
enable = true;
# NOTE: Using imperative config because of secrets.
config = null;
configDir = "/var/lib/hass";
extraComponents = [
"caldav"
"met"
"sun"
"systemmonitor"
"zha"
];
extraPackages =
python3Packages: with python3Packages; [
aiodhcpwatcher
aiodiscover
aiogithubapi
arrow
async-upnp-client
av
gtts
ha-ffmpeg
hassil
home-assistant-intents
mutagen
numpy
pymicro-vad
pynacl
pyspeex-noise
python-telegram-bot
pyturbojpeg
zeroconf
];
# lovelaceConfig = {
# title = "Home IoT control center.";
# };
};
# HACK: Delay so that nextcloud calendar can reply on reboot.
systemd = {
services."home-assistant".wantedBy = lib.mkForce [ ];
timers.fixsystemd = {
timerConfig = {
OnBootSec = 60;
Unit = "home-assistant.service";
};
wantedBy = [ "timers.target" ];
};
};
};
};
};
}

View file

@ -1,79 +0,0 @@
{
__findFile,
config,
container,
lib,
pkgsJobber,
poetry2nixJobber,
...
}: let
cfg = config.container.module.jobber;
script = import <package/jobber> {
pkgs = pkgsJobber;
poetry2nix = poetry2nixJobber;
};
in {
options.container.module.jobber = {
enable = lib.mkEnableOption "Stanley - the button pusher.";
address = lib.mkOption {
default = "10.1.0.32";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/jobber";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.jobber = container.mkContainer cfg {
bindMounts = {
"/data" = {
hostPath = "${cfg.storage}/data";
isReadOnly = true;
};
};
enableTun = true;
config = { ... }: let
packages = [
script
] ++ (with pkgsJobber; [
firefox
geckodriver
openvpn
python311
]);
in container.mkContainerConfig cfg {
networking = lib.mkForce {
nameservers = [
"10.30.218.2"
];
};
systemd.services.jobber = {
description = "My job is pushing the button.";
enable = true;
path = packages;
wantedBy = [
"multi-user.target"
];
environment = {
PYTHONDONTWRITEBYTECODE = "1";
PYTHONUNBUFFERED = "1";
};
serviceConfig = {
ExecStart = "${script}/bin/jobber -u";
Restart = "on-failure";
Type = "simple";
};
};
};
};
};
}

View file

@ -1,224 +0,0 @@
# Guide: https://nixos-mailserver.readthedocs.io/en/latest/setup-guide.html
{
config,
const,
container,
lib,
pkgs,
util,
...
}: let
cfg = config.container.module.mail;
domain = config.container.domain;
in {
options.container.module.mail = {
enable = lib.mkEnableOption "the email server.";
address = lib.mkOption {
default = "10.1.0.5";
type = lib.types.str;
};
port = lib.mkOption {
default = 80;
type = lib.types.int;
};
domain = lib.mkOption {
default = "mail.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/mail";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.mail = container.mkContainer cfg {
bindMounts = {
"/var/lib/dovecot/indices" = {
hostPath = "${cfg.storage}/data/indices";
isReadOnly = false;
};
"/var/vmail" = {
hostPath = "${cfg.storage}/data/vmail";
isReadOnly = false;
};
"/var/sieve" = {
hostPath = "${cfg.storage}/data/sieve";
isReadOnly = false;
};
"/var/dkim" = {
hostPath = "${cfg.storage}/data/dkim";
isReadOnly = false;
};
"/acme" = {
hostPath = "${config.container.module.proxy.storage}/letsencrypt";
isReadOnly = true;
};
};
config = { config, ... }: container.mkContainerConfig cfg {
imports = [
(builtins.fetchTarball {
sha256 = "sha256:0clvw4622mqzk1aqw1qn6shl9pai097q62mq1ibzscnjayhp278b";
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-${const.stateVersion}/nixos-mailserver-nixos-${const.stateVersion}.tar.gz";
})
];
mailserver = {
enable = true;
domains = [ domain ];
fqdn = cfg.domain;
sendingFqdn = domain;
# Use `mkpasswd -sm bcrypt`.
loginAccounts = let
defaultQuota = "1G";
in {
"admin@${domain}" = {
hashedPassword = "$2b$05$1O.dxXxaVshcBNybcqDRYuTlnYt3jDBwfPZWoDtP4BjOLoL0StYsi";
name = "admin";
quota = defaultQuota;
};
"account@${domain}" = {
hashedPassword = "$2b$05$sCyZHdk98KqQ1qsTIvbrUeRJlNBOwBqDgpdc1QxiSnONlEkZ8xGNO";
name = "account";
quota = defaultQuota;
};
"hi@${domain}" = {
hashedPassword = "$2b$05$6fT5hIhzIasNfp9IQr/ds.5RuxH95VKU3QJWlX3hmrAzDF3mExanq";
name = "hi";
quota = defaultQuota;
aliases = [
"voronind@${domain}"
];
};
"job@${domain}" = {
hashedPassword = "$2b$05$.sUmv2.9EWPfLwJn/oZw2e1UbR7HrpNQ2THc5jjX3ysy7CY8ZWHUC";
name = "job";
quota = defaultQuota;
};
"trash@${domain}" = {
hashedPassword = "$2b$05$kn5ygZjN9NR3LXjnKKRw/.DXaZQNW.1XEottlCFIoKiDpIj.JGLJm";
name = "trash";
quota = defaultQuota;
catchAll = [
domain
];
};
"noreply@${domain}" = {
hashedPassword = "$2b$05$TaKwoYmcmkAhsRRv6xG5wOkChcz50cB9BP6QPUDKNAcxMbrY6AeMK";
name = "noreply";
quota = defaultQuota;
sendOnly = true;
};
};
enableImap = true;
enableImapSsl = true;
enableSubmission = true;
enableSubmissionSsl = true;
enableManageSieve = true;
virusScanning = false;
certificateFile = "/acme/live/${domain}/cert.pem";
certificateScheme = "manual";
keyFile = "/acme/live/${domain}/privkey.pem";
dkimKeyDirectory = "/var/dkim";
indexDir = "/var/lib/dovecot/indices";
mailDirectory = "/var/vmail";
sieveDirectory = "/var/sieve";
mailboxes = let
mkSpecialBox = specialUse: {
${specialUse} = {
inherit specialUse;
auto = "subscribe";
};
};
in builtins.foldl' (acc: box: acc // (mkSpecialBox box)) {} [
"All"
"Archive"
"Drafts"
"Junk"
"Sent"
"Trash"
];
dmarcReporting = {
inherit domain;
enable = true;
organizationName = "voronind";
# email = "noreply@${domain}";
};
# monitoring = {
# enable = true;
# alertAddress = "admin@${domain}";
# };
};
services = {
roundcube = {
enable = true;
hostName = cfg.domain;
dicts = with pkgs.aspellDicts; [
en
ru
];
plugins = [
"managesieve"
];
extraConfig = util.trimTabs ''
$config['smtp_server'] = "localhost:25";
$config['smtp_auth_type'] = null;
$config['smtp_user'] = "";
$config['smtp_pass'] = "";
# $config['smtp_user'] = "%u";
# $config['smtp_pass'] = "%p";
'';
};
nginx.virtualHosts.${cfg.domain} = {
enableACME = false;
forceSSL = false;
};
};
systemd = {
services.autoexpunge = {
description = "Delete old mail";
serviceConfig = {
Type = "oneshot";
};
path = [
pkgs.dovecot
];
script = util.trimTabs ''
doveadm expunge -A mailbox Junk SENTBEFORE 7d
doveadm expunge -A mailbox Trash SENTBEFORE 30d
doveadm expunge -u trash@voronind.com mailbox Inbox SENTBEFORE 30d
doveadm purge -A
'';
};
timers.autoexpunge = {
timerConfig = {
OnCalendar = "daily";
Persistent = true;
Unit = "autoexpunge.service";
};
wantedBy = [
"timers.target"
];
};
};
};
};
};
}

View file

@ -1,101 +0,0 @@
# NOTE: Imperative part:
# 1. You need to change PSQL tables owner from root to onlyoffice, too. They don't do that automatically for some reason.
# 2. TODO: Generate JWT secret at /var/lib/onlyoffice/jwt, i.e. 9wLfMGha1YrfvWpb5hyYjZf8pvJQ3swS
# See https://git.voronind.com/voronind/nixos/issues/74
{
config,
container,
lib,
pkgs,
util,
...
}: let
cfg = config.container.module.office;
in {
options.container.module.office = {
enable = lib.mkEnableOption "the office web suite.";
address = lib.mkOption {
default = "10.1.0.21";
type = lib.types.str;
};
port = lib.mkOption {
default = 8000;
type = lib.types.int;
};
domain = lib.mkOption {
default = "office.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/office";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.office = container.mkContainer cfg {
bindMounts = {
"/var/lib/onlyoffice" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
# HACK: Temporarely run in docker due to https://github.com/ONLYOFFICE/onlyoffice-nextcloud/issues/931
config = { pkgs, ... }: container.mkContainerConfig cfg {
virtualisation.oci-containers.backend = "docker";
virtualisation.oci-containers.containers.office = {
autoStart = true;
image = "dockerhub.timeweb.cloud/onlyoffice/documentserver:latest";
# ports = [ "${toString cfg.port}:8000" ];
extraOptions = [
"--network=host"
"--privileged"
];
environment = {
AMQP_URI = "amqp://guest:guest@${config.container.module.rabbitmq.address}:${toString config.container.module.rabbitmq.port}";
DB_HOST = config.container.module.postgres.address;
DB_NAME = "onlyoffice";
DB_PORT = toString config.container.module.postgres.port;
DB_PWD = "onlyoffice";
DB_USER = "onlyoffice";
JWT_ENABLED = "true";
JWT_SECRET = "8wLfKGha8YRfvwpB5hYYjZf8vtUQs3wS";
};
};
};
# config = { pkgs, ... }: container.mkContainerConfig cfg {
# # HACK: For whatever reason it does not detect my global allowUnfree (I pass pkgs from host system in mkContainerConfig).
# nixpkgs.overlays = [ (final: prev: {
# corefonts = prev.corefonts.overrideAttrs (old: {
# meta.license = mkForce licenses.mit;
# });
# })];
# services.onlyoffice = let
# dbName = "onlyoffice";
# in {
# enable = true;
# hostname = cfg.domain;
# postgresName = dbName;
# postgresHost = config.container.module.postgres.address;
# postgresUser = dbName;
# postgresPasswordFile = "${pkgs.writeText "OfficeDbPassword" dbName}";
# jwtSecretFile = "/var/lib/onlyoffice/jwt";
# rabbitmqUrl = "amqp://guest:guest@${config.container.module.rabbitmq.address}:${toString config.container.module.rabbitmq.port}";
# examplePort = cfg.port;
# enableExampleServer = true;
# };
# };
};
};
}

View file

@ -1,99 +0,0 @@
{
config,
container,
lib,
pkgs,
...
}: let
cfg = config.container.module.paper;
in {
options.container.module.paper = {
enable = lib.mkEnableOption "the paper scans manager.";
address = lib.mkOption {
default = "10.1.0.40";
type = lib.types.str;
};
port = lib.mkOption {
default = 28981;
type = lib.types.int;
};
domain = lib.mkOption {
default = "paper.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/paper";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.paper = container.mkContainer cfg {
bindMounts = {
"/var/lib/paperless" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
"/var/lib/paperless/media" = {
hostPath = "${lib.elemAt config.container.media.paper 0}";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
services.paperless = {
enable = true;
address = "0.0.0.0";
dataDir = "/var/lib/paperless";
port = cfg.port;
passwordFile = pkgs.writeText "PaperlessPassword" "root"; # NOTE: Only for initial setup, change later.
settings = {
PAPERLESS_ADMIN_USER = "root";
PAPERLESS_DBENGINE = "postgresql";
PAPERLESS_DBHOST = config.container.module.postgres.address;
PAPERLESS_DBNAME = "paperless";
PAPERLESS_DBPASS = "paperless";
PAPERLESS_DBPORT = config.container.module.postgres.port;
PAPERLESS_DBUSER = "paperless";
PAPERLESS_OCR_LANGUAGE = "rus";
PAPERLESS_REDIS = "redis://${config.container.module.redis.address}:${toString config.container.module.redis.port}";
PAPERLESS_URL = "https://${cfg.domain}";
};
};
# HACK: This is required for TCP postgres connection.
systemd = {
services = {
paperless-scheduler = {
serviceConfig.PrivateNetwork = lib.mkForce false;
wantedBy = lib.mkForce [ ];
};
paperless-consumer = {
serviceConfig.PrivateNetwork = lib.mkForce false;
wantedBy = lib.mkForce [ ];
};
paperless-web = {
wantedBy = lib.mkForce [ ];
};
paperless-task-queue = {
wantedBy = lib.mkForce [ ];
};
};
timers.fixsystemd = {
timerConfig = {
OnBootSec = 5;
Unit = "paperless-web.service";
};
wantedBy = [
"timers.target"
];
};
};
};
};
};
}

View file

@ -1,59 +0,0 @@
{
config,
container,
lib,
...
}: let
cfg = config.container.module.pass;
in {
options.container.module.pass = {
enable = lib.mkEnableOption "the password manager.";
address = lib.mkOption {
default = "10.1.0.9";
type = lib.types.str;
};
port = lib.mkOption {
default = 8000;
type = lib.types.int;
};
domain = lib.mkOption {
default = "pass.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/pass";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.pass = container.mkContainer cfg {
bindMounts = {
"/var/lib/vaultwarden" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
services.vaultwarden = {
enable = true;
dbBackend = "sqlite";
environmentFile = "/var/lib/vaultwarden/Env";
config = {
DATA_FOLDER = "/var/lib/vaultwarden";
DOMAIN = "http://${cfg.domain}";
ROCKET_ADDRESS = cfg.address;
ROCKET_PORT = cfg.port;
SIGNUPS_ALLOWED = false;
WEB_VAULT_ENABLED = true;
};
};
};
};
};
}

View file

@ -1,134 +0,0 @@
{
__findFile,
config,
container,
lib,
pkgs,
util,
...
} @args: let
cfg = config.container.module.paste;
package = (pkgs.callPackage <package/privatebin> args);
in {
options.container.module.paste = {
enable = lib.mkEnableOption "the text share platform.";
address = lib.mkOption {
default = "10.1.0.14";
type = lib.types.str;
};
port = lib.mkOption {
default = 80;
type = lib.types.int;
};
domain = lib.mkOption {
default = "paste.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/paste";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"config"
"data"
"nginxtmp"
"tmp"
];
containers.paste = container.mkContainer cfg {
bindMounts = {
"/srv/data" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
"/tmp" = {
hostPath = "${cfg.storage}/tmp";
isReadOnly = false;
};
"/var/lib/nginx/tmp" = {
hostPath = "${cfg.storage}/nginxtmp";
isReadOnly = false;
};
"/srv/config" = {
hostPath = "${cfg.storage}/config";
isReadOnly = false;
};
};
config = { config, ... }: container.mkContainerConfig cfg {
environment.systemPackages = [
package
];
systemd.packages = [
package
];
users.users.paste = {
group = "nginx";
isSystemUser = true;
};
services = {
phpfpm.pools.paste = {
group = "nginx";
user = "paste";
phpPackage = pkgs.php;
settings = {
"catch_workers_output" = true;
"listen.owner" = "nginx";
"php_admin_flag[log_errors]" = true;
"php_admin_value[error_log]" = "stderr";
"pm" = "dynamic";
"pm.max_children" = "32";
"pm.max_requests" = "500";
"pm.max_spare_servers" = "4";
"pm.min_spare_servers" = "2";
"pm.start_servers" = "2";
};
phpEnv = {
# CONFIG_PATH = "${package}/cfg"; # NOTE: Not working?
};
};
nginx = {
enable = true;
virtualHosts.${cfg.domain} = container.mkServer {
default = true;
root = "${package}";
locations = {
"/".extraConfig = util.trimTabs ''
rewrite ^ /index.php;
'';
"~ \\.php$".extraConfig = util.trimTabs ''
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:${config.services.phpfpm.pools.paste.socket};
include ${config.services.nginx.package}/conf/fastcgi.conf;
include ${config.services.nginx.package}/conf/fastcgi_params;
'';
"~ \\.(js|css|ttf|woff2?|png|jpe?g|svg)$".extraConfig = util.trimTabs ''
add_header Cache-Control "public, max-age=15778463";
add_header Referrer-Policy no-referrer;
add_header X-Content-Type-Options nosniff;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Robots-Tag none;
add_header X-XSS-Protection "1; mode=block";
access_log off;
'';
};
extraConfig = util.trimTabs ''
try_files $uri /index.php;
'';
};
};
};
};
};
};
}

View file

@ -1,95 +0,0 @@
{
config,
container,
lib,
pkgs,
...
}: let
cfg = config.container.module.postgres;
in {
options.container.module.postgres = {
enable = lib.mkEnableOption "the PostgreSQL server.";
address = lib.mkOption {
default = "10.1.0.3";
type = lib.types.str;
};
port = lib.mkOption {
default = 5432;
type = lib.types.int;
};
storage = lib.mkOption {
default = "${config.container.storage}/postgres";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.postgres = container.mkContainer cfg {
bindMounts = {
"/var/lib/postgresql/data" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
services.postgresql = let
# Populate with services here.
configurations = with config.container.module; {
forgejo = git;
invidious = yt;
mattermost = chat;
nextcloud = cloud;
onlyoffice = office;
paperless = paper;
privatebin = paste;
};
access = configurations // {
all.address = config.container.host;
};
authentication = let
rules = lib.mapAttrsToList (db: cfg:
"host ${db} ${db} ${cfg.address}/32 trust"
) access;
in builtins.foldl' (acc: item: acc + "${item}\n") "" rules;
ensureDatabases = [
"root"
] ++ lib.mapAttrsToList (name: _: name) configurations;
ensureUsers = map (name: {
inherit name;
ensureDBOwnership = true;
ensureClauses = if name == "root" then {
createdb = true;
createrole = true;
superuser = true;
} else { };
}) ensureDatabases;
in {
inherit authentication ensureDatabases ensureUsers;
enable = true;
dataDir = "/var/lib/postgresql/data/14";
enableTCPIP = true;
package = pkgs.postgresql_14;
# NOTE: Debug mode.
# settings = {
# log_connections = true;
# log_destination = lib.mkForce "syslog";
# log_disconnections = true;
# log_statement = "all";
# logging_collector = true;
# };
};
};
};
};
}

View file

@ -1,72 +0,0 @@
# NOTE: Login to contaier, run passwd and use that root/pw combo for administration. `AllowFrom = all` doesn't seem to work.
# ipp://192.168.2.237
# Pantum M6500W-Series
{
__findFile,
config,
container,
lib,
pkgs,
...
} @args: let
cfg = config.container.module.print;
host = config.container.host;
package = pkgs.callPackage <package/print> args;
in {
options.container.module.print = {
enable = lib.mkEnableOption "the printing server.";
address = lib.mkOption {
default = "10.1.0.46";
type = lib.types.str;
};
port = lib.mkOption {
default = 631;
type = lib.types.int;
};
domain = lib.mkOption {
default = "print.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/print";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.print = container.mkContainer cfg {
bindMounts = {
"/var/lib/cups" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
networking.interfaces."eth0".ipv4.routes = [
{
address = "192.168.2.237"; # NOTE: Printer's IP address.
prefixLength = 32;
via = host;
}
];
services.printing = {
enable = true;
allowFrom = [ "all" ];
browsing = true;
defaultShared = true;
drivers = [ package ];
listenAddresses = [ "${cfg.address}:${toString cfg.port}" ];
startWhenNeeded = true;
stateless = false;
webInterface = true;
};
};
};
};
}

View file

@ -1,94 +0,0 @@
# NOTE: To generate self-signed certs use: `openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./privkey.pem -out ./fullchain.pem`
# For dhparams: `openssl dhparam -out ./ssl-dhparam.pem 4096`
# Example for options-ssl-nginx.conf:
# ```
# ssl_session_cache shared:le_nginx_SSL:10m;
# ssl_session_timeout 1440m;
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_prefer_server_ciphers off;
# ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
# ```
# For certbot to generate new keys: `certbot certonly --manual --manual-public-ip-logging-ok --preferred-challenges dns-01 --server https://acme-v02.api.letsencrypt.org/directory -d "*.voronind.com" -d voronind.com`
{
config,
container,
lib,
pkgs,
util,
...
} @args: let
cfg = config.container.module.proxy;
virtualHosts = util.catSet (util.ls ./proxy/host) args;
in {
options.container.module.proxy = {
enable = lib.mkEnableOption "the proxy server.";
address = lib.mkOption {
default = "10.1.0.2";
type = lib.types.str;
};
port = lib.mkOption {
default = 443;
type = lib.types.int;
};
storage = lib.mkOption {
default = "${config.container.storage}/proxy";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"challenge"
"letsencrypt"
];
containers.proxy = container.mkContainer cfg {
bindMounts = {
"/etc/letsencrypt" = {
hostPath = "${cfg.storage}/letsencrypt";
isReadOnly = false;
};
"/var/www/.well-known" = {
hostPath = "${cfg.storage}/challenge";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
environment.systemPackages = with pkgs; [
certbot
];
services.nginx = {
inherit virtualHosts;
enable = true;
clientMaxBodySize = "4096m";
recommendedOptimisation = true;
recommendedProxySettings = true;
appendConfig = util.trimTabs ''
worker_processes 4;
'';
eventsConfig = util.trimTabs ''
worker_connections 4096;
'';
appendHttpConfig = util.trimTabs ''
proxy_max_temp_file_size 0;
proxy_buffering off;
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
return 403;
}
'';
};
};
};
};
}

View file

@ -1,53 +0,0 @@
{
config,
container,
lib,
pkgs,
util,
...
}: let
cfg = config.container.module.rabbitmq;
in {
options.container.module.rabbitmq = {
enable = lib.mkEnableOption "the mqtt server.";
address = lib.mkOption {
default = "10.1.0.28";
type = lib.types.str;
};
port = lib.mkOption {
default = 5672;
type = lib.types.int;
};
storage = lib.mkOption {
default = "${config.container.storage}/rabbitmq";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.rabbitmq = container.mkContainer cfg {
bindMounts = {
"/var/lib/rabbitmq" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
services.rabbitmq = {
enable = true;
dataDir = "/var/lib/rabbitmq";
listenAddress = cfg.address;
port = cfg.port;
configItems = {
"loopback_users" = "none";
};
};
};
};
};
}

View file

@ -1,59 +0,0 @@
{
config,
container,
lib,
pkgs,
...
}: let
cfg = config.container.module.read;
in {
options.container.module.read = {
enable = lib.mkEnableOption "the reading server.";
address = lib.mkOption {
default = "10.1.0.39";
type = lib.types.str;
};
port = lib.mkOption {
default = 5000;
type = lib.types.int;
};
domain = lib.mkOption {
default = "read.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/read";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.read = container.mkContainer cfg {
bindMounts = {
"/var/lib/kavita" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
}
// container.attachMedia "book" true
// container.attachMedia "manga" true
;
config = { ... }: container.mkContainerConfig cfg {
services.kavita = {
enable = true;
dataDir = "/var/lib/kavita";
tokenKeyFile = pkgs.writeText "KavitaToken" "xY19aQOa939/Ie6GCRGbubVK8zRwrgBY/20AuyMpYshUjwK1Uyl7bw1yknVh6jJIFIfwq2vAjeotOUq7NEsf9Q==";
settings = {
IpAddresses = cfg.address;
Port = cfg.port;
};
};
};
};
};
}

View file

@ -1,35 +0,0 @@
{
config,
container,
lib,
...
}: let
cfg = config.container.module.redis;
in {
options.container.module.redis = {
enable = lib.mkEnableOption "the Redis server.";
address = lib.mkOption {
default = "10.1.0.38";
type = lib.types.str;
};
port = lib.mkOption {
default = 6379;
type = lib.types.int;
};
};
config = lib.mkIf cfg.enable {
containers.redis = container.mkContainer cfg {
config = { ... }: container.mkContainerConfig cfg {
services.redis.servers.main = {
enable = true;
port = cfg.port;
bind = cfg.address;
extraParams = [
"--protected-mode no"
];
};
};
};
};
}

View file

@ -1,138 +0,0 @@
{
config,
container,
lib,
pkgs,
...
}: let
cfg = config.container.module.search;
in {
options.container.module.search = {
enable = lib.mkEnableOption "the search frontend.";
address = lib.mkOption {
default = "10.1.0.26";
type = lib.types.str;
};
port = lib.mkOption {
default = 8080;
type = lib.types.int;
};
domain = lib.mkOption {
default = "search.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/search";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
containers.search = container.mkContainer cfg {
config = { ... }: container.mkContainerConfig cfg {
services.searx = {
enable = true;
package = pkgs.searxng;
# REF: https://github.com/searxng/searxng/blob/master/searx/settings.yml
settings = {
general = {
debug = false;
enable_metrics = false;
instance_name = "SearX";
};
server = {
bind_address = cfg.address;
image_proxy = false;
limiter = false;
method = "GET";
port = cfg.port;
public_instance = false;
secret_key = "searxxx";
};
search = {
autocomplete = "";
autocomplete_min = 4;
default_lang = "auto";
safe_search = 0;
};
ui = {
center_alignment = false;
default_locale = "";
default_theme = "simple";
hotkeys = "vim";
infinite_scroll = false;
simple_style = "dark";
};
outgoing = {
enable_http2 = true;
max_request_timeout = 10.0;
pool_connections = 100;
pool_maxsize = 20;
request_timeout = 3.0;
# proxies = {
# "all://" = with config.container.module; [
# # "socks5:${frkn.address}:${frkn.port}"
# "socks5:${frkn.address}:1081"
# # "socks5:${frkn.address}:9150"
# ];
# };
# using_tor_proxy = true;
# extra_proxy_timeout = 10;
};
# plugins = [ ];
enabled_plugins = [
"Basic Calculator"
"Hostnames plugin"
"Tracker URL remover"
];
hostnames = {
replace = with config.container.module; {
"(.*\.)?youtu\.be$" = yt.domain;
"(.*\.)?youtube\.com$" = yt.domain;
};
remove = [
"(.*\.)?dzen\.ru$"
"(.*\.)?facebook.com$"
"(.*\.)?gosuslugi\.ru$"
"(.*\.)?quora\.com$"
"(.*\.)?rutube\.ru$"
"(.*\.)?vk\.com$"
];
low_priority = [
"(.*\.)?google(\..*)?$"
"(.*\.)?microsoft\.com$"
];
high_priority = [
"(.*\.)?4pda.to$"
"(.*\.)?github.com$"
"(.*\.)?wikipedia.org$"
];
};
categories_as_tabs = {
files = { };
general = { };
images = { };
it = { };
map = { };
news = { };
videos = { };
};
engines = let
mkEnable = name: {
inherit name;
disabled = false;
};
mkDisable = name: {
inherit name;
disabled = true;
};
in [
(mkEnable "bing")
(mkDisable "qwant")
];
};
};
};
};
};
}

View file

@ -1,66 +0,0 @@
{
config,
container,
lib,
...
}: let
cfg = config.container.module.status;
in {
options.container.module.status = {
enable = lib.mkEnableOption "the status monitor.";
address = lib.mkOption {
default = "10.1.0.22";
type = lib.types.str;
};
port = lib.mkOption {
default = 3001;
type = lib.types.int;
};
domain = lib.mkOption {
default = "status.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/status";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.status = container.mkContainer cfg {
bindMounts = {
"/var/lib/uptime-kuma" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
networking = {
nameservers = lib.mkForce [
config.container.module.dns.address
];
};
services.uptime-kuma = {
enable = true;
settings = {
DATA_DIR = "/var/lib/uptime-kuma/";
HOST = cfg.address;
PORT = toString cfg.port;
};
};
systemd.services.uptime-kuma = {
serviceConfig = {
DynamicUser = lib.mkForce false;
};
};
};
};
};
}

View file

@ -1,62 +0,0 @@
{
config,
container,
lib,
...
}: let
cfg = config.container.module.stock;
in {
options.container.module.stock = {
enable = lib.mkEnableOption "the stock management.";
address = lib.mkOption {
default = "10.1.0.45";
type = lib.types.str;
};
port = lib.mkOption {
default = 80;
type = lib.types.int;
};
domain = lib.mkOption {
default = "stock.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/stock";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.stock = container.mkContainer cfg {
bindMounts = {
"/var/lib/grocy" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { ... }: container.mkContainerConfig cfg {
services.grocy = {
enable = true;
dataDir = "/var/lib/grocy";
hostName = cfg.domain;
nginx = {
enableSSL = false;
};
settings = {
calendar = {
firstDayOfWeek = 1;
showWeekNumber = true;
};
culture = "en";
currency = "RUB";
};
};
};
};
};
}

View file

@ -1,60 +0,0 @@
{
config,
container,
lib,
...
}: let
cfg = config.container.module.terraria;
in {
options.container.module.terraria = {
enable = lib.mkEnableOption "the Terraria server.";
address = lib.mkOption {
default = "10.1.0.77";
type = lib.types.str;
};
port = lib.mkOption {
default = 22777;
type = lib.types.int;
};
storage = lib.mkOption {
default = "${config.container.storage}/terraria";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
containers.terraria = container.mkContainer cfg {
bindMounts = {
"/var/lib/terraria" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
};
config = { pkgs, ... }: container.mkContainerConfig cfg {
# NOTE: Admin with `tmux -S /var/lib/terraria/terraria.sock attach-session -t 0`
environment.systemPackages = with pkgs; [ tmux ];
services.terraria = let
dataDir = "/var/lib/terraria";
in {
inherit (cfg) port;
inherit dataDir;
enable = true;
autoCreatedWorldSize = "large";
maxPlayers = 4;
messageOfTheDay = "<3";
noUPnP = false;
openFirewall = false;
password = "mishadima143";
secure = false;
worldPath = "${dataDir}/.local/share/Terraria/Worlds/Together.wld";
};
};
};
};
}

View file

@ -1,122 +0,0 @@
# easyrsa --days=36500 init-pki
# easyrsa --days=36500 build-ca
# easyrsa --days=36500 build-server-full <SERVER_NAME> nopass
# easyrsa --days=36500 build-client-full <CLIENT_NAME> nopass
# easyrsa gen-crl
# openssl dhparam -out dh2048.pem 2048
# Don't forget to set tls hostname on the client to match SERVER_NAME *AND* disable ipv6 ?
# easyrsa revoke <CLIENT_NAME>
# easyrsa gen-crl
# restart container
# SEE: https://github.com/OpenVPN/openvpn/blob/master/sample/sample-config-files/server.conf
# SRC: https://github.com/TinCanTech/easy-tls
{
config,
container,
lib,
pkgs,
util,
...
}: let
cfg = config.container.module.vpn;
in {
options.container.module.vpn = {
enable = lib.mkEnableOption "the vpn server.";
address = lib.mkOption {
default = "10.1.0.23";
type = lib.types.str;
};
port = lib.mkOption {
default = 22145;
type = lib.types.int;
};
storage = lib.mkOption {
default = "${config.container.storage}/vpn";
type = lib.types.str;
};
clients = lib.mkOption {
default = "10.1.1.0/24";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"data"
];
# HACK: I have no idea how to fully manage the container interface via networkd, so just add a route manually.
systemd.services.vpn-route = util.mkStaticSystemdService {
enable = true;
description = "Hack vpn routes on host";
after = [ "container@vpn.service" ];
wants = [ "container@vpn.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
};
script = ''
${pkgs.iproute2}/bin/ip route add ${cfg.clients} via ${cfg.address} dev ve-vpn || true
'';
};
containers.vpn = container.mkContainer cfg {
bindMounts = {
"/data" = {
hostPath = "${cfg.storage}/data";
isReadOnly = true;
};
};
config = { ... }: container.mkContainerConfig cfg {
boot.kernel.sysctl = {
"net.ipv4.conf.all.src_valid_mark" = 1;
"net.ipv4.ip_forward" = 1;
};
environment.systemPackages = with pkgs; [
easyrsa
openvpn
];
users = {
groups.openvpn = {};
users.openvpn = {
group = "openvpn";
isSystemUser = true;
uid = 1000;
};
};
# NOTE: Change the `server` to match `cfg.clients` or write a substring here.
services.openvpn.servers.vpn = {
autoStart = true;
config = util.trimTabs ''
ca /data/pki/ca.crt
cert /data/pki/issued/home.crt
client-to-client
crl-verify /data/pki/crl.pem
dev tun
dh /data/dh2048.pem
explicit-exit-notify 1
group openvpn
ifconfig-pool-persist ipp.txt
keepalive 10 120
key /data/pki/private/home.key
persist-tun
port ${toString cfg.port}
proto udp
push "dhcp-option DNS 10.0.0.1"
push "dhcp-option DNS 10.0.0.1"
push "route 10.0.0.0 255.0.0.0"
push "route 192.168.1.0 255.255.255.0"
server 10.1.1.0 255.255.255.0
status openvpn-status.log
topology subnet
user openvpn
verb 4
'';
};
};
};
};
}

View file

@ -1,87 +0,0 @@
{
config,
container,
lib,
...
}: let
cfg = config.container.module.watch;
in {
options.container.module.watch = {
enable = lib.mkEnableOption "the media server.";
address = lib.mkOption {
default = "10.1.0.11";
type = lib.types.str;
};
port = lib.mkOption {
default = 8096;
type = lib.types.int;
};
domain = lib.mkOption {
default = "watch.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/watch";
type = lib.types.str;
};
memLimit = lib.mkOption {
default = "8G";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
systemd.tmpfiles.rules = container.mkContainerDir cfg [
"cache"
"data"
];
containers.watch = container.mkContainer cfg {
bindMounts = {
"/var/lib/jellyfin" = {
hostPath = "${cfg.storage}/data";
isReadOnly = false;
};
"/var/cache/jellyfin" = {
hostPath = "${cfg.storage}/cache";
isReadOnly = false;
};
"/dev/dri" = {
hostPath = "/dev/dri";
isReadOnly = false;
};
}
// container.attachMedia "anime" true
// container.attachMedia "download" true
// container.attachMedia "movie" true
// container.attachMedia "music" true
// container.attachMedia "photo" true
// container.attachMedia "porn" true
// container.attachMedia "show" true
// container.attachMedia "study" true
// container.attachMedia "work" true
// container.attachMedia "youtube" true
;
allowedDevices = [
{
modifier = "rwm";
node = "/dev/dri/renderD128";
}
];
config = { ... }: container.mkContainerConfig cfg {
systemd.services.jellyfin.serviceConfig.MemoryLimit = cfg.memLimit;
services.jellyfin = {
enable = true;
cacheDir = "/var/cache/jellyfin";
dataDir = "/var/lib/jellyfin";
};
# users.users.jellyfin.extraGroups = [
# "video"
# "render"
# ];
};
};
};
}

View file

@ -1,65 +0,0 @@
{
__findFile,
config,
container,
inputs,
lib,
pkgs,
pkgsMaster,
...
}: let
cfg = config.container.module.yt;
in {
options.container.module.yt = {
enable = lib.mkEnableOption "the YouTube frontend.";
address = lib.mkOption {
default = "10.1.0.19";
type = lib.types.str;
};
port = lib.mkOption {
default = 3000;
type = lib.types.int;
};
domain = lib.mkOption {
default = "yt.${config.container.domain}";
type = lib.types.str;
};
storage = lib.mkOption {
default = "${config.container.storage}/yt";
type = lib.types.str;
};
};
config = lib.mkIf cfg.enable {
containers.yt = container.mkContainer cfg {
config = { ... }: container.mkContainerConfig cfg {
disabledModules = [ "services/web-apps/invidious.nix" ];
imports = [ "${inputs.nixpkgsMaster}/nixos/modules/services/web-apps/invidious.nix" ];
services.invidious = {
enable = true;
domain = cfg.domain;
package = pkgsMaster.invidious;
port = cfg.port;
nginx.enable = false;
database = {
host = config.container.module.postgres.address;
port = config.container.module.postgres.port;
createLocally = false;
passwordFile = "${pkgs.writeText "InvidiousDbPassword" "invidious"}";
};
settings = {
captcha_enabled = false;
check_tables = true;
external_port = 443;
https_only = true;
registration_enabled = false;
admins = [
"root"
];
};
};
};
};
};
}

View file

@ -1,57 +0,0 @@
{
config,
lib,
...
}: let
cfg = config.container;
in {
options.container = {
enable = lib.mkEnableOption "Containers!!";
autoStart = lib.mkOption {
default = false;
type = lib.types.bool;
};
host = lib.mkOption {
default = "0.0.0.0";
type = lib.types.str;
};
localAccess = lib.mkOption {
default = "0.0.0.0";
type = lib.types.str;
};
storage = lib.mkOption {
default = "/tmp/container";
type = lib.types.str;
};
domain = lib.mkOption {
default = "local";
type = lib.types.str;
};
interface = lib.mkOption {
default = "lo";
type = lib.types.str;
};
media = lib.mkOption {
default = { };
type = lib.types.attrs;
};
};
config = lib.mkIf cfg.enable {
# This is the network for all the containers.
# They are not available to the external interface by default,
# instead they all expose specific ports in their configuration.
networking = {
nat = {
enable = true;
externalInterface = config.container.interface;
internalInterfaces = [
"ve-+"
];
};
networkmanager.unmanaged = [
"interface-name:ve-*"
];
};
};
}

View file

@ -1,30 +0,0 @@
{
config,
container,
util,
...
}: let
address = "192.168.2.249";
domain = "camera.${config.container.domain}";
port = 554;
in {
${domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
return 301 rtsp://${address}:${toString port}/live/main;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,33 +0,0 @@
{
config,
container,
util,
...
}: let
cfg = config.container.module.change;
name = "change";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
add_header Referrer-Policy 'origin';
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,31 +0,0 @@
{
config,
container,
util,
...
}: let
cfg = config.container.module.chat;
name = "chat";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,34 +0,0 @@
{
config,
container,
util,
...
}: let
cfg = config.container.module.cloud;
name = "cloud";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location ~ ^/(settings/admin|settings/users|settings/apps|login|api) {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
location / {
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,30 +0,0 @@
{
config,
container,
util,
...
}: let
cfg = config.container.module.download;
name = "download";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,33 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.git;
name = "git";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location ~ ^/(admin|api|user) {
allow ${config.container.localAccess};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
location / {
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,30 +0,0 @@
{
config,
container,
util,
...
}: let
cfg = config.container.module.home;
name = "home";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,37 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.iot;
name = "iot";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,30 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.mail;
name = "mail";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,31 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.office;
name = "office";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
# allow ${config.container.localAccess};
# allow ${config.container.module.status.address};
# allow ${config.container.module.vpn.clients};
# allow ${config.container.module.frkn.address};
# deny all;
add_header X-Forwarded-Proto https;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,30 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.paper;
name = "paper";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,30 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.pass;
name = "pass";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,29 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.paste;
name = "paste";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location = / {
return 403;
}
location / {
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,35 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.print;
name = "print";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
proxy_set_header Host "127.0.0.1";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,32 +0,0 @@
{
container,
config,
util,
...
}: let
address = "192.168.2.237";
domain = "printer.${config.container.domain}";
name = "printer";
port = 80;
in {
${domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${address}:${toString port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,30 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.read;
name = "read";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,26 +0,0 @@
{
container,
config,
util,
...
}: let
domain = "resume.${config.container.domain}";
in {
${domain} = container.mkServer {
extraConfig = util.trimTabs ''
server_name ${domain};
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
if ($http_accept_language ~ ru) {
return 301 https://${config.container.module.git.domain}/voronind/resume/releases/download/latest/VoronindRu.pdf;
}
return 301 https://${config.container.module.git.domain}/voronind/resume/releases/download/latest/VoronindEn.pdf;
'';
};
}

View file

@ -1,32 +0,0 @@
{
container,
config,
util,
...
}: let
address = "10.0.0.2";
domain = "router.${config.container.domain}";
name = "router";
port = 80;
in {
${domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${address}:${toString port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,30 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.search;
name = "search";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,37 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.status;
name = "sstatus";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location ~ ^/(dashboard|settings) {
allow ${config.container.localAccess};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
location / {
allow ${config.container.localAccess};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,30 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.stock;
name = "stock";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,30 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.watch;
name = "watch";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -1,40 +0,0 @@
{
container,
config,
util,
...
}: let
cfg = config.container.module.yt;
name = "yt";
in {
${cfg.domain} = container.mkServer {
extraConfig = util.trimTabs ''
listen 443 ssl;
set ''$${name} ${cfg.address}:${toString cfg.port};
location / {
allow ${config.container.localAccess};
allow ${config.container.module.status.address};
allow ${config.container.module.vpn.clients};
allow ${config.container.module.frkn.address};
deny all;
proxy_pass http://''$${name}$request_uri;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_hide_header Content-Security-Policy;
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
}
ssl_certificate /etc/letsencrypt/live/${config.container.domain}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${config.container.domain}/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -120,7 +120,6 @@
# HM config. # HM config.
./home/NixOs.nix ./home/NixOs.nix
] ]
++ (self.ls ./container)
++ (self.ls ./host/${system}/${hostname}) ++ (self.ls ./host/${system}/${hostname})
++ (self.ls ./option) ++ (self.ls ./option)
++ (self.ls ./config) ++ (self.ls ./config)
@ -128,14 +127,11 @@
++ (self.ls ./system) ++ (self.ls ./system)
; ;
specialArgs = let specialArgs = let
pkgs = nixpkgs.legacyPackages.${system}.pkgs; lib = nixpkgs.lib;
lib = nixpkgs.lib; util = import ./lib/Util.nix { inherit lib; };
config = self.nixosConfigurations.${hostname}.config;
util = import ./lib/Util.nix { inherit lib; };
in { in {
inherit (self) const __findFile; inherit (self) const __findFile;
inherit inputs self poetry2nixJobber util; inherit inputs self poetry2nixJobber util;
container = import ./lib/Container.nix { inherit lib pkgs config util; inherit (self) const; };
pkgsJobber = nixpkgsJobber.legacyPackages.${system}.pkgs; pkgsJobber = nixpkgsJobber.legacyPackages.${system}.pkgs;
pkgsMaster = nixpkgsMaster.legacyPackages.${system}.pkgs; pkgsMaster = nixpkgsMaster.legacyPackages.${system}.pkgs;
pkgsUnstable = nixpkgsUnstable.legacyPackages.${system}.pkgs; pkgsUnstable = nixpkgsUnstable.legacyPackages.${system}.pkgs;

View file

@ -17,7 +17,7 @@
path_src="/storage/hot" path_src="/storage/hot"
path_mount="/storage/cold_1" path_mount="/storage/cold_1"
path_backup="''${path_mount}/backup" path_backup="''${path_mount}/backup"
path_container="''${path_backup}/home" path_data="''${path_backup}/home"
path_media="/storage/cold_1 /storage/cold_2" path_media="/storage/cold_1 /storage/cold_2"
# Check if backup drive is mounted. # Check if backup drive is mounted.
@ -41,10 +41,10 @@
archive ColdMedia.txt && rm ColdMedia.txt || report "Backup : Failed to archive media list!" archive ColdMedia.txt && rm ColdMedia.txt || report "Backup : Failed to archive media list!"
cd - cd -
# Backup containers. # Backup data.
container=$(archive container/) data=$(archive data/)
bupsize=$(tdu ''${container} | awk '{print $1}') bupsize=$(tdu ''${data} | awk '{print $1}')
mv ''${container} ''${path_container}/ || report "Backup : Failed to save containers!" mv ''${data} ''${path_data}/ || report "Backup : Failed to save data!"
# Backup some media. # Backup some media.
cd ''${path_src} cd ''${path_src}
@ -61,14 +61,9 @@
archive_prune ColdMediaTxt 30 archive_prune ColdMediaTxt 30
cd - cd -
# Prune old container copies. # Prune old data copies.
cd ''${path_container} cd ''${path_data}
archive_prune Container 7 archive_prune Data 7
cd -
# Prune game saves.
cd "''${path_backup}/save/"
archive_prune
cd - cd -
# Sync writes. # Sync writes.
@ -90,6 +85,7 @@ in {
curl curl
gawk gawk
gnutar gnutar
mount
procps procps
pv pv
xz xz

View file

@ -0,0 +1,40 @@
{
lib,
...
}: let
storage = "/storage/hot/data";
binds = [
(mkBind "change" "/var/lib/changedetection-io")
(mkBind "cups" "/var/lib/cups")
(mkBind "deluge" "/var/lib/deluge/.config/deluge")
(mkBind "dkim" "/var/dkim")
(mkBind "dovecot_index" "/var/lib/dovecot/indices")
(mkBind "forgejo" "/var/lib/forgejo")
(mkBind "grocy" "/var/lib/grocy")
(mkBind "hass" "/var/lib/hass")
(mkBind "jellyfin" "/var/lib/jellyfin")
(mkBind "jellyfin_cache" "/var/cache/jellyfin")
(mkBind "kavita" "/var/lib/kavita")
(mkBind "letsencrypt" "/etc/letsencrypt")
(mkBind "nextcloud" "/var/lib/nextcloud")
(mkBind "ovpn" "/var/lib/ovpn")
(mkBind "paperless" "/var/lib/paperless")
(mkBind "postgres" "/var/lib/postgresql")
(mkBind "rabbitmq" "/var/lib/rabbitmq")
(mkBind "sieve" "/var/sieve")
(mkBind "terraria" "/var/lib/terraria")
(mkBind "uptime_kuma" "/var/lib/uptime-kuma")
(mkBind "vaultwarden" "/var/lib/vaultwarden")
(mkBind "vmail" "/var/vmail")
];
mkBind = name: path: {
${path} = {
device = "${storage}/${name}";
options = [ "bind" "nofail" "X-mount.mkdir=1777" ];
};
};
in {
fileSystems = lib.foldl' (acc: bind: acc // bind) { } binds;
}

View file

@ -0,0 +1,97 @@
{ ... }: {
services.blocky = {
enable = true;
# REF: https://0xerr0r.github.io/blocky/main/configuration/
settings = {
bootstrapDns = "tcp+udp:1.1.1.1";
ports.dns = 53;
# connectIPVersion = "v4";
# httpPort = "80";
upstreams.groups = {
default = [
"https://dns.quad9.net/dns-query"
];
};
caching = {
maxItemsCount = 100000;
maxTime = "30m";
minTime = "5m";
prefetchExpires = "2h";
prefetchMaxItemsCount = 100000;
prefetchThreshold = 5;
prefetching = true;
};
blocking = {
blockTTL = "1m";
blockType = "zeroIP";
loading = {
refreshPeriod = "24h";
strategy = "blocking";
downloads = {
attempts = 3;
cooldown = "10s";
timeout = "5m";
};
};
# SRC: https://oisd.nl
# SRC: https://v.firebog.net
denylists = {
suspicious = [
"https://raw.githubusercontent.com/PolishFiltersTeam/KADhosts/master/KADhosts.txt"
"https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" # https://github.com/StevenBlack/hosts
"https://v.firebog.net/hosts/static/w3kbl.txt"
];
ads = [
"https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext"
"https://raw.githubusercontent.com/bigdargon/hostsVN/master/hosts"
"https://v.firebog.net/hosts/AdguardDNS.txt"
"https://v.firebog.net/hosts/Admiral.txt"
"https://v.firebog.net/hosts/Easylist.txt"
];
tracking = [
"https://hostfiles.frogeye.fr/firstparty-trackers-hosts.txt"
"https://v.firebog.net/hosts/Easyprivacy.txt"
"https://v.firebog.net/hosts/Prigent-Ads.txt"
];
malicious = [
"https://gitlab.com/quidsup/notrack-blocklists/raw/master/notrack-malware.txt"
"https://osint.digitalside.it/Threat-Intel/lists/latestdomains.txt"
"https://phishing.army/download/phishing_army_blocklist_extended.txt"
"https://raw.githubusercontent.com/AssoEchap/stalkerware-indicators/master/generated/hosts"
"https://raw.githubusercontent.com/Spam404/lists/master/main-blacklist.txt"
"https://urlhaus.abuse.ch/downloads/hostfile/"
"https://v.firebog.net/hosts/Prigent-Crypto.txt"
"https://v.firebog.net/hosts/Prigent-Malware.txt"
];
other = [
"https://big.oisd.nl/domainswild"
"https://zerodot1.gitlab.io/CoinBlockerLists/hosts_browser"
];
};
# allowlists = {
# other = [
# "/.*.vk.com/"
# ];
# };
clientGroupsBlock = {
default = [
"ads"
"malicious"
"other"
"suspicious"
"tracking"
];
};
};
customDNS.mapping = let
block = host: { ${host} = "0.0.0.0"; };
in {
"voronind.com" = "10.0.0.1,fd09:8d46:b26::1";
}
// block "gosuslugi.ru"
// block "rutube.ru"
# // block "vk.com"
;
};
};
}

View file

@ -0,0 +1,9 @@
{ ... }: {
services.changedetection-io = {
enable = true;
baseURL = "change.voronind.com";
behindProxy = true;
listenAddress = "0.0.0.0";
port = 5001;
};
}

View file

@ -1,88 +0,0 @@
{ ... }: {
container = {
enable = true;
autoStart = true;
domain = "voronind.com";
host = "188.242.247.132";
interface = "enp8s0";
localAccess = "10.0.0.0/24";
storage = "/storage/hot/container";
module = {
change.enable = true;
cloud.enable = true;
dns.enable = true;
download.enable = true;
frkn.enable = true;
git.enable = true;
home.enable = true;
iot.enable = true;
jobber.enable = true;
mail.enable = true;
office.enable = true;
paper.enable = true;
pass.enable = true;
paste.enable = true;
postgres.enable = true;
print.enable = true;
proxy.enable = true;
rabbitmq.enable = true;
read.enable = true;
redis.enable = true;
search.enable = true;
status.enable = true;
stock.enable = true;
terraria.enable = true;
vpn.enable = true;
watch.enable = true;
yt.enable = true;
};
media = {
anime = [
"/storage/cold_1/anime"
"/storage/cold_2/anime"
];
book = [
"/storage/hot/book"
];
download = [
"/storage/hot/download"
];
manga = [
"/storage/cold_1/manga"
"/storage/cold_2/manga"
];
movie = [
"/storage/cold_1/movie"
"/storage/cold_2/movie"
];
music = [
"/storage/cold_2/music"
];
paper = [
"/storage/hot/paper"
];
porn = [
"/storage/cold_2/porn"
];
photo = [
"/storage/hot/container/cloud/data/data/cakee/files/photo"
"/storage/cold_1/backup/tmp/photo"
];
show = [
"/storage/cold_1/show"
"/storage/cold_2/show"
];
study = [
"/storage/cold_1/study"
"/storage/cold_2/study"
];
work = [
"/storage/cold_2/work"
];
youtube = [
"/storage/cold_1/youtube"
"/storage/cold_2/youtube"
];
};
};
}

View file

@ -0,0 +1,22 @@
# NOTE: Login to contaier, run passwd and use that root/pw combo for administration. `AllowFrom = all` doesn't seem to work.
# ipp://10.0.0.10
# Pantum M6500W-Series
{
__findFile,
pkgs,
...
} @args: let
package = pkgs.callPackage <package/print> args;
in {
services.printing = {
enable = true;
allowFrom = [ "all" ];
browsing = true;
defaultShared = true;
drivers = [ package ];
listenAddresses = [ "0.0.0.0:631" ];
startWhenNeeded = true;
stateless = false;
webInterface = true;
};
}

View file

@ -0,0 +1,23 @@
{ ... }: {
services.cloudflare-dyndns = {
enable = true;
apiTokenFile = "/storage/hot/data/CfToken";
deleteMissing = false;
ipv4 = true;
ipv6 = true;
proxied = false;
domains = let
domain = "voronind.com";
in [
domain
] ++ map (sub: "${sub}.${domain}") [
"cloud"
"git"
"mail"
"office"
"paste"
"play"
"vpn"
];
};
}

View file

@ -0,0 +1,14 @@
{
lib,
...
}: {
services.deluge = {
enable = true;
web.enable = true;
};
systemd.services.deluged.serviceConfig = {
MemoryMax = "4G";
Restart = lib.mkForce "always";
RuntimeMaxSec = "3h";
};
}

View file

@ -0,0 +1,54 @@
{ ... }: {
services.forgejo = {
enable = true;
stateDir = "/var/lib/forgejo";
database = {
createDatabase = true;
name = "forgejo";
type = "postgres";
user = "forgejo";
};
settings = let
gcArgs = "--aggressive --no-cruft --prune=now";
gcTimeout = 600;
in {
"cron.cleanup_actions".ENABLED = true;
"cron.update_mirrors".SCHEDULE = "@midnight";
"git".GC_ARGS = gcArgs;
"git.timeout".GC = gcTimeout;
"log".LEVEL = "Error";
"repo-archive".ENABLED = false;
"repository.issue".MAX_PINNED = 99999;
"repository.pull-request".DEFAULT_MERGE_STYLE = "rebase";
"service".DISABLE_REGISTRATION = true;
"server" = {
DOMAIN = "git.voronind.com";
HTTP_ADDR = "0.0.0.0";
ROOT_URL = "https://git.voronind.com";
BUILTIN_SSH_SERVER_USER = "git";
DISABLE_SSH = false;
SSH_PORT = 22144;
START_SSH_SERVER = true;
};
"ui" = {
AMBIGUOUS_UNICODE_DETECTION = false;
};
"repository" = {
DEFAULT_PRIVATE = "private";
DEFAULT_PUSH_CREATE_PRIVATE = true;
};
"cron" = {
ENABLED = true;
RUN_AT_START = true;
};
"cron.git_gc_repos" = {
ENABLED = true;
ARGS = gcArgs;
SCHEDULE = "@midnight";
TIMEOUT = gcTimeout;
};
};
};
}

View file

@ -0,0 +1,41 @@
{
pkgs,
...
}: {
services = {
tor = {
enable = true;
openFirewall = true;
settings = let
exclude = "{RU},{UA},{BY},{KZ},{CN},{??}";
in {
# ExcludeExitNodes = exclude;
# ExcludeNodes = exclude;
# DNSPort = dnsport;
UseBridges = true;
ClientTransportPlugin = "obfs4 exec ${pkgs.obfs4}/bin/lyrebird";
Bridge = [
"obfs4 121.45.140.249:12123 0922E212E33B04F0B7C1E398161E8EDE06734F26 cert=3AQ4iJFAzxzt7a/zgXIiFEs6fvrXInXt1Dtr09DgnpvUzG/iiyRTdXYZKSYpI124Zt3ZUA iat-mode=0"
"obfs4 145.239.31.71:10161 882125D15B59BB82BE66F999056CB676D3F061F8 cert=AnD+EvcBMuQDVM7PwW7NgFAzW1M5jDm7DjQtIIcBSjoyAf1FJ2p535rrYL2Kk8POAd0+aw iat-mode=0"
"obfs4 79.137.11.45:45072 ECA3197D49A29DDECD4ACBF9BCF15E4987B78137 cert=2FKyLWkPgMNCWxBD3cNOTRxJH3XP+HdStPGKMjJfw2YbvVjihIp3X2BCrtxQya9m5II5XA iat-mode=0"
"obfs4 94.103.89.153:4443 5617848964FD6546968B5BF3FFA6C11BCCABE58B cert=tYsmuuTe9phJS0Gh8NKIpkVZP/XKs7gJCqi31o8LClwYetxzFz0fQZgsMwhNcIlZ0HG5LA iat-mode=0"
];
};
client = {
enable = true;
# dns.enable = true;
socksListenAddress = {
IsolateDestAddr = true;
port = 9050;
# addr = cfg.address;
# port = cfg.torport;
};
};
};
xray = {
enable = true;
settingsFile = "/storage/hot/data/XrayClient.json";
};
};
}

View file

@ -0,0 +1,16 @@
{ ... }: {
services.grocy = {
enable = true;
# dataDir = "/var/lib/grocy";
hostName = "stock.voronind.com";
nginx.enableSSL = false;
settings = {
calendar = {
firstDayOfWeek = 1;
showWeekNumber = true;
};
culture = "en";
currency = "RUB";
};
};
}

View file

@ -0,0 +1,45 @@
{ ... }: {
# Allow Hass to talk to Zigbee dongle.
users.users.hass.extraGroups = [
"dialout"
"tty"
];
services.home-assistant = {
# NOTE: Missing: hacs. Inside hacs: `card-mod`, `Clock Weather Card`, `WallPanel` and `Yandex.Station`.
enable = true;
# NOTE: Using imperative config because of secrets.
config = null;
extraComponents = [
"caldav"
"met"
"sun"
"systemmonitor"
"zha"
];
extraPackages = python3Packages: with python3Packages; [
aiodhcpwatcher
aiodiscover
aiogithubapi
arrow
async-upnp-client
av
go2rtc-client
gtts
ha-ffmpeg
hassil
home-assistant-intents
mutagen
numpy
pymicro-vad
pynacl
pyspeex-noise
python-telegram-bot
pyturbojpeg
zeroconf
];
# lovelaceConfig = {
# title = "Home IoT control center.";
# };
};
}

View file

@ -0,0 +1,15 @@
{
__findFile,
pkgs,
util,
...
} @args: let
package = (pkgs.callPackage <package/homer> args);
in {
services.nginx = {
enable = true;
virtualHosts."home.voronind.com" = {
root = "${package}";
};
};
}

View file

@ -0,0 +1,31 @@
{
__findFile,
config,
inputs,
pkgs,
pkgsMaster,
...
}: {
disabledModules = [ "services/web-apps/invidious.nix" ];
imports = [ "${inputs.nixpkgsMaster}/nixos/modules/services/web-apps/invidious.nix" ];
services.invidious = {
enable = true;
domain = "yt.voronind.com";
package = pkgsMaster.invidious;
port = 3001;
nginx.enable = false;
database = {
createLocally = true;
# passwordFile = "${pkgs.writeText "InvidiousDbPassword" "invidious"}";
};
settings = {
admins = [ "root" ];
captcha_enabled = false;
check_tables = true;
external_port = 443;
https_only = true;
registration_enabled = false;
};
};
}

View file

@ -0,0 +1,14 @@
{ ... }: {
# systemd.services.jellyfin.serviceConfig.MemoryMax = cfg.memLimit;
users.users.jellyfin.extraGroups = [
"video"
"render"
];
services.jellyfin = {
enable = true;
# cacheDir = "/var/cache/jellyfin";
# dataDir = "/var/lib/jellyfin";
};
}

View file

@ -0,0 +1,70 @@
# Use `nixos-container login jobber` as root and empty pw.
{
__findFile,
const,
lib,
pkgsJobber,
poetry2nixJobber,
...
}: let
script = import <package/jobber> {
pkgs = pkgsJobber;
poetry2nix = poetry2nixJobber;
};
in {
networking.nat = {
enable = true;
externalInterface = "enp8s0";
internalInterfaces = [ "ve-+" ];
};
containers.jobber = {
autoStart = true;
enableTun = true;
privateNetwork = true;
hostAddress = "188.242.247.132";
localAddress = "10.1.0.2";
config = { ... }: let
packages = [
script
] ++ (with pkgsJobber; [
firefox
geckodriver
openvpn
python311
]);
in {
boot.isContainer = true;
system.stateVersion = const.stateVersion;
users = {
users.root.password = "";
mutableUsers = false;
};
networking = {
useHostResolvConf = lib.mkForce false;
nameservers = [
"10.30.218.2"
];
};
systemd.services.jobber = {
description = "My job is pushing the button.";
enable = true;
path = packages;
wantedBy = [
"multi-user.target"
];
environment = {
PYTHONDONTWRITEBYTECODE = "1";
PYTHONUNBUFFERED = "1";
};
serviceConfig = {
ExecStart = "${script}/bin/jobber -u";
Restart = "on-failure";
Type = "simple";
};
};
};
};
}

View file

@ -0,0 +1,13 @@
{
pkgs,
...
}: {
services.kavita = {
enable = true;
tokenKeyFile = pkgs.writeText "KavitaToken" "xY19aQOa939/Ie6GCRGbubVK8zRwrgBY/20AuyMpYshUjwK1Uyl7bw1yknVh6jJIFIfwq2vAjeotOUq7NEsf9Q==";
settings = {
# IpAddresses = cfg.address;
Port = 5000;
};
};
}

View file

@ -0,0 +1,166 @@
# REF: https://nixos-mailserver.readthedocs.io/en/latest/setup-guide.html
{
pkgs,
util,
...
}: let
domain = "voronind.com";
# SEE: https://gitlab.com/simple-nixos-mailserver/nixos-mailserver#release-branches
version = "24.05";
sha256 = "sha256:0clvw4622mqzk1aqw1qn6shl9pai097q62mq1ibzscnjayhp278b";
in {
imports = [
(builtins.fetchTarball {
inherit sha256;
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-${version}/nixos-mailserver-nixos-${version}.tar.gz";
})
];
mailserver = {
enable = true;
domains = [ domain ];
fqdn = "mail.${domain}";
sendingFqdn = domain;
localDnsResolver = false;
# Use `mkpasswd -sm bcrypt`.
loginAccounts = let
defaultQuota = "1G";
in {
"admin@${domain}" = {
hashedPassword = "$2b$05$1O.dxXxaVshcBNybcqDRYuTlnYt3jDBwfPZWoDtP4BjOLoL0StYsi";
name = "admin";
quota = defaultQuota;
};
"account@${domain}" = {
hashedPassword = "$2b$05$sCyZHdk98KqQ1qsTIvbrUeRJlNBOwBqDgpdc1QxiSnONlEkZ8xGNO";
name = "account";
quota = defaultQuota;
};
"hi@${domain}" = {
hashedPassword = "$2b$05$6fT5hIhzIasNfp9IQr/ds.5RuxH95VKU3QJWlX3hmrAzDF3mExanq";
name = "hi";
quota = defaultQuota;
aliases = [
"voronind@${domain}"
];
};
"job@${domain}" = {
hashedPassword = "$2b$05$.sUmv2.9EWPfLwJn/oZw2e1UbR7HrpNQ2THc5jjX3ysy7CY8ZWHUC";
name = "job";
quota = defaultQuota;
};
"trash@${domain}" = {
hashedPassword = "$2b$05$kn5ygZjN9NR3LXjnKKRw/.DXaZQNW.1XEottlCFIoKiDpIj.JGLJm";
name = "trash";
quota = defaultQuota;
catchAll = [
domain
];
};
"noreply@${domain}" = {
hashedPassword = "$2b$05$TaKwoYmcmkAhsRRv6xG5wOkChcz50cB9BP6QPUDKNAcxMbrY6AeMK";
name = "noreply";
quota = defaultQuota;
sendOnly = true;
};
};
enableImap = true;
enableImapSsl = true;
enableSubmission = true;
enableSubmissionSsl = true;
enableManageSieve = true;
virusScanning = false;
certificateFile = "/etc/letsencrypt/live/${domain}/cert.pem";
certificateScheme = "manual";
keyFile = "/etc/letsencrypt/live/${domain}/privkey.pem";
dkimKeyDirectory = "/var/dkim";
indexDir = "/var/lib/dovecot/indices";
mailDirectory = "/var/vmail";
sieveDirectory = "/var/sieve";
mailboxes = let
mkSpecialBox = specialUse: {
${specialUse} = {
inherit specialUse;
auto = "subscribe";
};
};
in builtins.foldl' (acc: box: acc // (mkSpecialBox box)) {} [
"All"
"Archive"
"Drafts"
"Junk"
"Sent"
"Trash"
];
dmarcReporting = {
inherit domain;
enable = true;
organizationName = "voronind";
# email = "noreply@${domain}";
};
# monitoring = {
# enable = true;
# alertAddress = "admin@${domain}";
# };
};
services = {
roundcube = {
enable = true;
hostName = "mail.${domain}";
dicts = with pkgs.aspellDicts; [
en
ru
];
plugins = [
"managesieve"
];
extraConfig = util.trimTabs ''
$config['smtp_server'] = "localhost:25";
$config['smtp_auth_type'] = null;
$config['smtp_user'] = "";
$config['smtp_pass'] = "";
# $config['smtp_user'] = "%u";
# $config['smtp_pass'] = "%p";
'';
};
};
systemd = {
services.autoexpunge = {
description = "Delete old mail";
serviceConfig = {
Type = "oneshot";
};
path = [
pkgs.dovecot
];
script = util.trimTabs ''
doveadm expunge -A mailbox Junk SENTBEFORE 7d
doveadm expunge -A mailbox Trash SENTBEFORE 30d
doveadm expunge -u trash@voronind.com mailbox Inbox SENTBEFORE 30d
doveadm purge -A
'';
};
timers.autoexpunge = {
timerConfig = {
OnCalendar = "daily";
Persistent = true;
Unit = "autoexpunge.service";
};
wantedBy = [
"timers.target"
];
};
};
}

View file

@ -1,6 +1,5 @@
# 10.0.0.0/24 & fd09:8d46:0b26::/48 - phys clients (lan). # 10.0.0.0/24 & fd09:8d46:0b26::/48 - phys clients (lan).
# 10.1.0.0/24 & fd76:c80a:8e86::/48 - containers. # 10.0.1.0/24 - vpn clients.
# 10.1.1.0/24 - vpn clients.
{ {
config, config,
const, const,
@ -8,10 +7,8 @@
util, util,
... ...
}: let }: let
external = "188.242.247.132"; # Wan host IP address. internal = "10.0.0.1"; # Lan host IP address.
internal = "10.0.0.1"; # Lan host IP address. internal6 = "fd09:8d46:b26:0:8079:82ff:fe1a:916a"; # Lan host IP6 address.
external6 = "2a05:3580:f42c:c800:aaa1:59ff:fe47:fda2"; # Wan host IP6 address.
internal6 = "fd09:8d46:b26::1"; # Lan host IP6 address.
lan = "br0"; # Lan interface. lan = "br0"; # Lan interface.
wan = "enp8s0"; # Wan interface. wan = "enp8s0"; # Wan interface.
@ -19,6 +16,9 @@ in {
# Disable SSH access from everywhere, configure access bellow. # Disable SSH access from everywhere, configure access bellow.
services.openssh.openFirewall = false; services.openssh.openFirewall = false;
# Disable systemd-resolved for DNS server.
services.resolved.enable = false;
# NOTE: Debugging. # NOTE: Debugging.
# systemd.services."systemd-networkd".environment.SYSTEMD_LOG_LEVEL = "debug"; # systemd.services."systemd-networkd".environment.SYSTEMD_LOG_LEVEL = "debug";
@ -82,7 +82,7 @@ in {
linkConfig.RequiredForOnline = "carrier"; linkConfig.RequiredForOnline = "carrier";
address = [ address = [
"${internal}/24" "${internal}/24"
"${internal6}/48" # "${internal6}/48"
]; ];
networkConfig = { networkConfig = {
DHCPPrefixDelegation = true; DHCPPrefixDelegation = true;
@ -96,7 +96,7 @@ in {
}; };
ipv6Prefixes = [ ipv6Prefixes = [
{ {
AddressAutoconfiguration = true; Assign = true;
Prefix = "${internal6}/64"; Prefix = "${internal6}/64";
} }
]; ];
@ -120,7 +120,7 @@ in {
UplinkInterface = wan; UplinkInterface = wan;
}; };
dhcpServerStaticLeases = let dhcpServerStaticLeases = let
mkStatic = Address: MACAddress: { dhcpServerStaticLeaseConfig = { inherit Address MACAddress; }; }; mkStatic = Address: MACAddress: { inherit Address MACAddress; };
in [ in [
# TODO: Add pocket. # TODO: Add pocket.
(mkStatic "10.0.0.2" "9c:9d:7e:8e:3d:c7") # Wifi AP. (mkStatic "10.0.0.2" "9c:9d:7e:8e:3d:c7") # Wifi AP.
@ -163,23 +163,9 @@ in {
logRefusedPackets = false; logRefusedPackets = false;
logRefusedUnicastsOnly = true; logRefusedUnicastsOnly = true;
extraCommands = let extraCommands = util.trimTabs ''
# Container configs. # Wan access for 10.0.0.0/8 subnet.
cfg = config.container.module; iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -d 0/0 -o ${wan} -j MASQUERADE
# Const.
tcp = "tcp";
udp = "udp";
# Create port forwarding rule.
mkForward = src: sport: dst: dport: proto: "iptables -t nat -I PREROUTING -d ${src} -p ${proto} --dport ${toString sport} -j DNAT --to-destination ${dst}:${toString dport}\n";
in (util.trimTabs ''
# Wan access for 10.0.0.0/24 subnet.
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -d 0/0 -o ${wan} -j MASQUERADE
# Full access from VPN clients.
iptables -I INPUT -j ACCEPT -s ${cfg.vpn.clients} -d ${internal}
iptables -I INPUT -j ACCEPT -s ${cfg.frkn.address} -d ${internal}
# Full access from Lan. # Full access from Lan.
iptables -I INPUT -j ACCEPT -i ${lan} -d ${internal} iptables -I INPUT -j ACCEPT -i ${lan} -d ${internal}
@ -187,52 +173,29 @@ in {
# Allow DHCP. # Allow DHCP.
iptables -I INPUT -j ACCEPT -i ${lan} -p udp --dport 67 iptables -I INPUT -j ACCEPT -i ${lan} -p udp --dport 67
'')
# Expose DNS server for internal network.
+ (mkForward internal cfg.dns.port cfg.dns.address cfg.dns.port tcp)
+ (mkForward internal cfg.dns.port cfg.dns.address cfg.dns.port udp)
# Email server. # Public email server.
+ (mkForward external 25 cfg.mail.address 25 tcp) ip46tables -I INPUT -j ACCEPT -i ${wan} -p tcp --dport 25
+ (mkForward internal 25 cfg.mail.address 25 tcp)
+ (mkForward internal 465 cfg.mail.address 465 tcp)
+ (mkForward internal 993 cfg.mail.address 993 tcp)
# FRKN internal proxy server. # Public VPN service.
+ (mkForward internal cfg.frkn.port cfg.frkn.address cfg.frkn.port tcp) ip46tables -I INPUT -j ACCEPT -i ${wan} -p udp --dport 22145
+ (mkForward internal cfg.frkn.torport cfg.frkn.address cfg.frkn.torport tcp) iptables -I INPUT -j ACCEPT -s 10.0.1.0/24 -d ${internal}
+ (mkForward internal cfg.frkn.xrayport cfg.frkn.address cfg.frkn.xrayport tcp)
+ (mkForward internal cfg.frkn.port cfg.frkn.address cfg.frkn.port udp)
+ (mkForward internal cfg.frkn.torport cfg.frkn.address cfg.frkn.torport udp)
+ (mkForward internal cfg.frkn.xrayport cfg.frkn.address cfg.frkn.xrayport udp)
# VPN connections. # Public Nginx.
+ (mkForward external cfg.vpn.port cfg.vpn.address cfg.vpn.port udp) ip46tables -I INPUT -j ACCEPT -i ${wan} -p tcp --dport 443
# Nginx HTTP. # Deluge torrenting ports.
+ (mkForward external cfg.proxy.port cfg.proxy.address cfg.proxy.port tcp) ip46tables -I INPUT -j ACCEPT -i ${wan} -p tcp --dport 54630
+ (mkForward internal cfg.proxy.port cfg.proxy.address cfg.proxy.port tcp) ip46tables -I INPUT -j ACCEPT -i ${wan} -p udp --dport 54630
ip46tables -I INPUT -j ACCEPT -i ${wan} -p tcp --dport 54631
ip46tables -I INPUT -j ACCEPT -i ${wan} -p udp --dport 54631
# Download ports for torrents. # Terraria server.
+ (mkForward external 54630 cfg.download.address 54630 tcp) ip46tables -I INPUT -j ACCEPT -i ${wan} -p tcp --dport 22777
+ (mkForward external 54631 cfg.download.address 54631 tcp)
+ (mkForward external 54630 cfg.download.address 54630 udp)
+ (mkForward external 54631 cfg.download.address 54631 udp)
# Git SSH connections. # Public SSH access.
+ (mkForward external cfg.git.portSsh cfg.git.address cfg.git.portSsh tcp) # ip46tables -I INPUT -j ACCEPT -i ${wan} -p tcp --dport 22143
+ (mkForward internal cfg.git.portSsh cfg.git.address cfg.git.portSsh tcp) '';
# Print serivce.
+ (mkForward internal cfg.print.port cfg.print.address cfg.print.port tcp)
# Terraria server.
+ (mkForward external cfg.terraria.port cfg.terraria.address cfg.terraria.port tcp)
+ (mkForward internal cfg.terraria.port cfg.terraria.address cfg.terraria.port tcp)
# SSH access from WAN.
# + (mkForward external 22143 config.container.host 22143 tcp)
;
}; };
}; };
} }

View file

@ -0,0 +1,38 @@
{
config,
pkgs,
...
}: {
services.nextcloud = {
enable = true;
database.createLocally = true;
extraAppsEnable = true;
hostName = "cloud.voronind.com";
https = true;
# package = pkgs.nextcloud29;
# phpOptions = {
# memory_limit = lib.mkForce "20G";
# };
config = {
adminpassFile = "${pkgs.writeText "NextcloudPassword" "root"}";
adminuser = "root";
dbname = "nextcloud";
# dbpassFile = "${pkgs.writeText "NextcloudDbPassword" "nextcloud"}";
dbtype = "pgsql";
dbuser = "nextcloud";
};
extraApps = {
inherit (config.services.nextcloud.package.packages.apps)
contacts calendar onlyoffice;
};
settings = {
allow_local_remote_servers = true;
trusted_domains = [
"cloud.voronind.com"
];
trusted_proxies = [
# proxy.address
];
};
};
}

View file

@ -0,0 +1,50 @@
# NOTE: To generate self-signed certs use: `openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./privkey.pem -out ./fullchain.pem`
# For dhparams: `openssl dhparam -out ./ssl-dhparam.pem 4096`
# Example for options-ssl-nginx.conf:
# ```
# ssl_session_cache shared:le_nginx_SSL:10m;
# ssl_session_timeout 1440m;
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_prefer_server_ciphers off;
# ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
# ```
# For certbot to generate new keys: `certbot certonly --manual --manual-public-ip-logging-ok --preferred-challenges dns-01 --server https://acme-v02.api.letsencrypt.org/directory -d "*.voronind.com" -d voronind.com`
{
pkgs,
util,
...
} @args: let
virtualHosts = util.catSet (util.ls ./nginx) args;
in {
environment.systemPackages = with pkgs; [ certbot ];
services.nginx = {
inherit virtualHosts;
enable = true;
clientMaxBodySize = "4096m";
recommendedOptimisation = true;
recommendedProxySettings = true;
appendConfig = util.trimTabs ''
worker_processes 4;
'';
eventsConfig = util.trimTabs ''
worker_connections 4096;
'';
appendHttpConfig = util.trimTabs ''
proxy_max_temp_file_size 0;
proxy_buffering off;
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
return 403;
}
'';
};
}

View file

@ -0,0 +1,20 @@
# NOTE: Imperative part:
# 1. You need to change PSQL tables owner from root to onlyoffice, too. They don't do that automatically for some reason.
# 2. TODO: Generate JWT secret at /var/lib/onlyoffice/jwt, i.e. 9wLfMGha1YrfvWpb5hyYjZf8pvJQ3swS
# SEE: https://git.voronind.com/voronind/nixos/issues/74
{
config,
lib,
...
}: {
# services.onlyoffice = {
# enable = true;
# hostname = "office.voronind.com";
# jwtSecretFile = "/var/www/onlyoffice/jwt";
#
# postgresName = "onlyoffice";
# postgresUser = "onlyoffice";
# # postgresPasswordFile = "${pkgs.writeText "OfficeDbPassword" dbName}";
# # rabbitmqUrl = "amqp://guest:guest@${config.container.module.rabbitmq.address}:${toString config.container.module.rabbitmq.port}";
# };
}

View file

@ -0,0 +1,62 @@
# easyrsa --days=36500 init-pki
# easyrsa --days=36500 build-ca
# easyrsa --days=36500 build-server-full <SERVER_NAME> nopass
# easyrsa --days=36500 build-client-full <CLIENT_NAME> nopass
# easyrsa gen-crl
# openssl dhparam -out dh2048.pem 2048
# Don't forget to set tls hostname on the client to match SERVER_NAME *AND* disable ipv6 ?
# easyrsa revoke <CLIENT_NAME>
# easyrsa gen-crl
# restart container
# SEE: https://github.com/OpenVPN/openvpn/blob/master/sample/sample-config-files/server.conf
# SRC: https://github.com/TinCanTech/easy-tls
{
pkgs,
util,
...
}: {
environment.systemPackages = with pkgs; [
easyrsa
openvpn
];
users = {
groups.openvpn = {};
users.openvpn = {
group = "openvpn";
isSystemUser = true;
# uid = 1000;
};
};
# NOTE: Change the `server` to match `cfg.clients` or write a substring here.
services.openvpn.servers.vpn = {
autoStart = true;
config = util.trimTabs ''
ca /var/lib/ovpn/pki/ca.crt
cert /var/lib/ovpn/pki/issued/home.crt
client-to-client
crl-verify /var/lib/ovpn/pki/crl.pem
dev tun
dh /var/lib/ovpn/dh2048.pem
explicit-exit-notify 1
group openvpn
ifconfig-pool-persist ipp.txt
keepalive 10 120
key /var/lib/ovpn/pki/private/home.key
persist-tun
port 22145
proto udp
push "dhcp-option DNS 10.0.0.1"
push "dhcp-option DNS 10.0.0.1"
push "route 10.0.0.0 255.0.0.0"
server 10.0.1.0 255.255.255.0
status openvpn-status.log
topology subnet
user openvpn
verb 4
'';
};
}

View file

@ -0,0 +1,24 @@
{
lib,
pkgs,
...
}: {
services.paperless = {
enable = true;
address = "0.0.0.0";
dataDir = "/var/lib/paperless";
# port = cfg.port;
passwordFile = pkgs.writeText "PaperlessPassword" "root"; # NOTE: Only for initial setup, change later.
settings = {
PAPERLESS_ADMIN_USER = "root";
PAPERLESS_DBHOST = "/run/postgresql";
PAPERLESS_DBENGINE = "postgresql";
PAPERLESS_DBNAME = "paperless";
PAPERLESS_DBPASS = "paperless";
PAPERLESS_DBUSER = "paperless";
PAPERLESS_OCR_LANGUAGE = "rus";
# PAPERLESS_REDIS = "redis://${config.container.module.redis.address}:${toString config.container.module.redis.port}";
PAPERLESS_URL = "https://paper.voronind.com";
};
};
}

View file

@ -0,0 +1,48 @@
{
config,
lib,
pkgs,
...
}: {
services.postgresql = let
# Populate with services here.
configurations = [
"forgejo"
"invidious"
"mattermost"
"nextcloud"
"onlyoffice"
"paperless"
"privatebin"
];
ensureDatabases = [ "root" ] ++ configurations;
ensureUsers = map (name: {
inherit name;
ensureDBOwnership = true;
ensureClauses = if name == "root" then {
createdb = true;
createrole = true;
superuser = true;
} else { };
}) ensureDatabases;
authentication = "local all all trust";
in {
inherit authentication ensureDatabases ensureUsers;
enable = true;
dataDir = "/var/lib/postgresql/14";
package = pkgs.postgresql_14;
# NOTE: Debug mode.
# settings = {
# log_connections = true;
# log_destination = lib.mkForce "syslog";
# log_disconnections = true;
# log_statement = "all";
# logging_collector = true;
# };
};
}

View file

@ -0,0 +1,45 @@
{
__findFile,
pkgs,
...
} @args: let
package = (pkgs.callPackage <package/privatebin> args);
in {
environment.systemPackages = [ package ];
systemd.packages = [ package ];
users.users.paste = {
group = "nginx";
isSystemUser = true;
};
services = {
phpfpm.pools.paste = {
group = "nginx";
user = "paste";
phpPackage = pkgs.php;
settings = {
"catch_workers_output" = true;
"listen.owner" = "nginx";
"php_admin_flag[log_errors]" = true;
"php_admin_value[error_log]" = "stderr";
"pm" = "dynamic";
"pm.max_children" = "32";
"pm.max_requests" = "500";
"pm.max_spare_servers" = "4";
"pm.min_spare_servers" = "2";
"pm.start_servers" = "2";
};
phpEnv = {
# CONFIG_PATH = "${package}/cfg"; # NOTE: Not working?
};
};
nginx = {
enable = true;
virtualHosts."paste.voronind.com" = {
root = "${package}";
};
};
};
}

View file

@ -0,0 +1,8 @@
{ ... }: {
services.rabbitmq = {
enable = true;
# configItems = {
# "loopback_users" = "none";
# };
};
}

View file

@ -0,0 +1,9 @@
{ ... }: {
services.redis.servers.main = {
enable = true;
# port = cfg.port;
# extraParams = [
# "--protected-mode no"
# ];
};
}

View file

@ -0,0 +1,108 @@
{
config,
pkgs,
...
}: {
services.searx = {
enable = true;
package = pkgs.searxng;
# REF: https://github.com/searxng/searxng/blob/master/searx/settings.yml
settings = {
general = {
debug = false;
enable_metrics = false;
instance_name = "SearX";
};
server = {
# bind_address = cfg.address;
image_proxy = false;
limiter = false;
method = "GET";
port = 34972;
public_instance = false;
secret_key = "searxxx";
};
search = {
autocomplete = "";
autocomplete_min = 4;
default_lang = "auto";
safe_search = 0;
};
ui = {
center_alignment = false;
default_locale = "";
default_theme = "simple";
hotkeys = "vim";
infinite_scroll = false;
simple_style = "dark";
};
outgoing = {
enable_http2 = true;
max_request_timeout = 10.0;
pool_connections = 100;
pool_maxsize = 20;
request_timeout = 3.0;
# proxies = {
# "all://" = with config.container.module; [
# # "socks5:${frkn.address}:${frkn.port}"
# "socks5:${frkn.address}:1081"
# # "socks5:${frkn.address}:9150"
# ];
# };
# using_tor_proxy = true;
# extra_proxy_timeout = 10;
};
# plugins = [ ];
enabled_plugins = [
"Basic Calculator"
"Hostnames plugin"
"Tracker URL remover"
];
hostnames = {
replace = {
"(.*\.)?youtu\.be$" = "yt.voronind.com";
"(.*\.)?youtube\.com$" = "yt.voronind.com";
};
remove = [
"(.*\.)?dzen\.ru$"
"(.*\.)?facebook.com$"
"(.*\.)?gosuslugi\.ru$"
"(.*\.)?quora\.com$"
"(.*\.)?rutube\.ru$"
"(.*\.)?vk\.com$"
];
low_priority = [
"(.*\.)?google(\..*)?$"
"(.*\.)?microsoft\.com$"
];
high_priority = [
"(.*\.)?4pda.to$"
"(.*\.)?github.com$"
"(.*\.)?wikipedia.org$"
];
};
categories_as_tabs = {
files = { };
general = { };
images = { };
it = { };
map = { };
news = { };
videos = { };
};
engines = let
mkEnable = name: {
inherit name;
disabled = false;
};
mkDisable = name: {
inherit name;
disabled = true;
};
in [
(mkEnable "bing")
(mkDisable "qwant")
];
};
};
}

View file

@ -0,0 +1,20 @@
{
pkgs,
...
}: {
# NOTE: Admin with `tmux -S /var/lib/terraria/terraria.sock attach-session -t 0`
environment.systemPackages = with pkgs; [ tmux ];
services.terraria = {
enable = true;
autoCreatedWorldSize = "large";
messageOfTheDay = "<3";
maxPlayers = 4;
noUPnP = false;
openFirewall = false;
password = "mishadima143";
port = 22777;
secure = false;
worldPath = "/var/lib/terraria/.local/share/Terraria/Worlds/World.wld";
};
}

View file

@ -0,0 +1,19 @@
{
lib,
...
}: {
services.uptime-kuma = {
enable = true;
settings = {
DATA_DIR = "/var/lib/uptime-kuma/";
PORT = "64901";
# HOST = cfg.address;
};
};
systemd.services.uptime-kuma = {
serviceConfig = {
DynamicUser = lib.mkForce false;
};
};
}

View file

@ -0,0 +1,15 @@
{ ... }: {
services.vaultwarden = {
enable = true;
dbBackend = "sqlite";
environmentFile = "/var/lib/vaultwarden/Env";
config = {
DATA_FOLDER = "/var/lib/vaultwarden";
DOMAIN = "https://pass.voronind.com";
# ROCKET_ADDRESS = cfg.address;
ROCKET_PORT = 8001;
SIGNUPS_ALLOWED = false;
WEB_VAULT_ENABLED = true;
};
};
}

View file

@ -0,0 +1,20 @@
{
util,
...
}: {
"camera.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
return 301 rtsp://10.0.0.12:554/live/main;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,23 @@
{
util,
...
}: {
"change.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
proxy_pass http://127.0.0.1:5001$request_uri;
add_header Referrer-Policy 'origin';
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,25 @@
{
util,
...
}: {
"print.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
proxy_pass http://127.0.0.1:631$request_uri;
proxy_set_header Host "127.0.0.1";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,20 @@
{
util,
...
}: {
"download.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
proxy_pass http://127.0.0.1:8112$request_uri;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,24 @@
{
util,
...
}: {
"git.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location ~ ^/(admin|api|user) {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
proxy_pass http://127.0.0.1:3000$request_uri;
}
location / {
proxy_pass http://127.0.0.1:3000$request_uri;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,30 @@
{
config,
lib,
util,
...
}: {
"stock.voronind.com" = {
locations."~ \\.php$".extraConfig = lib.mkForce (util.trimTabs ''
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:${config.services.phpfpm.pools.grocy.socket};
include ${config.services.nginx.package}/conf/fastcgi.conf;
include ${config.services.nginx.package}/conf/fastcgi_params;
'');
extraConfig = lib.mkForce (util.trimTabs ''
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
try_files $uri /index.php;
'');
};
}

View file

@ -0,0 +1,27 @@
{
util,
...
}: {
"iot.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:8123$request_uri;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,20 @@
{
util,
...
}: {
"home.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
try_files $uri $uri/index.html;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,30 @@
{
util,
...
}: {
"yt.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
proxy_pass http://127.0.0.1:3001$request_uri;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_hide_header Content-Security-Policy;
proxy_hide_header X-Frame-Options;
proxy_hide_header X-Content-Type-Options;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,20 @@
{
util,
...
}: {
"watch.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
proxy_pass http://127.0.0.1:8096$request_uri;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,20 @@
{
util,
...
}: {
"read.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
proxy_pass http://127.0.0.1:5000$request_uri;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

View file

@ -0,0 +1,29 @@
{
config,
lib,
util,
...
}: {
"mail.voronind.com" = {
enableACME = false;
forceSSL = false;
locations."~* \\.php(/|$)".extraConfig = lib.mkForce (util.trimTabs ''
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
fastcgi_pass unix:${config.services.phpfpm.pools.roundcube.socket};
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include ${config.services.nginx.package}/conf/fastcgi.conf;
'');
extraConfig = lib.mkForce (util.trimTabs ''
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'');
};
}

View file

@ -0,0 +1,22 @@
{
util,
...
}: {
"cloud.voronind.com" = {
locations."~ ^/(settings/admin|settings/users|settings/apps|login|api)".extraConfig = util.trimTabs ''
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
try_files $uri $uri/ /index.php$request_uri;
'';
extraConfig = util.trimTabs ''
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -0,0 +1,21 @@
{
lib,
util,
...
}: {
"office.voronind.com" = {
locations."/".extraConfig = lib.mkForce (util.trimTabs ''
add_header X-Forwarded-Proto https;
proxy_pass http://127.0.0.1:8000$request_uri;
'');
extraConfig = util.trimTabs ''
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
};
}

View file

@ -0,0 +1,20 @@
{
util,
...
}: {
"paper.voronind.com".extraConfig = util.trimTabs ''
listen 443 ssl;
location / {
allow 10.0.0.0/8;
allow fd09:8d46:b26::/48;
deny all;
proxy_pass http://127.0.0.1:28981$request_uri;
}
ssl_certificate /etc/letsencrypt/live/voronind.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/voronind.com/privkey.pem;
include /etc/letsencrypt/conf/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/conf/ssl-dhparams.pem;
'';
}

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