Files
hearth/nixos/system/hectic-lab/hectic-lab.nix
T

386 lines
12 KiB
Nix

{
inputs,
flake,
self,
...
}:
{
config,
pkgs,
lib,
modulesPath,
...
}:
with builtins;
with lib;
let
domain = "hectic-lab.com";
matrixDomain = "accord.tube";
sslOpts = {
sslCertificate = config.sops.secrets."ssl/porkbun/${domain}/domain.cert.pem".path;
sslCertificateKey = config.sops.secrets."ssl/porkbun/${domain}/private.key.pem".path;
};
in {
imports = [
self.nixosModules.hectic
self.nixosModules.matrix-cluster
inputs.sops-nix.nixosModules.sops
self.nixosModules."shadowsocks-rust" # NOTE(nrv): impl
self.nixosModules."shadowsocks" # NOTE(nrv): usage/instance
inputs.hectic-landing.nixosModules.hectic-landing
(import ./containers.nix { inherit flake self inputs; })
(import ./mechabellum.nix { inherit flake self inputs domain sslOpts; })
(import (./. + "/sentinèlla.nix") { inherit flake self inputs domain sslOpts; })
];
services.hectic-landing = {
enable = true;
package = inputs.hectic-landing.packages.${pkgs.stdenv.hostPlatform.system}.hectic-landing;
domain = domain;
port = 3000;
host = "127.0.0.1";
};
# NOTE(yukkop): both nixos-mailserver and hectic-landing module set
# security.acme.defaults.email. Force the mailserver-aligned address.
security.acme.defaults.email = lib.mkForce "security@${domain}";
hectic = {
archetype.dev.enable = true;
hardware.hetzner-cloud = {
enable = true;
networkMatchConfigName = "enp1s0";
ipv4 = "128.140.75.58";
ipv6 = "2a01:4f8:c2c:d54a";
};
services.matrix = {
enable = false;
};
generic.matrix-cluster = {
enable = true;
role = "primary";
inherit matrixDomain;
signingKeyFile = config.sops.secrets."matrix/signing-key".path;
secretsFile = config.sops.secrets."matrix/secrets".path;
turnSecretFile = config.sops.secrets."matrix/turn-secret".path;
publicIp = "128.140.75.58";
users = {
yukkop = {
passwordFile = config.sops.secrets."matrix/users/yukkop/password".path;
admin = true;
};
liquiz = {
passwordFile = config.sops.secrets."matrix/users/liquiz/password".path;
};
vismajor = {
passwordFile = config.sops.secrets."matrix/users/vismajor/password".path;
};
lvgkcfjl = {
passwordFile = config.sops.secrets."matrix/users/lvgkcfjl/password".path;
};
};
objectStorage.s3 = {
bucket = "matrix-hectic-lab";
regionName = "hel1";
endpointUrl = "https://hel1.your-objectstorage.com";
credentialsFile = config.sops.secrets."matrix/object-storage/credentials".path;
};
replication = {
peerHost = "91.198.166.181";
passwordFile = config.sops.secrets."matrix/postgres-replication-password".path;
allowedSourceIPs = [ "91.198.166.181/32" ];
};
acme = {
enable = true;
porkbunApiKeyFile = config.sops.secrets."matrix/porkbun-api-key".path;
porkbunSecretApiKeyFile = config.sops.secrets."matrix/porkbun-secret-api-key".path;
};
};
services.media-browser = {
enable = true;
port = 3001;
s3Bucket = "matrix-hectic-lab";
s3Endpoint = "https://hel1.your-objectstorage.com";
s3Region = "hel1";
s3CredentialsFile = config.sops.secrets."matrix/object-storage/credentials".path;
};
};
# NOTE(yukkop): disk was provisioned by Hetzner rescue image, disko was never
# run, so partition labels don't exist. Override fileSystems with actual UUIDs.
fileSystems."/" = lib.mkForce {
device = "/dev/disk/by-uuid/48ba7286-d019-4cdc-9784-459767979b07";
fsType = "ext4";
};
fileSystems."/boot" = lib.mkForce {
device = "/dev/disk/by-uuid/71F2-4E98";
fsType = "vfat";
options = [ "umask=0077" ];
};
programs.zsh.enable = true;
programs.zsh.interactiveShellInit = ''
setopt vi
'';
environment.systemPackages = with pkgs; [
git
rsync
python311
kitty
];
# Secrets config
sops = {
gnupg.sshKeyPaths = [ ];
age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
defaultSopsFile = "${flake}/sus/hectic-lab.yaml";
};
users.users.root.openssh.authorizedKeys.keys = [
# yukkop
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMuP5NSfEQmO6m77xBWZvZ3hk7cw1q2k2vbsFd37rybU u0_a327@localhost"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJBLxMo5icX2Xyng7mcWGnIi+c4ZbVygjPhuU8noCkfZ"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxgLlX/15Fk7PgIc9FSrA7oRtA8qK4GXfOhj7ZlNUaJ nix-on-droid@localhost"
# snuff
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFouceNUxI3bGC24/hfA8J3VuBpvTcZh3KhixgrMiLte"
# nrv
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIE/EhBI6sJb2yHbTkqhZiCzUrsLE6t+CZe7RhS22z7w5 nrv@adamantia"
# github workflow
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKPEUArBxu7NUULT7Pi8ArtVxY1uVbIBSaeRKtqz1sz1"
];
users.users.ds4d = { # NOTE(nrv): artishoque
isNormalUser = true;
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINcjBc57N6MxtMYAHEB/nwZ+OGsG3P1KWO1ZXvzQyhKn ds4d@ds4d"
];
};
users.users.sshuttle = {
isNormalUser = true;
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKd4iU2E5fiwPwBbeo1ZPo0YBFEj9qBPew/KitaO+OHU"
];
};
sops.secrets."mailserver/security/hashedPassword" = {};
sops.secrets."mailserver/yukkop/hashedPassword" = {};
sops.secrets."mailserver/daniil-perlyk/hashedPassword" = {};
sops.secrets."mailserver/snuff/hashedPassword" = {};
sops.secrets."mailserver/antoshka/hashedPassword" = {};
sops.secrets."mailserver/founders/hashedPassword" = {};
sops.secrets."mailserver/lvgkcfjl/hashedPassword" = {};
sops.secrets."init-postgresql" = {
key = "init-postgresql";
};
sops.secrets."matrix/secrets" = {
key = "matrix/secrets";
owner = "matrix-synapse";
sopsFile = "${flake}/sus/matrix-cluster.yaml";
};
sops.secrets."matrix/turn-secret" = {
key = "matrix/turn-secret";
owner = "turnserver";
group = "turnserver";
mode = "0400";
sopsFile = "${flake}/sus/matrix-cluster.yaml";
};
sops.secrets."matrix/users/yukkop/password" = {
key = "matrix/users/yukkop/password";
owner = "matrix-synapse";
};
sops.secrets."matrix/users/liquiz/password" = {
key = "matrix/users/liquiz/password";
owner = "matrix-synapse";
};
sops.secrets."matrix/users/vismajor/password" = {
key = "matrix/users/vismajor/password";
owner = "matrix-synapse";
};
sops.secrets."matrix/users/lvgkcfjl/password" = {
key = "matrix/users/lvgkcfjl/password";
owner = "matrix-synapse";
};
sops.secrets."matrix/object-storage/credentials" = {
key = "matrix/object-storage/credentials";
owner = "matrix-synapse";
mode = "0400";
sopsFile = "${flake}/sus/matrix-cluster.yaml";
};
# Shared cluster secrets (PL standby also reads from this file).
sops.secrets."matrix/signing-key" = {
key = "matrix/signing-key";
owner = "matrix-synapse";
mode = "0400";
sopsFile = "${flake}/sus/matrix-cluster.yaml";
};
sops.secrets."matrix/postgres-replication-password" = {
key = "matrix/postgres-replication-password";
owner = "postgres";
mode = "0400";
sopsFile = "${flake}/sus/matrix-cluster.yaml";
};
sops.secrets."matrix/porkbun-api-key" = {
key = "matrix/porkbun-api-key";
mode = "0400";
sopsFile = "${flake}/sus/matrix-cluster.yaml";
};
sops.secrets."matrix/porkbun-secret-api-key" = {
key = "matrix/porkbun-secret-api-key";
mode = "0400";
sopsFile = "${flake}/sus/matrix-cluster.yaml";
};
services.mailserver = {
enable = true;
domain = domain;
loginAccounts = {
"security" = {
hashedPasswordFile = config.sops.secrets."mailserver/security/hashedPassword".path;
};
"founders" = {
hashedPasswordFile = config.sops.secrets."mailserver/founders/hashedPassword".path;
};
"lvgkcfjl" = {
hashedPasswordFile = config.sops.secrets."mailserver/lvgkcfjl/hashedPassword".path;
};
"yukkop" = {
hashedPasswordFile = config.sops.secrets."mailserver/yukkop/hashedPassword".path;
};
"daniil-perlyk" = {
hashedPasswordFile = config.sops.secrets."mailserver/daniil-perlyk/hashedPassword".path;
};
"snuff" = {
hashedPasswordFile = config.sops.secrets."mailserver/snuff/hashedPassword".path;
};
"antoshka" = {
hashedPasswordFile = config.sops.secrets."mailserver/antoshka/hashedPassword".path;
};
};
};
mailserver.stateVersion = 3;
services.redis.servers."vproxy-bot-test-state" = {
enable = true;
port = 6379;
};
services.mysql = {
enable = true;
package = pkgs.mariadb;
};
networking.firewall = {
allowedTCPPorts = [
80
443
3306 # mysql
25565
55228 # ss-bfs
];
allowedUDPPorts = [
51820 # wg-bfs
55228 # ss-bfs
];
# Postgres replication: only the PL standby peer may reach 5432.
extraInputRules = ''
ip saddr 91.198.166.181/32 tcp dport 5432 accept
'';
};
virtualisation.docker.enable = true;
systemd.tmpfiles.rules = [
"d /var/www/store 0755 nginx nginx -"
];
sops.secrets."ssl/porkbun/${domain}/domain.cert.pem" = { group = "nginx"; mode = "0440"; };
sops.secrets."ssl/porkbun/${domain}/private.key.pem" = { group = "nginx"; mode = "0440"; };
sops.secrets."ssl/porkbun/${domain}/public.key.pem" = { group = "nginx"; mode = "0440"; };
services.nginx = {
enable = true;
# NOTE(yukkop): virtualHosts.${domain} is owned by the hectic-landing module
# (ACME-managed). See services.hectic-landing above.
virtualHosts."store.${domain}" = sslOpts // {
forceSSL = true;
root = "/var/www/store";
locations."/" = {
extraConfig = ''
autoindex on;
'';
};
};
virtualHosts."snuff.${domain}" = sslOpts // {
forceSSL = true;
locations."/" = {
extraConfig = ''
proxy_pass http://188.32.215.29:3993/;
proxy_redirect off;
'';
};
};
virtualHosts."nrv.${domain}" = sslOpts // {
forceSSL = true;
locations."/" = {
extraConfig = ''
proxy_pass http://127.0.0.1:22842/;
proxy_redirect off;
'';
};
};
virtualHosts."yukkop.${domain}" = sslOpts // {
forceSSL = true;
locations."/" = {
extraConfig = ''
proxy_pass http://127.0.0.1:9855/;
proxy_redirect off;
'';
};
};
};
# === WireGuard (disabled) ===
sops.secrets."wg-bfs/private-key" = {};
# networking.wireguard.interfaces = let
# subnet = "10.13.37";
# externalInterface = "eth0";
# in {
# wg-bfs = {
# ips = [ "${subnet}.1/24" ];
# listenPort = 51820;
# postSetup = ''
# ${pkgs.iptables}/bin/iptables -t 'nat' -A 'POSTROUTING' -s '${subnet}.0/24' -o '${externalInterface}' -j 'MASQUERADE'
# '';
# postShutdown = ''
# ${pkgs.iptables}/bin/iptables -t 'nat' -D 'POSTROUTING' -s '${subnet}.0/24' -o '${externalInterface}' -j 'MASQUERADE'
# '';
# privateKeyFile = config.sops.secrets."wg-bfs/private-key".path;
# generatePrivateKeyFile = false;
# peers = with lib; with builtins; let
# pubkeys = [
# "3dVzf1jxnVVTkLAyxedW+kRQBexZDzYDwpaLIcTrLjc=" # nrv (host: 2)
# "Kk2d0ncj24rO0qbuKh4V4t1OLnmVYbeaYvuEnL2OPFM=" # lysmi (host: 3)
# "BkM/NEDbR/XQ6WYQ0Yt+nJrc2HFCVsoW4QxBmkqxHn8=" # yukkop (host: 4)
# ];
# hosts = lists.range 2 254;
# zipped = zipLists pubkeys hosts;
# in flip map zipped ({ fst, snd }: {
# publicKey = "${fst}";
# allowedIPs = [ "${subnet}.${toString snd}/32" ];
# });
# };
# };
}