initial commit
flake-check / flake-check (push) Canceled after 0s
lint / lint (push) Canceled after 0s
diff-tag / dix (push) Canceled after 0s

This commit is contained in:
Floweynt
2026-08-31 08:41:45 -05:00
commit 76fa38e34c
54 changed files with 6747 additions and 0 deletions
+386
View File
@@ -0,0 +1,386 @@
{
name,
hostInfo,
dsl,
pkgs,
lib,
containers,
...
}:
let
inherit (dsl) mkContainer;
inherit (hostInfo.paths) data;
nodePort = 9100;
cadvPort = 9200;
containerNetworkRulesFile =
let
mkRules =
metric: record:
lib.mapAttrsToList (cname: c: {
inherit record;
expr = "rate(${metric}{device=\"${c.iface}\"}[5m])";
labels = {
container = cname;
};
}) containers;
in
pkgs.writeText "container-network.yml" (
builtins.toJSON {
groups = [
{
name = "container-network";
rules =
mkRules "node_network_receive_bytes_total" "container_network_receive_rate"
++ mkRules "node_network_transmit_bytes_total" "container_network_transmit_rate";
}
];
}
);
containerDefinedRulesFile = pkgs.writeText "container-defined.yml" (
builtins.toJSON {
groups = [
{
name = "container-defined";
rules = lib.mapAttrsToList (cname: _: {
record = "container_defined";
expr = "1";
labels = {
container = cname;
};
}) containers;
}
];
}
);
alloyConfig = pkgs.writeText "config.alloy" ''
loki.source.journal "journal" {
max_age = "12h"
path = "/var/log/journal"
labels = { job = "systemd-journal" }
relabel_rules = loki.relabel.journal.rules
forward_to = [loki.write.local.receiver]
}
loki.relabel "journal" {
forward_to = []
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
rule {
source_labels = ["__journal__hostname"]
target_label = "host"
}
rule {
source_labels = ["__journal_syslog_identifier"]
target_label = "syslog_id"
}
rule {
source_labels = ["__journal_priority_keyword"]
target_label = "level"
}
rule {
source_labels = ["__journal__systemd_machine_name"]
target_label = "container"
}
}
loki.write "local" {
endpoint {
url = "http://127.0.0.1:3100/loki/api/v1/push"
}
}
'';
dashboards = lib.filterAttrs (name: _: lib.hasSuffix "-dashboard" name) (
dsl.listMap ./overseer (_: path: import path { inherit pkgs lib; })
);
dashboardDir = pkgs.linkFarm "grafana-dashboards" (
lib.mapAttrsToList (name: dashboard: {
name = "${lib.removeSuffix "-dashboard" name}.json";
path = dashboard;
}) dashboards
);
landingRoot = pkgs.writeTextDir "index.html" ''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Overseer</title>
<style>
body { font-family: sans-serif; max-width: 480px; margin: 4rem auto; }
ul { padding: 0; list-style: none; }
li { margin: .5rem 0; }
</style>
</head>
<body>
<h1>Overseer</h1>
<ul>
<li><a data-host="stats.localhost">stats.localhost</a> — Grafana</li>
<li><a data-host="prometheus.localhost">prometheus.localhost</a> — Prometheus</li>
</ul>
<script>
const p = location.port && location.port !== "80" ? ":" + location.port : "";
document.querySelectorAll("a[data-host]").forEach(a => {
const h = a.dataset.host;
a.href = "http://" + h + p;
a.textContent = h + p;
});
</script>
</body>
</html>
'';
in
let
c =
mkContainer {
inherit name;
net.access = [
{
to = "@host";
tcp = [
nodePort
cadvPort
];
}
];
svc.autoStart = true;
resources.fs = {
"/var/lib/loki" = {
hostPath = "${data}/overseer/loki/data";
isReadOnly = false;
};
"/var/lib/prometheus2" = {
hostPath = "${data}/overseer/prometheus/data";
isReadOnly = false;
};
"/var/lib/grafana" = {
hostPath = "${data}/overseer/grafana";
isReadOnly = false;
};
"/var/log/journal" = {
hostPath = "/var/log/journal";
isReadOnly = true;
};
};
containerConfig = _: {
services.loki = {
enable = true;
configuration = {
auth_enabled = false;
server = {
http_listen_port = 3100;
grpc_listen_port = 9096;
};
common = {
instance_addr = "127.0.0.1";
path_prefix = "/var/lib/loki";
storage.filesystem = {
chunks_directory = "/var/lib/loki/chunks";
rules_directory = "/var/lib/loki/rules";
};
replication_factor = 1;
ring.kvstore.store = "inmemory";
};
schema_config.configs = [
{
from = "2020-10-24";
store = "tsdb";
object_store = "filesystem";
schema = "v13";
index = {
prefix = "index_";
period = "24h";
};
}
];
};
};
services.alloy = {
enable = true;
configPath = alloyConfig;
};
services.prometheus = {
enable = true;
port = 9090;
globalConfig.scrape_interval = "15s";
scrapeConfigs = [
{
job_name = "node";
static_configs = [ { targets = [ "${hostAddr}:${toString nodePort}" ]; } ];
}
{
job_name = "cadvisor";
static_configs = [ { targets = [ "${hostAddr}:${toString cadvPort}" ]; } ];
}
{
job_name = "prometheus";
static_configs = [ { targets = [ "localhost:9090" ]; } ];
}
];
ruleFiles = [
containerNetworkRulesFile
containerDefinedRulesFile
(dsl.mkContainerLimitsRuleFile pkgs containers)
];
rules = [
''
groups:
- name: containers
rules:
- alert: ContainerFailed
expr: node_systemd_unit_state{name=~"container@.*",state="failed"} == 1
for: 1m
labels:
severity: critical
annotations:
summary: "{{ $labels.name }} entered failed state"
- alert: ContainerOOMKill
expr: increase(container_oom_events_total{id=~"/machine.slice/container@.+\\.service.*"}[5m]) > 0
labels:
severity: warning
annotations:
summary: "OOM kill in {{ $labels.id }}"
''
];
};
services.grafana = {
enable = true;
dataDir = "/var/lib/grafana";
settings = {
server.http_port = 3000;
server.http_addr = "127.0.0.1";
# WORKAROUND: Grafana 10+ requires an explicit secret_key; omitting it
# causes auth session tokens to be regenerated on every restart
security.secret_key = "SW2YcwTIb9zpOOhoPsMm";
"auth".disable_login_form = true;
"auth.anonymous" = {
enabled = true;
org_role = "Admin";
};
};
provision = {
enable = true;
datasources.settings = {
apiVersion = 1;
datasources = [
{
name = "Prometheus";
type = "prometheus";
uid = "prometheus";
access = "proxy";
url = "http://localhost:9090";
isDefault = true;
jsonData.timeInterval = "15s";
}
{
name = "Loki";
type = "loki";
uid = "loki";
access = "proxy";
url = "http://127.0.0.1:3100";
}
];
};
dashboards.settings = {
apiVersion = 1;
providers = [
{
name = "default";
type = "file";
disableDeletion = true;
options.path = toString dashboardDir;
}
];
};
};
};
services.nginx = {
enable = true;
virtualHosts."_" = {
listen = [
{
addr = "0.0.0.0";
port = 80;
ssl = false;
extraParameters = [ "default_server" ];
}
];
root = "${landingRoot}";
};
virtualHosts."stats.localhost" = {
listen = [
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
locations."/" = {
proxyPass = "http://127.0.0.1:3000";
proxyWebsockets = true;
extraConfig = "proxy_set_header Host $host;";
};
};
virtualHosts."prometheus.localhost" = {
listen = [
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
locations."/" = {
proxyPass = "http://127.0.0.1:9090";
};
};
};
systemd.services = lib.genAttrs [ "loki" "alloy" "prometheus" "grafana" "nginx" ] (_: {
serviceConfig.FailureAction = "exit-force";
});
};
net.expose.tcp = [ 80 ];
resources.mem = "2G";
}
// {
hostConfig = {
services.prometheus.exporters.node = {
enable = true;
listenAddress = "0.0.0.0";
port = nodePort;
enabledCollectors = [
"systemd"
"processes"
];
disabledCollectors = [
"netstat"
"softnet"
];
# radeon sometimes breaks on kexec
extraFlags = [
"--collector.systemd.enable-task-metrics"
"--collector.hwmon.chip-exclude=radeon"
];
};
services.cadvisor = {
enable = true;
listenAddress = "0.0.0.0";
port = cadvPort;
};
};
};
hostAddr = c.hostAddr;
in
c
@@ -0,0 +1,61 @@
{ pkgs, lib }:
let
inherit (import ./helpers.nix { inherit pkgs lib; }) mkRow loki mkDashboard;
mkLogs =
{
title,
expr,
w ? 24,
h ? 10,
}:
{
inherit title;
type = "logs";
datasource = loki;
gridPos = { inherit w h; };
options = {
showTime = true;
showLabels = true;
sortOrder = "Descending";
wrapLogMessage = true;
enableLogDetails = true;
};
targets = [
{
datasource = loki;
inherit expr;
}
];
};
in
mkDashboard {
name = "access-logs.json";
title = "Access Logs";
uid = "access-logs";
from = "now-6h";
panels = [
(mkRow "HTTP")
(mkLogs {
title = "nginx (access + error)";
expr = ''{syslog_id="nginx"}'';
})
(mkRow "SSH")
(mkLogs {
title = "sshpiper (all connections)";
expr = ''{container="sshpiper"}'';
})
(mkLogs {
title = "Failed / invalid auth attempts (all backends, incl. host)";
expr = ''{syslog_id=~"sshd.*"} |~ "Failed|Invalid|error:"'';
})
(mkLogs {
title = "sshd auth events (all backends, incl. host)";
expr = ''{syslog_id=~"sshd.*"}'';
})
(mkLogs {
title = "Host sshd (port 5952)";
expr = ''{syslog_id=~"sshd.*", container=""}'';
})
];
}
@@ -0,0 +1,251 @@
{ pkgs, lib }:
let
inherit (import ./helpers.nix { inherit pkgs lib; }) prom mkDashboard;
in
mkDashboard {
name = "containers.json";
title = "Containers";
uid = "containers";
from = "now-3h";
panels = [
{
title = "Container States";
type = "piechart";
gridPos = {
h = 8;
w = 8;
};
datasource = prom;
options = {
pieType = "donut";
legend = {
displayMode = "table";
placement = "right";
values = [
"value"
"percent"
];
};
tooltip.mode = "single";
};
fieldConfig.overrides = [
{
matcher = {
id = "byName";
options = "active";
};
properties = [
{
id = "color";
value = {
mode = "fixed";
fixedColor = "green";
};
}
];
}
{
matcher = {
id = "byName";
options = "failed";
};
properties = [
{
id = "color";
value = {
mode = "fixed";
fixedColor = "red";
};
}
];
}
{
matcher = {
id = "byName";
options = "inactive";
};
properties = [
{
id = "color";
value = {
mode = "fixed";
fixedColor = "blue";
};
}
];
}
{
matcher = {
id = "byName";
options = "activating";
};
properties = [
{
id = "color";
value = {
mode = "fixed";
fixedColor = "yellow";
};
}
];
}
{
matcher = {
id = "byName";
options = "unknown";
};
properties = [
{
id = "color";
value = {
mode = "fixed";
fixedColor = "gray";
};
}
];
}
];
targets = [
{
datasource = prom;
expr = ''count by (state) ((label_replace(node_systemd_unit_state{name=~"container@.*"} == 1, "container", "$1", "name", "container@(.+)\\.service")) or on(container) label_replace(container_defined, "state", "unknown", "container", ".+"))'';
instant = true;
legendFormat = "{{state}}";
}
];
}
{
title = "Container States";
type = "table";
gridPos = {
h = 16;
w = 24;
};
datasource = prom;
options.footer.show = false;
fieldConfig = {
defaults = { };
overrides = [
{
matcher = {
id = "byName";
options = "State";
};
properties = [
{
id = "custom.displayMode";
value = "color-text";
}
{
id = "mappings";
value = [
{
type = "value";
options.active = {
text = "active";
color = "green";
index = 0;
};
}
{
type = "value";
options.failed = {
text = "failed";
color = "red";
index = 1;
};
}
{
type = "value";
options.activating = {
text = "activating";
color = "yellow";
index = 2;
};
}
{
type = "value";
options.inactive = {
text = "inactive";
color = "blue";
index = 3;
};
}
{
type = "value";
options.unknown = {
text = "unknown";
color = "gray";
index = 4;
};
}
];
}
];
}
];
};
targets = [
{
datasource = prom;
expr = ''
(label_replace(node_systemd_unit_state{name=~"container@.*"} == 1, "container", "$1", "name", "container@(.+)\\.service"))
or on(container)
label_replace(container_defined, "state", "unknown", "container", ".+")
'';
instant = true;
format = "table";
legendFormat = "__auto";
}
];
transformations = [
{
id = "organize";
options = {
excludeByName = {
Time = true;
Value = true;
"__name__" = true;
instance = true;
job = true;
name = true;
};
renameByName = {
container = "Container";
state = "State";
};
};
}
];
}
{
title = "Container State (1 = running, 0 = stopped/failed)";
type = "timeseries";
gridPos = {
h = 8;
w = 24;
};
datasource = prom;
fieldConfig.defaults = {
custom.lineWidth = 2;
color.mode = "palette-classic";
min = 0;
max = 1;
};
options = {
tooltip.mode = "multi";
legend = {
displayMode = "table";
placement = "bottom";
};
};
targets = [
{
datasource = prom;
expr = ''label_replace(node_systemd_unit_state{name=~"container@.*",state="active"}, "container", "$1", "name", "container@(.+).service")'';
legendFormat = "{{container}}";
}
];
}
];
}
+228
View File
@@ -0,0 +1,228 @@
{ pkgs, lib }:
let
prom = {
type = "prometheus";
uid = "prometheus";
};
loki = {
type = "loki";
uid = "loki";
};
autoLayout =
panels:
let
step =
acc: panel:
let
w = panel.gridPos.w;
h = panel.gridPos.h;
wrap = acc.x + w > 24;
x = if wrap then 0 else acc.x;
y = if wrap then acc.y + acc.rowH else acc.y;
rowH = if wrap then h else lib.max acc.rowH h;
in
{
x = x + w;
inherit y rowH;
result = acc.result ++ [
(
panel
// {
gridPos = panel.gridPos // {
inherit x y;
};
}
)
];
};
in
(lib.foldl' step {
x = 0;
y = 0;
rowH = 0;
result = [ ];
} panels).result;
T = expr: legendFormat: {
datasource = prom;
inherit expr legendFormat;
};
mkRow = title: {
inherit title;
type = "row";
collapsed = false;
panels = [ ];
gridPos = {
w = 24;
h = 1;
};
};
mkStat =
{
title,
w ? 4,
h ? 4,
expr,
unit ? "short",
decimals ? 2,
}:
{
inherit title;
type = "stat";
datasource = prom;
gridPos = { inherit w h; };
fieldConfig.defaults = {
inherit unit decimals;
color.mode = "thresholds";
thresholds = {
mode = "absolute";
steps = [
{
color = "blue";
value = null;
}
];
};
};
options = {
reduceOptions.calcs = [ "lastNotNull" ];
colorMode = "value";
textMode = "value";
};
targets = [
{
datasource = prom;
inherit expr;
instant = true;
legendFormat = "";
}
];
};
mkGauge =
{
title,
w ? 4,
h ? 4,
expr,
unit ? "percent",
min ? 0,
max ? 100,
}:
{
inherit title;
type = "gauge";
datasource = prom;
gridPos = { inherit w h; };
fieldConfig.defaults = {
inherit unit min max;
thresholds = {
mode = "absolute";
steps = [
{
color = "green";
value = null;
}
{
color = "yellow";
value = 70;
}
{
color = "red";
value = 90;
}
];
};
};
options = {
reduceOptions.calcs = [ "lastNotNull" ];
orientation = "auto";
showThresholdLabels = false;
showThresholdMarkers = true;
};
targets = [
{
datasource = prom;
inherit expr;
instant = true;
legendFormat = "";
}
];
};
mkTs =
{
title,
w ? 24,
h ? 8,
unit ? "short",
targets,
}:
{
inherit title targets;
type = "timeseries";
datasource = prom;
gridPos = { inherit w h; };
fieldConfig.defaults = {
inherit unit;
custom = {
lineWidth = 1;
fillOpacity = 5;
};
color.mode = "palette-classic";
};
options = {
tooltip.mode = "multi";
legend = {
displayMode = "table";
placement = "bottom";
calcs = [
"mean"
"max"
"lastNotNull"
];
};
};
};
mkDashboard =
{
name,
title,
uid,
from ? "now-1h",
panels,
templating ? [ ],
}:
pkgs.writeText name (
builtins.toJSON (
{
inherit title uid;
schemaVersion = 37;
refresh = "30s";
time = {
inherit from;
to = "now";
};
panels = lib.imap1 (id: p: p // { inherit id; }) (autoLayout panels);
}
// lib.optionalAttrs (templating != [ ]) { templating.list = templating; }
)
);
in
{
inherit
prom
loki
autoLayout
T
mkRow
mkStat
mkGauge
mkTs
mkDashboard
;
}
@@ -0,0 +1,167 @@
{ pkgs, lib }:
let
inherit (import ./helpers.nix { inherit pkgs lib; })
T
mkRow
mkStat
mkGauge
mkTs
mkDashboard
;
in
mkDashboard {
name = "host.json";
title = "Host";
uid = "host";
panels = [
(mkRow "Overview")
(mkGauge {
title = "CPU";
expr = ''100 * (1 - avg(rate(node_cpu_seconds_total{mode="idle"}[5m])))'';
})
(mkGauge {
title = "Memory used";
expr = "100 * (1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)";
})
(mkStat {
title = "Load (1m)";
unit = "short";
decimals = 2;
expr = "node_load1";
})
(mkStat {
title = "Load (5m)";
unit = "short";
decimals = 2;
expr = "node_load5";
})
(mkStat {
title = "Uptime";
unit = "dtdurations";
decimals = 0;
expr = "node_time_seconds - node_boot_time_seconds";
})
(mkRow "CPU")
(mkTs {
title = "CPU usage (%)";
unit = "percent";
targets = [
(T ''avg(rate(node_cpu_seconds_total{mode="user"}[5m])) * 100'' "user")
(T ''avg(rate(node_cpu_seconds_total{mode="system"}[5m])) * 100'' "system")
(T ''avg(rate(node_cpu_seconds_total{mode="iowait"}[5m])) * 100'' "iowait")
(T ''avg(rate(node_cpu_seconds_total{mode="steal"}[5m])) * 100'' "steal")
(T ''avg(rate(node_cpu_seconds_total{mode="softirq"}[5m])) * 100'' "softirq")
];
})
(mkTs {
title = "Per-core usage (%)";
unit = "percent";
targets = [
(T ''(1 - rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100'' "cpu{{cpu}}")
];
})
(mkTs {
title = "CPU temperatures";
unit = "celsius";
targets = [
(T "node_hwmon_temp_celsius" "{{chip}} {{sensor}}")
];
})
(mkTs {
title = "Load average";
targets = [
(T "node_load1" "1m")
(T "node_load5" "5m")
(T "node_load15" "15m")
];
})
(mkRow "Memory")
(mkTs {
title = "Memory";
unit = "bytes";
targets = [
(T "node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes" "used")
(T "node_memory_Cached_bytes + node_memory_Buffers_bytes" "cache + buffers")
(T "node_memory_MemAvailable_bytes" "available")
(T "node_memory_MemTotal_bytes" "total")
];
})
(mkTs {
title = "Swap";
unit = "bytes";
targets = [
(T "node_memory_SwapTotal_bytes - node_memory_SwapFree_bytes" "used")
(T "node_memory_SwapTotal_bytes" "total")
];
})
(mkRow "Disk")
(mkTs {
title = "Filesystem usage (%)";
unit = "percent";
targets = [
(T ''100 * (1 - node_filesystem_avail_bytes{fstype=~"btrfs|ext4|xfs|vfat|f2fs"} / node_filesystem_size_bytes{fstype=~"btrfs|ext4|xfs|vfat|f2fs"})'' "{{mountpoint}}")
];
})
(mkTs {
title = "Disk IO (bytes/s)";
unit = "Bps";
targets = [
(T ''rate(node_disk_read_bytes_total{device!~"loop.*"}[5m])'' "{{device}} read")
(T ''rate(node_disk_written_bytes_total{device!~"loop.*"}[5m])'' "{{device}} write")
];
})
(mkTs {
title = "Disk IO utilisation (%)";
unit = "percent";
targets = [
(T ''rate(node_disk_io_time_seconds_total{device!~"loop.*"}[5m]) * 100'' "{{device}}")
];
})
(mkRow "Network")
(mkTs {
title = "Network (bytes/s)";
unit = "Bps";
targets = [
(T ''rate(node_network_receive_bytes_total{device!~"lo|br-.*|vh-.*|vc-.*"}[5m])'' "{{device}} rx")
(T ''rate(node_network_transmit_bytes_total{device!~"lo|br-.*|vh-.*|vc-.*"}[5m])'' "{{device}} tx")
];
})
(mkTs {
title = "Network errors + drops (pps)";
targets = [
(T ''rate(node_network_receive_errs_total{device!~"lo|br-.*|vh-.*|vc-.*"}[5m])'' "{{device}} rx err")
(T ''rate(node_network_transmit_errs_total{device!~"lo|br-.*|vh-.*|vc-.*"}[5m])'' "{{device}} tx err")
(T ''rate(node_network_receive_drop_total{device!~"lo|br-.*|vh-.*|vc-.*"}[5m])'' "{{device}} rx drop")
(T ''rate(node_network_transmit_drop_total{device!~"lo|br-.*|vh-.*|vc-.*"}[5m])'' "{{device}} tx drop")
];
})
(mkRow "System")
(mkTs {
title = "Context switches + forks (ops/s)";
targets = [
(T "rate(node_context_switches_total[5m])" "context switches")
(T "rate(node_forks_total[5m])" "forks")
];
})
(mkTs {
title = "File descriptors";
targets = [
(T "node_filefd_allocated" "allocated")
(T "node_filefd_maximum" "limit")
];
})
(mkTs {
title = "Processes";
targets = [
(T "node_procs_running" "running")
(T "node_procs_blocked" "blocked on IO")
];
})
];
}
@@ -0,0 +1,203 @@
{ pkgs, lib }:
let
inherit (import ./helpers.nix { inherit pkgs lib; })
T
mkRow
mkStat
mkTs
prom
mkDashboard
;
ctid = "id=~\"/machine.slice/container@.+\\\\.service/payload/.+\"";
ctsel = "id=~\"/machine.slice/container@$container\\\\.service/payload/.+\"";
ctall = "id=~\"/machine.slice/container@.+\\\\.service.*\"";
ctnet = "id=~\"/machine.slice/container@$container\\\\.service.*\"";
labeled =
m:
"label_replace(${m}, \"container\", \"$1\", \"id\", \"/machine.slice/container@(.+)\\\\.service/payload.+\")";
byContainer = m: "sum by (container) (${labeled m})";
labeledAll =
m:
"label_replace(${m}, \"container\", \"$1\", \"id\", \"/machine.slice/container@(.+)\\\\.service.*\")";
byContainerAll = m: "sum by (container) (${labeledAll m})";
nmSvc = m: "label_replace(${m}, \"container\", \"$1\", \"name\", \"container@(.+)\\\\.service\")";
orZero = m: fb: "(${m}) or on(container) (${fb} * 0)";
cpuAll =
rate:
orZero (byContainer "rate(container_cpu_usage_seconds_total{${ctid}}[${rate}])") "container_limit_cpu_cores";
memAll = orZero (byContainerAll "container_memory_rss{${ctall}}") "container_limit_memory_bytes";
taskAll = nmSvc "node_systemd_unit_tasks_current{name=~\"container@.+\"}";
in
mkDashboard {
name = "resources.json";
title = "Container Resources";
uid = "container-resources";
templating = [
{
name = "container";
type = "query";
datasource = prom;
query = "label_values(container_limit_cpu_cores, container)";
refresh = 2;
sort = 1;
includeAll = true;
multi = true;
current = {
selected = true;
text = [ "All" ];
value = [ "$__all" ];
};
}
];
panels = [
(mkRow "Overview")
(mkStat {
title = "Running";
decimals = 0;
expr = "count(node_systemd_unit_state{name=~\"container@.*\",state=\"active\"} == 1) or vector(0)";
})
(mkStat {
title = "CPU used";
decimals = 2;
expr = "sum(${cpuAll "5m"}) or vector(0)";
})
(mkStat {
title = "CPU limit";
decimals = 2;
expr = "sum(container_limit_cpu_cores) or vector(0)";
})
(mkStat {
title = "Mem RSS";
decimals = 1;
unit = "bytes";
expr = "sum(${memAll}) or vector(0)";
})
(mkStat {
title = "Mem limit";
decimals = 1;
unit = "bytes";
expr = "sum(container_limit_memory_bytes) or vector(0)";
})
(mkStat {
title = "Tasks";
decimals = 0;
expr = "sum(${taskAll}) or vector(0)";
})
(mkRow "All Containers")
(mkTs {
title = "CPU (cores)";
h = 24;
targets = [ (T (cpuAll "5m") "{{container}}") ];
})
(mkTs {
title = "Memory RSS";
unit = "bytes";
h = 24;
targets = [ (T memAll "{{container}}") ];
})
(mkTs {
title = "Tasks";
h = 24;
targets = [ (T taskAll "{{container}}") ];
})
(mkTs {
title = "CPU throttled %";
unit = "percent";
h = 12;
targets = [
(T "(${byContainer "rate(container_cpu_cfs_throttled_periods_total{${ctid}}[5m])"} / ${byContainer "rate(container_cpu_cfs_periods_total{${ctid}}[5m])"}) * 100 or on(container) (container_limit_cpu_cores * 0)" "{{container}}")
];
})
(mkTs {
title = "Network egress (bytes/s)";
unit = "Bps";
h = 24;
targets = [ (T "sum by (container) (container_network_receive_rate)" "{{container}}") ];
})
(mkTs {
title = "Network ingress (bytes/s)";
unit = "Bps";
h = 24;
targets = [ (T "sum by (container) (container_network_transmit_rate)" "{{container}}") ];
})
(mkTs {
title = "Disk read (bytes/s)";
unit = "Bps";
h = 24;
targets = [ (T (byContainer "rate(container_fs_reads_bytes_total{${ctid}}[5m])") "{{container}}") ];
})
(mkTs {
title = "Disk write (bytes/s)";
unit = "Bps";
h = 24;
targets = [
(T (byContainer "rate(container_fs_writes_bytes_total{${ctid}}[5m])") "{{container}}")
];
})
{
type = "row";
title = "$container";
repeat = "container";
collapsed = true;
panels = [ ];
gridPos = {
w = 24;
h = 1;
};
}
(mkTs {
title = "CPU (cores)";
targets = [
(T "sum(rate(container_cpu_usage_seconds_total{${ctsel}}[5m]))" "used")
(T "container_limit_cpu_cores{container=\"$container\"}" "limit")
];
})
(mkTs {
title = "CPU throttled %";
unit = "percent";
targets = [
(T "(sum(rate(container_cpu_cfs_throttled_periods_total{${ctsel}}[5m])) / sum(rate(container_cpu_cfs_periods_total{${ctsel}}[5m]))) * 100 or vector(0)" "throttled %")
];
})
(mkTs {
title = "Memory";
unit = "bytes";
targets = [
(T "sum(container_memory_rss{${ctnet}})" "RSS")
(T "sum(container_memory_working_set_bytes{${ctnet}})" "working set")
(T "container_limit_memory_bytes{container=\"$container\"}" "limit")
];
})
(mkTs {
title = "Network (bytes/s)";
unit = "Bps";
targets = [
(T "container_network_receive_rate{container=\"$container\"}" "{{device}} ↑")
(T "container_network_transmit_rate{container=\"$container\"}" "{{device}} ↓")
];
})
(mkTs {
title = "Disk IO (bytes/s)";
unit = "Bps";
targets = [
(T "sum(rate(container_fs_reads_bytes_total{${ctsel}}[5m]))" "read")
(T "sum(rate(container_fs_writes_bytes_total{${ctsel}}[5m]))" "write")
];
})
(mkTs {
title = "Tasks";
targets = [ (T "node_systemd_unit_tasks_current{name=\"container@$container.service\"}" "tasks") ];
})
];
}
+54
View File
@@ -0,0 +1,54 @@
{
name,
hostInfo,
dsl,
...
}:
let
inherit (dsl) mkContainer mkService;
inherit (hostInfo.paths) data secrets;
in
mkContainer {
inherit name;
net.access = [ { to = "@internet"; } ];
svc.autoStart = true;
resources.fs = {
"/config" = {
hostPath = "${data}/web/devenv/config";
isReadOnly = false;
};
"/workspace" = {
hostPath = "${data}/web/devenv/workspace";
isReadOnly = false;
};
"/run/secrets/code-server-env" = {
hostPath = "${secrets}/code-server-env";
isReadOnly = true;
};
};
users = [
{
name = "coder";
uid = 1000;
home = "/config";
}
];
service =
{ pkgs, lib, ... }:
mkService {
description = "code-server";
execStart = "${lib.getExe' pkgs.code-server "code-server"} --bind-addr 0.0.0.0:8080 --user-data-dir /config/data --extensions-dir /config/extensions --auth password /workspace";
networkOnline = true;
serviceConfig = {
EnvironmentFile = "/run/secrets/code-server-env";
User = "coder";
ReadWritePaths = [
"/config"
"/workspace"
];
};
};
net.expose.tcp = [ 8080 ];
resources.mem = "4G";
resources.cpu = 2;
}
+178
View File
@@ -0,0 +1,178 @@
{
name,
hostInfo,
dsl,
...
}:
let
inherit (dsl) mkContainer mkVm;
inherit (hostInfo.paths) data secrets;
ciRunnerData = "${data}/web/git-ci-runner";
ciRunnerSecrets = "${secrets}/ci-runner";
in
{
${name} = mkContainer {
inherit name;
net.access = [ { to = "@internet"; } ];
net.forward.tcp = [
{
host = 23;
container = 8023;
}
];
svc.autoStart = true;
resources.fs."/var/lib/gitea" = {
hostPath = "${data}/web/git";
isReadOnly = false;
};
containerConfig = _: {
services.gitea = {
enable = true;
stateDir = "/var/lib/gitea";
settings = {
server = {
HTTP_ADDR = "0.0.0.0";
HTTP_PORT = 3000;
DOMAIN = "git.floweytf.com";
ROOT_URL = "https://git.floweytf.com/";
START_SSH_SERVER = true;
BUILTIN_SSH_SERVER_USER = "git";
SSH_PORT = 8023;
};
service.DISABLE_REGISTRATION = true;
};
};
users.users.gitea.uid = 1000;
users.groups.gitea.gid = 1000;
time.timeZone = "UTC";
systemd.services.gitea.serviceConfig.FailureAction = "exit-force";
};
net.expose.tcp = [ 3000 ];
};
ci-runner = mkVm {
name = "ci-runner";
net.access = [
{
to = "git";
tcp = [ 3000 ];
}
{ to = "@internet"; }
];
svc.autoStart = true;
resources.cpu = 4;
resources.mem = "8G";
resources.fs = [
{
proto = "virtiofs";
tag = "nix-rw-store";
source = "${ciRunnerData}/nix-rw-store";
mountPoint = "/nix/.rw-store";
}
{
proto = "virtiofs";
tag = "docker-data";
source = "${ciRunnerData}/docker";
mountPoint = "/var/lib/docker";
}
{
proto = "virtiofs";
tag = "runner-state";
source = "${ciRunnerData}/runner-state";
mountPoint = "/var/lib/gitea-runner";
}
{
proto = "virtiofs";
tag = "runner-token";
source = ciRunnerSecrets;
mountPoint = "/run/ci-runner-secrets";
readOnly = true;
}
];
guestConfig =
{
pkgs,
utils,
lib,
...
}:
let
runnerInstanceName = "ci-runner";
ciJobImage = pkgs.dockerTools.buildLayeredImage {
name = "ci-job-image";
tag = "latest";
contents = with pkgs; [
bashInteractive
coreutils
gitMinimal
nixVersions.latest
nodejs
cacert
];
config = {
Cmd = [ "/bin/sh" ];
Env = [
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
"NIX_SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
"NIX_REMOTE=daemon"
];
};
};
in
{
boot.kernelModules = [ "kvm-intel" ];
boot.extraModprobeConfig = "options kvm_intel nested=1";
microvm.writableStoreOverlay = "/nix/.rw-store";
nix.settings = {
experimental-features = [
"nix-command"
"flakes"
];
max-jobs = "auto";
};
nix.gc = {
automatic = true;
dates = "daily";
options = "--delete-older-than 14d";
};
# nix.optimise.automatic = true; - doesn't work with microvm.writableStoreOverlay
virtualisation.docker.enable = true;
systemd.services.load-ci-job-image = {
description = "Load the CI job Docker image";
wantedBy = [ "multi-user.target" ];
after = [ "docker.service" ];
requires = [ "docker.service" ];
before = [ "gitea-runner-${utils.escapeSystemdPath runnerInstanceName}.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${lib.getExe' pkgs.docker "docker"} load -i ${ciJobImage}";
};
};
services.gitea-actions-runner.instances.${runnerInstanceName} = {
enable = true;
name = runnerInstanceName;
url = "http://git:3000/";
tokenFile = "/run/ci-runner-secrets/token";
labels = [
"nix:docker://ci-job-image:latest"
];
settings.container = {
options = "--device=/dev/kvm --volume=/nix/store:/nix/store:ro --volume=/nix/var/nix/daemon-socket:/nix/var/nix/daemon-socket";
valid_volumes = [
"/nix/store"
"/nix/var/nix/daemon-socket"
];
};
};
};
};
}
+79
View File
@@ -0,0 +1,79 @@
{
name,
pkgs,
lib,
dsl,
containers,
customPkgs,
...
}:
let
inherit (dsl) mkContainer mkService collectContainers;
mkHopperConfig =
{ routes }:
let
header = ''
listen = "0.0.0.0:25565"
[routing.routes]
'';
mkRoute = { domain, ip }: ''
"${domain}" = { ip = "${ip}" }
'';
in
header + lib.concatMapStrings mkRoute routes;
backends = collectContainers "hopperRegister" containers;
mcRoutes = map ({ container, data }: {
domain = "${data.hopperSubdomain}.mc.floweytf.com";
ip = "${container.name}:${toString data.hopperPort}";
}) backends;
hopperCfg = pkgs.writeText "Config.toml" (mkHopperConfig {
routes = mcRoutes;
});
in
{
${name} = mkContainer {
inherit name;
net.access = map ({ container, data }: {
to = container.name;
tcp = [ data.hopperPort ];
}) backends;
svc.autoStart = true;
net.expose.tcp = [ 25565 ];
net.forward.tcp = [ 25565 ];
resources.fs."/Config.toml" = {
hostPath = toString hopperCfg;
isReadOnly = true;
};
service =
_:
mkService {
description = "Hopper Minecraft reverse proxy";
execStart = lib.getExe' customPkgs.hopper-rs "hopper";
workingDir = "/";
networkOnline = true;
serviceConfig = {
DynamicUser = true;
PrivateTmp = true;
PrivateDevices = true;
CapabilityBoundingSet = "";
RestrictAddressFamilies = "AF_INET AF_INET6";
};
};
};
_hopperRegister =
{
hopperSubdomain ? null,
hopperPort ? 25565,
}:
self: {
hopperRegister = {
hopperSubdomain = if hopperSubdomain == null then self.name else hopperSubdomain;
inherit hopperPort;
};
};
}
+264
View File
@@ -0,0 +1,264 @@
{
name,
pkgs,
lib,
hostInfo,
dsl,
...
}:
let
inherit (dsl) mkContainer mkService;
inherit (hostInfo.paths) data secrets;
domain = "mail.floweytf.com";
accountName = "flowey";
stateDir = "/var/lib/stalwart";
acmeDir = "/etc/mail-acme";
taggingScript = ''
require ["envelope", "variables", "imap4flags", "fileinto"];
if envelope :localpart :matches "to" "*" {
addflag "''${1}";
}
fileinto "INBOX";
'';
cli = lib.getExe' pkgs.stalwart-cli "stalwart-cli";
jq = lib.getExe pkgs.jq;
curl = lib.getExe pkgs.curl;
grep = lib.getExe' pkgs.gnugrep "grep";
paste = lib.getExe' pkgs.coreutils "paste";
# use sqlite b/c rocksdb segfaults
configJsonTemplate = pkgs.writeText "stalwart-config.json" (
builtins.toJSON {
"@type" = "Sqlite";
path = "${stateDir}/data.sqlite3";
}
);
staticPlan = pkgs.writeText "mail-static-plan.json" (
builtins.concatStringsSep "\n" (
map builtins.toJSON [
{
"@type" = "upsert";
object = "Domain";
matchOn = [ "name" ];
value."domain-mail" = {
name = domain;
catchAllAddress = "${accountName}@${domain}";
isEnabled = true;
};
}
{
"@type" = "upsert";
object = "SieveSystemScript";
matchOn = [ "name" ];
value."sieve-tag" = {
name = "tag-by-recipient";
isActive = true;
contents = taggingScript;
};
}
]
)
+ "\n"
);
stalwartAuthEnv = ''
export STALWART_URL="http://127.0.0.1:8080"
export STALWART_USER="admin"
export STALWART_PASSWORD="$(cat "$CREDENTIALS_DIRECTORY/admin_password")"
'';
readyGate = pkgs.writeShellScript "mail-ready-gate" ''
set -uo pipefail
until ${curl} -sS -o /dev/null "http://127.0.0.1:8080/api/account"; do
if ! systemctl -q is-active stalwart.service; then
echo "mail: stalwart is not running" >&2
exit 1
fi
sleep 1
done
'';
bootstrapScript = pkgs.writeShellScript "mail-bootstrap" ''
set -uo pipefail
${stalwartAuthEnv}
out="$(${cli} update Bootstrap singleton \
--field defaultDomain=${domain} \
--field serverHostname=${domain} \
--field generateDkimKeys=true \
--field requestTlsCertificate=false \
--field 'directory={"@type":"Internal"}' 2>&1)"
status=$?
if [ "$status" -eq 0 ]; then
echo "mail-bootstrap: first-time setup complete, restarting stalwart into normal mode" >&2
systemctl restart stalwart.service
exit 0
fi
if ${grep} -qi 'bootstrap mode' <<<"$out"; then
exit 0
fi
echo "mail-bootstrap: $out" >&2
exit 1
'';
certCredCert = "/run/credentials/stalwart.service/mail_tls_cert";
certCredKey = "/run/credentials/stalwart.service/mail_tls_key";
retryOnFailure = {
Restart = "on-failure";
RestartSec = 5;
};
retryLimit = {
StartLimitBurst = 5;
StartLimitIntervalSec = 120;
};
provisionScript = pkgs.writeShellScript "mail-provision" ''
set -euo pipefail
${stalwartAuthEnv}
export account_pass="$(cat "$CREDENTIALS_DIRECTORY/account_password")"
account_entry="$(${jq} -nc --arg name "${accountName}" \
'{"@type":"upsert","object":"Account","matchOn":["name"],"value":{("account-"+$name):{"@type":"User","name":$name,"domainId":"#domain-mail","credentials":{"0":{"@type":"Password","secret":env.account_pass}}}}}')"
{ cat ${staticPlan}; echo "$account_entry"; } | ${cli} apply --stdin
existing_cert_ids="$(${cli} query Certificate --json --fields id | ${jq} -r .id | ${paste} -sd, -)"
if [ -n "$existing_cert_ids" ]; then
${cli} delete Certificate --ids "$existing_cert_ids"
fi
${cli} create Certificate \
--field 'certificate={"@type":"File","filePath":"${certCredCert}"}' \
--field 'privateKey={"@type":"File","filePath":"${certCredKey}"}'
systemctl restart stalwart.service
'';
in
mkContainer {
inherit name;
net.access = [ { to = "@internet"; } ];
svc.autoStart = true;
net.forward.tcp = [
25
993
];
resources.fs = {
"${stateDir}" = {
hostPath = "${data}/core/mail";
isReadOnly = false;
};
"${acmeDir}" = {
hostPath = "${data}/core/nginx/acme/${domain}";
isReadOnly = true;
};
"/run/secrets/mail-admin-password" = {
hostPath = "${secrets}/mail-admin-password";
isReadOnly = true;
};
"/run/secrets/mail-account-password" = {
hostPath = "${secrets}/mail-account-password";
isReadOnly = true;
};
};
users = [
{
name = "stalwart";
uid = 1000;
home = stateDir;
}
];
containerConfig = _: {
systemd.tmpfiles.rules = [
"Z ${stateDir} 0750 stalwart stalwart -"
"C ${stateDir}/config.json 0640 stalwart stalwart - ${configJsonTemplate}"
];
systemd.services.stalwart = mkService {
description = "Stalwart Mail Server";
execStart = pkgs.writeShellScript "stalwart-run" ''
set -euo pipefail
export STALWART_RECOVERY_ADMIN="admin:$(cat "$CREDENTIALS_DIRECTORY/admin_password")"
exec ${lib.getExe' pkgs.stalwart_0_16 "stalwart"} -c ${stateDir}/config.json
'';
serviceConfig = {
User = "stalwart";
Group = "stalwart";
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
ReadWritePaths = [ stateDir ];
LoadCredential = [
"admin_password:/run/secrets/mail-admin-password"
"mail_tls_cert:${acmeDir}/fullchain.pem"
"mail_tls_key:${acmeDir}/key.pem"
];
Restart = "on-failure";
RestartSec = 5;
};
};
systemd.paths.mail-cert-renew = {
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathChanged = "${acmeDir}/key.pem";
Unit = "mail-provision.service";
};
};
systemd.services.mail-ready = mkService {
description = "Wait for the Stalwart management API";
execStart = readyGate;
after = [ "stalwart.service" ];
wants = [ "stalwart.service" ];
wantedBy = [ ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
TimeoutStartSec = 60;
};
};
systemd.services.mail-bootstrap = mkService {
description = "One-time Stalwart bootstrap (server identity + admin account)";
execStart = bootstrapScript;
after = [ "mail-ready.service" ];
wants = [ "mail-ready.service" ];
unitConfig = retryLimit;
serviceConfig = {
Type = "oneshot";
LoadCredential = [ "admin_password:/run/secrets/mail-admin-password" ];
}
// retryOnFailure;
};
systemd.services.mail-provision = mkService {
description = "Provision mail.floweytf.com domain + catch-all account in Stalwart";
execStart = provisionScript;
after = [
"mail-ready.service"
"mail-bootstrap.service"
];
wants = [
"mail-ready.service"
"mail-bootstrap.service"
];
serviceConfig = {
Type = "oneshot";
LoadCredential = [
"admin_password:/run/secrets/mail-admin-password"
"account_password:/run/secrets/mail-account-password"
];
}
// retryOnFailure;
unitConfig = retryLimit;
};
};
}
+41
View File
@@ -0,0 +1,41 @@
{
name,
hostInfo,
dsl,
...
}:
let
inherit (dsl) mkContainer mkService;
inherit (hostInfo.paths) data;
in
mkContainer {
inherit name;
svc.autoStart = true;
resources.fs."/app/data" = {
hostPath = "${data}/web/reposilite";
isReadOnly = false;
};
users = [
{
name = "reposilite";
uid = 1000;
home = "/app";
}
];
service =
{ pkgs, lib, ... }:
mkService {
description = "Reposilite";
execStart = "${lib.getExe' pkgs.reposilite "reposilite"} --working-directory /app/data";
workingDir = "/app/data";
serviceConfig = {
User = "reposilite";
PrivateTmp = true;
ReadWritePaths = [ "/app/data" ];
};
}
// {
environment.JAVA_OPTS = "-Xmx128M";
};
net.expose.tcp = [ 8080 ];
}
+205
View File
@@ -0,0 +1,205 @@
{
lib,
pkgs,
hostInfo,
dsl,
self,
...
}:
let
inherit (dsl) mkContainer mkService;
inherit (hostInfo.paths) data;
mkMcTrait =
{
jdk,
dataPath,
extraPackages ? [ ],
}:
{
resources.fs."/data" = {
hostPath = dataPath;
isReadOnly = false;
};
users = [
{
name = "runner";
uid = 1000;
}
];
containerConfig =
{
pkgs,
...
}:
{
environment.systemPackages = [
jdk
pkgs.bash
]
++ extraPackages;
systemd.services.minecraft = mkService {
description = "Minecraft server";
wantedBy = [ "multi-user.target" ];
after = [
"local-fs.target"
"container-ready.target"
];
wants = [ "container-ready.target" ];
path = [
jdk
pkgs.bash
];
execStart = "${lib.getExe' pkgs.bash "bash"} -c ${
lib.escapeShellArg (
"exec ${lib.getExe' pkgs.dtach "dtach"} -N /run/ct-attach/attach.sock "
+ "${lib.getExe' pkgs.util-linux "script"} -qfc /data/launch.sh "
+ ">(exec ${lib.getExe' pkgs.systemd "systemd-cat"} -t minecraft)"
)
}";
serviceConfig = {
WorkingDirectory = "/data";
User = "runner";
RuntimeDirectory = "ct-attach";
RuntimeDirectoryMode = "0700";
StandardInput = "null";
StandardOutput = "journal";
StandardError = "journal";
ReadWritePaths = [ "/data" ];
};
};
};
};
extractAttrs =
l: s: lib.mergeAttrsList (map (f: lib.optionalAttrs (s ? ${f}) { ${f} = s.${f}; }) l);
mkMcSet =
{ dataDir }:
servers:
lib.listToAttrs (
map (
args@{
name,
jdk,
autoStart ? false,
allowInternet ? true,
dataPath ? "${dataDir}/${name}",
extraPackages ? [ ],
mem ? "6G",
cpu ? 2,
...
}:
lib.nameValuePair "mc-${name}" (mkContainer {
name = "mc-${name}";
net.access = lib.optional allowInternet { to = "@internet"; };
net.expose.tcp = [ (args.hopperPort or 25565) ];
svc.autoStart = autoStart;
resources = { inherit cpu mem; };
traits = [ (mkMcTrait { inherit jdk dataPath extraPackages; }) ];
miscProperties = self._hopperRegister (extractAttrs [ "hopperSubdomain" "hopperPort" ] args);
includedInTests = false;
})
) servers
);
jdk8FontPkgs = with pkgs; [
freetype
fontconfig
dejavu_fonts
];
mcServers = [
{
name = "nomifactory";
jdk = pkgs.jdk8;
extraPackages = jdk8FontPkgs;
hopperSubdomain = "nomi";
hopperPort = 25555;
}
{
name = "monifactory";
jdk = pkgs.jdk21;
hopperSubdomain = "moni";
}
{
name = "rad2";
jdk = pkgs.jdk8;
extraPackages = jdk8FontPkgs;
}
{
name = "smp";
jdk = pkgs.jdk17;
}
{
name = "rc4";
jdk = pkgs.jdk17;
}
{
name = "vh";
jdk = pkgs.jdk17;
hopperPort = 25555;
}
{
name = "gregblock";
jdk = pkgs.jdk8;
extraPackages = jdk8FontPkgs;
}
{
name = "tfg";
jdk = pkgs.jdk17;
}
{
name = "ratgame";
jdk = pkgs.jdk21;
}
{
name = "maidrpg";
jdk = pkgs.jdk17;
}
{
name = "maidsmp";
jdk = pkgs.jdk21;
}
{
name = "maid-event";
jdk = pkgs.jdk21;
hopperSubdomain = "maidevent";
dataPath = "${data}/mc/maidevent/speedrun";
}
{
name = "gtnh";
jdk = pkgs.jdk21;
}
{
name = "turing";
jdk = pkgs.jdk21;
}
{
name = "tfg2";
jdk = pkgs.jdk21;
autoStart = true;
mem = "10G";
cpu = 8;
}
{
name = "tl";
jdk = pkgs.jdk21;
}
{
name = "botm";
jdk = pkgs.jdk17;
}
{
name = "rogule-build";
jdk = pkgs.jdk21;
}
{
name = "rogule-dev";
jdk = pkgs.jdk21;
}
];
in
mkMcSet {
dataDir = "${data}/mc";
} mcServers
+84
View File
@@ -0,0 +1,84 @@
{
name,
pkgs,
lib,
hostInfo,
dsl,
inputs,
...
}:
let
inherit (dsl) mkContainer mkService;
inherit (hostInfo.paths) data secrets;
inherit (inputs) mtce;
dataDir = "/var/lib/mtce-bot";
threads = 64;
timeout = 1800;
configJson = pkgs.writeText "mtce-bot-config.json" (
builtins.toJSON {
inherit threads timeout;
}
);
mtce-engine = mtce.lib.mkMtce pkgs {
cpuFlags = [
"-O3"
"-march=cascadelake"
];
vectorizedBitSize = 512;
};
mtce-bot = mtce.lib.mkBot pkgs;
in
mkContainer {
inherit name;
svc.autoStart = true;
resources.fs = {
"${dataDir}/data.json" = {
hostPath = "${data}/misc/mtce-discord-bot/data.json";
isReadOnly = false;
};
"${dataDir}/audit" = {
hostPath = "${data}/misc/mtce-discord-bot/audit";
isReadOnly = false;
};
"${dataDir}/config.json" = {
hostPath = "${configJson}";
isReadOnly = true;
};
"${dataDir}/mtce" = {
hostPath = lib.getExe' mtce-engine "mtce";
isReadOnly = true;
};
"/run/secrets/mtce-discord-bot-env" = {
hostPath = "${secrets}/mtce-discord-bot-env";
isReadOnly = true;
};
};
users = [
{
name = "bot";
uid = 1000;
}
];
service =
_:
mkService {
description = "Discord bot (monumenta-charm-eval)";
execStart = lib.getExe' mtce-bot "mtce-bot";
workingDir = dataDir;
networkOnline = true;
serviceConfig = {
User = "bot";
EnvironmentFile = "/run/secrets/mtce-discord-bot-env";
PrivateTmp = true;
CapabilityBoundingSet = "";
ReadWritePaths = [ dataDir ];
};
};
resources.mem = "2G";
resources.cpu = 64;
net.access = [ { to = "@internet"; } ];
includedInTests = false;
}
+215
View File
@@ -0,0 +1,215 @@
{
name,
hostInfo,
dsl,
lib,
containers,
...
}:
let
inherit (dsl) mkContainer;
inherit (hostInfo.paths) data;
mkNginxConfig =
{
email,
vhosts,
resolver,
}:
let
mkVhostEntries =
{
domain,
upstream ? null,
websockets ? true,
redirect ? true,
listenHttp ? false,
extraLocations ? { },
}:
let
httpsListen = {
addr = "0.0.0.0";
port = 443;
ssl = true;
};
httpListen = {
addr = "0.0.0.0";
port = 80;
ssl = false;
};
defaultLoc = lib.optionalAttrs (upstream != null) {
"/" = {
proxyWebsockets = websockets;
# avoid startup error when containers aren't registered yet
# proxyPass is left unset for that reason, so recommendedProxySettings
# won't auto-include its headers here - set them manually to match
extraConfig = ''
set $backend "${upstream.container.name}:${toString upstream.port}";
proxy_pass http://$backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $hostname;
'';
};
};
acmePrimaryOnMain = !redirect && listenHttp;
mainVhost = lib.nameValuePair domain (
{
listen = [ httpsListen ] ++ lib.optional listenHttp httpListen;
locations = defaultLoc // extraLocations;
}
// (
if acmePrimaryOnMain then
{
addSSL = true;
enableACME = true;
}
else
{
onlySSL = true;
useACMEHost = domain;
}
)
);
redirectVhost = lib.nameValuePair "${domain}-redirect" {
serverName = domain;
enableACME = true;
listen = [ httpListen ];
locations = {
"/" = {
return = "301 https://$host$request_uri";
};
};
};
in
[ mainVhost ] ++ lib.optional redirect redirectVhost;
in
{
access = map (v: {
to = v.upstream.container.name;
tcp = [ v.upstream.port ];
}) (lib.filter (v: v.upstream or null != null) vhosts);
containerConfig =
{
lib,
config,
...
}:
{
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
clientMaxBodySize = "100M";
appendHttpConfig = ''
access_log syslog:server=unix:/dev/log,tag=nginx combined;
'';
resolver = {
addresses = [ resolver ];
ipv6 = false;
valid = "30s";
};
virtualHosts = lib.listToAttrs (lib.concatLists (map mkVhostEntries vhosts)) // {
"_" = {
listen = [
{
addr = "0.0.0.0";
port = 443;
extraParameters = [
"ssl"
"default_server"
];
}
{
addr = "0.0.0.0";
port = 80;
extraParameters = [ "default_server" ];
}
];
extraConfig = "ssl_reject_handshake on;";
locations."/".return = "404";
};
};
};
security.acme = {
acceptTerms = true;
defaults.email = email;
};
systemd.services =
lib.mapAttrs' (
cert: _:
lib.nameValuePair "acme-${cert}" {
after = [ "container-ready.target" ];
wants = [ "container-ready.target" ];
}
) config.security.acme.certs
// {
nginx.serviceConfig.FailureAction = "exit-force";
};
};
};
up = container: port: { inherit container port; };
nginxSetup =
hostAddr:
mkNginxConfig {
email = "floweydev@gmail.com";
resolver = hostAddr;
vhosts = [
{
domain = "devenv.floweytf.com";
upstream = up containers.devenv 8080;
}
{
domain = "maven.floweytf.com";
upstream = up containers.maven 8080;
}
{
domain = "git.floweytf.com";
upstream = up containers.git 3000;
}
{
domain = "utils.floweytf.com";
redirect = false;
listenHttp = true;
extraLocations = {
"/static/" = {
alias = "/usr/share/nginx/static/";
extraConfig = "autoindex on;";
};
};
}
{
# no upstream, vhost only exists for certs
domain = "mail.floweytf.com";
}
];
};
in
let
setup = nginxSetup hostAddr;
c = mkContainer {
inherit name;
net.access = setup.access ++ [ { to = "@internet"; } ];
svc.autoStart = true;
svc.depends = lib.attrVals [ "devenv" "maven" "git" ] containers;
net.forward.tcp = [
80
443
];
resources.fs."/var/lib/acme" = {
hostPath = "${data}/core/nginx/acme";
isReadOnly = false;
};
inherit (setup) containerConfig;
};
hostAddr = c.hostAddr;
in
c
+64
View File
@@ -0,0 +1,64 @@
{
name,
hostInfo,
dsl,
...
}:
let
inherit (dsl) mkContainer;
inherit (hostInfo.paths) data;
in
mkContainer {
inherit name;
net.access = [ { to = "@internet"; } ];
svc.autoStart = false;
net.expose.tcp = [ 53 ];
# TODO: figure out how to have this be the external dns resolver only
resources.fs."/var/lib/pihole" = {
hostPath = "${data}/misc/pihole";
isReadOnly = false;
};
containerConfig = _: {
services.pihole-ftl = {
enable = true;
stateDirectory = "/var/lib/pihole";
openFirewallDNS = true;
settings.dns.upstreams = [
"1.1.1.1"
"8.8.8.8"
];
lists = [
{
url = "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts";
type = "block";
description = "StevenBlack unified hosts";
enabled = true;
}
{
url = "https://big.oisd.nl/domainswild";
type = "block";
description = "OISD Big";
enabled = true;
}
{
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/domains/pro.txt";
type = "block";
description = "HaGeZi Multi Pro";
enabled = true;
}
{
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/domains/tif.txt";
type = "block";
description = "HaGeZi Threat Intelligence Feeds";
enabled = true;
}
];
};
services.pihole-web = {
enable = true;
ports = [ "443s" ];
};
systemd.services.pihole-ftl.serviceConfig.FailureAction = "exit-force";
};
resources.mem = "512M";
}
+186
View File
@@ -0,0 +1,186 @@
{
pkgs,
hostInfo,
dsl,
self,
lib,
customPkgs,
...
}:
let
inherit (dsl) mkContainer;
inherit (hostInfo.paths) server data;
sshTrait = {
resources.fs."/etc/ssh/authorized_keys" = {
hostPath = "${server}/keypair/id_ed25519.pub";
isReadOnly = true;
};
users = [
{
name = "runner";
uid = 1000;
}
];
containerConfig =
{
lib,
pkgs,
...
}:
lib.mkMerge [
{
services.openssh = {
enable = true;
authorizedKeysFiles = [ "/etc/ssh/authorized_keys" ];
settings = {
PasswordAuthentication = false;
UseDns = false;
PrintMotd = false;
};
};
systemd.services.sshd.serviceConfig.FailureAction = "exit-force";
}
{
environment.systemPackages = [
customPkgs.kitty-terminfo
pkgs.wezterm.terminfo
];
}
];
};
mkSsh =
{
name,
packages ? [ ],
nixDaemon ? true,
routingInfo ? {
username = lib.strings.removePrefix "ssh-" name;
},
net ? { },
resources ? { },
traits ? [ ],
}:
mkContainer {
inherit name;
inherit net resources;
svc.autoStart = true;
isolation.nixDaemon = nixDaemon;
traits = [ sshTrait ] ++ traits;
packages = with pkgs; [ tmux ] ++ packages;
miscProperties = self._sshpiperRegister routingInfo;
};
in
{
ssh-admin = mkSsh {
name = "ssh-admin";
net.access = [
{
to = "overseer";
tcp = [ 80 ];
}
];
resources.fs = {
"/home/runner/data" = {
hostPath = server;
isReadOnly = false;
};
"/run/dbus/system_bus_socket" = {
hostPath = "/run/dbus/system_bus_socket";
isReadOnly = false;
};
};
packages = with pkgs; [
git
systemd
];
};
ssh-botm = mkSsh {
name = "ssh-botm";
resources.fs."/home/runner/botm" = {
hostPath = "${data}/mc/botm";
isReadOnly = false;
};
packages = with pkgs; [
python3
jdk17
tmux
];
};
ssh-generic = mkSsh {
name = "ssh-generic";
net.access = [ { to = "@internet"; } ];
resources = {
fs."/home/runner/data" = {
hostPath = "${data}/ssh/generic";
isReadOnly = false;
};
mem = "16G";
cpu = 65;
};
routingInfo = [
{
username = "autumn";
keysName = "generic";
}
];
};
ssh-proxy = mkSsh {
name = "ssh-proxy";
resources.fs."/home/runner/data" = {
hostPath = "${data}/ssh/nyaa";
isReadOnly = true;
};
packages = with pkgs; [ python3 ];
routingInfo = [
{
username = "nyaa";
keysName = "nyaa";
}
];
};
ssh-rogule = mkSsh {
name = "ssh-rogule";
resources.fs = {
"/home/runner/rogule-dev" = {
hostPath = "${data}/mc/rogule-dev";
isReadOnly = false;
};
"/home/runner/rogule-build" = {
hostPath = "${data}/mc/rogule-build";
isReadOnly = false;
};
};
packages = with pkgs; [
python3
jdk21
];
};
ssh-starwalker = mkSsh {
name = "ssh-starwalker";
resources.fs."/home/runner/starwalker" = {
hostPath = "${data}/mc/starwalker";
isReadOnly = false;
};
packages = with pkgs; [
jdk21
];
};
ssh-tl = mkSsh {
name = "ssh-tl";
resources.fs."/home/runner/tl" = {
hostPath = "${data}/mc/tl";
isReadOnly = false;
};
packages = with pkgs; [
jdk21
];
};
}
+122
View File
@@ -0,0 +1,122 @@
{
name,
pkgs,
hostInfo,
dsl,
lib,
containers,
customPkgs,
...
}:
let
inherit (dsl) mkContainer mkService collectContainers;
inherit (hostInfo.paths) server secrets;
dataDir = hostInfo.paths.data;
mkSshpiperConfig =
{
pipes,
privateKey ? "/common_id",
}:
let
header = ''
version: "1.0"
pipes:
'';
mkPipe =
{
username,
host,
keysName ? username,
}:
''
- from:
- username: "${username}"
authorized_keys: /etc/sshpiperd/keys/user_${keysName}/authorized_keys
to:
host: ${host}:22
username: "runner"
private_key: "${privateKey}"
ignore_hostkey: true
'';
in
header + lib.concatMapStrings mkPipe pipes;
sshPipes = lib.concatMap (
{ container, data }: map (lib.mergeAttrs { host = container.name; }) data
) (collectContainers "sshpiperRoutes" containers);
sshpiperYaml = pkgs.writeText "sshpiper.yaml" (mkSshpiperConfig {
pipes = sshPipes;
});
in
{
${name} = mkContainer {
inherit name;
net.access = map (p: {
to = p.host;
tcp = [ 22 ];
}) sshPipes;
svc.autoStart = true;
net.forward.tcp = [
{
host = 22;
container = 2222;
}
];
resources.fs = {
"/etc/sshpiperd/keys" = {
hostPath = "${dataDir}/core/sshpiper";
isReadOnly = true;
};
"/common_id" = {
hostPath = "${server}/keypair/id_ed25519";
isReadOnly = true;
};
"/etc/ssh/ssh_host_ed25519_key" = {
hostPath = "${secrets}/ssh_host_ed25519_key";
isReadOnly = true;
};
"/etc/ssh/ssh_host_rsa_key" = {
hostPath = "${secrets}/ssh_host_rsa_key";
isReadOnly = true;
};
"/etc/ssh/ssh_host_ecdsa_key" = {
hostPath = "${secrets}/ssh_host_ecdsa_key";
isReadOnly = true;
};
};
service =
{ pkgs, ... }:
mkService {
description = "sshpiper SSH multiplexer";
execStart = "${lib.getExe' customPkgs.sshpiper "sshpiperd"} --log-level trace --server-key '/etc/ssh/ssh_host_*_key' ${lib.getExe' customPkgs.sshpiper "yaml"} --config /run/sshpiperd/config.yaml";
networkOnline = true;
serviceConfig = {
# sshpiperd rejects world-readable configs; copy store (444) -> runtime (600)
ExecStartPre = "${lib.getExe' pkgs.coreutils "install"} -m 600 ${sshpiperYaml} /run/sshpiperd/config.yaml";
RuntimeDirectory = "sshpiperd";
PrivateTmp = true;
RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX";
};
};
};
_sshpiperRegister =
let
doTranslate =
self:
{
username,
keysName ? username,
}:
{
inherit username keysName;
host = self.name;
};
in
args: self: {
sshpiperRoutes =
if lib.isAttrs args then [ (doTranslate self args) ] else lib.map (doTranslate self) args;
};
}