Got ldap on nutboy3 and jabber on legatus
This commit is contained in:
parent
c87448ff13
commit
541890c08f
@ -111,11 +111,18 @@ in {
|
||||
description = "Map of host to domains to domain options.";
|
||||
default = { };
|
||||
};
|
||||
|
||||
challenge-path = mkOption {
|
||||
type = str;
|
||||
description = "Web-accessible path for responding to ACME challenges.";
|
||||
default = "/run/fudo-acme/challenge";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
security.acme.certs = mapAttrs (domain: domainOpts: {
|
||||
email = domainOpts.admin-email;
|
||||
webroot = cfg.challenge-path;
|
||||
extraDomainNames = domainOpts.extra-domains;
|
||||
}) localDomains;
|
||||
|
||||
@ -130,12 +137,14 @@ in {
|
||||
recommendedProxySettings = true;
|
||||
|
||||
virtualHosts.${config.instance.host-fqdn} = {
|
||||
enableACME = true;
|
||||
forceSSL = true;
|
||||
|
||||
# Just...force override if you want this to point somewhere.
|
||||
locations."/" = {
|
||||
return = "403 Forbidden";
|
||||
serverAliases = attrNames localDomains;
|
||||
locations = {
|
||||
"/.well-known/acme-challenge" = {
|
||||
root = cfg.challenge-path;
|
||||
};
|
||||
"/" = {
|
||||
return = "301 https://$host$request_uri";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -156,7 +165,9 @@ in {
|
||||
copyOpts.chain
|
||||
copyOpts.private-key
|
||||
]) copies;
|
||||
in unique (concatMap (i: unique i) copy-paths);
|
||||
in (unique (concatMap (i: unique i) copy-paths)) ++ [
|
||||
"d \"${cfg.challenge-path}\" 755 acme nginx - -"
|
||||
];
|
||||
|
||||
services = concatMapAttrs (domain: domainOpts:
|
||||
concatMapAttrs (copy: copyOpts: let
|
||||
|
@ -55,6 +55,8 @@ let
|
||||
in {
|
||||
options.fudo.backplane = with types; {
|
||||
|
||||
enable = mkEnableOption "Enable backplane (jabber) server on this host.";
|
||||
|
||||
client-hosts = mkOption {
|
||||
type = attrsOf (submodule clientHostOpts);
|
||||
description = "List of backplane client options.";
|
||||
@ -67,7 +69,7 @@ in {
|
||||
default = {};
|
||||
};
|
||||
|
||||
backplane-host = mkOption {
|
||||
backplane-hostname = mkOption {
|
||||
type = types.str;
|
||||
description = "Hostname of the backplane XMPP server.";
|
||||
};
|
||||
|
@ -86,7 +86,7 @@ in {
|
||||
partOf = [ "backplane-dns.target" ];
|
||||
requires = cfg.required-services ++ [ "postgresql.service" ];
|
||||
environment = {
|
||||
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = backplane-cfg.backplane-host;
|
||||
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = backplane-cfg.backplane-hostname;
|
||||
FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane-role.role;
|
||||
FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane-role.password-file;
|
||||
FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.database.host;
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
with lib;
|
||||
{
|
||||
config = mkIf config.fudo.jabber.enable {
|
||||
config = mkIf config.fudo.backplane.enable {
|
||||
fudo = let
|
||||
cfg = config.fudo.backplane;
|
||||
|
||||
hostname = config.instance.hostname;
|
||||
|
||||
backplane-server = cfg.backplane-host;
|
||||
backplane-server = cfg.backplane-hostname;
|
||||
|
||||
generate-auth-file = name: files: let
|
||||
make-entry = name: passwd-file:
|
||||
@ -40,6 +40,8 @@ with lib;
|
||||
};
|
||||
|
||||
jabber = {
|
||||
enable = true;
|
||||
|
||||
environment = {
|
||||
FUDO_HOST_PASSWD_FILE =
|
||||
secrets.backplane-host-auth.target-file;
|
||||
|
@ -21,6 +21,18 @@ in {
|
||||
example = "My Fancy Chat Site";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = str;
|
||||
description = "System user as which to run the server.";
|
||||
default = "mattermost";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = str;
|
||||
description = "System group as which to run the server.";
|
||||
default = "mattermost";
|
||||
};
|
||||
|
||||
smtp = {
|
||||
server = mkOption {
|
||||
type = str;
|
||||
@ -111,8 +123,6 @@ in {
|
||||
};
|
||||
mattermost-config-file-template =
|
||||
pkgs.writeText "mattermost-config.json.template" (builtins.toJSON modified-config);
|
||||
mattermost-user = "mattermost";
|
||||
mattermost-group = "mattermost";
|
||||
|
||||
generate-mattermost-config = target: template: smtp-passwd-file: db-passwd-file:
|
||||
pkgs.writeScript "mattermost-config-generator.sh" ''
|
||||
@ -124,13 +134,12 @@ in {
|
||||
in {
|
||||
users = {
|
||||
users = {
|
||||
${mattermost-user} = {
|
||||
${cfg.user} = {
|
||||
isSystemUser = true;
|
||||
group = mattermost-group;
|
||||
};
|
||||
};
|
||||
|
||||
groups = { ${mattermost-group} = { members = [ mattermost-user ]; }; };
|
||||
groups.${cfg.group}.members = [ cfg.user ];
|
||||
};
|
||||
|
||||
fudo.system.services.mattermost = {
|
||||
@ -146,54 +155,25 @@ in {
|
||||
cfg.database.password-file}
|
||||
cp ${cfg.smtp.password-file} ${cfg.state-directory}/config/config.json
|
||||
cp -uRL ${pkg}/client ${cfg.state-directory}
|
||||
chown ${mattermost-user}:${mattermost-group} ${cfg.state-directory}/client
|
||||
chown ${cfg.user}:${cfg.group} ${cfg.state-directory}/client
|
||||
chmod 0750 ${cfg.state-directory}/client
|
||||
'';
|
||||
execStart = "${pkg}/bin/mattermost";
|
||||
workingDirectory = cfg.state-directory;
|
||||
user = mattermost-user;
|
||||
group = mattermost-group;
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
};
|
||||
|
||||
systemd = {
|
||||
|
||||
tmpfiles.rules = [
|
||||
"d ${cfg.state-directory} 0750 ${mattermost-user} ${mattermost-group} - -"
|
||||
"d ${cfg.state-directory}/config 0750 ${mattermost-user} ${mattermost-group} - -"
|
||||
"d ${cfg.state-directory} 0750 ${cfg.user} ${cfg.group} - -"
|
||||
"d ${cfg.state-directory}/config 0750 ${cfg.user} ${cfg.group} - -"
|
||||
"L ${cfg.state-directory}/bin - - - - ${pkg}/bin"
|
||||
"L ${cfg.state-directory}/fonts - - - - ${pkg}/fonts"
|
||||
"L ${cfg.state-directory}/i18n - - - - ${pkg}/i18n"
|
||||
"L ${cfg.state-directory}/templates - - - - ${pkg}/templates"
|
||||
];
|
||||
|
||||
# services.mattermost = {
|
||||
# description = "Mattermost Chat Server";
|
||||
# wantedBy = [ "multi-user.target" ];
|
||||
# after = [ "network.target" ];
|
||||
|
||||
# preStart = ''
|
||||
# ${generate-mattermost-config
|
||||
# mattermost-config-target
|
||||
# mattermost-config-file-template
|
||||
# cfg.smtp.password-file
|
||||
# cfg.database.password-file}
|
||||
# cp ${cfg.smtp.password-file} ${cfg.state-directory}/config/config.json
|
||||
# cp -uRL ${pkg}/client ${cfg.state-directory}
|
||||
# chown ${mattermost-user}:${mattermost-group} ${cfg.state-directory}/client
|
||||
# chmod 0750 ${cfg.state-directory}/client
|
||||
# '';
|
||||
|
||||
# serviceConfig = {
|
||||
# PermissionsStartOnly = true;
|
||||
# ExecStart = "${pkg}/bin/mattermost";
|
||||
# WorkingDirectory = cfg.state-directory;
|
||||
# Restart = "always";
|
||||
# RestartSec = "10";
|
||||
# LimitNOFILE = "49152";
|
||||
# User = mattermost-user;
|
||||
# Group = mattermost-group;
|
||||
# };
|
||||
# };
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
|
@ -76,7 +76,7 @@ in {
|
||||
"${cfg.user}" = {
|
||||
isSystemUser = true;
|
||||
createHome = true;
|
||||
home = "/var/home/${cfg.user}";
|
||||
home = "/run/home/${cfg.user}";
|
||||
group = cfg.user;
|
||||
};
|
||||
};
|
||||
@ -90,8 +90,8 @@ in {
|
||||
|
||||
systemd = {
|
||||
tmpfiles.rules = [
|
||||
"d /var/home 755 root - - -"
|
||||
"d /var/home/${cfg.user} 700 ${cfg.user} - - -"
|
||||
"d /run/home 755 root - - -"
|
||||
"d /run/home/${cfg.user} 700 ${cfg.user} - - -"
|
||||
];
|
||||
|
||||
timers.backplane-dns-client = {
|
||||
|
@ -4,6 +4,8 @@ with lib;
|
||||
let
|
||||
hostname = config.instance.hostname;
|
||||
|
||||
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||
|
||||
siteOpts = { ... }: with types; {
|
||||
options = {
|
||||
enableACME = mkOption {
|
||||
@ -45,30 +47,34 @@ let
|
||||
loglevel = cfg.log-level;
|
||||
|
||||
access_rules = {
|
||||
c2s = { allow = "all"; };
|
||||
announce = { allow = "admin"; };
|
||||
configure = { allow = "admin"; };
|
||||
pubsub_createnode = { allow = "local"; };
|
||||
c2s.allow = "all";
|
||||
announce.allow = "admin";
|
||||
configure.allow = "admin";
|
||||
pubsub_createnode.allow = "admin";
|
||||
};
|
||||
|
||||
acl = {
|
||||
admin = {
|
||||
acl.admin = {
|
||||
user = concatMap
|
||||
(admin: map (site: "${admin}@${site}")
|
||||
(attrNames cfg.sites))
|
||||
cfg.admins;
|
||||
};
|
||||
};
|
||||
|
||||
hosts = attrNames cfg.sites;
|
||||
|
||||
listen = map (ip: {
|
||||
# By default, listen on all ips
|
||||
listen = let
|
||||
common = {
|
||||
port = cfg.port;
|
||||
module = "ejabberd_c2s";
|
||||
ip = ip;
|
||||
starttls = true;
|
||||
starttls_required = true;
|
||||
}) cfg.listen-ips;
|
||||
};
|
||||
in
|
||||
if (cfg.listen-ips != null) then
|
||||
map (ip: { ip = ip; } // common)
|
||||
cfg.listen-ips
|
||||
else [ common ];
|
||||
|
||||
certfiles = concatMapAttrsToList
|
||||
(site: siteOpts:
|
||||
@ -86,27 +92,33 @@ let
|
||||
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);
|
||||
secrets;
|
||||
swapper = concatStringsSep " | " secret-swappers;
|
||||
in pkgs.writeShellScript "ejabberd-generate-config.sh" ''
|
||||
[ -f \$${target} ] && rm -f ${target}
|
||||
echo "Copying from ${template} to ${target}"
|
||||
touch ${target}
|
||||
chmod go-rwx ${target}
|
||||
chmod u+rw ${target}
|
||||
cat ${template} | ${swapper} > ${target}
|
||||
echo "Copying from ${template} to ${target} completed"
|
||||
'';
|
||||
|
||||
cfg = config.fudo.jabber;
|
||||
|
||||
log-dir = "${cfg.state-directory}/logs";
|
||||
spool-dir = "${cfg.state-directory}/spool";
|
||||
|
||||
in {
|
||||
options.fudo.jabber = with types; {
|
||||
enable = mkEnableOption "Enable ejabberd server.";
|
||||
|
||||
listen-ips = mkOption {
|
||||
type = listOf str;
|
||||
type = nullOr (listOf str);
|
||||
description = "IPs on which to listen for Jabber connections.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
@ -147,7 +159,7 @@ in {
|
||||
config-file = mkOption {
|
||||
type = str;
|
||||
description = "Location at which to generate the configuration file.";
|
||||
default = "/run/ejabberd/ejabberd.yaml";
|
||||
default = "/run/ejabberd/config/ejabberd.yaml";
|
||||
};
|
||||
|
||||
log-level = mkOption {
|
||||
@ -160,6 +172,12 @@ in {
|
||||
default = 3;
|
||||
};
|
||||
|
||||
state-directory = mkOption {
|
||||
type = str;
|
||||
description = "Path at which to store ejabberd state.";
|
||||
default = "/var/lib/ejabberd";
|
||||
};
|
||||
|
||||
environment = mkOption {
|
||||
type = attrsOf str;
|
||||
description = "Environment variables to set for the ejabberd daemon.";
|
||||
@ -187,27 +205,38 @@ in {
|
||||
};
|
||||
}) cfg.sites;
|
||||
|
||||
system = {
|
||||
services.ejabberd-config-generator = let
|
||||
config-generator =
|
||||
enter-secrets config-file-template cfg.secret-files cfg.config-file;
|
||||
secrets.host-secrets.${hostname}.ejabberd-password-env = let
|
||||
env-vars = mapAttrsToList (secret: file: "${secret}=${readFile file}")
|
||||
cfg.secret-files;
|
||||
in {
|
||||
script = "${config-generator}";
|
||||
readWritePaths = [ config-dir ];
|
||||
workingDirectory = config-dir;
|
||||
source-file = pkgs.writeText "ejabberd-password-env"
|
||||
(concatStringsSep "\n" env-vars);
|
||||
target-file = "/run/ejabberd/environment/config-passwords.env";
|
||||
user = cfg.user;
|
||||
description = "Generate ejabberd config file with necessary passwords.";
|
||||
postStart = ''
|
||||
chown ${cfg.user} ${cfg.config-file}
|
||||
chmod 0400 ${cfg.config-file}
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
# system = {
|
||||
# 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.group} ${cfg.config-file}
|
||||
# chmod 0400 ${cfg.config-file}
|
||||
# '';
|
||||
# };
|
||||
# };
|
||||
};
|
||||
|
||||
systemd = {
|
||||
tmpfiles.rules = [
|
||||
"d '${config-dir}' 0700 ${cfg.user} ${cfg.group} - -'"
|
||||
"d ${config-dir} 0700 ${cfg.user} ${cfg.group} - -"
|
||||
"d ${cfg.state-directory} 0750 ${cfg.user} ${cfg.group} - -"
|
||||
];
|
||||
|
||||
services = {
|
||||
@ -216,6 +245,22 @@ in {
|
||||
requires = [ "ejabberd-config-generator.service" ];
|
||||
environment = cfg.environment;
|
||||
};
|
||||
|
||||
ejabberd-config-generator = let
|
||||
config-generator =
|
||||
enter-secrets config-file-template (attrNames cfg.secret-files) cfg.config-file;
|
||||
in {
|
||||
description = "Generate ejabberd config file containing passwords.";
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
ExecStart = "${config-generator}";
|
||||
ExecStartPost = pkgs.writeShellScript "protect-ejabberd-config.sh" ''
|
||||
chown ${cfg.user}:${cfg.group} ${cfg.config-file}
|
||||
chmod 0400 ${cfg.config-file}
|
||||
'';
|
||||
EnvironmentFile = host-secrets.ejabberd-password-env.target-file;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@ -226,6 +271,9 @@ in {
|
||||
group = cfg.group;
|
||||
|
||||
configFile = cfg.config-file;
|
||||
|
||||
logsDir = log-dir;
|
||||
spoolDir = spool-dir;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -364,12 +364,6 @@ in {
|
||||
attrs = {
|
||||
objectClass = [ "olcDatabaseConfig" "olcFrontendConfig" ];
|
||||
olcDatabase = "{-1}frontend";
|
||||
olcAccess = makeAccess {
|
||||
"*" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"*" = "none";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
"olcDatabase={0}config" = {
|
||||
@ -378,7 +372,6 @@ in {
|
||||
olcDatabase = "{0}config";
|
||||
olcAccess = makeAccess {
|
||||
"*" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"*" = "none";
|
||||
};
|
||||
};
|
||||
@ -392,36 +385,33 @@ in {
|
||||
# olcRootDN = "cn=admin,${cfg.base}";
|
||||
# olcRootPW = FIXME; # NOTE: this should be hashed...
|
||||
olcDbDirectory = "${cfg.state-directory}/database";
|
||||
olcDbIndex = [ "objectClass eq" "uid eq" ];
|
||||
olcDbIndex = [ "objectClass eq" "uid pres,eq" ];
|
||||
olcAccess = makeAccess {
|
||||
"attrs=userPassword,shadowLastChange" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
# "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"dn.exact=cn=auth_reader,${cfg.base}" = "read";
|
||||
"dn.exact=cn=replicator,${cfg.base}" = "read";
|
||||
"self" = "write";
|
||||
"*" = "auth";
|
||||
};
|
||||
"dn=cn=admin,ou=groups,${cfg.base}" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"users" = "read";
|
||||
"*" = "none";
|
||||
# "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"anonymous" = "auth";
|
||||
"dn.children=dc=fudo,dc=org" = "read";
|
||||
};
|
||||
"dn.subtree=ou=groups,${cfg.base} attrs=memberUid" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"dn.regex=cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" = "write";
|
||||
"users" = "read";
|
||||
"*" = "none";
|
||||
# "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
# "dn.regex=cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" = "write";
|
||||
"anonymous" = "auth";
|
||||
"dn.children=dc=fudo,dc=org" = "read";
|
||||
};
|
||||
"dn.subtree=ou=members,${cfg.base} attrs=cn,sn,homeDirectory,loginShell,gecos,description,homeDirectory,uidNumber,gidNumber" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"dn.exact=cn=user_db_reader,${cfg.base}" = "read";
|
||||
"users" = "read";
|
||||
"*" = "none";
|
||||
# "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"anonymous" = "auth";
|
||||
"dn.children=dc=fudo,dc=org" = "read";
|
||||
};
|
||||
"*" = {
|
||||
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"users" = "read";
|
||||
"*" = "none";
|
||||
# "dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
|
||||
"anonymous" = "auth";
|
||||
"dn.children=dc=fudo,dc=org" = "read";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -1,8 +1,28 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
{
|
||||
let
|
||||
cfg = config.fudo.ssh;
|
||||
hostname = config.instance.hostname;
|
||||
|
||||
in {
|
||||
options.fudo.ssh = with types; {
|
||||
whitelistIPs = mkOption {
|
||||
type = listOf str;
|
||||
description =
|
||||
"IPs to which fail2ban rules will not apply (on top of local networks).";
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
services.fail2ban = {
|
||||
ignoreIP =
|
||||
config.instance.local-networks ++ cfg.whitelistIPs;
|
||||
maxretry = if config.fudo.hosts.${hostname}.hardened then 3
|
||||
else 20;
|
||||
};
|
||||
|
||||
programs.ssh.knownHosts = let
|
||||
keyed-hosts =
|
||||
filterAttrs (h: o: o.ssh-pubkeys != [])
|
||||
|
Loading…
Reference in New Issue
Block a user