2015-08-03 17:04:10 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.services.gale;
|
|
|
|
# we convert the path to a string to avoid it being copied to the nix store,
|
|
|
|
# otherwise users could read the private key as all files in the store are
|
|
|
|
# world-readable
|
|
|
|
keyPath = toString cfg.keyPath;
|
|
|
|
# ...but we refer to the pubkey file using a path so that we can ensure the
|
|
|
|
# config gets rebuilt if the public key changes (we can assume the private key
|
|
|
|
# will never change without the public key having changed)
|
|
|
|
gpubFile = cfg.keyPath + "/${cfg.domain}.gpub";
|
|
|
|
home = "/var/lib/gale";
|
|
|
|
keysPrepared = cfg.keyPath != null && lib.pathExists cfg.keyPath;
|
|
|
|
in
|
|
|
|
{
|
|
|
|
options = {
|
|
|
|
services.gale = {
|
|
|
|
enable = mkEnableOption "the Gale messaging daemon";
|
|
|
|
|
|
|
|
user = mkOption {
|
|
|
|
default = "gale";
|
|
|
|
type = types.str;
|
|
|
|
description = "Username for the Gale daemon.";
|
|
|
|
};
|
|
|
|
|
|
|
|
group = mkOption {
|
|
|
|
default = "gale";
|
|
|
|
type = types.str;
|
|
|
|
description = "Group name for the Gale daemon.";
|
|
|
|
};
|
|
|
|
|
|
|
|
setuidWrapper = mkOption {
|
|
|
|
default = null;
|
|
|
|
description = "Configuration for the Gale gksign setuid wrapper.";
|
|
|
|
};
|
|
|
|
|
|
|
|
domain = mkOption {
|
|
|
|
default = "";
|
|
|
|
type = types.str;
|
|
|
|
description = "Domain name for the Gale system.";
|
|
|
|
};
|
|
|
|
|
|
|
|
keyPath = mkOption {
|
|
|
|
default = null;
|
|
|
|
type = types.nullOr types.path;
|
|
|
|
description = ''
|
|
|
|
Directory containing the key pair for this Gale domain. The expected
|
|
|
|
filename will be taken from the domain option with ".gpri" and ".gpub"
|
|
|
|
appended.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
extraConfig = mkOption {
|
|
|
|
type = types.lines;
|
|
|
|
default = "";
|
|
|
|
description = ''
|
|
|
|
Additional text to be added to <filename>/etc/gale/conf</filename>.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
config = mkMerge [
|
|
|
|
(mkIf cfg.enable {
|
|
|
|
assertions = [{
|
|
|
|
assertion = cfg.domain != "";
|
|
|
|
message = "A domain must be set for Gale.";
|
|
|
|
}];
|
|
|
|
|
|
|
|
warnings = mkIf (!keysPrepared) [
|
|
|
|
"You must run gale-install in order to generate a domain key."
|
|
|
|
];
|
|
|
|
|
|
|
|
system.activationScripts.gale = mkIf cfg.enable (
|
|
|
|
stringAfter [ "users" "groups" ] ''
|
2016-01-18 19:54:23 +00:00
|
|
|
chmod 755 ${home}
|
2015-08-03 17:04:10 +00:00
|
|
|
mkdir -m 0777 -p ${home}/auth/cache
|
|
|
|
mkdir -m 1777 -p ${home}/auth/local # GALE_DOMAIN.gpub
|
|
|
|
mkdir -m 0700 -p ${home}/auth/private # ROOT.gpub
|
|
|
|
mkdir -m 0755 -p ${home}/auth/trusted # ROOT
|
|
|
|
mkdir -m 0700 -p ${home}/.gale
|
|
|
|
mkdir -m 0700 -p ${home}/.gale/auth
|
|
|
|
mkdir -m 0700 -p ${home}/.gale/auth/private # GALE_DOMAIN.gpri
|
|
|
|
|
|
|
|
ln -sf ${pkgs.gale}/etc/gale/auth/trusted/ROOT "${home}/auth/trusted/ROOT"
|
2016-01-18 19:54:23 +00:00
|
|
|
chown ${cfg.user}:${cfg.group} ${home} ${home}/auth ${home}/auth/*
|
|
|
|
chown ${cfg.user}:${cfg.group} ${home}/.gale ${home}/.gale/auth ${home}/.gale/auth/private
|
2015-08-03 17:04:10 +00:00
|
|
|
''
|
|
|
|
);
|
|
|
|
|
|
|
|
environment = {
|
|
|
|
etc = {
|
|
|
|
"gale/auth".source = home + "/auth"; # symlink /var/lib/gale/auth
|
|
|
|
"gale/conf".text = ''
|
|
|
|
GALE_USER ${cfg.user}
|
|
|
|
GALE_DOMAIN ${cfg.domain}
|
|
|
|
${cfg.extraConfig}
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
systemPackages = [ pkgs.gale ];
|
|
|
|
};
|
|
|
|
|
2019-09-14 17:51:29 +00:00
|
|
|
users.users.${cfg.user} = {
|
2015-08-03 17:04:10 +00:00
|
|
|
description = "Gale daemon";
|
|
|
|
uid = config.ids.uids.gale;
|
|
|
|
group = cfg.group;
|
|
|
|
home = home;
|
|
|
|
createHome = true;
|
2019-09-14 17:51:29 +00:00
|
|
|
};
|
2015-08-03 17:04:10 +00:00
|
|
|
|
2018-06-29 23:58:35 +00:00
|
|
|
users.groups = [{
|
2015-08-03 17:04:10 +00:00
|
|
|
name = cfg.group;
|
|
|
|
gid = config.ids.gids.gale;
|
|
|
|
}];
|
|
|
|
})
|
|
|
|
(mkIf (cfg.enable && keysPrepared) {
|
|
|
|
assertions = [
|
|
|
|
{
|
|
|
|
assertion = cfg.keyPath != null
|
|
|
|
&& lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpub");
|
|
|
|
message = "Couldn't find a Gale public key for ${cfg.domain}.";
|
|
|
|
}
|
|
|
|
{
|
|
|
|
assertion = cfg.keyPath != null
|
|
|
|
&& lib.pathExists (cfg.keyPath + "/${cfg.domain}.gpri");
|
|
|
|
message = "Couldn't find a Gale private key for ${cfg.domain}.";
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
services.gale.setuidWrapper = {
|
|
|
|
program = "gksign";
|
|
|
|
source = "${pkgs.gale}/bin/gksign";
|
|
|
|
owner = cfg.user;
|
|
|
|
group = cfg.group;
|
|
|
|
setuid = true;
|
|
|
|
setgid = false;
|
|
|
|
};
|
|
|
|
|
2017-01-29 07:58:12 +00:00
|
|
|
security.wrappers.gksign = cfg.setuidWrapper;
|
2015-08-03 17:04:10 +00:00
|
|
|
|
|
|
|
systemd.services.gale-galed = {
|
|
|
|
description = "Gale messaging daemon";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
wants = [ "gale-gdomain.service" ];
|
|
|
|
after = [ "network.target" ];
|
|
|
|
|
|
|
|
preStart = ''
|
2016-01-18 19:54:23 +00:00
|
|
|
install -m 0640 -o ${cfg.user} -g ${cfg.group} ${keyPath}/${cfg.domain}.gpri "${home}/.gale/auth/private/"
|
|
|
|
install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/.gale/auth/private/${cfg.domain}.gpub"
|
|
|
|
install -m 0644 -o ${cfg.user} -g ${cfg.group} ${gpubFile} "${home}/auth/local/${cfg.domain}.gpub"
|
2015-08-03 17:04:10 +00:00
|
|
|
'';
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
Type = "forking";
|
|
|
|
ExecStart = "@${pkgs.gale}/bin/galed galed";
|
|
|
|
User = cfg.user;
|
|
|
|
Group = cfg.group;
|
|
|
|
PermissionsStartOnly = true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
systemd.services.gale-gdomain = {
|
|
|
|
description = "Gale AKD daemon";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
requires = [ "gale-galed.service" ];
|
|
|
|
after = [ "gale-galed.service" ];
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
Type = "forking";
|
|
|
|
ExecStart = "@${pkgs.gale}/bin/gdomain gdomain";
|
|
|
|
User = cfg.user;
|
|
|
|
Group = cfg.group;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
})
|
|
|
|
];
|
|
|
|
}
|