Merge pull request #45470 from Infinisil/znc-config
nixos/znc: More flexible module, cleanups
This commit is contained in:
commit
e443bbf6fd
|
@ -122,6 +122,14 @@
|
||||||
the Python 2 or 3 version of the package.
|
the Python 2 or 3 version of the package.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Options
|
||||||
|
<literal>services.znc.confOptions.networks.<replaceable>name</replaceable>.userName</literal> and
|
||||||
|
<literal>services.znc.confOptions.networks.<replaceable>name</replaceable>.modulePackages</literal>
|
||||||
|
were removed. They were never used for anything and can therefore safely be removed.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -635,7 +635,7 @@
|
||||||
./services/networking/zerobin.nix
|
./services/networking/zerobin.nix
|
||||||
./services/networking/zeronet.nix
|
./services/networking/zeronet.nix
|
||||||
./services/networking/zerotierone.nix
|
./services/networking/zerotierone.nix
|
||||||
./services/networking/znc.nix
|
./services/networking/znc/default.nix
|
||||||
./services/printing/cupsd.nix
|
./services/printing/cupsd.nix
|
||||||
./services/scheduling/atd.nix
|
./services/scheduling/atd.nix
|
||||||
./services/scheduling/chronos.nix
|
./services/scheduling/chronos.nix
|
||||||
|
|
|
@ -1,431 +0,0 @@
|
||||||
{ config, lib, pkgs, ...}:
|
|
||||||
|
|
||||||
with lib;
|
|
||||||
|
|
||||||
let
|
|
||||||
cfg = config.services.znc;
|
|
||||||
|
|
||||||
defaultUser = "znc"; # Default user to own process.
|
|
||||||
|
|
||||||
# Default user and pass:
|
|
||||||
# un=znc
|
|
||||||
# pw=nixospass
|
|
||||||
|
|
||||||
defaultUserName = "znc";
|
|
||||||
defaultPassBlock = "
|
|
||||||
<Pass password>
|
|
||||||
Method = sha256
|
|
||||||
Hash = e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93
|
|
||||||
Salt = l5Xryew4g*!oa(ECfX2o
|
|
||||||
</Pass>
|
|
||||||
";
|
|
||||||
|
|
||||||
modules = pkgs.buildEnv {
|
|
||||||
name = "znc-modules";
|
|
||||||
paths = cfg.modulePackages;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Keep znc.conf in nix store, then symlink or copy into `dataDir`, depending on `mutable`.
|
|
||||||
mkZncConf = confOpts: ''
|
|
||||||
Version = 1.6.3
|
|
||||||
${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.modules}
|
|
||||||
|
|
||||||
<Listener l>
|
|
||||||
Port = ${toString confOpts.port}
|
|
||||||
IPv4 = true
|
|
||||||
IPv6 = true
|
|
||||||
SSL = ${boolToString confOpts.useSSL}
|
|
||||||
${lib.optionalString (confOpts.uriPrefix != null) "URIPrefix = ${confOpts.uriPrefix}"}
|
|
||||||
</Listener>
|
|
||||||
|
|
||||||
<User ${confOpts.userName}>
|
|
||||||
${confOpts.passBlock}
|
|
||||||
Admin = true
|
|
||||||
Nick = ${confOpts.nick}
|
|
||||||
AltNick = ${confOpts.nick}_
|
|
||||||
Ident = ${confOpts.nick}
|
|
||||||
RealName = ${confOpts.nick}
|
|
||||||
${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.userModules}
|
|
||||||
|
|
||||||
${ lib.concatStringsSep "\n" (lib.mapAttrsToList (name: net: ''
|
|
||||||
<Network ${name}>
|
|
||||||
${concatMapStrings (m: "LoadModule = ${m}\n") net.modules}
|
|
||||||
Server = ${net.server} ${lib.optionalString net.useSSL "+"}${toString net.port} ${net.password}
|
|
||||||
${concatMapStrings (c: "<Chan #${c}>\n</Chan>\n") net.channels}
|
|
||||||
${lib.optionalString net.hasBitlbeeControlChannel ''
|
|
||||||
<Chan &bitlbee>
|
|
||||||
</Chan>
|
|
||||||
''}
|
|
||||||
${net.extraConf}
|
|
||||||
</Network>
|
|
||||||
'') confOpts.networks) }
|
|
||||||
</User>
|
|
||||||
${confOpts.extraZncConf}
|
|
||||||
'';
|
|
||||||
|
|
||||||
zncConfFile = pkgs.writeTextFile {
|
|
||||||
name = "znc.conf";
|
|
||||||
text = if cfg.zncConf != ""
|
|
||||||
then cfg.zncConf
|
|
||||||
else mkZncConf cfg.confOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
networkOpts = { ... }: {
|
|
||||||
options = {
|
|
||||||
server = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
example = "chat.freenode.net";
|
|
||||||
description = ''
|
|
||||||
IRC server address.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
port = mkOption {
|
|
||||||
type = types.int;
|
|
||||||
default = 6697;
|
|
||||||
example = 6697;
|
|
||||||
description = ''
|
|
||||||
IRC server port.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
userName = mkOption {
|
|
||||||
default = "";
|
|
||||||
example = "johntron";
|
|
||||||
type = types.string;
|
|
||||||
description = ''
|
|
||||||
A nick identity specific to the IRC server.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
password = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "";
|
|
||||||
description = ''
|
|
||||||
IRC server password, such as for a Slack gateway.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
useSSL = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
description = ''
|
|
||||||
Whether to use SSL to connect to the IRC server.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
modulePackages = mkOption {
|
|
||||||
type = types.listOf types.package;
|
|
||||||
default = [];
|
|
||||||
example = [ "pkgs.zncModules.push" "pkgs.zncModules.fish" ];
|
|
||||||
description = ''
|
|
||||||
External ZNC modules to build.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
modules = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [ "simple_away" ];
|
|
||||||
example = literalExample "[ simple_away sasl ]";
|
|
||||||
description = ''
|
|
||||||
ZNC modules to load.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
channels = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [];
|
|
||||||
example = [ "nixos" ];
|
|
||||||
description = ''
|
|
||||||
IRC channels to join.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
hasBitlbeeControlChannel = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = ''
|
|
||||||
Whether to add the special Bitlbee operations channel.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
extraConf = mkOption {
|
|
||||||
default = "";
|
|
||||||
type = types.lines;
|
|
||||||
example = ''
|
|
||||||
Encoding = ^UTF-8
|
|
||||||
FloodBurst = 4
|
|
||||||
FloodRate = 1.00
|
|
||||||
IRCConnectEnabled = true
|
|
||||||
Ident = johntron
|
|
||||||
JoinDelay = 0
|
|
||||||
Nick = johntron
|
|
||||||
'';
|
|
||||||
description = ''
|
|
||||||
Extra config for the network.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
in
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
###### Interface
|
|
||||||
|
|
||||||
options = {
|
|
||||||
services.znc = {
|
|
||||||
enable = mkOption {
|
|
||||||
default = false;
|
|
||||||
type = types.bool;
|
|
||||||
description = ''
|
|
||||||
Enable a ZNC service for a user.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
user = mkOption {
|
|
||||||
default = "znc";
|
|
||||||
example = "john";
|
|
||||||
type = types.string;
|
|
||||||
description = ''
|
|
||||||
The name of an existing user account to use to own the ZNC server process.
|
|
||||||
If not specified, a default user will be created to own the process.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
group = mkOption {
|
|
||||||
default = "";
|
|
||||||
example = "users";
|
|
||||||
type = types.string;
|
|
||||||
description = ''
|
|
||||||
Group to own the ZNCserver process.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
dataDir = mkOption {
|
|
||||||
default = "/var/lib/znc/";
|
|
||||||
example = "/home/john/.znc/";
|
|
||||||
type = types.path;
|
|
||||||
description = ''
|
|
||||||
The data directory. Used for configuration files and modules.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
openFirewall = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
description = ''
|
|
||||||
Whether to open ports in the firewall for ZNC.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
zncConf = mkOption {
|
|
||||||
default = "";
|
|
||||||
example = "See: http://wiki.znc.in/Configuration";
|
|
||||||
type = types.lines;
|
|
||||||
description = ''
|
|
||||||
Config file as generated with `znc --makeconf` to use for the whole ZNC configuration.
|
|
||||||
If specified, `confOptions` will be ignored, and this value, as-is, will be used.
|
|
||||||
If left empty, a conf file with default values will be used.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
confOptions = {
|
|
||||||
modules = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [ "webadmin" "adminlog" ];
|
|
||||||
example = [ "partyline" "webadmin" "adminlog" "log" ];
|
|
||||||
description = ''
|
|
||||||
A list of modules to include in the `znc.conf` file.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
userModules = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [ "chansaver" "controlpanel" ];
|
|
||||||
example = [ "chansaver" "controlpanel" "fish" "push" ];
|
|
||||||
description = ''
|
|
||||||
A list of user modules to include in the `znc.conf` file.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
userName = mkOption {
|
|
||||||
default = defaultUserName;
|
|
||||||
example = "johntron";
|
|
||||||
type = types.string;
|
|
||||||
description = ''
|
|
||||||
The user name used to log in to the ZNC web admin interface.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
networks = mkOption {
|
|
||||||
default = { };
|
|
||||||
type = with types; attrsOf (submodule networkOpts);
|
|
||||||
description = ''
|
|
||||||
IRC networks to connect the user to.
|
|
||||||
'';
|
|
||||||
example = {
|
|
||||||
"freenode" = {
|
|
||||||
server = "chat.freenode.net";
|
|
||||||
port = 6697;
|
|
||||||
useSSL = true;
|
|
||||||
modules = [ "simple_away" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
nick = mkOption {
|
|
||||||
default = "znc-user";
|
|
||||||
example = "john";
|
|
||||||
type = types.string;
|
|
||||||
description = ''
|
|
||||||
The IRC nick.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
passBlock = mkOption {
|
|
||||||
example = defaultPassBlock;
|
|
||||||
type = types.string;
|
|
||||||
description = ''
|
|
||||||
Generate with `nix-shell -p znc --command "znc --makepass"`.
|
|
||||||
This is the password used to log in to the ZNC web admin interface.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
port = mkOption {
|
|
||||||
default = 5000;
|
|
||||||
example = 5000;
|
|
||||||
type = types.int;
|
|
||||||
description = ''
|
|
||||||
Specifies the port on which to listen.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
useSSL = mkOption {
|
|
||||||
default = true;
|
|
||||||
type = types.bool;
|
|
||||||
description = ''
|
|
||||||
Indicates whether the ZNC server should use SSL when listening on the specified port. A self-signed certificate will be generated.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
uriPrefix = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
example = "/znc/";
|
|
||||||
description = ''
|
|
||||||
An optional URI prefix for the ZNC web interface. Can be
|
|
||||||
used to make ZNC available behind a reverse proxy.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
extraZncConf = mkOption {
|
|
||||||
default = "";
|
|
||||||
type = types.lines;
|
|
||||||
description = ''
|
|
||||||
Extra config to `znc.conf` file.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
modulePackages = mkOption {
|
|
||||||
type = types.listOf types.package;
|
|
||||||
default = [ ];
|
|
||||||
example = literalExample "[ pkgs.zncModules.fish pkgs.zncModules.push ]";
|
|
||||||
description = ''
|
|
||||||
A list of global znc module packages to add to znc.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
mutable = mkOption {
|
|
||||||
default = true;
|
|
||||||
type = types.bool;
|
|
||||||
description = ''
|
|
||||||
Indicates whether to allow the contents of the `dataDir` directory to be changed
|
|
||||||
by the user at run-time.
|
|
||||||
If true, modifications to the ZNC configuration after its initial creation are not
|
|
||||||
overwritten by a NixOS system rebuild.
|
|
||||||
If false, the ZNC configuration is rebuilt by every system rebuild.
|
|
||||||
If the user wants to manage the ZNC service using the web admin interface, this value
|
|
||||||
should be set to true.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
extraFlags = mkOption {
|
|
||||||
default = [ ];
|
|
||||||
example = [ "--debug" ];
|
|
||||||
type = types.listOf types.str;
|
|
||||||
description = ''
|
|
||||||
Extra flags to use when executing znc command.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
###### Implementation
|
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
|
||||||
|
|
||||||
networking.firewall = mkIf cfg.openFirewall {
|
|
||||||
allowedTCPPorts = [ cfg.confOptions.port ];
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.znc = {
|
|
||||||
description = "ZNC Server";
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [ "network.service" ];
|
|
||||||
serviceConfig = {
|
|
||||||
User = cfg.user;
|
|
||||||
Group = cfg.group;
|
|
||||||
Restart = "always";
|
|
||||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
|
||||||
ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
|
|
||||||
};
|
|
||||||
preStart = ''
|
|
||||||
${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}/configs
|
|
||||||
|
|
||||||
# If mutable, regenerate conf file every time.
|
|
||||||
${optionalString (!cfg.mutable) ''
|
|
||||||
${pkgs.coreutils}/bin/echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated."
|
|
||||||
${pkgs.coreutils}/bin/rm -f ${cfg.dataDir}/configs/znc.conf
|
|
||||||
''}
|
|
||||||
|
|
||||||
# Ensure essential files exist.
|
|
||||||
if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
|
|
||||||
${pkgs.coreutils}/bin/echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
|
|
||||||
${pkgs.coreutils}/bin/cp --no-clobber ${zncConfFile} ${cfg.dataDir}/configs/znc.conf
|
|
||||||
${pkgs.coreutils}/bin/chmod u+rw ${cfg.dataDir}/configs/znc.conf
|
|
||||||
${pkgs.coreutils}/bin/chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
|
|
||||||
${pkgs.coreutils}/bin/echo "No znc.pem file found in ${cfg.dataDir}. Creating one now."
|
|
||||||
${pkgs.znc}/bin/znc --makepem --datadir ${cfg.dataDir}
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Symlink modules
|
|
||||||
rm ${cfg.dataDir}/modules || true
|
|
||||||
ln -fs ${modules}/lib/znc ${cfg.dataDir}/modules
|
|
||||||
'';
|
|
||||||
script = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${toString cfg.extraFlags}";
|
|
||||||
};
|
|
||||||
|
|
||||||
users.users = optional (cfg.user == defaultUser)
|
|
||||||
{ name = defaultUser;
|
|
||||||
description = "ZNC server daemon owner";
|
|
||||||
group = defaultUser;
|
|
||||||
uid = config.ids.uids.znc;
|
|
||||||
home = cfg.dataDir;
|
|
||||||
createHome = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
users.groups = optional (cfg.user == defaultUser)
|
|
||||||
{ name = defaultUser;
|
|
||||||
gid = config.ids.gids.znc;
|
|
||||||
members = [ defaultUser ];
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
306
nixos/modules/services/networking/znc/default.nix
Normal file
306
nixos/modules/services/networking/znc/default.nix
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
{ config, lib, pkgs, ...}:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
cfg = config.services.znc;
|
||||||
|
|
||||||
|
defaultUser = "znc";
|
||||||
|
|
||||||
|
modules = pkgs.buildEnv {
|
||||||
|
name = "znc-modules";
|
||||||
|
paths = cfg.modulePackages;
|
||||||
|
};
|
||||||
|
|
||||||
|
listenerPorts = concatMap (l: optional (l ? Port) l.Port)
|
||||||
|
(attrValues (cfg.config.Listener or {}));
|
||||||
|
|
||||||
|
# Converts the config option to a string
|
||||||
|
semanticString = let
|
||||||
|
|
||||||
|
sortedAttrs = set: sort (l: r:
|
||||||
|
if l == "extraConfig" then false # Always put extraConfig last
|
||||||
|
else if isAttrs set.${l} == isAttrs set.${r} then l < r
|
||||||
|
else isAttrs set.${r} # Attrsets should be last, makes for a nice config
|
||||||
|
# This last case occurs when any side (but not both) is an attrset
|
||||||
|
# The order of these is correct when the attrset is on the right
|
||||||
|
# which we're just returning
|
||||||
|
) (attrNames set);
|
||||||
|
|
||||||
|
# Specifies an attrset that encodes the value according to its type
|
||||||
|
encode = name: value: {
|
||||||
|
null = [];
|
||||||
|
bool = [ "${name} = ${boolToString value}" ];
|
||||||
|
int = [ "${name} = ${toString value}" ];
|
||||||
|
|
||||||
|
# extraConfig should be inserted verbatim
|
||||||
|
string = [ (if name == "extraConfig" then value else "${name} = ${value}") ];
|
||||||
|
|
||||||
|
# Values like `Foo = [ "bar" "baz" ];` should be transformed into
|
||||||
|
# Foo=bar
|
||||||
|
# Foo=baz
|
||||||
|
list = concatMap (encode name) value;
|
||||||
|
|
||||||
|
# Values like `Foo = { bar = { Baz = "baz"; Qux = "qux"; Florps = null; }; };` should be transmed into
|
||||||
|
# <Foo bar>
|
||||||
|
# Baz=baz
|
||||||
|
# Qux=qux
|
||||||
|
# </Foo>
|
||||||
|
set = concatMap (subname: [
|
||||||
|
"<${name} ${subname}>"
|
||||||
|
] ++ map (line: "\t${line}") (toLines value.${subname}) ++ [
|
||||||
|
"</${name}>"
|
||||||
|
]) (filter (v: v != null) (attrNames value));
|
||||||
|
|
||||||
|
}.${builtins.typeOf value};
|
||||||
|
|
||||||
|
# One level "above" encode, acts upon a set and uses encode on each name,value pair
|
||||||
|
toLines = set: concatMap (name: encode name set.${name}) (sortedAttrs set);
|
||||||
|
|
||||||
|
in
|
||||||
|
concatStringsSep "\n" (toLines cfg.config);
|
||||||
|
|
||||||
|
semanticTypes = with types; rec {
|
||||||
|
zncAtom = nullOr (either (either int bool) str);
|
||||||
|
zncAttr = attrsOf (nullOr zncConf);
|
||||||
|
zncAll = either (either zncAtom (listOf zncAtom)) zncAttr;
|
||||||
|
zncConf = attrsOf (zncAll // {
|
||||||
|
# Since this is a recursive type and the description by default contains
|
||||||
|
# the description of its subtypes, infinite recursion would occur without
|
||||||
|
# explicitly breaking this cycle
|
||||||
|
description = "znc values (null, atoms (str, int, bool), list of atoms, or attrsets of znc values)";
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
imports = [ ./options.nix ];
|
||||||
|
|
||||||
|
options = {
|
||||||
|
services.znc = {
|
||||||
|
enable = mkEnableOption "ZNC";
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
default = "znc";
|
||||||
|
example = "john";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
The name of an existing user account to use to own the ZNC server
|
||||||
|
process. If not specified, a default user will be created.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
default = defaultUser;
|
||||||
|
example = "users";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
Group to own the ZNC process.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
default = "/var/lib/znc/";
|
||||||
|
example = "/home/john/.znc/";
|
||||||
|
type = types.path;
|
||||||
|
description = ''
|
||||||
|
The state directory for ZNC. The config and the modules will be linked
|
||||||
|
to from this directory as well.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
openFirewall = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to open ports in the firewall for ZNC. Does work with
|
||||||
|
ports for listeners specified in
|
||||||
|
<option>services.znc.config.Listener</option>.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkOption {
|
||||||
|
type = semanticTypes.zncConf;
|
||||||
|
default = {};
|
||||||
|
example = literalExample ''
|
||||||
|
{
|
||||||
|
LoadModule = [ "webadmin" "adminlog" ];
|
||||||
|
User.paul = {
|
||||||
|
Admin = true;
|
||||||
|
Nick = "paul";
|
||||||
|
AltNick = "paul1";
|
||||||
|
LoadModule = [ "chansaver" "controlpanel" ];
|
||||||
|
Network.freenode = {
|
||||||
|
Server = "chat.freenode.net +6697";
|
||||||
|
LoadModule = [ "simple_away" ];
|
||||||
|
Chan = {
|
||||||
|
"#nixos" = { Detached = false; };
|
||||||
|
"##linux" = { Disabled = true; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Pass.password = {
|
||||||
|
Method = "sha256";
|
||||||
|
Hash = "e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93";
|
||||||
|
Salt = "l5Xryew4g*!oa(ECfX2o";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Configuration for ZNC, see
|
||||||
|
<literal>https://wiki.znc.in/Configuration</literal> for details. The
|
||||||
|
Nix value declared here will be translated directly to the xml-like
|
||||||
|
format ZNC expects. This is much more flexible than the legacy options
|
||||||
|
under <option>services.znc.confOptions.*</option>, but also can't do
|
||||||
|
any type checking.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
You can use <command>nix-instantiate --eval --strict '<nixpkgs/nixos>' -A config.services.znc.config</command>
|
||||||
|
to view the current value. By default it contains a listener for port
|
||||||
|
5000 with SSL enabled.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Nix attributes called <literal>extraConfig</literal> will be inserted
|
||||||
|
verbatim into the resulting config file.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
If <option>services.znc.useLegacyConfig</option> is turned on, the
|
||||||
|
option values in <option>services.znc.confOptions.*</option> will be
|
||||||
|
gracefully be applied to this option.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
If you intend to update the configuration through this option, be sure
|
||||||
|
to enable <option>services.znc.mutable</option>, otherwise none of the
|
||||||
|
changes here will be applied after the initial deploy.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
configFile = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
example = "~/.znc/configs/znc.conf";
|
||||||
|
description = ''
|
||||||
|
Configuration file for ZNC. It is recommended to use the
|
||||||
|
<option>config</option> option instead.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Setting this option will override any auto-generated config file
|
||||||
|
through the <option>confOptions</option> or <option>config</option>
|
||||||
|
options.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
modulePackages = mkOption {
|
||||||
|
type = types.listOf types.package;
|
||||||
|
default = [ ];
|
||||||
|
example = literalExample "[ pkgs.zncModules.fish pkgs.zncModules.push ]";
|
||||||
|
description = ''
|
||||||
|
A list of global znc module packages to add to znc.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
mutable = mkOption {
|
||||||
|
default = true; # TODO: Default to true when config is set, make sure to not delete the old config if present
|
||||||
|
type = types.bool;
|
||||||
|
description = ''
|
||||||
|
Indicates whether to allow the contents of the
|
||||||
|
<literal>dataDir</literal> directory to be changed by the user at
|
||||||
|
run-time.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
If enabled, modifications to the ZNC configuration after its initial
|
||||||
|
creation are not overwritten by a NixOS rebuild. If disabled, the
|
||||||
|
ZNC configuration is rebuilt on every NixOS rebuild.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
If the user wants to manage the ZNC service using the web admin
|
||||||
|
interface, this option should be enabled.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraFlags = mkOption {
|
||||||
|
default = [ ];
|
||||||
|
example = [ "--debug" ];
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = ''
|
||||||
|
Extra arguments to use for executing znc.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
###### Implementation
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
|
services.znc = {
|
||||||
|
configFile = mkDefault (pkgs.writeText "znc-generated.conf" semanticString);
|
||||||
|
config = {
|
||||||
|
Version = (builtins.parseDrvName pkgs.znc.name).version;
|
||||||
|
Listener.l.Port = mkDefault 5000;
|
||||||
|
Listener.l.SSL = mkDefault true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall listenerPorts;
|
||||||
|
|
||||||
|
systemd.services.znc = {
|
||||||
|
description = "ZNC Server";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
Restart = "always";
|
||||||
|
ExecStart = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${escapeShellArgs cfg.extraFlags}";
|
||||||
|
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||||
|
ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
|
||||||
|
};
|
||||||
|
preStart = ''
|
||||||
|
mkdir -p ${cfg.dataDir}/configs
|
||||||
|
|
||||||
|
# If mutable, regenerate conf file every time.
|
||||||
|
${optionalString (!cfg.mutable) ''
|
||||||
|
echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated."
|
||||||
|
rm -f ${cfg.dataDir}/configs/znc.conf
|
||||||
|
''}
|
||||||
|
|
||||||
|
# Ensure essential files exist.
|
||||||
|
if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then
|
||||||
|
echo "No znc.conf file found in ${cfg.dataDir}. Creating one now."
|
||||||
|
cp --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf
|
||||||
|
chmod u+rw ${cfg.dataDir}/configs/znc.conf
|
||||||
|
chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then
|
||||||
|
echo "No znc.pem file found in ${cfg.dataDir}. Creating one now."
|
||||||
|
${pkgs.znc}/bin/znc --makepem --datadir ${cfg.dataDir}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Symlink modules
|
||||||
|
rm ${cfg.dataDir}/modules || true
|
||||||
|
ln -fs ${modules}/lib/znc ${cfg.dataDir}/modules
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users = optional (cfg.user == defaultUser)
|
||||||
|
{ name = defaultUser;
|
||||||
|
description = "ZNC server daemon owner";
|
||||||
|
group = defaultUser;
|
||||||
|
uid = config.ids.uids.znc;
|
||||||
|
home = cfg.dataDir;
|
||||||
|
createHome = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.groups = optional (cfg.user == defaultUser)
|
||||||
|
{ name = defaultUser;
|
||||||
|
gid = config.ids.gids.znc;
|
||||||
|
members = [ defaultUser ];
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
268
nixos/modules/services/networking/znc/options.nix
Normal file
268
nixos/modules/services/networking/znc/options.nix
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
{ lib, config, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
cfg = config.services.znc;
|
||||||
|
|
||||||
|
networkOpts = {
|
||||||
|
options = {
|
||||||
|
|
||||||
|
server = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "chat.freenode.net";
|
||||||
|
description = ''
|
||||||
|
IRC server address.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.ints.u16;
|
||||||
|
default = 6697;
|
||||||
|
description = ''
|
||||||
|
IRC server port.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
password = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "";
|
||||||
|
description = ''
|
||||||
|
IRC server password, such as for a Slack gateway.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
useSSL = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to use SSL to connect to the IRC server.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
modules = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ "simple_away" ];
|
||||||
|
example = literalExample "[ simple_away sasl ]";
|
||||||
|
description = ''
|
||||||
|
ZNC network modules to load.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
channels = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
example = [ "nixos" ];
|
||||||
|
description = ''
|
||||||
|
IRC channels to join.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
hasBitlbeeControlChannel = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to add the special Bitlbee operations channel.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraConf = mkOption {
|
||||||
|
default = "";
|
||||||
|
type = types.lines;
|
||||||
|
example = ''
|
||||||
|
Encoding = ^UTF-8
|
||||||
|
FloodBurst = 4
|
||||||
|
FloodRate = 1.00
|
||||||
|
IRCConnectEnabled = true
|
||||||
|
Ident = johntron
|
||||||
|
JoinDelay = 0
|
||||||
|
Nick = johntron
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
Extra config for the network. Consider using
|
||||||
|
<option>services.znc.config</option> instead.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
options = {
|
||||||
|
services.znc = {
|
||||||
|
|
||||||
|
useLegacyConfig = mkOption {
|
||||||
|
default = true;
|
||||||
|
type = types.bool;
|
||||||
|
description = ''
|
||||||
|
Whether to propagate the legacy options under
|
||||||
|
<option>services.znc.confOptions.*</option> to the znc config. If this
|
||||||
|
is turned on, the znc config will contain a user with the default name
|
||||||
|
"znc", global modules "webadmin" and "adminlog" will be enabled by
|
||||||
|
default, and more, all controlled through the
|
||||||
|
<option>services.znc.confOptions.*</option> options.
|
||||||
|
You can use <command>nix-instantiate --eval --strict '<nixpkgs/nixos>' -A config.services.znc.config</command>
|
||||||
|
to view the current value of the config.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
In any case, if you need more flexibility,
|
||||||
|
<option>services.znc.config</option> can be used to override/add to
|
||||||
|
all of the legacy options.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
confOptions = {
|
||||||
|
modules = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ "webadmin" "adminlog" ];
|
||||||
|
example = [ "partyline" "webadmin" "adminlog" "log" ];
|
||||||
|
description = ''
|
||||||
|
A list of modules to include in the `znc.conf` file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
userModules = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [ "chansaver" "controlpanel" ];
|
||||||
|
example = [ "chansaver" "controlpanel" "fish" "push" ];
|
||||||
|
description = ''
|
||||||
|
A list of user modules to include in the `znc.conf` file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
userName = mkOption {
|
||||||
|
default = "znc";
|
||||||
|
example = "johntron";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
The user name used to log in to the ZNC web admin interface.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
networks = mkOption {
|
||||||
|
default = { };
|
||||||
|
type = with types; attrsOf (submodule networkOpts);
|
||||||
|
description = ''
|
||||||
|
IRC networks to connect the user to.
|
||||||
|
'';
|
||||||
|
example = literalExample ''
|
||||||
|
{
|
||||||
|
"freenode" = {
|
||||||
|
server = "chat.freenode.net";
|
||||||
|
port = 6697;
|
||||||
|
useSSL = true;
|
||||||
|
modules = [ "simple_away" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
nick = mkOption {
|
||||||
|
default = "znc-user";
|
||||||
|
example = "john";
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
The IRC nick.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
passBlock = mkOption {
|
||||||
|
example = literalExample ''
|
||||||
|
<Pass password>
|
||||||
|
Method = sha256
|
||||||
|
Hash = e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93
|
||||||
|
Salt = l5Xryew4g*!oa(ECfX2o
|
||||||
|
</Pass>
|
||||||
|
'';
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
Generate with `nix-shell -p znc --command "znc --makepass"`.
|
||||||
|
This is the password used to log in to the ZNC web admin interface.
|
||||||
|
You can also set this through
|
||||||
|
<option>services.znc.config.User.<username>.Pass.Method</option>
|
||||||
|
and co.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
default = 5000;
|
||||||
|
type = types.int;
|
||||||
|
description = ''
|
||||||
|
Specifies the port on which to listen.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
useSSL = mkOption {
|
||||||
|
default = true;
|
||||||
|
type = types.bool;
|
||||||
|
description = ''
|
||||||
|
Indicates whether the ZNC server should use SSL when listening on
|
||||||
|
the specified port. A self-signed certificate will be generated.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
uriPrefix = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
example = "/znc/";
|
||||||
|
description = ''
|
||||||
|
An optional URI prefix for the ZNC web interface. Can be
|
||||||
|
used to make ZNC available behind a reverse proxy.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraZncConf = mkOption {
|
||||||
|
default = "";
|
||||||
|
type = types.lines;
|
||||||
|
description = ''
|
||||||
|
Extra config to `znc.conf` file.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.useLegacyConfig {
|
||||||
|
|
||||||
|
services.znc.config = let
|
||||||
|
c = cfg.confOptions;
|
||||||
|
# defaults here should override defaults set in the non-legacy part
|
||||||
|
mkDefault = mkOverride 900;
|
||||||
|
in {
|
||||||
|
LoadModule = mkDefault c.modules;
|
||||||
|
Listener.l = {
|
||||||
|
Port = mkDefault c.port;
|
||||||
|
IPv4 = mkDefault true;
|
||||||
|
IPv6 = mkDefault true;
|
||||||
|
SSL = mkDefault c.useSSL;
|
||||||
|
};
|
||||||
|
User.${c.userName} = {
|
||||||
|
Admin = mkDefault true;
|
||||||
|
Nick = mkDefault c.nick;
|
||||||
|
AltNick = mkDefault "${c.nick}_";
|
||||||
|
Ident = mkDefault c.nick;
|
||||||
|
RealName = mkDefault c.nick;
|
||||||
|
LoadModule = mkDefault c.userModules;
|
||||||
|
Network = mapAttrs (name: net: {
|
||||||
|
LoadModule = mkDefault net.modules;
|
||||||
|
Server = mkDefault "${net.server} ${optionalString net.useSSL "+"}${toString net.port} ${net.password}";
|
||||||
|
Chan = optionalAttrs net.hasBitlbeeControlChannel { "&bitlbee" = mkDefault {}; } //
|
||||||
|
listToAttrs (map (n: nameValuePair "#${n}" (mkDefault {})) net.channels);
|
||||||
|
extraConfig = if net.extraConf == "" then mkDefault null else net.extraConf;
|
||||||
|
}) c.networks;
|
||||||
|
extraConfig = [ c.passBlock ] ++ optional (c.extraZncConf != "") c.extraZncConf;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
imports = [
|
||||||
|
(mkRemovedOptionModule ["services" "znc" "zncConf"] ''
|
||||||
|
Instead of `services.znc.zncConf = "... foo ...";`, use
|
||||||
|
`services.znc.configFile = pkgs.writeText "znc.conf" "... foo ...";`.
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
}
|
Loading…
Reference in a new issue