New way to deploy apps in my server

Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2026-04-11 02:26:00 +02:00
parent 29bdd6468a
commit 28b7923e47
22 changed files with 759 additions and 407 deletions
+7
View File
@@ -9,6 +9,13 @@
networking.firewall = {
allowedTCPPorts = [53];
allowedUDPPorts = [53];
# Allow containers to reach adguard UI (for glance dns-stats widget)
extraCommands = ''
iptables -I INPUT 1 -s 10.233.0.0/16 -p tcp --dport 3000 -j ACCEPT
'';
extraStopCommands = ''
iptables -D INPUT -s 10.233.0.0/16 -p tcp --dport 3000 -j ACCEPT 2>/dev/null || true
'';
};
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."adguard.${config.var.domain}" = "http://localhost:${toString config.services.adguardhome.port}";
+30 -23
View File
@@ -1,26 +1,33 @@
{ config, inputs, lib, ... }:
let
inherit (import ./mk-container.nix { inherit lib config; }) mkContainer;
in
{
config,
inputs,
pkgs,
...
}: {
services = {
nginx.virtualHosts."wall.local" = {
root = "${inputs.awesome-wallpapers.packages.${pkgs.system}.default}/share/awesome-wallpapers";
listen = [
{
addr = "127.0.0.1";
port = 8759;
}
];
locations."/" = {
tryFiles = "$uri $uri/ /index.html";
imports = [
(mkContainer {
name = "wallpapers";
hostIp = "10.233.4.1";
containerIp = "10.233.4.2";
nixosConfig = { pkgs, ... }: {
services.nginx = {
enable = true;
virtualHosts."wallpapers" = {
root = "${inputs.awesome-wallpapers.packages.${pkgs.system}.default}/share/awesome-wallpapers";
listen = [{ addr = "0.0.0.0"; port = 8080; }];
locations."/" = {
tryFiles = "$uri $uri/ /index.html";
};
extraConfig = ''
port_in_redirect off;
absolute_redirect off;
'';
};
};
networking.firewall.allowedTCPPorts = [ 8080 ];
system.stateVersion = "24.05";
};
extraConfig = ''
port_in_redirect off;
absolute_redirect off;
'';
};
cloudflared.tunnels."${config.var.tunnelId}".ingress."wallpapers.${config.var.domain}" = "http://127.0.0.1:8759";
};
})
];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."wallpapers.${config.var.domain}" = "http://10.233.4.2:8080";
}
+48 -36
View File
@@ -1,45 +1,57 @@
{
config,
inputs,
pkgs,
lib,
...
}: {
services = {
nginx.virtualHosts = {
"blog.local" = {
root = "${inputs.blog.packages.${pkgs.system}.default}/share/blog";
listen = [
{
addr = "127.0.0.1";
port = 8758;
}
];
locations."/" = {
tryFiles = "$uri $uri/ /index.html";
}: let
inherit (import ./mk-container.nix {inherit lib config;}) mkContainer;
domain = config.var.domain;
in {
imports = [
(mkContainer {
name = "blog";
hostIp = "10.233.3.1";
containerIp = "10.233.3.2";
nixosConfig = {pkgs, ...}: {
services.nginx = {
enable = true;
virtualHosts = {
"blog" = {
root = "${inputs.blog.packages.${pkgs.system}.default}/share/blog";
listen = [
{
addr = "0.0.0.0";
port = 8080;
}
];
locations."/" = {
tryFiles = "$uri $uri/ /index.html";
};
extraConfig = ''
port_in_redirect off;
absolute_redirect off;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' data: https://umami.${domain}; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://git.${domain}; connect-src 'self' https://umami.${domain};" always;
'';
};
"www-redirect" = {
listen = [
{
addr = "0.0.0.0";
port = 8081;
}
];
extraConfig = "return 301 https://${domain}$request_uri;";
};
};
};
extraConfig = ''
port_in_redirect off;
absolute_redirect off;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' data: https://umami.${config.var.domain}; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://git.${config.var.domain}; connect-src 'self' https://umami.${config.var.domain};" always;
'';
networking.firewall.allowedTCPPorts = [8080 8081];
system.stateVersion = "24.05";
};
})
];
"www.blog.local" = {
listen = [
{
addr = "127.0.0.1";
port = 8189;
}
];
extraConfig = ''
return 301 https://${config.var.domain}$request_uri;
'';
};
};
cloudflared.tunnels."${config.var.tunnelId}".ingress = {
"${config.var.domain}" = "http://127.0.0.1:8758";
"www.${config.var.domain}" = "http://127.0.0.1:8189";
};
services.cloudflared.tunnels."${config.var.tunnelId}".ingress = {
"${config.var.domain}" = "http://10.233.3.2:8080";
"www.${config.var.domain}" = "http://10.233.3.2:8081";
};
}
+24 -17
View File
@@ -1,19 +1,26 @@
# Cyberchef is a web app for analyzing and decoding data.
{ config, lib, ... }:
let
inherit (import ./mk-container.nix { inherit lib config; }) mkContainer;
in
{
config,
pkgs,
...
}: {
services = {
nginx.virtualHosts."cyberchef.local" = {
root = "${pkgs.cyberchef}/share/cyberchef";
listen = [
{
addr = "127.0.0.1";
port = 8754;
}
];
};
cloudflared.tunnels."${config.var.tunnelId}".ingress."cyberchef.${config.var.domain}" = "http://localhost:8754";
};
imports = [
(mkContainer {
name = "cyberchef";
hostIp = "10.233.5.1";
containerIp = "10.233.5.2";
nixosConfig = { pkgs, ... }: {
services.nginx = {
enable = true;
virtualHosts."cyberchef" = {
root = "${pkgs.cyberchef}/share/cyberchef";
listen = [{ addr = "0.0.0.0"; port = 8080; }];
};
};
networking.firewall.allowedTCPPorts = [ 8080 ];
system.stateVersion = "24.05";
};
})
];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."cyberchef.${config.var.domain}" = "http://10.233.5.2:8080";
}
+31 -6
View File
@@ -1,8 +1,33 @@
{config, ...}: {
services.default-creds = {
enable = true;
port = 8087;
};
{ config, inputs, lib, ... }:
let
inherit (import ./mk-container.nix { inherit lib config; }) mkContainer;
domain = config.var.domain;
in
{
imports = [
(mkContainer {
name = "def-creds";
hostIp = "10.233.6.1";
containerIp = "10.233.6.2";
nixosConfig = { ... }: {
imports = [ inputs.default-creds.nixosModules.default ];
services.default-creds = {
enable = true;
port = 8087;
};
networking.firewall.allowedTCPPorts = [ 8087 ];
systemd.services.default-creds.environment = {
HOST = lib.mkForce "0.0.0.0";
PUBLIC_UMAMI_URL = "https://umami.${domain}";
PUBLIC_UMAMI_WEBSITE_ID = "7197484c-01ad-488e-9caa-5ab7b7595f08";
UMAMI_URL = "https://umami.${domain}";
UMAMI_WEBSITE_ID = "7197484c-01ad-488e-9caa-5ab7b7595f08";
};
system.stateVersion = "24.05";
};
})
];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."default-creds.${config.var.domain}" = "http://localhost:${toString config.services.default-creds.port}";
services.default-creds.enable = lib.mkForce false;
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."default-creds.${config.var.domain}" = "http://10.233.6.2:8087";
}
-26
View File
@@ -1,26 +0,0 @@
# Eleakxir is a search engine for leaked data.
{
config,
inputs,
pkgs,
...
}: {
users.groups.eleakxir = {};
users.users.hadi.extraGroups = ["eleakxir"];
services.eleakxir = {
enable = true;
port = 9198;
user = "eleakxir";
group = "eleakxir";
limit = 1000;
folders = ["/var/lib/eleakxir/leaks/" "/mnt/data/big-leaks/"];
debug = true;
};
environment.systemPackages = [
inputs.eleakxir.packages.${pkgs.stdenv.hostPlatform.system}.leak-utils
];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."eleakxir-back.${config.var.domain}" = "http://localhost:${toString config.services.eleakxir.port}";
}
+68 -57
View File
@@ -1,68 +1,79 @@
{
config,
pkgs,
lib,
...
}: let
{ config, pkgs, lib, ... }:
let
inherit (import ./mk-container.nix { inherit lib config; }) mkContainer;
domain = config.var.domain;
catppuccin-gitea = pkgs.fetchzip {
url = "https://github.com/catppuccin/gitea/releases/download/v1.0.2/catppuccin-gitea.tar.gz";
sha256 = "sha256-rZHLORwLUfIFcB6K9yhrzr+UwdPNQVSadsw6rg8Q7gs=";
stripRoot = false;
};
in {
services = {
postgresql = {
enable = true;
ensureDatabases = [config.services.gitea.user]; # "gitea" par défaut
ensureUsers = [
{
name = config.services.gitea.database.user;
ensureDBOwnership = true;
}
];
};
gitea = {
enable = true;
database = {
type = "postgres";
in
{
imports = [
(mkContainer {
name = "gitea";
hostIp = "10.233.11.1";
containerIp = "10.233.11.2";
internet = true;
bindMounts."/var/lib/gitea" = {
hostPath = "/var/lib/gitea";
isReadOnly = false;
};
settings = {
server = {
HTTP_ADDR = "127.0.0.1";
HTTP_PORT = 3002;
ROOT_URL = "https://git.${config.var.domain}/";
DOMAIN = "git.${config.var.domain}";
LANDING_PAGE = "/anotherhadi";
nixosConfig = { lib, ... }: {
users.users.gitea.uid = lib.mkForce 978;
users.groups.gitea.gid = lib.mkForce 968;
services.postgresql = {
enable = true;
ensureDatabases = [ "gitea" ];
ensureUsers = [{
name = "gitea";
ensureDBOwnership = true;
}];
};
service = {
REGISTER_MANUAL_CONFIRM = true;
DISABLE_REGISTRATION = true;
DEFAULT_KEEP_EMAIL_PRIVATE = true;
SHOW_REGISTRATION_BUTTON = false;
services.gitea = {
enable = true;
database.type = "postgres";
settings = {
server = {
HTTP_ADDR = "0.0.0.0";
HTTP_PORT = 3002;
ROOT_URL = "https://git.${domain}/";
DOMAIN = "git.${domain}";
LANDING_PAGE = "/anotherhadi";
};
service = {
REGISTER_MANUAL_CONFIRM = true;
DISABLE_REGISTRATION = true;
DEFAULT_KEEP_EMAIL_PRIVATE = true;
SHOW_REGISTRATION_BUTTON = false;
};
ui = {
DEFAULT_THEME = "catppuccin-mocha-mauve";
THEMES = "catppuccin-latte-mauve,catppuccin-frappe-mauve,catppuccin-macchiato-mauve,catppuccin-mocha-mauve";
};
explore = {
DISABLE_USERS_PAGE = true;
DISABLE_ORGANIZATIONS_PAGE = true;
};
repository.DISABLE_STARS = true;
mailer.ENABLED = false;
api.ENABLE_SWAGGER = false;
other.SHOW_FOOTER_VERSION = false;
};
};
ui = {
DEFAULT_THEME = "catppuccin-mocha-mauve";
THEMES = "catppuccin-latte-mauve,catppuccin-frappe-mauve,catppuccin-macchiato-mauve,catppuccin-mocha-mauve";
};
explore = {
DISABLE_USERS_PAGE = true;
DISABLE_ORGANIZATIONS_PAGE = true;
};
repository = {
DISABLE_STARS = true;
};
mailer = {ENABLED = false;};
api = {ENABLE_SWAGGER = false;};
other = {SHOW_FOOTER_VERSION = false;};
systemd.services.gitea.preStart = lib.mkAfter ''
mkdir -p /var/lib/gitea/custom/public/assets
ln -sfn ${catppuccin-gitea} /var/lib/gitea/custom/public/assets/css
'';
networking.firewall.allowedTCPPorts = [ 3002 ];
system.stateVersion = "24.05";
};
};
})
];
cloudflared.tunnels."${config.var.tunnelId}".ingress."git.${config.var.domain}" = "http://localhost:3002";
};
systemd.services.gitea.preStart = lib.mkAfter ''
mkdir -p ${config.services.gitea.stateDir}/custom/public/assets
ln -sfn ${catppuccin-gitea} ${config.services.gitea.stateDir}/custom/public/assets/css
'';
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."git.${domain}" = "http://10.233.11.2:3002";
}
+93 -49
View File
@@ -1,56 +1,100 @@
# Glance is a self-hosted homepage/dashboard service.
{ config, lib, ... }:
let
inherit (import ../mk-container.nix { inherit lib config; }) mkContainer;
domain = config.var.domain;
hostIp = "10.233.12.1";
# Convert 6-char hex color to "H S L" string for glance (integers, no % sign)
hexToGlanceHsl = hex:
let
h = lib.toLower hex;
d = c:
if c == "a" then 10 else if c == "b" then 11 else if c == "c" then 12
else if c == "d" then 13 else if c == "e" then 14 else if c == "f" then 15
else lib.toInt c;
byte = pos: d (builtins.substring pos 1 h) * 16 + d (builtins.substring (pos + 1) 1 h);
ri = byte 0; gi = byte 2; bi = byte 4;
r = ri * 1.0 / 255.0;
g = gi * 1.0 / 255.0;
b = bi * 1.0 / 255.0;
mx = if r >= g && r >= b then "r" else if g >= b then "g" else "b";
mn = if r <= g && r <= b then "r" else if g <= b then "g" else "b";
cmax = if mx == "r" then r else if mx == "g" then g else b;
cmin = if mn == "r" then r else if mn == "g" then g else b;
delta = cmax - cmin;
l = (cmax + cmin) / 2.0;
s = if delta < 0.0001 then 0.0
else if l <= 0.5 then delta / (cmax + cmin)
else delta / (2.0 - cmax - cmin);
hue =
if delta < 0.0001 then 0.0
else if mx == "r" then let raw = 60.0 * (g - b) / delta; in if raw < 0.0 then raw + 360.0 else raw
else if mx == "g" then 60.0 * ((b - r) / delta + 2.0)
else 60.0 * ((r - g) / delta + 4.0);
in "${toString (builtins.floor (hue + 0.5))} ${toString (builtins.floor (s * 100.0 + 0.5))} ${toString (builtins.floor (l * 100.0 + 0.5))}";
c = config.stylix.base16Scheme;
in
{
config,
lib,
...
}: {
imports = [./home.nix ./server.nix];
# 0444 so the glance user inside the container can read the bind-mounted file
sops.secrets.adguard-pwd.mode = "0444";
services = {
glance = {
enable = true;
settings = {
# theme.contrast-multiplier = lib.mkForce 1.4;
server.port = 5678;
imports = [
(mkContainer {
name = "glance";
hostIp = hostIp;
containerIp = "10.233.12.2";
internet = true;
bindMounts."/run/secrets/adguard-pwd" = {
hostPath = config.sops.secrets.adguard-pwd.path;
isReadOnly = true;
};
};
cloudflared.tunnels."${config.var.tunnelId}".ingress."home.${config.var.domain}" = "http://localhost:8755";
nixosConfig = { lib, ... }: {
_module.args.domain = domain;
_module.args.adguardUrl = "http://${hostIp}:3000";
imports = [ ./home.nix ./server.nix ];
nginx.virtualHosts."glance.local" = {
listen = [
{
addr = "127.0.0.1";
port = 8755;
}
];
locations."/" = {
proxyPass = "http://127.0.0.1:${toString config.services.glance.settings.server.port}";
extraConfig = ''
proxy_cache_valid 200 30m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status;
'';
services.glance = {
enable = true;
settings = {
server = {
port = 5678;
host = "127.0.0.1";
};
theme = {
light = false;
background-color = hexToGlanceHsl c.base00; # background
primary-color = hexToGlanceHsl c.base0D; # accent (iris/purple)
positive-color = hexToGlanceHsl c.base0B; # positive (pine/teal)
negative-color = hexToGlanceHsl c.base08; # negative (love/rose)
};
};
};
services.nginx = {
enable = true;
appendHttpConfig = ''
proxy_cache_path /var/cache/nginx/glance levels=1:2 keys_zone=glance:1m inactive=30m max_size=100m;
'';
virtualHosts."glance" = {
listen = [{ addr = "0.0.0.0"; port = 8080; }];
locations."/" = {
proxyPass = "http://127.0.0.1:5678";
extraConfig = ''
proxy_cache glance;
proxy_cache_valid 200 30m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status;
'';
};
};
};
networking.firewall.allowedTCPPorts = [ 8080 ];
system.stateVersion = "24.05";
};
};
};
})
];
systemd.services.glance.serviceConfig = {
DynamicUser = lib.mkForce false;
User = "glance";
Group = "glance";
};
users = {
groups.glance = {};
users.glance = {
isSystemUser = true;
description = "Glance user";
group = "glance";
};
};
sops.secrets.adguard-pwd = {
owner = "glance";
mode = "0600";
};
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."home.${domain}" = "http://10.233.12.2:8080";
}
+5 -5
View File
@@ -1,4 +1,4 @@
{config, ...}: {
{ domain, ... }: {
services.glance.settings.pages = [
{
name = "Home";
@@ -97,11 +97,11 @@
}
{
title = "Mazanoke (image downgrading)";
url = "https://mazanoke.${config.var.domain}";
url = "https://mazanoke.${domain}";
}
{
title = "Stirling PDF";
url = "https://pdf.${config.var.domain}";
url = "https://pdf.${domain}";
}
{
title = "Vert (file converter)";
@@ -200,11 +200,11 @@
links = [
{
title = "Nix 4 Cyber";
url = "https://n4c.${config.var.domain}";
url = "https://n4c.${domain}";
}
{
title = "Cyberchef";
url = "https://cyberchef.${config.var.domain}";
url = "https://cyberchef.${domain}";
}
{
title = "TryHackMe";
+47 -23
View File
@@ -1,4 +1,8 @@
{config, ...}: {
{
domain,
adguardUrl,
...
}: {
services.glance.settings.pages = [
{
name = "Server";
@@ -26,42 +30,62 @@
sites = [
{
title = "Adguard";
url = "https://adguard.${config.var.domain}";
url = "https://adguard.${domain}";
icon = "si:adguard";
}
{
title = "Blog";
url = "https://${domain}";
icon = "si:blogger";
}
{
title = "Gitea";
url = "https://git.${domain}";
icon = "si:gitea";
}
{
title = "Mealie";
url = "https://mealie.${config.var.domain}";
url = "https://mealie.${domain}";
icon = "si:mealie";
}
{
title = "Linkding";
url = "https://linkding.${config.var.domain}";
icon = "sh:linkding";
title = "Umami";
url = "https://umami.${domain}";
icon = "si:umami";
}
{
title = "Iknowyou";
url = "https://iknowyou.${domain}";
icon = "sh:iknowyou";
}
{
title = "Iknowyou Prod";
url = "https://iknowyou-prod.${domain}";
icon = "sh:iknowyou";
}
{
title = "Wallpapers";
url = "https://wallpapers.${domain}";
icon = "si:unsplash";
}
{
title = "Mazanoke";
url = "https://mazanoke.${config.var.domain}";
url = "https://mazanoke.${domain}";
icon = "sh:mazanoke";
}
{
title = "Stirling PDF";
url = "https://pdf.${config.var.domain}";
url = "https://pdf.${domain}";
icon = "sh:stirling-pdf";
}
{
title = "Default-creds";
url = "https://default-creds.${config.var.domain}";
url = "https://default-creds.${domain}";
icon = "si:passbolt";
}
{
title = "Blog";
url = "https://${config.var.domain}";
icon = "si:blogger";
}
{
title = "Cyberchef";
url = "https://cyberchef.${config.var.domain}";
url = "https://cyberchef.${domain}";
icon = "si:codechef";
}
];
@@ -73,37 +97,37 @@
sites = [
{
title = "Jellyfin";
url = "https://media.${config.var.domain}";
url = "https://media.${domain}";
icon = "si:jellyfin";
}
{
title = "Jellyseerr";
url = "https://demandemedia.${config.var.domain}";
url = "https://demandemedia.${domain}";
icon = "si:odysee";
}
{
title = "Radarr";
url = "https://radarr.${config.var.domain}";
url = "https://radarr.${domain}";
icon = "si:radarr";
}
{
title = "Sonarr";
url = "https://sonarr.${config.var.domain}";
url = "https://sonarr.${domain}";
icon = "si:sonarr";
}
{
title = "Bazarr";
url = "https://bazarr.${config.var.domain}";
url = "https://bazarr.${domain}";
icon = "si:subtitleedit";
}
{
title = "Prowlarr";
url = "https://prowlarr.${config.var.domain}";
url = "https://prowlarr.${domain}";
icon = "si:podcastindex";
}
{
title = "Transmission";
url = "https://transmission.${config.var.domain}";
url = "https://transmission.${domain}";
icon = "si:transmission";
}
];
@@ -114,7 +138,7 @@
{
type = "dns-stats";
service = "adguard";
url = "http://localhost:${toString config.services.adguardhome.port}";
url = adguardUrl;
username = "hadi";
password = "\${secret:adguard-pwd}";
}
+64
View File
@@ -0,0 +1,64 @@
{
config,
inputs,
lib,
...
}: let
inherit (import ./mk-container.nix {inherit lib config;}) mkContainer;
in {
imports = [
(mkContainer {
name = "iky-prod";
hostIp = "10.233.1.1";
containerIp = "10.233.1.2";
internet = true;
bindMounts."/etc/iky/config.yaml" = {
hostPath = "/var/lib/iknowyou-prod/config.yaml";
isReadOnly = false;
};
nixosConfig = {...}: {
imports = [inputs.iknowyou.nixosModules.default];
users.users.iknowyou.uid = 999;
users.groups.iknowyou.gid = 999;
services.iknowyou = {
enable = true;
port = 8080;
openFirewall = true;
};
system.stateVersion = "24.05";
};
})
(mkContainer {
name = "iky-demo";
hostIp = "10.233.2.1";
containerIp = "10.233.2.2";
nixosConfig = {...}: {
imports = [inputs.iknowyou.nixosModules.default];
services.iknowyou = {
enable = true;
port = 8080;
openFirewall = true;
};
systemd.services.iknowyou.environment.IKY_DEMO = "true";
system.stateVersion = "24.05";
};
})
];
users.users.iknowyou = {
isSystemUser = true;
group = "iknowyou";
uid = 999;
};
users.groups.iknowyou.gid = 999;
systemd.tmpfiles.rules = [
"f /var/lib/iknowyou-prod/config.yaml 0600 iknowyou iknowyou -"
];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress = {
"iknowyou-prod.${config.var.domain}" = "http://10.233.1.2:8080";
"iknowyou.${config.var.domain}" = "http://10.233.2.2:8080";
};
}
+42 -36
View File
@@ -1,39 +1,45 @@
# Mazanoke is a self-hosted image downgrader
{ config, lib, ... }:
let
inherit (import ./mk-container.nix { inherit lib config; }) mkContainer;
in
{
config,
pkgs,
...
}: let
version = "1.1.5";
mazanoke-pkg = pkgs.stdenv.mkDerivation {
inherit version;
pname = "mazanoke";
src = pkgs.fetchFromGitHub {
owner = "civilblur";
repo = "mazanoke";
rev = "v${version}";
hash = "sha256-B/AF4diMNxN94BzpZP/C+K8kNj9q+4SDKWa/qd4LrVU=";
};
installPhase = ''
mkdir -p $out/share/mazanoke
cp -r ./index.html ./favicon.ico ./manifest.json ./service-worker.js ./assets $out/share/mazanoke/
'';
};
in {
services = {
nginx.virtualHosts."mazanoke.local" = {
root = "${mazanoke-pkg}/share/mazanoke";
locations."/" = {
index = "index.html";
};
listen = [
imports = [
(mkContainer {
name = "mazanoke";
hostIp = "10.233.7.1";
containerIp = "10.233.7.2";
nixosConfig = { pkgs, ... }:
let
version = "1.1.5";
mazanoke-pkg = pkgs.stdenv.mkDerivation {
inherit version;
pname = "mazanoke";
src = pkgs.fetchFromGitHub {
owner = "civilblur";
repo = "mazanoke";
rev = "v${version}";
hash = "sha256-B/AF4diMNxN94BzpZP/C+K8kNj9q+4SDKWa/qd4LrVU=";
};
installPhase = ''
mkdir -p $out/share/mazanoke
cp -r ./index.html ./favicon.ico ./manifest.json ./service-worker.js ./assets $out/share/mazanoke/
'';
};
in
{
addr = "127.0.0.1";
port = 8755;
}
];
};
cloudflared.tunnels."${config.var.tunnelId}".ingress."mazanoke.${config.var.domain}" = "http://localhost:8755";
};
services.nginx = {
enable = true;
virtualHosts."mazanoke" = {
root = "${mazanoke-pkg}/share/mazanoke";
listen = [{ addr = "0.0.0.0"; port = 8080; }];
locations."/" = { index = "index.html"; };
};
};
networking.firewall.allowedTCPPorts = [ 8080 ];
system.stateVersion = "24.05";
};
})
];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."mazanoke.${config.var.domain}" = "http://10.233.7.2:8080";
}
+22 -7
View File
@@ -1,9 +1,24 @@
# Mealie is a recipe management and meal planning application.
{config, ...}: {
services.mealie = {
enable = true;
port = 8092;
};
{ config, lib, ... }:
let
inherit (import ./mk-container.nix { inherit lib config; }) mkContainer;
in
{
imports = [
(mkContainer {
name = "mealie";
hostIp = "10.233.8.1";
containerIp = "10.233.8.2";
internet = true;
nixosConfig = { ... }: {
services.mealie = {
enable = true;
port = 8080;
};
networking.firewall.allowedTCPPorts = [ 8080 ];
system.stateVersion = "24.05";
};
})
];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."mealie.${config.var.domain}" = "http://localhost:${toString config.services.mealie.port}";
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."mealie.${config.var.domain}" = "http://10.233.8.2:8080";
}
+61
View File
@@ -0,0 +1,61 @@
{ lib, config }:
# Returns a NixOS module (attrset), to be used in `imports`.
#
# Options:
# internet - allow outbound internet access via NAT (default: false)
# externalInterface - WAN interface for NAT, required when internet = true
# bindMounts - host paths to mount into the container (see containers.<name>.bindMounts)
# config - NixOS module for the container
{
mkContainer =
{
name,
hostIp,
containerIp,
internet ? false,
externalInterface ? config.var.networkInterface,
bindMounts ? {},
nixosConfig,
}:
assert lib.assertMsg
(lib.stringLength "ve-${name}" <= 15)
"mkContainer: interface name 've-${name}' is ${toString (lib.stringLength "ve-${name}")} chars, max is 15";
{
containers.${name} = {
autoStart = true;
privateNetwork = true;
hostAddress = hostIp;
localAddress = containerIp;
inherit bindMounts;
config = { ... }: {
imports = [ nixosConfig ];
networking.nameservers = lib.mkIf internet [ "1.1.1.1" "1.0.0.1" ];
};
};
}
// (lib.optionalAttrs internet {
boot.kernel.sysctl."net.ipv4.ip_forward" = lib.mkDefault true;
networking.nat = {
enable = true;
externalInterface = externalInterface;
internalInterfaces = [ "ve-${name}" ];
};
# CONTAINER-FWD (defined by another module) blocks all forwarding by default.
# Insert rules in FORWARD before it: allow return traffic, block LAN, allow internet.
networking.firewall.extraCommands = ''
iptables -I FORWARD 1 -s ${containerIp} -m conntrack --ctstate NEW -j ACCEPT
iptables -I FORWARD 1 -s ${containerIp} -d 192.168.0.0/16 -j DROP
iptables -I FORWARD 1 -s ${containerIp} -d 172.16.0.0/12 -j DROP
iptables -I FORWARD 1 -s ${containerIp} -d 10.0.0.0/8 -j DROP
iptables -I FORWARD 1 -d ${containerIp} -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
'';
networking.firewall.extraStopCommands = ''
iptables -D FORWARD -s ${containerIp} -m conntrack --ctstate NEW -j ACCEPT 2>/dev/null || true
iptables -D FORWARD -s ${containerIp} -d 192.168.0.0/16 -j DROP 2>/dev/null || true
iptables -D FORWARD -s ${containerIp} -d 172.16.0.0/12 -j DROP 2>/dev/null || true
iptables -D FORWARD -s ${containerIp} -d 10.0.0.0/8 -j DROP 2>/dev/null || true
iptables -D FORWARD -d ${containerIp} -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true
'';
});
}
+21 -7
View File
@@ -1,9 +1,23 @@
# Stirling-pdf is a high-performance PDF editor and viewer service.
{config, ...}: {
services.stirling-pdf = {
enable = true;
environment."SERVER_PORT" = "8083";
};
{ config, lib, ... }:
let
inherit (import ./mk-container.nix { inherit lib config; }) mkContainer;
in
{
imports = [
(mkContainer {
name = "stirling-pdf";
hostIp = "10.233.9.1";
containerIp = "10.233.9.2";
nixosConfig = { ... }: {
services.stirling-pdf = {
enable = true;
environment."SERVER_PORT" = "8080";
};
networking.firewall.allowedTCPPorts = [ 8080 ];
system.stateVersion = "24.05";
};
})
];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."pdf.${config.var.domain}" = "http://localhost:${toString config.services.stirling-pdf.environment.SERVER_PORT}";
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."pdf.${config.var.domain}" = "http://10.233.9.2:8080";
}
+36 -23
View File
@@ -1,24 +1,37 @@
{config, ...}: {
sops.secrets = {
umami-secret = {
mode = "0600";
};
};
services.umami = {
enable = true;
settings = {
PORT = 8097;
APP_SECRET_FILE = config.sops.secrets.umami-secret.path;
DISABLE_TELEMETRY = true;
DISABLE_BOT_CHECK = true;
};
};
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."umami.${config.var.domain}" = "http://localhost:${toString config.services.umami.settings.PORT}";
systemd.services.default-creds.environment = {
PUBLIC_UMAMI_URL = "https://umami.${config.var.domain}";
# FIXME:
PUBLIC_UMAMI_WEBSITE_ID = "7197484c-01ad-488e-9caa-5ab7b7595f08";
UMAMI_URL = "http://localhost:8097";
UMAMI_WEBSITE_ID = "7197484c-01ad-488e-9caa-5ab7b7595f08";
};
{ config, lib, ... }:
let
inherit (import ./mk-container.nix { inherit lib config; }) mkContainer;
in
{
sops.secrets.umami-secret.mode = "0400";
imports = [
(mkContainer {
name = "umami";
hostIp = "10.233.10.1";
containerIp = "10.233.10.2";
bindMounts."/run/secrets/umami-secret" = {
hostPath = config.sops.secrets.umami-secret.path;
isReadOnly = true;
};
nixosConfig = { ... }: {
services.umami = {
enable = true;
settings = {
PORT = 8080;
HOSTNAME = "0.0.0.0";
APP_SECRET_FILE = "/run/secrets/umami-secret";
DISABLE_TELEMETRY = true;
DISABLE_BOT_CHECK = true;
};
};
# PrivateUsers breaks systemd-creds inside nspawn containers (nested user namespaces)
systemd.services.umami.serviceConfig.PrivateUsers = lib.mkForce false;
networking.firewall.allowedTCPPorts = [ 8080 ];
system.stateVersion = "24.05";
};
})
];
services.cloudflared.tunnels."${config.var.tunnelId}".ingress."umami.${config.var.domain}" = "http://10.233.10.2:8080";
}