{ config, lib, pkgs, ... }: with lib; let site = "forum.test.selby.ca"; hostname = config.instance.hostname; host-secrets = config.fudo.secrets.host-secrets.${hostname}; database-name = "forum_selby_ca"; database-user = "forum_selby_ca"; state-directory = "/state/selby/forum"; password-injector-sql = csv-file: pkgs.stdenv.mkDerivation { name = "${site}-password-injector-sql"; phases = [ "installPhase" ]; buildInputs = [ pkgs.ruby ]; installPhase = '' ${password-convert-script csv-file} ''; }; password-convert-script = csv-file: pkgs.writeScript "vanilla-forum-password-convert.rb" '' #!${pkgs.ruby}/bin/ruby require 'csv' data = CSV::readlines("${csv-file}") File::open(ENV["out"], "w") { |sql| data.each { |row| sql.puts("UPDATE users SET import_pass='#{row[2]}' FROM user_emails WHERE users.id = user_emails.user_id AND user_emails.email = '#{row[1]}';") } } ''; head-pkgs = let nixos = pkgs.fetchgit { url = "https://github.com/nixos/nixpkgs.git"; rev = "21.11"; sha256 = "162dywda2dvfj1248afxc45kcrg83appjd0nmdb541hl7rnncf02"; }; in import "${nixos}" { system = pkgs.system; config = pkgs.config; overlays = pkgs.overlays; }; in { config = let admin-pw-file = "admin.passwd"; db-pw-file = "database.passwd"; data-file = "data-forum_selby_ca.txt"; in { fudo = { secrets.host-secrets.${hostname} = let selby-discourse-db-password = pkgs.lib.passwd.stablerandom-passwd-file "selby-discourse-database-password" "selby-discourse-database-password-${config.instance.build-seed}"; files = config.fudo.secrets.files; in { selby-discourse-database-passwd = { source-file = selby-discourse-db-password; target-file = "/run/selby/forum/${db-pw-file}"; user = "root"; }; postgresql-selby-discourse-password = { source-file = selby-discourse-db-password; target-file = "/run/postgres/selby-discourse.passwd"; user = config.services.postgresql.superUser; }; selby-discourse-admin = { source-file = pkgs.lib.passwd.stablerandom-passwd-file "selby-discourse-admin" "selby-discourse-admin-${config.instance.build-seed}"; target-file = "/run/selby/forum/${admin-pw-file}"; user = "root"; }; selby-forum-data = { source-file = files.blobs."selby-forum-2021-12-14.clean"; target-file = "/run/selby/forum/${data-file}"; user = "root"; }; selby-forum-passwords-sql = { source-file = "${password-injector-sql files.blobs."forum_selby_ca-passwd.csv"}"; target-file = "/run/postgres/selby/forum-passwords.sql"; user = config.services.postgresql.superUser; }; }; postgresql = { databases.${database-name}.users = [ "niten" ]; users.${database-user} = { password-file = host-secrets.postgresql-selby-discourse-password.target-file; databases.${database-name} = { access = "CONNECT,CREATE"; entity-access = { "ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE"; "ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE"; }; }; }; }; }; ## Fuckin what why won't this cert work? services.nginx = { enable = true; recommendedOptimisation = true; recommendedGzipSettings = true; recommendedProxySettings = true; virtualHosts = { "${site}" = { enableACME = true; forceSSL = true; locations."/".proxyPass = "http://192.168.55.2:80"; }; }; }; containers.selby-forum = let parent-host = "192.168.55.1"; container-host = "192.168.55.2"; in { ephemeral = true; privateNetwork = true; localAddress = container-host; hostAddress = parent-host; autoStart = true; bindMounts = { "/run/selby-discourse" = { hostPath = "/run/selby/forum"; isReadOnly = true; }; "/var/lib/discourse" = { hostPath = state-directory; isReadOnly = false; }; }; config = { config, lib, ... }: let discourse-user = config.systemd.services.discourse.serviceConfig.User; in { nixpkgs.pkgs = head-pkgs; environment.systemPackages = with pkgs; [ nmap ]; networking.firewall.enable = false; services.discourse = { enable = true; hostname = site; enableACME = false; plugins = with config.services.discourse.package.plugins; [ discourse-migratepassword ]; admin = { username = "admin"; fullName = "Admin"; email = "admin@selby.ca"; passwordFile = "/etc/selby-discourse/${admin-pw-file}"; }; database = { name = database-name; host = parent-host; username = database-user; passwordFile = "/etc/selby-discourse/${db-pw-file}"; }; }; environment.etc = { "selby-discourse/${admin-pw-file}" = { source = "/run/selby-discourse/${admin-pw-file}"; user = discourse-user; mode = "0400"; }; "selby-discourse/${db-pw-file}" = { source = "/run/selby-discourse/${db-pw-file}"; user = discourse-user; mode = "0400"; }; "selby-discourse/${data-file}" = { source = "/run/selby-discourse/${data-file}"; user = discourse-user; mode = "0400"; }; }; systemd = { tmpfiles.rules = [ "/run/discourse 750 ${discourse-user} ${discourse-user} - -" "/var/lib/discourse 750 ${discourse-user} ${discourse-user} - -" ]; services = { discourse = { after = [ "multi-user.target" ]; }; discourse-import-vanilla = let env-without-path = filterAttrs (attr: _: attr != "PATH") config.systemd.services.discourse.environment; selby-forum-data = container-selby-forum-data-file; in { description = "One-off job to import Vanilla forum."; path = config.systemd.services.discourse.path; environment = env-without-path; serviceConfig = { User = config.systemd.services.discourse.serviceConfig.User; Group = config.systemd.services.discourse.serviceConfig.Group; Type = "oneshot"; WorkingDirectory = config.systemd.services.discourse.serviceConfig.WorkingDirectory; ExecStart = pkgs.writeShellScript "import-vanilla-forum.sh" '' ruby script/import_scripts/vanilla.rb /etc/selby-discourse/${data-file} ''; }; }; }; }; }; }; systemd = { tmpfiles.rules = [ "d ${state-directory} 750 - - - -" ]; services = { discourse-prepare = { description = "Do discourse's superuser-requiring database work for it."; wantedBy = [ "container@forum-selby-ca.service" ]; before = [ "container@forum-selby-ca.service" ]; requires = [ config.fudo.postgresql.systemd-target ]; after = [ config.fudo.postgresql.systemd-target ]; path = with pkgs; [ postgresql ]; serviceConfig = { User = config.services.postgresql.superUser; ExecStart = pkgs.writeShellScript "discourse-prepare.sh" '' psql -d ${database-name} -c "CREATE EXTENSION IF NOT EXISTS hstore;" psql -d ${database-name} -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" ''; }; }; discourse-add-password-hash = let alter-user-script = pkgs.writeText "create-password-column.sql" '' ALTER TABLE users ADD COLUMN IF NOT EXISTS import_pass VARCHAR (128); ''; in { description = "One-off job to add user password hashes from Vanilla forum."; path = with pkgs; [ postgresql ]; serviceConfig = { User = config.services.postgresql.superUser; Type = "oneshot"; ExecStart = pkgs.writeShellScript "import-vanilla-passwords.sh" '' psql -d ${database-name} -f ${alter-user-script} psql -d ${database-name} -f ${host-secrets.selby-forum-passwords-sql.target-file} ''; }; }; }; }; }; }