You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

572 lines
17 KiB
Nix

let
nixpkgsOptions = {
overlays = [
(self: super: {
/* NOTE: Namespaced under `pkgs.cryto.*` to prevent naming conflicts with upstream nixpkgs */
cryto = {
fetchFromCrytoGit = self.callPackage ./lib/fetch/from-cryto-git.nix {};
nodeApplication = self.callPackage ./lib/node-application.nix {};
unpack = self.callPackage ./lib/unpack.nix {};
mobileProxy = self.callPackage ./packages/mobile-proxy { configFile = null; };
matrixRooms = self.callPackage ./packages/matrix-rooms {};
pastebinStream = self.callPackage ./packages/pastebin-stream { errorPath = null; };
};
})
];
};
pkgs = (import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/nixos-21.11.tar.gz") nixpkgsOptions);
presets = {
base = (import ./presets/base.nix);
kvm = (import ./presets/kvm.nix);
};
nginxPresets = {
phpDisabled = (import ./presets/nginx/php-disabled.nix);
reverseProxy = (import ./presets/nginx/reverse-proxy.nix);
letsEncrypt = (import ./presets/nginx/lets-encrypt.nix);
};
nodes = (import ./data/nodes.nix);
tincConfiguration = (import ./lib/tinc-configuration.nix);
trackSystemMetrics = (import ./lib/track-system-metrics.nix);
trackServiceMetrics = (import ./lib/track-service-metrics.nix);
httpHealthChecks = (import ./lib/http-health-checks.nix);
nginx = (import ./lib/nginx.nix);
daemon = (import ./lib/daemon.nix);
errorReporter = (import ./lib/error-reporter.nix);
in {
network = {
inherit pkgs;
description = "Cryto";
};
"machine-borg2-01.cryto.net" = let
self = nodes."machine-borg2-01.cryto.net";
in { pkgs, lib, ... }: {
system.stateVersion = "18.09";
networking.hostName = "machine-borg2-01";
# FIXME: Why is this needed?
nixpkgs.overlays = [];
imports = [
presets.base
presets.kvm
./hardware-configurations/machine-borg2-01.nix
(tincConfiguration { hostname = self.hostname; nodes = nodes; })
(trackSystemMetrics self.internalIpv4)
(trackServiceMetrics self.internalIpv4)
];
boot.loader.grub.device = lib.mkForce "/dev/vda";
users.extraUsers = {
backup-f0x = {
createHome = true;
home = "/home/backup-f0x";
};
backup-haless = {
createHome = true;
home = "/home/backup-haless";
};
};
users.extraGroups = {
backup-f0x = { members = [ "backup-f0x" ]; };
backup-haless = { members = [ "backup-haless" ]; };
};
services.borgbackup.repos = {
"f0x" = {
allowSubRepos = true;
quota = "400G";
path = "/home/backup-f0x";
user = "backup-f0x";
group = "backup-f0x";
authorizedKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINjJDP2TDyj1X/L6gNgHCXASIWoW/VnJ77FQy39VRTi8 f0x@elephantus"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+dwsWe1/ujR0N4IxPv7mfyiuKWURc6QwYNJ+VV8KA6 f0x@behemoth"
];
authorizedKeysAppendOnly = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG7WSUY6Y2lsIawo8dPBu4/Omx6c7/1SMD9ve/vpcorN borg-backup@terra"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDeMWPR38zXAbURVTJs+yGDnld5kO7bcgp/70l4wJG0k borg-backup@luna"
];
};
"haless" = {
allowSubRepos = true;
path = "/home/backup-haless";
user = "backup-haless";
group = "backup-haless";
authorizedKeys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzV5dI01NhwuL6ayiO0STcSQiDf7lEtu63NuLZKQUdZVuVHIqyt3Gquks2OI1NZGrJdXA315yw89ZqyMo+z7gSGHEV6P0fAXKW6G78JOFWsA5lGpaLxTsZ6Q7r0Z9FMqDvA5Jlsyznyj9hhO1cz01WPLzB92ypd9ifldtrAQIYQItxGXOuRkBJiShuIRqtr4Q2chXiOoRZKb4v4Gyt/UPxTpvfM/zcOz0zi1d4ijSbLqgIUJhxvrWADfdgEQ77unepDoD+HT51QBX7dj8RuYivxLSA3vpfNeCgt2CYBf6FYnmWkWSnN1RCtQPJNxsMuLzC2ZBbIkz0tDgcIBPbHxGr sven@linux-rfa7.site"
];
authorizedKeysAppendOnly = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFAOpXsDxE7SXeSw/kjgzdwEkNsL9REMabMqYVPM9rem root@machine-haless-03.cryto.net"
];
};
};
};
"machine-haless-03.cryto.net" = let
self = nodes."machine-haless-03.cryto.net";
in { pkgs, lib, config, ... }@args: {
system.stateVersion = "19.03";
networking.hostName = "machine-haless-03";
imports = [
presets.base
presets.kvm
./hardware-configurations/machine-haless-03.nix
(tincConfiguration { hostname = self.hostname; nodes = nodes; })
(trackSystemMetrics self.internalIpv4)
(trackServiceMetrics self.internalIpv4)
(httpHealthChecks {
both = [
"iomfats.cryto.net"
"castleroland.cryto.net"
"awesomedude.cryto.net"
"matrix-rooms.cryto.net"
"validatem.cryto.net"
"nixos-manual-mdx.cryto.net"
"geojson.cryto.net"
"ossworks.nl"
];
})
(daemon {
name = "mobile-proxy";
displayName = "Mobile Proxy";
fakeHome = true; # Needed for Babel
binaryPath = "${pkgs.cryto.mobileProxy.override { configFile = ./data/mobile-proxy/config.jsx; }}/bin/mobile-proxy";
environmentVariables = {};
})
(daemon {
name = "matrix-rooms";
displayName = "Matrix Room List Viewer";
fakeHome = true; # Needed for Babel
binaryPath = "${pkgs.cryto.matrixRooms}/bin/matrix-room-list-viewer";
environmentVariables = { NODE_ENV = "production"; };
})
(nginx {
"modular-matrix.cryto.net" = [
(nginxPresets.letsEncrypt)
{ root = ./sources/modular-matrix; }
];
"geojson.cryto.net" = [
(nginxPresets.letsEncrypt)
{ root = ../../image-to-geojson/static; }
];
"validatem.cryto.net" = [
(nginxPresets.letsEncrypt)
{ root = ../../validatem/site/build; }
];
"ossworks.nl" = [
(nginxPresets.letsEncrypt)
{ root = ../../ossworks-site/build; }
];
"nixos-manual-mdx.cryto.net" = [
(nginxPresets.letsEncrypt)
{ root = ../../nixos-manual-mdx/build; }
];
"haless.cryto.net" = [
(nginxPresets.letsEncrypt)
{
locations."/shadow/" = {
alias = ./sources/shadow-generator;
};
locations."/knex-mirror/" = {
alias = ./sources/knex-mirror;
};
}
];
"books.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.phpDisabled)
];
"todo.cryto.net" = [
(nginxPresets.phpDisabled)
];
"learn.cryto.net" = [
(nginxPresets.phpDisabled)
];
"vps-list.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.phpDisabled)
];
"iomfats.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.reverseProxy "http://127.0.0.1:3000/")
];
"castleroland.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.reverseProxy "http://127.0.0.1:3000/")
];
"awesomedude.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.reverseProxy "http://127.0.0.1:3000/")
];
"matrix-rooms.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.reverseProxy "http://127.0.0.1:3842/")
];
})
];
# FIXME: Verify that this actually works...
services.borgbackup.jobs.system = {
paths = "/";
exclude = [
"/nix"
"/boot"
"/sys"
"/run"
"/tmp"
"/dev"
"/proc"
];
repo = "backup-haless@machine-borg2-01.cryto.net:haless-03";
encryption = {
mode = "repokey-blake2";
passphrase = (import ../private/machine-haless-03.cryto.net/borg-passphrase.nix);
};
compression = "auto,zlib";
startAt = "daily";
};
};
"machine-konjassiem-02.cryto.net" = let
self = nodes."machine-konjassiem-02.cryto.net";
in { pkgs, lib, config, ... }@args: {
system.stateVersion = "20.03";
networking.hostName = "machine-konjassiem-02";
imports = [
presets.base
presets.kvm
./hardware-configurations/machine-konjassiem-02.nix
(tincConfiguration { hostname = self.hostname; nodes = nodes; })
(trackSystemMetrics self.internalIpv4)
(trackServiceMetrics self.internalIpv4)
(httpHealthChecks {
both = [ "git.cryto.net" ];
})
(nginx {
"git.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.reverseProxy "http://127.0.0.1:3000/")
];
})
];
services.postgresql = {
enable = true;
ensureDatabases = [ "gitea" ];
ensureUsers = [{
name = "git";
ensurePermissions = {
"DATABASE gitea" = "ALL PRIVILEGES";
};
}];
};
users.users.git = {
description = "Gitea Service";
home = "/var/lib/gitea";
useDefaultShell = true;
group = "git";
isSystemUser = true;
};
users.groups.git = {};
# NOTE: Workaround that removes `setuid` from the disallowed syscall list, because otherwise sendmail/opensmtpd breaks
# systemd.services.gitea.serviceConfig.SystemCallFilter = lib.mkForce "~@clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @raw-io @reboot @resources @swap";
# Temporary workaround to make opensmtpd sendmail work (ref. https://github.com/NixOS/nixpkgs/issues/103446)
# Can remain enabled
systemd.services.gitea.serviceConfig.PrivateMounts = lib.mkForce true;
systemd.services.gitea.serviceConfig.PrivateTmp = lib.mkForce true;
systemd.services.gitea.serviceConfig.ProtectControlGroups = lib.mkForce true;
systemd.services.gitea.serviceConfig.ProtectHome = lib.mkForce true;
systemd.services.gitea.serviceConfig.ProtectSystem = lib.mkForce "full"; # downgraded from "strict"
# Have to be disabled
systemd.services.gitea.serviceConfig.LockPersonality = lib.mkForce false;
systemd.services.gitea.serviceConfig.MemoryDenyWriteExecute = lib.mkForce false;
systemd.services.gitea.serviceConfig.NoNewPrivileges = lib.mkForce false;
systemd.services.gitea.serviceConfig.PrivateDevices = lib.mkForce false;
systemd.services.gitea.serviceConfig.PrivateUsers = lib.mkForce false;
systemd.services.gitea.serviceConfig.ProtectClock = lib.mkForce false;
systemd.services.gitea.serviceConfig.ProtectHostname = lib.mkForce false;
systemd.services.gitea.serviceConfig.ProtectKernelLogs = lib.mkForce false;
systemd.services.gitea.serviceConfig.ProtectKernelModules = lib.mkForce false;
systemd.services.gitea.serviceConfig.ProtectKernelTunables = lib.mkForce false;
systemd.services.gitea.serviceConfig.RestrictAddressFamilies = lib.mkForce [];
systemd.services.gitea.serviceConfig.RestrictRealtime = lib.mkForce false;
systemd.services.gitea.serviceConfig.RestrictSUIDSGID = lib.mkForce false;
systemd.services.gitea.serviceConfig.SystemCallArchitectures = lib.mkForce "";
systemd.services.gitea.serviceConfig.SystemCallFilter = lib.mkForce [];
# FIXME: This isn't really a secret... and should probably be a part of the Gitea module instead
deployment.secrets.gitea-header = {
source = builtins.toFile "header.tmpl" "<style> * { tab-size: 4; } </style>";
destination = "/var/lib/gitea/custom/templates/custom/header.tmpl";
owner = { user = "git"; };
action = [ "systemctl" "restart" "gitea.service" ];
};
# FIXME: Healthcheck for this!
services.gitea = {
enable = true;
user = "git";
appName = "Cryto Git";
repositoryRoot = "/var/lib/repositories";
log.level = "Info";
domain = "git.cryto.net";
httpAddress = "localhost";
httpPort = 3000;
rootUrl = "https://git.cryto.net/";
database = {
createDatabase = false;
type = "postgres";
socket = "/run/postgresql";
user = "git";
};
settings = {
server = {
LOCAL_ROOT_URL = "http://localhost:3000/";
};
database = {
LOG_SQL = false;
};
service = {
ENABLE_CAPTCHA = true;
REGISTER_EMAIL_CONFIRM = true;
ENABLE_NOTIFY_MAIL = true;
ENABLE_USER_HEATMAP = false;
};
security = {
PASSWORD_COMPLEXITY = "off";
INTERNAL_TOKEN = lib.mkForce "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE2MDU0NzQ1Mzh9.XqS6-ha22VNgtUP_mVkZXCMmst-lO8blFAEpWMSlU5g";
};
session = {
PROVIDER = "file";
};
mailer = {
ENABLED = true;
MAILER_TYPE = "sendmail";
FROM = "\"Cryto Git\" <noreply@git.cryto.net>";
SENDMAIL_PATH = "${pkgs.system-sendmail}/bin/sendmail";
};
};
};
# FIXME: DKIM/DMARC
services.opensmtpd = {
enable = true;
serverConfiguration = ''
listen on lo
action "outbound" relay
match from local for any action "outbound"
'';
};
};
"machine-pikachu-02.cryto.net" = let
self = nodes."machine-pikachu-02.cryto.net";
in { pkgs, lib, config, ... }@args: {
system.stateVersion = "19.03";
networking.hostName = "machine-pikachu-02";
imports = [
presets.base
presets.kvm
./hardware-configurations/machine-pikachu-02.nix
(tincConfiguration { hostname = self.hostname; nodes = nodes; })
(trackSystemMetrics self.internalIpv4)
(trackServiceMetrics self.internalIpv4)
];
};
"machine-osmium-01.cryto.net" = let
self = nodes."machine-osmium-01.cryto.net";
pastebinStreamPackage = pkgs.cryto.pastebinStream.override { errorPath = "/var/lib/pastebin-stream/errors"; };
in { pkgs, lib, config, ... }@args: {
system.stateVersion = "16.09";
networking.hostName = "machine-osmium-01";
imports = [
presets.base
./hardware-configurations/machine-osmium-01.nix
(tincConfiguration { hostname = self.hostname; nodes = nodes; })
(trackSystemMetrics self.internalIpv4)
(trackServiceMetrics self.internalIpv4)
(httpHealthChecks {
both = [
"pastebin-stream.cryto.net"
];
})
(daemon {
name = "pastebin-stream";
displayName = "pastebin-stream";
fakeHome = false;
binaryPath = "${pastebinStreamPackage}/bin/pastebin-stream";
environmentVariables = {};
})
(errorReporter {
serviceName = "pastebin-stream";
binaryPath = "${pastebinStreamPackage}/node_modules/.bin/report-errors";
errorPath = "/var/lib/pastebin-stream/errors";
from = "ops@cryto.net";
to = "admin@cryto.net";
})
(nginx {
"pastebin-stream.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.reverseProxy "http://localhost:3000/")
];
})
];
};
"machine-workbot-01.cryto.net" = let
self = nodes."machine-workbot-01.cryto.net";
in { pkgs, lib, config, ... }@args: {
system.stateVersion = "18.03";
networking.hostName = "machine-workbot-01";
imports = [
presets.base
./hardware-configurations/machine-workbot-01.nix
(tincConfiguration { hostname = self.hostname; nodes = nodes; })
(trackSystemMetrics "127.0.0.1")
(trackServiceMetrics "127.0.0.1")
(httpHealthChecks {
both = [
"hydra.cryto.net"
"prometheus.cryto.net"
"metrics.cryto.net"
# "nix-cache.cryto.net" # Not directory-indexable
];
})
(nginx {
"hydra.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.reverseProxy "http://localhost:3333/")
];
"prometheus.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.reverseProxy "http://localhost:9090/")
];
"metrics.cryto.net" = [
(nginxPresets.letsEncrypt)
(nginxPresets.reverseProxy "http://localhost:8452/")
];
"nix-cache.cryto.net" = [
(nginxPresets.letsEncrypt)
{ root = "/var/lib/hydra-builds"; }
];
})
];
services.postgresql = {
enable = true;
};
services.hydra = {
enable = true;
port = 3333;
hydraURL = "http://hydra.cryto.net/";
notificationSender = "hydra@cryto.net";
useSubstitutes = false;
minimumDiskFree = 20;
minimumDiskFreeEvaluator = 20;
buildMachinesFiles = pkgs.lib.mkIf (config.nix.buildMachines == []) [];
extraConfig = ''
store_uri = file:///var/lib/hydra-builds?secret-key=/var/lib/hydra/binary-cache.key&write-nar-listing=1
binary_cache_public_uri = https://nix-cache.cryto.net
'';
};
services.prometheus = {
enable = true;
globalConfig = {
scrape_interval = "30s";
};
scrapeConfigs = let
nameInstance = address: name: {
source_labels = [ "__address__" ];
target_label = "instance";
regex = address;
replacement = name;
};
mapToPort = port: builtins.map (host: "${host.internalIpv4}:${builtins.toString port}");
mapToPortRelabel = port: builtins.map (host: (nameInstance "${host.internalIpv4}:${builtins.toString port}" host.friendlyName));
# Replace the workbot node (ie. ourselves) with an entry that points directly at localhost instead
nodes_ = builtins.attrValues (nodes // {
"machine-workbot-01.cryto.net" = {
friendlyName = "workbot";
internalIpv4 = "localhost";
};
});
in [
{
job_name = "prometheus";
static_configs = [{
targets = [
"localhost:9090"
];
}];
relabel_configs = [
(nameInstance "localhost:9090" "workbot")
];
} {
job_name = "nodes";
scrape_interval = "10s";
static_configs = [{
targets = mapToPort 9100 nodes_;
}];
relabel_configs = mapToPortRelabel 9100 nodes_;
} {
job_name = "systemd";
scrape_interval = "60s";
static_configs = [{
targets = mapToPort 9333 nodes_;
}];
relabel_configs = mapToPortRelabel 9333 nodes_;
}
];
};
services.grafana = {
enable = true;
port = 8452;
rootUrl = "https://metrics.cryto.net/";
security = let
credentials = import ../private/grafana-credentials.nix;
in {
adminUser = credentials.username;
adminPassword = credentials.password;
};
auth = {
anonymous.enable = true;
};
};
networking.firewall.allowedTCPPorts = [
80
443
];
};
}