318 lines
10 KiB
Nix
318 lines
10 KiB
Nix
{
|
|
description = "Basic MediaWiki primary + replica deployment";
|
|
|
|
nixConfig = {
|
|
max-jobs = "auto";
|
|
cores = 0;
|
|
};
|
|
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
agenix = {
|
|
url = "github:ryantm/agenix";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
deploy-rs = {
|
|
url = "github:serokell/deploy-rs";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
disko = {
|
|
url = "github:nix-community/disko";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
nixos-anywhere = {
|
|
url = "github:nix-community/nixos-anywhere";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
};
|
|
|
|
outputs =
|
|
{
|
|
self,
|
|
nixpkgs,
|
|
agenix,
|
|
deploy-rs,
|
|
disko,
|
|
nixos-anywhere,
|
|
...
|
|
}:
|
|
let
|
|
system = "x86_64-linux";
|
|
pkgs = nixpkgs.legacyPackages.${system};
|
|
lib = nixpkgs.lib;
|
|
|
|
siteConfig = rec {
|
|
wikiName = "Noisebridge";
|
|
baseDomain = "noisebridge.net";
|
|
replicaSubdomain = "replica";
|
|
sshUser = "root";
|
|
primaryHostName = "main-wiki";
|
|
replicaHostName = "replica-wiki";
|
|
|
|
database = {
|
|
name = "noisebridge_mediawiki";
|
|
mediawikiUser = "wiki";
|
|
replicationUser = "repl";
|
|
};
|
|
|
|
hosts = {
|
|
primary = {
|
|
nixosName = primaryHostName;
|
|
tailscaleName = primaryHostName;
|
|
};
|
|
replica = {
|
|
nixosName = replicaHostName;
|
|
tailscaleName = replicaHostName;
|
|
};
|
|
};
|
|
};
|
|
|
|
mkPublicDomain =
|
|
role:
|
|
if role == "primary" then
|
|
siteConfig.baseDomain
|
|
else
|
|
"${siteConfig.replicaSubdomain}.${siteConfig.baseDomain}";
|
|
|
|
mkHostMeta =
|
|
role:
|
|
siteConfig.hosts.${role}
|
|
// {
|
|
inherit role;
|
|
publicDomain = mkPublicDomain role;
|
|
};
|
|
|
|
mkHost =
|
|
{
|
|
hostMeta,
|
|
hostModule,
|
|
roleModules,
|
|
}:
|
|
lib.nixosSystem {
|
|
inherit system;
|
|
specialArgs = {
|
|
inherit agenix siteConfig hostMeta;
|
|
};
|
|
modules = [
|
|
agenix.nixosModules.default
|
|
disko.nixosModules.disko
|
|
hostModule
|
|
./disk-config.nix
|
|
./modules/common.nix
|
|
./modules/security.nix
|
|
./modules/tailscale.nix
|
|
./modules/caddy.nix
|
|
./modules/mediawiki-base.nix
|
|
]
|
|
++ roleModules;
|
|
};
|
|
|
|
primaryMeta = mkHostMeta "primary";
|
|
replicaMeta = mkHostMeta "replica";
|
|
in
|
|
{
|
|
nixosConfigurations = {
|
|
main-wiki = mkHost {
|
|
hostMeta = primaryMeta;
|
|
hostModule = ./hosts/main-wiki;
|
|
roleModules = [
|
|
./modules/wiki-primary/mysql.nix
|
|
./modules/wiki-primary/mediawiki.nix
|
|
];
|
|
};
|
|
|
|
replica-wiki = mkHost {
|
|
hostMeta = replicaMeta;
|
|
hostModule = ./hosts/replica-wiki;
|
|
roleModules = [
|
|
./modules/wiki-replica/mysql.nix
|
|
./modules/wiki-replica/mediawiki.nix
|
|
];
|
|
};
|
|
};
|
|
|
|
deploy.nodes = {
|
|
main-wiki = {
|
|
hostname = primaryMeta.tailscaleName;
|
|
profiles.system = {
|
|
user = siteConfig.sshUser;
|
|
sshUser = siteConfig.sshUser;
|
|
path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.main-wiki;
|
|
};
|
|
};
|
|
|
|
replica-wiki = {
|
|
hostname = replicaMeta.tailscaleName;
|
|
profiles.system = {
|
|
user = siteConfig.sshUser;
|
|
sshUser = siteConfig.sshUser;
|
|
path = deploy-rs.lib.${system}.activate.nixos self.nixosConfigurations.replica-wiki;
|
|
};
|
|
};
|
|
};
|
|
|
|
checks = builtins.mapAttrs (_: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib;
|
|
|
|
apps.${system} = {
|
|
deploy = {
|
|
type = "app";
|
|
program = "${pkgs.writeShellScript "deploy-noisebridge" ''
|
|
if [ "$#" -eq 0 ] || [ "''${1#-}" != "$1" ]; then
|
|
exec ${deploy-rs.packages.${system}.default}/bin/deploy \
|
|
--auto-rollback true \
|
|
--magic-rollback true \
|
|
path:.# \
|
|
"$@"
|
|
fi
|
|
|
|
exec ${deploy-rs.packages.${system}.default}/bin/deploy \
|
|
--auto-rollback true \
|
|
--magic-rollback true \
|
|
"$@"
|
|
''}";
|
|
meta.description = "Deploy all Noisebridge wiki hosts by default";
|
|
};
|
|
|
|
bootstrap-host = {
|
|
type = "app";
|
|
program = "${pkgs.writeShellScript "bootstrap-host" ''
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
nix run .#bootstrap-host -- <main-wiki|replica-wiki> <target-host> [ssh-identity-file]
|
|
nix run .#bootstrap-host -- <main-target-host> <replica-target-host> [ssh-identity-file]
|
|
|
|
Examples:
|
|
nix run .#bootstrap-host -- main-wiki root@203.0.113.10 ~/.ssh/do-bootstrap
|
|
nix run .#bootstrap-host -- root@203.0.113.10 root@203.0.113.11 ~/.ssh/do-bootstrap
|
|
EOF
|
|
}
|
|
|
|
if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
repo_root="$(pwd)"
|
|
if [ ! -f "$repo_root/flake.nix" ]; then
|
|
printf 'Run bootstrap-host from the repo root\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
ssh_identity_file=""
|
|
main_target=""
|
|
replica_target=""
|
|
selected_host=""
|
|
|
|
case "$1" in
|
|
main-wiki|replica-wiki)
|
|
selected_host="$1"
|
|
if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
if [ "$selected_host" = "main-wiki" ]; then
|
|
main_target="$2"
|
|
else
|
|
replica_target="$2"
|
|
fi
|
|
ssh_identity_file="''${3:-}"
|
|
;;
|
|
*)
|
|
main_target="$1"
|
|
replica_target="$2"
|
|
ssh_identity_file="''${3:-}"
|
|
;;
|
|
esac
|
|
|
|
install_root="$(mktemp -d)"
|
|
cleanup() {
|
|
rm -rf "$install_root"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
ensure_host_key() {
|
|
local host_name="$1"
|
|
local private_key_file="$repo_root/.bootstrap/$host_name/host.age"
|
|
local public_key_file="$repo_root/secrets/hosts/$host_name.age.pub"
|
|
local public_key
|
|
|
|
mkdir -p "$(dirname "$private_key_file")" "$(dirname "$public_key_file")"
|
|
|
|
if [ ! -s "$private_key_file" ]; then
|
|
public_key="$(${pkgs.age}/bin/age-keygen -o "$private_key_file" | sed 's/^Public key: //')"
|
|
chmod 600 "$private_key_file"
|
|
printf 'Generated host age key for %s\n' "$host_name"
|
|
else
|
|
public_key="$(${pkgs.age}/bin/age-keygen -y "$private_key_file")"
|
|
printf 'Reused existing host age key for %s\n' "$host_name"
|
|
fi
|
|
|
|
printf '%s\n' "$public_key" > "$public_key_file"
|
|
}
|
|
|
|
run_bootstrap() {
|
|
local host_name="$1"
|
|
local target_host="$2"
|
|
local private_key_file="$repo_root/.bootstrap/$host_name/host.age"
|
|
local -a nixos_anywhere_args
|
|
|
|
mkdir -p "$install_root/var/lib/agenix"
|
|
${pkgs.coreutils}/bin/install -m 0400 "$private_key_file" "$install_root/var/lib/agenix/host.age"
|
|
|
|
nixos_anywhere_args=(
|
|
--extra-files "$install_root"
|
|
--flake "path:$repo_root#$host_name"
|
|
)
|
|
|
|
if [ -n "$ssh_identity_file" ]; then
|
|
nixos_anywhere_args+=( -i "$ssh_identity_file" )
|
|
fi
|
|
|
|
printf 'Bootstrapping %s onto %s\n' "$host_name" "$target_host"
|
|
${
|
|
nixos-anywhere.packages.${system}.default
|
|
}/bin/nixos-anywhere "''${nixos_anywhere_args[@]}" "$target_host"
|
|
rm -f "$install_root/var/lib/agenix/host.age"
|
|
}
|
|
|
|
if [ -n "$main_target" ]; then
|
|
ensure_host_key main-wiki
|
|
fi
|
|
if [ -n "$replica_target" ]; then
|
|
ensure_host_key replica-wiki
|
|
fi
|
|
|
|
printf 'Rekeying agenix secrets\n'
|
|
${agenix.packages.${system}.default}/bin/agenix -r
|
|
|
|
if [ -n "$main_target" ]; then
|
|
run_bootstrap main-wiki "$main_target"
|
|
fi
|
|
if [ -n "$replica_target" ]; then
|
|
run_bootstrap replica-wiki "$replica_target"
|
|
fi
|
|
|
|
printf '\nBootstrap complete. The hosts should now join Tailscale using their configured hostnames.\n'
|
|
''}";
|
|
meta.description = "Install NixOS on one or both raw hosts and seed agenix identities";
|
|
};
|
|
};
|
|
|
|
devShells.${system}.default = pkgs.mkShell {
|
|
packages = with pkgs; [
|
|
agenix.packages.${system}.default
|
|
deploy-rs.packages.${system}.default
|
|
nixos-anywhere.packages.${system}.default
|
|
mariadb.client
|
|
rsync
|
|
curl
|
|
jq
|
|
age
|
|
openssl
|
|
];
|
|
};
|
|
};
|
|
}
|