feat(sterni/machines): add edwin

This adds edwin, the machine running sterni.lv, as well as my
idiosyncratic deployment solution. It is based on instantiating the
system configuration locally (where you'd work on the configuration),
copying the derivation files to the remote machine where the system
derivation is realised and deployed. Unfortunately, the first step tends
to be quite slow (despite gzip compression), so this may not be the
definite way despite its advantages.

Change-Id: I30f597692338df3981e01a1b7eee9cdad48f94cb
Reviewed-on: https://cl.tvl.fyi/c/depot/+/7293
Reviewed-by: sterni <sternenseemann@systemli.org>
Tested-by: BuildkiteCI
This commit is contained in:
sterni 2022-06-06 12:37:11 +02:00
parent 7b4a545699
commit 2490ce968c
18 changed files with 693 additions and 0 deletions

View file

@ -0,0 +1 @@
Subdirectories are manually reexposed by default.nix as the contain NixOS modules

View file

@ -0,0 +1,77 @@
{ depot, lib, pkgs, ... }:
let
bins = depot.nix.getBins pkgs.nq [ "fq" "nq" ];
machines = lib.mapAttrs
(name: _:
depot.ops.nixos.nixosFor (import (./. + ("/" + name)))
)
(lib.filterAttrs (_: type: type == "directory") (builtins.readDir ./.));
# TODO(sterni): share code with rebuild-system
localDeployScriptFor = { system, ... }:
pkgs.writeShellScript "local-deploy-${system.name}" ''
set -eu
nix-env -p /nix/var/nix/profiles/system --set "${system}"
"${system}/bin/switch-to-configuration" switch
'';
# Builds the system on the remote machine
deployScriptFor = { system, ... }@machine:
pkgs.writeShellScript "remote-deploy-${system.name}" ''
set -eu
if [ $# != 1 ]; then
printf 'usage: %s [USER@]HOST' "$0"
exit 100
fi
readonly TARGET_HOST="$1"
readonly DEPLOY_DRV="${
builtins.unsafeDiscardOutputDependency (
# Wrapper script around localDeployScriptFor that merely starts the
# local deploy script using and nq and then waits using fq. This means
# we can't Ctrl-C the deploy and it won't be terminated by a lost
# connection.
pkgs.writeShellScript "queue-deploy-${system.name}" ''
readonly STATE_DIR="''${XDG_STATE_HOME:-$HOME/.local/state}/sterni-deploy"
mkdir -p "$STATE_DIR"
export NQDIR="$STATE_DIR"
"${bins.nq}" "${localDeployScriptFor machine}"
"${bins.fq}"
''
).drvPath
}"
nix-copy-closure -s --gzip --to "$TARGET_HOST" "$DEPLOY_DRV"
readonly DEPLOY_OUT="$(ssh "$TARGET_HOST" "nix-store -r '$DEPLOY_DRV'")"
ssh "$TARGET_HOST" "$DEPLOY_OUT"
'';
in
depot.nix.readTree.drvTargets (
# this somehow becomes necessarily ugly with nixpkgs-fmt
machines // { inherit deployScriptFor; } //
lib.mapAttrs'
(name: _: {
name = "${name}System";
value = machines.${name}.system;
})
machines
//
lib.mapAttrs'
(name: _: {
name = "${name}Deploy";
value = deployScriptFor machines.${name};
})
machines
)

View file

@ -0,0 +1,77 @@
{ config, lib, pkgs, depot, ... }:
{
imports = [
# Third party modules we use
"${depot.third_party.agenix.src}/modules/age.nix"
# These modules touch things related to booting (filesystems, initrd network…)
./hardware.nix
./network.nix
# These modules configure services, websites etc.
./minecraft.nix
./gopher.nix
./http/sterni.lv.nix
./http/code.sterni.lv.nix
./http/flipdot.openlab-augsburg.de.nix
./http/likely-music.sterni.lv.nix
];
config = {
time.timeZone = "Europe/Berlin";
nixpkgs.config.allowUnfreeRedistributable = true;
nix.package = pkgs.nix_2_3;
tvl.cache.enable = true;
services = {
journald.extraConfig = ''
SystemMaxUse=1024M
'';
openssh.enable = true;
};
security.acme = {
defaults.email = builtins.getAttr "email" (
builtins.head (
builtins.filter (attrs: attrs.username == "sterni") depot.ops.users
)
);
acceptTerms = true;
};
programs = {
fish.enable = true;
mosh.enable = true;
tmux.enable = true;
};
environment.systemPackages = [
pkgs.weechat
pkgs.wget
pkgs.git
pkgs.stow
pkgs.htop
pkgs.foot.terminfo
pkgs.vim
];
users = {
users = {
root.openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
lukas = {
isNormalUser = true;
extraGroups = [ "wheel" "http" ];
openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
shell = "${pkgs.fish}/bin/fish";
};
};
};
nix.settings.trusted-users = [
"lukas"
];
system.stateVersion = "20.09";
};
}

View file

@ -0,0 +1,19 @@
{ depot, ... }:
{
config = {
services.spacecookie = {
enable = true;
openFirewall = true;
settings = {
hostname = "sterni.lv";
root = depot.users.sterni.lv.gopher;
log = {
enable = true;
hide-ips = true;
hide-time = true;
};
};
};
};
}

View file

@ -0,0 +1,64 @@
{ config, lib, pkgs, depot, ... }:
{
config = {
boot = {
loader.grub = {
enable = true;
version = 2;
# TODO(sterni): use /dev/disk/by-id ?
devices = [
"/dev/sda"
"/dev/sdb"
];
};
kernelModules = [
"kvm-intel"
];
initrd.availableKernelModules = [
"ahci"
"sd_mod"
"btrfs"
"realtek"
"r8169"
];
};
boot.initrd.luks.devices = {
"crypt1".device = "/dev/disk/by-uuid/02ac34ee-be10-401b-90c2-1c6aa54c4d5f";
"crypt2".device = "/dev/disk/by-uuid/7ce07191-e704-4aed-a60f-dfa3ce386b26";
"crypt-swap1".device = "/dev/disk/by-uuid/fec7155c-6a65-4f25-b271-43763e4c31eb";
"crypt-swap2".device = "/dev/disk/by-uuid/7b0a03fc-51de-4578-9811-94b00df09d88";
};
fileSystems = {
"/" = {
device = "/dev/disk/by-label/root";
fsType = "btrfs";
};
"/boot" = {
device = "/dev/disk/by-label/boot";
fsType = "btrfs";
};
};
swapDevices = [
{ device = "/dev/disk/by-label/swap1"; }
{ device = "/dev/disk/by-label/swap2"; }
];
powerManagement.cpuFreqGovernor = "performance";
hardware = {
enableRedistributableFirmware = true;
cpu.intel.updateMicrocode = true;
};
nix.settings = {
max-jobs = 2;
cores = 4;
};
};
}

View file

@ -0,0 +1,120 @@
{ depot, pkgs, lib, config, ... }:
# TODO(sterni): automatically sync repositories with upstream if needed
let
virtualHost = "code.sterni.lv";
repos = {
spacecookie = {
description = "gopher server (and library for Haskell)";
};
gopher-proxy = {
description = "Gopher over HTTP proxy";
};
emoji-generic = {
description = "generic emoji library for Haskell (wip)";
};
grav2ty = {
description = "realistic 2d space game";
};
likely-music = {
description = "experimental application for probabilistic music composition";
};
logbook = {
description = "file format for keeping a personal log";
};
sternenblog = {
description = "file based cgi blog software";
};
haskell-dot-time = {
description = "UTC-centric time library for haskell with dot time support";
defaultBranch = "main";
};
buchstabensuppe = {
description = "toy font rendering for low pixelcount, high contrast displays";
defaultBranch = "main";
};
};
cgitRepoEntry = name: repo:
let
repoName = repos.name or name;
path = repo.path or "${repoName}.git";
in
lib.concatStringsSep "\n" (
[
"repo.url=${repoName}"
"repo.path=/srv/git/${path}"
]
++ lib.optional (repo ? description) "repo.desc=${repo.description}"
++ lib.optional (repo ? defaultBranch) "repo.defbranch=${repo.defaultBranch}"
);
cgitHead = pkgs.writeText "cgit-head.html" ''
<style>
#summary {
max-width: 80em;
}
#summary * {
max-width: 100%;
}
</style>
'';
cgitConfig = pkgs.writeText "cgitrc" ''
virtual-root=/
enable-http-clone=1
clone-url=https://${virtualHost}/$CGIT_REPO_URL
enable-blame=1
enable-log-filecount=1
enable-log-linecount=1
enable-index-owner=0
enable-blame=1
enable-commit-graph=1
root-title=code
root-desc=sterni's git repositories
css=/cgit.css
head-include=${cgitHead}
mimetype-file=${pkgs.mime-types}/etc/mime.types
about-filter=${depot.tools.cheddar.about-filter}/bin/cheddar-about
source-filter=${depot.tools.cheddar}/bin/cheddar
readme=:README.md
readme=:readme.md
${builtins.concatStringsSep "\n\n" (lib.mapAttrsToList cgitRepoEntry repos)}
'';
in
{
imports = [
./nginx.nix
./fcgiwrap.nix
];
config = {
services.nginx.virtualHosts."${virtualHost}" = {
enableACME = true;
forceSSL = true;
root = "${pkgs.cgit-pink}/cgit/";
extraConfig = ''
try_files $uri @cgit;
location @cgit {
include ${pkgs.nginx}/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME ${pkgs.cgit-pink}/cgit/cgit.cgi;
fastcgi_param PATH_INFO $uri;
fastcgi_param QUERY_STRING $args;
fastcgi_param HTTP_HOST $server_name;
fastcgi_param CGIT_CONFIG ${cgitConfig};
fastcgi_pass unix:${toString config.services.fcgiwrap.socketAddress};
}
'';
};
};
}

