diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 7e19f00f909..79c1c1e000c 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -135,6 +135,7 @@
influxdb = 125;
nsd = 126;
gitolite = 127;
+ znc = 128;
# When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
@@ -243,6 +244,7 @@
teamspeak = 124;
influxdb = 125;
nsd = 126;
+ znc = 128;
# When adding a gid, make sure it doesn't match an existing uid. And don't use gids above 399!
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 94180372afe..a9039eea71d 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -237,6 +237,7 @@
./services/networking/wicd.nix
./services/networking/wpa_supplicant.nix
./services/networking/xinetd.nix
+ ./services/networking/znc.nix
./services/printing/cupsd.nix
./services/scheduling/atd.nix
./services/scheduling/cron.nix
diff --git a/nixos/modules/services/networking/znc.nix b/nixos/modules/services/networking/znc.nix
new file mode 100644
index 00000000000..a40fd924741
--- /dev/null
+++ b/nixos/modules/services/networking/znc.nix
@@ -0,0 +1,294 @@
+{ 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 = "
+
+ Method = sha256
+ Hash = e2ce303c7ea75c571d80d8540a8699b46535be6a085be3414947d638e48d9e93
+ Salt = l5Xryew4g*!oa(ECfX2o
+
+ ";
+
+ confOptions = { ... }: {
+ options = {
+ modules = mkOption {
+ type = types.listOf types.string;
+ default = [ "partyline" "webadmin" "adminlog" "log" ];
+ example = [ "partyline" "webadmin" "adminlog" "log" ];
+ description = ''
+ A list of modules to include in the `znc.conf` file.
+ '';
+ };
+
+ userName = mkOption {
+ default = defaultUserName;
+ example = "johntron";
+ type = types.string;
+ description = ''
+ The user name to use when generating the `znc.conf` file.
+ This is the user name used by the user logging into the ZNC web admin.
+ '';
+ };
+
+ nick = mkOption {
+ default = "znc-user";
+ example = "john";
+ type = types.string;
+ description = ''
+ The IRC nick to use when generating the `znc.conf` file.
+ '';
+ };
+
+ passBlock = mkOption {
+ default = defaultPassBlock;
+ example = "Must be the block generated by the `znc --makepass` command.";
+ type = types.string;
+ description = ''
+ The pass block to use when generating the `znc.conf` file.
+ This is the password used by the user logging into the ZNC web admin.
+ This is the block generated by the `znc --makepass` command.
+ !!! If not specified, please change this after starting the service. !!!
+ '';
+ };
+
+ port = mkOption {
+ default = "5000";
+ example = "5000";
+ type = types.string;
+ description = ''
+ Specifies the port on which to listen.
+ '';
+ };
+
+ useSSL = mkOption {
+ default = true;
+ example = true;
+ type = types.bool;
+ description = ''
+ Indicates whether the ZNC server should use SSL when listening on the specified port.
+ '';
+ };
+
+ };
+ };
+
+ # Keep znc.conf in nix store, then symlink or copy into `dataDir`, depending on `mutable`.
+ mkZncConf = confOpts: ''
+ // Also check http://en.znc.in/wiki/Configuration
+
+ AnonIPLimit = 10
+ ConnectDelay = 5
+ # Add `LoadModule = x` for each module...
+ ${concatMapStrings (n: "LoadModule = ${n}\n") confOpts.modules}
+ MaxBufferSize = 500
+ ProtectWebSessions = true
+ SSLCertFile = ${cfg.dataDir}/znc.pem
+ ServerThrottle = 30
+ Skin = dark-clouds
+ StatusPrefix = *
+ Version = 1.2
+
+
+ AllowIRC = true
+ AllowWeb = true
+ IPv4 = true
+ IPv6 = false
+ Port = ${if confOpts.useSSL then "+" else ""}${confOpts.port}
+ SSL = ${if confOpts.useSSL then "true" else "false"}
+
+
+
+ Admin = true
+ Allow = *
+ AltNick = ${confOpts.nick}_
+ AppendTimestamp = false
+ AutoClearChanBuffer = false
+ Buffer = 150
+ ChanModes = +stn
+ DenyLoadMod = false
+ DenySetBindHost = false
+ Ident = ident
+ JoinTries = 10
+ MaxJoins = 0
+ MaxNetworks = 1
+ MultiClients = true
+ Nick = ${confOpts.nick}
+ PrependTimestamp = true
+ QuitMsg = Quit
+ RealName = ${confOpts.nick}
+ TimestampFormat = [%H:%M:%S]
+
+ ${confOpts.passBlock}
+
+ '';
+
+ zncConfFile = pkgs.writeTextFile {
+ name = "znc.conf";
+ text = if cfg.zncConf != ""
+ then cfg.zncConf
+ else mkZncConf cfg.confOptions;
+ };
+
+in
+
+{
+
+ ###### Interface
+
+ options = {
+ services.znc = {
+ enable = mkOption {
+ default = false;
+ example = true;
+ 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.
+ '';
+ };
+
+ dataDir = mkOption {
+ default = "/home/${cfg.user}/.znc";
+ example = "/home/john/.znc";
+ type = types.string;
+ description = ''
+ The data directory. Used for configuration files and modules.
+ '';
+ };
+
+ zncConf = mkOption {
+ default = "";
+ example = "See: http://wiki.znc.in/Configuration";
+ type = types.string;
+ description = ''
+ The contents of the `znc.conf` file to use when creating it.
+ 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.
+ Recommended to generate with `znc --makeconf` command.
+ '';
+ };
+
+ confOptions = mkOption {
+ default = {};
+ example = {
+ modules = [ "log" ];
+ userName = "john";
+ nick = "johntron";
+ };
+ type = types.optionSet;
+ description = ''
+ Values to use when creating a `znc.conf` file.
+ '';
+ options = confOptions;
+ };
+
+ mutable = mkOption {
+ default = false;
+ example = 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.string;
+ description = ''
+ Extra flags to use when executing znc command.
+ '';
+ };
+ };
+ };
+
+
+ ###### Implementation
+
+ config = mkIf cfg.enable {
+
+ systemd.services."znc-${cfg.user}" = {
+ description = "ZNC Server of ${cfg.user}.";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.service" ];
+ path = [ pkgs.znc ];
+ serviceConfig = {
+ User = "${cfg.user}";
+ Restart = "always";
+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
+ };
+ preStart = ''
+ ${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}
+ ${pkgs.coreutils}/bin/chown ${cfg.user} ${cfg.dataDir} -R
+ ${pkgs.coreutils}/bin/mkdir -p ${cfg.dataDir}/configs
+
+ # If mutable, regenerate conf file every time.
+ ${optionalString (!cfg.mutable) ''
+ ${pkgs.coreutils}/echo "znc-${cfg.user} is set to be system-managed. Now deleting old znc.conf file to be regenerated."
+ ${pkgs.coreutils}/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."
+ ${if (!cfg.mutable)
+ then "${pkgs.coreutils}/bin/ln --force -s ${zncConfFile} ${cfg.dataDir}/configs/znc.conf"
+ else ''
+ ${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
+ fi
+ '';
+ script = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${cfg.extraFlags}";
+ };
+
+ users.extraUsers = optional (cfg.user == defaultUser)
+ { name = defaultUser;
+ description = "ZNC server daemon owner";
+ group = defaultUser;
+ uid = config.ids.uids.znc;
+ createHome = true;
+ createUser = true;
+ };
+
+ users.extraGroups = optional (cfg.user == defaultUser)
+ { name = defaultUser;
+ gid = config.ids.gids.znc;
+ members = [ defaultUser ];
+ };
+
+ };
+}