278 lines
8.4 KiB
Nix
278 lines
8.4 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
let
|
|
relative-hostname = "forum.test";
|
|
|
|
strip-hash = filename: head (builtins.match "^[a-zA-Z0-9]+-(.+)$" filename);
|
|
|
|
clean-utf8-file = filename:
|
|
pkgs.stdenv.mkDerivation {
|
|
name = "${strip-hash (baseNameOf filename)}.utf8";
|
|
|
|
phases = [ "installPhase" ];
|
|
|
|
installPhase = "iconv -c -f utf-8 -t utf-8 -o $out ${filename}";
|
|
};
|
|
|
|
in {
|
|
options.fudo.services.selby-forum = with types; {
|
|
enable = mkEnableOption "Enable Selby Forum on this host.";
|
|
|
|
state-directory = mkOption {
|
|
type = str;
|
|
description = "Directory at which to store Selby Forum state.";
|
|
};
|
|
|
|
legacy-forum-data = mkOption {
|
|
type = path;
|
|
description = "Path to legacy Vanilla forum data.";
|
|
};
|
|
|
|
external-interface = mkOption {
|
|
type = str;
|
|
description = "Public-facing network interface on this host.";
|
|
};
|
|
|
|
mail = {
|
|
host = mkOption {
|
|
type = str;
|
|
description = "Mail server hostname.";
|
|
};
|
|
};
|
|
};
|
|
|
|
config = (let
|
|
hostname = config.instance.hostname;
|
|
|
|
cfg = config.fudo.services.selby-forum;
|
|
|
|
host-fqdn = let host-domain = config.fudo.hosts.${hostname}.domain;
|
|
in "${hostname}.${host-domain}";
|
|
|
|
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
|
|
|
parent-ip = "192.168.92.1";
|
|
child-ip = "192.168.92.2";
|
|
|
|
mkPasswd = name:
|
|
pkgs.lib.passwd.stablerandom-passwd-file "selby-forum-${name}"
|
|
"selby-forum-${name}-${config.instance.build-seed}";
|
|
|
|
mail-user = "selby-forum";
|
|
mail-password = mkPasswd "mail-password";
|
|
|
|
database-name = "forum_selby_ca";
|
|
database-user = "forum_selby_ca";
|
|
|
|
runtime-path = "/run/selby/forum";
|
|
|
|
domain-name = "selby.ca";
|
|
zone-name = config.fudo.domains.${domain-name}.zone;
|
|
|
|
forum-hostname = "${relative-hostname}.${domain-name}";
|
|
|
|
in {
|
|
networking = mkIf cfg.enable {
|
|
nat = {
|
|
enable = true;
|
|
internalInterfaces = [ "ve-selby-forum" ];
|
|
externalInterface = cfg.external-interface;
|
|
};
|
|
};
|
|
|
|
services.nginx = mkIf cfg.enable {
|
|
enable = true;
|
|
recommendedOptimisation = true;
|
|
recommendedProxySettings = true;
|
|
|
|
virtualHosts.${forum-hostname} = {
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
locations."/".proxyPass = "http://${child-ip}:80";
|
|
};
|
|
};
|
|
|
|
fudo = {
|
|
# Email user
|
|
system-users.${mail-user} = {
|
|
description = "Selby Forum";
|
|
ldap-hashed-password =
|
|
pkgs.lib.passwd.hash-ldap-passwd "selby-forum-mail-passwd"
|
|
mail-password;
|
|
};
|
|
|
|
zones.${zone-name}.aliases = { ${relative-hostname} = "${host-fqdn}."; };
|
|
|
|
secrets.host-secrets.${hostname} = mkIf cfg.enable
|
|
(let db-passwd = mkPasswd "database-password";
|
|
in {
|
|
selby-forum-database-password = {
|
|
source-file = db-passwd;
|
|
target-file = "${runtime-path}/db.passwd";
|
|
};
|
|
postgres-selby-forum-password = {
|
|
source-file = db-passwd;
|
|
target-file = "/run/postgres-users/selby-forum.passwd";
|
|
user = config.services.postgresql.superUser;
|
|
};
|
|
selby-forum-admin-passwd = {
|
|
source-file = mkPasswd "admin-password";
|
|
target-file = "${runtime-path}/admin.passwd";
|
|
};
|
|
selby-forum-mail-passwd = {
|
|
source-file = mail-password;
|
|
target-file = "${runtime-path}/mail.passwd";
|
|
};
|
|
legacy-selby-forum-data = {
|
|
source-file = "${clean-utf8-file cfg.legacy-forum-data}";
|
|
target-file = "${runtime-path}/selby-forum-data.sql";
|
|
};
|
|
});
|
|
|
|
postgresql = mkIf cfg.enable {
|
|
local-networks = [ "192.168.92.0/30" ];
|
|
databases.${database-name}.users = config.instance.local-admins;
|
|
users.${database-user} = {
|
|
password-file =
|
|
host-secrets.postgres-selby-forum-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";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
systemd = mkIf cfg.enable {
|
|
tmpfiles.rules = [ "d ${cfg.state-directory} 750 - - - -" ];
|
|
|
|
services.discourse-prepare-selby-forum = {
|
|
description =
|
|
"Perform superuser tasks for Discourse, which doesn't have SU perms.";
|
|
wantedBy = [ "container@selby-forum.service" ];
|
|
before = [ "container@selby-forum.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-selby-forum.sh" ''
|
|
psql -d ${database-name} -c "CREATE EXTENSION IF NOT EXISTS hstore;"
|
|
psql -d ${database-name} -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
containers.selby-forum = mkIf cfg.enable {
|
|
ephemeral = true;
|
|
privateNetwork = true;
|
|
hostAddress = parent-ip;
|
|
localAddress = child-ip;
|
|
autoStart = true;
|
|
|
|
bindMounts = {
|
|
${runtime-path} = {
|
|
hostPath = runtime-path;
|
|
isReadOnly = true;
|
|
};
|
|
"/var/lib/discourse" = {
|
|
hostPath = cfg.state-directory;
|
|
isReadOnly = false;
|
|
};
|
|
};
|
|
|
|
config = { config, lib, ... }:
|
|
let
|
|
discourse-user = config.systemd.services.discourse.serviceConfig.User;
|
|
in {
|
|
networking = {
|
|
defaultGateway = parent-ip;
|
|
firewall.enable = false;
|
|
};
|
|
|
|
services.discourse = {
|
|
enable = true;
|
|
hostname = forum-hostname;
|
|
enableACME = false;
|
|
admin = {
|
|
username = "admin";
|
|
fullName = "Admin";
|
|
email = "admin@selby.ca";
|
|
passwordFile = "/etc/selby-forum/admin.passwd";
|
|
};
|
|
database = {
|
|
name = database-name;
|
|
host = parent-ip;
|
|
username = database-user;
|
|
passwordFile = "/etc/selby-forum/db.passwd";
|
|
};
|
|
mail.outgoing = {
|
|
username = mail-user;
|
|
passwordFile = "/etc/selby-forum/mail.passwd";
|
|
domain = domain-name;
|
|
forceTLS = true;
|
|
serverAddress = cfg.mail.host;
|
|
};
|
|
};
|
|
|
|
environment.etc = {
|
|
"selby-forum/admin.passwd" = {
|
|
source = "/run/selby/forum/admin.passwd";
|
|
user = discourse-user;
|
|
mode = "0400";
|
|
};
|
|
"selby-forum/db.passwd" = {
|
|
source = "/run/selby/forum/db.passwd";
|
|
user = discourse-user;
|
|
mode = "0400";
|
|
};
|
|
"selby-forum/selby-forum-data.sql" = {
|
|
source = "/run/selby/forum/selby-forum-data.sql";
|
|
user = discourse-user;
|
|
mode = "0400";
|
|
};
|
|
"selby-forum/mail.passwd" = {
|
|
source = "/run/selby/forum/mail.passwd";
|
|
user = discourse-user;
|
|
mode = "0400";
|
|
};
|
|
};
|
|
|
|
systemd = {
|
|
tmpfiles.rules =
|
|
[ "d /var/lib/discourse 700 ${discourse-user} - - -" ];
|
|
|
|
services = {
|
|
discourse = { after = [ "multi-user.target" ]; };
|
|
|
|
discourse-import-selby-forum = let
|
|
env-without-path = filterAttrs (attr: _: attr != "PATH")
|
|
config.systemd.services.discourse.environment;
|
|
in {
|
|
description = "One-off job to import Vanilla Selby Forum data.";
|
|
path = config.systemd.services.discourse.path;
|
|
environment = env-without-path;
|
|
serviceConfig = {
|
|
User = discourse-user;
|
|
type = "oneshot";
|
|
WorkingDirectory =
|
|
config.systemd.services.discourse.serviceConfig.WorkingDirectory;
|
|
ExecStart = pkgs.writeShellScript
|
|
"import-vanilla-selby-forum-data.sh" ''
|
|
ruby script/import_scripts/vanilla.rb /etc/selby-forum/selby-forum-data.sql
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
});
|
|
}
|