View file

@ -0,0 +1,15 @@
{ ... }:
{
imports = [
./nginx.nix
];
config.services.fcgiwrap = {
enable = true;
socketType = "unix";
socketAddress = "/run/fcgiwrap.sock";
user = "http";
group = "http";
};
}

View file

@ -0,0 +1,36 @@
{ depot, lib, config, ... }:
let
inherit (depot.users.sterni.external.flipdot-gschichtler)
bahnhofshalle
warteraum
nixosModule
;
in
{
imports = [
nixosModule
./nginx.nix
];
config = {
age.secrets = lib.genAttrs [
"warteraum-salt"
"warteraum-tokens"
]
(name: {
file = depot.users.sterni.secrets."${name}.age";
});
services.flipdot-gschichtler = {
enable = true;
virtualHost = "flipdot.openlab-augsburg.de";
packages = {
inherit bahnhofshalle warteraum;
};
saltFile = config.age.secretsDir + "/warteraum-salt";
tokensFile = config.age.secretsDir + "/warteraum-tokens";
};
};
}

View file

@ -0,0 +1,23 @@
{ depot, ... }:
let
inherit (depot.users.sterni.external.likely-music)
nixosModule
likely-music
;
in
{
imports = [
./nginx.nix
nixosModule
];
config = {
services.likely-music = {
enable = true;
virtualHost = "likely-music.sterni.lv";
package = likely-music;
};
};
}

