557 lines
16 KiB
Nix
557 lines
16 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}: let
|
|
inherit (config.age) secrets;
|
|
inherit (builtins) toString;
|
|
in {
|
|
imports = [
|
|
./gitea.nix
|
|
./authelia.nix
|
|
];
|
|
config = {
|
|
age.secrets.cloudflaredns = {
|
|
file = ../../secrets/cloudflare-dns.age;
|
|
group = "secrets";
|
|
};
|
|
|
|
age.secrets.htpasswd-cam = {
|
|
file = ../../secrets/htpasswd-cam.age;
|
|
group = "nginx";
|
|
mode = "0750";
|
|
};
|
|
age.secrets.htpasswd = {
|
|
file = ../../secrets/htpasswd.age;
|
|
group = "nginx";
|
|
mode = "0750";
|
|
};
|
|
|
|
age.secrets.authelia-users = {
|
|
file = ../../secrets/authelia-users.age;
|
|
group = "authelia-shared";
|
|
mode = "0750";
|
|
};
|
|
|
|
age.secrets.authelia-jwt = {
|
|
file = ../../secrets/authelia-jwt.age;
|
|
group = "authelia-shared";
|
|
mode = "0750";
|
|
};
|
|
|
|
age.secrets.authelia-storage = {
|
|
file = ../../secrets/authelia-storage.age;
|
|
group = "authelia-shared";
|
|
mode = "0750";
|
|
};
|
|
|
|
age.secrets.authelia-session = {
|
|
file = ../../secrets/authelia-session.age;
|
|
group = "authelia-shared";
|
|
mode = "0750";
|
|
};
|
|
|
|
age.secrets.anki = {
|
|
file = ../../secrets/anki-user.age;
|
|
};
|
|
|
|
age.secrets.homepage = {
|
|
file = ../../secrets/homepage.age;
|
|
};
|
|
|
|
age.secrets.paperless-admin = {
|
|
file = ../../secrets/paperless-admin.age;
|
|
};
|
|
|
|
users.groups.secrets = {};
|
|
users.users.acme.extraGroups = ["secrets"];
|
|
|
|
security.acme = {
|
|
acceptTerms = true;
|
|
maxConcurrentRenewals = 1;
|
|
|
|
defaults.email = "iancoguz@gmail.com";
|
|
|
|
certs = {
|
|
"protogen.io" = {
|
|
credentialFiles = {
|
|
"CLOUDFLARE_EMAIL_FILE" = pkgs.writeText "email" "iancoguz@gmail.com";
|
|
"CLOUDFLARE_API_KEY_FILE" = config.age.secrets.cloudflaredns.path;
|
|
};
|
|
|
|
dnsProvider = "cloudflare";
|
|
domain = "protogen.io";
|
|
extraDomainNames = [
|
|
"*.protogen.io"
|
|
"nullbite.com"
|
|
"*.nullbite.com"
|
|
"nullbite.dev"
|
|
"*.nullbite.dev"
|
|
"nbt.sh"
|
|
"*.nbt.sh"
|
|
"proot.link"
|
|
"*.proot.link"
|
|
"nullbite.xyz"
|
|
"*.nullbite.xyz"
|
|
];
|
|
};
|
|
};
|
|
};
|
|
|
|
users.users.nginx.extraGroups = ["acme"];
|
|
|
|
networking.firewall.allowedTCPPorts = [
|
|
80
|
|
443
|
|
# this is needed for node to work for some reason
|
|
8123
|
|
];
|
|
|
|
users.groups.authelia-shared = {};
|
|
services.authelia.instances =
|
|
lib.mapAttrs (inst: opts: {
|
|
enable = true;
|
|
group = "authelia-shared";
|
|
secrets = {
|
|
jwtSecretFile = config.age.secrets.authelia-jwt.path;
|
|
storageEncryptionKeyFile = config.age.secrets.authelia-storage.path;
|
|
sessionSecretFile = config.age.secrets.authelia-session.path;
|
|
};
|
|
settings = {
|
|
access_control.default_policy = "one_factor";
|
|
storage.local.path = "/var/lib/authelia-${inst}/db.sqlite";
|
|
session.cookies = [
|
|
{
|
|
domain = "protogen.io";
|
|
authelia_url = "https://auth.protogen.io";
|
|
default_redirection_url = "https://searx.protogen.io";
|
|
}
|
|
{
|
|
domain = "nbt.sh";
|
|
authelia_url = "https://auth.nbt.sh";
|
|
default_redirection_url = "https://admin.nbt.sh";
|
|
}
|
|
{
|
|
domain = "proot.link";
|
|
authelia_url = "https://auth.proot.link";
|
|
default_redirection_url = "https://admin.proot.link";
|
|
}
|
|
];
|
|
session.redis = {
|
|
host = config.services.redis.servers.authelia.unixSocket;
|
|
};
|
|
notifier.filesystem.filename = "/var/lib/authelia-${inst}/notification.txt";
|
|
authentication_backend.file.path = config.age.secrets.authelia-users.path;
|
|
server.port = lib.mkIf (opts ? port) (opts.port or null);
|
|
theme = "auto";
|
|
};
|
|
}) {
|
|
main = {
|
|
domain = "protogen.io";
|
|
# port = 9091 # default
|
|
};
|
|
};
|
|
|
|
services.redis = {
|
|
servers.authelia = {
|
|
enable = true;
|
|
};
|
|
};
|
|
|
|
users.users."${config.services.authelia.instances.main.user}".extraGroups = let
|
|
name = config.services.redis.servers.authelia.user;
|
|
in [name];
|
|
|
|
services.nginx = {
|
|
enable = true;
|
|
recommendedProxySettings = true;
|
|
recommendedTlsSettings = true;
|
|
recommendedOptimisation = true;
|
|
|
|
commonHttpConfig = ''
|
|
port_in_redirect off;
|
|
'';
|
|
|
|
virtualHosts = let
|
|
useACMEHost = "protogen.io";
|
|
mkProxy = args @ {
|
|
upstream ? "http://127.0.0.1:${builtins.toString args.port}",
|
|
auth ? false,
|
|
authelia ? false,
|
|
extraConfig ? {},
|
|
...
|
|
}:
|
|
lib.mkMerge [
|
|
{
|
|
inherit useACMEHost;
|
|
forceSSL = true;
|
|
locations."/" = {
|
|
proxyPass = upstream;
|
|
proxyWebsockets = true;
|
|
};
|
|
}
|
|
(lib.mkIf auth {
|
|
basicAuthFile = config.age.secrets.htpasswd.path;
|
|
})
|
|
(lib.mkIf authelia {
|
|
authelia.instance = lib.mkDefault "main";
|
|
})
|
|
extraConfig
|
|
];
|
|
|
|
# mkReverseProxy = port: {
|
|
# inherit useACMEHost;
|
|
# forceSSL = true;
|
|
# locations."/" = {
|
|
# proxyPass = "http://127.0.0.1:${builtins.toString port}";
|
|
# proxyWebsockets = true;
|
|
# };
|
|
# };
|
|
|
|
mkAuthProxy = port:
|
|
mkProxy {
|
|
inherit port;
|
|
authelia = true;
|
|
};
|
|
|
|
mkReverseProxy = port: mkProxy {inherit port;};
|
|
in
|
|
(lib.mapAttrs (domain: instance: {
|
|
forceSSL = true;
|
|
inherit useACMEHost;
|
|
authelia.endpoint = {inherit instance;};
|
|
}) {
|
|
"auth.protogen.io" = "main";
|
|
"auth.nbt.sh" = "main";
|
|
"auth.proot.link" = "main";
|
|
})
|
|
// {
|
|
"changedetection.protogen.io" = mkReverseProxy 5000;
|
|
|
|
# firefly
|
|
"firefly.protogen.io" = mkReverseProxy 8083;
|
|
"firefly-import.protogen.io" = mkAuthProxy 8084;
|
|
|
|
"gitea.protogen.io" = mkReverseProxy 3000;
|
|
|
|
# home assistant
|
|
"hass.protogen.io" = mkReverseProxy 8123;
|
|
"node.protogen.io" = mkReverseProxy 1880;
|
|
"z2m.protogen.io" = mkAuthProxy 8124;
|
|
"vsc-hass.protogen.io" = mkReverseProxy 1881;
|
|
|
|
# jellyfin
|
|
"room.protogen.io" = mkReverseProxy 8096;
|
|
"deemix.protogen.io" = mkAuthProxy 6595;
|
|
|
|
# libreddit auth 8087
|
|
"libreddit.protogen.io" = {
|
|
locations."/".return = "302 https://redlib.protogen.io$request_uri";
|
|
forceSSL = true;
|
|
useACMEHost = "protogen.io";
|
|
};
|
|
"redlib.protogen.io" = mkAuthProxy 8087;
|
|
"rss.protogen.io" = mkReverseProxy 8082;
|
|
"blahaj.protogen.io" = mkReverseProxy 8086;
|
|
"paper.protogen.io" = mkReverseProxy config.services.paperless.port;
|
|
|
|
# octoprint (proxy_addr is 10.10.1.8)
|
|
"print.protogen.io" = lib.mkMerge [
|
|
(mkProxy {
|
|
authelia = true;
|
|
upstream = "http://10.10.1.8:80";
|
|
})
|
|
{
|
|
locations."/webcam" = {
|
|
proxyPass = "http://10.10.1.8:80$request_uri";
|
|
proxyWebsockets = true;
|
|
basicAuthFile = config.age.secrets.htpasswd-cam.path;
|
|
authelia.method = null;
|
|
};
|
|
}
|
|
];
|
|
|
|
# searx auth 8088 (none for /favicon.ico, /autocompleter, /opensearch.xml)
|
|
"search.protogen.io".locations."/".return = "302 https://searx.protogen.io$request_uri";
|
|
"searx.protogen.io" = let
|
|
port = 8088;
|
|
in
|
|
mkProxy {
|
|
authelia = true;
|
|
inherit port;
|
|
extraConfig = {
|
|
locations = lib.genAttrs ["/favicon.ico" "/autocompleter" "/opensearch.xml"] (attr: {
|
|
proxyPass = "http://localhost:${builtins.toString port}";
|
|
proxyWebsockets = true;
|
|
authelia.method = null;
|
|
extraConfig = ''
|
|
auth_basic off;
|
|
'';
|
|
});
|
|
};
|
|
};
|
|
|
|
# URL shortener
|
|
"nbt.sh" = mkProxy {
|
|
port = 8090;
|
|
extraConfig.serverAliases = ["proot.link"];
|
|
};
|
|
"admin.nbt.sh" = mkProxy {
|
|
authelia = true;
|
|
port = 8091;
|
|
extraConfig.serverAliases = ["admin.proot.link"];
|
|
};
|
|
|
|
# uptime
|
|
"uptime.protogen.io" = mkReverseProxy 3001;
|
|
"kuma.protogen.io".locations."/".return = "301 https://uptime.protogen.io";
|
|
|
|
"anki.protogen.io" = mkReverseProxy config.services.anki-sync-server.port;
|
|
|
|
# homepage
|
|
"home.protogen.io" = mkAuthProxy 8089;
|
|
|
|
"lounge.protogen.io" = mkAuthProxy 9000;
|
|
|
|
"trackmap.protogen.io" = let
|
|
root = pkgs.modpacks.notlite-ctm-static;
|
|
in {
|
|
useACMEHost = "protogen.io";
|
|
forceSSL = true;
|
|
authelia.instance = "main";
|
|
locations."/" = {
|
|
inherit root;
|
|
extraConfig = ''
|
|
autoindex off;
|
|
'';
|
|
};
|
|
locations."/api/" = {
|
|
proxyPass = "http://10.10.0.3:3876";
|
|
proxyWebsockets = true;
|
|
extraConfig = ''
|
|
chunked_transfer_encoding off;
|
|
proxy_buffering off;
|
|
proxy_cache off;
|
|
'';
|
|
};
|
|
};
|
|
|
|
# main site
|
|
"protogen.io" = {
|
|
serverAliases = ["x.protogen.io"];
|
|
useACMEHost = "protogen.io";
|
|
forceSSL = true;
|
|
locations."/" = {
|
|
root = "/srv/http";
|
|
extraConfig = ''
|
|
autoindex on;
|
|
'';
|
|
};
|
|
};
|
|
|
|
# fallback for known hosts
|
|
"nullbite.com" = {
|
|
forceSSL = true;
|
|
useACMEHost = "protogen.io";
|
|
locations."/" = {
|
|
return = "302 https://protogen.io$request_uri";
|
|
};
|
|
serverAliases = ["www.nullbite.com" "nullbite.dev" "www.nullbite.dev" "www.protogen.io" "nullbite.xyz" "www.nullbite.xyz"];
|
|
};
|
|
|
|
# show blank page for unknown hosts
|
|
"localhost" = {
|
|
default = true;
|
|
addSSL = true;
|
|
useACMEHost = "protogen.io";
|
|
locations."/" = {
|
|
return = "404";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
# https://gethomepage.dev
|
|
services.homepage-dashboard = let
|
|
entry = name: value: {"${name}" = value;};
|
|
makeBookmark = name: {...} @ attrs: entry name [attrs];
|
|
makeBookmark' = name: icon: abbr: href: makeBookmark name ({inherit abbr href;} // lib.optionalAttrs (icon != null) {inherit icon;});
|
|
in {
|
|
enable = true;
|
|
listenPort = 8089;
|
|
environmentFile = secrets.homepage.path;
|
|
|
|
# bookmarks customCSS customJS docker environmentFile kubernetes services settings widgets
|
|
settings = {
|
|
theme = "dark";
|
|
color = "slate";
|
|
};
|
|
|
|
widgets = [
|
|
(entry "resources" {
|
|
cpu = true;
|
|
memory = true;
|
|
disk = "/";
|
|
})
|
|
(entry "search" {
|
|
provider = "custom";
|
|
target = "_self";
|
|
url = "https://searx.protogen.io/search?q=";
|
|
suggestionUrl = "https://searx.protogen.io/autocompleter?q=";
|
|
showSearchSuggestions = true;
|
|
})
|
|
];
|
|
|
|
services = let
|
|
service = name: subdomain: icon: {...} @ attrs:
|
|
entry name ({
|
|
href = "https://${subdomain}.protogen.io";
|
|
inherit icon;
|
|
}
|
|
// attrs);
|
|
basicService = name: subdomain: icon: service name subdomain icon {};
|
|
in [
|
|
(entry "unsorted" [
|
|
(basicService "Firefly III" "firefly" "firefly-iii")
|
|
(basicService "Gitea" "gitea" "gitea")
|
|
(basicService "Home Assistant" "hass" "home-assistant")
|
|
(basicService "Node-RED" "node" "node-red")
|
|
(basicService "Zigbee2MQTT" "z2m" "zigbee2mqtt")
|
|
(basicService "Code Server (Home Assistant)" "vsc-hass" "vscode")
|
|
(basicService "Deemix" "deemix" "deemix")
|
|
(basicService "Miniflux" "rss" "miniflux")
|
|
(basicService "mlmym" "blahaj" "lemmy-light")
|
|
(basicService "Octoprint" "print" "octoprint")
|
|
(basicService "SearXNG" "searx" "searxng")
|
|
(basicService "TheLounge" "lounge" "thelounge")
|
|
(basicService "Paperless" "paper" "paperless-ngx")
|
|
(entry "Shlink" {
|
|
href = "https://admin.nbt.sh";
|
|
icon = "shlink";
|
|
})
|
|
(basicService "Create Track Map" "trackmap" "")
|
|
|
|
((x:
|
|
service x x x {
|
|
widget = {
|
|
};
|
|
}) "changedetection")
|
|
|
|
(service "Uptime Kuma" "uptime" "uptime-kuma" {
|
|
widget = {
|
|
type = "uptimekuma";
|
|
url = "https://uptime.protogen.io";
|
|
slug = "all";
|
|
};
|
|
})
|
|
(service "Jellyfin" "room" "jellyfin" {
|
|
widget = {
|
|
type = "jellyfin";
|
|
url = "https://room.protogen.io";
|
|
key = "{{HOMEPAGE_VAR_JELLYFIN}}";
|
|
enableBlocks = true;
|
|
enableNowPlaying = true;
|
|
enableUser = true;
|
|
};
|
|
})
|
|
])
|
|
];
|
|
|
|
bookmarks = [
|
|
(entry "Admin" [
|
|
(makeBookmark' "Backblaze" "backblaze" "BZ" "https://secure.backblaze.com/user_signin.htm")
|
|
(makeBookmark' "Cloudflare" "cloudflare" "CF" "https://dash.cloudflare.com")
|
|
(makeBookmark' "Porkbun" "porkbun" "PB" "https://porkbun.com/account/domainsSpeedy")
|
|
(makeBookmark' "Namecheap" "namechea" "NC" "https://ap.www.namecheap.com/")
|
|
])
|
|
(entry "Developer" [
|
|
(makeBookmark' "GitHub" "github" "GH" "https://github.com")
|
|
])
|
|
(entry "Local" [
|
|
(makeBookmark' "Syncthing" "syncthing" "ST" "http://127.0.0.1:8384")
|
|
(makeBookmark' "Iris" null "IR" "http://localhost:6680/iris/")
|
|
])
|
|
(entry "Entertainment" [
|
|
(makeBookmark' "Redlib" "redlib" "RL" "https://redlib.protogen.io")
|
|
])
|
|
];
|
|
};
|
|
|
|
virtualisation.docker = {
|
|
enable = true;
|
|
storageDriver = "btrfs";
|
|
};
|
|
|
|
# needed for mDNS in Home Assistant
|
|
networking.firewall.allowedUDPPorts = [5353];
|
|
|
|
systemd.services.redlib.environment = {
|
|
REDLIB_DEFAULT_SUBSCRIPTIONS = lib.pipe ./reddit-subscriptions.txt [
|
|
builtins.readFile
|
|
(lib.splitString "\n")
|
|
(lib.filter (x: x != ""))
|
|
(lib.concatStringsSep "+")
|
|
];
|
|
REDLIB_DEFAULT_SHOW_NSFW = "on";
|
|
REDLIB_DEFAULT_BLUR_NSFW = "on";
|
|
REDLIB_DEFAULT_BLUR_SPOILER = "on";
|
|
REDLIB_DEFAULT_USE_HLS = "on";
|
|
REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION = "on";
|
|
|
|
REDLIB_ENABLE_RSS = "on";
|
|
};
|
|
|
|
services.redlib = {
|
|
enable = true;
|
|
port = 8087;
|
|
};
|
|
|
|
services.thelounge = {
|
|
enable = true;
|
|
extraConfig = {
|
|
prefetch = true;
|
|
prefetchStorage = true;
|
|
prefetchMaxImageSize = 8192;
|
|
};
|
|
};
|
|
|
|
services.redis.servers.paperless.enable = true;
|
|
services.paperless = {
|
|
enable = true;
|
|
# default is "localhost", binding should not rely on DNS (even if
|
|
# localhost is hard-coded 99.999% of the time)
|
|
address = "127.0.0.1";
|
|
passwordFile = secrets.paperless-admin.path;
|
|
settings = {
|
|
PAPERLESS_ADMIN_USER = "nullbite";
|
|
PAPERLESS_REDIS = "unix://${config.services.redis.servers.paperless.unixSocket}";
|
|
PAPERLESS_URL = "https://paper.protogen.io";
|
|
PAPERLESS_TIKA_ENABLED = true;
|
|
PAPERLESS_TIKA_ENDPOINT = "http://localhost:${toString config.services.tika.port}";
|
|
PAPERLESS_TIKA_GOTENBERG_ENDPOINT = "http://localhost:${toString config.services.gotenberg.port}";
|
|
};
|
|
};
|
|
users.users."${config.services.paperless.user}".extraGroups = let
|
|
name = config.services.redis.servers.paperless.group;
|
|
in [name];
|
|
|
|
services.gotenberg = {
|
|
enable = true;
|
|
port = 3002;
|
|
};
|
|
services.tika.enable = true;
|
|
|
|
services.anki-sync-server = {
|
|
enable = true;
|
|
address = "127.0.0.1";
|
|
users = [
|
|
{
|
|
username = "nullbite";
|
|
passwordFile = config.age.secrets.anki.path;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
}
|