237 lines
5.8 KiB
Nix
237 lines
5.8 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
let
|
|
hostname = config.instance.hostname;
|
|
|
|
siteOpts = { ... }: with types; {
|
|
options = {
|
|
enableACME = mkOption {
|
|
type = bool;
|
|
description = "Use ACME to get SSL certificates for this site.";
|
|
default = true;
|
|
};
|
|
|
|
site-config = mkOption {
|
|
type = attrs;
|
|
description = "Site-specific configuration.";
|
|
};
|
|
};
|
|
};
|
|
|
|
concatMapAttrs = f: attrs:
|
|
foldr (a: b: a // b) {} (mapAttrs f attrs);
|
|
|
|
concatMapAttrsToList = f: attr:
|
|
concatMap (i: i) (attrValues (mapAttrs f attr));
|
|
|
|
host-domains = config.fudo.acme.host-domains.${hostname};
|
|
|
|
siteCerts = site: let
|
|
cert-copy = host-domains.${site}.local-copies.ejabberd;
|
|
in [
|
|
cert-copy.certificate
|
|
cert-copy.private-key
|
|
cert-copy.chain
|
|
];
|
|
|
|
siteCertService = site:
|
|
host-domains.${site}.local-copies.ejabberd.service;
|
|
|
|
config-file-template = let
|
|
jabber-config = {
|
|
loglevel = cfg.log-level;
|
|
|
|
access_rules = {
|
|
c2s = { allow = "all"; };
|
|
announce = { allow = "admin"; };
|
|
configure = { allow = "admin"; };
|
|
pubsub_createnode = { allow = "local"; };
|
|
};
|
|
|
|
acl = {
|
|
admin = {
|
|
user = concatMap
|
|
(admin: map (site: "${admin}@${site}")
|
|
(attrNames cfg.sites))
|
|
cfg.admins;
|
|
};
|
|
};
|
|
|
|
hosts = attrNames cfg.sites;
|
|
|
|
listen = map (ip: {
|
|
port = cfg.port;
|
|
module = "ejabberd_c2s";
|
|
ip = ip;
|
|
starttls = true;
|
|
starttls_required = true;
|
|
}) cfg.listen-ips;
|
|
|
|
certfiles = concatMapAttrsToList
|
|
(site: siteOpts:
|
|
if (siteOpts.enableACME) then
|
|
(siteCerts site)
|
|
else [])
|
|
cfg.sites;
|
|
|
|
host_config =
|
|
mapAttrs (site: siteOpts: siteOpts.site-config)
|
|
cfg.sites;
|
|
};
|
|
|
|
config-file = builtins.toJSON jabber-config;
|
|
in pkgs.writeText "ejabberd.config.yml.template" config-file;
|
|
|
|
enter-secrets = template: secrets: target: let
|
|
secret-readers = concatStringsSep "\n"
|
|
(mapAttrsToList
|
|
(secret: file: "${secret}=$(cat ${file})")
|
|
secrets);
|
|
secret-swappers = map
|
|
(secret: "sed s/${secret}/\$${secret}/g")
|
|
(attrNames secrets);
|
|
swapper = concatStringsSep " | " secret-swappers;
|
|
in pkgs.writeShellScript "ejabberd-generate-config.sh" ''
|
|
cat ${template} | ${swapper} > ${target}
|
|
'';
|
|
|
|
cfg = config.fudo.jabber;
|
|
|
|
in {
|
|
options.fudo.jabber = with types; {
|
|
enable = mkEnableOption "Enable ejabberd server.";
|
|
|
|
listen-ips = mkOption {
|
|
type = listOf str;
|
|
description = "IPs on which to listen for Jabber connections.";
|
|
};
|
|
|
|
port = mkOption {
|
|
type = port;
|
|
description = "Port on which to listen for Jabber connections.";
|
|
default = 5222;
|
|
};
|
|
|
|
user = mkOption {
|
|
type = str;
|
|
description = "User as which to run the ejabberd server.";
|
|
default = "ejabberd";
|
|
};
|
|
|
|
group = mkOption {
|
|
type = str;
|
|
description = "Group as which to run the ejabberd server.";
|
|
default = "ejabberd";
|
|
};
|
|
|
|
admins = mkOption {
|
|
type = listOf str;
|
|
description = "List of admin users for the server.";
|
|
default = [];
|
|
};
|
|
|
|
sites = mkOption {
|
|
type = attrsOf (submodule siteOpts);
|
|
description = "List of sites on which to listen for Jabber connections.";
|
|
};
|
|
|
|
secret-files = mkOption {
|
|
type = attrsOf str;
|
|
description = "Map of secret-name to file. File contents will be subbed for the name in the config.";
|
|
default = {};
|
|
};
|
|
|
|
config-file = mkOption {
|
|
type = str;
|
|
description = "Location at which to generate the configuration file.";
|
|
default = "/run/ejabberd/ejabberd.yaml";
|
|
};
|
|
|
|
log-level = mkOption {
|
|
type = int;
|
|
description = ''
|
|
Log level at which to run the server.
|
|
|
|
See: https://docs.ejabberd.im/admin/guide/troubleshooting/
|
|
'';
|
|
default = 3;
|
|
};
|
|
|
|
environment = mkOption {
|
|
type = attrsOf str;
|
|
description = "Environment variables to set for the ejabberd daemon.";
|
|
default = {};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
users = {
|
|
users.${cfg.user} = {
|
|
isSystemUser = true;
|
|
};
|
|
|
|
groups.${cfg.group} = {
|
|
members = [ cfg.user ];
|
|
};
|
|
};
|
|
|
|
fudo = {
|
|
acme.host-domains.${hostname} = mapAttrs (site: siteCfg:
|
|
mkIf siteCfg.enableACME {
|
|
local-copies.ejabberd = {
|
|
user = cfg.user;
|
|
group = cfg.group;
|
|
};
|
|
}) cfg.sites;
|
|
|
|
system = let
|
|
config-dir = dirOf cfg.config-file;
|
|
in {
|
|
ensure-directories.${config-dir} = {
|
|
user = cfg.user;
|
|
perms = "0700";
|
|
};
|
|
|
|
services.ejabberd-config-generator = let
|
|
config-generator =
|
|
enter-secrets config-file-template cfg.secret-files cfg.config-file;
|
|
in {
|
|
script = "${config-generator}";
|
|
readWritePaths = [ config-dir ];
|
|
workingDirectory = config-dir;
|
|
user = cfg.user;
|
|
description = "Generate ejabberd config file with necessary passwords.";
|
|
postStart = ''
|
|
chown ${cfg.user} ${cfg.config-file}
|
|
chmod 0400 ${cfg.config-file}
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
systemd = {
|
|
tmpfiles.rules = [
|
|
"D '${dirOf cfg.config-file}' 0550 ${cfg.user} ${cfg.group} - -"
|
|
];
|
|
|
|
services = {
|
|
ejabberd = {
|
|
wants = map (site: siteCertService site) (attrNames cfg.sites);
|
|
requires = [ "ejabberd-config-generator.service" ];
|
|
environment = cfg.environment;
|
|
};
|
|
};
|
|
};
|
|
|
|
services.ejabberd = {
|
|
enable = true;
|
|
|
|
user = cfg.user;
|
|
group = cfg.group;
|
|
|
|
configFile = cfg.config-file;
|
|
};
|
|
};
|
|
}
|