Compare commits
4 commits
11fbbd62eb
...
7032ddef37
Author | SHA1 | Date | |
---|---|---|---|
Bruno BELANYI | 7032ddef37 | ||
Bruno BELANYI | 81e12969eb | ||
Bruno BELANYI | b97eff2479 | ||
Bruno BELANYI | 4f742b69f2 |
27
lib/lists.nix
Normal file
27
lib/lists.nix
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{ lib, ... }:
|
||||||
|
let
|
||||||
|
inherit (lib) filter foldl';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Count the number of appararitions of each value in a list.
|
||||||
|
#
|
||||||
|
# countValues ::
|
||||||
|
# [ any ] -> ({ any = int; })
|
||||||
|
countValues =
|
||||||
|
let
|
||||||
|
addToCount = acc: x:
|
||||||
|
let
|
||||||
|
v = toString x;
|
||||||
|
in
|
||||||
|
acc // { ${v} = (acc.${v} or 0) + 1; };
|
||||||
|
in
|
||||||
|
foldl' addToCount { };
|
||||||
|
|
||||||
|
# Filter a list using a predicate function after applying a map.
|
||||||
|
#
|
||||||
|
# mapFilter ::
|
||||||
|
# (value -> bool)
|
||||||
|
# (any -> value)
|
||||||
|
# [ any ]
|
||||||
|
mapFilter = pred: f: attrs: filter pred (map f attrs);
|
||||||
|
}
|
|
@ -4,28 +4,12 @@ let
|
||||||
cfg = config.my.services.blog;
|
cfg = config.my.services.blog;
|
||||||
domain = config.networking.domain;
|
domain = config.networking.domain;
|
||||||
|
|
||||||
makeHostInfo = name: {
|
makeHostInfo = subdomain: {
|
||||||
name = "${name}.${domain}";
|
inherit subdomain;
|
||||||
value = "/var/www/${name}";
|
root = "/var/www/${subdomain}";
|
||||||
};
|
};
|
||||||
|
|
||||||
hostsInfo = [
|
hostsInfo = map makeHostInfo [ "cv" "dev" "key" ];
|
||||||
{
|
|
||||||
name = domain;
|
|
||||||
value = "/var/www/blog";
|
|
||||||
}
|
|
||||||
] ++ builtins.map makeHostInfo [ "cv" "dev" "key" ];
|
|
||||||
|
|
||||||
hosts = builtins.listToAttrs hostsInfo;
|
|
||||||
|
|
||||||
makeVirtualHost = with lib.attrsets;
|
|
||||||
name: root: nameValuePair "${name}" {
|
|
||||||
forceSSL = true;
|
|
||||||
useACMEHost = domain;
|
|
||||||
inherit root;
|
|
||||||
# Make my blog the default landing site
|
|
||||||
default = (name == domain);
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.blog = {
|
options.my.services.blog = {
|
||||||
|
@ -33,7 +17,17 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.nginx.virtualHosts = with lib.attrsets;
|
services.nginx.virtualHosts = {
|
||||||
mapAttrs' makeVirtualHost hosts;
|
# This is not a subdomain, cannot use my nginx wrapper module
|
||||||
|
${domain} = {
|
||||||
|
forceSSL = true;
|
||||||
|
useACMEHost = domain;
|
||||||
|
root = "/var/www/blog";
|
||||||
|
default = true; # Redirect to my blog
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Those are all subdomains, no problem
|
||||||
|
my.services.nginx.virtualHosts = hostsInfo;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.calibre-web;
|
cfg = config.my.services.calibre-web;
|
||||||
domain = config.networking.domain;
|
|
||||||
calibreDomain = "library.${domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.calibre-web = with lib; {
|
options.my.services.calibre-web = with lib; {
|
||||||
|
@ -39,12 +37,12 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts."${calibreDomain}" = {
|
my.services.nginx.virtualHosts = [
|
||||||
forceSSL = true;
|
{
|
||||||
useACMEHost = domain;
|
subdomain = "library";
|
||||||
|
inherit (cfg) port;
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}/";
|
}
|
||||||
};
|
];
|
||||||
|
|
||||||
my.services.backup = {
|
my.services.backup = {
|
||||||
paths = [
|
paths = [
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
let
|
let
|
||||||
cfg = config.my.services.drone;
|
cfg = config.my.services.drone;
|
||||||
|
|
||||||
domain = config.networking.domain;
|
|
||||||
droneDomain = "drone.${domain}";
|
|
||||||
|
|
||||||
hasRunner = (name: builtins.elem name cfg.runners);
|
hasRunner = (name: builtins.elem name cfg.runners);
|
||||||
|
|
||||||
execPkg = pkgs.drone-runner-exec;
|
execPkg = pkgs.drone-runner-exec;
|
||||||
|
@ -59,7 +56,7 @@ in
|
||||||
];
|
];
|
||||||
Environment = [
|
Environment = [
|
||||||
"DRONE_DATABASE_DATASOURCE=postgres:///drone?host=/run/postgresql"
|
"DRONE_DATABASE_DATASOURCE=postgres:///drone?host=/run/postgresql"
|
||||||
"DRONE_SERVER_HOST=${droneDomain}"
|
"DRONE_SERVER_HOST=drone.${config.networking.domain}"
|
||||||
"DRONE_SERVER_PROTO=https"
|
"DRONE_SERVER_PROTO=https"
|
||||||
"DRONE_DATABASE_DRIVER=postgres"
|
"DRONE_DATABASE_DRIVER=postgres"
|
||||||
"DRONE_SERVER_PORT=:${toString cfg.port}"
|
"DRONE_SERVER_PORT=:${toString cfg.port}"
|
||||||
|
@ -91,12 +88,12 @@ in
|
||||||
}];
|
}];
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts."${droneDomain}" = {
|
my.services.nginx.virtualHosts = [
|
||||||
forceSSL = true;
|
{
|
||||||
useACMEHost = domain;
|
subdomain = "drone";
|
||||||
|
inherit (cfg) port;
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
}
|
||||||
};
|
];
|
||||||
|
|
||||||
# Docker runner
|
# Docker runner
|
||||||
systemd.services.drone-runner-docker = lib.mkIf (hasRunner "docker") {
|
systemd.services.drone-runner-docker = lib.mkIf (hasRunner "docker") {
|
||||||
|
@ -107,7 +104,7 @@ in
|
||||||
confinement.enable = true;
|
confinement.enable = true;
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Environment = [
|
Environment = [
|
||||||
"DRONE_SERVER_HOST=${droneDomain}"
|
"DRONE_SERVER_HOST=drone.${config.networking.domain}"
|
||||||
"DRONE_SERVER_PROTO=https"
|
"DRONE_SERVER_PROTO=https"
|
||||||
"DRONE_RUNNER_CAPACITY=10"
|
"DRONE_RUNNER_CAPACITY=10"
|
||||||
"CLIENT_DRONE_RPC_HOST=127.0.0.1:${toString cfg.port}"
|
"CLIENT_DRONE_RPC_HOST=127.0.0.1:${toString cfg.port}"
|
||||||
|
@ -156,7 +153,7 @@ in
|
||||||
];
|
];
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Environment = [
|
Environment = [
|
||||||
"DRONE_SERVER_HOST=${droneDomain}"
|
"DRONE_SERVER_HOST=drone.${config.networking.domain}"
|
||||||
"DRONE_SERVER_PROTO=https"
|
"DRONE_SERVER_PROTO=https"
|
||||||
"DRONE_RUNNER_CAPACITY=10"
|
"DRONE_RUNNER_CAPACITY=10"
|
||||||
"CLIENT_DRONE_RPC_HOST=127.0.0.1:${toString cfg.port}"
|
"CLIENT_DRONE_RPC_HOST=127.0.0.1:${toString cfg.port}"
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.flood;
|
cfg = config.my.services.flood;
|
||||||
|
|
||||||
domain = config.networking.domain;
|
|
||||||
webuiDomain = "flood.${domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.flood = with lib; {
|
options.my.services.flood = with lib; {
|
||||||
|
@ -43,11 +40,11 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts."${webuiDomain}" = {
|
my.services.nginx.virtualHosts = [
|
||||||
forceSSL = true;
|
{
|
||||||
useACMEHost = domain;
|
subdomain = "flood";
|
||||||
|
inherit (cfg) port;
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
}
|
||||||
};
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.gitea;
|
cfg = config.my.services.gitea;
|
||||||
domain = config.networking.domain;
|
|
||||||
giteaDomain = "gitea.${config.networking.domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.gitea = with lib; {
|
options.my.services.gitea = with lib; {
|
||||||
|
@ -17,34 +15,38 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.gitea = {
|
services.gitea =
|
||||||
enable = true;
|
let
|
||||||
|
giteaDomain = "gitea.${config.networking.domain}";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
enable = true;
|
||||||
|
|
||||||
appName = "Ambroisie's forge";
|
appName = "Ambroisie's forge";
|
||||||
httpPort = cfg.port;
|
httpPort = cfg.port;
|
||||||
domain = giteaDomain;
|
domain = giteaDomain;
|
||||||
rootUrl = "https://${giteaDomain}";
|
rootUrl = "https://${giteaDomain}";
|
||||||
|
|
||||||
user = "git";
|
user = "git";
|
||||||
lfs.enable = true;
|
lfs.enable = true;
|
||||||
|
|
||||||
useWizard = false;
|
useWizard = false;
|
||||||
disableRegistration = true;
|
disableRegistration = true;
|
||||||
|
|
||||||
# only send cookies via HTTPS
|
# only send cookies via HTTPS
|
||||||
cookieSecure = true;
|
cookieSecure = true;
|
||||||
|
|
||||||
database = {
|
database = {
|
||||||
type = "postgres"; # Automatic setup
|
type = "postgres"; # Automatic setup
|
||||||
user = "git"; # User needs to be the same as gitea user
|
user = "git"; # User needs to be the same as gitea user
|
||||||
|
};
|
||||||
|
|
||||||
|
# NixOS module uses `gitea dump` to backup repositories and the database,
|
||||||
|
# but it produces a single .zip file that's not very backup friendly.
|
||||||
|
# I configure my backup system manually below.
|
||||||
|
dump.enable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
# NixOS module uses `gitea dump` to backup repositories and the database,
|
|
||||||
# but it produces a single .zip file that's not very backup friendly.
|
|
||||||
# I configure my backup system manually below.
|
|
||||||
dump.enable = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users.git = {
|
users.users.git = {
|
||||||
description = "Gitea Service";
|
description = "Gitea Service";
|
||||||
home = config.services.gitea.stateDir;
|
home = config.services.gitea.stateDir;
|
||||||
|
@ -60,12 +62,12 @@ in
|
||||||
users.groups.git = { };
|
users.groups.git = { };
|
||||||
|
|
||||||
# Proxy to Gitea
|
# Proxy to Gitea
|
||||||
services.nginx.virtualHosts."${giteaDomain}" = {
|
my.services.nginx.virtualHosts = [
|
||||||
forceSSL = true;
|
{
|
||||||
useACMEHost = domain;
|
subdomain = "gitea";
|
||||||
|
inherit (cfg) port;
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}/";
|
}
|
||||||
};
|
];
|
||||||
|
|
||||||
my.services.backup = {
|
my.services.backup = {
|
||||||
paths = [
|
paths = [
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
let
|
let
|
||||||
cfg = config.my.services.indexers;
|
cfg = config.my.services.indexers;
|
||||||
|
|
||||||
domain = config.networking.domain;
|
|
||||||
jackettDomain = "jackett.${config.networking.domain}";
|
|
||||||
nzbhydraDomain = "nzbhydra.${config.networking.domain}";
|
|
||||||
|
|
||||||
jackettPort = 9117;
|
jackettPort = 9117;
|
||||||
nzbhydraPort = 5076;
|
nzbhydraPort = 5076;
|
||||||
in
|
in
|
||||||
|
@ -29,25 +25,19 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
services.nginx.virtualHosts."${jackettDomain}" =
|
|
||||||
lib.mkIf cfg.jackett.enable {
|
|
||||||
forceSSL = true;
|
|
||||||
useACMEHost = domain;
|
|
||||||
|
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString jackettPort}/";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.nzbhydra2 = lib.mkIf cfg.nzbhydra.enable {
|
services.nzbhydra2 = lib.mkIf cfg.nzbhydra.enable {
|
||||||
enable = true;
|
enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts."${nzbhydraDomain}" =
|
my.services.nginx.virtualHosts = [
|
||||||
lib.mkIf cfg.nzbhydra.enable {
|
{
|
||||||
forceSSL = true;
|
subdomain = "jackett";
|
||||||
useACMEHost = domain;
|
port = jackettPort;
|
||||||
|
}
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString nzbhydraPort}/";
|
{
|
||||||
};
|
subdomain = "nzbhydra";
|
||||||
|
port = nzbhydraPort;
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.jellyfin;
|
cfg = config.my.services.jellyfin;
|
||||||
domain = config.networking.domain;
|
|
||||||
jellyfinDomain = "jellyfin.${config.networking.domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.jellyfin = {
|
options.my.services.jellyfin = {
|
||||||
|
@ -16,22 +14,23 @@ in
|
||||||
group = "media";
|
group = "media";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Proxy to Jellyfin
|
my.services.nginx.virtualHosts = [
|
||||||
services.nginx.virtualHosts."${jellyfinDomain}" = {
|
{
|
||||||
forceSSL = true;
|
subdomain = "jellyfin";
|
||||||
useACMEHost = domain;
|
port = 8096;
|
||||||
|
extraConfig = {
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://127.0.0.1:8096/";
|
extraConfig = ''
|
||||||
extraConfig = ''
|
proxy_buffering off;
|
||||||
proxy_buffering off;
|
'';
|
||||||
'';
|
};
|
||||||
};
|
# Too bad for the repetition...
|
||||||
|
locations."/socket" = {
|
||||||
locations."/socket" = {
|
proxyPass = "http://127.0.0.1:8096/";
|
||||||
proxyPass = "http://127.0.0.1:8096/";
|
proxyWebsockets = true;
|
||||||
proxyWebsockets = true;
|
};
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,6 @@ let
|
||||||
cfg = config.my.services.lohr;
|
cfg = config.my.services.lohr;
|
||||||
settingsFormat = pkgs.formats.yaml { };
|
settingsFormat = pkgs.formats.yaml { };
|
||||||
|
|
||||||
domain = config.networking.domain;
|
|
||||||
lohrDomain = "lohr.${config.networking.domain}";
|
|
||||||
|
|
||||||
lohrPkg = pkgs.ambroisie.lohr;
|
lohrPkg = pkgs.ambroisie.lohr;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
@ -75,13 +72,11 @@ in
|
||||||
};
|
};
|
||||||
users.groups.lohr = { };
|
users.groups.lohr = { };
|
||||||
|
|
||||||
services.nginx.virtualHosts."${lohrDomain}" = {
|
my.services.nginx.virtualHosts = [
|
||||||
forceSSL = true;
|
{
|
||||||
useACMEHost = domain;
|
subdomain = "lohr";
|
||||||
|
inherit (cfg) port;
|
||||||
locations."/" = {
|
}
|
||||||
proxyPass = "http://127.0.0.1:${toString cfg.port}/";
|
];
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,35 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
my.services.nginx.virtualHosts = [
|
||||||
|
# Element Web app deployment
|
||||||
|
{
|
||||||
|
subdomain = "chat";
|
||||||
|
root = pkgs.element-web.override {
|
||||||
|
conf = {
|
||||||
|
default_server_config = {
|
||||||
|
"m.homeserver" = {
|
||||||
|
"base_url" = "https://matrix.${domain}";
|
||||||
|
"server_name" = domain;
|
||||||
|
};
|
||||||
|
"m.identity_server" = {
|
||||||
|
"base_url" = "https://vector.im";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
showLabsSettings = true;
|
||||||
|
defaultCountryCode = "FR"; # cocorico
|
||||||
|
roomDirectory = {
|
||||||
|
"servers" = [
|
||||||
|
"matrix.org"
|
||||||
|
"mozilla.org"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Those are too complicated to use my wrapper...
|
||||||
services.nginx.virtualHosts = {
|
services.nginx.virtualHosts = {
|
||||||
"matrix.${domain}" = {
|
"matrix.${domain}" = {
|
||||||
onlySSL = true;
|
onlySSL = true;
|
||||||
|
@ -192,34 +221,6 @@ in
|
||||||
return 200 '${builtins.toJSON client}';
|
return 200 '${builtins.toJSON client}';
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# Element Web app deployment
|
|
||||||
"chat.${domain}" = {
|
|
||||||
useACMEHost = domain;
|
|
||||||
forceSSL = true;
|
|
||||||
|
|
||||||
root = pkgs.element-web.override {
|
|
||||||
conf = {
|
|
||||||
default_server_config = {
|
|
||||||
"m.homeserver" = {
|
|
||||||
"base_url" = "https://matrix.${domain}";
|
|
||||||
"server_name" = domain;
|
|
||||||
};
|
|
||||||
"m.identity_server" = {
|
|
||||||
"base_url" = "https://vector.im";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
showLabsSettings = true;
|
|
||||||
defaultCountryCode = "FR"; # cocorico
|
|
||||||
roomDirectory = {
|
|
||||||
"servers" = [
|
|
||||||
"matrix.org"
|
|
||||||
"mozilla.org"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# For administration tools.
|
# For administration tools.
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.miniflux;
|
cfg = config.my.services.miniflux;
|
||||||
|
|
||||||
domain = config.networking.domain;
|
|
||||||
minifluxDomain = "reader.${config.networking.domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.miniflux = with lib; {
|
options.my.services.miniflux = with lib; {
|
||||||
|
@ -23,7 +20,7 @@ in
|
||||||
description = "Password of the admin user";
|
description = "Password of the admin user";
|
||||||
};
|
};
|
||||||
|
|
||||||
privatePort = mkOption {
|
port = mkOption {
|
||||||
type = types.port;
|
type = types.port;
|
||||||
default = 9876;
|
default = 9876;
|
||||||
example = 8080;
|
example = 8080;
|
||||||
|
@ -45,8 +42,8 @@ in
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
# Virtual hosts settings
|
# Virtual hosts settings
|
||||||
BASE_URL = "https://${minifluxDomain}";
|
BASE_URL = "https://reader.${config.networking.domain}";
|
||||||
LISTEN_ADDR = "localhost:${toString cfg.privatePort}";
|
LISTEN_ADDR = "localhost:${toString cfg.port}";
|
||||||
# I want fast updates
|
# I want fast updates
|
||||||
POLLING_FREQUENCY = "30";
|
POLLING_FREQUENCY = "30";
|
||||||
BATCH_SIZE = "50";
|
BATCH_SIZE = "50";
|
||||||
|
@ -56,12 +53,11 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Proxy to Jellyfin
|
my.services.nginx.virtualHosts = [
|
||||||
services.nginx.virtualHosts."${minifluxDomain}" = {
|
{
|
||||||
forceSSL = true;
|
subdomain = "reader";
|
||||||
useACMEHost = domain;
|
inherit (cfg) port;
|
||||||
|
}
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString cfg.privatePort}/";
|
];
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.monitoring;
|
cfg = config.my.services.monitoring;
|
||||||
|
|
||||||
domain = config.networking.domain;
|
|
||||||
grafanaDomain = "monitoring.${config.networking.domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.monitoring = with lib; {
|
options.my.services.monitoring = with lib; {
|
||||||
|
@ -52,7 +49,7 @@ in
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.grafana = {
|
services.grafana = {
|
||||||
enable = true;
|
enable = true;
|
||||||
domain = grafanaDomain;
|
domain = "monitoring.${config.networking.domain}";
|
||||||
port = cfg.grafana.port;
|
port = cfg.grafana.port;
|
||||||
addr = "127.0.0.1"; # Proxied through Nginx
|
addr = "127.0.0.1"; # Proxied through Nginx
|
||||||
|
|
||||||
|
@ -115,16 +112,11 @@ in
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx = {
|
my.services.nginx.virtualHosts = [
|
||||||
virtualHosts.${grafanaDomain} = {
|
{
|
||||||
forceSSL = true;
|
subdomain = "monitoring";
|
||||||
useACMEHost = domain;
|
inherit (cfg.grafana) port;
|
||||||
|
}
|
||||||
locations."/" = {
|
];
|
||||||
proxyPass = "http://127.0.0.1:${toString cfg.grafana.port}";
|
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.navidrome;
|
cfg = config.my.services.navidrome;
|
||||||
domain = config.networking.domain;
|
|
||||||
navidromeDomain = "music.${config.networking.domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.navidrome = with lib; {
|
options.my.services.navidrome = with lib; {
|
||||||
|
@ -23,7 +21,7 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
privatePort = mkOption {
|
port = mkOption {
|
||||||
type = types.port;
|
type = types.port;
|
||||||
default = 4533;
|
default = 4533;
|
||||||
example = 8080;
|
example = 8080;
|
||||||
|
@ -42,21 +40,18 @@ in
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
settings = cfg.settings // {
|
settings = cfg.settings // {
|
||||||
Port = cfg.privatePort;
|
Port = cfg.port;
|
||||||
Address = "127.0.0.1"; # Behind reverse proxy, so only loopback
|
Address = "127.0.0.1"; # Behind reverse proxy, so only loopback
|
||||||
MusicFolder = cfg.musicFolder;
|
MusicFolder = cfg.musicFolder;
|
||||||
LogLevel = "info";
|
LogLevel = "info";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts."${navidromeDomain}" = {
|
my.services.nginx.virtualHosts = [
|
||||||
forceSSL = true;
|
{
|
||||||
useACMEHost = domain;
|
subdomain = "music";
|
||||||
|
inherit (cfg) port;
|
||||||
locations."/" = {
|
}
|
||||||
proxyPass = "http://127.0.0.1:${toString cfg.privatePort}/";
|
];
|
||||||
proxyWebsockets = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.nextcloud;
|
cfg = config.my.services.nextcloud;
|
||||||
domain = config.networking.domain;
|
|
||||||
nextcloudDomain = "nextcloud.${config.networking.domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.nextcloud = with lib; {
|
options.my.services.nextcloud = with lib; {
|
||||||
|
@ -31,7 +29,7 @@ in
|
||||||
services.nextcloud = {
|
services.nextcloud = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = pkgs.nextcloud22;
|
package = pkgs.nextcloud22;
|
||||||
hostName = nextcloudDomain;
|
hostName = "nextcloud.${config.networking.domain}";
|
||||||
home = "/var/lib/nextcloud";
|
home = "/var/lib/nextcloud";
|
||||||
maxUploadSize = cfg.maxSize;
|
maxUploadSize = cfg.maxSize;
|
||||||
config = {
|
config = {
|
||||||
|
@ -59,11 +57,10 @@ in
|
||||||
after = [ "postgresql.service" ];
|
after = [ "postgresql.service" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts."${nextcloudDomain}" = {
|
# The service above configures the domain, no need for my wrapper
|
||||||
|
services.nginx.virtualHosts."nextcloud.${config.networking.domain}" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
useACMEHost = domain;
|
useACMEHost = config.networking.domain;
|
||||||
|
|
||||||
locations."/".proxyPass = "http://127.0.0.1:3000/";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
my.services.backup = {
|
my.services.backup = {
|
||||||
|
|
|
@ -1,12 +1,147 @@
|
||||||
# Configuration shamelessly stolen from [1]
|
# A simple abstraction layer for almost all of my services' needs
|
||||||
#
|
|
||||||
# [1]: https://github.com/delroth/infra.delroth.net/blob/master/common/nginx.nix
|
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.my.services.nginx;
|
||||||
|
|
||||||
|
virtualHostOption = with lib; types.submodule {
|
||||||
|
options = {
|
||||||
|
subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "dev";
|
||||||
|
description = ''
|
||||||
|
Which subdomain, under config.networking.domain, to use
|
||||||
|
for this virtual host.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = with types; nullOr port;
|
||||||
|
default = null;
|
||||||
|
example = 8080;
|
||||||
|
description = ''
|
||||||
|
Which port to proxy to, through 127.0.0.1, for this virtual host.
|
||||||
|
This option is incompatible with `root`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
root = mkOption {
|
||||||
|
type = with types; nullOr path;
|
||||||
|
default = null;
|
||||||
|
example = "/var/www/blog";
|
||||||
|
description = ''
|
||||||
|
The root folder for this virtual host. This option is incompatible
|
||||||
|
with `port`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConfig = mkOption {
|
||||||
|
type = types.attrs; # FIXME: forward type of virtualHosts
|
||||||
|
example = litteralExample ''
|
||||||
|
{
|
||||||
|
locations."/socket" = {
|
||||||
|
proxyPass = "http://127.0.0.1:8096/";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Any extra configuration that should be applied to this virtual host.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
{
|
{
|
||||||
# Whenever something defines an nginx vhost, ensure that nginx defaults are
|
options.my.services.nginx = with lib; {
|
||||||
# properly set.
|
enable =
|
||||||
config = lib.mkIf ((builtins.attrNames config.services.nginx.virtualHosts) != [ ]) {
|
mkEnableOption "Nginx, activates when `virtualHosts` is not empty" // {
|
||||||
|
default = builtins.length cfg.virtualHosts != 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
monitoring = {
|
||||||
|
enable = my.mkDisableOption "monitoring through grafana and prometheus";
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualHosts = mkOption {
|
||||||
|
type = types.listOf virtualHostOption;
|
||||||
|
default = [ ];
|
||||||
|
example = litteralExample ''
|
||||||
|
[
|
||||||
|
{
|
||||||
|
subdomain = "gitea";
|
||||||
|
port = 8080;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
subdomain = "dev";
|
||||||
|
root = "/var/www/dev";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
subdomain = "jellyfin";
|
||||||
|
port = 8096;
|
||||||
|
extraConfig = {
|
||||||
|
locations."/socket" = {
|
||||||
|
proxyPass = "http://127.0.0.1:8096/";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
List of virtual hosts to set-up using default settings.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
assertions = [ ]
|
||||||
|
++ (lib.flip builtins.map cfg.virtualHosts ({ subdomain, ... } @ args:
|
||||||
|
let
|
||||||
|
conflicts = [ "port" "root" ];
|
||||||
|
optionsNotNull = builtins.map (v: args.${v} != null) conflicts;
|
||||||
|
optionsSet = lib.filter lib.id optionsNotNull;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assertion = builtins.length optionsSet == 1;
|
||||||
|
message = ''
|
||||||
|
Subdomain '${subdomain}' must have exactly one of ${
|
||||||
|
lib.concatStringsSep ", " (builtins.map (v: "'${v}'") conflicts)
|
||||||
|
} configured.
|
||||||
|
'';
|
||||||
|
}))
|
||||||
|
++ (
|
||||||
|
let
|
||||||
|
ports = lib.my.mapFilter
|
||||||
|
(v: v != null)
|
||||||
|
({ port, ... }: port)
|
||||||
|
cfg.virtualHosts;
|
||||||
|
portCounts = lib.my.countValues ports;
|
||||||
|
nonUniquesCounts = lib.filterAttrs (_: v: v != 1) portCounts;
|
||||||
|
nonUniques = builtins.attrNames nonUniquesCounts;
|
||||||
|
mkAssertion = port: {
|
||||||
|
assertion = false;
|
||||||
|
message = "Port ${port} cannot appear in multiple virtual hosts.";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
map mkAssertion nonUniques
|
||||||
|
) ++ (
|
||||||
|
let
|
||||||
|
subs = map ({ subdomain, ... }: subdomain) cfg.virtualHosts;
|
||||||
|
subsCounts = lib.my.countValues subs;
|
||||||
|
nonUniquesCounts = lib.filterAttrs (_: v: v != 1) subsCounts;
|
||||||
|
nonUniques = builtins.attrNames nonUniquesCounts;
|
||||||
|
mkAssertion = v: {
|
||||||
|
assertion = false;
|
||||||
|
message = ''
|
||||||
|
Subdomain '${v}' cannot appear in multiple virtual hosts.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in
|
||||||
|
map mkAssertion nonUniques
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
services.nginx = {
|
services.nginx = {
|
||||||
enable = true;
|
enable = true;
|
||||||
statusPage = true; # For monitoring scraping.
|
statusPage = true; # For monitoring scraping.
|
||||||
|
@ -15,6 +150,33 @@
|
||||||
recommendedOptimisation = true;
|
recommendedOptimisation = true;
|
||||||
recommendedTlsSettings = true;
|
recommendedTlsSettings = true;
|
||||||
recommendedProxySettings = true;
|
recommendedProxySettings = true;
|
||||||
|
|
||||||
|
virtualHosts =
|
||||||
|
let
|
||||||
|
domain = config.networking.domain;
|
||||||
|
mkVHost = ({ subdomain, ... } @ args: lib.nameValuePair
|
||||||
|
"${subdomain}.${domain}"
|
||||||
|
(builtins.foldl' lib.recursiveUpdate { } [
|
||||||
|
# Base configuration
|
||||||
|
{
|
||||||
|
forceSSL = true;
|
||||||
|
useACMEHost = domain;
|
||||||
|
}
|
||||||
|
# Proxy to port
|
||||||
|
(lib.optionalAttrs (args.port != null) {
|
||||||
|
locations."/".proxyPass =
|
||||||
|
"http://127.0.0.1:${toString args.port}";
|
||||||
|
})
|
||||||
|
# Serve filesystem content
|
||||||
|
(lib.optionalAttrs (args.root != null) {
|
||||||
|
inherit (args) root;
|
||||||
|
})
|
||||||
|
# VHost specific configuration
|
||||||
|
args.extraConfig
|
||||||
|
])
|
||||||
|
);
|
||||||
|
in
|
||||||
|
lib.my.genAttrs' cfg.virtualHosts mkVHost;
|
||||||
};
|
};
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
networking.firewall.allowedTCPPorts = [ 80 443 ];
|
||||||
|
@ -22,10 +184,10 @@
|
||||||
# Nginx needs to be able to read the certificates
|
# Nginx needs to be able to read the certificates
|
||||||
users.users.nginx.extraGroups = [ "acme" ];
|
users.users.nginx.extraGroups = [ "acme" ];
|
||||||
|
|
||||||
# Use DNS wildcard certificate
|
|
||||||
security.acme = {
|
security.acme = {
|
||||||
email = "bruno.acme@belanyi.fr";
|
email = "bruno.acme@belanyi.fr";
|
||||||
acceptTerms = true;
|
acceptTerms = true;
|
||||||
|
# Use DNS wildcard certificate
|
||||||
certs =
|
certs =
|
||||||
let
|
let
|
||||||
domain = config.networking.domain;
|
domain = config.networking.domain;
|
||||||
|
@ -40,8 +202,8 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
# Setup monitoring
|
|
||||||
services.grafana.provision.dashboards = [
|
services.grafana.provision.dashboards = lib.mkIf cfg.monitoring.enable [
|
||||||
{
|
{
|
||||||
name = "NGINX";
|
name = "NGINX";
|
||||||
options.path = pkgs.nur.repos.alarsyo.grafanaDashboards.nginx;
|
options.path = pkgs.nur.repos.alarsyo.grafanaDashboards.nginx;
|
||||||
|
@ -49,7 +211,7 @@
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
services.prometheus = {
|
services.prometheus = lib.mkIf cfg.monitoring.enable {
|
||||||
exporters.nginx = {
|
exporters.nginx = {
|
||||||
enable = true;
|
enable = true;
|
||||||
listenAddress = "127.0.0.1";
|
listenAddress = "127.0.0.1";
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.pirate;
|
cfg = config.my.services.pirate;
|
||||||
domain = config.networking.domain;
|
|
||||||
|
|
||||||
ports = {
|
ports = {
|
||||||
sonarr = 8989;
|
sonarr = 8989;
|
||||||
|
@ -22,15 +21,8 @@ let
|
||||||
})
|
})
|
||||||
ports);
|
ports);
|
||||||
|
|
||||||
redirections = with lib.attrsets;
|
redirections = lib.flip lib.mapAttrsToList ports
|
||||||
(mapAttrs'
|
(subdomain: port: { inherit subdomain port; });
|
||||||
(service: port: nameValuePair "${service}.${domain}" {
|
|
||||||
forceSSL = true;
|
|
||||||
useACMEHost = domain;
|
|
||||||
|
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${builtins.toString port}/";
|
|
||||||
})
|
|
||||||
ports);
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.pirate = {
|
options.my.services.pirate = {
|
||||||
|
@ -38,6 +30,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services = managers // { nginx.virtualHosts = redirections; };
|
services = managers;
|
||||||
|
my.services.nginx.virtualHosts = redirections;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.podgrab;
|
cfg = config.my.services.podgrab;
|
||||||
|
|
||||||
domain = config.networking.domain;
|
|
||||||
podgrabDomain = "podgrab.${domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.podgrab = with lib; {
|
options.my.services.podgrab = with lib; {
|
||||||
|
@ -34,11 +31,11 @@ in
|
||||||
inherit (cfg) passwordFile port;
|
inherit (cfg) passwordFile port;
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts."${podgrabDomain}" = {
|
my.services.nginx.virtualHosts = [
|
||||||
forceSSL = true;
|
{
|
||||||
useACMEHost = domain;
|
subdomain = "podgrab";
|
||||||
|
inherit (cfg) port;
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString cfg.port}";
|
}
|
||||||
};
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.rss-bridge;
|
cfg = config.my.services.rss-bridge;
|
||||||
domain = config.networking.domain;
|
|
||||||
rss-bridgeDomain = "rss-bridge.${config.networking.domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.rss-bridge = {
|
options.my.services.rss-bridge = {
|
||||||
|
@ -14,12 +12,13 @@ in
|
||||||
services.rss-bridge = {
|
services.rss-bridge = {
|
||||||
enable = true;
|
enable = true;
|
||||||
whitelist = [ "*" ]; # Whitelist all
|
whitelist = [ "*" ]; # Whitelist all
|
||||||
virtualHost = rss-bridgeDomain; # Setup virtual host
|
virtualHost = "rss-bridge.${config.networking.domain}";
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts."${rss-bridgeDomain}" = {
|
# The service above configures the domain, no need for my wrapper
|
||||||
|
services.nginx.virtualHosts."rss-bridge.${config.networking.domain}" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
useACMEHost = domain;
|
useACMEHost = config.networking.domain;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.sabnzbd;
|
cfg = config.my.services.sabnzbd;
|
||||||
|
|
||||||
domain = config.networking.domain;
|
|
||||||
sabnzbdDomain = "sabnzbd.${domain}";
|
|
||||||
port = 9090; # NOTE: not declaratively set...
|
port = 9090; # NOTE: not declaratively set...
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
@ -18,11 +15,11 @@ in
|
||||||
group = "media";
|
group = "media";
|
||||||
};
|
};
|
||||||
|
|
||||||
services.nginx.virtualHosts."${sabnzbdDomain}" = {
|
my.services.nginx.virtualHosts = [
|
||||||
forceSSL = true;
|
{
|
||||||
useACMEHost = domain;
|
subdomain = "sabnzbd";
|
||||||
|
inherit port;
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString port}";
|
}
|
||||||
};
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
{ config, lib, ... }:
|
{ config, lib, ... }:
|
||||||
let
|
let
|
||||||
cfg = config.my.services.transmission;
|
cfg = config.my.services.transmission;
|
||||||
|
|
||||||
domain = config.networking.domain;
|
|
||||||
webuiDomain = "transmission.${domain}";
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.my.services.transmission = with lib; {
|
options.my.services.transmission = with lib; {
|
||||||
|
@ -34,7 +31,7 @@ in
|
||||||
description = "Download base directory";
|
description = "Download base directory";
|
||||||
};
|
};
|
||||||
|
|
||||||
privatePort = mkOption {
|
port = mkOption {
|
||||||
type = types.port;
|
type = types.port;
|
||||||
default = 9091;
|
default = 9091;
|
||||||
example = 8080;
|
example = 8080;
|
||||||
|
@ -63,7 +60,7 @@ in
|
||||||
peer-port = cfg.peerPort;
|
peer-port = cfg.peerPort;
|
||||||
|
|
||||||
rpc-enabled = true;
|
rpc-enabled = true;
|
||||||
rpc-port = cfg.privatePort;
|
rpc-port = cfg.port;
|
||||||
rpc-authentication-required = true;
|
rpc-authentication-required = true;
|
||||||
|
|
||||||
rpc-username = cfg.username;
|
rpc-username = cfg.username;
|
||||||
|
@ -77,12 +74,12 @@ in
|
||||||
|
|
||||||
# Default transmission webui, I prefer combustion but its development
|
# Default transmission webui, I prefer combustion but its development
|
||||||
# seems to have stalled
|
# seems to have stalled
|
||||||
services.nginx.virtualHosts."${webuiDomain}" = {
|
my.services.nginx.virtualHosts = [
|
||||||
forceSSL = true;
|
{
|
||||||
useACMEHost = domain;
|
subdomain = "transmission";
|
||||||
|
inherit (cfg) port;
|
||||||
locations."/".proxyPass = "http://127.0.0.1:${toString cfg.privatePort}";
|
}
|
||||||
};
|
];
|
||||||
|
|
||||||
networking.firewall = {
|
networking.firewall = {
|
||||||
allowedTCPPorts = [ cfg.peerPort ];
|
allowedTCPPorts = [ cfg.peerPort ];
|
||||||
|
|
Loading…
Reference in a new issue