View file

@ -0,0 +1,28 @@
{ ... }:
{
config = {
users = {
users.http = {
isSystemUser = true;
group = "http";
};
groups.http = { };
};
services.nginx = {
enable = true;
recommendedTlsSettings = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
user = "http";
group = "http";
appendHttpConfig = ''
charset utf-8;
'';
};
};
}

View file

@ -0,0 +1,16 @@
{ ... }:
{
imports = [
./nginx.nix
];
config = {
services.nginx.virtualHosts."sterni.lv" = {
enableACME = true;
forceSSL = true;
# TODO(sterni): take website from store, replace /tmp with a simple LRU thing
root = toString /srv/http;
};
};
}

View file

@ -0,0 +1,118 @@
{ pkgs, depot, config, ... }:
let
carpet = pkgs.fetchurl {
url = "https://github.com/gnembon/fabric-carpet/releases/download/1.4.44/fabric-carpet-1.16.5-1.4.44+v210714.jar";
sha256 = "099nwspgxv7h2k3mwwmgcykmwfcb7yg1azb38fd4ravv97z4l3j8";
};
carpet-extra = pkgs.fetchurl {
url = "https://github.com/gnembon/carpet-extra/releases/download/1.4.43/carpet-extra-1.16.5-1.4.43.jar";
sha256 = "0w3gng1xyiqybm1qv4gbbsyqry3dr2ndvynx14qb59wxlw0dv5za";
};
userGroup = "minecraft";
makeJvmOpts = megs: [
"-Xms${toString megs}M"
"-Xmx${toString megs}M"
];
whitelist = {
spreadwasser = "242a66eb-2df2-4585-9a28-ac763ad0d0f9";
sternenseemann = "d8e48069-1905-4886-a5da-a4ee917ee254";
};
rconPasswordFile = config.age.secretsDir + "/minecraft-rcon";
baseProperties = {
white-list = true;
allow-flight = true;
difficulty = "hard";
function-permission-level = 4;
snooper-enabled = false;
view-distance = 12;
sync-chunk-writes = "false"; # the single biggest performance fix
max-tick-time = 6000000; # TODO(sterni): disable watchdog via carpet
};
in
{
imports = [
../../modules/minecraft-fabric.nix
];
config = {
environment.systemPackages = [
pkgs.mcrcon
pkgs.jre
];
users = {
users."${userGroup}" = {
isNormalUser = true;
openssh.authorizedKeys.keys = depot.users.sterni.keys.all;
shell = "${pkgs.fish}/bin/fish";
};
groups."${userGroup}" = { };
};
age.secrets = {
minecraft-rcon.file = depot.users.sterni.secrets."minecraft-rcon.age";
};
services.minecraft-fabric-server = {
creative = {
enable = true;
version = "1.16.5";
mods = [
carpet
carpet-extra
];
world = config.users.users.${userGroup}.home + "/worlds/creative";
jvmOpts = makeJvmOpts 2048;
user = userGroup;
group = userGroup;
inherit whitelist rconPasswordFile;
ops = whitelist;
serverProperties = baseProperties // {
server-port = 25566;
"rcon.port" = 25576;
gamemode = "creative";
enable-command-block = true;
motd = "storage design server";
spawn-protection = 2;
};
};
carpet = {
enable = true;
version = "1.16.5";
mods = [
carpet
carpet-extra
];
world = config.users.users.${userGroup}.home + "/worlds/carpet";
jvmOpts = makeJvmOpts 4096;
user = userGroup;
group = userGroup;
inherit whitelist rconPasswordFile;
ops = whitelist;
serverProperties = baseProperties // {
server-port = 25565;
"rcon.port" = 25575;
motd = "ich tu fleissig hustlen nenn mich bob der baumeister";
level-seed = 7240251176989694927; # for posterity
};
};
};
};
}

View file

@ -0,0 +1,62 @@
{ config, pkgs, lib, depot, ... }:
let
ipv6 = "2a01:4f8:151:54d0::/64";
ipv4 = "176.9.107.207";
gatewayv4 = "176.9.107.193";
netmaskv4 = "255.255.255.224";
in
{
config = {
boot = {
kernelParams = [
"ip=${ipv4}::${gatewayv4}:${netmaskv4}::eth0:none"
];
initrd.network = {
enable = true;
ssh = {
enable = true;
authorizedKeys = depot.users.sterni.keys.all;
hostKeys = [
"/etc/nixos/unlock_rsa_key_openssh"
"/etc/nixos/unlock_ed25519_key_openssh"
];
};
postCommands = ''
echo 'cryptsetup-askpass' >> /root/.profile
'';
};
};
networking = {
usePredictableInterfaceNames = false;
useDHCP = false;
interfaces."eth0".useDHCP = false;
hostName = "edwin";
firewall = {
enable = true;
allowPing = true;
allowedTCPPorts = [ 22 80 443 ];
};
};
systemd.network = {
enable = true;
networks."eth0".extraConfig = ''
[Match]
Name = eth0
[Network]
Address = ${ipv6}
Gateway = fe80::1
Address = ${ipv4}/27
Gateway = ${gatewayv4}
'';
};
};
}

View file

@ -0,0 +1,3 @@
{ depot, ... }:
depot.ops.secrets.mkSecrets ./. (import ./secrets.nix)

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> ssh-ed25519 aXKGcg VELHhE9AlsAUspZj8M9zzOcjaml3/KSuNAae73TOOEk
0vpPVz2TFMK2MLxHzMVO3a9QvnU9MfYcNO+JpMRRhN8
-> ssh-ed25519 34g70A 28ldud+S2mz83kcIkEGv5XWWOdXUN/vetsqho7kiCh4
/P+hJqj9r3KEi0VD15yg0MHyy0XgYUU5/zpMRrLaysM
-> .-grease }}M
ennsvHEhRup8I8R23GPWlILkCIMZmAuMT2F22SQPdjU
--- e6u1rsLXltysnQqp3x73HfHLhqzTfkIV3mXaCtW1cxE
!NiAÔûO¥¿4¯…P&÷ÔŽü ½äFFàóÛúÿRrz<72><7A> ¹Ý

View file

@ -0,0 +1,15 @@
let
nonremote = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJk+KvgvI2oJTppMASNUfMcMkA2G5ZNt+HnWDzaXKLlo"
];
edwin = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+OZ8f++cnvd4E2kFyn9jEoVpxi7LfjRvyQwzE8a5Ll"
];
in
{
"warteraum-salt.age".publicKeys = nonremote ++ edwin;
"warteraum-tokens.age".publicKeys = nonremote ++ edwin;
"minecraft-rcon.age".publicKeys = nonremote ++ edwin;
}

Binary file not shown.

View file

@ -0,0 +1,10 @@
age-encryption.org/v1
-> ssh-ed25519 aXKGcg yHE1bla5BN1Kgows1tdeswamJQHfzGpv8fL3qZs04k0
rR1O25EIQXctnyVsQCZO47bM44KFhmOZ7ePiecKrZ40
-> ssh-ed25519 34g70A voVJDU9DIrT0z6X/mAi0tQqXthRZAyrzsPXOTIIzKUw
yEiIaD9jblO44/RaoiPA0mjvRToNc4Ur9GcwfG9TSVo
-> =UOH^-Z4-grease Do<;So
l0F72v4UD8r5kbpNIT2i1IUT6ttXZhuPE91H2tucMc5TKRvGDvpdJNpQ+P+XmX2M
661iYooyust5TGZsXJFHVYg
--- To85A7ohH2Sjfy8js2+JzV0c86dmDO2JCH8TK7OtVtM
Œ¾VÃq·%n!óM#`3­°Ç;1îúßwFª¦Cç<43>ªH'îèYÄò‚å­˜]Ì-„þè½Q1Äž •«Ëm