diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services/networking')
220 files changed, 33637 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/networking/3proxy.nix b/nixpkgs/nixos/modules/services/networking/3proxy.nix new file mode 100644 index 000000000000..ae8a4958ca96 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/3proxy.nix @@ -0,0 +1,426 @@ +{ config, lib, pkgs, ... }: +with lib; +let + pkg = pkgs._3proxy; + cfg = config.services._3proxy; + optionalList = list: if list == [ ] then "*" else concatMapStringsSep "," toString list; +in { + options.services._3proxy = { + enable = mkEnableOption "3proxy"; + confFile = mkOption { + type = types.path; + example = "/var/lib/3proxy/3proxy.conf"; + description = '' + Ignore all other 3proxy options and load configuration from this file. + ''; + }; + usersFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/lib/3proxy/3proxy.passwd"; + description = '' + Load users and passwords from this file. + + Example users file with plain-text passwords: + + <literal> + test1:CL:password1 + test2:CL:password2 + </literal> + + Example users file with md5-crypted passwords: + + <literal> + test1:CR:$1$tFkisVd2$1GA8JXkRmTXdLDytM/i3a1 + test2:CR:$1$rkpibm5J$Aq1.9VtYAn0JrqZ8M.1ME. + </literal> + + You can generate md5-crypted passwords via https://unix4lyfe.org/crypt/ + Note that htpasswd tool generates incompatible md5-crypted passwords. + Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/How-To-(incomplete)#USERS">documentation</link> for more information. + ''; + }; + services = mkOption { + type = types.listOf (types.submodule { + options = { + type = mkOption { + type = types.enum [ + "proxy" + "socks" + "pop3p" + "ftppr" + "admin" + "dnspr" + "tcppm" + "udppm" + ]; + example = "proxy"; + description = '' + Service type. The following values are valid: + + <itemizedlist> + <listitem><para> + <literal>"proxy"</literal>: HTTP/HTTPS proxy (default port 3128). + </para></listitem> + <listitem><para> + <literal>"socks"</literal>: SOCKS 4/4.5/5 proxy (default port 1080). + </para></listitem> + <listitem><para> + <literal>"pop3p"</literal>: POP3 proxy (default port 110). + </para></listitem> + <listitem><para> + <literal>"ftppr"</literal>: FTP proxy (default port 21). + </para></listitem> + <listitem><para> + <literal>"admin"</literal>: Web interface (default port 80). + </para></listitem> + <listitem><para> + <literal>"dnspr"</literal>: Caching DNS proxy (default port 53). + </para></listitem> + <listitem><para> + <literal>"tcppm"</literal>: TCP portmapper. + </para></listitem> + <listitem><para> + <literal>"udppm"</literal>: UDP portmapper. + </para></listitem> + </itemizedlist> + ''; + }; + bindAddress = mkOption { + type = types.str; + default = "[::]"; + example = "127.0.0.1"; + description = '' + Address used for service. + ''; + }; + bindPort = mkOption { + type = types.nullOr types.int; + default = null; + example = 3128; + description = '' + Override default port used for service. + ''; + }; + maxConnections = mkOption { + type = types.int; + default = 100; + example = 1000; + description = '' + Maximum number of simulationeous connections to this service. + ''; + }; + auth = mkOption { + type = types.listOf (types.enum [ "none" "iponly" "strong" ]); + example = [ "iponly" "strong" ]; + description = '' + Authentication type. The following values are valid: + + <itemizedlist> + <listitem><para> + <literal>"none"</literal>: disables both authentication and authorization. You can not use ACLs. + </para></listitem> + <listitem><para> + <literal>"iponly"</literal>: specifies no authentication. ACLs authorization is used. + </para></listitem> + <listitem><para> + <literal>"strong"</literal>: authentication by username/password. If user is not registered his access is denied regardless of ACLs. + </para></listitem> + </itemizedlist> + + Double authentication is possible, e.g. + + <literal> + { + auth = [ "iponly" "strong" ]; + acl = [ + { + rule = "allow"; + targets = [ "192.168.0.0/16" ]; + } + { + rule = "allow" + users = [ "user1" "user2" ]; + } + ]; + } + </literal> + In this example strong username authentication is not required to access 192.168.0.0/16. + ''; + }; + acl = mkOption { + type = types.listOf (types.submodule { + options = { + rule = mkOption { + type = types.enum [ "allow" "deny" ]; + example = "allow"; + description = '' + ACL rule. The following values are valid: + + <itemizedlist> + <listitem><para> + <literal>"allow"</literal>: connections allowed. + </para></listitem> + <listitem><para> + <literal>"deny"</literal>: connections not allowed. + </para></listitem> + </itemizedlist> + ''; + }; + users = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "user1" "user2" "user3" ]; + description = '' + List of users, use empty list for any. + ''; + }; + sources = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "127.0.0.1" "192.168.1.0/24" ]; + description = '' + List of source IP range, use empty list for any. + ''; + }; + targets = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "127.0.0.1" "192.168.1.0/24" ]; + description = '' + List of target IP ranges, use empty list for any. + May also contain host names instead of addresses. + It's possible to use wildmask in the begginning and in the the end of hostname, e.g. *badsite.com or *badcontent*. + Hostname is only checked if hostname presents in request. + ''; + }; + targetPorts = mkOption { + type = types.listOf types.int; + default = [ ]; + example = [ 80 443 ]; + description = '' + List of target ports, use empty list for any. + ''; + }; + }; + }); + default = [ ]; + example = literalExample '' + [ + { + rule = "allow"; + users = [ "user1" ]; + } + { + rule = "allow"; + sources = [ "192.168.1.0/24" ]; + } + { + rule = "deny"; + } + ] + ''; + description = '' + Use this option to limit user access to resources. + ''; + }; + extraArguments = mkOption { + type = types.nullOr types.str; + default = null; + example = "-46"; + description = '' + Extra arguments for service. + Consult "Options" section in <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available arguments. + ''; + }; + extraConfig = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + Extra configuration for service. Use this to configure things like bandwidth limiter or ACL-based redirection. + Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available options. + ''; + }; + }; + }); + default = [ ]; + example = literalExample '' + [ + { + type = "proxy"; + bindAddress = "192.168.1.24"; + bindPort = 3128; + auth = [ "none" ]; + } + { + type = "proxy"; + bindAddress = "10.10.1.20"; + bindPort = 3128; + auth = [ "iponly" ]; + } + { + type = "socks"; + bindAddress = "172.17.0.1"; + bindPort = 1080; + auth = [ "strong" ]; + } + ] + ''; + description = '' + Use this option to define 3proxy services. + ''; + }; + denyPrivate = mkOption { + type = types.bool; + default = true; + description = '' + Whether to deny access to private IP ranges including loopback. + ''; + }; + privateRanges = mkOption { + type = types.listOf types.str; + default = [ + "0.0.0.0/8" + "127.0.0.0/8" + "10.0.0.0/8" + "100.64.0.0/10" + "172.16.0.0/12" + "192.168.0.0/16" + "::" + "::1" + "fc00::/7" + ]; + example = [ + "0.0.0.0/8" + "127.0.0.0/8" + "10.0.0.0/8" + "100.64.0.0/10" + "172.16.0.0/12" + "192.168.0.0/16" + "::" + "::1" + "fc00::/7" + ]; + description = '' + What IP ranges to deny access when denyPrivate is set tu true. + ''; + }; + resolution = mkOption { + type = types.submodule { + options = { + nserver = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "127.0.0.53" "192.168.1.3:5353/tcp" ]; + description = '' + List of nameservers to use. + + Up to 5 nservers may be specified. If no nserver is configured, + default system name resolution functions are used. + ''; + }; + nscache = mkOption { + type = types.int; + default = 65535; + example = 65535; + description = "Set name cache size for IPv4."; + }; + nscache6 = mkOption { + type = types.int; + default = 65535; + example = 65535; + description = "Set name cache size for IPv6."; + }; + nsrecord = mkOption { + type = types.attrsOf types.str; + default = { }; + example = literalExample '' + { + "files.local" = "192.168.1.12"; + "site.local" = "192.168.1.43"; + } + ''; + description = "Adds static nsrecords."; + }; + }; + }; + default = { }; + description = '' + Use this option to configure name resolution and DNS caching. + ''; + }; + extraConfig = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + Extra configuration, appended to the 3proxy configuration file. + Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available options. + ''; + }; + }; + + config = mkIf cfg.enable { + services._3proxy.confFile = mkDefault (pkgs.writeText "3proxy.conf" '' + # log to stdout + log + + ${concatMapStringsSep "\n" (x: "nserver " + x) cfg.resolution.nserver} + + nscache ${toString cfg.resolution.nscache} + nscache6 ${toString cfg.resolution.nscache6} + + ${concatMapStringsSep "\n" (x: "nsrecord " + x) + (mapAttrsToList (name: value: "${name} ${value}") + cfg.resolution.nsrecord)} + + ${optionalString (cfg.usersFile != null) + ''users $"${cfg.usersFile}"'' + } + + ${concatMapStringsSep "\n" (service: '' + auth ${concatStringsSep " " service.auth} + + ${optionalString (cfg.denyPrivate) + "deny * * ${optionalList cfg.privateRanges}"} + + ${concatMapStringsSep "\n" (acl: + "${acl.rule} ${ + concatMapStringsSep " " optionalList [ + acl.users + acl.sources + acl.targets + acl.targetPorts + ] + }") service.acl} + + maxconn ${toString service.maxConnections} + + ${optionalString (service.extraConfig != null) service.extraConfig} + + ${service.type} -i${toString service.bindAddress} ${ + optionalString (service.bindPort != null) + "-p${toString service.bindPort}" + } ${ + optionalString (service.extraArguments != null) service.extraArguments + } + + flush + '') cfg.services} + ${optionalString (cfg.extraConfig != null) cfg.extraConfig} + ''); + systemd.services."3proxy" = { + description = "Tiny free proxy server"; + documentation = [ "https://github.com/z3APA3A/3proxy/wiki" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + DynamicUser = true; + StateDirectory = "3proxy"; + ExecStart = "${pkg}/bin/3proxy ${cfg.confFile}"; + Restart = "on-failure"; + }; + }; + }; + + meta.maintainers = with maintainers; [ misuzu ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/amuled.nix b/nixpkgs/nixos/modules/services/networking/amuled.nix new file mode 100644 index 000000000000..1128ee2c3e61 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/amuled.nix @@ -0,0 +1,77 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.amule; + user = if cfg.user != null then cfg.user else "amule"; +in + +{ + + ###### interface + + options = { + + services.amule = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run the AMule daemon. You need to manually run "amuled --ec-config" to configure the service for the first time. + ''; + }; + + dataDir = mkOption { + default = ''/home/${user}/''; + description = '' + The directory holding configuration, incoming and temporary files. + ''; + }; + + user = mkOption { + default = null; + description = '' + The user the AMule daemon should run as. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.users = mkIf (cfg.user == null) [ + { name = "amule"; + description = "AMule daemon"; + group = "amule"; + uid = config.ids.uids.amule; + } ]; + + users.groups = mkIf (cfg.user == null) [ + { name = "amule"; + gid = config.ids.gids.amule; + } ]; + + systemd.services.amuled = { + description = "AMule daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + preStart = '' + mkdir -p ${cfg.dataDir} + chown ${user} ${cfg.dataDir} + ''; + + script = '' + ${pkgs.su}/bin/su -s ${pkgs.runtimeShell} ${user} \ + -c 'HOME="${cfg.dataDir}" ${pkgs.amuleDaemon}/bin/amuled' + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/aria2.nix b/nixpkgs/nixos/modules/services/networking/aria2.nix new file mode 100644 index 000000000000..156fef144791 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/aria2.nix @@ -0,0 +1,131 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.aria2; + + homeDir = "/var/lib/aria2"; + + settingsDir = "${homeDir}"; + sessionFile = "${homeDir}/aria2.session"; + downloadDir = "${homeDir}/Downloads"; + + rangesToStringList = map (x: builtins.toString x.from +"-"+ builtins.toString x.to); + + settingsFile = pkgs.writeText "aria2.conf" + '' + dir=${cfg.downloadDir} + listen-port=${concatStringsSep "," (rangesToStringList cfg.listenPortRange)} + rpc-listen-port=${toString cfg.rpcListenPort} + rpc-secret=${cfg.rpcSecret} + ''; + +in +{ + options = { + services.aria2 = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether or not to enable the headless Aria2 daemon service. + + Aria2 daemon can be controlled via the RPC interface using + one of many WebUI (http://localhost:6800/ by default). + + Targets are downloaded to ${downloadDir} by default and are + accessible to users in the "aria2" group. + ''; + }; + openPorts = mkOption { + type = types.bool; + default = false; + description = '' + Open listen and RPC ports found in listenPortRange and rpcListenPort + options in the firewall. + ''; + }; + downloadDir = mkOption { + type = types.path; + default = downloadDir; + description = '' + Directory to store downloaded files. + ''; + }; + listenPortRange = mkOption { + type = types.listOf types.attrs; + default = [ { from = 6881; to = 6999; } ]; + description = '' + Set UDP listening port range used by DHT(IPv4, IPv6) and UDP tracker. + ''; + }; + rpcListenPort = mkOption { + type = types.int; + default = 6800; + description = "Specify a port number for JSON-RPC/XML-RPC server to listen to. Possible Values: 1024-65535"; + }; + rpcSecret = mkOption { + type = types.str; + default = "aria2rpc"; + description = '' + Set RPC secret authorization token. + Read https://aria2.github.io/manual/en/html/aria2c.html#rpc-auth to know how this option value is used. + ''; + }; + extraArguments = mkOption { + type = types.separatedString " "; + example = "--rpc-listen-all --remote-time=true"; + default = ""; + description = '' + Additional arguments to be passed to Aria2. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + # Need to open ports for proper functioning + networking.firewall = mkIf cfg.openPorts { + allowedUDPPortRanges = config.services.aria2.listenPortRange; + allowedTCPPorts = [ config.services.aria2.rpcListenPort ]; + }; + + users.users.aria2 = { + group = "aria2"; + uid = config.ids.uids.aria2; + description = "aria2 user"; + home = homeDir; + createHome = false; + }; + + users.groups.aria2.gid = config.ids.gids.aria2; + + systemd.tmpfiles.rules = [ + "d '${homeDir}' 0770 aria2 aria2 - -" + "d '${config.services.aria2.downloadDir}' 0770 aria2 aria2 - -" + ]; + + systemd.services.aria2 = { + description = "aria2 Service"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + if [[ ! -e "${sessionFile}" ]] + then + touch "${sessionFile}" + fi + cp -f "${settingsFile}" "${settingsDir}/aria2.conf" + ''; + + serviceConfig = { + Restart = "on-abort"; + ExecStart = "${pkgs.aria2}/bin/aria2c --enable-rpc --conf-path=${settingsDir}/aria2.conf ${config.services.aria2.extraArguments} --save-session=${sessionFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + User = "aria2"; + Group = "aria2"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/asterisk.nix b/nixpkgs/nixos/modules/services/networking/asterisk.nix new file mode 100644 index 000000000000..03a2544b9a7e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/asterisk.nix @@ -0,0 +1,264 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.asterisk; + + asteriskUser = "asterisk"; + asteriskGroup = "asterisk"; + + varlibdir = "/var/lib/asterisk"; + spooldir = "/var/spool/asterisk"; + logdir = "/var/log/asterisk"; + + # Add filecontents from files of useTheseDefaultConfFiles to confFiles, do not override + defaultConfFiles = subtractLists (attrNames cfg.confFiles) cfg.useTheseDefaultConfFiles; + allConfFiles = + cfg.confFiles // + builtins.listToAttrs (map (x: { name = x; + value = builtins.readFile (cfg.package + "/etc/asterisk/" + x); }) + defaultConfFiles); + + asteriskEtc = pkgs.stdenv.mkDerivation + ((mapAttrs' (name: value: nameValuePair + # Fudge the names to make bash happy + ((replaceChars ["."] ["_"] name) + "_") + (value) + ) allConfFiles) // + { + confFilesString = concatStringsSep " " ( + attrNames allConfFiles + ); + + name = "asterisk-etc"; + + # Default asterisk.conf file + # (Notice that astetcdir will be set to the path of this derivation) + asteriskConf = '' + [directories] + astetcdir => /etc/asterisk + astmoddir => ${cfg.package}/lib/asterisk/modules + astvarlibdir => /var/lib/asterisk + astdbdir => /var/lib/asterisk + astkeydir => /var/lib/asterisk + astdatadir => /var/lib/asterisk + astagidir => /var/lib/asterisk/agi-bin + astspooldir => /var/spool/asterisk + astrundir => /run/asterisk + astlogdir => /var/log/asterisk + astsbindir => ${cfg.package}/sbin + ''; + extraConf = cfg.extraConfig; + + # Loading all modules by default is considered sensible by the authors of + # "Asterisk: The Definitive Guide". Secure sites will likely want to + # specify their own "modules.conf" in the confFiles option. + modulesConf = '' + [modules] + autoload=yes + ''; + + # Use syslog for logging so logs can be viewed with journalctl + loggerConf = '' + [general] + + [logfiles] + syslog.local0 => notice,warning,error + ''; + + buildCommand = '' + mkdir -p "$out" + + # Create asterisk.conf, pointing astetcdir to the path of this derivation + echo "$asteriskConf" | sed "s|@out@|$out|g" > "$out"/asterisk.conf + echo "$extraConf" >> "$out"/asterisk.conf + + echo "$modulesConf" > "$out"/modules.conf + + echo "$loggerConf" > "$out"/logger.conf + + # Config files specified in confFiles option override all other files + for i in $confFilesString; do + conf=$(echo "$i"_ | sed 's/\./_/g') + echo "''${!conf}" > "$out"/"$i" + done + ''; + }); +in + +{ + options = { + services.asterisk = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the Asterisk PBX server. + ''; + }; + + extraConfig = mkOption { + default = ""; + type = types.lines; + example = '' + [options] + verbose=3 + debug=3 + ''; + description = '' + Extra configuration options appended to the default + <literal>asterisk.conf</literal> file. + ''; + }; + + confFiles = mkOption { + default = {}; + type = types.attrsOf types.str; + example = literalExample + '' + { + "extensions.conf" = ''' + [tests] + ; Dial 100 for "hello, world" + exten => 100,1,Answer() + same => n,Wait(1) + same => n,Playback(hello-world) + same => n,Hangup() + + [softphones] + include => tests + + [unauthorized] + '''; + "sip.conf" = ''' + [general] + allowguest=no ; Require authentication + context=unauthorized ; Send unauthorized users to /dev/null + srvlookup=no ; Don't do DNS lookup + udpbindaddr=0.0.0.0 ; Listen on all interfaces + nat=force_rport,comedia ; Assume device is behind NAT + + [softphone](!) + type=friend ; Match on username first, IP second + context=softphones ; Send to softphones context in + ; extensions.conf file + host=dynamic ; Device will register with asterisk + disallow=all ; Manually specify codecs to allow + allow=g722 + allow=ulaw + allow=alaw + + [myphone](softphone) + secret=GhoshevFew ; Change this password! + '''; + "logger.conf" = ''' + [general] + + [logfiles] + ; Add debug output to log + syslog.local0 => notice,warning,error,debug + '''; + } + ''; + description = '' + Sets the content of config files (typically ending with + <literal>.conf</literal>) in the Asterisk configuration directory. + + Note that if you want to change <literal>asterisk.conf</literal>, it + is preferable to use the <option>services.asterisk.extraConfig</option> + option over this option. If <literal>"asterisk.conf"</literal> is + specified with the <option>confFiles</option> option (not recommended), + you must be prepared to set your own <literal>astetcdir</literal> + path. + + See + <link xlink:href="http://www.asterisk.org/community/documentation"/> + for more examples of what is possible here. + ''; + }; + + useTheseDefaultConfFiles = mkOption { + default = [ "ari.conf" "acl.conf" "agents.conf" "amd.conf" "calendar.conf" "cdr.conf" "cdr_syslog.conf" "cdr_custom.conf" "cel.conf" "cel_custom.conf" "cli_aliases.conf" "confbridge.conf" "dundi.conf" "features.conf" "hep.conf" "iax.conf" "pjsip.conf" "pjsip_wizard.conf" "phone.conf" "phoneprov.conf" "queues.conf" "res_config_sqlite3.conf" "res_parking.conf" "statsd.conf" "udptl.conf" "unistim.conf" ]; + type = types.listOf types.str; + example = [ "sip.conf" "dundi.conf" ]; + description = ''Sets these config files to the default content. The default value for + this option contains all necesscary files to avoid errors at startup. + This does not override settings via <option>services.asterisk.confFiles</option>. + ''; + }; + + extraArguments = mkOption { + default = []; + type = types.listOf types.str; + example = + [ "-vvvddd" "-e" "1024" ]; + description = '' + Additional command line arguments to pass to Asterisk. + ''; + }; + package = mkOption { + type = types.package; + default = pkgs.asterisk; + defaultText = "pkgs.asterisk"; + description = "The Asterisk package to use."; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + environment.etc.asterisk.source = asteriskEtc; + + users.users.asterisk = + { name = asteriskUser; + group = asteriskGroup; + uid = config.ids.uids.asterisk; + description = "Asterisk daemon user"; + home = varlibdir; + }; + + users.groups.asterisk = + { name = asteriskGroup; + gid = config.ids.gids.asterisk; + }; + + systemd.services.asterisk = { + description = '' + Asterisk PBX server + ''; + + wantedBy = [ "multi-user.target" ]; + + # Do not restart, to avoid disruption of running calls. Restart unit by yourself! + restartIfChanged = false; + + preStart = '' + # Copy skeleton directory tree to /var + for d in '${varlibdir}' '${spooldir}' '${logdir}'; do + # TODO: Make exceptions for /var directories that likely should be updated + if [ ! -e "$d" ]; then + mkdir -p "$d" + cp --recursive ${cfg.package}/"$d"/* "$d"/ + chown --recursive ${asteriskUser}:${asteriskGroup} "$d" + find "$d" -type d | xargs chmod 0755 + fi + done + ''; + + serviceConfig = { + ExecStart = + let + # FIXME: This doesn't account for arguments with spaces + argString = concatStringsSep " " cfg.extraArguments; + in + "${cfg.package}/bin/asterisk -U ${asteriskUser} -C /etc/asterisk/asterisk.conf ${argString} -F"; + ExecReload = ''${cfg.package}/bin/asterisk -x "core reload" + ''; + Type = "forking"; + PIDFile = "/run/asterisk/asterisk.pid"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/atftpd.nix b/nixpkgs/nixos/modules/services/networking/atftpd.nix new file mode 100644 index 000000000000..e7fd48c99a85 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/atftpd.nix @@ -0,0 +1,65 @@ +# NixOS module for atftpd TFTP server + +{ config, pkgs, lib, ... }: + +with lib; + +let + + cfg = config.services.atftpd; + +in + +{ + + options = { + + services.atftpd = { + + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable the atftpd TFTP server. By default, the server + binds to address 0.0.0.0. + ''; + }; + + extraOptions = mkOption { + default = []; + type = types.listOf types.str; + example = literalExample '' + [ "--bind-address 192.168.9.1" + "--verbose=7" + ] + ''; + description = '' + Extra command line arguments to pass to atftp. + ''; + }; + + root = mkOption { + default = "/srv/tftp"; + type = types.path; + description = '' + Document root directory for the atftpd. + ''; + }; + + }; + + }; + + config = mkIf cfg.enable { + + systemd.services.atftpd = { + description = "TFTP Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + # runs as nobody + serviceConfig.ExecStart = "${pkgs.atftp}/sbin/atftpd --daemon --no-fork ${lib.concatStringsSep " " cfg.extraOptions} ${cfg.root}"; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/autossh.nix b/nixpkgs/nixos/modules/services/networking/autossh.nix new file mode 100644 index 000000000000..a8d9a027e9fa --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/autossh.nix @@ -0,0 +1,113 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.autossh; + +in + +{ + + ###### interface + + options = { + + services.autossh = { + + sessions = mkOption { + type = types.listOf (types.submodule { + options = { + name = mkOption { + type = types.str; + example = "socks-peer"; + description = "Name of the local AutoSSH session"; + }; + user = mkOption { + type = types.str; + example = "bill"; + description = "Name of the user the AutoSSH session should run as"; + }; + monitoringPort = mkOption { + type = types.int; + default = 0; + example = 20000; + description = '' + Port to be used by AutoSSH for peer monitoring. Note, that + AutoSSH also uses mport+1. Value of 0 disables the keep-alive + style monitoring + ''; + }; + extraArguments = mkOption { + type = types.separatedString " "; + example = "-N -D4343 bill@socks.example.net"; + description = '' + Arguments to be passed to AutoSSH and retransmitted to SSH + process. Some meaningful options include -N (don't run remote + command), -D (open SOCKS proxy on local port), -R (forward + remote port), -L (forward local port), -v (Enable debug). Check + ssh manual for the complete list. + ''; + }; + }; + }); + + default = []; + description = '' + List of AutoSSH sessions to start as systemd services. Each service is + named 'autossh-{session.name}'. + ''; + + example = [ + { + name="socks-peer"; + user="bill"; + monitoringPort = 20000; + extraArguments="-N -D4343 billremote@socks.host.net"; + } + ]; + + }; + }; + + }; + + ###### implementation + + config = mkIf (cfg.sessions != []) { + + systemd.services = + + lib.fold ( s : acc : acc // + { + "autossh-${s.name}" = + let + mport = if s ? monitoringPort then s.monitoringPort else 0; + in + { + description = "AutoSSH session (" + s.name + ")"; + + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + # To be able to start the service with no network connection + environment.AUTOSSH_GATETIME="0"; + + # How often AutoSSH checks the network, in seconds + environment.AUTOSSH_POLL="30"; + + serviceConfig = { + User = "${s.user}"; + # AutoSSH may exit with 0 code if the SSH session was + # gracefully terminated by either local or remote side. + Restart = "on-success"; + ExecStart = "${pkgs.autossh}/bin/autossh -M ${toString mport} ${s.extraArguments}"; + }; + }; + }) {} cfg.sessions; + + environment.systemPackages = [ pkgs.autossh ]; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix new file mode 100644 index 000000000000..c876b252e8cd --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix @@ -0,0 +1,285 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.avahi; + + yesNo = yes : if yes then "yes" else "no"; + + avahiDaemonConf = with cfg; pkgs.writeText "avahi-daemon.conf" '' + [server] + ${# Users can set `networking.hostName' to the empty string, when getting + # a host name from DHCP. In that case, let Avahi take whatever the + # current host name is; setting `host-name' to the empty string in + # `avahi-daemon.conf' would be invalid. + optionalString (hostName != "") "host-name=${hostName}"} + browse-domains=${concatStringsSep ", " browseDomains} + use-ipv4=${yesNo ipv4} + use-ipv6=${yesNo ipv6} + ${optionalString (interfaces!=null) "allow-interfaces=${concatStringsSep "," interfaces}"} + ${optionalString (domainName!=null) "domain-name=${domainName}"} + allow-point-to-point=${yesNo allowPointToPoint} + ${optionalString (cacheEntriesMax!=null) "cache-entries-max=${toString cacheEntriesMax}"} + + [wide-area] + enable-wide-area=${yesNo wideArea} + + [publish] + disable-publishing=${yesNo (!publish.enable)} + disable-user-service-publishing=${yesNo (!publish.userServices)} + publish-addresses=${yesNo (publish.userServices || publish.addresses)} + publish-hinfo=${yesNo publish.hinfo} + publish-workstation=${yesNo publish.workstation} + publish-domain=${yesNo publish.domain} + + [reflector] + enable-reflector=${yesNo reflector} + ${extraConfig} + ''; +in +{ + options.services.avahi = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run the Avahi daemon, which allows Avahi clients + to use Avahi's service discovery facilities and also allows + the local machine to advertise its presence and services + (through the mDNS responder implemented by `avahi-daemon'). + ''; + }; + + hostName = mkOption { + type = types.str; + default = config.networking.hostName; + defaultText = literalExample "config.networking.hostName"; + description = '' + Host name advertised on the LAN. If not set, avahi will use the value + of <option>config.networking.hostName</option>. + ''; + }; + + domainName = mkOption { + type = types.str; + default = "local"; + description = '' + Domain name for all advertisements. + ''; + }; + + browseDomains = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "0pointer.de" "zeroconf.org" ]; + description = '' + List of non-local DNS domains to be browsed. + ''; + }; + + ipv4 = mkOption { + type = types.bool; + default = true; + description = "Whether to use IPv4."; + }; + + ipv6 = mkOption { + type = types.bool; + default = false; + description = "Whether to use IPv6."; + }; + + interfaces = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + description = '' + List of network interfaces that should be used by the <command>avahi-daemon</command>. + Other interfaces will be ignored. If <literal>null</literal>, all local interfaces + except loopback and point-to-point will be used. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = true; + description = '' + Whether to open the firewall for UDP port 5353. + ''; + }; + + allowPointToPoint = mkOption { + type = types.bool; + default = false; + description= '' + Whether to use POINTTOPOINT interfaces. Might make mDNS unreliable due to usually large + latencies with such links and opens a potential security hole by allowing mDNS access from Internet + connections. + ''; + }; + + wideArea = mkOption { + type = types.bool; + default = true; + description = "Whether to enable wide-area service discovery."; + }; + + reflector = mkOption { + type = types.bool; + default = false; + description = "Reflect incoming mDNS requests to all allowed network interfaces."; + }; + + extraServiceFiles = mkOption { + type = with types; attrsOf (either str path); + default = {}; + example = literalExample '' + { + ssh = "''${pkgs.avahi}/etc/avahi/services/ssh.service"; + smb = ''' + <?xml version="1.0" standalone='no'?><!--*-nxml-*--> + <!DOCTYPE service-group SYSTEM "avahi-service.dtd"> + <service-group> + <name replace-wildcards="yes">%h</name> + <service> + <type>_smb._tcp</type> + <port>445</port> + </service> + </service-group> + '''; + } + ''; + description = '' + Specify custom service definitions which are placed in the avahi service directory. + See the <citerefentry><refentrytitle>avahi.service</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> manpage for detailed information. + ''; + }; + + publish = { + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to allow publishing in general."; + }; + + userServices = mkOption { + type = types.bool; + default = false; + description = "Whether to publish user services. Will set <literal>addresses=true</literal>."; + }; + + addresses = mkOption { + type = types.bool; + default = false; + description = "Whether to register mDNS address records for all local IP addresses."; + }; + + hinfo = mkOption { + type = types.bool; + default = false; + description = '' + Whether to register a mDNS HINFO record which contains information about the + local operating system and CPU. + ''; + }; + + workstation = mkOption { + type = types.bool; + default = false; + description = '' + Whether to register a service of type "_workstation._tcp" on the local LAN. + ''; + }; + + domain = mkOption { + type = types.bool; + default = false; + description = "Whether to announce the locally used domain name for browsing by other hosts."; + }; + }; + + nssmdns = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the mDNS NSS (Name Service Switch) plug-in. + Enabling it allows applications to resolve names in the `.local' + domain by transparently querying the Avahi daemon. + ''; + }; + + cacheEntriesMax = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Number of resource records to be cached per interface. Use 0 to + disable caching. Avahi daemon defaults to 4096 if not set. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra config to append to avahi-daemon.conf. + ''; + }; + }; + + config = mkIf cfg.enable { + users.users.avahi = { + description = "avahi-daemon privilege separation user"; + home = "/var/empty"; + group = "avahi"; + isSystemUser = true; + }; + + users.groups.avahi = {}; + + system.nssModules = optional cfg.nssmdns pkgs.nssmdns; + system.nssDatabases.hosts = optionals cfg.nssmdns (mkMerge [ + [ "mdns_minimal [NOTFOUND=return]" ] + (mkOrder 1501 [ "mdns" ]) # 1501 to ensure it's after dns + ]); + + environment.systemPackages = [ pkgs.avahi ]; + + environment.etc = (mapAttrs' (n: v: nameValuePair + "avahi/services/${n}.service" + { ${if types.path.check v then "source" else "text"} = v; } + ) cfg.extraServiceFiles); + + systemd.sockets.avahi-daemon = { + description = "Avahi mDNS/DNS-SD Stack Activation Socket"; + listenStreams = [ "/run/avahi-daemon/socket" ]; + wantedBy = [ "sockets.target" ]; + }; + + systemd.tmpfiles.rules = [ "d /run/avahi-daemon - avahi avahi -" ]; + + systemd.services.avahi-daemon = { + description = "Avahi mDNS/DNS-SD Stack"; + wantedBy = [ "multi-user.target" ]; + requires = [ "avahi-daemon.socket" ]; + + # Make NSS modules visible so that `avahi_nss_support ()' can + # return a sensible value. + environment.LD_LIBRARY_PATH = config.system.nssModules.path; + + path = [ pkgs.coreutils pkgs.avahi ]; + + serviceConfig = { + NotifyAccess = "main"; + BusName = "org.freedesktop.Avahi"; + Type = "dbus"; + ExecStart = "${pkgs.avahi}/sbin/avahi-daemon --syslog -f ${avahiDaemonConf}"; + }; + }; + + services.dbus.enable = true; + services.dbus.packages = [ pkgs.avahi ]; + + networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ 5353 ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/babeld.nix b/nixpkgs/nixos/modules/services/networking/babeld.nix new file mode 100644 index 000000000000..e62c74d0069d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/babeld.nix @@ -0,0 +1,95 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.babeld; + + conditionalBoolToString = value: if (isBool value) then (boolToString value) else (toString value); + + paramsString = params: + concatMapStringsSep " " (name: "${name} ${conditionalBoolToString (getAttr name params)}") + (attrNames params); + + interfaceConfig = name: + let + interface = getAttr name cfg.interfaces; + in + "interface ${name} ${paramsString interface}\n"; + + configFile = with cfg; pkgs.writeText "babeld.conf" ( + (optionalString (cfg.interfaceDefaults != null) '' + default ${paramsString cfg.interfaceDefaults} + '') + + (concatMapStrings interfaceConfig (attrNames cfg.interfaces)) + + extraConfig); + +in + +{ + + ###### interface + + options = { + + services.babeld = { + + enable = mkEnableOption "the babeld network routing daemon"; + + interfaceDefaults = mkOption { + default = null; + description = '' + A set describing default parameters for babeld interfaces. + See <citerefentry><refentrytitle>babeld</refentrytitle><manvolnum>8</manvolnum></citerefentry> for options. + ''; + type = types.nullOr (types.attrsOf types.unspecified); + example = + { + type = "tunnel"; + split-horizon = true; + }; + }; + + interfaces = mkOption { + default = {}; + description = '' + A set describing babeld interfaces. + See <citerefentry><refentrytitle>babeld</refentrytitle><manvolnum>8</manvolnum></citerefentry> for options. + ''; + type = types.attrsOf (types.attrsOf types.unspecified); + example = + { enp0s2 = + { type = "wired"; + hello-interval = 5; + split-horizon = "auto"; + }; + }; + }; + + extraConfig = mkOption { + default = ""; + description = '' + Options that will be copied to babeld.conf. + See <citerefentry><refentrytitle>babeld</refentrytitle><manvolnum>8</manvolnum></citerefentry> for details. + ''; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.babeld.enable { + + systemd.services.babeld = { + description = "Babel routing daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig.ExecStart = "${pkgs.babeld}/bin/babeld -c ${configFile}"; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/bind.nix b/nixpkgs/nixos/modules/services/networking/bind.nix new file mode 100644 index 000000000000..faad88635759 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/bind.nix @@ -0,0 +1,205 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.bind; + + bindUser = "named"; + + confFile = pkgs.writeText "named.conf" + '' + include "/etc/bind/rndc.key"; + controls { + inet 127.0.0.1 allow {localhost;} keys {"rndc-key";}; + }; + + acl cachenetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.cacheNetworks} }; + acl badnetworks { ${concatMapStrings (entry: " ${entry}; ") cfg.blockedNetworks} }; + + options { + listen-on { ${concatMapStrings (entry: " ${entry}; ") cfg.listenOn} }; + listen-on-v6 { ${concatMapStrings (entry: " ${entry}; ") cfg.listenOnIpv6} }; + allow-query { cachenetworks; }; + blackhole { badnetworks; }; + forward first; + forwarders { ${concatMapStrings (entry: " ${entry}; ") cfg.forwarders} }; + directory "/run/named"; + pid-file "/run/named/named.pid"; + ${cfg.extraOptions} + }; + + ${cfg.extraConfig} + + ${ concatMapStrings + ({ name, file, master ? true, slaves ? [], masters ? [], extraConfig ? "" }: + '' + zone "${name}" { + type ${if master then "master" else "slave"}; + file "${file}"; + ${ if master then + '' + allow-transfer { + ${concatMapStrings (ip: "${ip};\n") slaves} + }; + '' + else + '' + masters { + ${concatMapStrings (ip: "${ip};\n") masters} + }; + '' + } + allow-query { any; }; + ${extraConfig} + }; + '') + cfg.zones } + ''; + +in + +{ + + ###### interface + + options = { + + services.bind = { + + enable = mkEnableOption "BIND domain name server"; + + cacheNetworks = mkOption { + default = ["127.0.0.0/24"]; + description = " + What networks are allowed to use us as a resolver. Note + that this is for recursive queries -- all networks are + allowed to query zones configured with the `zones` option. + It is recommended that you limit cacheNetworks to avoid your + server being used for DNS amplification attacks. + "; + }; + + blockedNetworks = mkOption { + default = []; + description = " + What networks are just blocked. + "; + }; + + ipv4Only = mkOption { + default = false; + description = " + Only use ipv4, even if the host supports ipv6. + "; + }; + + forwarders = mkOption { + default = config.networking.nameservers; + description = " + List of servers we should forward requests to. + "; + }; + + listenOn = mkOption { + default = ["any"]; + type = types.listOf types.str; + description = " + Interfaces to listen on. + "; + }; + + listenOnIpv6 = mkOption { + default = ["any"]; + type = types.listOf types.str; + description = " + Ipv6 interfaces to listen on. + "; + }; + + zones = mkOption { + default = []; + description = " + List of zones we claim authority over. + master=false means slave server; slaves means addresses + who may request zone transfer. + "; + example = [{ + name = "example.com"; + master = false; + file = "/var/dns/example.com"; + masters = ["192.168.0.1"]; + slaves = []; + extraConfig = ""; + }]; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = " + Extra lines to be added verbatim to the generated named configuration file. + "; + }; + + extraOptions = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to be added verbatim to the options section of the + generated named configuration file. + ''; + }; + + configFile = mkOption { + type = types.path; + default = confFile; + defaultText = "confFile"; + description = " + Overridable config file to use for named. By default, that + generated by nixos. + "; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + networking.resolvconf.useLocalResolver = mkDefault true; + + users.users.${bindUser} = + { uid = config.ids.uids.bind; + description = "BIND daemon user"; + }; + + systemd.services.bind = { + description = "BIND Domain Name Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + preStart = '' + mkdir -m 0755 -p /etc/bind + if ! [ -f "/etc/bind/rndc.key" ]; then + ${pkgs.bind.out}/sbin/rndc-confgen -c /etc/bind/rndc.key -u ${bindUser} -a -A hmac-sha256 2>/dev/null + fi + + ${pkgs.coreutils}/bin/mkdir -p /run/named + chown ${bindUser} /run/named + ''; + + serviceConfig = { + ExecStart = "${pkgs.bind.out}/sbin/named -u ${bindUser} ${optionalString cfg.ipv4Only "-4"} -c ${cfg.configFile} -f"; + ExecReload = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' reload"; + ExecStop = "${pkgs.bind.out}/sbin/rndc -k '/etc/bind/rndc.key' stop"; + }; + + unitConfig.Documentation = "man:named(8)"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/bird.nix b/nixpkgs/nixos/modules/services/networking/bird.nix new file mode 100644 index 000000000000..4ae35875c0f0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/bird.nix @@ -0,0 +1,78 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) mkEnableOption mkIf mkOption types; + + generic = variant: + let + cfg = config.services.${variant}; + pkg = pkgs.${variant}; + birdBin = if variant == "bird6" then "bird6" else "bird"; + birdc = if variant == "bird6" then "birdc6" else "birdc"; + descr = + { bird = "1.9.x with IPv4 suport"; + bird6 = "1.9.x with IPv6 suport"; + bird2 = "2.x"; + }.${variant}; + in { + ###### interface + options = { + services.${variant} = { + enable = mkEnableOption "BIRD Internet Routing Daemon (${descr})"; + config = mkOption { + type = types.lines; + description = '' + BIRD Internet Routing Daemon configuration file. + <link xlink:href='http://bird.network.cz/'/> + ''; + }; + }; + }; + + ###### implementation + config = mkIf cfg.enable { + environment.systemPackages = [ pkg ]; + + environment.etc."bird/${variant}.conf".source = pkgs.writeTextFile { + name = "${variant}.conf"; + text = cfg.config; + checkPhase = '' + ${pkg}/bin/${birdBin} -d -p -c $out + ''; + }; + + systemd.services.${variant} = { + description = "BIRD Internet Routing Daemon (${descr})"; + wantedBy = [ "multi-user.target" ]; + reloadIfChanged = true; + restartTriggers = [ config.environment.etc."bird/${variant}.conf".source ]; + serviceConfig = { + Type = "forking"; + Restart = "on-failure"; + ExecStart = "${pkg}/bin/${birdBin} -c /etc/bird/${variant}.conf -u ${variant} -g ${variant}"; + ExecReload = "${pkg}/bin/${birdc} configure"; + ExecStop = "${pkg}/bin/${birdc} down"; + CapabilityBoundingSet = [ "CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_SETUID" "CAP_SETGID" + # see bird/sysdep/linux/syspriv.h + "CAP_NET_BIND_SERVICE" "CAP_NET_BROADCAST" "CAP_NET_ADMIN" "CAP_NET_RAW" ]; + ProtectSystem = "full"; + ProtectHome = "yes"; + SystemCallFilter="~@cpu-emulation @debug @keyring @module @mount @obsolete @raw-io"; + MemoryDenyWriteExecute = "yes"; + }; + }; + users = { + users.${variant} = { + description = "BIRD Internet Routing Daemon user"; + group = variant; + }; + groups.${variant} = {}; + }; + }; + }; + +in + +{ + imports = map generic [ "bird" "bird6" "bird2" ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/bitcoind.nix b/nixpkgs/nixos/modules/services/networking/bitcoind.nix new file mode 100644 index 000000000000..4e00a8865474 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/bitcoind.nix @@ -0,0 +1,193 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.bitcoind; + pidFile = "${cfg.dataDir}/bitcoind.pid"; + configFile = pkgs.writeText "bitcoin.conf" '' + ${optionalString cfg.testnet "testnet=1"} + ${optionalString (cfg.dbCache != null) "dbcache=${toString cfg.dbCache}"} + ${optionalString (cfg.prune != null) "prune=${toString cfg.prune}"} + + # Connection options + ${optionalString (cfg.port != null) "port=${toString cfg.port}"} + + # RPC server options + ${optionalString (cfg.rpc.port != null) "rpcport=${toString cfg.rpc.port}"} + ${concatMapStringsSep "\n" + (rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}") + (attrValues cfg.rpc.users) + } + + # Extra config options (from bitcoind nixos service) + ${cfg.extraConfig} + ''; + cmdlineOptions = escapeShellArgs [ + "-conf=${cfg.configFile}" + "-datadir=${cfg.dataDir}" + "-pid=${pidFile}" + ]; + + rpcUserOpts = { name, ... }: { + options = { + name = mkOption { + type = types.str; + example = "alice"; + description = '' + Username for JSON-RPC connections. + ''; + }; + passwordHMAC = mkOption { + type = with types; uniq (strMatching "[0-9a-f]+\\$[0-9a-f]{64}"); + example = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae"; + description = '' + Password HMAC-SHA-256 for JSON-RPC connections. Must be a string of the + format <SALT-HEX>$<HMAC-HEX>. + ''; + }; + }; + config = { + name = mkDefault name; + }; + }; +in { + options = { + + services.bitcoind = { + enable = mkEnableOption "Bitcoin daemon"; + + package = mkOption { + type = types.package; + default = pkgs.bitcoind; + defaultText = "pkgs.bitcoind"; + description = "The package providing bitcoin binaries."; + }; + configFile = mkOption { + type = types.path; + default = configFile; + example = "/etc/bitcoind.conf"; + description = "The configuration file path to supply bitcoind."; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + par=16 + rpcthreads=16 + logips=1 + ''; + description = "Additional configurations to be appended to <filename>bitcoin.conf</filename>."; + }; + dataDir = mkOption { + type = types.path; + default = "/var/lib/bitcoind"; + description = "The data directory for bitcoind."; + }; + + user = mkOption { + type = types.str; + default = "bitcoin"; + description = "The user as which to run bitcoind."; + }; + group = mkOption { + type = types.str; + default = cfg.user; + description = "The group as which to run bitcoind."; + }; + + rpc = { + port = mkOption { + type = types.nullOr types.port; + default = null; + description = "Override the default port on which to listen for JSON-RPC connections."; + }; + users = mkOption { + default = {}; + example = literalExample '' + { + alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae"; + bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99"; + } + ''; + type = with types; loaOf (submodule rpcUserOpts); + description = '' + RPC user information for JSON-RPC connnections. + ''; + }; + }; + + testnet = mkOption { + type = types.bool; + default = false; + description = "Whether to use the test chain."; + }; + port = mkOption { + type = types.nullOr types.port; + default = null; + description = "Override the default port on which to listen for connections."; + }; + dbCache = mkOption { + type = types.nullOr (types.ints.between 4 16384); + default = null; + example = 4000; + description = "Override the default database cache size in megabytes."; + }; + prune = mkOption { + type = types.nullOr (types.coercedTo + (types.enum [ "disable" "manual" ]) + (x: if x == "disable" then 0 else 1) + types.ints.unsigned + ); + default = null; + example = 10000; + description = '' + Reduce storage requirements by enabling pruning (deleting) of old + blocks. This allows the pruneblockchain RPC to be called to delete + specific blocks, and enables automatic pruning of old blocks if a + target size in MiB is provided. This mode is incompatible with -txindex + and -rescan. Warning: Reverting this setting requires re-downloading + the entire blockchain. ("disable" = disable pruning blocks, "manual" + = allow manual pruning via RPC, >=550 = automatically prune block files + to stay under the specified target size in MiB) + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + systemd.tmpfiles.rules = [ + "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -" + "L '${cfg.dataDir}/bitcoin.conf' - - - - '${cfg.configFile}'" + ]; + systemd.services.bitcoind = { + description = "Bitcoin daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + ExecStart = "${cfg.package}/bin/bitcoind ${cmdlineOptions}"; + Restart = "on-failure"; + + # Hardening measures + PrivateTmp = "true"; + ProtectSystem = "full"; + NoNewPrivileges = "true"; + PrivateDevices = "true"; + MemoryDenyWriteExecute = "true"; + }; + }; + users.users.${cfg.user} = { + name = cfg.user; + group = cfg.group; + description = "Bitcoin daemon user"; + home = cfg.dataDir; + isSystemUser = true; + }; + users.groups.${cfg.group} = { + name = cfg.group; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/bitlbee.nix b/nixpkgs/nixos/modules/services/networking/bitlbee.nix new file mode 100644 index 000000000000..9ebf382fce42 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/bitlbee.nix @@ -0,0 +1,193 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.bitlbee; + bitlbeeUid = config.ids.uids.bitlbee; + + bitlbeePkg = pkgs.bitlbee.override { + enableLibPurple = cfg.libpurple_plugins != []; + enablePam = cfg.authBackend == "pam"; + }; + + bitlbeeConfig = pkgs.writeText "bitlbee.conf" + '' + [settings] + RunMode = Daemon + User = bitlbee + ConfigDir = ${cfg.configDir} + DaemonInterface = ${cfg.interface} + DaemonPort = ${toString cfg.portNumber} + AuthMode = ${cfg.authMode} + AuthBackend = ${cfg.authBackend} + Plugindir = ${pkgs.bitlbee-plugins cfg.plugins}/lib/bitlbee + ${lib.optionalString (cfg.hostName != "") "HostName = ${cfg.hostName}"} + ${lib.optionalString (cfg.protocols != "") "Protocols = ${cfg.protocols}"} + ${cfg.extraSettings} + + [defaults] + ${cfg.extraDefaults} + ''; + + purple_plugin_path = + lib.concatMapStringsSep ":" + (plugin: "${plugin}/lib/pidgin/:${plugin}/lib/purple-2/") + cfg.libpurple_plugins + ; + +in + +{ + + ###### interface + + options = { + + services.bitlbee = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run the BitlBee IRC to other chat network gateway. + Running it allows you to access the MSN, Jabber, Yahoo! and ICQ chat + networks via an IRC client. + ''; + }; + + interface = mkOption { + default = "127.0.0.1"; + description = '' + The interface the BitlBee deamon will be listening to. If `127.0.0.1', + only clients on the local host can connect to it; if `0.0.0.0', clients + can access it from any network interface. + ''; + }; + + portNumber = mkOption { + default = 6667; + description = '' + Number of the port BitlBee will be listening to. + ''; + }; + + authBackend = mkOption { + default = "storage"; + type = types.enum [ "storage" "pam" ]; + description = '' + How users are authenticated + storage -- save passwords internally + pam -- Linux PAM authentication + ''; + }; + + authMode = mkOption { + default = "Open"; + type = types.enum [ "Open" "Closed" "Registered" ]; + description = '' + The following authentication modes are available: + Open -- Accept connections from anyone, use NickServ for user authentication. + Closed -- Require authorization (using the PASS command during login) before allowing the user to connect at all. + Registered -- Only allow registered users to use this server; this disables the register- and the account command until the user identifies himself. + ''; + }; + + hostName = mkOption { + default = ""; + type = types.str; + description = '' + Normally, BitlBee gets a hostname using getsockname(). If you have a nicer + alias for your BitlBee daemon, you can set it here and BitlBee will identify + itself with that name instead. + ''; + }; + + plugins = mkOption { + type = types.listOf types.package; + default = []; + example = literalExample "[ pkgs.bitlbee-facebook ]"; + description = '' + The list of bitlbee plugins to install. + ''; + }; + + libpurple_plugins = mkOption { + type = types.listOf types.package; + default = []; + example = literalExample "[ pkgs.purple-matrix ]"; + description = '' + The list of libpurple plugins to install. + ''; + }; + + configDir = mkOption { + default = "/var/lib/bitlbee"; + type = types.path; + description = '' + Specify an alternative directory to store all the per-user configuration + files. + ''; + }; + + protocols = mkOption { + default = ""; + type = types.str; + description = '' + This option allows to remove the support of protocol, even if compiled + in. If nothing is given, there are no restrictions. + ''; + }; + + extraSettings = mkOption { + default = ""; + description = '' + Will be inserted in the Settings section of the config file. + ''; + }; + + extraDefaults = mkOption { + default = ""; + description = '' + Will be inserted in the Default section of the config file. + ''; + }; + + }; + + }; + + ###### implementation + + config = mkMerge [ + (mkIf config.services.bitlbee.enable { + users.users.bitlbee = { + uid = bitlbeeUid; + description = "BitlBee user"; + home = "/var/lib/bitlbee"; + createHome = true; + }; + + users.groups.bitlbee = { + gid = config.ids.gids.bitlbee; + }; + + systemd.services.bitlbee = { + environment.PURPLE_PLUGIN_PATH = purple_plugin_path; + description = "BitlBee IRC to other chat networks gateway"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig.User = "bitlbee"; + serviceConfig.ExecStart = "${bitlbeePkg}/sbin/bitlbee -F -n -c ${bitlbeeConfig}"; + }; + + environment.systemPackages = [ bitlbeePkg ]; + + }) + (mkIf (config.services.bitlbee.authBackend == "pam") { + security.pam.services.bitlbee = {}; + }) + ]; + +} diff --git a/nixpkgs/nixos/modules/services/networking/charybdis.nix b/nixpkgs/nixos/modules/services/networking/charybdis.nix new file mode 100644 index 000000000000..43829d36e417 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/charybdis.nix @@ -0,0 +1,107 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) mkEnableOption mkIf mkOption singleton types; + inherit (pkgs) coreutils charybdis; + cfg = config.services.charybdis; + + configFile = pkgs.writeText "charybdis.conf" '' + ${cfg.config} + ''; +in + +{ + + ###### interface + + options = { + + services.charybdis = { + + enable = mkEnableOption "Charybdis IRC daemon"; + + config = mkOption { + type = types.str; + description = '' + Charybdis IRC daemon configuration file. + ''; + }; + + statedir = mkOption { + type = types.path; + default = "/var/lib/charybdis"; + description = '' + Location of the state directory of charybdis. + ''; + }; + + user = mkOption { + type = types.str; + default = "ircd"; + description = '' + Charybdis IRC daemon user. + ''; + }; + + group = mkOption { + type = types.str; + default = "ircd"; + description = '' + Charybdis IRC daemon group. + ''; + }; + + motd = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + Charybdis MOTD text. + + Charybdis will read its MOTD from /etc/charybdis/ircd.motd . + If set, the value of this option will be written to this path. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable (lib.mkMerge [ + { + users.users.${cfg.user} = { + description = "Charybdis IRC daemon user"; + uid = config.ids.uids.ircd; + group = cfg.group; + }; + + users.groups.${cfg.group} = { + gid = config.ids.gids.ircd; + }; + + systemd.tmpfiles.rules = [ + "d ${cfg.statedir} - ${cfg.user} ${cfg.group} - -" + ]; + + systemd.services.charybdis = { + description = "Charybdis IRC daemon"; + wantedBy = [ "multi-user.target" ]; + environment = { + BANDB_DBPATH = "${cfg.statedir}/ban.db"; + }; + serviceConfig = { + ExecStart = "${charybdis}/bin/charybdis -foreground -logfile /dev/stdout -configfile ${configFile}"; + Group = cfg.group; + User = cfg.user; + }; + }; + + } + + (mkIf (cfg.motd != null) { + environment.etc."charybdis/ircd.motd".text = cfg.motd; + }) + ]); +} diff --git a/nixpkgs/nixos/modules/services/networking/cjdns.nix b/nixpkgs/nixos/modules/services/networking/cjdns.nix new file mode 100644 index 000000000000..5f8ac96b2292 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/cjdns.nix @@ -0,0 +1,294 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + pkg = pkgs.cjdns; + + cfg = config.services.cjdns; + + connectToSubmodule = + { ... }: + { options = + { password = mkOption { + type = types.str; + description = "Authorized password to the opposite end of the tunnel."; + }; + publicKey = mkOption { + type = types.str; + description = "Public key at the opposite end of the tunnel."; + }; + hostname = mkOption { + default = ""; + example = "foobar.hype"; + type = types.str; + description = "Optional hostname to add to /etc/hosts; prevents reverse lookup failures."; + }; + }; + }; + + # Additional /etc/hosts entries for peers with an associated hostname + cjdnsExtraHosts = pkgs.runCommandNoCC "cjdns-hosts" {} '' + exec >$out + ${concatStringsSep "\n" (mapAttrsToList (k: v: + optionalString (v.hostname != "") + "echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}") + (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo))} + ''; + + parseModules = x: + x // { connectTo = mapAttrs (name: value: { inherit (value) password publicKey; }) x.connectTo; }; + + cjdrouteConf = builtins.toJSON ( recursiveUpdate { + admin = { + bind = cfg.admin.bind; + password = "@CJDNS_ADMIN_PASSWORD@"; + }; + authorizedPasswords = map (p: { password = p; }) cfg.authorizedPasswords; + interfaces = { + ETHInterface = if (cfg.ETHInterface.bind != "") then [ (parseModules cfg.ETHInterface) ] else [ ]; + UDPInterface = if (cfg.UDPInterface.bind != "") then [ (parseModules cfg.UDPInterface) ] else [ ]; + }; + + privateKey = "@CJDNS_PRIVATE_KEY@"; + + resetAfterInactivitySeconds = 100; + + router = { + interface = { type = "TUNInterface"; }; + ipTunnel = { + allowedConnections = []; + outgoingConnections = []; + }; + }; + + security = [ { exemptAngel = 1; setuser = "nobody"; } ]; + + } cfg.extraConfig); + +in + +{ + options = { + + services.cjdns = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the cjdns network encryption + and routing engine. A file at /etc/cjdns.keys will + be created if it does not exist to contain a random + secret key that your IPv6 address will be derived from. + ''; + }; + + extraConfig = mkOption { + type = types.attrs; + default = {}; + example = { router.interface.tunDevice = "tun10"; }; + description = '' + Extra configuration, given as attrs, that will be merged recursively + with the rest of the JSON generated by this module, at the root node. + ''; + }; + + confFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/etc/cjdroute.conf"; + description = '' + Ignore all other cjdns options and load configuration from this file. + ''; + }; + + authorizedPasswords = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ + "snyrfgkqsc98qh1y4s5hbu0j57xw5s0" + "z9md3t4p45mfrjzdjurxn4wuj0d8swv" + "49275fut6tmzu354pq70sr5b95qq0vj" + ]; + description = '' + Any remote cjdns nodes that offer these passwords on + connection will be allowed to route through this node. + ''; + }; + + admin = { + bind = mkOption { + type = types.str; + default = "127.0.0.1:11234"; + description = '' + Bind the administration port to this address and port. + ''; + }; + }; + + UDPInterface = { + bind = mkOption { + type = types.str; + default = ""; + example = "192.168.1.32:43211"; + description = '' + Address and port to bind UDP tunnels to. + ''; + }; + connectTo = mkOption { + type = types.attrsOf ( types.submodule ( connectToSubmodule ) ); + default = { }; + example = literalExample '' + { + "192.168.1.1:27313" = { + hostname = "homer.hype"; + password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM"; + publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k"; + }; + } + ''; + description = '' + Credentials for making UDP tunnels. + ''; + }; + }; + + ETHInterface = { + bind = mkOption { + type = types.str; + default = ""; + example = "eth0"; + description = + '' + Bind to this device for native ethernet operation. + <literal>all</literal> is a pseudo-name which will try to connect to all devices. + ''; + }; + + beacon = mkOption { + type = types.int; + default = 2; + description = '' + Auto-connect to other cjdns nodes on the same network. + Options: + 0: Disabled. + 1: Accept beacons, this will cause cjdns to accept incoming + beacon messages and try connecting to the sender. + 2: Accept and send beacons, this will cause cjdns to broadcast + messages on the local network which contain a randomly + generated per-session password, other nodes which have this + set to 1 or 2 will hear the beacon messages and connect + automatically. + ''; + }; + + connectTo = mkOption { + type = types.attrsOf ( types.submodule ( connectToSubmodule ) ); + default = { }; + example = literalExample '' + { + "01:02:03:04:05:06" = { + hostname = "homer.hype"; + password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM"; + publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k"; + }; + } + ''; + description = '' + Credentials for connecting look similar to UDP credientials + except they begin with the mac address. + ''; + }; + }; + + addExtraHosts = mkOption { + type = types.bool; + default = false; + description = '' + Whether to add cjdns peers with an associated hostname to + <filename>/etc/hosts</filename>. Beware that enabling this + incurs heavy eval-time costs. + ''; + }; + + }; + + }; + + config = mkIf cfg.enable { + + boot.kernelModules = [ "tun" ]; + + # networking.firewall.allowedUDPPorts = ... + + systemd.services.cjdns = { + description = "cjdns: routing engine designed for security, scalability, speed and ease of use"; + wantedBy = [ "multi-user.target" "sleep.target"]; + after = [ "network-online.target" ]; + bindsTo = [ "network-online.target" ]; + + preStart = if cfg.confFile != null then "" else '' + [ -e /etc/cjdns.keys ] && source /etc/cjdns.keys + + if [ -z "$CJDNS_PRIVATE_KEY" ]; then + shopt -s lastpipe + ${pkg}/bin/makekeys | { read private ipv6 public; } + + umask 0077 + echo "CJDNS_PRIVATE_KEY=$private" >> /etc/cjdns.keys + echo -e "CJDNS_IPV6=$ipv6\nCJDNS_PUBLIC_KEY=$public" > /etc/cjdns.public + + chmod 600 /etc/cjdns.keys + chmod 444 /etc/cjdns.public + fi + + if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then + echo "CJDNS_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 96)" \ + >> /etc/cjdns.keys + fi + ''; + + script = ( + if cfg.confFile != null then "${pkg}/bin/cjdroute < ${cfg.confFile}" else + '' + source /etc/cjdns.keys + (cat <<'EOF' + ${cjdrouteConf} + EOF + ) | sed \ + -e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \ + -e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \ + | ${pkg}/bin/cjdroute + '' + ); + + serviceConfig = { + Type = "forking"; + Restart = "always"; + StartLimitInterval = 0; + RestartSec = 1; + CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW CAP_SETUID"; + ProtectSystem = true; + # Doesn't work on i686, causing service to fail + MemoryDenyWriteExecute = !pkgs.stdenv.isi686; + ProtectHome = true; + PrivateTmp = true; + }; + }; + + networking.hostFiles = mkIf cfg.addExtraHosts [ cjdnsExtraHosts ]; + + assertions = [ + { assertion = ( cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != null ); + message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined."; + } + { assertion = config.networking.enableIPv6; + message = "networking.enableIPv6 must be enabled for CJDNS to work"; + } + ]; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/cntlm.nix b/nixpkgs/nixos/modules/services/networking/cntlm.nix new file mode 100644 index 000000000000..5b5068e43d7c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/cntlm.nix @@ -0,0 +1,121 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.cntlm; + + configFile = if cfg.configText != "" then + pkgs.writeText "cntlm.conf" '' + ${cfg.configText} + '' + else + pkgs.writeText "lighttpd.conf" '' + # Cntlm Authentication Proxy Configuration + Username ${cfg.username} + Domain ${cfg.domain} + Password ${cfg.password} + ${optionalString (cfg.netbios_hostname != "") "Workstation ${cfg.netbios_hostname}"} + ${concatMapStrings (entry: "Proxy ${entry}\n") cfg.proxy} + ${optionalString (cfg.noproxy != []) "NoProxy ${concatStringsSep ", " cfg.noproxy}"} + + ${concatMapStrings (port: '' + Listen ${toString port} + '') cfg.port} + + ${cfg.extraConfig} + ''; + +in + +{ + + options.services.cntlm = { + + enable = mkEnableOption "cntlm, which starts a local proxy"; + + username = mkOption { + description = '' + Proxy account name, without the possibility to include domain name ('at' sign is interpreted literally). + ''; + }; + + domain = mkOption { + description = ''Proxy account domain/workgroup name.''; + }; + + password = mkOption { + default = "/etc/cntlm.password"; + type = types.str; + description = ''Proxy account password. Note: use chmod 0600 on /etc/cntlm.password for security.''; + }; + + netbios_hostname = mkOption { + type = types.str; + default = ""; + description = '' + The hostname of your machine. + ''; + }; + + proxy = mkOption { + description = '' + A list of NTLM/NTLMv2 authenticating HTTP proxies. + + Parent proxy, which requires authentication. The same as proxy on the command-line, can be used more than once to specify unlimited + number of proxies. Should one proxy fail, cntlm automatically moves on to the next one. The connect request fails only if the whole + list of proxies is scanned and (for each request) and found to be invalid. Command-line takes precedence over the configuration file. + ''; + example = [ "proxy.example.com:81" ]; + }; + + noproxy = mkOption { + description = '' + A list of domains where the proxy is skipped. + ''; + default = []; + example = [ "*.example.com" "example.com" ]; + }; + + port = mkOption { + default = [3128]; + description = "Specifies on which ports the cntlm daemon listens."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Additional config appended to the end of the generated <filename>cntlm.conf</filename>."; + }; + + configText = mkOption { + type = types.lines; + default = ""; + description = "Verbatim contents of <filename>cntlm.conf</filename>."; + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + systemd.services.cntlm = { + description = "CNTLM is an NTLM / NTLM Session Response / NTLMv2 authenticating HTTP proxy"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "cntlm"; + ExecStart = '' + ${pkgs.cntlm}/bin/cntlm -U cntlm -c ${configFile} -v -f + ''; + }; + }; + + users.users.cntlm = { + name = "cntlm"; + description = "cntlm system-wide daemon"; + isSystemUser = true; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/connman.nix b/nixpkgs/nixos/modules/services/networking/connman.nix new file mode 100644 index 000000000000..6ccc2dffb267 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/connman.nix @@ -0,0 +1,161 @@ +{ config, lib, pkgs, ... }: + +with pkgs; +with lib; + +let + cfg = config.services.connman; + configFile = pkgs.writeText "connman.conf" '' + [General] + NetworkInterfaceBlacklist=${concatStringsSep "," cfg.networkInterfaceBlacklist} + + ${cfg.extraConfig} + ''; + enableIwd = cfg.wifi.backend == "iwd"; +in { + + imports = [ + (mkRenamedOptionModule [ "networking" "connman" ] [ "services" "connman" ]) + ]; + + ###### interface + + options = { + + services.connman = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to use ConnMan for managing your network connections. + ''; + }; + + enableVPN = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable ConnMan VPN service. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = '' + ''; + description = '' + Configuration lines appended to the generated connman configuration file. + ''; + }; + + networkInterfaceBlacklist = mkOption { + type = with types; listOf str; + default = [ "vmnet" "vboxnet" "virbr" "ifb" "ve" ]; + description = '' + Default blacklisted interfaces, this includes NixOS containers interfaces (ve). + ''; + }; + + wifi = { + backend = mkOption { + type = types.enum [ "wpa_supplicant" "iwd" ]; + default = "wpa_supplicant"; + description = '' + Specify the Wi-Fi backend used. + Currently supported are <option>wpa_supplicant</option> or <option>iwd</option>. + ''; + }; + }; + + extraFlags = mkOption { + type = with types; listOf str; + default = [ ]; + example = [ "--nodnsproxy" ]; + description = '' + Extra flags to pass to connmand + ''; + }; + + package = mkOption { + type = types.path; + description = "The connman package / build flavor"; + default = connman; + example = literalExample "pkgs.connmanFull"; + }; + + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + assertions = [{ + assertion = !config.networking.useDHCP; + message = "You can not use services.connman with networking.useDHCP"; + }{ + # TODO: connman seemingly can be used along network manager and + # connmanFull supports this - so this should be worked out somehow + assertion = !config.networking.networkmanager.enable; + message = "You can not use services.connman with networking.networkmanager"; + }]; + + environment.systemPackages = [ cfg.package ]; + + systemd.services.connman = { + description = "Connection service"; + wantedBy = [ "multi-user.target" ]; + after = [ "syslog.target" ] ++ optional enableIwd "iwd.service"; + requires = optional enableIwd "iwd.service"; + serviceConfig = { + Type = "dbus"; + BusName = "net.connman"; + Restart = "on-failure"; + ExecStart = toString ([ + "${cfg.package}/sbin/connmand" + "--config=${configFile}" + "--nodaemon" + ] ++ optional enableIwd "--wifi=iwd_agent" + ++ cfg.extraFlags); + StandardOutput = "null"; + }; + }; + + systemd.services.connman-vpn = mkIf cfg.enableVPN { + description = "ConnMan VPN service"; + wantedBy = [ "multi-user.target" ]; + after = [ "syslog.target" ]; + before = [ "connman" ]; + serviceConfig = { + Type = "dbus"; + BusName = "net.connman.vpn"; + ExecStart = "${cfg.package}/sbin/connman-vpnd -n"; + StandardOutput = "null"; + }; + }; + + systemd.services.net-connman-vpn = mkIf cfg.enableVPN { + description = "D-BUS Service"; + serviceConfig = { + Name = "net.connman.vpn"; + before = [ "connman" ]; + ExecStart = "${cfg.package}/sbin/connman-vpnd -n"; + User = "root"; + SystemdService = "connman-vpn.service"; + }; + }; + + networking = { + useDHCP = false; + wireless = { + enable = mkIf (!enableIwd) true; + iwd = mkIf enableIwd { + enable = true; + }; + }; + networkmanager.enable = false; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/consul.nix b/nixpkgs/nixos/modules/services/networking/consul.nix new file mode 100644 index 000000000000..f7d2afead06c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/consul.nix @@ -0,0 +1,254 @@ +{ config, lib, pkgs, utils, ... }: + +with lib; +let + + dataDir = "/var/lib/consul"; + cfg = config.services.consul; + + configOptions = { + data_dir = dataDir; + ui = cfg.webUi; + } // cfg.extraConfig; + + configFiles = [ "/etc/consul.json" "/etc/consul-addrs.json" ] + ++ cfg.extraConfigFiles; + + devices = attrValues (filterAttrs (_: i: i != null) cfg.interface); + systemdDevices = forEach devices + (i: "sys-subsystem-net-devices-${utils.escapeSystemdPath i}.device"); +in +{ + options = { + + services.consul = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enables the consul daemon. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.consul; + defaultText = "pkgs.consul"; + description = '' + The package used for the Consul agent and CLI. + ''; + }; + + + webUi = mkOption { + type = types.bool; + default = false; + description = '' + Enables the web interface on the consul http port. + ''; + }; + + leaveOnStop = mkOption { + type = types.bool; + default = false; + description = '' + If enabled, causes a leave action to be sent when closing consul. + This allows a clean termination of the node, but permanently removes + it from the cluster. You probably don't want this option unless you + are running a node which going offline in a permanent / semi-permanent + fashion. + ''; + }; + + interface = { + + advertise = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The name of the interface to pull the advertise_addr from. + ''; + }; + + bind = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The name of the interface to pull the bind_addr from. + ''; + }; + + }; + + forceIpv4 = mkOption { + type = types.bool; + default = false; + description = '' + Whether we should force the interfaces to only pull ipv4 addresses. + ''; + }; + + dropPrivileges = mkOption { + type = types.bool; + default = true; + description = '' + Whether the consul agent should be run as a non-root consul user. + ''; + }; + + extraConfig = mkOption { + default = { }; + description = '' + Extra configuration options which are serialized to json and added + to the config.json file. + ''; + }; + + extraConfigFiles = mkOption { + default = [ ]; + type = types.listOf types.str; + description = '' + Additional configuration files to pass to consul + NOTE: These will not trigger the service to be restarted when altered. + ''; + }; + + alerts = { + enable = mkEnableOption "consul-alerts"; + + package = mkOption { + description = "Package to use for consul-alerts."; + default = pkgs.consul-alerts; + defaultText = "pkgs.consul-alerts"; + type = types.package; + }; + + listenAddr = mkOption { + description = "Api listening address."; + default = "localhost:9000"; + type = types.str; + }; + + consulAddr = mkOption { + description = "Consul api listening adddress"; + default = "localhost:8500"; + type = types.str; + }; + + watchChecks = mkOption { + description = "Whether to enable check watcher."; + default = true; + type = types.bool; + }; + + watchEvents = mkOption { + description = "Whether to enable event watcher."; + default = true; + type = types.bool; + }; + }; + + }; + + }; + + config = mkIf cfg.enable ( + mkMerge [{ + + users.users.consul = { + description = "Consul agent daemon user"; + uid = config.ids.uids.consul; + # The shell is needed for health checks + shell = "/run/current-system/sw/bin/bash"; + }; + + environment = { + etc."consul.json".text = builtins.toJSON configOptions; + # We need consul.d to exist for consul to start + etc."consul.d/dummy.json".text = "{ }"; + systemPackages = [ cfg.package ]; + }; + + systemd.services.consul = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ] ++ systemdDevices; + bindsTo = systemdDevices; + restartTriggers = [ config.environment.etc."consul.json".source ] + ++ mapAttrsToList (_: d: d.source) + (filterAttrs (n: _: hasPrefix "consul.d/" n) config.environment.etc); + + serviceConfig = { + ExecStart = "@${cfg.package}/bin/consul consul agent -config-dir /etc/consul.d" + + concatMapStrings (n: " -config-file ${n}") configFiles; + ExecReload = "${cfg.package}/bin/consul reload"; + PermissionsStartOnly = true; + User = if cfg.dropPrivileges then "consul" else null; + Restart = "on-failure"; + TimeoutStartSec = "infinity"; + } // (optionalAttrs (cfg.leaveOnStop) { + ExecStop = "${cfg.package}/bin/consul leave"; + }); + + path = with pkgs; [ iproute gnugrep gawk consul ]; + preStart = '' + mkdir -m 0700 -p ${dataDir} + chown -R consul ${dataDir} + + # Determine interface addresses + getAddrOnce () { + ip addr show dev "$1" \ + | grep 'inet${optionalString (cfg.forceIpv4) " "}.*scope global' \ + | awk -F '[ /\t]*' '{print $3}' | head -n 1 + } + getAddr () { + ADDR="$(getAddrOnce $1)" + LEFT=60 # Die after 1 minute + while [ -z "$ADDR" ]; do + sleep 1 + LEFT=$(expr $LEFT - 1) + if [ "$LEFT" -eq "0" ]; then + echo "Address lookup timed out" + exit 1 + fi + ADDR="$(getAddrOnce $1)" + done + echo "$ADDR" + } + echo "{" > /etc/consul-addrs.json + delim=" " + '' + + concatStrings (flip mapAttrsToList cfg.interface (name: i: + optionalString (i != null) '' + echo "$delim \"${name}_addr\": \"$(getAddr "${i}")\"" >> /etc/consul-addrs.json + delim="," + '')) + + '' + echo "}" >> /etc/consul-addrs.json + ''; + }; + } + + (mkIf (cfg.alerts.enable) { + systemd.services.consul-alerts = { + wantedBy = [ "multi-user.target" ]; + after = [ "consul.service" ]; + + path = [ cfg.package ]; + + serviceConfig = { + ExecStart = '' + ${cfg.alerts.package}/bin/consul-alerts start \ + --alert-addr=${cfg.alerts.listenAddr} \ + --consul-addr=${cfg.alerts.consulAddr} \ + ${optionalString cfg.alerts.watchChecks "--watch-checks"} \ + ${optionalString cfg.alerts.watchEvents "--watch-events"} + ''; + User = if cfg.dropPrivileges then "consul" else null; + Restart = "on-failure"; + }; + }; + }) + + ]); +} diff --git a/nixpkgs/nixos/modules/services/networking/coredns.nix b/nixpkgs/nixos/modules/services/networking/coredns.nix new file mode 100644 index 000000000000..afb2b547a465 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/coredns.nix @@ -0,0 +1,50 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.coredns; + configFile = pkgs.writeText "Corefile" cfg.config; +in { + options.services.coredns = { + enable = mkEnableOption "Coredns dns server"; + + config = mkOption { + default = ""; + example = '' + . { + whoami + } + ''; + type = types.lines; + description = "Verbatim Corefile to use. See <link xlink:href=\"https://coredns.io/manual/toc/#configuration\"/> for details."; + }; + + package = mkOption { + default = pkgs.coredns; + defaultText = "pkgs.coredns"; + type = types.package; + description = "Coredns package to use."; + }; + }; + + config = mkIf cfg.enable { + systemd.services.coredns = { + description = "Coredns dns server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + PermissionsStartOnly = true; + LimitNPROC = 512; + LimitNOFILE = 1048576; + CapabilityBoundingSet = "cap_net_bind_service"; + AmbientCapabilities = "cap_net_bind_service"; + NoNewPrivileges = true; + DynamicUser = true; + ExecStart = "${getBin cfg.package}/bin/coredns -conf=${configFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -SIGUSR1 $MAINPID"; + Restart = "on-failure"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/corerad.nix b/nixpkgs/nixos/modules/services/networking/corerad.nix new file mode 100644 index 000000000000..5d73c0a0d779 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/corerad.nix @@ -0,0 +1,85 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.corerad; + + writeTOML = name: x: + pkgs.runCommandNoCCLocal name { + passAsFile = ["config"]; + config = builtins.toJSON x; + buildInputs = [ pkgs.go-toml ]; + } "jsontoml < $configPath > $out"; + +in { + meta.maintainers = with maintainers; [ mdlayher ]; + + options.services.corerad = { + enable = mkEnableOption "CoreRAD IPv6 NDP RA daemon"; + + settings = mkOption { + type = types.uniq types.attrs; + example = literalExample '' + { + interfaces = [ + # eth0 is an upstream interface monitoring for IPv6 router advertisements. + { + name = "eth0"; + monitor = true; + } + # eth1 is a downstream interface advertising IPv6 prefixes for SLAAC. + { + name = "eth1"; + advertise = true; + prefix = [{ prefix = "::/64"; }]; + } + ]; + # Optionally enable Prometheus metrics. + debug = { + address = "localhost:9430"; + prometheus = true; + }; + } + ''; + description = '' + Configuration for CoreRAD, see <link xlink:href="https://github.com/mdlayher/corerad/blob/master/internal/config/default.toml"/> + for supported values. Ignored if configFile is set. + ''; + }; + + configFile = mkOption { + type = types.path; + example = literalExample "\"\${pkgs.corerad}/etc/corerad/corerad.toml\""; + description = "Path to CoreRAD TOML configuration file."; + }; + + package = mkOption { + default = pkgs.corerad; + defaultText = literalExample "pkgs.corerad"; + type = types.package; + description = "CoreRAD package to use."; + }; + }; + + config = mkIf cfg.enable { + # Prefer the config file over settings if both are set. + services.corerad.configFile = mkDefault (writeTOML "corerad.toml" cfg.settings); + + systemd.services.corerad = { + description = "CoreRAD IPv6 NDP RA daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + LimitNPROC = 512; + LimitNOFILE = 1048576; + CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW"; + AmbientCapabilities = "CAP_NET_ADMIN CAP_NET_RAW"; + NoNewPrivileges = true; + DynamicUser = true; + ExecStart = "${getBin cfg.package}/bin/corerad -c=${cfg.configFile}"; + Restart = "on-failure"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/coturn.nix b/nixpkgs/nixos/modules/services/networking/coturn.nix new file mode 100644 index 000000000000..1bfbc307c59d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/coturn.nix @@ -0,0 +1,334 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.coturn; + pidfile = "/run/turnserver/turnserver.pid"; + configFile = pkgs.writeText "turnserver.conf" '' +listening-port=${toString cfg.listening-port} +tls-listening-port=${toString cfg.tls-listening-port} +alt-listening-port=${toString cfg.alt-listening-port} +alt-tls-listening-port=${toString cfg.alt-tls-listening-port} +${concatStringsSep "\n" (map (x: "listening-ip=${x}") cfg.listening-ips)} +${concatStringsSep "\n" (map (x: "relay-ip=${x}") cfg.relay-ips)} +min-port=${toString cfg.min-port} +max-port=${toString cfg.max-port} +${lib.optionalString cfg.lt-cred-mech "lt-cred-mech"} +${lib.optionalString cfg.no-auth "no-auth"} +${lib.optionalString cfg.use-auth-secret "use-auth-secret"} +${lib.optionalString (cfg.static-auth-secret != null) ("static-auth-secret=${cfg.static-auth-secret}")} +realm=${cfg.realm} +${lib.optionalString cfg.no-udp "no-udp"} +${lib.optionalString cfg.no-tcp "no-tcp"} +${lib.optionalString cfg.no-tls "no-tls"} +${lib.optionalString cfg.no-dtls "no-dtls"} +${lib.optionalString cfg.no-udp-relay "no-udp-relay"} +${lib.optionalString cfg.no-tcp-relay "no-tcp-relay"} +${lib.optionalString (cfg.cert != null) "cert=${cfg.cert}"} +${lib.optionalString (cfg.pkey != null) "pkey=${cfg.pkey}"} +${lib.optionalString (cfg.dh-file != null) ("dh-file=${cfg.dh-file}")} +no-stdout-log +syslog +pidfile=${pidfile} +${lib.optionalString cfg.secure-stun "secure-stun"} +${lib.optionalString cfg.no-cli "no-cli"} +cli-ip=${cfg.cli-ip} +cli-port=${toString cfg.cli-port} +${lib.optionalString (cfg.cli-password != null) ("cli-password=${cfg.cli-password}")} +${cfg.extraConfig} +''; +in { + options = { + services.coturn = { + enable = mkEnableOption "coturn TURN server"; + listening-port = mkOption { + type = types.int; + default = 3478; + description = '' + TURN listener port for UDP and TCP. + Note: actually, TLS and DTLS sessions can connect to the + "plain" TCP and UDP port(s), too - if allowed by configuration. + ''; + }; + tls-listening-port = mkOption { + type = types.int; + default = 5349; + description = '' + TURN listener port for TLS. + Note: actually, "plain" TCP and UDP sessions can connect to the TLS and + DTLS port(s), too - if allowed by configuration. The TURN server + "automatically" recognizes the type of traffic. Actually, two listening + endpoints (the "plain" one and the "tls" one) are equivalent in terms of + functionality; but we keep both endpoints to satisfy the RFC 5766 specs. + For secure TCP connections, we currently support SSL version 3 and + TLS version 1.0, 1.1 and 1.2. + For secure UDP connections, we support DTLS version 1. + ''; + }; + alt-listening-port = mkOption { + type = types.int; + default = cfg.listening-port + 1; + defaultText = "listening-port + 1"; + description = '' + Alternative listening port for UDP and TCP listeners; + default (or zero) value means "listening port plus one". + This is needed for RFC 5780 support + (STUN extension specs, NAT behavior discovery). The TURN Server + supports RFC 5780 only if it is started with more than one + listening IP address of the same family (IPv4 or IPv6). + RFC 5780 is supported only by UDP protocol, other protocols + are listening to that endpoint only for "symmetry". + ''; + }; + alt-tls-listening-port = mkOption { + type = types.int; + default = cfg.tls-listening-port + 1; + defaultText = "tls-listening-port + 1"; + description = '' + Alternative listening port for TLS and DTLS protocols. + ''; + }; + listening-ips = mkOption { + type = types.listOf types.str; + default = []; + example = [ "203.0.113.42" "2001:DB8::42" ]; + description = '' + Listener IP addresses of relay server. + If no IP(s) specified in the config file or in the command line options, + then all IPv4 and IPv6 system IPs will be used for listening. + ''; + }; + relay-ips = mkOption { + type = types.listOf types.str; + default = []; + example = [ "203.0.113.42" "2001:DB8::42" ]; + description = '' + Relay address (the local IP address that will be used to relay the + packets to the peer). + Multiple relay addresses may be used. + The same IP(s) can be used as both listening IP(s) and relay IP(s). + + If no relay IP(s) specified, then the turnserver will apply the default + policy: it will decide itself which relay addresses to be used, and it + will always be using the client socket IP address as the relay IP address + of the TURN session (if the requested relay address family is the same + as the family of the client socket). + ''; + }; + min-port = mkOption { + type = types.int; + default = 49152; + description = '' + Lower bound of UDP relay endpoints + ''; + }; + max-port = mkOption { + type = types.int; + default = 65535; + description = '' + Upper bound of UDP relay endpoints + ''; + }; + lt-cred-mech = mkOption { + type = types.bool; + default = false; + description = '' + Use long-term credential mechanism. + ''; + }; + no-auth = mkOption { + type = types.bool; + default = false; + description = '' + This option is opposite to lt-cred-mech. + (TURN Server with no-auth option allows anonymous access). + If neither option is defined, and no users are defined, + then no-auth is default. If at least one user is defined, + in this file or in command line or in usersdb file, then + lt-cred-mech is default. + ''; + }; + use-auth-secret = mkOption { + type = types.bool; + default = false; + description = '' + TURN REST API flag. + Flag that sets a special authorization option that is based upon authentication secret. + This feature can be used with the long-term authentication mechanism, only. + This feature purpose is to support "TURN Server REST API", see + "TURN REST API" link in the project's page + https://github.com/coturn/coturn/ + + This option is used with timestamp: + + usercombo -> "timestamp:userid" + turn user -> usercombo + turn password -> base64(hmac(secret key, usercombo)) + + This allows TURN credentials to be accounted for a specific user id. + If you don't have a suitable id, the timestamp alone can be used. + This option is just turning on secret-based authentication. + The actual value of the secret is defined either by option static-auth-secret, + or can be found in the turn_secret table in the database. + ''; + }; + static-auth-secret = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + 'Static' authentication secret value (a string) for TURN REST API only. + If not set, then the turn server + will try to use the 'dynamic' value in turn_secret table + in user database (if present). The database-stored value can be changed on-the-fly + by a separate program, so this is why that other mode is 'dynamic'. + ''; + }; + realm = mkOption { + type = types.str; + default = config.networking.hostName; + example = "example.com"; + description = '' + The default realm to be used for the users when no explicit + origin/realm relationship was found in the database, or if the TURN + server is not using any database (just the commands-line settings + and the userdb file). Must be used with long-term credentials + mechanism or with TURN REST API. + ''; + }; + cert = mkOption { + type = types.nullOr types.str; + default = null; + example = "/var/lib/acme/example.com/fullchain.pem"; + description = '' + Certificate file in PEM format. + ''; + }; + pkey = mkOption { + type = types.nullOr types.str; + default = null; + example = "/var/lib/acme/example.com/key.pem"; + description = '' + Private key file in PEM format. + ''; + }; + dh-file = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Use custom DH TLS key, stored in PEM format in the file. + ''; + }; + secure-stun = mkOption { + type = types.bool; + default = false; + description = '' + Require authentication of the STUN Binding request. + By default, the clients are allowed anonymous access to the STUN Binding functionality. + ''; + }; + no-cli = mkOption { + type = types.bool; + default = false; + description = '' + Turn OFF the CLI support. + ''; + }; + cli-ip = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + Local system IP address to be used for CLI server endpoint. + ''; + }; + cli-port = mkOption { + type = types.int; + default = 5766; + description = '' + CLI server port. + ''; + }; + cli-password = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + CLI access password. + For the security reasons, it is recommended to use the encrypted + for of the password (see the -P command in the turnadmin utility). + ''; + }; + no-udp = mkOption { + type = types.bool; + default = false; + description = "Disable UDP client listener"; + }; + no-tcp = mkOption { + type = types.bool; + default = false; + description = "Disable TCP client listener"; + }; + no-tls = mkOption { + type = types.bool; + default = false; + description = "Disable TLS client listener"; + }; + no-dtls = mkOption { + type = types.bool; + default = false; + description = "Disable DTLS client listener"; + }; + no-udp-relay = mkOption { + type = types.bool; + default = false; + description = "Disable UDP relay endpoints"; + }; + no-tcp-relay = mkOption { + type = types.bool; + default = false; + description = "Disable TCP relay endpoints"; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Additional configuration options"; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.turnserver = + { uid = config.ids.uids.turnserver; + description = "coturn TURN server user"; + }; + users.groups.turnserver = + { gid = config.ids.gids.turnserver; + members = [ "turnserver" ]; + }; + + systemd.services.coturn = { + description = "coturn TURN server"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + unitConfig = { + Documentation = "man:coturn(1) man:turnadmin(1) man:turnserver(1)"; + }; + + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.coturn}/bin/turnserver -c ${configFile}"; + RuntimeDirectory = "turnserver"; + User = "turnserver"; + Group = "turnserver"; + AmbientCapabilities = + mkIf ( + cfg.listening-port < 1024 || + cfg.alt-listening-port < 1024 || + cfg.tls-listening-port < 1024 || + cfg.alt-tls-listening-port < 1024 || + cfg.min-port < 1024 + ) "cap_net_bind_service"; + Restart = "on-abort"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/dante.nix b/nixpkgs/nixos/modules/services/networking/dante.nix new file mode 100644 index 000000000000..20d4faa1cdb1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dante.nix @@ -0,0 +1,62 @@ +{ config, lib, pkgs, ... }: +with lib; + +let + cfg = config.services.dante; + confFile = pkgs.writeText "dante-sockd.conf" '' + user.privileged: root + user.unprivileged: dante + logoutput: syslog + + ${cfg.config} + ''; +in + +{ + meta = { + maintainers = with maintainers; [ arobyn ]; + }; + + options = { + services.dante = { + enable = mkEnableOption "Dante SOCKS proxy"; + + config = mkOption { + type = types.lines; + description = '' + Contents of Dante's configuration file. + NOTE: user.privileged, user.unprivileged and logoutput are set by the service. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { assertion = cfg.config != ""; + message = "please provide Dante configuration file contents"; + } + ]; + + users.users.dante = { + description = "Dante SOCKS proxy daemon user"; + isSystemUser = true; + group = "dante"; + }; + users.groups.dante = {}; + + systemd.services.dante = { + description = "Dante SOCKS v4 and v5 compatible proxy server"; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.dante}/bin/sockd -f ${confFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + # Can crash sometimes; see https://github.com/NixOS/nixpkgs/pull/39005#issuecomment-381828708 + Restart = "on-failure"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/ddclient.nix b/nixpkgs/nixos/modules/services/networking/ddclient.nix new file mode 100644 index 000000000000..053efe712709 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ddclient.nix @@ -0,0 +1,209 @@ +{ config, pkgs, lib, ... }: + +let + cfg = config.services.ddclient; + boolToStr = bool: if bool then "yes" else "no"; + dataDir = "/var/lib/ddclient"; + + configText = '' + # This file can be used as a template for configFile or is automatically generated by Nix options. + cache=${dataDir}/ddclient.cache + foreground=YES + use=${cfg.use} + login=${cfg.username} + password=${cfg.password} + protocol=${cfg.protocol} + ${lib.optionalString (cfg.script != "") "script=${cfg.script}"} + ${lib.optionalString (cfg.server != "") "server=${cfg.server}"} + ${lib.optionalString (cfg.zone != "") "zone=${cfg.zone}"} + ssl=${boolToStr cfg.ssl} + wildcard=YES + quiet=${boolToStr cfg.quiet} + verbose=${boolToStr cfg.verbose} + ${cfg.extraConfig} + ${lib.concatStringsSep "," cfg.domains} + ''; + +in + +with lib; + +{ + + imports = [ + (mkChangedOptionModule [ "services" "ddclient" "domain" ] [ "services" "ddclient" "domains" ] + (config: + let value = getAttrFromPath [ "services" "ddclient" "domain" ] config; + in if value != "" then [ value ] else [])) + (mkRemovedOptionModule [ "services" "ddclient" "homeDir" ] "") + ]; + + ###### interface + + options = { + + services.ddclient = with lib.types; { + + enable = mkOption { + default = false; + type = bool; + description = '' + Whether to synchronise your machine's IP address with a dynamic DNS provider (e.g. dyndns.org). + ''; + }; + + domains = mkOption { + default = [ "" ]; + type = listOf str; + description = '' + Domain name(s) to synchronize. + ''; + }; + + username = mkOption { + default = ""; + type = str; + description = '' + User name. + ''; + }; + + password = mkOption { + default = ""; + type = str; + description = '' + Password. WARNING: The password becomes world readable in the Nix store. + ''; + }; + + interval = mkOption { + default = "10min"; + type = str; + description = '' + The interval at which to run the check and update. + See <command>man 7 systemd.time</command> for the format. + ''; + }; + + configFile = mkOption { + default = "/etc/ddclient.conf"; + type = path; + description = '' + Path to configuration file. + When set to the default '/etc/ddclient.conf' it will be populated with the various other options in this module. When it is changed (for example: '/root/nixos/secrets/ddclient.conf') the file read directly to configure ddclient. This is a source of impurity. + The purpose of this is to avoid placing secrets into the store. + ''; + example = "/root/nixos/secrets/ddclient.conf"; + }; + + protocol = mkOption { + default = "dyndns2"; + type = str; + description = '' + Protocol to use with dynamic DNS provider (see https://sourceforge.net/p/ddclient/wiki/protocols). + ''; + }; + + server = mkOption { + default = ""; + type = str; + description = '' + Server address. + ''; + }; + + ssl = mkOption { + default = true; + type = bool; + description = '' + Whether to use to use SSL/TLS to connect to dynamic DNS provider. + ''; + }; + + + quiet = mkOption { + default = false; + type = bool; + description = '' + Print no messages for unnecessary updates. + ''; + }; + + script = mkOption { + default = ""; + type = str; + description = '' + script as required by some providers. + ''; + }; + + use = mkOption { + default = "web, web=checkip.dyndns.com/, web-skip='Current IP Address: '"; + type = str; + description = '' + Method to determine the IP address to send to the dynamic DNS provider. + ''; + }; + + verbose = mkOption { + default = true; + type = bool; + description = '' + Print verbose information. + ''; + }; + + zone = mkOption { + default = ""; + type = str; + description = '' + zone as required by some providers. + ''; + }; + + extraConfig = mkOption { + default = ""; + type = lines; + description = '' + Extra configuration. Contents will be added verbatim to the configuration file. + ''; + }; + }; + }; + + + ###### implementation + + config = mkIf config.services.ddclient.enable { + environment.etc."ddclient.conf" = { + enable = cfg.configFile == "/etc/ddclient.conf"; + mode = "0600"; + text = configText; + }; + + systemd.services.ddclient = { + description = "Dynamic DNS Client"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + restartTriggers = [ config.environment.etc."ddclient.conf".source ]; + + serviceConfig = rec { + DynamicUser = true; + RuntimeDirectory = StateDirectory; + StateDirectory = builtins.baseNameOf dataDir; + Type = "oneshot"; + ExecStartPre = "!${lib.getBin pkgs.coreutils}/bin/install -m666 ${cfg.configFile} /run/${RuntimeDirectory}/ddclient.conf"; + ExecStart = "${lib.getBin pkgs.ddclient}/bin/ddclient -file /run/${RuntimeDirectory}/ddclient.conf"; + }; + }; + + systemd.timers.ddclient = { + description = "Run ddclient"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = cfg.interval; + OnUnitInactiveSec = cfg.interval; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/dhcpcd.nix b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix new file mode 100644 index 000000000000..0507b739d499 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix @@ -0,0 +1,225 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + dhcpcd = if !config.boot.isContainer then pkgs.dhcpcd else pkgs.dhcpcd.override { udev = null; }; + + cfg = config.networking.dhcpcd; + + interfaces = attrValues config.networking.interfaces; + + enableDHCP = config.networking.dhcpcd.enable && + (config.networking.useDHCP || any (i: i.useDHCP == true) interfaces); + + # Don't start dhcpcd on explicitly configured interfaces or on + # interfaces that are part of a bridge, bond or sit device. + ignoredInterfaces = + map (i: i.name) (filter (i: if i.useDHCP != null then !i.useDHCP else i.ipv4.addresses != [ ]) interfaces) + ++ mapAttrsToList (i: _: i) config.networking.sits + ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bridges)) + ++ flatten (concatMap (i: attrNames (filterAttrs (_: config: config.type != "internal") i.interfaces)) (attrValues config.networking.vswitches)) + ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) config.networking.bonds)) + ++ config.networking.dhcpcd.denyInterfaces; + + arrayAppendOrNull = a1: a2: if a1 == null && a2 == null then null + else if a1 == null then a2 else if a2 == null then a1 + else a1 ++ a2; + + # If dhcp is disabled but explicit interfaces are enabled, + # we need to provide dhcp just for those interfaces. + allowInterfaces = arrayAppendOrNull cfg.allowInterfaces + (if !config.networking.useDHCP && enableDHCP then + map (i: i.name) (filter (i: i.useDHCP == true) interfaces) else null); + + # Config file adapted from the one that ships with dhcpcd. + dhcpcdConf = pkgs.writeText "dhcpcd.conf" + '' + # Inform the DHCP server of our hostname for DDNS. + hostname + + # A list of options to request from the DHCP server. + option domain_name_servers, domain_name, domain_search, host_name + option classless_static_routes, ntp_servers, interface_mtu + + # A ServerID is required by RFC2131. + # Commented out because of many non-compliant DHCP servers in the wild :( + #require dhcp_server_identifier + + # A hook script is provided to lookup the hostname if not set by + # the DHCP server, but it should not be run by default. + nohook lookup-hostname + + # Ignore peth* devices; on Xen, they're renamed physical + # Ethernet cards used for bridging. Likewise for vif* and tap* + # (Xen) and virbr* and vnet* (libvirt). + denyinterfaces ${toString ignoredInterfaces} lo peth* vif* tap* tun* virbr* vnet* vboxnet* sit* + + # Use the list of allowed interfaces if specified + ${optionalString (allowInterfaces != null) "allowinterfaces ${toString allowInterfaces}"} + + # Immediately fork to background if specified, otherwise wait for IP address to be assigned + ${{ + background = "background"; + any = "waitip"; + ipv4 = "waitip 4"; + ipv6 = "waitip 6"; + both = "waitip 4\nwaitip 6"; + if-carrier-up = ""; + }.${cfg.wait}} + + ${cfg.extraConfig} + ''; + + exitHook = pkgs.writeText "dhcpcd.exit-hook" + '' + if [ "$reason" = BOUND -o "$reason" = REBOOT ]; then + # Restart ntpd. We need to restart it to make sure that it + # will actually do something: if ntpd cannot resolve the + # server hostnames in its config file, then it will never do + # anything ever again ("couldn't resolve ..., giving up on + # it"), so we silently lose time synchronisation. This also + # applies to openntpd. + /run/current-system/systemd/bin/systemctl try-reload-or-restart ntpd.service openntpd.service chronyd.service || true + fi + + ${cfg.runHook} + ''; + +in + +{ + + ###### interface + + options = { + + networking.dhcpcd.enable = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable dhcpcd for device configuration. This is mainly to + explicitly disable dhcpcd (for example when using networkd). + ''; + }; + + networking.dhcpcd.persistent = mkOption { + type = types.bool; + default = false; + description = '' + Whenever to leave interfaces configured on dhcpcd daemon + shutdown. Set to true if you have your root or store mounted + over the network or this machine accepts SSH connections + through DHCP interfaces and clients should be notified when + it shuts down. + ''; + }; + + networking.dhcpcd.denyInterfaces = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Disable the DHCP client for any interface whose name matches + any of the shell glob patterns in this list. The purpose of + this option is to blacklist virtual interfaces such as those + created by Xen, libvirt, LXC, etc. + ''; + }; + + networking.dhcpcd.allowInterfaces = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + description = '' + Enable the DHCP client for any interface whose name matches + any of the shell glob patterns in this list. Any interface not + explicitly matched by this pattern will be denied. This pattern only + applies when non-null. + ''; + }; + + networking.dhcpcd.extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Literal string to append to the config file generated for dhcpcd. + ''; + }; + + networking.dhcpcd.runHook = mkOption { + type = types.lines; + default = ""; + example = "if [[ $reason =~ BOUND ]]; then echo $interface: Routers are $new_routers - were $old_routers; fi"; + description = '' + Shell code that will be run after all other hooks. See + `man dhcpcd-run-hooks` for details on what is possible. + ''; + }; + + networking.dhcpcd.wait = mkOption { + type = types.enum [ "background" "any" "ipv4" "ipv6" "both" "if-carrier-up" ]; + default = "any"; + description = '' + This option specifies when the dhcpcd service will fork to background. + If set to "background", dhcpcd will fork to background immediately. + If set to "ipv4" or "ipv6", dhcpcd will wait for the corresponding IP + address to be assigned. If set to "any", dhcpcd will wait for any type + (IPv4 or IPv6) to be assigned. If set to "both", dhcpcd will wait for + both an IPv4 and an IPv6 address before forking. + The option "if-carrier-up" is equivalent to "any" if either ethernet + is plugged nor WiFi is powered, and to "background" otherwise. + ''; + }; + + }; + + + ###### implementation + + config = mkIf enableDHCP { + + systemd.services.dhcpcd = let + cfgN = config.networking; + hasDefaultGatewaySet = (cfgN.defaultGateway != null && cfgN.defaultGateway.address != "") + && (!cfgN.enableIPv6 || (cfgN.defaultGateway6 != null && cfgN.defaultGateway6.address != "")); + in + { description = "DHCP Client"; + + wantedBy = [ "multi-user.target" ] ++ optional (!hasDefaultGatewaySet) "network-online.target"; + wants = [ "network.target" "systemd-udev-settle.service" ]; + before = [ "network-online.target" ]; + after = [ "systemd-udev-settle.service" ]; + + restartTriggers = [ exitHook ]; + + # Stopping dhcpcd during a reconfiguration is undesirable + # because it brings down the network interfaces configured by + # dhcpcd. So do a "systemctl restart" instead. + stopIfChanged = false; + + path = [ dhcpcd pkgs.nettools pkgs.openresolv ]; + + unitConfig.ConditionCapability = "CAP_NET_ADMIN"; + + serviceConfig = + { Type = "forking"; + PIDFile = "/run/dhcpcd.pid"; + ExecStart = "@${dhcpcd}/sbin/dhcpcd dhcpcd --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}"; + ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind"; + Restart = "always"; + }; + }; + + environment.systemPackages = [ dhcpcd ]; + + environment.etc."dhcpcd.exit-hook".source = exitHook; + + powerManagement.resumeCommands = mkIf config.systemd.services.dhcpcd.enable + '' + # Tell dhcpcd to rebind its interfaces if it's running. + /run/current-system/systemd/bin/systemctl reload dhcpcd.service + ''; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/dhcpd.nix b/nixpkgs/nixos/modules/services/networking/dhcpd.nix new file mode 100644 index 000000000000..67f7d8118870 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dhcpd.nix @@ -0,0 +1,214 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg4 = config.services.dhcpd4; + cfg6 = config.services.dhcpd6; + + writeConfig = cfg: pkgs.writeText "dhcpd.conf" + '' + default-lease-time 600; + max-lease-time 7200; + authoritative; + ddns-update-style interim; + log-facility local1; # see dhcpd.nix + + ${cfg.extraConfig} + + ${lib.concatMapStrings + (machine: '' + host ${machine.hostName} { + hardware ethernet ${machine.ethernetAddress}; + fixed-address ${machine.ipAddress}; + } + '') + cfg.machines + } + ''; + + dhcpdService = postfix: cfg: optionalAttrs cfg.enable { + "dhcpd${postfix}" = { + description = "DHCPv${postfix} server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + preStart = '' + mkdir -m 755 -p ${cfg.stateDir} + chown dhcpd:nogroup ${cfg.stateDir} + touch ${cfg.stateDir}/dhcpd.leases + ''; + + serviceConfig = + let + configFile = if cfg.configFile != null then cfg.configFile else writeConfig cfg; + args = [ "@${pkgs.dhcp}/sbin/dhcpd" "dhcpd${postfix}" "-${postfix}" + "-pf" "/run/dhcpd${postfix}/dhcpd.pid" + "-cf" "${configFile}" + "-lf" "${cfg.stateDir}/dhcpd.leases" + "-user" "dhcpd" "-group" "nogroup" + ] ++ cfg.extraFlags + ++ cfg.interfaces; + + in { + ExecStart = concatMapStringsSep " " escapeShellArg args; + Type = "forking"; + Restart = "always"; + RuntimeDirectory = [ "dhcpd${postfix}" ]; + PIDFile = "/run/dhcpd${postfix}/dhcpd.pid"; + }; + }; + }; + + machineOpts = { ... }: { + + options = { + + hostName = mkOption { + type = types.str; + example = "foo"; + description = '' + Hostname which is assigned statically to the machine. + ''; + }; + + ethernetAddress = mkOption { + type = types.str; + example = "00:16:76:9a:32:1d"; + description = '' + MAC address of the machine. + ''; + }; + + ipAddress = mkOption { + type = types.str; + example = "192.168.1.10"; + description = '' + IP address of the machine. + ''; + }; + + }; + }; + + dhcpConfig = postfix: { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the DHCPv${postfix} server. + ''; + }; + + stateDir = mkOption { + type = types.path; + # We use /var/lib/dhcp for DHCPv4 to save backwards compatibility. + default = "/var/lib/dhcp${if postfix == "4" then "" else postfix}"; + description = '' + State directory for the DHCP server. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + option subnet-mask 255.255.255.0; + option broadcast-address 192.168.1.255; + option routers 192.168.1.5; + option domain-name-servers 130.161.158.4, 130.161.33.17, 130.161.180.1; + option domain-name "example.org"; + subnet 192.168.1.0 netmask 255.255.255.0 { + range 192.168.1.100 192.168.1.200; + } + ''; + description = '' + Extra text to be appended to the DHCP server configuration + file. Currently, you almost certainly need to specify something + there, such as the options specifying the subnet mask, DNS servers, + etc. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Additional command line flags to be passed to the dhcpd daemon. + ''; + }; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + The path of the DHCP server configuration file. If no file + is specified, a file is generated using the other options. + ''; + }; + + interfaces = mkOption { + type = types.listOf types.str; + default = ["eth0"]; + description = '' + The interfaces on which the DHCP server should listen. + ''; + }; + + machines = mkOption { + type = with types; listOf (submodule machineOpts); + default = []; + example = [ + { hostName = "foo"; + ethernetAddress = "00:16:76:9a:32:1d"; + ipAddress = "192.168.1.10"; + } + { hostName = "bar"; + ethernetAddress = "00:19:d1:1d:c4:9a"; + ipAddress = "192.168.1.11"; + } + ]; + description = '' + A list mapping Ethernet addresses to IPv${postfix} addresses for the + DHCP server. + ''; + }; + + }; + +in + +{ + + imports = [ + (mkRenamedOptionModule [ "services" "dhcpd" ] [ "services" "dhcpd4" ]) + ]; + + ###### interface + + options = { + + services.dhcpd4 = dhcpConfig "4"; + services.dhcpd6 = dhcpConfig "6"; + + }; + + + ###### implementation + + config = mkIf (cfg4.enable || cfg6.enable) { + + users = { + users.dhcpd = { + uid = config.ids.uids.dhcpd; + description = "DHCP daemon user"; + }; + }; + + systemd.services = dhcpdService "4" cfg4 // dhcpdService "6" cfg6; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/dnscache.nix b/nixpkgs/nixos/modules/services/networking/dnscache.nix new file mode 100644 index 000000000000..d06032daecc7 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnscache.nix @@ -0,0 +1,108 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.dnscache; + + dnscache-root = pkgs.runCommand "dnscache-root" { preferLocalBuild = true; } '' + mkdir -p $out/{servers,ip} + + ${concatMapStrings (ip: '' + touch "$out/ip/"${lib.escapeShellArg ip} + '') cfg.clientIps} + + ${concatStrings (mapAttrsToList (host: ips: '' + ${concatMapStrings (ip: '' + echo ${lib.escapeShellArg ip} >> "$out/servers/"${lib.escapeShellArg host} + '') ips} + '') cfg.domainServers)} + + # if a list of root servers was not provided in config, copy it + # over. (this is also done by dnscache-conf, but we 'rm -rf + # /var/lib/dnscache/root' below & replace it wholesale with this, + # so we have to ensure servers/@ exists ourselves.) + if [ ! -e $out/servers/@ ]; then + # symlink does not work here, due chroot + cp ${pkgs.djbdns}/etc/dnsroots.global $out/servers/@; + fi + ''; + +in { + + ###### interface + + options = { + services.dnscache = { + + enable = mkOption { + default = false; + type = types.bool; + description = "Whether to run the dnscache caching dns server."; + }; + + ip = mkOption { + default = "0.0.0.0"; + type = types.str; + description = "IP address on which to listen for connections."; + }; + + clientIps = mkOption { + default = [ "127.0.0.1" ]; + type = types.listOf types.str; + description = "Client IP addresses (or prefixes) from which to accept connections."; + example = ["192.168" "172.23.75.82"]; + }; + + domainServers = mkOption { + default = { }; + type = types.attrsOf (types.listOf types.str); + description = '' + Table of {hostname: server} pairs to use as authoritative servers for hosts (and subhosts). + If entry for @ is not specified predefined list of root servers is used. + ''; + example = literalExample '' + { + "@" = ["8.8.8.8" "8.8.4.4"]; + "example.com" = ["192.168.100.100"]; + } + ''; + }; + + forwardOnly = mkOption { + default = false; + type = types.bool; + description = '' + Whether to treat root servers (for @) as caching + servers, requesting addresses the same way a client does. This is + needed if you want to use e.g. Google DNS as your upstream DNS. + ''; + }; + + }; + }; + + ###### implementation + + config = mkIf config.services.dnscache.enable { + environment.systemPackages = [ pkgs.djbdns ]; + users.users.dnscache.isSystemUser = true; + + systemd.services.dnscache = { + description = "djbdns dnscache server"; + wantedBy = [ "multi-user.target" ]; + path = with pkgs; [ bash daemontools djbdns ]; + preStart = '' + rm -rf /var/lib/dnscache + dnscache-conf dnscache dnscache /var/lib/dnscache ${config.services.dnscache.ip} + rm -rf /var/lib/dnscache/root + ln -sf ${dnscache-root} /var/lib/dnscache/root + ''; + script = '' + cd /var/lib/dnscache/ + ${optionalString cfg.forwardOnly "export FORWARDONLY=1"} + exec ./run + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy2.nix b/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy2.nix new file mode 100644 index 000000000000..28691e838277 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy2.nix @@ -0,0 +1,62 @@ +{ config, lib, pkgs, ... }: with lib; + +let + cfg = config.services.dnscrypt-proxy2; +in + +{ + options.services.dnscrypt-proxy2 = { + enable = mkEnableOption "dnscrypt-proxy2"; + + settings = mkOption { + description = '' + Attrset that is converted and passed as TOML config file. + For available params, see: <link xlink:href="https://github.com/DNSCrypt/dnscrypt-proxy/blob/master/dnscrypt-proxy/example-dnscrypt-proxy.toml"/> + ''; + example = literalExample '' + { + sources.public-resolvers = { + urls = [ "https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md" ]; + cache_file = "public-resolvers.md"; + minisign_key = "RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3"; + refresh_delay = 72; + }; + } + ''; + type = types.attrs; + default = {}; + }; + + configFile = mkOption { + description = '' + Path to TOML config file. See: <link xlink:href="https://github.com/DNSCrypt/dnscrypt-proxy/blob/master/dnscrypt-proxy/example-dnscrypt-proxy.toml"/> + If this option is set, it will override any configuration done in options.services.dnscrypt-proxy2.settings. + ''; + example = "/etc/dnscrypt-proxy/dnscrypt-proxy.toml"; + type = types.path; + default = pkgs.runCommand "dnscrypt-proxy.toml" { + json = builtins.toJSON cfg.settings; + passAsFile = [ "json" ]; + } '' + ${pkgs.remarshal}/bin/json2toml < $jsonPath > $out + ''; + defaultText = literalExample "TOML file generated from services.dnscrypt-proxy2.settings"; + }; + }; + + config = mkIf cfg.enable { + + networking.nameservers = lib.mkDefault [ "127.0.0.1" ]; + + systemd.services.dnscrypt-proxy2 = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + DynamicUser = true; + ExecStart = "${pkgs.dnscrypt-proxy2}/bin/dnscrypt-proxy -config ${cfg.configFile}"; + Restart = "always"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix b/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix new file mode 100644 index 000000000000..b9333cd19a2a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix @@ -0,0 +1,281 @@ +{ config, lib, pkgs, ... }: +with lib; + +let + cfg = config.services.dnscrypt-wrapper; + dataDir = "/var/lib/dnscrypt-wrapper"; + + mkPath = path: default: + if path != null + then toString path + else default; + + publicKey = mkPath cfg.providerKey.public "${dataDir}/public.key"; + secretKey = mkPath cfg.providerKey.secret "${dataDir}/secret.key"; + + daemonArgs = with cfg; [ + "--listen-address=${address}:${toString port}" + "--resolver-address=${upstream.address}:${toString upstream.port}" + "--provider-name=${providerName}" + "--provider-publickey-file=${publicKey}" + "--provider-secretkey-file=${secretKey}" + "--provider-cert-file=${providerName}.crt" + "--crypt-secretkey-file=${providerName}.key" + ]; + + genKeys = '' + # generates time-limited keypairs + keyGen() { + dnscrypt-wrapper --gen-crypt-keypair \ + --crypt-secretkey-file=${cfg.providerName}.key + + dnscrypt-wrapper --gen-cert-file \ + --crypt-secretkey-file=${cfg.providerName}.key \ + --provider-cert-file=${cfg.providerName}.crt \ + --provider-publickey-file=${publicKey} \ + --provider-secretkey-file=${secretKey} \ + --cert-file-expire-days=${toString cfg.keys.expiration} + } + + cd ${dataDir} + + # generate provider keypair (first run only) + ${optionalString (cfg.providerKey.public == null || cfg.providerKey.secret == null) '' + if [ ! -f ${publicKey} ] || [ ! -f ${secretKey} ]; then + dnscrypt-wrapper --gen-provider-keypair + fi + ''} + + # generate new keys for rotation + if [ ! -f ${cfg.providerName}.key ] || [ ! -f ${cfg.providerName}.crt ]; then + keyGen + fi + ''; + + rotateKeys = '' + # check if keys are not expired + keyValid() { + fingerprint=$(dnscrypt-wrapper --show-provider-publickey | awk '{print $(NF)}') + dnscrypt-proxy --test=${toString (cfg.keys.checkInterval + 1)} \ + --resolver-address=127.0.0.1:${toString cfg.port} \ + --provider-name=${cfg.providerName} \ + --provider-key=$fingerprint + } + + cd ${dataDir} + + # archive old keys and restart the service + if ! keyValid; then + echo "certificate soon to become invalid; backing up old cert" + mkdir -p oldkeys + mv -v ${cfg.providerName}.key oldkeys/${cfg.providerName}-$(date +%F-%T).key + mv -v ${cfg.providerName}.crt oldkeys/${cfg.providerName}-$(date +%F-%T).crt + systemctl restart dnscrypt-wrapper + fi + ''; + + + # This is the fork of the original dnscrypt-proxy maintained by Dyne.org. + # dnscrypt-proxy2 doesn't provide the `--test` feature that is needed to + # correctly implement key rotation of dnscrypt-wrapper ephemeral keys. + dnscrypt-proxy1 = pkgs.callPackage + ({ stdenv, fetchFromGitHub, autoreconfHook + , pkgconfig, libsodium, ldns, openssl, systemd }: + + stdenv.mkDerivation rec { + pname = "dnscrypt-proxy"; + version = "2019-08-20"; + + src = fetchFromGitHub { + owner = "dyne"; + repo = "dnscrypt-proxy"; + rev = "07ac3825b5069adc28e2547c16b1d983a8ed8d80"; + sha256 = "0c4mq741q4rpmdn09agwmxap32kf0vgfz7pkhcdc5h54chc3g3xy"; + }; + + configureFlags = optional stdenv.isLinux "--with-systemd"; + + nativeBuildInputs = [ autoreconfHook pkgconfig ]; + + # <ldns/ldns.h> depends on <openssl/ssl.h> + buildInputs = [ libsodium openssl.dev ldns ] ++ optional stdenv.isLinux systemd; + + postInstall = '' + # Previous versions required libtool files to load plugins; they are + # now strictly optional. + rm $out/lib/dnscrypt-proxy/*.la + ''; + + meta = { + description = "A tool for securing communications between a client and a DNS resolver"; + homepage = "https://github.com/dyne/dnscrypt-proxy"; + license = licenses.isc; + maintainers = with maintainers; [ rnhmjoj ]; + platforms = platforms.linux; + }; + }) { }; + +in { + + + ###### interface + + options.services.dnscrypt-wrapper = { + enable = mkEnableOption "DNSCrypt wrapper"; + + address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + The DNSCrypt wrapper will bind to this IP address. + ''; + }; + + port = mkOption { + type = types.int; + default = 5353; + description = '' + The DNSCrypt wrapper will listen for DNS queries on this port. + ''; + }; + + providerName = mkOption { + type = types.str; + default = "2.dnscrypt-cert.${config.networking.hostName}"; + example = "2.dnscrypt-cert.myresolver"; + description = '' + The name that will be given to this DNSCrypt resolver. + Note: the resolver name must start with <literal>2.dnscrypt-cert.</literal>. + ''; + }; + + providerKey.public = mkOption { + type = types.nullOr types.path; + default = null; + example = "/etc/secrets/public.key"; + description = '' + The filepath to the provider public key. If not given a new + provider key pair will be generated on the first run. + ''; + }; + + providerKey.secret = mkOption { + type = types.nullOr types.path; + default = null; + example = "/etc/secrets/secret.key"; + description = '' + The filepath to the provider secret key. If not given a new + provider key pair will be generated on the first run. + ''; + }; + + upstream.address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + The IP address of the upstream DNS server DNSCrypt will "wrap". + ''; + }; + + upstream.port = mkOption { + type = types.int; + default = 53; + description = '' + The port of the upstream DNS server DNSCrypt will "wrap". + ''; + }; + + keys.expiration = mkOption { + type = types.int; + default = 30; + description = '' + The duration (in days) of the time-limited secret key. + This will be automatically rotated before expiration. + ''; + }; + + keys.checkInterval = mkOption { + type = types.int; + default = 1440; + description = '' + The time interval (in minutes) between key expiration checks. + ''; + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.users.dnscrypt-wrapper = { + description = "dnscrypt-wrapper daemon user"; + home = "${dataDir}"; + createHome = true; + isSystemUser = true; + }; + users.groups.dnscrypt-wrapper = { }; + + security.polkit.extraConfig = '' + // Allow dnscrypt-wrapper user to restart dnscrypt-wrapper.service + polkit.addRule(function(action, subject) { + if (action.id == "org.freedesktop.systemd1.manage-units" && + action.lookup("unit") == "dnscrypt-wrapper.service" && + subject.user == "dnscrypt-wrapper") { + return polkit.Result.YES; + } + }); + ''; + + systemd.services.dnscrypt-wrapper = { + description = "dnscrypt-wrapper daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.dnscrypt-wrapper ]; + + serviceConfig = { + User = "dnscrypt-wrapper"; + WorkingDirectory = dataDir; + Restart = "on-failure"; + ExecStart = "${pkgs.dnscrypt-wrapper}/bin/dnscrypt-wrapper ${toString daemonArgs}"; + }; + + preStart = genKeys; + }; + + + systemd.services.dnscrypt-wrapper-rotate = { + after = [ "network.target" ]; + requires = [ "dnscrypt-wrapper.service" ]; + description = "Rotates DNSCrypt wrapper keys if soon to expire"; + + path = with pkgs; [ dnscrypt-wrapper dnscrypt-proxy1 gawk ]; + script = rotateKeys; + serviceConfig.User = "dnscrypt-wrapper"; + }; + + + systemd.timers.dnscrypt-wrapper-rotate = { + description = "Periodically check DNSCrypt wrapper keys for expiration"; + wantedBy = [ "multi-user.target" ]; + + timerConfig = { + Unit = "dnscrypt-wrapper-rotate.service"; + OnBootSec = "1min"; + OnUnitActiveSec = cfg.keys.checkInterval * 60; + }; + }; + + assertions = with cfg; [ + { assertion = (providerKey.public == null && providerKey.secret == null) || + (providerKey.secret != null && providerKey.public != null); + message = "The secret and public provider key must be set together."; + } + ]; + + }; + + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; + +} diff --git a/nixpkgs/nixos/modules/services/networking/dnsdist.nix b/nixpkgs/nixos/modules/services/networking/dnsdist.nix new file mode 100644 index 000000000000..8249da69bc1a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnsdist.nix @@ -0,0 +1,60 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.dnsdist; + configFile = pkgs.writeText "dndist.conf" '' + setLocal('${cfg.listenAddress}:${toString cfg.listenPort}') + ${cfg.extraConfig} + ''; +in { + options = { + services.dnsdist = { + enable = mkEnableOption "dnsdist domain name server"; + + listenAddress = mkOption { + type = types.str; + description = "Listen IP Address"; + default = "0.0.0.0"; + }; + listenPort = mkOption { + type = types.int; + description = "Listen port"; + default = 53; + }; + + extraConfig = mkOption { + type = types.lines; + default = '' + ''; + description = '' + Extra lines to be added verbatim to dnsdist.conf. + ''; + }; + }; + }; + + config = mkIf config.services.dnsdist.enable { + systemd.services.dnsdist = { + description = "dnsdist load balancer"; + wantedBy = [ "multi-user.target" ]; + after = ["network.target"]; + + serviceConfig = { + Restart="on-failure"; + RestartSec="1"; + DynamicUser = true; + StartLimitInterval="0"; + PrivateDevices=true; + AmbientCapabilities="CAP_NET_BIND_SERVICE"; + CapabilityBoundingSet="CAP_NET_BIND_SERVICE"; + ExecStart = "${pkgs.dnsdist}/bin/dnsdist --supervised --disable-syslog --config ${configFile}"; + ProtectHome=true; + RestrictAddressFamilies="AF_UNIX AF_INET AF_INET6"; + LimitNOFILE="16384"; + TasksMax="8192"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/dnsmasq.nix b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix new file mode 100644 index 000000000000..377d7bc57058 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix @@ -0,0 +1,128 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.dnsmasq; + dnsmasq = pkgs.dnsmasq; + stateDir = "/var/lib/dnsmasq"; + + dnsmasqConf = pkgs.writeText "dnsmasq.conf" '' + dhcp-leasefile=${stateDir}/dnsmasq.leases + ${optionalString cfg.resolveLocalQueries '' + conf-file=/etc/dnsmasq-conf.conf + resolv-file=/etc/dnsmasq-resolv.conf + ''} + ${flip concatMapStrings cfg.servers (server: '' + server=${server} + '')} + ${cfg.extraConfig} + ''; + +in + +{ + + ###### interface + + options = { + + services.dnsmasq = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run dnsmasq. + ''; + }; + + resolveLocalQueries = mkOption { + type = types.bool; + default = true; + description = '' + Whether dnsmasq should resolve local queries (i.e. add 127.0.0.1 to + /etc/resolv.conf). + ''; + }; + + servers = mkOption { + type = types.listOf types.str; + default = []; + example = [ "8.8.8.8" "8.8.4.4" ]; + description = '' + The DNS servers which dnsmasq should query. + ''; + }; + + alwaysKeepRunning = mkOption { + type = types.bool; + default = false; + description = '' + If enabled, systemd will always respawn dnsmasq even if shut down manually. The default, disabled, will only restart it on error. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration directives that should be added to + <literal>dnsmasq.conf</literal>. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + networking.nameservers = + optional cfg.resolveLocalQueries "127.0.0.1"; + + services.dbus.packages = [ dnsmasq ]; + + users.users.dnsmasq = { + uid = config.ids.uids.dnsmasq; + description = "Dnsmasq daemon user"; + }; + + networking.resolvconf = mkIf cfg.resolveLocalQueries { + useLocalResolver = mkDefault true; + + extraConfig = '' + dnsmasq_conf=/etc/dnsmasq-conf.conf + dnsmasq_resolv=/etc/dnsmasq-resolv.conf + ''; + }; + + systemd.services.dnsmasq = { + description = "Dnsmasq Daemon"; + after = [ "network.target" "systemd-resolved.service" ]; + wantedBy = [ "multi-user.target" ]; + path = [ dnsmasq ]; + preStart = '' + mkdir -m 755 -p ${stateDir} + touch ${stateDir}/dnsmasq.leases + chown -R dnsmasq ${stateDir} + touch /etc/dnsmasq-{conf,resolv}.conf + dnsmasq --test + ''; + serviceConfig = { + Type = "dbus"; + BusName = "uk.org.thekelleys.dnsmasq"; + ExecStart = "${dnsmasq}/bin/dnsmasq -k --enable-dbus --user=dnsmasq -C ${dnsmasqConf}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + PrivateTmp = true; + ProtectSystem = true; + ProtectHome = true; + Restart = if cfg.alwaysKeepRunning then "always" else "on-failure"; + }; + restartTriggers = [ config.environment.etc.hosts.source ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/ejabberd.nix b/nixpkgs/nixos/modules/services/networking/ejabberd.nix new file mode 100644 index 000000000000..a5af25b983b9 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ejabberd.nix @@ -0,0 +1,157 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.ejabberd; + + ctlcfg = pkgs.writeText "ejabberdctl.cfg" '' + ERL_EPMD_ADDRESS=127.0.0.1 + ${cfg.ctlConfig} + ''; + + ectl = ''${cfg.package}/bin/ejabberdctl ${optionalString (cfg.configFile != null) "--config ${cfg.configFile}"} --ctl-config "${ctlcfg}" --spool "${cfg.spoolDir}" --logs "${cfg.logsDir}"''; + + dumps = lib.escapeShellArgs cfg.loadDumps; + +in { + + ###### interface + + options = { + + services.ejabberd = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable ejabberd server"; + }; + + package = mkOption { + type = types.package; + default = pkgs.ejabberd; + defaultText = "pkgs.ejabberd"; + description = "ejabberd server package to use"; + }; + + user = mkOption { + type = types.str; + default = "ejabberd"; + description = "User under which ejabberd is ran"; + }; + + group = mkOption { + type = types.str; + default = "ejabberd"; + description = "Group under which ejabberd is ran"; + }; + + spoolDir = mkOption { + type = types.path; + default = "/var/lib/ejabberd"; + description = "Location of the spooldir of ejabberd"; + }; + + logsDir = mkOption { + type = types.path; + default = "/var/log/ejabberd"; + description = "Location of the logfile directory of ejabberd"; + }; + + configFile = mkOption { + type = types.nullOr types.path; + description = "Configuration file for ejabberd in YAML format"; + default = null; + }; + + ctlConfig = mkOption { + type = types.lines; + default = ""; + description = "Configuration of ejabberdctl"; + }; + + loadDumps = mkOption { + type = types.listOf types.path; + default = []; + description = "Configuration dumps that should be loaded on the first startup"; + example = literalExample "[ ./myejabberd.dump ]"; + }; + + imagemagick = mkOption { + type = types.bool; + default = false; + description = "Add ImageMagick to server's path; allows for image thumbnailing"; + }; + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + users.users = optionalAttrs (cfg.user == "ejabberd") { + ejabberd = { + group = cfg.group; + home = cfg.spoolDir; + createHome = true; + uid = config.ids.uids.ejabberd; + }; + }; + + users.groups = optionalAttrs (cfg.group == "ejabberd") { + ejabberd.gid = config.ids.gids.ejabberd; + }; + + systemd.services.ejabberd = { + description = "ejabberd server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = [ pkgs.findutils pkgs.coreutils ] ++ lib.optional cfg.imagemagick pkgs.imagemagick; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + ExecStart = "${ectl} foreground"; + ExecStop = "${ectl} stop"; + ExecReload = "${ectl} reload_config"; + }; + + preStart = '' + if [ -z "$(ls -A '${cfg.spoolDir}')" ]; then + touch "${cfg.spoolDir}/.firstRun" + fi + ''; + + postStart = '' + while ! ${ectl} status >/dev/null 2>&1; do + if ! kill -0 "$MAINPID"; then exit 1; fi + sleep 0.1 + done + + if [ -e "${cfg.spoolDir}/.firstRun" ]; then + rm "${cfg.spoolDir}/.firstRun" + for src in ${dumps}; do + find "$src" -type f | while read dump; do + echo "Loading configuration dump at $dump" + ${ectl} load "$dump" + done + done + fi + ''; + }; + + systemd.tmpfiles.rules = [ + "d '${cfg.logsDir}' 0750 ${cfg.user} ${cfg.group} -" + "d '${cfg.spoolDir}' 0700 ${cfg.user} ${cfg.group} -" + ]; + + security.pam.services.ejabberd = {}; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/epmd.nix b/nixpkgs/nixos/modules/services/networking/epmd.nix new file mode 100644 index 000000000000..692b75e4f086 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/epmd.nix @@ -0,0 +1,56 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.epmd; + +in + +{ + ###### interface + options.services.epmd = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable socket activation for Erlang Port Mapper Daemon (epmd), + which acts as a name server on all hosts involved in distributed + Erlang computations. + ''; + }; + package = mkOption { + type = types.package; + default = pkgs.erlang; + description = '' + The Erlang package to use to get epmd binary. That way you can re-use + an Erlang runtime that is already installed for other purposes. + ''; + }; + }; + + ###### implementation + config = mkIf cfg.enable { + systemd.sockets.epmd = rec { + description = "Erlang Port Mapper Daemon Activation Socket"; + wantedBy = [ "sockets.target" ]; + before = wantedBy; + socketConfig = { + ListenStream = "4369"; + Accept = "false"; + }; + }; + + systemd.services.epmd = { + description = "Erlang Port Mapper Daemon"; + after = [ "network.target" ]; + requires = [ "epmd.socket" ]; + + serviceConfig = { + DynamicUser = true; + ExecStart = "${cfg.package}/bin/epmd -systemd"; + Type = "notify"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/ergo.nix b/nixpkgs/nixos/modules/services/networking/ergo.nix new file mode 100644 index 000000000000..c52de30dc361 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ergo.nix @@ -0,0 +1,141 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.ergo; + + inherit (lib) mkEnableOption mkIf mkOption optionalString types; + + configFile = pkgs.writeText "ergo.conf" ('' +ergo { + directory = "${cfg.dataDir}" + node { + mining = false + } + wallet.secretStorage.secretDir = "${cfg.dataDir}/wallet/keystore" +} + +scorex { + network { + bindAddress = "${cfg.listen.ip}:${toString cfg.listen.port}" + } +'' + optionalString (cfg.api.keyHash != null) '' + restApi { + apiKeyHash = "${cfg.api.keyHash}" + bindAddress = "${cfg.api.listen.ip}:${toString cfg.api.listen.port}" + } +'' + '' +} +''); + +in { + + options = { + + services.ergo = { + enable = mkEnableOption "Ergo service"; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/ergo"; + description = "The data directory for the Ergo node."; + }; + + listen = { + ip = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "IP address on which the Ergo node should listen."; + }; + + port = mkOption { + type = types.port; + default = 9006; + description = "Listen port for the Ergo node."; + }; + }; + + api = { + keyHash = mkOption { + type = types.nullOr types.str; + default = null; + example = "324dcf027dd4a30a932c441f365a25e86b173defa4b8e58948253471b81b72cf"; + description = "Hex-encoded Blake2b256 hash of an API key as a 64-chars long Base16 string."; + }; + + listen = { + ip = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "IP address that the Ergo node API should listen on if <option>api.keyHash</option> is defined."; + }; + + port = mkOption { + type = types.port; + default = 9052; + description = "Listen port for the API endpoint if <option>api.keyHash</option> is defined."; + }; + }; + }; + + testnet = mkOption { + type = types.bool; + default = false; + description = "Connect to testnet network instead of the default mainnet."; + }; + + user = mkOption { + type = types.str; + default = "ergo"; + description = "The user as which to run the Ergo node."; + }; + + group = mkOption { + type = types.str; + default = cfg.user; + description = "The group as which to run the Ergo node."; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open ports in the firewall for the Ergo node as well as the API."; + }; + }; + }; + + config = mkIf cfg.enable { + + systemd.tmpfiles.rules = [ + "d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -" + ]; + + systemd.services.ergo = { + description = "ergo server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + ExecStart = ''${pkgs.ergo}/bin/ergo \ + ${optionalString (!cfg.testnet) + "--mainnet"} \ + -c ${configFile}''; + }; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.listen.port ] ++ [ cfg.api.listen.port ]; + }; + + users.users.${cfg.user} = { + name = cfg.user; + group = cfg.group; + description = "Ergo daemon user"; + home = cfg.dataDir; + isSystemUser = true; + }; + + users.groups.${cfg.group} = {}; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/eternal-terminal.nix b/nixpkgs/nixos/modules/services/networking/eternal-terminal.nix new file mode 100644 index 000000000000..a2e5b30dc0f0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/eternal-terminal.nix @@ -0,0 +1,95 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.eternal-terminal; + +in + +{ + + ###### interface + + options = { + + services.eternal-terminal = { + + enable = mkEnableOption "Eternal Terminal server"; + + port = mkOption { + default = 2022; + type = types.int; + description = '' + The port the server should listen on. Will use the server's default (2022) if not specified. + + Make sure to open this port in the firewall if necessary. + ''; + }; + + verbosity = mkOption { + default = 0; + type = types.enum (lib.range 0 9); + description = '' + The verbosity level (0-9). + ''; + }; + + silent = mkOption { + default = false; + type = types.bool; + description = '' + If enabled, disables all logging. + ''; + }; + + logSize = mkOption { + default = 20971520; + type = types.int; + description = '' + The maximum log size. + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + # We need to ensure the et package is fully installed because + # the (remote) et client runs the `etterminal` binary when it + # connects. + environment.systemPackages = [ pkgs.eternal-terminal ]; + + systemd.services = { + eternal-terminal = { + description = "Eternal Terminal server."; + wantedBy = [ "multi-user.target" ]; + after = [ "syslog.target" "network.target" ]; + serviceConfig = { + Type = "forking"; + ExecStart = "${pkgs.eternal-terminal}/bin/etserver --daemon --cfgfile=${pkgs.writeText "et.cfg" '' + ; et.cfg : Config file for Eternal Terminal + ; + + [Networking] + port = ${toString cfg.port} + + [Debug] + verbose = ${toString cfg.verbosity} + silent = ${if cfg.silent then "1" else "0"} + logsize = ${toString cfg.logSize} + ''}"; + Restart = "on-failure"; + KillMode = "process"; + }; + }; + }; + }; + + meta = { + maintainers = with lib.maintainers; [ pingiun ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/fakeroute.nix b/nixpkgs/nixos/modules/services/networking/fakeroute.nix new file mode 100644 index 000000000000..7916ad4098a7 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/fakeroute.nix @@ -0,0 +1,65 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.fakeroute; + routeConf = pkgs.writeText "route.conf" (concatStringsSep "\n" cfg.route); + +in + +{ + + ###### interface + + options = { + + services.fakeroute = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the fakeroute service. + ''; + }; + + route = mkOption { + type = types.listOf types.str; + default = []; + example = [ + "216.102.187.130" + "4.0.1.122" + "198.116.142.34" + "63.199.8.242" + ]; + description = '' + Fake route that will appear after the real + one to any host running a traceroute. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + systemd.services.fakeroute = { + description = "Fakeroute Daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "forking"; + User = "root"; + ExecStart = "${pkgs.fakeroute}/bin/fakeroute -f ${routeConf}"; + }; + }; + + }; + + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; + +} diff --git a/nixpkgs/nixos/modules/services/networking/ferm.nix b/nixpkgs/nixos/modules/services/networking/ferm.nix new file mode 100644 index 000000000000..07338ccf4d9c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ferm.nix @@ -0,0 +1,63 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.ferm; + + configFile = pkgs.stdenv.mkDerivation { + name = "ferm.conf"; + text = cfg.config; + preferLocalBuild = true; + buildCommand = '' + echo -n "$text" > $out + ${cfg.package}/bin/ferm --noexec $out + ''; + }; +in { + options = { + services.ferm = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable Ferm Firewall. + *Warning*: Enabling this service WILL disable the existing NixOS + firewall! Default firewall rules provided by packages are not + considered at the moment. + ''; + }; + config = mkOption { + description = "Verbatim ferm.conf configuration."; + default = ""; + defaultText = "empty firewall, allows any traffic"; + type = types.lines; + }; + package = mkOption { + description = "The ferm package."; + type = types.package; + default = pkgs.ferm; + defaultText = "pkgs.ferm"; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.firewall.enable = false; + systemd.services.ferm = { + description = "Ferm Firewall"; + after = [ "ipset.target" ]; + before = [ "network-pre.target" ]; + wants = [ "network-pre.target" ]; + wantedBy = [ "multi-user.target" ]; + reloadIfChanged = true; + serviceConfig = { + Type="oneshot"; + RemainAfterExit = "yes"; + ExecStart = "${cfg.package}/bin/ferm ${configFile}"; + ExecReload = "${cfg.package}/bin/ferm ${configFile}"; + ExecStop = "${cfg.package}/bin/ferm -F ${configFile}"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/firefox/sync-server.nix b/nixpkgs/nixos/modules/services/networking/firefox/sync-server.nix new file mode 100644 index 000000000000..6842aa735617 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/firefox/sync-server.nix @@ -0,0 +1,183 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.firefox.syncserver; + + defaultDbLocation = "/var/db/firefox-sync-server/firefox-sync-server.db"; + defaultSqlUri = "sqlite:///${defaultDbLocation}"; + + syncServerIni = pkgs.writeText "syncserver.ini" '' + [DEFAULT] + overrides = ${cfg.privateConfig} + + [server:main] + use = egg:gunicorn + host = ${cfg.listen.address} + port = ${toString cfg.listen.port} + + [app:main] + use = egg:syncserver + + [syncserver] + public_url = ${cfg.publicUrl} + ${optionalString (cfg.sqlUri != "") "sqluri = ${cfg.sqlUri}"} + allow_new_users = ${boolToString cfg.allowNewUsers} + + [browserid] + backend = tokenserver.verifiers.LocalVerifier + audiences = ${removeSuffix "/" cfg.publicUrl} + ''; + + user = "syncserver"; + group = "syncserver"; +in + +{ + meta.maintainers = with lib.maintainers; [ nadrieril ]; + + options = { + services.firefox.syncserver = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable a Firefox Sync Server, this give the opportunity to + Firefox users to store all synchronized data on their own server. To use this + server, Firefox users should visit the <option>about:config</option>, and + replicate the following change + + <screen> + services.sync.tokenServerURI: http://localhost:5000/token/1.0/sync/1.5 + </screen> + + where <option>http://localhost:5000/</option> corresponds to the + public url of the server. + ''; + }; + + listen.address = mkOption { + type = types.str; + default = "127.0.0.1"; + example = "0.0.0.0"; + description = '' + Address on which the sync server listen to. + ''; + }; + + listen.port = mkOption { + type = types.int; + default = 5000; + description = '' + Port on which the sync server listen to. + ''; + }; + + publicUrl = mkOption { + type = types.str; + default = "http://localhost:5000/"; + example = "http://sync.example.com/"; + description = '' + Public URL with which firefox users can use to access the sync server. + ''; + }; + + allowNewUsers = mkOption { + type = types.bool; + default = true; + description = '' + Whether to allow new-user signups on the server. Only request by + existing accounts will be honored. + ''; + }; + + sqlUri = mkOption { + type = types.str; + default = defaultSqlUri; + example = "postgresql://scott:tiger@localhost/test"; + description = '' + The location of the database. This URL is composed of + <option>dialect[+driver]://user:password@host/dbname[?key=value..]</option>, + where <option>dialect</option> is a database name such as + <option>mysql</option>, <option>oracle</option>, <option>postgresql</option>, + etc., and <option>driver</option> the name of a DBAPI, such as + <option>psycopg2</option>, <option>pyodbc</option>, <option>cx_oracle</option>, + etc. The <link + xlink:href="http://docs.sqlalchemy.org/en/rel_0_9/core/engines.html#database-urls"> + SQLAlchemy documentation</link> provides more examples and describe the syntax of + the expected URL. + ''; + }; + + privateConfig = mkOption { + type = types.str; + default = "/etc/firefox/syncserver-secret.ini"; + description = '' + The private config file is used to extend the generated config with confidential + information, such as the <option>syncserver.sqlUri</option> setting if it contains a + password, and the <option>syncserver.secret</option> setting is used by the server to + generate cryptographically-signed authentication tokens. + + If this file does not exists, then it is created with a generated + <option>syncserver.secret</option> settings. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + systemd.services.syncserver = { + after = [ "network.target" ]; + description = "Firefox Sync Server"; + wantedBy = [ "multi-user.target" ]; + path = [ + pkgs.coreutils + (pkgs.python.withPackages (ps: [ pkgs.syncserver ps.gunicorn ])) + ]; + + serviceConfig = { + User = user; + Group = group; + PermissionsStartOnly = true; + }; + + preStart = '' + if ! test -e ${cfg.privateConfig}; then + mkdir -p $(dirname ${cfg.privateConfig}) + echo > ${cfg.privateConfig} '[syncserver]' + chmod 600 ${cfg.privateConfig} + echo >> ${cfg.privateConfig} "secret = $(head -c 20 /dev/urandom | sha1sum | tr -d ' -')" + fi + chmod 600 ${cfg.privateConfig} + chmod 755 $(dirname ${cfg.privateConfig}) + chown ${user}:${group} ${cfg.privateConfig} + + '' + optionalString (cfg.sqlUri == defaultSqlUri) '' + if ! test -e $(dirname ${defaultDbLocation}); then + mkdir -m 700 -p $(dirname ${defaultDbLocation}) + chown ${user}:${group} $(dirname ${defaultDbLocation}) + fi + + # Move previous database file if it exists + oldDb="/var/db/firefox-sync-server.db" + if test -f $oldDb; then + mv $oldDb ${defaultDbLocation} + chown ${user}:${group} ${defaultDbLocation} + fi + ''; + + script = '' + gunicorn --paste ${syncServerIni} + ''; + }; + + users.users.${user} = { + inherit group; + isSystemUser = true; + }; + + users.groups.${group} = {}; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/fireqos.nix b/nixpkgs/nixos/modules/services/networking/fireqos.nix new file mode 100644 index 000000000000..0b34f0b6b8b4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/fireqos.nix @@ -0,0 +1,52 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.fireqos; + fireqosConfig = pkgs.writeText "fireqos.conf" "${cfg.config}"; +in { + options.services.fireqos = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + If enabled, FireQOS will be launched with the specified + configuration given in `config`. + ''; + }; + + config = mkOption { + type = types.str; + default = ""; + example = '' + interface wlp3s0 world-in input rate 10mbit ethernet + class web commit 50kbit + match tcp ports 80,443 + + interface wlp3s0 world-out input rate 10mbit ethernet + class web commit 50kbit + match tcp ports 80,443 + ''; + description = '' + The FireQOS configuration goes here. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.fireqos = { + description = "FireQOS"; + after = [ "network.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.firehol}/bin/fireqos start ${fireqosConfig}"; + ExecStop = [ + "${pkgs.firehol}/bin/fireqos stop" + "${pkgs.firehol}/bin/fireqos clear_all_qos" + ]; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/firewall.nix b/nixpkgs/nixos/modules/services/networking/firewall.nix new file mode 100644 index 000000000000..cdc3a172ea70 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/firewall.nix @@ -0,0 +1,585 @@ +/* This module enables a simple firewall. + + The firewall can be customised in arbitrary ways by setting + ‘networking.firewall.extraCommands’. For modularity, the firewall + uses several chains: + + - ‘nixos-fw’ is the main chain for input packet processing. + + - ‘nixos-fw-accept’ is called for accepted packets. If you want + additional logging, or want to reject certain packets anyway, you + can insert rules at the start of this chain. + + - ‘nixos-fw-log-refuse’ and ‘nixos-fw-refuse’ are called for + refused packets. (The former jumps to the latter after logging + the packet.) If you want additional logging, or want to accept + certain packets anyway, you can insert rules at the start of + this chain. + + - ‘nixos-fw-rpfilter’ is used as the main chain in the raw table, + called from the built-in ‘PREROUTING’ chain. If the kernel + supports it and `cfg.checkReversePath` is set this chain will + perform a reverse path filter test. + + - ‘nixos-drop’ is used while reloading the firewall in order to drop + all traffic. Since reloading isn't implemented in an atomic way + this'll prevent any traffic from leaking through while reloading + the firewall. However, if the reloading fails, the ‘firewall-stop’ + script will be called which in return will effectively disable the + complete firewall (in the default configuration). + +*/ + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.networking.firewall; + + inherit (config.boot.kernelPackages) kernel; + + kernelHasRPFilter = ((kernel.config.isEnabled or (x: false)) "IP_NF_MATCH_RPFILTER") || (kernel.features.netfilterRPFilter or false); + + helpers = import ./helpers.nix { inherit config lib; }; + + writeShScript = name: text: let dir = pkgs.writeScriptBin name '' + #! ${pkgs.runtimeShell} -e + ${text} + ''; in "${dir}/bin/${name}"; + + defaultInterface = { default = mapAttrs (name: value: cfg.${name}) commonOptions; }; + allInterfaces = defaultInterface // cfg.interfaces; + + startScript = writeShScript "firewall-start" '' + ${helpers} + + # Flush the old firewall rules. !!! Ideally, updating the + # firewall would be atomic. Apparently that's possible + # with iptables-restore. + ip46tables -D INPUT -j nixos-fw 2> /dev/null || true + for chain in nixos-fw nixos-fw-accept nixos-fw-log-refuse nixos-fw-refuse; do + ip46tables -F "$chain" 2> /dev/null || true + ip46tables -X "$chain" 2> /dev/null || true + done + + + # The "nixos-fw-accept" chain just accepts packets. + ip46tables -N nixos-fw-accept + ip46tables -A nixos-fw-accept -j ACCEPT + + + # The "nixos-fw-refuse" chain rejects or drops packets. + ip46tables -N nixos-fw-refuse + + ${if cfg.rejectPackets then '' + # Send a reset for existing TCP connections that we've + # somehow forgotten about. Send ICMP "port unreachable" + # for everything else. + ip46tables -A nixos-fw-refuse -p tcp ! --syn -j REJECT --reject-with tcp-reset + ip46tables -A nixos-fw-refuse -j REJECT + '' else '' + ip46tables -A nixos-fw-refuse -j DROP + ''} + + + # The "nixos-fw-log-refuse" chain performs logging, then + # jumps to the "nixos-fw-refuse" chain. + ip46tables -N nixos-fw-log-refuse + + ${optionalString cfg.logRefusedConnections '' + ip46tables -A nixos-fw-log-refuse -p tcp --syn -j LOG --log-level info --log-prefix "refused connection: " + ''} + ${optionalString (cfg.logRefusedPackets && !cfg.logRefusedUnicastsOnly) '' + ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type broadcast \ + -j LOG --log-level info --log-prefix "refused broadcast: " + ip46tables -A nixos-fw-log-refuse -m pkttype --pkt-type multicast \ + -j LOG --log-level info --log-prefix "refused multicast: " + ''} + ip46tables -A nixos-fw-log-refuse -m pkttype ! --pkt-type unicast -j nixos-fw-refuse + ${optionalString cfg.logRefusedPackets '' + ip46tables -A nixos-fw-log-refuse \ + -j LOG --log-level info --log-prefix "refused packet: " + ''} + ip46tables -A nixos-fw-log-refuse -j nixos-fw-refuse + + + # The "nixos-fw" chain does the actual work. + ip46tables -N nixos-fw + + # Clean up rpfilter rules + ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2> /dev/null || true + ip46tables -t raw -F nixos-fw-rpfilter 2> /dev/null || true + ip46tables -t raw -X nixos-fw-rpfilter 2> /dev/null || true + + ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) '' + # Perform a reverse-path test to refuse spoofers + # For now, we just drop, as the raw table doesn't have a log-refuse yet + ip46tables -t raw -N nixos-fw-rpfilter 2> /dev/null || true + ip46tables -t raw -A nixos-fw-rpfilter -m rpfilter --validmark ${optionalString (cfg.checkReversePath == "loose") "--loose"} -j RETURN + + # Allows this host to act as a DHCP4 client without first having to use APIPA + iptables -t raw -A nixos-fw-rpfilter -p udp --sport 67 --dport 68 -j RETURN + + # Allows this host to act as a DHCPv4 server + iptables -t raw -A nixos-fw-rpfilter -s 0.0.0.0 -d 255.255.255.255 -p udp --sport 68 --dport 67 -j RETURN + + ${optionalString cfg.logReversePathDrops '' + ip46tables -t raw -A nixos-fw-rpfilter -j LOG --log-level info --log-prefix "rpfilter drop: " + ''} + ip46tables -t raw -A nixos-fw-rpfilter -j DROP + + ip46tables -t raw -A PREROUTING -j nixos-fw-rpfilter + ''} + + # Accept all traffic on the trusted interfaces. + ${flip concatMapStrings cfg.trustedInterfaces (iface: '' + ip46tables -A nixos-fw -i ${iface} -j nixos-fw-accept + '')} + + # Accept packets from established or related connections. + ip46tables -A nixos-fw -m conntrack --ctstate ESTABLISHED,RELATED -j nixos-fw-accept + + # Accept connections to the allowed TCP ports. + ${concatStrings (mapAttrsToList (iface: cfg: + concatMapStrings (port: + '' + ip46tables -A nixos-fw -p tcp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"} + '' + ) cfg.allowedTCPPorts + ) allInterfaces)} + + # Accept connections to the allowed TCP port ranges. + ${concatStrings (mapAttrsToList (iface: cfg: + concatMapStrings (rangeAttr: + let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in + '' + ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"} + '' + ) cfg.allowedTCPPortRanges + ) allInterfaces)} + + # Accept packets on the allowed UDP ports. + ${concatStrings (mapAttrsToList (iface: cfg: + concatMapStrings (port: + '' + ip46tables -A nixos-fw -p udp --dport ${toString port} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"} + '' + ) cfg.allowedUDPPorts + ) allInterfaces)} + + # Accept packets on the allowed UDP port ranges. + ${concatStrings (mapAttrsToList (iface: cfg: + concatMapStrings (rangeAttr: + let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in + '' + ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept ${optionalString (iface != "default") "-i ${iface}"} + '' + ) cfg.allowedUDPPortRanges + ) allInterfaces)} + + # Accept IPv4 multicast. Not a big security risk since + # probably nobody is listening anyway. + #iptables -A nixos-fw -d 224.0.0.0/4 -j nixos-fw-accept + + # Optionally respond to ICMPv4 pings. + ${optionalString cfg.allowPing '' + iptables -w -A nixos-fw -p icmp --icmp-type echo-request ${optionalString (cfg.pingLimit != null) + "-m limit ${cfg.pingLimit} " + }-j nixos-fw-accept + ''} + + ${optionalString config.networking.enableIPv6 '' + # Accept all ICMPv6 messages except redirects and node + # information queries (type 139). See RFC 4890, section + # 4.4. + ip6tables -A nixos-fw -p icmpv6 --icmpv6-type redirect -j DROP + ip6tables -A nixos-fw -p icmpv6 --icmpv6-type 139 -j DROP + ip6tables -A nixos-fw -p icmpv6 -j nixos-fw-accept + + # Allow this host to act as a DHCPv6 client + ip6tables -A nixos-fw -d fe80::/64 -p udp --dport 546 -j nixos-fw-accept + ''} + + ${cfg.extraCommands} + + # Reject/drop everything else. + ip46tables -A nixos-fw -j nixos-fw-log-refuse + + + # Enable the firewall. + ip46tables -A INPUT -j nixos-fw + ''; + + stopScript = writeShScript "firewall-stop" '' + ${helpers} + + # Clean up in case reload fails + ip46tables -D INPUT -j nixos-drop 2>/dev/null || true + + # Clean up after added ruleset + ip46tables -D INPUT -j nixos-fw 2>/dev/null || true + + ${optionalString (kernelHasRPFilter && (cfg.checkReversePath != false)) '' + ip46tables -t raw -D PREROUTING -j nixos-fw-rpfilter 2>/dev/null || true + ''} + + ${cfg.extraStopCommands} + ''; + + reloadScript = writeShScript "firewall-reload" '' + ${helpers} + + # Create a unique drop rule + ip46tables -D INPUT -j nixos-drop 2>/dev/null || true + ip46tables -F nixos-drop 2>/dev/null || true + ip46tables -X nixos-drop 2>/dev/null || true + ip46tables -N nixos-drop + ip46tables -A nixos-drop -j DROP + + # Don't allow traffic to leak out until the script has completed + ip46tables -A INPUT -j nixos-drop + + ${cfg.extraStopCommands} + + if ${startScript}; then + ip46tables -D INPUT -j nixos-drop 2>/dev/null || true + else + echo "Failed to reload firewall... Stopping" + ${stopScript} + exit 1 + fi + ''; + + canonicalizePortList = + ports: lib.unique (builtins.sort builtins.lessThan ports); + + commonOptions = { + allowedTCPPorts = mkOption { + type = types.listOf types.port; + default = [ ]; + apply = canonicalizePortList; + example = [ 22 80 ]; + description = + '' + List of TCP ports on which incoming connections are + accepted. + ''; + }; + + allowedTCPPortRanges = mkOption { + type = types.listOf (types.attrsOf types.port); + default = [ ]; + example = [ { from = 8999; to = 9003; } ]; + description = + '' + A range of TCP ports on which incoming connections are + accepted. + ''; + }; + + allowedUDPPorts = mkOption { + type = types.listOf types.port; + default = [ ]; + apply = canonicalizePortList; + example = [ 53 ]; + description = + '' + List of open UDP ports. + ''; + }; + + allowedUDPPortRanges = mkOption { + type = types.listOf (types.attrsOf types.port); + default = [ ]; + example = [ { from = 60000; to = 61000; } ]; + description = + '' + Range of open UDP ports. + ''; + }; + }; + +in + +{ + + ###### interface + + options = { + + networking.firewall = { + enable = mkOption { + type = types.bool; + default = true; + description = + '' + Whether to enable the firewall. This is a simple stateful + firewall that blocks connection attempts to unauthorised TCP + or UDP ports on this machine. It does not affect packet + forwarding. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.iptables; + defaultText = "pkgs.iptables"; + example = literalExample "pkgs.iptables-nftables-compat"; + description = + '' + The iptables package to use for running the firewall service." + ''; + }; + + logRefusedConnections = mkOption { + type = types.bool; + default = true; + description = + '' + Whether to log rejected or dropped incoming connections. + ''; + }; + + logRefusedPackets = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to log all rejected or dropped incoming packets. + This tends to give a lot of log messages, so it's mostly + useful for debugging. + ''; + }; + + logRefusedUnicastsOnly = mkOption { + type = types.bool; + default = true; + description = + '' + If <option>networking.firewall.logRefusedPackets</option> + and this option are enabled, then only log packets + specifically directed at this machine, i.e., not broadcasts + or multicasts. + ''; + }; + + rejectPackets = mkOption { + type = types.bool; + default = false; + description = + '' + If set, refused packets are rejected rather than dropped + (ignored). This means that an ICMP "port unreachable" error + message is sent back to the client (or a TCP RST packet in + case of an existing connection). Rejecting packets makes + port scanning somewhat easier. + ''; + }; + + trustedInterfaces = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "enp0s2" ]; + description = + '' + Traffic coming in from these interfaces will be accepted + unconditionally. Traffic from the loopback (lo) interface + will always be accepted. + ''; + }; + + allowPing = mkOption { + type = types.bool; + default = true; + description = + '' + Whether to respond to incoming ICMPv4 echo requests + ("pings"). ICMPv6 pings are always allowed because the + larger address space of IPv6 makes network scanning much + less effective. + ''; + }; + + pingLimit = mkOption { + type = types.nullOr (types.separatedString " "); + default = null; + example = "--limit 1/minute --limit-burst 5"; + description = + '' + If pings are allowed, this allows setting rate limits + on them. If non-null, this option should be in the form of + flags like "--limit 1/minute --limit-burst 5" + ''; + }; + + checkReversePath = mkOption { + type = types.either types.bool (types.enum ["strict" "loose"]); + default = kernelHasRPFilter; + example = "loose"; + description = + '' + Performs a reverse path filter test on a packet. If a reply + to the packet would not be sent via the same interface that + the packet arrived on, it is refused. + + If using asymmetric routing or other complicated routing, set + this option to loose mode or disable it and setup your own + counter-measures. + + This option can be either true (or "strict"), "loose" (only + drop the packet if the source address is not reachable via any + interface) or false. Defaults to the value of + kernelHasRPFilter. + + (needs kernel 3.3+) + ''; + }; + + logReversePathDrops = mkOption { + type = types.bool; + default = false; + description = + '' + Logs dropped packets failing the reverse path filter test if + the option networking.firewall.checkReversePath is enabled. + ''; + }; + + connectionTrackingModules = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "ftp" "irc" "sane" "sip" "tftp" "amanda" "h323" "netbios_sn" "pptp" "snmp" ]; + description = + '' + List of connection-tracking helpers that are auto-loaded. + The complete list of possible values is given in the example. + + As helpers can pose as a security risk, it is advised to + set this to an empty list and disable the setting + networking.firewall.autoLoadConntrackHelpers unless you + know what you are doing. Connection tracking is disabled + by default. + + Loading of helpers is recommended to be done through the + CT target. More info: + https://home.regit.org/netfilter-en/secure-use-of-helpers/ + ''; + }; + + autoLoadConntrackHelpers = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to auto-load connection-tracking helpers. + See the description at networking.firewall.connectionTrackingModules + + (needs kernel 3.5+) + ''; + }; + + extraCommands = mkOption { + type = types.lines; + default = ""; + example = "iptables -A INPUT -p icmp -j ACCEPT"; + description = + '' + Additional shell commands executed as part of the firewall + initialisation script. These are executed just before the + final "reject" firewall rule is added, so they can be used + to allow packets that would otherwise be refused. + ''; + }; + + extraPackages = mkOption { + type = types.listOf types.package; + default = [ ]; + example = literalExample "[ pkgs.ipset ]"; + description = + '' + Additional packages to be included in the environment of the system + as well as the path of networking.firewall.extraCommands. + ''; + }; + + extraStopCommands = mkOption { + type = types.lines; + default = ""; + example = "iptables -P INPUT ACCEPT"; + description = + '' + Additional shell commands executed as part of the firewall + shutdown script. These are executed just after the removal + of the NixOS input rule, or if the service enters a failed + state. + ''; + }; + + interfaces = mkOption { + default = { }; + type = with types; attrsOf (submodule [ { options = commonOptions; } ]); + description = + '' + Interface-specific open ports. + ''; + }; + } // commonOptions; + + }; + + + ###### implementation + + # FIXME: Maybe if `enable' is false, the firewall should still be + # built but not started by default? + config = mkIf cfg.enable { + + networking.firewall.trustedInterfaces = [ "lo" ]; + + environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages; + + boot.kernelModules = (optional cfg.autoLoadConntrackHelpers "nf_conntrack") + ++ map (x: "nf_conntrack_${x}") cfg.connectionTrackingModules; + boot.extraModprobeConfig = optionalString cfg.autoLoadConntrackHelpers '' + options nf_conntrack nf_conntrack_helper=1 + ''; + + assertions = [ + # This is approximately "checkReversePath -> kernelHasRPFilter", + # but the checkReversePath option can include non-boolean + # values. + { assertion = cfg.checkReversePath == false || kernelHasRPFilter; + message = "This kernel does not support rpfilter"; } + ]; + + systemd.services.firewall = { + description = "Firewall"; + wantedBy = [ "sysinit.target" ]; + wants = [ "network-pre.target" ]; + before = [ "network-pre.target" ]; + after = [ "systemd-modules-load.service" ]; + + path = [ cfg.package ] ++ cfg.extraPackages; + + # FIXME: this module may also try to load kernel modules, but + # containers don't have CAP_SYS_MODULE. So the host system had + # better have all necessary modules already loaded. + unitConfig.ConditionCapability = "CAP_NET_ADMIN"; + unitConfig.DefaultDependencies = false; + + reloadIfChanged = true; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "@${startScript} firewall-start"; + ExecReload = "@${reloadScript} firewall-reload"; + ExecStop = "@${stopScript} firewall-stop"; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/flannel.nix b/nixpkgs/nixos/modules/services/networking/flannel.nix new file mode 100644 index 000000000000..4c040112d28d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/flannel.nix @@ -0,0 +1,191 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.flannel; + + networkConfig = filterAttrs (n: v: v != null) { + Network = cfg.network; + SubnetLen = cfg.subnetLen; + SubnetMin = cfg.subnetMin; + SubnetMax = cfg.subnetMax; + Backend = cfg.backend; + }; +in { + options.services.flannel = { + enable = mkEnableOption "flannel"; + + package = mkOption { + description = "Package to use for flannel"; + type = types.package; + default = pkgs.flannel; + defaultText = "pkgs.flannel"; + }; + + publicIp = mkOption { + description = '' + IP accessible by other nodes for inter-host communication. + Defaults to the IP of the interface being used for communication. + ''; + type = types.nullOr types.str; + default = null; + }; + + iface = mkOption { + description = '' + Interface to use (IP or name) for inter-host communication. + Defaults to the interface for the default route on the machine. + ''; + type = types.nullOr types.str; + default = null; + }; + + etcd = { + endpoints = mkOption { + description = "Etcd endpoints"; + type = types.listOf types.str; + default = ["http://127.0.0.1:2379"]; + }; + + prefix = mkOption { + description = "Etcd key prefix"; + type = types.str; + default = "/coreos.com/network"; + }; + + caFile = mkOption { + description = "Etcd certificate authority file"; + type = types.nullOr types.path; + default = null; + }; + + certFile = mkOption { + description = "Etcd cert file"; + type = types.nullOr types.path; + default = null; + }; + + keyFile = mkOption { + description = "Etcd key file"; + type = types.nullOr types.path; + default = null; + }; + }; + + kubeconfig = mkOption { + description = '' + Path to kubeconfig to use for storing flannel config using the + Kubernetes API + ''; + type = types.nullOr types.path; + default = null; + }; + + network = mkOption { + description = " IPv4 network in CIDR format to use for the entire flannel network."; + type = types.str; + }; + + nodeName = mkOption { + description = '' + Needed when running with Kubernetes as backend as this cannot be auto-detected"; + ''; + type = types.nullOr types.str; + default = with config.networking; (hostName + optionalString (domain != null) ".${domain}"); + example = "node1.example.com"; + }; + + storageBackend = mkOption { + description = "Determines where flannel stores its configuration at runtime"; + type = types.enum ["etcd" "kubernetes"]; + default = "etcd"; + }; + + subnetLen = mkOption { + description = '' + The size of the subnet allocated to each host. Defaults to 24 (i.e. /24) + unless the Network was configured to be smaller than a /24 in which case + it is one less than the network. + ''; + type = types.int; + default = 24; + }; + + subnetMin = mkOption { + description = '' + The beginning of IP range which the subnet allocation should start with. + Defaults to the first subnet of Network. + ''; + type = types.nullOr types.str; + default = null; + }; + + subnetMax = mkOption { + description = '' + The end of IP range which the subnet allocation should start with. + Defaults to the last subnet of Network. + ''; + type = types.nullOr types.str; + default = null; + }; + + backend = mkOption { + description = "Type of backend to use and specific configurations for that backend."; + type = types.attrs; + default = { + Type = "vxlan"; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.flannel = { + description = "Flannel Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + environment = { + FLANNELD_PUBLIC_IP = cfg.publicIp; + FLANNELD_IFACE = cfg.iface; + } // optionalAttrs (cfg.storageBackend == "etcd") { + FLANNELD_ETCD_ENDPOINTS = concatStringsSep "," cfg.etcd.endpoints; + FLANNELD_ETCD_KEYFILE = cfg.etcd.keyFile; + FLANNELD_ETCD_CERTFILE = cfg.etcd.certFile; + FLANNELD_ETCD_CAFILE = cfg.etcd.caFile; + ETCDCTL_CERT_FILE = cfg.etcd.certFile; + ETCDCTL_KEY_FILE = cfg.etcd.keyFile; + ETCDCTL_CA_FILE = cfg.etcd.caFile; + ETCDCTL_PEERS = concatStringsSep "," cfg.etcd.endpoints; + } // optionalAttrs (cfg.storageBackend == "kubernetes") { + FLANNELD_KUBE_SUBNET_MGR = "true"; + FLANNELD_KUBECONFIG_FILE = cfg.kubeconfig; + NODE_NAME = cfg.nodeName; + }; + path = [ pkgs.iptables ]; + preStart = '' + mkdir -p /run/flannel + touch /run/flannel/docker + '' + optionalString (cfg.storageBackend == "etcd") '' + echo "setting network configuration" + until ${pkgs.etcdctl}/bin/etcdctl set /coreos.com/network/config '${builtins.toJSON networkConfig}' + do + echo "setting network configuration, retry" + sleep 1 + done + ''; + serviceConfig = { + ExecStart = "${cfg.package}/bin/flannel"; + Restart = "always"; + RestartSec = "10s"; + }; + }; + + services.etcd.enable = mkDefault (cfg.storageBackend == "etcd" && cfg.etcd.endpoints == ["http://127.0.0.1:2379"]); + + # for some reason, flannel doesn't let you configure this path + # see: https://github.com/coreos/flannel/blob/master/Documentation/configuration.md#configuration + environment.etc."kube-flannel/net-conf.json" = mkIf (cfg.storageBackend == "kubernetes") { + source = pkgs.writeText "net-conf.json" (builtins.toJSON networkConfig); + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/flashpolicyd.nix b/nixpkgs/nixos/modules/services/networking/flashpolicyd.nix new file mode 100644 index 000000000000..7f25083307c7 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/flashpolicyd.nix @@ -0,0 +1,85 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.flashpolicyd; + + flashpolicyd = pkgs.stdenv.mkDerivation { + name = "flashpolicyd-0.6"; + + src = pkgs.fetchurl { + name = "flashpolicyd_v0.6.zip"; + url = "https://download.adobe.com/pub/adobe/devnet/flashplayer/articles/socket_policy_files/flashpolicyd_v0.6.zip"; + sha256 = "16zk237233npwfq1m4ksy4g5lzy1z9fp95w7pz0cdlpmv0fv9sm3"; + }; + + buildInputs = [ pkgs.unzip pkgs.perl ]; + + installPhase = "mkdir $out; cp -pr * $out/; chmod +x $out/*/*.pl"; + }; + + flashpolicydWrapper = pkgs.writeScriptBin "flashpolicyd" + '' + #! ${pkgs.runtimeShell} + exec ${flashpolicyd}/Perl_xinetd/in.flashpolicyd.pl \ + --file=${pkgs.writeText "flashpolixy.xml" cfg.policy} \ + 2> /dev/null + ''; + +in + +{ + + ###### interface + + options = { + + services.flashpolicyd = { + + enable = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to enable the Flash Policy server. This is + necessary if you want Flash applications to make + connections to your server. + ''; + }; + + policy = mkOption { + default = + '' + <?xml version="1.0"?> + <!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd"> + <cross-domain-policy> + <site-control permitted-cross-domain-policies="master-only"/> + <allow-access-from domain="*" to-ports="*" /> + </cross-domain-policy> + ''; + description = "The policy to be served. The default is to allow connections from any domain to any port."; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + services.xinetd.enable = true; + + services.xinetd.services = singleton + { name = "flashpolicy"; + port = 843; + unlisted = true; + server = "${flashpolicydWrapper}/bin/flashpolicyd"; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/freenet.nix b/nixpkgs/nixos/modules/services/networking/freenet.nix new file mode 100644 index 000000000000..3da3ab0c7df4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/freenet.nix @@ -0,0 +1,64 @@ +# NixOS module for Freenet daemon + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.freenet; + varDir = "/var/lib/freenet"; + +in + +{ + + ### configuration + + options = { + + services.freenet = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Enable the Freenet daemon"; + }; + + nice = mkOption { + type = types.int; + default = 10; + description = "Set the nice level for the Freenet daemon"; + }; + + }; + + }; + + ### implementation + + config = mkIf cfg.enable { + + systemd.services.freenet = { + description = "Freenet daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig.ExecStart = "${pkgs.freenet}/bin/freenet"; + serviceConfig.User = "freenet"; + serviceConfig.UMask = "0007"; + serviceConfig.WorkingDirectory = varDir; + serviceConfig.Nice = cfg.nice; + }; + + users.users.freenet = { + group = "freenet"; + description = "Freenet daemon user"; + home = varDir; + createHome = true; + uid = config.ids.uids.freenet; + }; + + users.groups.freenet.gid = config.ids.gids.freenet; + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/freeradius.nix b/nixpkgs/nixos/modules/services/networking/freeradius.nix new file mode 100644 index 000000000000..f3fdd576b65c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/freeradius.nix @@ -0,0 +1,84 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.freeradius; + + freeradiusService = cfg: + { + description = "FreeRadius server"; + wantedBy = ["multi-user.target"]; + after = ["network.target"]; + wants = ["network.target"]; + preStart = '' + ${pkgs.freeradius}/bin/radiusd -C -d ${cfg.configDir} -l stdout + ''; + + serviceConfig = { + ExecStart = "${pkgs.freeradius}/bin/radiusd -f -d ${cfg.configDir} -l stdout" + + optionalString cfg.debug " -xx"; + ExecReload = [ + "${pkgs.freeradius}/bin/radiusd -C -d ${cfg.configDir} -l stdout" + "${pkgs.coreutils}/bin/kill -HUP $MAINPID" + ]; + User = "radius"; + ProtectSystem = "full"; + ProtectHome = "on"; + Restart = "on-failure"; + RestartSec = 2; + }; + }; + + freeradiusConfig = { + enable = mkEnableOption "the freeradius server"; + + configDir = mkOption { + type = types.path; + default = "/etc/raddb"; + description = '' + The path of the freeradius server configuration directory. + ''; + }; + + debug = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable debug logging for freeradius (-xx + option). This should not be left on, since it includes + sensitive data such as passwords in the logs. + ''; + }; + + }; + +in + +{ + + ###### interface + + options = { + services.freeradius = freeradiusConfig; + }; + + + ###### implementation + + config = mkIf (cfg.enable) { + + users = { + users.radius = { + /*uid = config.ids.uids.radius;*/ + description = "Radius daemon user"; + }; + }; + + systemd.services.freeradius = freeradiusService cfg; + warnings = optional cfg.debug "Freeradius debug logging is enabled. This will log passwords in plaintext to the journal!"; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/gale.nix b/nixpkgs/nixos/modules/services/networking/gale.nix new file mode 100644 index 000000000000..cb954fd836bc --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gale.nix @@ -0,0 +1,181 @@ +{ 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" ] '' + chmod 755 ${home} + 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" + chown ${cfg.user}:${cfg.group} ${home} ${home}/auth ${home}/auth/* + chown ${cfg.user}:${cfg.group} ${home}/.gale ${home}/.gale/auth ${home}/.gale/auth/private + '' + ); + + 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 ]; + }; + + users.users.${cfg.user} = { + description = "Gale daemon"; + uid = config.ids.uids.gale; + group = cfg.group; + home = home; + createHome = true; + }; + + users.groups = [{ + 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; + }; + + security.wrappers.gksign = cfg.setuidWrapper; + + systemd.services.gale-galed = { + description = "Gale messaging daemon"; + wantedBy = [ "multi-user.target" ]; + wants = [ "gale-gdomain.service" ]; + after = [ "network.target" ]; + + preStart = '' + 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" + ''; + + 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; + }; + }; + }) + ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/gateone.nix b/nixpkgs/nixos/modules/services/networking/gateone.nix new file mode 100644 index 000000000000..4456a95402ed --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gateone.nix @@ -0,0 +1,59 @@ +{ config, lib, pkgs, ...}: +with lib; +let + cfg = config.services.gateone; +in +{ +options = { + services.gateone = { + enable = mkEnableOption "GateOne server"; + pidDir = mkOption { + default = "/run/gateone"; + type = types.path; + description = ''Path of pid files for GateOne.''; + }; + settingsDir = mkOption { + default = "/var/lib/gateone"; + type = types.path; + description = ''Path of configuration files for GateOne.''; + }; + }; +}; +config = mkIf cfg.enable { + environment.systemPackages = with pkgs.pythonPackages; [ + gateone pkgs.openssh pkgs.procps pkgs.coreutils pkgs.cacert]; + + users.users.gateone = { + description = "GateOne privilege separation user"; + uid = config.ids.uids.gateone; + home = cfg.settingsDir; + }; + users.groups.gateone.gid = config.ids.gids.gateone; + + systemd.services.gateone = with pkgs; { + description = "GateOne web-based terminal"; + path = [ pythonPackages.gateone nix openssh procps coreutils ]; + preStart = '' + if [ ! -d ${cfg.settingsDir} ] ; then + mkdir -m 0750 -p ${cfg.settingsDir} + chown -R gateone.gateone ${cfg.settingsDir} + fi + if [ ! -d ${cfg.pidDir} ] ; then + mkdir -m 0750 -p ${cfg.pidDir} + chown -R gateone.gateone ${cfg.pidDir} + fi + ''; + #unitConfig.RequiresMountsFor = "${cfg.settingsDir}"; + serviceConfig = { + ExecStart = ''${pythonPackages.gateone}/bin/gateone --settings_dir=${cfg.settingsDir} --pid_file=${cfg.pidDir}/gateone.pid --gid=${toString config.ids.gids.gateone} --uid=${toString config.ids.uids.gateone}''; + User = "gateone"; + Group = "gateone"; + WorkingDirectory = cfg.settingsDir; + }; + + wantedBy = [ "multi-user.target" ]; + requires = [ "network.target" ]; + }; +}; +} + diff --git a/nixpkgs/nixos/modules/services/networking/gdomap.nix b/nixpkgs/nixos/modules/services/networking/gdomap.nix new file mode 100644 index 000000000000..3d829cb69135 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gdomap.nix @@ -0,0 +1,29 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + # + # interface + # + options = { + services.gdomap = { + enable = mkEnableOption "GNUstep Distributed Objects name server"; + }; + }; + + # + # implementation + # + config = mkIf config.services.gdomap.enable { + # NOTE: gdomap runs as root + # TODO: extra user for gdomap? + systemd.services.gdomap = { + description = "gdomap server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = [ pkgs.gnustep.base ]; + serviceConfig.ExecStart = "${pkgs.gnustep.base}/bin/gdomap -f"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/git-daemon.nix b/nixpkgs/nixos/modules/services/networking/git-daemon.nix new file mode 100644 index 000000000000..52c895215fbe --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/git-daemon.nix @@ -0,0 +1,130 @@ +{ config, lib, pkgs, ... }: +with lib; +let + + cfg = config.services.gitDaemon; + +in +{ + + ###### interface + + options = { + services.gitDaemon = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable Git daemon, which allows public hosting of git repositories + without any access controls. This is mostly intended for read-only access. + + You can allow write access by setting daemon.receivepack configuration + item of the repository to true. This is solely meant for a closed LAN setting + where everybody is friendly. + + If you need any access controls, use something else. + ''; + }; + + basePath = mkOption { + type = types.str; + default = ""; + example = "/srv/git/"; + description = '' + Remap all the path requests as relative to the given path. For example, + if you set base-path to /srv/git, then if you later try to pull + git://example.com/hello.git, Git daemon will interpret the path as /srv/git/hello.git. + ''; + }; + + exportAll = mkOption { + type = types.bool; + default = false; + description = '' + Publish all directories that look like Git repositories (have the objects + and refs subdirectories), even if they do not have the git-daemon-export-ok file. + + If disabled, you need to touch .git/git-daemon-export-ok in each repository + you want the daemon to publish. + + Warning: enabling this without a repository whitelist or basePath + publishes every git repository you have. + ''; + }; + + repositories = mkOption { + type = types.listOf types.str; + default = []; + example = [ "/srv/git" "/home/user/git/repo2" ]; + description = '' + A whitelist of paths of git repositories, or directories containing repositories + all of which would be published. Paths must not end in "/". + + Warning: leaving this empty and enabling exportAll publishes all + repositories in your filesystem or basePath if specified. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = ""; + example = "example.com"; + description = "Listen on a specific IP address or hostname."; + }; + + port = mkOption { + type = types.int; + default = 9418; + description = "Port to listen on."; + }; + + options = mkOption { + type = types.str; + default = ""; + description = "Extra configuration options to be passed to Git daemon."; + }; + + user = mkOption { + type = types.str; + default = "git"; + description = "User under which Git daemon would be running."; + }; + + group = mkOption { + type = types.str; + default = "git"; + description = "Group under which Git daemon would be running."; + }; + + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + users.users = optionalAttrs (cfg.user == "git") { + git = { + uid = config.ids.uids.git; + description = "Git daemon user"; + }; + }; + + users.groups = optionalAttrs (cfg.group == "git") { + git.gid = config.ids.gids.git; + }; + + systemd.services.git-daemon = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "${pkgs.git}/bin/git daemon --reuseaddr " + + (optionalString (cfg.basePath != "") "--base-path=${cfg.basePath} ") + + (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ") + + "--port=${toString cfg.port} --user=${cfg.user} --group=${cfg.group} ${cfg.options} " + + "--verbose " + (optionalString cfg.exportAll "--export-all ") + concatStringsSep " " cfg.repositories; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/gnunet.nix b/nixpkgs/nixos/modules/services/networking/gnunet.nix new file mode 100644 index 000000000000..69d4ed047756 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gnunet.nix @@ -0,0 +1,166 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.gnunet; + + homeDir = "/var/lib/gnunet"; + + configFile = with cfg; pkgs.writeText "gnunetd.conf" + '' + [PATHS] + SERVICEHOME = ${homeDir} + + [ats] + WAN_QUOTA_IN = ${toString load.maxNetDownBandwidth} b + WAN_QUOTA_OUT = ${toString load.maxNetUpBandwidth} b + + [datastore] + QUOTA = ${toString fileSharing.quota} MB + + [transport-udp] + PORT = ${toString udp.port} + ADVERTISED_PORT = ${toString udp.port} + + [transport-tcp] + PORT = ${toString tcp.port} + ADVERTISED_PORT = ${toString tcp.port} + + ${extraOptions} + ''; + +in + +{ + + ###### interface + + options = { + + services.gnunet = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run the GNUnet daemon. GNUnet is GNU's anonymous + peer-to-peer communication and file sharing framework. + ''; + }; + + fileSharing = { + quota = mkOption { + type = types.int; + default = 1024; + description = '' + Maximum file system usage (in MiB) for file sharing. + ''; + }; + }; + + udp = { + port = mkOption { + type = types.port; + default = 2086; # assigned by IANA + description = '' + The UDP port for use by GNUnet. + ''; + }; + }; + + tcp = { + port = mkOption { + type = types.port; + default = 2086; # assigned by IANA + description = '' + The TCP port for use by GNUnet. + ''; + }; + }; + + load = { + maxNetDownBandwidth = mkOption { + type = types.int; + default = 50000; + description = '' + Maximum bandwidth usage (in bits per second) for GNUnet + when downloading data. + ''; + }; + + maxNetUpBandwidth = mkOption { + type = types.int; + default = 50000; + description = '' + Maximum bandwidth usage (in bits per second) for GNUnet + when downloading data. + ''; + }; + + hardNetUpBandwidth = mkOption { + type = types.int; + default = 0; + description = '' + Hard bandwidth limit (in bits per second) when uploading + data. + ''; + }; + }; + + package = mkOption { + type = types.package; + default = pkgs.gnunet; + defaultText = "pkgs.gnunet"; + description = "Overridable attribute of the gnunet package to use."; + example = literalExample "pkgs.gnunet_git"; + }; + + extraOptions = mkOption { + type = types.lines; + default = ""; + description = '' + Additional options that will be copied verbatim in `gnunet.conf'. + See `gnunet.conf(5)' for details. + ''; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.gnunet.enable { + + users.users.gnunet = { + group = "gnunet"; + description = "GNUnet User"; + home = homeDir; + createHome = true; + uid = config.ids.uids.gnunet; + }; + + users.groups.gnunet.gid = config.ids.gids.gnunet; + + # The user tools that talk to `gnunetd' should come from the same source, + # so install them globally. + environment.systemPackages = [ cfg.package ]; + + systemd.services.gnunet = { + description = "GNUnet"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ cfg.package pkgs.miniupnpc ]; + environment.TMPDIR = "/tmp"; + serviceConfig.PrivateTmp = true; + serviceConfig.ExecStart = "${cfg.package}/lib/gnunet/libexec/gnunet-service-arm -c ${configFile}"; + serviceConfig.User = "gnunet"; + serviceConfig.UMask = "0007"; + serviceConfig.WorkingDirectory = homeDir; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/go-neb.nix b/nixpkgs/nixos/modules/services/networking/go-neb.nix new file mode 100644 index 000000000000..991ae38f30a5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/go-neb.nix @@ -0,0 +1,53 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.go-neb; + + configFile = pkgs.writeText "config.yml" (builtins.toJSON cfg.config); +in { + options.services.go-neb = { + enable = mkEnableOption "Extensible matrix bot written in Go"; + + bindAddress = mkOption { + type = types.str; + description = "Port (and optionally address) to listen on."; + default = ":4050"; + }; + + baseUrl = mkOption { + type = types.str; + description = "Public-facing endpoint that can receive webhooks."; + }; + + config = mkOption { + type = types.uniq types.attrs; + description = '' + Your <filename>config.yaml</filename> as a Nix attribute set. + See <link xlink:href="https://github.com/matrix-org/go-neb/blob/master/config.sample.yaml">config.sample.yaml</link> + for possible options. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.go-neb = { + description = "Extensible matrix bot written in Go"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + environment = { + BASE_URL = cfg.baseUrl; + BIND_ADDRESS = cfg.bindAddress; + CONFIG_FILE = configFile; + }; + + serviceConfig = { + ExecStart = "${pkgs.go-neb}/bin/go-neb"; + DynamicUser = true; + }; + }; + }; + + meta.maintainers = with maintainers; [ hexa maralorn ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/go-shadowsocks2.nix b/nixpkgs/nixos/modules/services/networking/go-shadowsocks2.nix new file mode 100644 index 000000000000..afbd7ea27c65 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/go-shadowsocks2.nix @@ -0,0 +1,30 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.go-shadowsocks2.server; +in { + options.services.go-shadowsocks2.server = { + enable = mkEnableOption "go-shadowsocks2 server"; + + listenAddress = mkOption { + type = types.str; + description = "Server listen address or URL"; + example = "ss://AEAD_CHACHA20_POLY1305:your-password@:8488"; + }; + }; + + config = mkIf cfg.enable { + systemd.services.go-shadowsocks2-server = { + description = "go-shadowsocks2 server"; + + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = "${pkgs.go-shadowsocks2}/bin/go-shadowsocks2 -s '${cfg.listenAddress}'"; + DynamicUser = true; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/gogoclient.nix b/nixpkgs/nixos/modules/services/networking/gogoclient.nix new file mode 100644 index 000000000000..99455b183144 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gogoclient.nix @@ -0,0 +1,85 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.gogoclient; +in + +{ + + ###### interface + + options = { + services.gogoclient = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Enable the gogoCLIENT IPv6 tunnel. + ''; + }; + autorun = mkOption { + type = types.bool; + default = true; + description = '' + Whether to automatically start the tunnel. + ''; + }; + + username = mkOption { + default = ""; + description = '' + Your Gateway6 login name, if any. + ''; + }; + + password = mkOption { + default = ""; + type = types.str; + description = '' + Path to a file (as a string), containing your gogoNET password, if any. + ''; + }; + + server = mkOption { + default = "anonymous.freenet6.net"; + example = "broker.freenet6.net"; + description = "The Gateway6 server to be used."; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + boot.kernelModules = [ "tun" ]; + + networking.enableIPv6 = true; + + systemd.services.gogoclient = { + description = "ipv6 tunnel"; + + after = [ "network.target" ]; + requires = [ "network.target" ]; + + unitConfig.RequiresMountsFor = "/var/lib/gogoc"; + + script = let authMethod = if cfg.password == "" then "anonymous" else "any"; in '' + mkdir -p -m 700 /var/lib/gogoc + cat ${pkgs.gogoclient}/share/${pkgs.gogoclient.name}/gogoc.conf.sample | \ + ${pkgs.gnused}/bin/sed \ + -e "s|^userid=|&${cfg.username}|" \ + -e "s|^passwd=|&${optionalString (cfg.password != "") "$(cat ${cfg.password})"}|" \ + -e "s|^server=.*|server=${cfg.server}|" \ + -e "s|^auth_method=.*|auth_method=${authMethod}|" \ + -e "s|^#log_file=|log_file=1|" > /var/lib/gogoc/gogoc.conf + cd /var/lib/gogoc + exec ${pkgs.gogoclient}/bin/gogoc -y -f /var/lib/gogoc/gogoc.conf + ''; + } // optionalAttrs cfg.autorun { + wantedBy = [ "multi-user.target" ]; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/gvpe.nix b/nixpkgs/nixos/modules/services/networking/gvpe.nix new file mode 100644 index 000000000000..92e87cd4640d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gvpe.nix @@ -0,0 +1,124 @@ +# GNU Virtual Private Ethernet + +{config, pkgs, lib, ...}: + +let + inherit (lib) mkOption mkIf; + + cfg = config.services.gvpe; + + finalConfig = if cfg.configFile != null then + cfg.configFile + else if cfg.configText != null then + pkgs.writeTextFile { + name = "gvpe.conf"; + text = cfg.configText; + } + else + throw "You must either specify contents of the config file or the config file itself for GVPE"; + + ifupScript = if cfg.ipAddress == null || cfg.subnet == null then + throw "Specify IP address and subnet (with mask) for GVPE" + else if cfg.nodename == null then + throw "You must set node name for GVPE" + else + (pkgs.writeTextFile { + name = "gvpe-if-up"; + text = '' + #! /bin/sh + + export PATH=$PATH:${pkgs.iproute}/sbin + + ip link set $IFNAME up + ip address add ${cfg.ipAddress} dev $IFNAME + ip route add ${cfg.subnet} dev $IFNAME + + ${cfg.customIFSetup} + ''; + executable = true; + }); +in + +{ + options = { + services.gvpe = { + enable = lib.mkEnableOption "gvpe"; + + nodename = mkOption { + default = null; + description ='' + GVPE node name + ''; + }; + configText = mkOption { + default = null; + example = '' + tcp-port = 655 + udp-port = 655 + mtu = 1480 + ifname = vpn0 + + node = alpha + hostname = alpha.example.org + connect = always + enable-udp = true + enable-tcp = true + on alpha if-up = if-up-0 + on alpha pid-file = /var/gvpe/gvpe.pid + ''; + description = '' + GVPE config contents + ''; + }; + configFile = mkOption { + default = null; + example = "/root/my-gvpe-conf"; + description = '' + GVPE config file, if already present + ''; + }; + ipAddress = mkOption { + default = null; + description = '' + IP address to assign to GVPE interface + ''; + }; + subnet = mkOption { + default = null; + example = "10.0.0.0/8"; + description = '' + IP subnet assigned to GVPE network + ''; + }; + customIFSetup = mkOption { + default = ""; + description = '' + Additional commands to apply in ifup script + ''; + }; + }; + }; + config = mkIf cfg.enable { + systemd.services.gvpe = { + description = "GNU Virtual Private Ethernet node"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + preStart = '' + mkdir -p /var/gvpe + mkdir -p /var/gvpe/pubkey + chown root /var/gvpe + chmod 700 /var/gvpe + cp ${finalConfig} /var/gvpe/gvpe.conf + cp ${ifupScript} /var/gvpe/if-up + ''; + + script = "${pkgs.gvpe}/sbin/gvpe -c /var/gvpe -D ${cfg.nodename} " + + " ${cfg.nodename}.pid-file=/var/gvpe/gvpe.pid" + + " ${cfg.nodename}.if-up=if-up" + + " &> /var/log/gvpe"; + + serviceConfig.Restart = "always"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/hans.nix b/nixpkgs/nixos/modules/services/networking/hans.nix new file mode 100644 index 000000000000..8334dc68d623 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hans.nix @@ -0,0 +1,145 @@ +# NixOS module for hans, ip over icmp daemon + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.hans; + + hansUser = "hans"; + +in +{ + + ### configuration + + options = { + + services.hans = { + clients = mkOption { + default = {}; + description = '' + Each attribute of this option defines a systemd service that + runs hans. Many or none may be defined. + The name of each service is + <literal>hans-<replaceable>name</replaceable></literal> + where <replaceable>name</replaceable> is the name of the + corresponding attribute name. + ''; + example = literalExample '' + { + foo = { + server = "192.0.2.1"; + extraConfig = "-v"; + } + } + ''; + type = types.attrsOf (types.submodule ( + { + options = { + server = mkOption { + type = types.str; + default = ""; + description = "IP address of server running hans"; + example = "192.0.2.1"; + }; + + extraConfig = mkOption { + type = types.str; + default = ""; + description = "Additional command line parameters"; + example = "-v"; + }; + + passwordFile = mkOption { + type = types.str; + default = ""; + description = "File that containts password"; + }; + + }; + })); + }; + + server = { + enable = mkOption { + type = types.bool; + default = false; + description = "enable hans server"; + }; + + ip = mkOption { + type = types.str; + default = ""; + description = "The assigned ip range"; + example = "198.51.100.0"; + }; + + respondToSystemPings = mkOption { + type = types.bool; + default = false; + description = "Force hans respond to ordinary pings"; + }; + + extraConfig = mkOption { + type = types.str; + default = ""; + description = "Additional command line parameters"; + example = "-v"; + }; + + passwordFile = mkOption { + type = types.str; + default = ""; + description = "File that containts password"; + }; + }; + + }; + }; + + ### implementation + + config = mkIf (cfg.server.enable || cfg.clients != {}) { + boot.kernel.sysctl = optionalAttrs cfg.server.respondToSystemPings { + "net.ipv4.icmp_echo_ignore_all" = 1; + }; + + boot.kernelModules = [ "tun" ]; + + systemd.services = + let + createHansClientService = name: cfg: + { + description = "hans client - ${name}"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "${pkgs.hans}/bin/hans -f -u ${hansUser} ${cfg.extraConfig} -c ${cfg.server} ${optionalString (cfg.passwordFile != "") "-p $(cat \"${cfg.passwordFile}\")"}"; + serviceConfig = { + RestartSec = "30s"; + Restart = "always"; + }; + }; + in + listToAttrs ( + mapAttrsToList + (name: value: nameValuePair "hans-${name}" (createHansClientService name value)) + cfg.clients + ) // { + hans = mkIf (cfg.server.enable) { + description = "hans, ip over icmp server daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "${pkgs.hans}/bin/hans -f -u ${hansUser} ${cfg.server.extraConfig} -s ${cfg.server.ip} ${optionalString cfg.server.respondToSystemPings "-r"} ${optionalString (cfg.server.passwordFile != "") "-p $(cat \"${cfg.server.passwordFile}\")"}"; + }; + }; + + users.users.${hansUser} = { + description = "Hans daemon user"; + isSystemUser = true; + }; + }; + + meta.maintainers = with maintainers; [ gnidorah ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/haproxy.nix b/nixpkgs/nixos/modules/services/networking/haproxy.nix new file mode 100644 index 000000000000..e9d72b35499d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/haproxy.nix @@ -0,0 +1,112 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.haproxy; + + haproxyCfg = pkgs.writeText "haproxy.conf" '' + global + # needed for hot-reload to work without dropping packets in multi-worker mode + stats socket /run/haproxy/haproxy.sock mode 600 expose-fd listeners level user + + ${cfg.config} + ''; + +in +with lib; +{ + options = { + services.haproxy = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable HAProxy, the reliable, high performance TCP/HTTP + load balancer. + ''; + }; + + user = mkOption { + type = types.str; + default = "haproxy"; + description = "User account under which haproxy runs."; + }; + + group = mkOption { + type = types.str; + default = "haproxy"; + description = "Group account under which haproxy runs."; + }; + + config = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + Contents of the HAProxy configuration file, + <filename>haproxy.conf</filename>. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + + assertions = [{ + assertion = cfg.config != null; + message = "You must provide services.haproxy.config."; + }]; + + # configuration file indirection is needed to support reloading + environment.etc."haproxy.cfg".source = haproxyCfg; + + systemd.services.haproxy = { + description = "HAProxy"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + Type = "notify"; + ExecStartPre = [ + # when the master process receives USR2, it reloads itself using exec(argv[0]), + # so we create a symlink there and update it before reloading + "${pkgs.coreutils}/bin/ln -sf ${pkgs.haproxy}/sbin/haproxy /run/haproxy/haproxy" + # when running the config test, don't be quiet so we can see what goes wrong + "/run/haproxy/haproxy -c -f ${haproxyCfg}" + ]; + ExecStart = "/run/haproxy/haproxy -Ws -f /etc/haproxy.cfg -p /run/haproxy/haproxy.pid"; + # support reloading + ExecReload = [ + "${pkgs.haproxy}/sbin/haproxy -c -f ${haproxyCfg}" + "${pkgs.coreutils}/bin/ln -sf ${pkgs.haproxy}/sbin/haproxy /run/haproxy/haproxy" + "${pkgs.coreutils}/bin/kill -USR2 $MAINPID" + ]; + KillMode = "mixed"; + SuccessExitStatus = "143"; + Restart = "always"; + RuntimeDirectory = "haproxy"; + # upstream hardening options + NoNewPrivileges = true; + ProtectHome = true; + ProtectSystem = "strict"; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + SystemCallFilter= "~@cpu-emulation @keyring @module @obsolete @raw-io @reboot @swap @sync"; + # needed in case we bind to port < 1024 + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + }; + }; + + users.users = optionalAttrs (cfg.user == "haproxy") { + haproxy = { + group = cfg.group; + isSystemUser = true; + }; + }; + + users.groups = optionalAttrs (cfg.group == "haproxy") { + haproxy = {}; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/helpers.nix b/nixpkgs/nixos/modules/services/networking/helpers.nix new file mode 100644 index 000000000000..d7d42de0e3a8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/helpers.nix @@ -0,0 +1,11 @@ +{ config, lib, ... }: '' + # Helper command to manipulate both the IPv4 and IPv6 tables. + ip46tables() { + iptables -w "$@" + ${ + lib.optionalString config.networking.enableIPv6 '' + ip6tables -w "$@" + '' + } + } +'' diff --git a/nixpkgs/nixos/modules/services/networking/heyefi.nix b/nixpkgs/nixos/modules/services/networking/heyefi.nix new file mode 100644 index 000000000000..fc2b5a848578 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/heyefi.nix @@ -0,0 +1,82 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.heyefi; +in + +{ + + ###### interface + + options = { + + services.heyefi = { + + enable = mkEnableOption "heyefi"; + + cardMacaddress = mkOption { + default = ""; + description = '' + An Eye-Fi card MAC address. + ''; + }; + + uploadKey = mkOption { + default = ""; + description = '' + An Eye-Fi card's upload key. + ''; + }; + + uploadDir = mkOption { + example = "/home/username/pictures"; + description = '' + The directory to upload the files to. + ''; + }; + + user = mkOption { + default = "root"; + description = '' + heyefi will be run under this user (user must exist, + this can be your user name). + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.heyefi = + { + description = "heyefi service"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "${cfg.user}"; + Restart = "always"; + ExecStart = "${pkgs.heyefi}/bin/heyefi"; + }; + + }; + + environment.etc."heyefi/heyefi.config".text = + '' + # /etc/heyefi/heyefi.conf: DO NOT EDIT -- this file has been generated automatically. + cards = [["${config.services.heyefi.cardMacaddress}","${config.services.heyefi.uploadKey}"]] + upload_dir = "${toString config.services.heyefi.uploadDir}" + ''; + + environment.systemPackages = [ pkgs.heyefi ]; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/hostapd.nix b/nixpkgs/nixos/modules/services/networking/hostapd.nix new file mode 100644 index 000000000000..5d73038363a9 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hostapd.nix @@ -0,0 +1,218 @@ +{ config, lib, pkgs, utils, ... }: + +# TODO: +# +# asserts +# ensure that the nl80211 module is loaded/compiled in the kernel +# wpa_supplicant and hostapd on the same wireless interface doesn't make any sense + +with lib; + +let + + cfg = config.services.hostapd; + + escapedInterface = utils.escapeSystemdPath cfg.interface; + + configFile = pkgs.writeText "hostapd.conf" '' + interface=${cfg.interface} + driver=${cfg.driver} + ssid=${cfg.ssid} + hw_mode=${cfg.hwMode} + channel=${toString cfg.channel} + ${optionalString (cfg.countryCode != null) ''country_code=${cfg.countryCode}''} + ${optionalString (cfg.countryCode != null) ''ieee80211d=1''} + + # logging (debug level) + logger_syslog=-1 + logger_syslog_level=${toString cfg.logLevel} + logger_stdout=-1 + logger_stdout_level=${toString cfg.logLevel} + + ctrl_interface=/run/hostapd + ctrl_interface_group=${cfg.group} + + ${optionalString cfg.wpa '' + wpa=2 + wpa_passphrase=${cfg.wpaPassphrase} + ''} + ${optionalString cfg.noScan "noscan=1"} + + ${cfg.extraConfig} + '' ; + +in + +{ + ###### interface + + options = { + + services.hostapd = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable putting a wireless interface into infrastructure mode, + allowing other wireless devices to associate with the wireless + interface and do wireless networking. A simple access point will + <option>enable hostapd.wpa</option>, + <option>hostapd.wpaPassphrase</option>, and + <option>hostapd.ssid</option>, as well as DHCP on the wireless + interface to provide IP addresses to the associated stations, and + NAT (from the wireless interface to an upstream interface). + ''; + }; + + interface = mkOption { + default = ""; + example = "wlp2s0"; + description = '' + The interfaces <command>hostapd</command> will use. + ''; + }; + + noScan = mkOption { + type = types.bool; + default = false; + description = '' + Do not scan for overlapping BSSs in HT40+/- mode. + Caution: turning this on will violate regulatory requirements! + ''; + }; + + driver = mkOption { + default = "nl80211"; + example = "hostapd"; + type = types.str; + description = '' + Which driver <command>hostapd</command> will use. + Most applications will probably use the default. + ''; + }; + + ssid = mkOption { + default = "nixos"; + example = "mySpecialSSID"; + type = types.str; + description = "SSID to be used in IEEE 802.11 management frames."; + }; + + hwMode = mkOption { + default = "g"; + type = types.enum [ "a" "b" "g" ]; + description = '' + Operation mode. + (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g). + ''; + }; + + channel = mkOption { + default = 7; + example = 11; + type = types.int; + description = '' + Channel number (IEEE 802.11) + Please note that some drivers do not use this value from + <command>hostapd</command> and the channel will need to be configured + separately with <command>iwconfig</command>. + ''; + }; + + group = mkOption { + default = "wheel"; + example = "network"; + type = types.str; + description = '' + Members of this group can control <command>hostapd</command>. + ''; + }; + + wpa = mkOption { + type = types.bool; + default = true; + description = '' + Enable WPA (IEEE 802.11i/D3.0) to authenticate with the access point. + ''; + }; + + wpaPassphrase = mkOption { + default = "my_sekret"; + example = "any_64_char_string"; + type = types.str; + description = '' + WPA-PSK (pre-shared-key) passphrase. Clients will need this + passphrase to associate with this access point. + Warning: This passphrase will get put into a world-readable file in + the Nix store! + ''; + }; + + logLevel = mkOption { + default = 2; + type = types.int; + description = '' + Levels (minimum value for logged events): + 0 = verbose debugging + 1 = debugging + 2 = informational messages + 3 = notification + 4 = warning + ''; + }; + + countryCode = mkOption { + default = null; + example = "US"; + type = with types; nullOr str; + description = '' + Country code (ISO/IEC 3166-1). Used to set regulatory domain. + Set as needed to indicate country in which device is operating. + This can limit available channels and transmit power. + These two octets are used as the first two octets of the Country String + (dot11CountryString). + If set this enables IEEE 802.11d. This advertises the countryCode and + the set of allowed channels and transmit power levels based on the + regulatory limits. + ''; + }; + + extraConfig = mkOption { + default = ""; + example = '' + auth_algo=0 + ieee80211n=1 + ht_capab=[HT40-][SHORT-GI-40][DSSS_CCK-40] + ''; + type = types.lines; + description = "Extra configuration options to put in hostapd.conf."; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.hostapd ]; + + services.udev.packages = optional (cfg.countryCode != null) [ pkgs.crda ]; + + systemd.services.hostapd = + { description = "hostapd wireless AP"; + + path = [ pkgs.hostapd ]; + after = [ "sys-subsystem-net-devices-${escapedInterface}.device" ]; + bindsTo = [ "sys-subsystem-net-devices-${escapedInterface}.device" ]; + requiredBy = [ "network-link-${cfg.interface}.service" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = + { ExecStart = "${pkgs.hostapd}/bin/hostapd ${configFile}"; + Restart = "always"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/htpdate.nix b/nixpkgs/nixos/modules/services/networking/htpdate.nix new file mode 100644 index 000000000000..6954e5b060c4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/htpdate.nix @@ -0,0 +1,80 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + inherit (pkgs) htpdate; + + cfg = config.services.htpdate; +in + +{ + + ###### interface + + options = { + + services.htpdate = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable htpdate daemon. + ''; + }; + + extraOptions = mkOption { + type = types.str; + default = ""; + description = '' + Additional command line arguments to pass to htpdate. + ''; + }; + + servers = mkOption { + type = types.listOf types.str; + default = [ "www.google.com" ]; + description = '' + HTTP servers to use for time synchronization. + ''; + }; + + proxy = mkOption { + type = types.str; + default = ""; + example = "127.0.0.1:8118"; + description = '' + HTTP proxy used for requests. + ''; + }; + + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.htpdate = { + description = "htpdate daemon"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/htpdate.pid"; + ExecStart = concatStringsSep " " [ + "${htpdate}/bin/htpdate" + "-D -u nobody" + "-a -s" + "-l" + "${optionalString (cfg.proxy != "") "-P ${cfg.proxy}"}" + "${cfg.extraOptions}" + "${concatStringsSep " " cfg.servers}" + ]; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/default.nix b/nixpkgs/nixos/modules/services/networking/hylafax/default.nix new file mode 100644 index 000000000000..d8ffa3fc04d2 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hylafax/default.nix @@ -0,0 +1,31 @@ +{ config, lib, pkgs, ... }: + +{ + + imports = [ + ./options.nix + ./systemd.nix + ]; + + config = lib.modules.mkIf config.services.hylafax.enable { + environment.systemPackages = [ pkgs.hylafaxplus ]; + users.users.uucp = { + uid = config.ids.uids.uucp; + group = "uucp"; + description = "Unix-to-Unix CoPy system"; + isSystemUser = true; + inherit (config.users.users.nobody) home; + }; + assertions = [{ + assertion = config.services.hylafax.modems != {}; + message = '' + HylaFAX cannot be used without modems. + Please define at least one modem with + <option>config.services.hylafax.modems</option>. + ''; + }]; + }; + + meta.maintainers = [ lib.maintainers.yarny ]; + +} diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/faxq-default.nix b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-default.nix new file mode 100644 index 000000000000..9b634650cf79 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-default.nix @@ -0,0 +1,12 @@ +{ ... }: + +# see man:hylafax-config(5) + +{ + + ModemGroup = [ ''"any:0:.*"'' ]; + ServerTracing = "0x78701"; + SessionTracing = "0x78701"; + UUCPLockDir = "/var/lock"; + +} diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh new file mode 100755 index 000000000000..8c39e9d20c18 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hylafax/faxq-wait.sh @@ -0,0 +1,29 @@ +#! @shell@ -e + +# skip this if there are no modems at all +if ! stat -t "@spoolAreaPath@"/etc/config.* >/dev/null 2>&1 +then + exit 0 +fi + +echo "faxq started, waiting for modem(s) to initialize..." + +for i in `seq @timeoutSec@0 -1 0` # gracefully timeout +do + sleep 0.1 + # done if status files exist, but don't mention initialization + if \ + stat -t "@spoolAreaPath@"/status/* >/dev/null 2>&1 \ + && \ + ! grep --silent --ignore-case 'initializing server' \ + "@spoolAreaPath@"/status/* + then + echo "modem(s) apparently ready" + exit 0 + fi + # if i reached 0, modems probably failed to initialize + if test $i -eq 0 + then + echo "warning: modem initialization timed out" + fi +done diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/hfaxd-default.nix b/nixpkgs/nixos/modules/services/networking/hylafax/hfaxd-default.nix new file mode 100644 index 000000000000..8999dae57f41 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hylafax/hfaxd-default.nix @@ -0,0 +1,10 @@ +{ ... }: + +# see man:hfaxd(8) + +{ + + ServerTracing = "0x91"; + XferLogFile = "/clientlog"; + +} diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/modem-default.nix b/nixpkgs/nixos/modules/services/networking/hylafax/modem-default.nix new file mode 100644 index 000000000000..7529b5b0aafd --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hylafax/modem-default.nix @@ -0,0 +1,22 @@ +{ pkgs, ... }: + +# see man:hylafax-config(5) + +{ + + TagLineFont = "etc/LiberationSans-25.pcf"; + TagLineLocale = ''en_US.UTF-8''; + + AdminGroup = "root"; # groups that can change server config + AnswerRotary = "fax"; # don't accept anything else but faxes + LogFileMode = "0640"; + PriorityScheduling = true; + RecvFileMode = "0640"; + ServerTracing = "0x78701"; + SessionTracing = "0x78701"; + UUCPLockDir = "/var/lock"; + + SendPageCmd = ''${pkgs.coreutils}/bin/false''; # prevent pager transmit + SendUUCPCmd = ''${pkgs.coreutils}/bin/false''; # prevent UUCP transmit + +} diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/options.nix b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix new file mode 100644 index 000000000000..4ac6d3fa8432 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hylafax/options.nix @@ -0,0 +1,375 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (lib.options) literalExample mkEnableOption mkOption; + inherit (lib.types) bool enum int lines loaOf nullOr path str submodule; + inherit (lib.modules) mkDefault mkIf mkMerge; + + commonDescr = '' + Values can be either strings or integers + (which will be added to the config file verbatimly) + or lists thereof + (which will be translated to multiple + lines with the same configuration key). + Boolean values are translated to "Yes" or "No". + The default contains some reasonable + configuration to yield an operational system. + ''; + + str1 = lib.types.addCheck str (s: s!=""); # non-empty string + int1 = lib.types.addCheck int (i: i>0); # positive integer + + configAttrType = + # Options in HylaFAX configuration files can be + # booleans, strings, integers, or list thereof + # representing multiple config directives with the same key. + # This type definition resolves all + # those types into a list of strings. + let + inherit (lib.types) attrsOf coercedTo listOf; + innerType = coercedTo bool (x: if x then "Yes" else "No") + (coercedTo int (toString) str); + in + attrsOf (coercedTo innerType lib.singleton (listOf innerType)); + + cfg = config.services.hylafax; + + modemConfigOptions = { name, config, ... }: { + options = { + name = mkOption { + type = str1; + example = "ttyS1"; + description = '' + Name of modem device, + will be searched for in <filename>/dev</filename>. + ''; + }; + type = mkOption { + type = str1; + example = "cirrus"; + description = '' + Name of modem configuration file, + will be searched for in <filename>config</filename> + in the spooling area directory. + ''; + }; + config = mkOption { + type = configAttrType; + example = { + AreaCode = "49"; + LocalCode = "30"; + FAXNumber = "123456"; + LocalIdentifier = "LostInBerlin"; + }; + description = '' + Attribute set of values for the given modem. + ${commonDescr} + Options defined here override options in + <option>commonModemConfig</option> for this modem. + ''; + }; + }; + config.name = mkDefault name; + config.config.Include = [ "config/${config.type}" ]; + }; + + defaultConfig = + let + inherit (config.security) wrapperDir; + inherit (config.services.mail.sendmailSetuidWrapper) program; + mkIfDefault = cond: value: mkIf cond (mkDefault value); + noWrapper = config.services.mail.sendmailSetuidWrapper==null; + # If a sendmail setuid wrapper exists, + # we add the path to the default configuration file. + # Otherwise, we use `false` to provoke + # an error if hylafax tries to use it. + c.sendmailPath = mkMerge [ + (mkIfDefault noWrapper ''${pkgs.coreutils}/bin/false'') + (mkIfDefault (!noWrapper) ''${wrapperDir}/${program}'') + ]; + importDefaultConfig = file: + lib.attrsets.mapAttrs + (lib.trivial.const mkDefault) + (import file { inherit pkgs; }); + c.commonModemConfig = importDefaultConfig ./modem-default.nix; + c.faxqConfig = importDefaultConfig ./faxq-default.nix; + c.hfaxdConfig = importDefaultConfig ./hfaxd-default.nix; + in + c; + + localConfig = + let + c.hfaxdConfig.UserAccessFile = cfg.userAccessFile; + c.faxqConfig = lib.attrsets.mapAttrs + (lib.trivial.const (v: mkIf (v!=null) v)) + { + AreaCode = cfg.areaCode; + CountryCode = cfg.countryCode; + LongDistancePrefix = cfg.longDistancePrefix; + InternationalPrefix = cfg.internationalPrefix; + }; + c.commonModemConfig = c.faxqConfig; + in + c; + +in + + +{ + + + options.services.hylafax = { + + enable = mkEnableOption ''HylaFAX server''; + + autostart = mkOption { + type = bool; + default = true; + example = false; + description = '' + Autostart the HylaFAX queue manager at system start. + If this is <literal>false</literal>, the queue manager + will still be started if there are pending + jobs or if a user tries to connect to it. + ''; + }; + + countryCode = mkOption { + type = nullOr str1; + default = null; + example = "49"; + description = ''Country code for server and all modems.''; + }; + + areaCode = mkOption { + type = nullOr str1; + default = null; + example = "30"; + description = ''Area code for server and all modems.''; + }; + + longDistancePrefix = mkOption { + type = nullOr str; + default = null; + example = "0"; + description = ''Long distance prefix for server and all modems.''; + }; + + internationalPrefix = mkOption { + type = nullOr str; + default = null; + example = "00"; + description = ''International prefix for server and all modems.''; + }; + + spoolAreaPath = mkOption { + type = path; + default = "/var/spool/fax"; + description = '' + The spooling area will be created/maintained + at the location given here. + ''; + }; + + userAccessFile = mkOption { + type = path; + default = "/etc/hosts.hfaxd"; + description = '' + The <filename>hosts.hfaxd</filename> + file entry in the spooling area + will be symlinked to the location given here. + This file must exist and be + readable only by the <literal>uucp</literal> user. + See hosts.hfaxd(5) for details. + This configuration permits access for all users: + <literal> + environment.etc."hosts.hfaxd" = { + mode = "0600"; + user = "uucp"; + text = ".*"; + }; + </literal> + Note that host-based access can be controlled with + <option>config.systemd.sockets.hylafax-hfaxd.listenStreams</option>; + by default, only 127.0.0.1 is permitted to connect. + ''; + }; + + sendmailPath = mkOption { + type = path; + example = literalExample "''${pkgs.postfix}/bin/sendmail"; + # '' ; # fix vim + description = '' + Path to <filename>sendmail</filename> program. + The default uses the local sendmail wrapper + (see <option>config.services.mail.sendmailSetuidWrapper</option>), + otherwise the <filename>false</filename> + binary to cause an error if used. + ''; + }; + + hfaxdConfig = mkOption { + type = configAttrType; + example.RecvqProtection = "0400"; + description = '' + Attribute set of lines for the global + hfaxd config file <filename>etc/hfaxd.conf</filename>. + ${commonDescr} + ''; + }; + + faxqConfig = mkOption { + type = configAttrType; + example = { + InternationalPrefix = "00"; + LongDistancePrefix = "0"; + }; + description = '' + Attribute set of lines for the global + faxq config file <filename>etc/config</filename>. + ${commonDescr} + ''; + }; + + commonModemConfig = mkOption { + type = configAttrType; + example = { + InternationalPrefix = "00"; + LongDistancePrefix = "0"; + }; + description = '' + Attribute set of default values for + modem config files <filename>etc/config.*</filename>. + ${commonDescr} + Think twice before changing + paths of fax-processing scripts. + ''; + }; + + modems = mkOption { + type = loaOf (submodule [ modemConfigOptions ]); + default = {}; + example.ttyS1 = { + type = "cirrus"; + config = { + FAXNumber = "123456"; + LocalIdentifier = "Smith"; + }; + }; + description = '' + Description of installed modems. + At least on modem must be defined + to enable the HylaFAX server. + ''; + }; + + spoolExtraInit = mkOption { + type = lines; + default = ""; + example = ''chmod 0755 . # everyone may read my faxes''; + description = '' + Additional shell code that is executed within the + spooling area directory right after its setup. + ''; + }; + + faxcron.enable.spoolInit = mkEnableOption '' + Purge old files from the spooling area with + <filename>faxcron</filename> + each time the spooling area is initialized. + ''; + faxcron.enable.frequency = mkOption { + type = nullOr str1; + default = null; + example = "daily"; + description = '' + Purge old files from the spooling area with + <filename>faxcron</filename> with the given frequency + (see systemd.time(7)). + ''; + }; + faxcron.infoDays = mkOption { + type = int1; + default = 30; + description = '' + Set the expiration time for data in the + remote machine information directory in days. + ''; + }; + faxcron.logDays = mkOption { + type = int1; + default = 30; + description = '' + Set the expiration time for + session trace log files in days. + ''; + }; + faxcron.rcvDays = mkOption { + type = int1; + default = 7; + description = '' + Set the expiration time for files in + the received facsimile queue in days. + ''; + }; + + faxqclean.enable.spoolInit = mkEnableOption '' + Purge old files from the spooling area with + <filename>faxqclean</filename> + each time the spooling area is initialized. + ''; + faxqclean.enable.frequency = mkOption { + type = nullOr str1; + default = null; + example = "daily"; + description = '' + Purge old files from the spooling area with + <filename>faxcron</filename> with the given frequency + (see systemd.time(7)). + ''; + }; + faxqclean.archiving = mkOption { + type = enum [ "never" "as-flagged" "always" ]; + default = "as-flagged"; + example = "always"; + description = '' + Enable or suppress job archiving: + <literal>never</literal> disables job archiving, + <literal>as-flagged</literal> archives jobs that + have been flagged for archiving by sendfax, + <literal>always</literal> forces archiving of all jobs. + See also sendfax(1) and faxqclean(8). + ''; + }; + faxqclean.doneqMinutes = mkOption { + type = int1; + default = 15; + example = literalExample ''24*60''; + description = '' + Set the job + age threshold (in minutes) that controls how long + jobs may reside in the doneq directory. + ''; + }; + faxqclean.docqMinutes = mkOption { + type = int1; + default = 60; + example = literalExample ''24*60''; + description = '' + Set the document + age threshold (in minutes) that controls how long + unreferenced files may reside in the docq directory. + ''; + }; + + }; + + + config.services.hylafax = + mkIf + (config.services.hylafax.enable) + (mkMerge [ defaultConfig localConfig ]) + ; + +} diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh b/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh new file mode 100755 index 000000000000..31e930e8c597 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hylafax/spool.sh @@ -0,0 +1,111 @@ +#! @shell@ -e + +# The following lines create/update the HylaFAX spool directory: +# Subdirectories/files with persistent data are kept, +# other directories/files are removed/recreated, +# mostly from the template spool +# directory in the HylaFAX package. + +# This block explains how the spool area is +# derived from the spool template in the HylaFAX package: +# +# + capital letter: directory; file otherwise +# + P/p: persistent directory +# + F/f: directory with symlinks per entry +# + T/t: temporary data +# + S/s: single symlink into package +# | +# | + u: change ownership to uucp:uucp +# | + U: ..also change access mode to user-only +# | | +# archive P U +# bin S +# client T u (client connection info) +# config S +# COPYRIGHT s +# dev T u (maybe some FIFOs) +# docq P U +# doneq P U +# etc F contains customized config files! +# etc/hosts.hfaxd f +# etc/xferfaxlog f +# info P u (database of called devices) +# log P u (communication logs) +# pollq P U +# recvq P u +# sendq P U +# status T u (modem status info files) +# tmp T U + + +shopt -s dotglob # if bash sees "*", it also includes dot files +lnsym () { ln --symbol "$@" ; } +lnsymfrc () { ln --symbolic --force "$@" ; } +cprd () { cp --remove-destination "$@" ; } +update () { install --owner=@faxuser@ --group=@faxgroup@ "$@" ; } + + +## create/update spooling area + +update --mode=0750 -d "@spoolAreaPath@" +cd "@spoolAreaPath@" + +persist=(archive docq doneq info log pollq recvq sendq) + +# remove entries that don't belong here +touch dummy # ensure "*" resolves to something +for k in * +do + keep=0 + for j in "${persist[@]}" xferfaxlog clientlog faxcron.lastrun + do + if test "$k" == "$j" + then + keep=1 + break + fi + done + if test "$keep" == "0" + then + rm --recursive "$k" + fi +done + +# create persistent data directories (unless they exist already) +update --mode=0700 -d "${persist[@]}" +chmod 0755 info log recvq + +# create ``xferfaxlog``, ``faxcron.lastrun``, ``clientlog`` +touch clientlog faxcron.lastrun xferfaxlog +chown @faxuser@:@faxgroup@ clientlog faxcron.lastrun xferfaxlog + +# create symlinks for frozen directories/files +lnsym --target-directory=. "@hylafax@"/spool/{COPYRIGHT,bin,config} + +# create empty temporary directories +update --mode=0700 -d client dev status +update -d tmp + + +## create and fill etc + +install -d "@spoolAreaPath@/etc" +cd "@spoolAreaPath@/etc" + +# create symlinks to all files in template's etc +lnsym --target-directory=. "@hylafax@/spool/etc"/* + +# set LOCKDIR in setup.cache +sed --regexp-extended 's|^(UUCP_LOCKDIR=).*$|\1'"'@lockPath@'|g" --in-place setup.cache + +# etc/{xferfaxlog,lastrun} are stored in the spool root +lnsymfrc --target-directory=. ../xferfaxlog +lnsymfrc --no-target-directory ../faxcron.lastrun lastrun + +# etc/hosts.hfaxd is provided by the NixOS configuration +lnsymfrc --no-target-directory "@userAccessFile@" hosts.hfaxd + +# etc/config and etc/config.${DEVID} must be copied: +# hfaxd reads these file after locking itself up in a chroot +cprd --no-target-directory "@globalConfigPath@" config +cprd --target-directory=. "@modemConfigPath@"/* diff --git a/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix b/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix new file mode 100644 index 000000000000..b9b9b9dca4f0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hylafax/systemd.nix @@ -0,0 +1,249 @@ +{ config, lib, pkgs, ... }: + + +let + + inherit (lib) mkIf mkMerge; + inherit (lib) concatStringsSep optionalString; + + cfg = config.services.hylafax; + mapModems = lib.forEach (lib.attrValues cfg.modems); + + mkConfigFile = name: conf: + # creates hylafax config file, + # makes sure "Include" is listed *first* + let + mkLines = conf: + (lib.concatLists + (lib.flip lib.mapAttrsToList conf + (k: map (v: ''${k}: ${v}'') + ))); + include = mkLines { Include = conf.Include or []; }; + other = mkLines ( conf // { Include = []; } ); + in + pkgs.writeText ''hylafax-config${name}'' + (concatStringsSep "\n" (include ++ other)); + + globalConfigPath = mkConfigFile "" cfg.faxqConfig; + + modemConfigPath = + let + mkModemConfigFile = { config, name, ... }: + mkConfigFile ''.${name}'' + (cfg.commonModemConfig // config); + mkLine = { name, type, ... }@modem: '' + # check if modem config file exists: + test -f "${pkgs.hylafaxplus}/spool/config/${type}" + ln \ + --symbolic \ + --no-target-directory \ + "${mkModemConfigFile modem}" \ + "$out/config.${name}" + ''; + in + pkgs.runCommand "hylafax-config-modems" { preferLocalBuild = true; } + ''mkdir --parents "$out/" ${concatStringsSep "\n" (mapModems mkLine)}''; + + setupSpoolScript = pkgs.substituteAll { + name = "hylafax-setup-spool.sh"; + src = ./spool.sh; + isExecutable = true; + inherit (pkgs.stdenv) shell; + hylafax = pkgs.hylafaxplus; + faxuser = "uucp"; + faxgroup = "uucp"; + lockPath = "/var/lock"; + inherit globalConfigPath modemConfigPath; + inherit (cfg) sendmailPath spoolAreaPath userAccessFile; + }; + + waitFaxqScript = pkgs.substituteAll { + # This script checks the modems status files + # and waits until all modems report readiness. + name = "hylafax-faxq-wait-start.sh"; + src = ./faxq-wait.sh; + isExecutable = true; + timeoutSec = toString 10; + inherit (pkgs.stdenv) shell; + inherit (cfg) spoolAreaPath; + }; + + sockets.hylafax-hfaxd = { + description = "HylaFAX server socket"; + documentation = [ "man:hfaxd(8)" ]; + wantedBy = [ "multi-user.target" ]; + listenStreams = [ "127.0.0.1:4559" ]; + socketConfig.FreeBind = true; + socketConfig.Accept = true; + }; + + paths.hylafax-faxq = { + description = "HylaFAX queue manager sendq watch"; + documentation = [ "man:faxq(8)" "man:sendq(5)" ]; + wantedBy = [ "multi-user.target" ]; + pathConfig.PathExistsGlob = [ ''${cfg.spoolAreaPath}/sendq/q*'' ]; + }; + + timers = mkMerge [ + ( + mkIf (cfg.faxcron.enable.frequency!=null) + { hylafax-faxcron.timerConfig.Persistent = true; } + ) + ( + mkIf (cfg.faxqclean.enable.frequency!=null) + { hylafax-faxqclean.timerConfig.Persistent = true; } + ) + ]; + + hardenService = + # Add some common systemd service hardening settings, + # but allow each service (here) to override + # settings by explicitely setting those to `null`. + # More hardening would be nice but makes + # customizing hylafax setups very difficult. + # If at all, it should only be added along + # with some options to customize it. + let + hardening = { + PrivateDevices = true; # breaks /dev/tty... + PrivateNetwork = true; + PrivateTmp = true; + ProtectControlGroups = true; + #ProtectHome = true; # breaks custom spool dirs + ProtectKernelModules = true; + ProtectKernelTunables = true; + #ProtectSystem = "strict"; # breaks custom spool dirs + RestrictNamespaces = true; + RestrictRealtime = true; + }; + filter = key: value: (value != null) || ! (lib.hasAttr key hardening); + apply = service: lib.filterAttrs filter (hardening // (service.serviceConfig or {})); + in + service: service // { serviceConfig = apply service; }; + + services.hylafax-spool = { + description = "HylaFAX spool area preparation"; + documentation = [ "man:hylafax-server(4)" ]; + script = '' + ${setupSpoolScript} + cd "${cfg.spoolAreaPath}" + ${cfg.spoolExtraInit} + if ! test -f "${cfg.spoolAreaPath}/etc/hosts.hfaxd" + then + echo hosts.hfaxd is missing + exit 1 + fi + ''; + serviceConfig.ExecStop = ''${setupSpoolScript}''; + serviceConfig.RemainAfterExit = true; + serviceConfig.Type = "oneshot"; + unitConfig.RequiresMountsFor = [ cfg.spoolAreaPath ]; + }; + + services.hylafax-faxq = { + description = "HylaFAX queue manager"; + documentation = [ "man:faxq(8)" ]; + requires = [ "hylafax-spool.service" ]; + after = [ "hylafax-spool.service" ]; + wants = mapModems ( { name, ... }: ''hylafax-faxgetty@${name}.service'' ); + wantedBy = mkIf cfg.autostart [ "multi-user.target" ]; + serviceConfig.Type = "forking"; + serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/faxq -q "${cfg.spoolAreaPath}"''; + # This delays the "readiness" of this service until + # all modems are initialized (or a timeout is reached). + # Otherwise, sending a fax with the fax service + # stopped will always yield a failed send attempt: + # The fax service is started when the job is created with + # `sendfax`, but modems need some time to initialize. + serviceConfig.ExecStartPost = [ ''${waitFaxqScript}'' ]; + # faxquit fails if the pipe is already gone + # (e.g. the service is already stopping) + serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}"''; + # disable some systemd hardening settings + serviceConfig.PrivateDevices = null; + serviceConfig.RestrictRealtime = null; + }; + + services."hylafax-hfaxd@" = { + description = "HylaFAX server"; + documentation = [ "man:hfaxd(8)" ]; + after = [ "hylafax-faxq.service" ]; + requires = [ "hylafax-faxq.service" ]; + serviceConfig.StandardInput = "socket"; + serviceConfig.StandardOutput = "socket"; + serviceConfig.ExecStart = ''${pkgs.hylafaxplus}/spool/bin/hfaxd -q "${cfg.spoolAreaPath}" -d -I''; + unitConfig.RequiresMountsFor = [ cfg.userAccessFile ]; + # disable some systemd hardening settings + serviceConfig.PrivateDevices = null; + serviceConfig.PrivateNetwork = null; + }; + + services.hylafax-faxcron = rec { + description = "HylaFAX spool area maintenance"; + documentation = [ "man:faxcron(8)" ]; + after = [ "hylafax-spool.service" ]; + requires = [ "hylafax-spool.service" ]; + wantedBy = mkIf cfg.faxcron.enable.spoolInit requires; + startAt = mkIf (cfg.faxcron.enable.frequency!=null) cfg.faxcron.enable.frequency; + serviceConfig.ExecStart = concatStringsSep " " [ + ''${pkgs.hylafaxplus}/spool/bin/faxcron'' + ''-q "${cfg.spoolAreaPath}"'' + ''-info ${toString cfg.faxcron.infoDays}'' + ''-log ${toString cfg.faxcron.logDays}'' + ''-rcv ${toString cfg.faxcron.rcvDays}'' + ]; + }; + + services.hylafax-faxqclean = rec { + description = "HylaFAX spool area queue cleaner"; + documentation = [ "man:faxqclean(8)" ]; + after = [ "hylafax-spool.service" ]; + requires = [ "hylafax-spool.service" ]; + wantedBy = mkIf cfg.faxqclean.enable.spoolInit requires; + startAt = mkIf (cfg.faxqclean.enable.frequency!=null) cfg.faxqclean.enable.frequency; + serviceConfig.ExecStart = concatStringsSep " " [ + ''${pkgs.hylafaxplus}/spool/bin/faxqclean'' + ''-q "${cfg.spoolAreaPath}"'' + ''-v'' + (optionalString (cfg.faxqclean.archiving!="never") ''-a'') + (optionalString (cfg.faxqclean.archiving=="always") ''-A'') + ''-j ${toString (cfg.faxqclean.doneqMinutes*60)}'' + ''-d ${toString (cfg.faxqclean.docqMinutes*60)}'' + ]; + }; + + mkFaxgettyService = { name, ... }: + lib.nameValuePair ''hylafax-faxgetty@${name}'' rec { + description = "HylaFAX faxgetty for %I"; + documentation = [ "man:faxgetty(8)" ]; + bindsTo = [ "dev-%i.device" ]; + requires = [ "hylafax-spool.service" ]; + after = bindsTo ++ requires; + before = [ "hylafax-faxq.service" "getty.target" ]; + unitConfig.StopWhenUnneeded = true; + unitConfig.AssertFileNotEmpty = ''${cfg.spoolAreaPath}/etc/config.%I''; + serviceConfig.UtmpIdentifier = "%I"; + serviceConfig.TTYPath = "/dev/%I"; + serviceConfig.Restart = "always"; + serviceConfig.KillMode = "process"; + serviceConfig.IgnoreSIGPIPE = false; + serviceConfig.ExecStart = ''-${pkgs.hylafaxplus}/spool/bin/faxgetty -q "${cfg.spoolAreaPath}" /dev/%I''; + # faxquit fails if the pipe is already gone + # (e.g. the service is already stopping) + serviceConfig.ExecStop = ''-${pkgs.hylafaxplus}/spool/bin/faxquit -q "${cfg.spoolAreaPath}" %I''; + # disable some systemd hardening settings + serviceConfig.PrivateDevices = null; + serviceConfig.RestrictRealtime = null; + }; + + modemServices = + lib.listToAttrs (mapModems mkFaxgettyService); + +in + +{ + config.systemd = mkIf cfg.enable { + inherit sockets timers paths; + services = lib.mapAttrs (lib.const hardenService) (services // modemServices); + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/i2p.nix b/nixpkgs/nixos/modules/services/networking/i2p.nix new file mode 100644 index 000000000000..3b6010531f13 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/i2p.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.i2p; + homeDir = "/var/lib/i2p"; +in { + ###### interface + options.services.i2p.enable = mkEnableOption "I2P router"; + + ###### implementation + config = mkIf cfg.enable { + users.users.i2p = { + group = "i2p"; + description = "i2p User"; + home = homeDir; + createHome = true; + uid = config.ids.uids.i2p; + }; + users.groups.i2p.gid = config.ids.gids.i2p; + systemd.services.i2p = { + description = "I2P router with administration interface for hidden services"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "i2p"; + WorkingDirectory = homeDir; + Restart = "on-abort"; + ExecStart = "${pkgs.i2p}/bin/i2prouter-plain"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/i2pd.nix b/nixpkgs/nixos/modules/services/networking/i2pd.nix new file mode 100644 index 000000000000..93a21fd4c97e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/i2pd.nix @@ -0,0 +1,684 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.i2pd; + + homeDir = "/var/lib/i2pd"; + + strOpt = k: v: k + " = " + v; + boolOpt = k: v: k + " = " + boolToString v; + intOpt = k: v: k + " = " + toString v; + lstOpt = k: xs: k + " = " + concatStringsSep "," xs; + optionalNullString = o: s: optional (s != null) (strOpt o s); + optionalNullBool = o: b: optional (b != null) (boolOpt o b); + optionalNullInt = o: i: optional (i != null) (intOpt o i); + optionalEmptyList = o: l: optional ([] != l) (lstOpt o l); + + mkEnableTrueOption = name: mkEnableOption name // { default = true; }; + + mkEndpointOpt = name: addr: port: { + enable = mkEnableOption name; + name = mkOption { + type = types.str; + default = name; + description = "The endpoint name."; + }; + address = mkOption { + type = types.str; + default = addr; + description = "Bind address for ${name} endpoint."; + }; + port = mkOption { + type = types.int; + default = port; + description = "Bind port for ${name} endoint."; + }; + }; + + i2cpOpts = name: { + length = mkOption { + type = types.int; + description = "Guaranteed minimum hops for ${name} tunnels."; + default = 3; + }; + quantity = mkOption { + type = types.int; + description = "Number of simultaneous ${name} tunnels."; + default = 5; + }; + }; + + mkKeyedEndpointOpt = name: addr: port: keyloc: + (mkEndpointOpt name addr port) // { + keys = mkOption { + type = with types; nullOr str; + default = keyloc; + description = '' + File to persist ${lib.toUpper name} keys. + ''; + }; + inbound = i2cpOpts name; + outbound = i2cpOpts name; + latency.min = mkOption { + type = with types; nullOr int; + description = "Min latency for tunnels."; + default = null; + }; + latency.max = mkOption { + type = with types; nullOr int; + description = "Max latency for tunnels."; + default = null; + }; + }; + + commonTunOpts = name: { + outbound = i2cpOpts name; + inbound = i2cpOpts name; + crypto.tagsToSend = mkOption { + type = types.int; + description = "Number of ElGamal/AES tags to send."; + default = 40; + }; + destination = mkOption { + type = types.str; + description = "Remote endpoint, I2P hostname or b32.i2p address."; + }; + keys = mkOption { + type = types.str; + default = name + "-keys.dat"; + description = "Keyset used for tunnel identity."; + }; + } // mkEndpointOpt name "127.0.0.1" 0; + + sec = name: "\n[" + name + "]"; + notice = "# DO NOT EDIT -- this file has been generated automatically."; + i2pdConf = let + opts = [ + notice + (strOpt "loglevel" cfg.logLevel) + (boolOpt "logclftime" cfg.logCLFTime) + (boolOpt "ipv4" cfg.enableIPv4) + (boolOpt "ipv6" cfg.enableIPv6) + (boolOpt "notransit" cfg.notransit) + (boolOpt "floodfill" cfg.floodfill) + (intOpt "netid" cfg.netid) + ] ++ (optionalNullInt "bandwidth" cfg.bandwidth) + ++ (optionalNullInt "port" cfg.port) + ++ (optionalNullString "family" cfg.family) + ++ (optionalNullString "datadir" cfg.dataDir) + ++ (optionalNullInt "share" cfg.share) + ++ (optionalNullBool "ssu" cfg.ssu) + ++ (optionalNullBool "ntcp" cfg.ntcp) + ++ (optionalNullString "ntcpproxy" cfg.ntcpProxy) + ++ (optionalNullString "ifname" cfg.ifname) + ++ (optionalNullString "ifname4" cfg.ifname4) + ++ (optionalNullString "ifname6" cfg.ifname6) + ++ [ + (sec "limits") + (intOpt "transittunnels" cfg.limits.transittunnels) + (intOpt "coresize" cfg.limits.coreSize) + (intOpt "openfiles" cfg.limits.openFiles) + (intOpt "ntcphard" cfg.limits.ntcpHard) + (intOpt "ntcpsoft" cfg.limits.ntcpSoft) + (intOpt "ntcpthreads" cfg.limits.ntcpThreads) + (sec "upnp") + (boolOpt "enabled" cfg.upnp.enable) + (sec "precomputation") + (boolOpt "elgamal" cfg.precomputation.elgamal) + (sec "reseed") + (boolOpt "verify" cfg.reseed.verify) + ] ++ (optionalNullString "file" cfg.reseed.file) + ++ (optionalEmptyList "urls" cfg.reseed.urls) + ++ (optionalNullString "floodfill" cfg.reseed.floodfill) + ++ (optionalNullString "zipfile" cfg.reseed.zipfile) + ++ (optionalNullString "proxy" cfg.reseed.proxy) + ++ [ + (sec "trust") + (boolOpt "enabled" cfg.trust.enable) + (boolOpt "hidden" cfg.trust.hidden) + ] ++ (optionalEmptyList "routers" cfg.trust.routers) + ++ (optionalNullString "family" cfg.trust.family) + ++ [ + (sec "websockets") + (boolOpt "enabled" cfg.websocket.enable) + (strOpt "address" cfg.websocket.address) + (intOpt "port" cfg.websocket.port) + (sec "exploratory") + (intOpt "inbound.length" cfg.exploratory.inbound.length) + (intOpt "inbound.quantity" cfg.exploratory.inbound.quantity) + (intOpt "outbound.length" cfg.exploratory.outbound.length) + (intOpt "outbound.quantity" cfg.exploratory.outbound.quantity) + (sec "ntcp2") + (boolOpt "enabled" cfg.ntcp2.enable) + (boolOpt "published" cfg.ntcp2.published) + (intOpt "port" cfg.ntcp2.port) + (sec "addressbook") + (strOpt "defaulturl" cfg.addressbook.defaulturl) + ] ++ (optionalEmptyList "subscriptions" cfg.addressbook.subscriptions) + ++ (flip map + (collect (proto: proto ? port && proto ? address) cfg.proto) + (proto: let protoOpts = [ + (sec proto.name) + (boolOpt "enabled" proto.enable) + (strOpt "address" proto.address) + (intOpt "port" proto.port) + ] ++ (if proto ? keys then optionalNullString "keys" proto.keys else []) + ++ (if proto ? auth then optionalNullBool "auth" proto.auth else []) + ++ (if proto ? user then optionalNullString "user" proto.user else []) + ++ (if proto ? pass then optionalNullString "pass" proto.pass else []) + ++ (if proto ? strictHeaders then optionalNullBool "strictheaders" proto.strictHeaders else []) + ++ (if proto ? hostname then optionalNullString "hostname" proto.hostname else []) + ++ (if proto ? outproxy then optionalNullString "outproxy" proto.outproxy else []) + ++ (if proto ? outproxyPort then optionalNullInt "outproxyport" proto.outproxyPort else []) + ++ (if proto ? outproxyEnable then optionalNullBool "outproxy.enabled" proto.outproxyEnable else []); + in (concatStringsSep "\n" protoOpts) + )); + in + pkgs.writeText "i2pd.conf" (concatStringsSep "\n" opts); + + tunnelConf = let opts = [ + notice + (flip map + (collect (tun: tun ? port && tun ? destination) cfg.outTunnels) + (tun: let outTunOpts = [ + (sec tun.name) + "type = client" + (intOpt "port" tun.port) + (strOpt "destination" tun.destination) + ] ++ (if tun ? destinationPort then optionalNullInt "destinationport" tun.destinationPort else []) + ++ (if tun ? keys then + optionalNullString "keys" tun.keys else []) + ++ (if tun ? address then + optionalNullString "address" tun.address else []) + ++ (if tun ? inbound.length then + optionalNullInt "inbound.length" tun.inbound.length else []) + ++ (if tun ? inbound.quantity then + optionalNullInt "inbound.quantity" tun.inbound.quantity else []) + ++ (if tun ? outbound.length then + optionalNullInt "outbound.length" tun.outbound.length else []) + ++ (if tun ? outbound.quantity then + optionalNullInt "outbound.quantity" tun.outbound.quantity else []) + ++ (if tun ? crypto.tagsToSend then + optionalNullInt "crypto.tagstosend" tun.crypto.tagsToSend else []); + in concatStringsSep "\n" outTunOpts)) + (flip map + (collect (tun: tun ? port && tun ? address) cfg.inTunnels) + (tun: let inTunOpts = [ + (sec tun.name) + "type = server" + (intOpt "port" tun.port) + (strOpt "host" tun.address) + ] ++ (if tun ? destination then + optionalNullString "destination" tun.destination else []) + ++ (if tun ? keys then + optionalNullString "keys" tun.keys else []) + ++ (if tun ? inPort then + optionalNullInt "inport" tun.inPort else []) + ++ (if tun ? accessList then + optionalEmptyList "accesslist" tun.accessList else []); + in concatStringsSep "\n" inTunOpts))]; + in pkgs.writeText "i2pd-tunnels.conf" opts; + + i2pdSh = pkgs.writeScriptBin "i2pd" '' + #!/bin/sh + exec ${pkgs.i2pd}/bin/i2pd \ + ${if cfg.address == null then "" else "--host="+cfg.address} \ + --service \ + --conf=${i2pdConf} \ + --tunconf=${tunnelConf} + ''; + +in + +{ + + imports = [ + (mkRenamedOptionModule [ "services" "i2pd" "extIp" ] [ "services" "i2pd" "address" ]) + ]; + + ###### interface + + options = { + + services.i2pd = { + + enable = mkEnableOption "I2Pd daemon" // { + description = '' + Enables I2Pd as a running service upon activation. + Please read http://i2pd.readthedocs.io/en/latest/ for further + configuration help. + ''; + }; + + logLevel = mkOption { + type = types.enum ["debug" "info" "warn" "error"]; + default = "error"; + description = '' + The log level. <command>i2pd</command> defaults to "info" + but that generates copious amounts of log messages. + + We default to "error" which is similar to the default log + level of <command>tor</command>. + ''; + }; + + logCLFTime = mkEnableOption "Full CLF-formatted date and time to log"; + + address = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Your external IP or hostname. + ''; + }; + + family = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Specify a family the router belongs to. + ''; + }; + + dataDir = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Alternative path to storage of i2pd data (RI, keys, peer profiles, ...) + ''; + }; + + share = mkOption { + type = types.int; + default = 100; + description = '' + Limit of transit traffic from max bandwidth in percents. + ''; + }; + + ifname = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Network interface to bind to. + ''; + }; + + ifname4 = mkOption { + type = with types; nullOr str; + default = null; + description = '' + IPv4 interface to bind to. + ''; + }; + + ifname6 = mkOption { + type = with types; nullOr str; + default = null; + description = '' + IPv6 interface to bind to. + ''; + }; + + ntcpProxy = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Proxy URL for NTCP transport. + ''; + }; + + ntcp = mkEnableTrueOption "ntcp"; + ssu = mkEnableTrueOption "ssu"; + + notransit = mkEnableOption "notransit" // { + description = '' + Tells the router to not accept transit tunnels during startup. + ''; + }; + + floodfill = mkEnableOption "floodfill" // { + description = '' + If the router is declared to be unreachable and needs introduction nodes. + ''; + }; + + netid = mkOption { + type = types.int; + default = 2; + description = '' + I2P overlay netid. + ''; + }; + + bandwidth = mkOption { + type = with types; nullOr int; + default = null; + description = '' + Set a router bandwidth limit integer in KBps. + If not set, <command>i2pd</command> defaults to 32KBps. + ''; + }; + + port = mkOption { + type = with types; nullOr int; + default = null; + description = '' + I2P listen port. If no one is given the router will pick between 9111 and 30777. + ''; + }; + + enableIPv4 = mkEnableTrueOption "IPv4 connectivity"; + enableIPv6 = mkEnableOption "IPv6 connectivity"; + nat = mkEnableTrueOption "NAT bypass"; + + upnp.enable = mkEnableOption "UPnP service discovery"; + upnp.name = mkOption { + type = types.str; + default = "I2Pd"; + description = '' + Name i2pd appears in UPnP forwardings list. + ''; + }; + + precomputation.elgamal = mkEnableTrueOption "Precomputed ElGamal tables" // { + description = '' + Whenever to use precomputated tables for ElGamal. + <command>i2pd</command> defaults to <literal>false</literal> + to save 64M of memory (and looses some performance). + + We default to <literal>true</literal> as that is what most + users want anyway. + ''; + }; + + reseed.verify = mkEnableOption "SU3 signature verification"; + + reseed.file = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Full path to SU3 file to reseed from. + ''; + }; + + reseed.urls = mkOption { + type = with types; listOf str; + default = []; + description = '' + Reseed URLs. + ''; + }; + + reseed.floodfill = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Path to router info of floodfill to reseed from. + ''; + }; + + reseed.zipfile = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Path to local .zip file to reseed from. + ''; + }; + + reseed.proxy = mkOption { + type = with types; nullOr str; + default = null; + description = '' + URL for reseed proxy, supports http/socks. + ''; + }; + + addressbook.defaulturl = mkOption { + type = types.str; + default = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"; + description = '' + AddressBook subscription URL for initial setup + ''; + }; + addressbook.subscriptions = mkOption { + type = with types; listOf str; + default = [ + "http://inr.i2p/export/alive-hosts.txt" + "http://i2p-projekt.i2p/hosts.txt" + "http://stats.i2p/cgi-bin/newhosts.txt" + ]; + description = '' + AddressBook subscription URLs + ''; + }; + + trust.enable = mkEnableOption "Explicit trust options"; + + trust.family = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Router Familiy to trust for first hops. + ''; + }; + + trust.routers = mkOption { + type = with types; listOf str; + default = []; + description = '' + Only connect to the listed routers. + ''; + }; + + trust.hidden = mkEnableOption "Router concealment"; + + websocket = mkEndpointOpt "websockets" "127.0.0.1" 7666; + + exploratory.inbound = i2cpOpts "exploratory"; + exploratory.outbound = i2cpOpts "exploratory"; + + ntcp2.enable = mkEnableTrueOption "NTCP2."; + ntcp2.published = mkEnableOption "NTCP2 publication"; + ntcp2.port = mkOption { + type = types.int; + default = 0; + description = '' + Port to listen for incoming NTCP2 connections (0=auto). + ''; + }; + + limits.transittunnels = mkOption { + type = types.int; + default = 2500; + description = '' + Maximum number of active transit sessions. + ''; + }; + + limits.coreSize = mkOption { + type = types.int; + default = 0; + description = '' + Maximum size of corefile in Kb (0 - use system limit). + ''; + }; + + limits.openFiles = mkOption { + type = types.int; + default = 0; + description = '' + Maximum number of open files (0 - use system default). + ''; + }; + + limits.ntcpHard = mkOption { + type = types.int; + default = 0; + description = '' + Maximum number of active transit sessions. + ''; + }; + + limits.ntcpSoft = mkOption { + type = types.int; + default = 0; + description = '' + Threshold to start probabalistic backoff with ntcp sessions (default: use system limit). + ''; + }; + + limits.ntcpThreads = mkOption { + type = types.int; + default = 1; + description = '' + Maximum number of threads used by NTCP DH worker. + ''; + }; + + proto.http = (mkEndpointOpt "http" "127.0.0.1" 7070) // { + + auth = mkEnableOption "Webconsole authentication"; + + user = mkOption { + type = types.str; + default = "i2pd"; + description = '' + Username for webconsole access + ''; + }; + + pass = mkOption { + type = types.str; + default = "i2pd"; + description = '' + Password for webconsole access. + ''; + }; + + strictHeaders = mkOption { + type = with types; nullOr bool; + default = null; + description = '' + Enable strict host checking on WebUI. + ''; + }; + + hostname = mkOption { + type = with types; nullOr str; + default = null; + description = '' + Expected hostname for WebUI. + ''; + }; + }; + + proto.httpProxy = (mkKeyedEndpointOpt "httpproxy" "127.0.0.1" 4444 "httpproxy-keys.dat") + // { + outproxy = mkOption { + type = with types; nullOr str; + default = null; + description = "Upstream outproxy bind address."; + }; + }; + proto.socksProxy = (mkKeyedEndpointOpt "socksproxy" "127.0.0.1" 4447 "socksproxy-keys.dat") + // { + outproxyEnable = mkEnableOption "SOCKS outproxy"; + outproxy = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Upstream outproxy bind address."; + }; + outproxyPort = mkOption { + type = types.int; + default = 4444; + description = "Upstream outproxy bind port."; + }; + }; + + proto.sam = mkEndpointOpt "sam" "127.0.0.1" 7656; + proto.bob = mkEndpointOpt "bob" "127.0.0.1" 2827; + proto.i2cp = mkEndpointOpt "i2cp" "127.0.0.1" 7654; + proto.i2pControl = mkEndpointOpt "i2pcontrol" "127.0.0.1" 7650; + + outTunnels = mkOption { + default = {}; + type = with types; attrsOf (submodule ( + { name, ... }: { + options = { + destinationPort = mkOption { + type = with types; nullOr int; + default = null; + description = "Connect to particular port at destination."; + }; + } // commonTunOpts name; + config = { + name = mkDefault name; + }; + } + )); + description = '' + Connect to someone as a client and establish a local accept endpoint + ''; + }; + + inTunnels = mkOption { + default = {}; + type = with types; attrsOf (submodule ( + { name, ... }: { + options = { + inPort = mkOption { + type = types.int; + default = 0; + description = "Service port. Default to the tunnel's listen port."; + }; + accessList = mkOption { + type = with types; listOf str; + default = []; + description = "I2P nodes that are allowed to connect to this service."; + }; + } // commonTunOpts name; + config = { + name = mkDefault name; + }; + } + )); + description = '' + Serve something on I2P network at port and delegate requests to address inPort. + ''; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.users.i2pd = { + group = "i2pd"; + description = "I2Pd User"; + home = homeDir; + createHome = true; + uid = config.ids.uids.i2pd; + }; + + users.groups.i2pd.gid = config.ids.gids.i2pd; + + systemd.services.i2pd = { + description = "Minimal I2P router"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = + { + User = "i2pd"; + WorkingDirectory = homeDir; + Restart = "on-abort"; + ExecStart = "${i2pdSh}/bin/i2pd"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/iodine.nix b/nixpkgs/nixos/modules/services/networking/iodine.nix new file mode 100644 index 000000000000..46051d7044b5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/iodine.nix @@ -0,0 +1,197 @@ +# NixOS module for iodine, ip over dns daemon + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.iodine; + + iodinedUser = "iodined"; + + /* is this path made unreadable by ProtectHome = true ? */ + isProtected = x: hasPrefix "/root" x || hasPrefix "/home" x; +in +{ + imports = [ + (mkRenamedOptionModule [ "services" "iodined" "enable" ] [ "services" "iodine" "server" "enable" ]) + (mkRenamedOptionModule [ "services" "iodined" "domain" ] [ "services" "iodine" "server" "domain" ]) + (mkRenamedOptionModule [ "services" "iodined" "ip" ] [ "services" "iodine" "server" "ip" ]) + (mkRenamedOptionModule [ "services" "iodined" "extraConfig" ] [ "services" "iodine" "server" "extraConfig" ]) + (mkRemovedOptionModule [ "services" "iodined" "client" ] "") + ]; + + ### configuration + + options = { + + services.iodine = { + clients = mkOption { + default = {}; + description = '' + Each attribute of this option defines a systemd service that + runs iodine. Many or none may be defined. + The name of each service is + <literal>iodine-<replaceable>name</replaceable></literal> + where <replaceable>name</replaceable> is the name of the + corresponding attribute name. + ''; + example = literalExample '' + { + foo = { + server = "tunnel.mdomain.com"; + relay = "8.8.8.8"; + extraConfig = "-v"; + } + } + ''; + type = types.attrsOf ( + types.submodule ( + { + options = { + server = mkOption { + type = types.str; + default = ""; + description = "Hostname of server running iodined"; + example = "tunnel.mydomain.com"; + }; + + relay = mkOption { + type = types.str; + default = ""; + description = "DNS server to use as an intermediate relay to the iodined server"; + example = "8.8.8.8"; + }; + + extraConfig = mkOption { + type = types.str; + default = ""; + description = "Additional command line parameters"; + example = "-l 192.168.1.10 -p 23"; + }; + + passwordFile = mkOption { + type = types.str; + default = ""; + description = "Path to a file containing the password."; + }; + }; + } + ) + ); + }; + + server = { + enable = mkOption { + type = types.bool; + default = false; + description = "enable iodined server"; + }; + + ip = mkOption { + type = types.str; + default = ""; + description = "The assigned ip address or ip range"; + example = "172.16.10.1/24"; + }; + + domain = mkOption { + type = types.str; + default = ""; + description = "Domain or subdomain of which nameservers point to us"; + example = "tunnel.mydomain.com"; + }; + + extraConfig = mkOption { + type = types.str; + default = ""; + description = "Additional command line parameters"; + example = "-l 192.168.1.10 -p 23"; + }; + + passwordFile = mkOption { + type = types.str; + default = ""; + description = "File that contains password"; + }; + }; + + }; + }; + + ### implementation + + config = mkIf (cfg.server.enable || cfg.clients != {}) { + environment.systemPackages = [ pkgs.iodine ]; + boot.kernelModules = [ "tun" ]; + + systemd.services = + let + createIodineClientService = name: cfg: + { + description = "iodine client - ${name}"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "exec ${pkgs.iodine}/bin/iodine -f -u ${iodinedUser} ${cfg.extraConfig} ${optionalString (cfg.passwordFile != "") "< \"${builtins.toString cfg.passwordFile}\""} ${cfg.relay} ${cfg.server}"; + serviceConfig = { + RestartSec = "30s"; + Restart = "always"; + + # hardening : + # Filesystem access + ProtectSystem = "strict"; + ProtectHome = if isProtected cfg.passwordFile then "read-only" else "true" ; + PrivateTmp = true; + ReadWritePaths = "/dev/net/tun"; + PrivateDevices = false; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + # Caps + NoNewPrivileges = true; + # Misc. + LockPersonality = true; + RestrictRealtime = true; + PrivateMounts = true; + MemoryDenyWriteExecute = true; + }; + }; + in + listToAttrs ( + mapAttrsToList + (name: value: nameValuePair "iodine-${name}" (createIodineClientService name value)) + cfg.clients + ) // { + iodined = mkIf (cfg.server.enable) { + description = "iodine, ip over dns server daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "exec ${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${optionalString (cfg.server.passwordFile != "") "< \"${builtins.toString cfg.server.passwordFile}\""} ${cfg.server.ip} ${cfg.server.domain}"; + serviceConfig = { + # Filesystem access + ProtectSystem = "strict"; + ProtectHome = if isProtected cfg.server.passwordFile then "read-only" else "true" ; + PrivateTmp = true; + ReadWritePaths = "/dev/net/tun"; + PrivateDevices = false; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + # Caps + NoNewPrivileges = true; + # Misc. + LockPersonality = true; + RestrictRealtime = true; + PrivateMounts = true; + MemoryDenyWriteExecute = true; + }; + }; + }; + + users.users.${iodinedUser} = { + uid = config.ids.uids.iodined; + description = "Iodine daemon user"; + }; + users.groups.iodined.gid = config.ids.gids.iodined; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/iperf3.nix b/nixpkgs/nixos/modules/services/networking/iperf3.nix new file mode 100644 index 000000000000..0fe378b225d7 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/iperf3.nix @@ -0,0 +1,97 @@ +{ config, lib, pkgs, ... }: with lib; +let + cfg = config.services.iperf3; + + api = { + enable = mkEnableOption "iperf3 network throughput testing server"; + port = mkOption { + type = types.ints.u16; + default = 5201; + description = "Server port to listen on for iperf3 client requsts."; + }; + affinity = mkOption { + type = types.nullOr types.ints.unsigned; + default = null; + description = "CPU affinity for the process."; + }; + bind = mkOption { + type = types.nullOr types.str; + default = null; + description = "Bind to the specific interface associated with the given address."; + }; + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Open ports in the firewall for iperf3."; + }; + verbose = mkOption { + type = types.bool; + default = false; + description = "Give more detailed output."; + }; + forceFlush = mkOption { + type = types.bool; + default = false; + description = "Force flushing output at every interval."; + }; + debug = mkOption { + type = types.bool; + default = false; + description = "Emit debugging output."; + }; + rsaPrivateKey = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to the RSA private key (not password-protected) used to decrypt authentication credentials from the client."; + }; + authorizedUsersFile = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to the configuration file containing authorized users credentials to run iperf tests."; + }; + extraFlags = mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Extra flags to pass to iperf3(1)."; + }; + }; + + imp = { + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + + systemd.services.iperf3 = { + description = "iperf3 daemon"; + unitConfig.Documentation = "man:iperf3(1) https://iperf.fr/iperf-doc.php"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Restart = "on-failure"; + RestartSec = 2; + DynamicUser = true; + PrivateDevices = true; + CapabilityBoundingSet = ""; + NoNewPrivileges = true; + ExecStart = '' + ${pkgs.iperf3}/bin/iperf \ + --server \ + --port ${toString cfg.port} \ + ${optionalString (cfg.affinity != null) "--affinity ${toString cfg.affinity}"} \ + ${optionalString (cfg.bind != null) "--bind ${cfg.bind}"} \ + ${optionalString (cfg.rsaPrivateKey != null) "--rsa-private-key-path ${cfg.rsaPrivateKey}"} \ + ${optionalString (cfg.authorizedUsersFile != null) "--authorized-users-path ${cfg.authorizedUsersFile}"} \ + ${optionalString cfg.verbose "--verbose"} \ + ${optionalString cfg.debug "--debug"} \ + ${optionalString cfg.forceFlush "--forceflush"} \ + ${escapeShellArgs cfg.extraFlags} + ''; + }; + }; + }; +in { + options.services.iperf3 = api; + config = mkIf cfg.enable imp; +} diff --git a/nixpkgs/nixos/modules/services/networking/ircd-hybrid/builder.sh b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/builder.sh new file mode 100644 index 000000000000..38312210df25 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/builder.sh @@ -0,0 +1,31 @@ +source $stdenv/setup + +doSub() { + local src=$1 + local dst=$2 + mkdir -p $(dirname $dst) + substituteAll $src $dst +} + +subDir=/ +for i in $scripts; do + if test "$(echo $i | cut -c1-2)" = "=>"; then + subDir=$(echo $i | cut -c3-) + else + dst=$out/$subDir/$(stripHash $i | sed 's/\.in//') + doSub $i $dst + chmod +x $dst # !!! + fi +done + +subDir=/ +for i in $substFiles; do + if test "$(echo $i | cut -c1-2)" = "=>"; then + subDir=$(echo $i | cut -c3-) + else + dst=$out/$subDir/$(stripHash $i | sed 's/\.in//') + doSub $i $dst + fi +done + +mkdir -p $out/bin diff --git a/nixpkgs/nixos/modules/services/networking/ircd-hybrid/control.in b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/control.in new file mode 100644 index 000000000000..312dfaada329 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/control.in @@ -0,0 +1,26 @@ +#! @shell@ -e + +# Make sure that the environment is deterministic. +export PATH=@coreutils@/bin + +if test "$1" = "start"; then + if ! @procps@/bin/pgrep ircd; then + if @ipv6Enabled@; then + while ! @iproute@/sbin/ip addr | + @gnugrep@/bin/grep inet6 | + @gnugrep@/bin/grep global; do + sleep 1; + done; + fi; + rm -rf /home/ircd + mkdir -p /home/ircd + chown ircd: /home/ircd + cd /home/ircd + env - HOME=/homeless-shelter $extraEnv \ + @su@/bin/su ircd --shell=/bin/sh -c ' @ircdHybrid@/bin/ircd -configfile @out@/conf/ircd.conf </dev/null -logfile /home/ircd/ircd.log' 2>&1 >/var/log/ircd-hybrid.out + fi; +fi + +if test "$1" = "stop" ; then + @procps@/bin/pkill ircd; +fi; diff --git a/nixpkgs/nixos/modules/services/networking/ircd-hybrid/default.nix b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/default.nix new file mode 100644 index 000000000000..91d0bf437d69 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/default.nix @@ -0,0 +1,125 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.ircdHybrid; + + ircdService = pkgs.stdenv.mkDerivation rec { + name = "ircd-hybrid-service"; + scripts = [ "=>/bin" ./control.in ]; + substFiles = [ "=>/conf" ./ircd.conf ]; + inherit (pkgs) ircdHybrid coreutils su iproute gnugrep procps; + + ipv6Enabled = boolToString config.networking.enableIPv6; + + inherit (cfg) serverName sid description adminEmail + extraPort; + + cryptoSettings = + (optionalString (cfg.rsaKey != null) "rsa_private_key_file = \"${cfg.rsaKey}\";\n") + + (optionalString (cfg.certificate != null) "ssl_certificate_file = \"${cfg.certificate}\";\n"); + + extraListen = map (ip: "host = \""+ip+"\";\nport = 6665 .. 6669, "+extraPort+"; ") cfg.extraIPs; + + builder = ./builder.sh; + }; + +in + +{ + + ###### interface + + options = { + + services.ircdHybrid = { + + enable = mkEnableOption "IRCD"; + + serverName = mkOption { + default = "hades.arpa"; + description = " + IRCD server name. + "; + }; + + sid = mkOption { + default = "0NL"; + description = " + IRCD server unique ID in a net of servers. + "; + }; + + description = mkOption { + default = "Hybrid-7 IRC server."; + description = " + IRCD server description. + "; + }; + + rsaKey = mkOption { + default = null; + example = literalExample "/root/certificates/irc.key"; + description = " + IRCD server RSA key. + "; + }; + + certificate = mkOption { + default = null; + example = literalExample "/root/certificates/irc.pem"; + description = " + IRCD server SSL certificate. There are some limitations - read manual. + "; + }; + + adminEmail = mkOption { + default = "<bit-bucket@example.com>"; + example = "<name@domain.tld>"; + description = " + IRCD server administrator e-mail. + "; + }; + + extraIPs = mkOption { + default = []; + example = ["127.0.0.1"]; + description = " + Extra IP's to bind. + "; + }; + + extraPort = mkOption { + default = "7117"; + description = " + Extra port to avoid filtering. + "; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.services.ircdHybrid.enable { + + users.users.ircd = + { description = "IRCD owner"; + group = "ircd"; + uid = config.ids.uids.ircd; + }; + + users.groups.ircd.gid = config.ids.gids.ircd; + + systemd.services.ircd-hybrid = { + description = "IRCD Hybrid server"; + after = [ "started networking" ]; + wantedBy = [ "multi-user.target" ]; + script = "${ircdService}/bin/control start"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/ircd-hybrid/ircd.conf b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/ircd.conf new file mode 100644 index 000000000000..17ef203840af --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/ircd.conf @@ -0,0 +1,1051 @@ +/* doc/example.conf - ircd-hybrid-7 Example configuration file + * Copyright (C) 2000-2006 Hybrid Development Team + * + * Written by ejb, wcampbel, db, leeh and others + * Other example configurations can be found in the source dir under + * etc/. + * + * $Id: example.conf 639 2006-06-01 14:12:21Z michael $ + */ + +/* IMPORTANT NOTES: + * + * auth {} blocks MUST be specified in order of precedence. The first one + * that matches a user will be used. So place spoofs first, then specials, + * then general access. + * + * Shell style (#), C++ style (//) and C style comments are supported. + * + * Files may be included by either: + * .include "filename" + * .include <filename> + * + * Times/durations are written as: + * 12 hours 30 minutes 1 second + * + * Valid units of time: + * month, week, day, hour, minute, second + * + * Valid units of size: + * megabyte/mbyte/mb, kilobyte/kbyte/kb, byte + * + * Sizes and times may be singular or plural. + */ + +/* EFNET NOTE: + * + * This config file is NOT suitable for EFNet. EFNet admins should use + * example.efnet.conf + */ + +/* + * serverinfo {}: contains information about the server. (OLD M:) + */ +serverinfo { + /* + * name: the name of our server. This cannot be changed at runtime. + */ + name = "@serverName@"; + + /* + * sid: a server's unique ID. This is three characters long and must + * be in the form [0-9][A-Z0-9][A-Z0-9]. The first character must be + * a digit, followed by 2 alpha-numerical letters. + * NOTE: The letters must be capitalized. This cannot be changed at runtime. + */ + sid = "@sid@"; + + /* + * description: the description of the server. '[' and ']' may not + * be used here for compatibility with older servers. + */ + description = "@description@"; + + /* + * network info: the name and description of the network this server + * is on. Shown in the 005 reply and used with serverhiding. + */ + network_name = "JustIRCNetwork"; + network_desc = "This is My Network"; + + /* + * hub: allow this server to act as a hub and have multiple servers + * connected to it. This may not be changed if there are active + * LazyLink servers. + */ + hub = no; + + /* + * vhost: the IP to bind to when we connect outward to ipv4 servers. + * This should be an ipv4 IP only, or "* for INADDR_ANY. + */ + #vhost = "192.169.0.1"; + + /* + * vhost6: the IP to bind to when we connect outward to ipv6 servers. + * This should be an ipv6 IP only, or "* for INADDR_ANY. + */ + #vhost6 = "3ffe:80e8:546::2"; + + /* max_clients: the maximum number of clients allowed to connect */ + max_clients = 512; + + /* + * rsa key: the path to the file containing our rsa key for cryptlink. + * + * Example command to store a 2048 bit RSA keypair in + * rsa.key, and the public key in rsa.pub: + * + * openssl genrsa -out rsa.key 2048 + * openssl rsa -in rsa.key -pubout -out rsa.pub + * chown <ircd-user>.<ircd.group> rsa.key rsa.pub + * chmod 0600 rsa.key + * chmod 0644 rsa.pub + */ + #rsa_private_key_file = "/usr/local/ircd/etc/rsa.key"; + + /* + * ssl certificate: the path to the file containing our ssl certificate + * for encrypted client connection. + * + * This assumes your private RSA key is stored in rsa.key. You + * MUST have an RSA key in order to generate the certificate + * + * openssl req -new -days 365 -x509 -key rsa.key -out cert.pem + * + * See http://www.openssl.org/docs/HOWTO/certificates.txt + * + * Please use the following values when generating the cert + * + * Organization Name: Network Name + * Organization Unit Name: changme.someirc.net + * Common Name: irc.someirc.net + * E-mail: you@domain.com + */ + #ssl_certificate_file = "/usr/local/ircd/etc/cert.pem"; + + @cryptoSettings@ +}; + +/* + * admin {}: contains admin information about the server. (OLD A:) + */ +admin { + name = "Anonymous Hero"; + description = "Main Server Administrator"; + email = "@adminEmail@"; +}; + +/* + * log {}: contains information about logfiles. + */ +log { + /* Do you want to enable logging to ircd.log? */ + use_logging = yes; + + /* + * logfiles: the logfiles to use for user connects, /oper uses, + * and failed /oper. These files must exist for logging to be used. + */ + fname_userlog = "/home/ircd/logs/userlog"; + fname_operlog = "/home/ircd/logs/operlog"; + fname_killlog = "/home/ircd/logs/kill"; + fname_klinelog = "/home/ircd/logs/kline"; + fname_glinelog = "/home/ircd/logs/gline"; + + /* + * log_level: the amount of detail to log in ircd.log. The + * higher, the more information is logged. May be changed + * once the server is running via /quote SET LOG. Either: + * L_CRIT, L_ERROR, L_WARN, L_NOTICE, L_TRACE, L_INFO or L_DEBUG + */ + log_level = L_INFO; +}; + +/* + * class {}: contains information about classes for users (OLD Y:) + */ +class { + /* name: the name of the class. classes are text now */ + name = "users"; + + /* + * ping_time: how often a client must reply to a PING from the + * server before they are dropped. + */ + ping_time = 90 seconds; + + /* + * number_per_ip: how many local users are allowed to connect + * from one IP (optional) + */ + number_per_ip = 10; + + /* + * max_local: how many local users are allowed to connect + * from one ident@host (optional) + */ + max_local = 50; + + /* + * max_global: network-wide limit of users per ident@host (optional) + */ + max_global = 50; + + /* + * max_number: the maximum number of users allowed in this class (optional) + */ + max_number = 10000; + + /* + * the following lines are optional and allow you to define + * how many users can connect from one /NN subnet + */ + /*cidr_bitlen_ipv4 = 24; + *cidr_bitlen_ipv6 = 120; + *number_per_cidr = 16;*/ + + /* + * sendq: the amount of data allowed in a clients queue before + * they are dropped. + */ + sendq = 100 kbytes; +}; + +class { + name = "opers"; + ping_time = 90 seconds; + number_per_ip = 10; + max_number = 100; + sendq = 100kbytes; +}; + +class { + name = "server"; + ping_time = 90 seconds; + + /* + * ping_warning: how fast a server must reply to a PING before + * a warning to opers is generated. + */ + ping_warning = 15 seconds; + + /* + * connectfreq: only used in server classes. Specifies the delay + * between autoconnecting to servers. + */ + connectfreq = 5 minutes; + + /* max number: the amount of servers to autoconnect to */ + max_number = 1; + + /* sendq: servers need a higher sendq as they send more data */ + sendq = 2 megabytes; +}; + +/* + * listen {}: contains information about the ports ircd listens on (OLD P:) + */ +listen { + /* + * port: the specific port to listen on. If no host is specified + * before, it will listen on all available IPs. + * + * Ports are separated via a comma, a range may be specified using ".." + */ + + /* port: listen on all available IPs, ports 6665 to 6669 */ + port = 6665 .. 6669; + + /* + * Listen on 192.168.0.1/6697 with ssl enabled and hidden from STATS P + * unless you are an administrator. + * + * NOTE: The "flags" directive has to come before "port". Always! + */ + #flags = hidden, ssl; + #host = "192.168.0.1"; + #port = 6697; + + /* + * host: set a specific IP/host the ports after the line will listen + * on. This may be ipv4 or ipv6. + */ + #host = "1.2.3.4"; + #port = 7000, 7001; + + #host = "3ffe:1234:a:b:c::d"; + #port = 7002; + + @extraListen@ +}; + +auth { + user = "*@*"; + class = "users"; + #flags = need_ident; +}; + +/* + * operator {}: defines ircd operators. (OLD O:) + * + * ircd-hybrid no longer supports local operators, privileges are + * controlled via flags. + */ +operator { + /* name: the name of the oper */ + /* NOTE: operator "opername"{} is also supported */ + name = "god"; + + /* + * user: the user@host required for this operator. CIDR is not + * supported. Multiple user="" lines are supported. + */ + user = "*god@*"; + user = "*@127.0.0.1"; + + /* + * password: the password required to oper. By default this will + * need to be encrypted using 'mkpasswd'. MD5 is supported. + */ + password = "iamoperator"; + + /* + * encrypted: controls whether the oper password above has been + * encrypted. (OLD CRYPT_OPER_PASSWORD now optional per operator) + */ + encrypted = no; + + /* + * rsa_public_key_file: the public key for this oper when using Challenge. + * A password should not be defined when this is used, see + * doc/challenge.txt for more information. + */ +# rsa_public_key_file = "/usr/local/ircd/etc/oper.pub"; + + /* class: the class the oper joins when they successfully /oper */ + class = "opers"; + + /* + * umodes: default usermodes opers get when they /oper. If defined, + * it will override oper_umodes settings in general {}. + * Available usermodes: + * + * +b - bots - See bot and drone flooding notices + * +c - cconn - Client connection/quit notices + * +D - deaf - Don't receive channel messages + * +d - debug - See debugging notices + * +f - full - See I: line full notices + * +G - softcallerid - Server Side Ignore for users not on your channels + * +g - callerid - Server Side Ignore (for privmsgs etc) + * +i - invisible - Not shown in NAMES or WHO unless you share a + * a channel + * +k - skill - See server generated KILL messages + * +l - locops - See LOCOPS messages + * +n - nchange - See client nick changes + * +r - rej - See rejected client notices + * +s - servnotice - See general server notices + * +u - unauth - See unauthorized client notices + * +w - wallop - See server generated WALLOPS + * +x - external - See remote server connection and split notices + * +y - spy - See LINKS, STATS, TRACE notices etc. + * +z - operwall - See oper generated WALLOPS + */ +# umodes = locops, servnotice, operwall, wallop; + + /* + * privileges: controls the activities and commands an oper is + * allowed to do on the server. All options default to no. + * Available options: + * + * global_kill: allows remote users to be /KILL'd (OLD 'O' flag) + * remote: allows remote SQUIT and CONNECT (OLD 'R' flag) + * remoteban: allows remote KLINE/UNKLINE + * kline: allows KILL, KLINE and DLINE (OLD 'K' flag) + * unkline: allows UNKLINE and UNDLINE (OLD 'U' flag) + * gline: allows GLINE (OLD 'G' flag) + * xline: allows XLINE (OLD 'X' flag) + * operwall: allows OPERWALL + * nick_changes: allows oper to see nickchanges (OLD 'N' flag) + * via usermode +n + * rehash: allows oper to REHASH config (OLD 'H' flag) + * die: allows DIE and RESTART (OLD 'D' flag) + * admin: gives admin privileges. admins + * may (un)load modules and see the + * real IPs of servers. + * hidden_admin: same as 'admin', but noone can recognize you as + * being an admin + * hidden_oper: not shown in /stats p (except for other operators) + */ + /* You can either use + * die = yes; + * rehash = yes; + * + * or in a flags statement i.e. + * flags = die, rehash; + * + * You can also negate a flag with ~ i.e. + * flags = ~remote; + * + */ + flags = global_kill, remote, kline, unkline, xline, + die, rehash, nick_changes, admin, operwall; +}; + +/* + * shared {}: users that are allowed to remote kline (OLD U:) + * + * NOTE: This can be effectively used for remote klines. + * Please note that there is no password authentication + * for users setting remote klines. You must also be + * /oper'd in order to issue a remote kline. + */ +shared { + /* + * name: the server the user must be on to set klines. If this is not + * specified, the user will be allowed to kline from all servers. + */ + name = "irc2.some.server"; + + /* + * user: the user@host mask that is allowed to set klines. If this is + * not specified, all users on the server above will be allowed to set + * a remote kline. + */ + user = "oper@my.host.is.spoofed"; + + /* + * type: list of what to share, options are as follows: + * kline - allow oper/server to kline + * tkline - allow temporary klines + * unkline - allow oper/server to unkline + * xline - allow oper/server to xline + * txline - allow temporary xlines + * unxline - allow oper/server to unxline + * resv - allow oper/server to resv + * tresv - allow temporary resvs + * unresv - allow oper/server to unresv + * locops - allow oper/server to locops - only used for servers that cluster + * all - allow oper/server to do all of the above (default) + */ + type = kline, unkline, resv; +}; + +/* + * kill {}: users that are not allowed to connect (OLD K:) + * Oper issued klines will be added to the specified kline config + */ +kill { + user = "bad@*.hacked.edu"; + reason = "Obviously hacked account"; +}; + +kill { + user = "^O[[:alpha:]]?[[:digit:]]+(x\.o|\.xo)$@^[[:alnum:]]{4}\.evilnet.org$"; + type = regex; +}; + +/* + * deny {}: IPs that are not allowed to connect (before DNS/ident lookup) + * Oper issued dlines will be added to the specified dline config + */ +deny { + ip = "10.0.1.0/24"; + reason = "Reconnecting vhosted bots"; +}; + +/* + * exempt {}: IPs that are exempt from deny {} and Dlines. (OLD d:) + */ +exempt { + ip = "192.168.0.0/16"; +}; + +/* + * resv {}: nicks and channels users may not use/join (OLD Q:) + */ +resv { + /* reason: the reason for the proceeding resv's */ + reason = "There are no services on this network"; + + /* resv: the nicks and channels users may not join/use */ + nick = "nickserv"; + nick = "chanserv"; + channel = "#services"; + + /* resv: wildcard masks are also supported in nicks only */ + reason = "Clone bots"; + nick = "clone*"; +}; + +/* + * gecos {}: The X: replacement, used for banning users based on + * their "realname". + */ +gecos { + name = "*sex*"; + reason = "Possible spambot"; +}; + +gecos { + name = "sub7server"; + reason = "Trojan drone"; +}; + +gecos { + name = "*http*"; + reason = "Spambot"; +}; + +gecos { + name = "^\[J[0o]hn Do[3e]\]-[0-9]{2,5}$"; + type = regex; +}; + +/* + * channel {}: The channel block contains options pertaining to channels + */ +channel { + /* + * disable_fake_channels: this option, if set to 'yes', will + * disallow clients to create or join channels that have one + * of the following ASCII characters in their name: + * + * 2 | bold + * 3 | mirc color + * 15 | plain text + * 22 | reverse + * 31 | underline + * 160 | non-breaking space + */ + disable_fake_channels = yes; + + /* + * restrict_channels: reverse channel RESVs logic, only reserved + * channels are allowed + */ + restrict_channels = no; + + /* + * disable_local_channels: prevent users from joining &channels. + */ + disable_local_channels = no; + + /* + * use_invex: Enable/disable channel mode +I, a n!u@h list of masks + * that can join a +i channel without an invite. + */ + use_invex = yes; + + /* + * use_except: Enable/disable channel mode +e, a n!u@h list of masks + * that can join a channel through a ban (+b). + */ + use_except = yes; + + /* + * use_knock: Allows users to request an invite to a channel that + * is locked somehow (+ikl). If the channel is +p or you are banned + * the knock will not be sent. + */ + use_knock = yes; + + /* + * knock_delay: The amount of time a user must wait between issuing + * the knock command. + */ + knock_delay = 1 minutes; + + /* + * knock_delay_channel: How often a knock to any specific channel + * is permitted, regardless of the user sending the knock. + */ + knock_delay_channel = 1 minute; + + /* + * burst_topicwho: enable sending of who set topic on topicburst + * default is yes + */ + burst_topicwho = yes; + + /* + * max_chans_per_user: The maximum number of channels a user can + * join/be on. + */ + max_chans_per_user = 25; + + /* quiet_on_ban: stop banned people talking in channels. */ + quiet_on_ban = yes; + + /* max_bans: maximum number of +b/e/I modes in a channel */ + max_bans = 1000; + + /* + * how many joins in how many seconds constitute a flood, use 0 to + * disable. +b opers will be notified (changeable via /set) + */ + join_flood_count = 100; + join_flood_time = 10 seconds; + + /* + * splitcode: The ircd will now check splitmode every few seconds. + * + * Either split users or split servers can activate splitmode, but + * both conditions must be met for the ircd to deactivate splitmode. + * + * You may force splitmode to be permanent by /quote set splitmode on + */ + + /* + * default_split_user_count: when the usercount is lower than this level, + * consider ourselves split. This must be set for automatic splitmode. + */ + default_split_user_count = 0; + + /* + * default_split_server_count: when the servercount is lower than this, + * consider ourselves split. This must be set for automatic splitmode. + */ + default_split_server_count = 0; + + /* split no create: disallow users creating channels on split. */ + no_create_on_split = yes; + + /* split: no join: disallow users joining channels at all on a split */ + no_join_on_split = no; +}; + +/* + * serverhide {}: The serverhide block contains the options regarding + * serverhiding + */ +serverhide { + /* + * flatten_links: this option will show all servers in /links appear + * that they are linked to this current server + */ + flatten_links = no; + + /* + * links_delay: how often to update the links file when it is + * flattened. + */ + links_delay = 5 minutes; + + /* + * hidden: hide this server from a /links output on servers that + * support it. This allows hub servers to be hidden etc. + */ + hidden = no; + + /* + * disable_hidden: prevent servers hiding themselves from a + * /links output. + */ + disable_hidden = no; + + /* + * hide_servers: hide remote servernames everywhere and instead use + * hidden_name and network_desc. + */ + hide_servers = no; + + /* + * Use this as the servername users see if hide_servers = yes. + */ + hidden_name = "*.hidden.com"; + + /* + * hide_server_ips: If this is disabled, opers will be unable to see servers + * ips and will be shown a masked ip, admins will be shown the real ip. + * + * If this is enabled, nobody can see a servers ip. *This is a kludge*, it + * has the side effect of hiding the ips everywhere, including logfiles. + * + * We recommend you leave this disabled, and just take care with who you + * give admin=yes; to. + */ + hide_server_ips = no; +}; + +/* + * general {}: The general block contains many of the options that were once + * compiled in options in config.h. The general block is read at start time. + */ +general { + /* + * gline_min_cidr: the minimum required length of a CIDR bitmask + * for IPv4 based glines + */ + gline_min_cidr = 16; + + /* + * gline_min_cidr6: the minimum required length of a CIDR bitmask + * for IPv6 based glines + */ + gline_min_cidr6 = 48; + + /* + * Whether to automatically set mode +i on connecting users. + */ + invisible_on_connect = yes; + + /* + * If you don't explicitly specify burst_away in your connect blocks, then + * they will default to the burst_away value below. + */ + burst_away = no; + + /* + * Show "actually using host <ip>" on /whois when possible. + */ + use_whois_actually = yes; + + /* + * Max time from the nickname change that still causes KILL + * automatically to switch for the current nick of that user. (seconds) + */ + kill_chase_time_limit = 90; + + /* + * If hide_spoof_ips is disabled, opers will be allowed to see the real IP of spoofed + * users in /trace etc. If this is defined they will be shown a masked IP. + */ + hide_spoof_ips = yes; + + /* + * Ignore bogus timestamps from other servers. Yes, this will desync + * the network, but it will allow chanops to resync with a valid non TS 0 + * + * This should be enabled network wide, or not at all. + */ + ignore_bogus_ts = no; + + /* + * disable_auth: completely disable ident lookups; if you enable this, + * be careful of what you set need_ident to in your auth {} blocks + */ + disable_auth = no; + + /* disable_remote_commands: disable users doing commands on remote servers */ + disable_remote_commands = no; + + /* + * tkline_expire_notices: enables or disables temporary kline/xline + * expire notices. + */ + tkline_expire_notices = no; + + /* + * default_floodcount: the default value of floodcount that is configurable + * via /quote set floodcount. This is the amount of lines a user + * may send to any other user/channel in one second. + */ + default_floodcount = 10; + + /* + * failed_oper_notice: send a notice to all opers on the server when + * someone tries to OPER and uses the wrong password, host or ident. + */ + failed_oper_notice = yes; + + /* + * dots_in_ident: the amount of '.' characters permitted in an ident + * reply before the user is rejected. + */ + dots_in_ident = 2; + + /* + * dot_in_ip6_addr: ircd-hybrid-6.0 and earlier will disallow hosts + * without a '.' in them. This will add one to the end. Only needed + * for older servers. + */ + dot_in_ip6_addr = no; + + /* + * min_nonwildcard: the minimum non wildcard characters in k/d/g lines + * placed via the server. klines hand placed are exempt from limits. + * wildcard chars: '.' ':' '*' '?' '@' '!' '#' + */ + min_nonwildcard = 4; + + /* + * min_nonwildcard_simple: the minimum non wildcard characters in + * gecos bans. wildcard chars: '*' '?' '#' + */ + min_nonwildcard_simple = 3; + + /* max_accept: maximum allowed /accept's for +g usermode */ + max_accept = 20; + + /* anti_nick_flood: enable the nickflood control code */ + anti_nick_flood = yes; + + /* nick flood: the nick changes allowed in the specified period */ + max_nick_time = 20 seconds; + max_nick_changes = 5; + + /* + * anti_spam_exit_message_time: the minimum time a user must be connected + * before custom quit messages are allowed. + */ + anti_spam_exit_message_time = 5 minutes; + + /* + * ts delta: the time delta allowed between server clocks before + * a warning is given, or before the link is dropped. all servers + * should run ntpdate/rdate to keep clocks in sync + */ + ts_warn_delta = 30 seconds; + ts_max_delta = 5 minutes; + + /* + * kline_with_reason: show the user the reason why they are k/d/glined + * on exit. May give away who set k/dline when set via tcm. + */ + kline_with_reason = yes; + + /* + * kline_reason: show this message to users on channel + * instead of the oper reason. + */ + kline_reason = "Connection closed"; + + /* + * reject_hold_time: wait this amount of time before disconnecting + * a rejected client. Use 0 to disable. + */ + reject_hold_time = 0; + + /* + * warn_no_nline: warn opers about servers that try to connect but + * we don't have a connect {} block for. Twits with misconfigured + * servers can get really annoying with this enabled. + */ + warn_no_nline = yes; + + /* + * stats_e_disabled: set this to 'yes' to disable "STATS e" for both + * operators and administrators. Doing so is a good idea in case + * there are any exempted (exempt{}) server IPs you don't want to + * see leaked. + */ + stats_e_disabled = no; + + /* stats_o_oper only: make stats o (opers) oper only */ + stats_o_oper_only = yes; + + /* stats_P_oper_only: make stats P (ports) oper only */ + stats_P_oper_only = yes; + + /* + * stats i oper only: make stats i (auth {}) oper only. set to: + * yes: show users no auth blocks, made oper only. + * masked: show users first matching auth block + * no: show users all auth blocks. + */ + stats_i_oper_only = yes; + + /* + * stats_k_oper_only: make stats k/K (klines) oper only. set to: + * yes: show users no auth blocks, made oper only + * masked: show users first matching auth block + * no: show users all auth blocks. + */ + stats_k_oper_only = yes; + + /* + * caller_id_wait: time between notifying a +g user that somebody + * is messaging them. + */ + caller_id_wait = 1 minute; + + /* + * opers_bypass_callerid: allows operators to bypass +g and message + * anyone who has it set (useful if you use services). + */ + opers_bypass_callerid = no; + + /* + * pace_wait_simple: time between use of less intensive commands + * (ADMIN, HELP, (L)USERS, VERSION, remote WHOIS) + */ + pace_wait_simple = 1 second; + + /* + * pace_wait: time between more intensive commands + * (INFO, LINKS, LIST, MAP, MOTD, STATS, WHO, wildcard WHOIS, WHOWAS) + */ + pace_wait = 10 seconds; + + /* + * short_motd: send clients a notice telling them to read the motd + * instead of forcing a motd to clients who may simply ignore it. + */ + short_motd = no; + + /* + * ping_cookie: require clients to respond exactly to a ping command, + * can help block certain types of drones and FTP PASV mode spoofing. + */ + ping_cookie = no; + + /* no_oper_flood: increase flood limits for opers. */ + no_oper_flood = yes; + + /* + * true_no_oper_flood: completely eliminate flood limits for opers + * and for clients with can_flood = yes in their auth {} blocks + */ + true_no_oper_flood = yes; + + /* oper_pass_resv: allow opers to over-ride RESVs on nicks/channels */ + oper_pass_resv = yes; + + /* + * idletime: the maximum amount of time a user may idle before + * they are disconnected + */ + idletime = 0; + + /* REMOVE ME. The following line checks you've been reading. */ + #havent_read_conf = 1; + + /* + * max_targets: the maximum amount of targets in a single + * PRIVMSG/NOTICE. Set to 999 NOT 0 for unlimited. + */ + max_targets = 4; + + /* + * client_flood: maximum amount of data in a clients queue before + * they are dropped for flooding. + */ + client_flood = 2560 bytes; + + /* + * message_locale: the default message locale + * Use "standard" for the compiled in defaults. + * To install the translated messages, go into messages/ in the + * source directory and run `make install'. + */ + message_locale = "standard"; + + /* + * usermodes configurable: a list of usermodes for the options below + * + * +b - bots - See bot and drone flooding notices + * +c - cconn - Client connection/quit notices + * +D - deaf - Don't receive channel messages + * +d - debug - See debugging notices + * +f - full - See I: line full notices + * +G - softcallerid - Server Side Ignore for users not on your channels + * +g - callerid - Server Side Ignore (for privmsgs etc) + * +i - invisible - Not shown in NAMES or WHO unless you share a + * a channel + * +k - skill - See server generated KILL messages + * +l - locops - See LOCOPS messages + * +n - nchange - See client nick changes + * +r - rej - See rejected client notices + * +s - servnotice - See general server notices + * +u - unauth - See unauthorized client notices + * +w - wallop - See server generated WALLOPS + * +x - external - See remote server connection and split notices + * +y - spy - See LINKS, STATS, TRACE notices etc. + * +z - operwall - See oper generated WALLOPS + */ + + /* oper_only_umodes: usermodes only opers may set */ + oper_only_umodes = bots, cconn, debug, full, skill, nchange, + rej, spy, external, operwall, locops, unauth; + + /* oper_umodes: default usermodes opers get when they /oper */ + oper_umodes = bots, locops, servnotice, operwall, wallop; + + /* + * servlink_path: path to 'servlink' program used by ircd to handle + * encrypted/compressed server <-> server links. + * + * only define if servlink is not in same directory as ircd itself. + */ + #servlink_path = "/usr/local/ircd/bin/servlink"; + + /* + * default_cipher_preference: default cipher to use for cryptlink when none is + * specified in connect block. + */ + #default_cipher_preference = "BF/168"; + + /* + * use_egd: if your system does not have *random devices yet you + * want to use OpenSSL and encrypted links, enable this. Beware - + * EGD is *very* CPU intensive when gathering data for its pool + */ +# use_egd = yes; + + /* + * egdpool_path: path to EGD pool. Not necessary for OpenSSL >= 0.9.7 + * which automatically finds the path. + */ +# egdpool_path = "/run/egd-pool"; + + + /* + * compression_level: level of compression for compressed links between + * servers. + * + * values are between: 1 (least compression, fastest) + * and: 9 (most compression, slowest). + */ +# compression_level = 6; + + /* + * throttle_time: the minimum amount of time between connections from + * the same ip. exempt {} blocks are excluded from this throttling. + * Offers protection against flooders who reconnect quickly. + * Set to 0 to disable. + */ + throttle_time = 10; +}; + +glines { + /* enable: enable glines, network wide temp klines */ + enable = yes; + + /* + * duration: the amount of time a gline will remain on your + * server before expiring + */ + duration = 1 day; + + /* + * logging: which types of rules you want to log when triggered + * (choose reject or block) + */ + logging = reject, block; + + /* + * NOTE: gline ACLs can cause a desync of glines throughout the + * network, meaning some servers may have a gline triggered, and + * others may not. Also, you only need insert rules for glines + * that you want to block and/or reject. If you want to accept and + * propagate the gline, do NOT put a rule for it. + */ + + /* user@host for rule to apply to */ + user = "god@I.still.hate.packets"; + /* server for rule to apply to */ + name = "hades.arpa"; + + /* + * action: action to take when a matching gline is found. options are: + * reject - do not apply the gline locally + * block - do not propagate the gline + */ + action = reject, block; + + user = "god@*"; + name = "*"; + action = block; +}; + diff --git a/nixpkgs/nixos/modules/services/networking/iwd.nix b/nixpkgs/nixos/modules/services/networking/iwd.nix new file mode 100644 index 000000000000..6be67a8b96f4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/iwd.nix @@ -0,0 +1,29 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.networking.wireless.iwd; +in { + options.networking.wireless.iwd.enable = mkEnableOption "iwd"; + + config = mkIf cfg.enable { + assertions = [{ + assertion = !config.networking.wireless.enable; + message = '' + Only one wireless daemon is allowed at the time: networking.wireless.enable and networking.wireless.iwd.enable are mutually exclusive. + ''; + }]; + + # for iwctl + environment.systemPackages = [ pkgs.iwd ]; + + services.dbus.packages = [ pkgs.iwd ]; + + systemd.packages = [ pkgs.iwd ]; + + systemd.services.iwd.wantedBy = [ "multi-user.target" ]; + }; + + meta.maintainers = with lib.maintainers; [ mic92 dtzWill ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/default.nix b/nixpkgs/nixos/modules/services/networking/keepalived/default.nix new file mode 100644 index 000000000000..c9ac2ee25990 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/keepalived/default.nix @@ -0,0 +1,303 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.keepalived; + + keepalivedConf = pkgs.writeText "keepalived.conf" '' + global_defs { + ${optionalString cfg.enableScriptSecurity "enable_script_security"} + ${snmpGlobalDefs} + ${cfg.extraGlobalDefs} + } + + ${vrrpScriptStr} + ${vrrpInstancesStr} + ${cfg.extraConfig} + ''; + + snmpGlobalDefs = with cfg.snmp; optionalString enable ( + optionalString (socket != null) "snmp_socket ${socket}\n" + + optionalString enableKeepalived "enable_snmp_keepalived\n" + + optionalString enableChecker "enable_snmp_checker\n" + + optionalString enableRfc "enable_snmp_rfc\n" + + optionalString enableRfcV2 "enable_snmp_rfcv2\n" + + optionalString enableRfcV3 "enable_snmp_rfcv3\n" + + optionalString enableTraps "enable_traps" + ); + + vrrpScriptStr = concatStringsSep "\n" (map (s: + '' + vrrp_script ${s.name} { + script "${s.script}" + interval ${toString s.interval} + fall ${toString s.fall} + rise ${toString s.rise} + timeout ${toString s.timeout} + weight ${toString s.weight} + user ${s.user} ${optionalString (s.group != null) s.group} + + ${s.extraConfig} + } + '' + ) vrrpScripts); + + vrrpInstancesStr = concatStringsSep "\n" (map (i: + '' + vrrp_instance ${i.name} { + interface ${i.interface} + state ${i.state} + virtual_router_id ${toString i.virtualRouterId} + priority ${toString i.priority} + ${optionalString i.noPreempt "nopreempt"} + + ${optionalString i.useVmac ( + "use_vmac" + optionalString (i.vmacInterface != null) " ${i.vmacInterface}" + )} + ${optionalString i.vmacXmitBase "vmac_xmit_base"} + + ${optionalString (i.unicastSrcIp != null) "unicast_src_ip ${i.unicastSrcIp}"} + unicast_peer { + ${concatStringsSep "\n" i.unicastPeers} + } + + virtual_ipaddress { + ${concatMapStringsSep "\n" virtualIpLine i.virtualIps} + } + + ${optionalString (builtins.length i.trackScripts > 0) '' + track_script { + ${concatStringsSep "\n" i.trackScripts} + } + ''} + + ${optionalString (builtins.length i.trackInterfaces > 0) '' + track_interface { + ${concatStringsSep "\n" i.trackInterfaces} + } + ''} + + ${i.extraConfig} + } + '' + ) vrrpInstances); + + virtualIpLine = (ip: + ip.addr + + optionalString (notNullOrEmpty ip.brd) " brd ${ip.brd}" + + optionalString (notNullOrEmpty ip.dev) " dev ${ip.dev}" + + optionalString (notNullOrEmpty ip.scope) " scope ${ip.scope}" + + optionalString (notNullOrEmpty ip.label) " label ${ip.label}" + ); + + notNullOrEmpty = s: !(s == null || s == ""); + + vrrpScripts = mapAttrsToList (name: config: + { + inherit name; + } // config + ) cfg.vrrpScripts; + + vrrpInstances = mapAttrsToList (iName: iConfig: + { + name = iName; + } // iConfig + ) cfg.vrrpInstances; + + vrrpInstanceAssertions = i: [ + { assertion = i.interface != ""; + message = "services.keepalived.vrrpInstances.${i.name}.interface option cannot be empty."; + } + { assertion = i.virtualRouterId >= 0 && i.virtualRouterId <= 255; + message = "services.keepalived.vrrpInstances.${i.name}.virtualRouterId must be an integer between 0..255."; + } + { assertion = i.priority >= 0 && i.priority <= 255; + message = "services.keepalived.vrrpInstances.${i.name}.priority must be an integer between 0..255."; + } + { assertion = i.vmacInterface == null || i.useVmac; + message = "services.keepalived.vrrpInstances.${i.name}.vmacInterface has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set."; + } + { assertion = !i.vmacXmitBase || i.useVmac; + message = "services.keepalived.vrrpInstances.${i.name}.vmacXmitBase has no effect when services.keepalived.vrrpInstances.${i.name}.useVmac is not set."; + } + ] ++ flatten (map (virtualIpAssertions i.name) i.virtualIps) + ++ flatten (map (vrrpScriptAssertion i.name) i.trackScripts); + + virtualIpAssertions = vrrpName: ip: [ + { assertion = ip.addr != ""; + message = "The 'addr' option for an services.keepalived.vrrpInstances.${vrrpName}.virtualIps entry cannot be empty."; + } + ]; + + vrrpScriptAssertion = vrrpName: scriptName: { + assertion = builtins.hasAttr scriptName cfg.vrrpScripts; + message = "services.keepalived.vrrpInstances.${vrrpName} trackscript ${scriptName} is not defined in services.keepalived.vrrpScripts."; + }; + + pidFile = "/run/keepalived.pid"; + +in +{ + + options = { + services.keepalived = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable Keepalived. + ''; + }; + + enableScriptSecurity = mkOption { + type = types.bool; + default = false; + description = '' + Don't run scripts configured to be run as root if any part of the path is writable by a non-root user. + ''; + }; + + snmp = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the builtin AgentX subagent. + ''; + }; + + socket = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Socket to use for connecting to SNMP master agent. If this value is + set to null, keepalived's default will be used, which is + unix:/var/agentx/master, unless using a network namespace, when the + default is udp:localhost:705. + ''; + }; + + enableKeepalived = mkOption { + type = types.bool; + default = false; + description = '' + Enable SNMP handling of vrrp element of KEEPALIVED MIB. + ''; + }; + + enableChecker = mkOption { + type = types.bool; + default = false; + description = '' + Enable SNMP handling of checker element of KEEPALIVED MIB. + ''; + }; + + enableRfc = mkOption { + type = types.bool; + default = false; + description = '' + Enable SNMP handling of RFC2787 and RFC6527 VRRP MIBs. + ''; + }; + + enableRfcV2 = mkOption { + type = types.bool; + default = false; + description = '' + Enable SNMP handling of RFC2787 VRRP MIB. + ''; + }; + + enableRfcV3 = mkOption { + type = types.bool; + default = false; + description = '' + Enable SNMP handling of RFC6527 VRRP MIB. + ''; + }; + + enableTraps = mkOption { + type = types.bool; + default = false; + description = '' + Enable SNMP traps. + ''; + }; + + }; + + vrrpScripts = mkOption { + type = types.attrsOf (types.submodule (import ./vrrp-script-options.nix { + inherit lib; + })); + default = {}; + description = "Declarative vrrp script config"; + }; + + vrrpInstances = mkOption { + type = types.attrsOf (types.submodule (import ./vrrp-instance-options.nix { + inherit lib; + })); + default = {}; + description = "Declarative vhost config"; + }; + + extraGlobalDefs = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to be added verbatim to the 'global_defs' block of the + configuration file + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to be added verbatim to the configuration file. + ''; + }; + + }; + }; + + config = mkIf cfg.enable { + + assertions = flatten (map vrrpInstanceAssertions vrrpInstances); + + systemd.timers.keepalived-boot-delay = { + description = "Keepalive Daemon delay to avoid instant transition to MASTER state"; + after = [ "network.target" "network-online.target" "syslog.target" ]; + requires = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + timerConfig = { + OnActiveSec = "5s"; + Unit = "keepalived.service"; + }; + }; + + systemd.services.keepalived = { + description = "Keepalive Daemon (LVS and VRRP)"; + after = [ "network.target" "network-online.target" "syslog.target" ]; + wants = [ "network-online.target" ]; + serviceConfig = { + Type = "forking"; + PIDFile = pidFile; + KillMode = "process"; + ExecStart = "${pkgs.keepalived}/sbin/keepalived" + + " -f ${keepalivedConf}" + + " -p ${pidFile}" + + optionalString cfg.snmp.enable " --snmp"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Restart = "always"; + RestartSec = "1s"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/virtual-ip-options.nix b/nixpkgs/nixos/modules/services/networking/keepalived/virtual-ip-options.nix new file mode 100644 index 000000000000..1b8889b1b472 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/keepalived/virtual-ip-options.nix @@ -0,0 +1,50 @@ +{ lib } : + +with lib; +{ + options = { + + addr = mkOption { + type = types.str; + description = '' + IP address, optionally with a netmask: IPADDR[/MASK] + ''; + }; + + brd = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The broadcast address on the interface. + ''; + }; + + dev = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The name of the device to add the address to. + ''; + }; + + scope = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The scope of the area where this address is valid. + ''; + }; + + label = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Each address may be tagged with a label string. In order to preserve + compatibility with Linux-2.0 net aliases, this string must coincide with + the name of the device or must be prefixed with the device name followed + by colon. + ''; + }; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix new file mode 100644 index 000000000000..85b9bc337726 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-instance-options.nix @@ -0,0 +1,135 @@ +{ lib } : + +with lib; +{ + options = { + + interface = mkOption { + type = types.str; + description = '' + Interface for inside_network, bound by vrrp. + ''; + }; + + state = mkOption { + type = types.enum [ "MASTER" "BACKUP" ]; + default = "BACKUP"; + description = '' + Initial state. As soon as the other machine(s) come up, an election will + be held and the machine with the highest "priority" will become MASTER. + So the entry here doesn't matter a whole lot. + ''; + }; + + virtualRouterId = mkOption { + type = types.int; + description = '' + Arbitrary unique number 0..255. Used to differentiate multiple instances + of vrrpd running on the same NIC (and hence same socket). + ''; + }; + + priority = mkOption { + type = types.int; + default = 100; + description = '' + For electing MASTER, highest priority wins. To be MASTER, make 50 more + than other machines. + ''; + }; + + noPreempt = mkOption { + type = types.bool; + default = false; + description = '' + VRRP will normally preempt a lower priority machine when a higher + priority machine comes online. "nopreempt" allows the lower priority + machine to maintain the master role, even when a higher priority machine + comes back online. NOTE: For this to work, the initial state of this + entry must be BACKUP. + ''; + }; + + useVmac = mkOption { + type = types.bool; + default = false; + description = '' + Use VRRP Virtual MAC. + ''; + }; + + vmacInterface = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Name of the vmac interface to use. keepalived will come up with a name + if you don't specify one. + ''; + }; + + vmacXmitBase = mkOption { + type = types.bool; + default = false; + description = '' + Send/Recv VRRP messages from base interface instead of VMAC interface. + ''; + }; + + unicastSrcIp = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Default IP for binding vrrpd is the primary IP on interface. If you + want to hide location of vrrpd, use this IP as src_addr for unicast + vrrp packets. + ''; + }; + + unicastPeers = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Do not send VRRP adverts over VRRP multicast group. Instead it sends + adverts to the following list of ip addresses using unicast design + fashion. It can be cool to use VRRP FSM and features in a networking + environment where multicast is not supported! IP Addresses specified can + IPv4 as well as IPv6. + ''; + }; + + virtualIps = mkOption { + type = types.listOf (types.submodule (import ./virtual-ip-options.nix { + inherit lib; + })); + default = []; + example = literalExample '' + TODO: Example + ''; + description = "Declarative vhost config"; + }; + + trackScripts = mkOption { + type = types.listOf types.str; + default = []; + example = [ "chk_cmd1" "chk_cmd2" ]; + description = "List of script names to invoke for health tracking."; + }; + + trackInterfaces = mkOption { + type = types.listOf types.str; + default = []; + example = [ "eth0" "eth1" ]; + description = "List of network interfaces to monitor for health tracking."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to be added verbatim to the vrrp_instance section. + ''; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-script-options.nix b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-script-options.nix new file mode 100644 index 000000000000..a3f794c40a89 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/keepalived/vrrp-script-options.nix @@ -0,0 +1,64 @@ +{ lib } : + +with lib; +with lib.types; +{ + options = { + + script = mkOption { + type = str; + example = "\${pkgs.curl} -f http://localhost:80"; + description = "(Path of) Script command to execute followed by args, i.e. cmd [args]..."; + }; + + interval = mkOption { + type = int; + default = 1; + description = "Seconds between script invocations."; + }; + + timeout = mkOption { + type = int; + default = 5; + description = "Seconds after which script is considered to have failed."; + }; + + weight = mkOption { + type = int; + default = 0; + description = "Following a failure, adjust the priority by this weight."; + }; + + rise = mkOption { + type = int; + default = 5; + description = "Required number of successes for OK transition."; + }; + + fall = mkOption { + type = int; + default = 3; + description = "Required number of failures for KO transition."; + }; + + user = mkOption { + type = str; + default = "keepalived_script"; + description = "Name of user to run the script under."; + }; + + group = mkOption { + type = nullOr str; + default = null; + description = "Name of group to run the script under. Defaults to user group."; + }; + + extraConfig = mkOption { + type = lines; + default = ""; + description = "Extra lines to be added verbatim to the vrrp_script section."; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/keybase.nix b/nixpkgs/nixos/modules/services/networking/keybase.nix new file mode 100644 index 000000000000..495102cb7eee --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/keybase.nix @@ -0,0 +1,47 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.keybase; + +in { + + ###### interface + + options = { + + services.keybase = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to start the Keybase service."; + }; + + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + # Upstream: https://github.com/keybase/client/blob/master/packaging/linux/systemd/keybase.service + systemd.user.services.keybase = { + description = "Keybase service"; + unitConfig.ConditionUser = "!@system"; + environment.KEYBASE_SERVICE_TYPE = "systemd"; + serviceConfig = { + Type = "notify"; + EnvironmentFile = [ + "-%E/keybase/keybase.autogen.env" + "-%E/keybase/keybase.env" + ]; + ExecStart = "${pkgs.keybase}/bin/keybase service"; + Restart = "on-failure"; + PrivateTmp = true; + }; + wantedBy = [ "default.target" ]; + }; + + environment.systemPackages = [ pkgs.keybase ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/kippo.nix b/nixpkgs/nixos/modules/services/networking/kippo.nix new file mode 100644 index 000000000000..553415a2f329 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/kippo.nix @@ -0,0 +1,117 @@ +# NixOS module for kippo honeypot ssh server +# See all the options for configuration details. +# +# Default port is 2222. Recommend using something like this for port redirection to default SSH port: +# networking.firewall.extraCommands = '' +# iptables -t nat -A PREROUTING -i IN_IFACE -p tcp --dport 22 -j REDIRECT --to-port 2222''; +# +# Lastly: use this service at your own risk. I am working on a way to run this inside a VM. +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.kippo; +in +{ + options = { + services.kippo = { + enable = mkOption { + default = false; + type = types.bool; + description = ''Enable the kippo honeypot ssh server.''; + }; + port = mkOption { + default = 2222; + type = types.int; + description = ''TCP port number for kippo to bind to.''; + }; + hostname = mkOption { + default = "nas3"; + type = types.str; + description = ''Hostname for kippo to present to SSH login''; + }; + varPath = mkOption { + default = "/var/lib/kippo"; + type = types.path; + description = ''Path of read/write files needed for operation and configuration.''; + }; + logPath = mkOption { + default = "/var/log/kippo"; + type = types.path; + description = ''Path of log files needed for operation and configuration.''; + }; + pidPath = mkOption { + default = "/run/kippo"; + type = types.path; + description = ''Path of pid files needed for operation.''; + }; + extraConfig = mkOption { + default = ""; + type = types.lines; + description = ''Extra verbatim configuration added to the end of kippo.cfg.''; + }; + }; + + }; + config = mkIf cfg.enable { + environment.systemPackages = with pkgs.pythonPackages; [ + python pkgs.kippo.twisted pycrypto pyasn1 ]; + + environment.etc."kippo.cfg".text = '' + # Automatically generated by NixOS. + # See ${pkgs.kippo}/src/kippo.cfg for details. + [honeypot] + log_path = ${cfg.logPath} + download_path = ${cfg.logPath}/dl + filesystem_file = ${cfg.varPath}/honeyfs + filesystem_file = ${cfg.varPath}/fs.pickle + data_path = ${cfg.varPath}/data + txtcmds_path = ${cfg.varPath}/txtcmds + public_key = ${cfg.varPath}/keys/public.key + private_key = ${cfg.varPath}/keys/private.key + ssh_port = ${toString cfg.port} + hostname = ${cfg.hostname} + ${cfg.extraConfig} + ''; + + users.users.kippo = { + description = "kippo web server privilege separation user"; + uid = 108; # why does config.ids.uids.kippo give an error? + }; + users.groups.kippo.gid = 108; + + systemd.services.kippo = with pkgs; { + description = "Kippo Web Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + environment.PYTHONPATH = "${pkgs.kippo}/src/:${pkgs.pythonPackages.pycrypto}/lib/python2.7/site-packages/:${pkgs.pythonPackages.pyasn1}/lib/python2.7/site-packages/:${pkgs.pythonPackages.python}/lib/python2.7/site-packages/:${pkgs.kippo.twisted}/lib/python2.7/site-packages/:."; + preStart = '' + if [ ! -d ${cfg.varPath}/ ] ; then + mkdir -p ${cfg.logPath}/tty + mkdir -p ${cfg.logPath}/dl + mkdir -p ${cfg.varPath}/keys + cp ${pkgs.kippo}/src/honeyfs ${cfg.varPath} -r + cp ${pkgs.kippo}/src/fs.pickle ${cfg.varPath}/fs.pickle + cp ${pkgs.kippo}/src/data ${cfg.varPath} -r + cp ${pkgs.kippo}/src/txtcmds ${cfg.varPath} -r + + chmod u+rw ${cfg.varPath} -R + chown kippo.kippo ${cfg.varPath} -R + chown kippo.kippo ${cfg.logPath} -R + chmod u+rw ${cfg.logPath} -R + fi + if [ ! -d ${cfg.pidPath}/ ] ; then + mkdir -p ${cfg.pidPath} + chmod u+rw ${cfg.pidPath} + chown kippo.kippo ${cfg.pidPath} + fi + ''; + + serviceConfig.ExecStart = "${pkgs.kippo.twisted}/bin/twistd -y ${pkgs.kippo}/src/kippo.tac --syslog --rundir=${cfg.varPath}/ --pidfile=${cfg.pidPath}/kippo.pid --prefix=kippo -n"; + serviceConfig.PermissionsStartOnly = true; + serviceConfig.User = "kippo"; + serviceConfig.Group = "kippo"; + }; +}; +} + + diff --git a/nixpkgs/nixos/modules/services/networking/knot.nix b/nixpkgs/nixos/modules/services/networking/knot.nix new file mode 100644 index 000000000000..12ff89fe8492 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/knot.nix @@ -0,0 +1,117 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.knot; + + configFile = pkgs.writeTextFile { + name = "knot.conf"; + text = (concatMapStringsSep "\n" (file: "include: ${file}") cfg.keyFiles) + "\n" + + cfg.extraConfig; + checkPhase = lib.optionalString (cfg.keyFiles == []) '' + ${cfg.package}/bin/knotc --config=$out conf-check + ''; + }; + + socketFile = "/run/knot/knot.sock"; + + knot-cli-wrappers = pkgs.stdenv.mkDerivation { + name = "knot-cli-wrappers"; + buildInputs = [ pkgs.makeWrapper ]; + buildCommand = '' + mkdir -p $out/bin + makeWrapper ${cfg.package}/bin/knotc "$out/bin/knotc" \ + --add-flags "--config=${configFile}" \ + --add-flags "--socket=${socketFile}" + makeWrapper ${cfg.package}/bin/keymgr "$out/bin/keymgr" \ + --add-flags "--config=${configFile}" + for executable in kdig khost kjournalprint knsec3hash knsupdate kzonecheck + do + ln -s "${cfg.package}/bin/$executable" "$out/bin/$executable" + done + mkdir -p "$out/share" + ln -s '${cfg.package}/share/man' "$out/share/" + ''; + }; +in { + options = { + services.knot = { + enable = mkEnableOption "Knot authoritative-only DNS server"; + + extraArgs = mkOption { + type = types.listOf types.str; + default = []; + description = '' + List of additional command line paramters for knotd + ''; + }; + + keyFiles = mkOption { + type = types.listOf types.path; + default = []; + description = '' + A list of files containing additional configuration + to be included using the include directive. This option + allows to include configuration like TSIG keys without + exposing them to the nix store readable to any process. + Note that using this option will also disable configuration + checks at build time. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to be added verbatim to knot.conf + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.knot-dns; + defaultText = "pkgs.knot-dns"; + description = '' + Which Knot DNS package to use + ''; + }; + }; + }; + + config = mkIf config.services.knot.enable { + users.users.knot = { + isSystemUser = true; + group = "knot"; + description = "Knot daemon user"; + }; + + users.groups.knot.gid = null; + systemd.services.knot = { + unitConfig.Documentation = "man:knotd(8) man:knot.conf(5) man:knotc(8) https://www.knot-dns.cz/docs/${cfg.package.version}/html/"; + description = cfg.package.meta.description; + wantedBy = [ "multi-user.target" ]; + wants = [ "network.target" ]; + after = ["network.target" ]; + + serviceConfig = { + Type = "notify"; + ExecStart = "${cfg.package}/bin/knotd --config=${configFile} --socket=${socketFile} ${concatStringsSep " " cfg.extraArgs}"; + ExecReload = "${knot-cli-wrappers}/bin/knotc reload"; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE CAP_SETPCAP"; + AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_SETPCAP"; + NoNewPrivileges = true; + User = "knot"; + RuntimeDirectory = "knot"; + StateDirectory = "knot"; + StateDirectoryMode = "0700"; + PrivateDevices = true; + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6"; + SystemCallArchitectures = "native"; + Restart = "on-abort"; + }; + }; + + environment.systemPackages = [ knot-cli-wrappers ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/kresd.nix b/nixpkgs/nixos/modules/services/networking/kresd.nix new file mode 100644 index 000000000000..c5a84eebd46f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/kresd.nix @@ -0,0 +1,145 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.kresd; + + # Convert systemd-style address specification to kresd config line(s). + # On Nix level we don't attempt to precisely validate the address specifications. + mkListen = kind: addr: let + al_v4 = builtins.match "([0-9.]\+):([0-9]\+)" addr; + al_v6 = builtins.match "\\[(.\+)]:([0-9]\+)" addr; + al_portOnly = builtins.match "()([0-9]\+)" addr; + al = findFirst (a: a != null) + (throw "services.kresd.*: incorrect address specification '${addr}'") + [ al_v4 al_v6 al_portOnly ]; + port = last al; + addrSpec = if al_portOnly == null then "'${head al}'" else "{'::', '127.0.0.1'}"; + in # freebind is set for compatibility with earlier kresd services; + # it could be configurable, for example. + '' + net.listen(${addrSpec}, ${port}, { kind = '${kind}', freebind = true }) + ''; + + configFile = pkgs.writeText "kresd.conf" ( + optionalString (cfg.listenDoH != []) '' + modules.load('http') + '' + + concatMapStrings (mkListen "dns") cfg.listenPlain + + concatMapStrings (mkListen "tls") cfg.listenTLS + + concatMapStrings (mkListen "doh") cfg.listenDoH + + cfg.extraConfig + ); + + package = if cfg.listenDoH == [] + then pkgs.knot-resolver # never force `extraFeatures = false` + else pkgs.knot-resolver.override { extraFeatures = true; }; +in { + meta.maintainers = [ maintainers.vcunat /* upstream developer */ ]; + + imports = [ + (mkChangedOptionModule [ "services" "kresd" "interfaces" ] [ "services" "kresd" "listenPlain" ] + (config: + let value = getAttrFromPath [ "services" "kresd" "interfaces" ] config; + in map + (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53") # Syntax depends on being IPv6 or IPv4. + value + ) + ) + (mkRemovedOptionModule [ "services" "kresd" "cacheDir" ] "Please use (bind-)mounting instead.") + ]; + + ###### interface + options.services.kresd = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable knot-resolver domain name server. + DNSSEC validation is turned on by default. + You can run <literal>sudo nc -U /run/knot-resolver/control/1</literal> + and give commands interactively to kresd@1.service. + ''; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to be added verbatim to the generated configuration file. + ''; + }; + listenPlain = mkOption { + type = with types; listOf str; + default = [ "[::1]:53" "127.0.0.1:53" ]; + example = [ "53" ]; + description = '' + What addresses and ports the server should listen on. + For detailed syntax see ListenStream in man systemd.socket. + ''; + }; + listenTLS = mkOption { + type = with types; listOf str; + default = []; + example = [ "198.51.100.1:853" "[2001:db8::1]:853" "853" ]; + description = '' + Addresses and ports on which kresd should provide DNS over TLS (see RFC 7858). + For detailed syntax see ListenStream in man systemd.socket. + ''; + }; + listenDoH = mkOption { + type = with types; listOf str; + default = []; + example = [ "198.51.100.1:443" "[2001:db8::1]:443" "443" ]; + description = '' + Addresses and ports on which kresd should provide DNS over HTTPS (see RFC 8484). + For detailed syntax see ListenStream in man systemd.socket. + ''; + }; + instances = mkOption { + type = types.ints.unsigned; + default = 1; + description = '' + The number of instances to start. They will be called kresd@{1,2,...}.service. + Knot Resolver uses no threads, so this is the way to scale. + You can dynamically start/stop them at will, so this is just system default. + ''; + }; + # TODO: perhaps options for more common stuff like cache size or forwarding + }; + + ###### implementation + config = mkIf cfg.enable { + environment.etc."knot-resolver/kresd.conf".source = configFile; # not required + + users.users.knot-resolver = + { isSystemUser = true; + group = "knot-resolver"; + description = "Knot-resolver daemon user"; + }; + users.groups.knot-resolver.gid = null; + + systemd.packages = [ package ]; # the units are patched inside the package a bit + + systemd.targets.kresd = { # configure units started by default + wantedBy = [ "multi-user.target" ]; + wants = [ "kres-cache-gc.service" ] + ++ map (i: "kresd@${toString i}.service") (range 1 cfg.instances); + }; + systemd.services."kresd@".serviceConfig = { + ExecStart = "${package}/bin/kresd --noninteractive " + + "-c ${package}/lib/knot-resolver/distro-preconfig.lua -c ${configFile}"; + # Ensure correct ownership in case UID or GID changes. + CacheDirectory = "knot-resolver"; + CacheDirectoryMode = "0750"; + }; + + environment.etc."tmpfiles.d/knot-resolver.conf".source = + "${package}/lib/tmpfiles.d/knot-resolver.conf"; + + # Try cleaning up the previously default location of cache file. + # Note that /var/cache/* should always be safe to remove. + # TODO: remove later, probably between 20.09 and 21.03 + systemd.tmpfiles.rules = [ "R /var/cache/kresd" ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/lambdabot.nix b/nixpkgs/nixos/modules/services/networking/lambdabot.nix new file mode 100644 index 000000000000..b7c8bd008fe1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/lambdabot.nix @@ -0,0 +1,82 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.lambdabot; + + rc = builtins.toFile "script.rc" cfg.script; + +in + +{ + + ### configuration + + options = { + + services.lambdabot = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Enable the Lambdabot IRC bot"; + }; + + package = mkOption { + type = types.package; + default = pkgs.lambdabot; + defaultText = "pkgs.lambdabot"; + description = "Used lambdabot package"; + }; + + script = mkOption { + type = types.str; + default = ""; + description = "Lambdabot script"; + }; + + }; + + }; + + ### implementation + + config = mkIf cfg.enable { + + systemd.services.lambdabot = { + description = "Lambdabot daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + # Workaround for https://github.com/lambdabot/lambdabot/issues/117 + script = '' + mkdir -p ~/.lambdabot + cd ~/.lambdabot + mkfifo /run/lambdabot/offline + ( + echo 'rc ${rc}' + while true; do + cat /run/lambdabot/offline + done + ) | ${cfg.package}/bin/lambdabot + ''; + serviceConfig = { + User = "lambdabot"; + RuntimeDirectory = [ "lambdabot" ]; + }; + }; + + users.users.lambdabot = { + group = "lambdabot"; + description = "Lambdabot daemon user"; + home = "/var/lib/lambdabot"; + createHome = true; + uid = config.ids.uids.lambdabot; + }; + + users.groups.lambdabot.gid = config.ids.gids.lambdabot; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/libreswan.nix b/nixpkgs/nixos/modules/services/networking/libreswan.nix new file mode 100644 index 000000000000..280158b89f61 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/libreswan.nix @@ -0,0 +1,129 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.libreswan; + + libexec = "${pkgs.libreswan}/libexec/ipsec"; + ipsec = "${pkgs.libreswan}/sbin/ipsec"; + + trim = chars: str: let + nonchars = filter (x : !(elem x.value chars)) + (imap0 (i: v: {ind = i; value = v;}) (stringToCharacters str)); + in + if length nonchars == 0 then "" + else substring (head nonchars).ind (add 1 (sub (last nonchars).ind (head nonchars).ind)) str; + indent = str: concatStrings (concatMap (s: [" " (trim [" " "\t"] s) "\n"]) (splitString "\n" str)); + configText = indent (toString cfg.configSetup); + connectionText = concatStrings (mapAttrsToList (n: v: + '' + conn ${n} + ${indent v} + + '') cfg.connections); + configFile = pkgs.writeText "ipsec.conf" + '' + config setup + ${configText} + + ${connectionText} + ''; + +in + +{ + + ###### interface + + options = { + + services.libreswan = { + + enable = mkEnableOption "libreswan ipsec service"; + + configSetup = mkOption { + type = types.lines; + default = '' + protostack=netkey + nat_traversal=yes + virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10 + ''; + example = '' + secretsfile=/root/ipsec.secrets + protostack=netkey + nat_traversal=yes + virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10 + ''; + description = "Options to go in the 'config setup' section of the libreswan ipsec configuration"; + }; + + connections = mkOption { + type = types.attrsOf types.lines; + default = {}; + example = { + myconnection = '' + auto=add + left=%defaultroute + leftid=@user + + right=my.vpn.com + + ikev2=no + ikelifetime=8h + ''; + }; + description = "A set of connections to define for the libreswan ipsec service"; + }; + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.libreswan pkgs.iproute ]; + + systemd.services.ipsec = { + description = "Internet Key Exchange (IKE) Protocol Daemon for IPsec"; + path = [ + "${pkgs.libreswan}" + "${pkgs.iproute}" + "${pkgs.procps}" + "${pkgs.nssTools}" + "${pkgs.iptables}" + "${pkgs.nettools}" + ]; + + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "simple"; + Restart = "always"; + EnvironmentFile = "-${pkgs.libreswan}/etc/sysconfig/pluto"; + ExecStartPre = [ + "${libexec}/addconn --config ${configFile} --checkconfig" + "${libexec}/_stackmanager start" + "${ipsec} --checknss" + "${ipsec} --checknflog" + ]; + ExecStart = "${libexec}/pluto --config ${configFile} --nofork \$PLUTO_OPTIONS"; + ExecStop = "${libexec}/whack --shutdown"; + ExecStopPost = [ + "${pkgs.iproute}/bin/ip xfrm policy flush" + "${pkgs.iproute}/bin/ip xfrm state flush" + "${ipsec} --stopnflog" + ]; + ExecReload = "${libexec}/whack --listen"; + }; + + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/lldpd.nix b/nixpkgs/nixos/modules/services/networking/lldpd.nix new file mode 100644 index 000000000000..d5de9c45d84b --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/lldpd.nix @@ -0,0 +1,39 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.lldpd; + +in + +{ + options.services.lldpd = { + enable = mkEnableOption "Link Layer Discovery Protocol Daemon"; + + extraArgs = mkOption { + type = types.listOf types.str; + default = []; + example = [ "-c" "-k" "-I eth0" ]; + description = "List of command line parameters for lldpd"; + }; + }; + + config = mkIf cfg.enable { + users.users._lldpd = { + description = "lldpd user"; + group = "_lldpd"; + home = "/run/lldpd"; + isSystemUser = true; + }; + users.groups._lldpd = {}; + + environment.systemPackages = [ pkgs.lldpd ]; + systemd.packages = [ pkgs.lldpd ]; + + systemd.services.lldpd = { + wantedBy = [ "multi-user.target" ]; + environment.LLDPD_OPTIONS = concatStringsSep " " cfg.extraArgs; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/logmein-hamachi.nix b/nixpkgs/nixos/modules/services/networking/logmein-hamachi.nix new file mode 100644 index 000000000000..11cbdda2f845 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/logmein-hamachi.nix @@ -0,0 +1,50 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.logmein-hamachi; + +in + +{ + + ###### interface + + options = { + + services.logmein-hamachi.enable = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to enable LogMeIn Hamachi, a proprietary + (closed source) commercial VPN software. + ''; + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.logmein-hamachi = { + description = "LogMeIn Hamachi Daemon"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "forking"; + ExecStart = "${pkgs.logmein-hamachi}/bin/hamachid"; + }; + }; + + environment.systemPackages = [ pkgs.logmein-hamachi ]; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/magic-wormhole-mailbox-server.nix b/nixpkgs/nixos/modules/services/networking/magic-wormhole-mailbox-server.nix new file mode 100644 index 000000000000..09d357cd2b6e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/magic-wormhole-mailbox-server.nix @@ -0,0 +1,28 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.magic-wormhole-mailbox-server; + dataDir = "/var/lib/magic-wormhole-mailbox-server;"; + python = pkgs.python3.withPackages (py: [ py.magic-wormhole-mailbox-server py.twisted ]); +in +{ + options.services.magic-wormhole-mailbox-server = { + enable = mkEnableOption "Enable Magic Wormhole Mailbox Server"; + }; + + config = mkIf cfg.enable { + systemd.services.magic-wormhole-mailbox-server = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + DynamicUser = true; + ExecStart = "${python}/bin/twistd --nodaemon wormhole-mailbox"; + WorkingDirectory = dataDir; + StateDirectory = baseNameOf dataDir; + }; + }; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/mailpile.nix b/nixpkgs/nixos/modules/services/networking/mailpile.nix new file mode 100644 index 000000000000..b79ee11d17db --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mailpile.nix @@ -0,0 +1,72 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.mailpile; + + hostname = cfg.hostname; + port = cfg.port; + +in + +{ + + ###### interface + + options = { + + services.mailpile = { + enable = mkEnableOption "Mailpile the mail client"; + + hostname = mkOption { + default = "localhost"; + description = "Listen to this hostname or ip."; + }; + port = mkOption { + default = "33411"; + description = "Listen on this port."; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.mailpile.enable { + + users.users.mailpile = + { uid = config.ids.uids.mailpile; + description = "Mailpile user"; + createHome = true; + home = "/var/lib/mailpile"; + }; + + users.groups.mailpile = + { gid = config.ids.gids.mailpile; + }; + + systemd.services.mailpile = + { + description = "Mailpile server."; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "mailpile"; + ExecStart = "${pkgs.mailpile}/bin/mailpile --www ${hostname}:${port} --wait"; + # mixed - first send SIGINT to main process, + # then after 2min send SIGKILL to whole group if neccessary + KillMode = "mixed"; + KillSignal = "SIGINT"; # like Ctrl+C - safe mailpile shutdown + TimeoutSec = 120; # wait 2min untill SIGKILL + }; + environment.MAILPILE_HOME = "/var/lib/mailpile/.local/share/Mailpile"; + }; + + environment.systemPackages = [ pkgs.mailpile ]; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/matterbridge.nix b/nixpkgs/nixos/modules/services/networking/matterbridge.nix new file mode 100644 index 000000000000..b8b4f37c84a8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/matterbridge.nix @@ -0,0 +1,120 @@ +{ options, config, pkgs, lib, ... }: + +with lib; + +let + + cfg = config.services.matterbridge; + + matterbridgeConfToml = + if cfg.configPath == null then + pkgs.writeText "matterbridge.toml" (cfg.configFile) + else + cfg.configPath; + +in + +{ + options = { + services.matterbridge = { + enable = mkEnableOption "Matterbridge chat platform bridge"; + + configPath = mkOption { + type = with types; nullOr str; + default = null; + example = "/etc/nixos/matterbridge.toml"; + description = '' + The path to the matterbridge configuration file. + ''; + }; + + configFile = mkOption { + type = types.str; + example = '' + # WARNING: as this file contains credentials, do not use this option! + # It is kept only for backwards compatibility, and would cause your + # credentials to be in the nix-store, thus with the world-readable + # permission bits. + # Use services.matterbridge.configPath instead. + + [irc] + [irc.freenode] + Server="irc.freenode.net:6667" + Nick="matterbot" + + [mattermost] + [mattermost.work] + # Do not prefix it with http:// or https:// + Server="yourmattermostserver.domain" + Team="yourteam" + Login="yourlogin" + Password="yourpass" + PrefixMessagesWithNick=true + + [[gateway]] + name="gateway1" + enable=true + [[gateway.inout]] + account="irc.freenode" + channel="#testing" + + [[gateway.inout]] + account="mattermost.work" + channel="off-topic" + ''; + description = '' + WARNING: THIS IS INSECURE, as your password will end up in + <filename>/nix/store</filename>, thus publicly readable. Use + <literal>services.matterbridge.configPath</literal> instead. + + The matterbridge configuration file in the TOML file format. + ''; + }; + user = mkOption { + type = types.str; + default = "matterbridge"; + description = '' + User which runs the matterbridge service. + ''; + }; + + group = mkOption { + type = types.str; + default = "matterbridge"; + description = '' + Group which runs the matterbridge service. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + warnings = optional options.services.matterbridge.configFile.isDefined + "The option services.matterbridge.configFile is insecure and should be replaced with services.matterbridge.configPath"; + + users.users = optionalAttrs (cfg.user == "matterbridge") + { matterbridge = { + group = "matterbridge"; + isSystemUser = true; + }; + }; + + users.groups = optionalAttrs (cfg.group == "matterbridge") + { matterbridge = { }; + }; + + systemd.services.matterbridge = { + description = "Matterbridge chat platform bridge"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + ExecStart = "${pkgs.matterbridge}/bin/matterbridge -conf ${matterbridgeConfToml}"; + Restart = "always"; + RestartSec = "10"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/minidlna.nix b/nixpkgs/nixos/modules/services/networking/minidlna.nix new file mode 100644 index 000000000000..c580ba47dad3 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/minidlna.nix @@ -0,0 +1,193 @@ +# Module for MiniDLNA, a simple DLNA server. +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.minidlna; + port = 8200; +in + +{ + ###### interface + options = { + services.minidlna.enable = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to enable MiniDLNA, a simple DLNA server. It serves + media files such as video and music to DLNA client devices + such as televisions and media players. + ''; + }; + + services.minidlna.mediaDirs = mkOption { + type = types.listOf types.str; + default = []; + example = [ "/data/media" "V,/home/alice/video" ]; + description = + '' + Directories to be scanned for media files. The prefixes + <literal>A,</literal>, <literal>V,</literal> and + <literal>P,</literal> restrict a directory to audio, video + or image files. The directories must be accessible to the + <literal>minidlna</literal> user account. + ''; + }; + + services.minidlna.friendlyName = mkOption { + type = types.str; + default = "${config.networking.hostName} MiniDLNA"; + defaultText = "$HOSTNAME MiniDLNA"; + example = "rpi3"; + description = + '' + Name that the DLNA server presents to clients. + ''; + }; + + services.minidlna.rootContainer = mkOption { + type = types.str; + default = "."; + example = "B"; + description = + '' + Use a different container as the root of the directory tree presented + to clients. The possible values are: + - "." - standard container + - "B" - "Browse Directory" + - "M" - "Music" + - "P" - "Pictures" + - "V" - "Video" + - Or, you can specify the ObjectID of your desired root container + (eg. 1$F for Music/Playlists) + If you specify "B" and the client device is audio-only then + "Music/Folders" will be used as root. + ''; + }; + + services.minidlna.loglevel = mkOption { + type = types.str; + default = "warn"; + example = "general,artwork,database,inotify,scanner,metadata,http,ssdp,tivo=warn"; + description = + '' + Defines the type of messages that should be logged, and down to + which level of importance they should be considered. + + The possible types are “artwork”, “database”, “general”, “http”, + “inotify”, “metadata”, “scanner”, “ssdp” and “tivo”. + + The levels are “off”, “fatal”, “error”, “warn”, “info” and + “debug”, listed here in order of decreasing importance. “off” + turns off logging messages entirely, “fatal” logs the most + critical messages only, and so on down to “debug” that logs every + single messages. + + The types are comma-separated, followed by an equal sign (‘=’), + followed by a level that applies to the preceding types. This can + be repeated, separating each of these constructs with a comma. + + Defaults to “general,artwork,database,inotify,scanner,metadata, + http,ssdp,tivo=warn” which logs every type of message at the + “warn” level. + ''; + }; + + services.minidlna.announceInterval = mkOption { + type = types.int; + default = 895; + description = + '' + The interval between announces (in seconds). + + By default miniDLNA will announce its presence on the network + approximately every 15 minutes. + + Many people prefer shorter announce intervals (e.g. 60 seconds) + on their home networks, especially when DLNA clients are + started on demand. + ''; + }; + + services.minidlna.config = mkOption { + type = types.lines; + description = + '' + The contents of MiniDLNA's configuration file. + When the service is activated, a basic template is generated + from the current options opened here. + ''; + }; + + services.minidlna.extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + # Not exhaustive example + # Support for streaming .jpg and .mp3 files to a TiVo supporting HMO. + enable_tivo=no + # SSDP notify interval, in seconds. + notify_interval=10 + # maximum number of simultaneous connections + # note: many clients open several simultaneous connections while + # streaming + max_connections=50 + # set this to yes to allow symlinks that point outside user-defined + # media_dirs. + wide_links=yes + ''; + description = + '' + Extra minidlna options not yet opened for configuration here + (strict_dlna, model_number, model_name, etc...). This is appended + to the current service already provided. + ''; + }; + }; + + ###### implementation + config = mkIf cfg.enable { + services.minidlna.config = + '' + port=${toString port} + friendly_name=${cfg.friendlyName} + db_dir=/var/cache/minidlna + log_level=${cfg.loglevel} + inotify=yes + root_container=${cfg.rootContainer} + ${concatMapStrings (dir: '' + media_dir=${dir} + '') cfg.mediaDirs} + notify_interval=${toString cfg.announceInterval} + ${cfg.extraConfig} + ''; + + users.users.minidlna = { + description = "MiniDLNA daemon user"; + group = "minidlna"; + uid = config.ids.uids.minidlna; + }; + + users.groups.minidlna.gid = config.ids.gids.minidlna; + + systemd.services.minidlna = + { description = "MiniDLNA Server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = + { User = "minidlna"; + Group = "minidlna"; + CacheDirectory = "minidlna"; + RuntimeDirectory = "minidlna"; + PIDFile = "/run/minidlna/pid"; + ExecStart = + "${pkgs.minidlna}/sbin/minidlnad -S -P /run/minidlna/pid" + + " -f ${pkgs.writeText "minidlna.conf" cfg.config}"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/miniupnpd.nix b/nixpkgs/nixos/modules/services/networking/miniupnpd.nix new file mode 100644 index 000000000000..c095d9948546 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/miniupnpd.nix @@ -0,0 +1,79 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.miniupnpd; + configFile = pkgs.writeText "miniupnpd.conf" '' + ext_ifname=${cfg.externalInterface} + enable_natpmp=${if cfg.natpmp then "yes" else "no"} + enable_upnp=${if cfg.upnp then "yes" else "no"} + + ${concatMapStrings (range: '' + listening_ip=${range} + '') cfg.internalIPs} + + ${cfg.appendConfig} + ''; +in +{ + options = { + services.miniupnpd = { + enable = mkEnableOption "MiniUPnP daemon"; + + externalInterface = mkOption { + type = types.str; + description = '' + Name of the external interface. + ''; + }; + + internalIPs = mkOption { + type = types.listOf types.str; + example = [ "192.168.1.1/24" "enp1s0" ]; + description = '' + The IP address ranges to listen on. + ''; + }; + + natpmp = mkEnableOption "NAT-PMP support"; + + upnp = mkOption { + default = true; + type = types.bool; + description = '' + Whether to enable UPNP support. + ''; + }; + + appendConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Configuration lines appended to the MiniUPnP config. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.extraCommands = '' + ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_init.sh -i ${cfg.externalInterface} + ''; + + networking.firewall.extraStopCommands = '' + ${pkgs.bash}/bin/bash -x ${pkgs.miniupnpd}/etc/miniupnpd/iptables_removeall.sh -i ${cfg.externalInterface} + ''; + + systemd.services.miniupnpd = { + description = "MiniUPnP daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${pkgs.miniupnpd}/bin/miniupnpd -f ${configFile}"; + PIDFile = "/run/miniupnpd.pid"; + Type = "forking"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/miredo.nix b/nixpkgs/nixos/modules/services/networking/miredo.nix new file mode 100644 index 000000000000..2c8393fb5b41 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/miredo.nix @@ -0,0 +1,92 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.miredo; + pidFile = "/run/miredo.pid"; + miredoConf = pkgs.writeText "miredo.conf" '' + InterfaceName ${cfg.interfaceName} + ServerAddress ${cfg.serverAddress} + ${optionalString (cfg.bindAddress != null) "BindAddress ${cfg.bindAddress}"} + ${optionalString (cfg.bindPort != null) "BindPort ${cfg.bindPort}"} + ''; +in +{ + + ###### interface + + options = { + + services.miredo = { + + enable = mkEnableOption "the Miredo IPv6 tunneling service"; + + package = mkOption { + type = types.package; + default = pkgs.miredo; + defaultText = "pkgs.miredo"; + description = '' + The package to use for the miredo daemon's binary. + ''; + }; + + serverAddress = mkOption { + default = "teredo.remlab.net"; + type = types.str; + description = '' + The hostname or primary IPv4 address of the Teredo server. + This setting is required if Miredo runs as a Teredo client. + "teredo.remlab.net" is an experimental service for testing only. + Please use another server for production and/or large scale deployments. + ''; + }; + + interfaceName = mkOption { + default = "teredo"; + type = types.str; + description = '' + Name of the network tunneling interface. + ''; + }; + + bindAddress = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + Depending on the local firewall/NAT rules, you might need to force + Miredo to use a fixed UDP port and or IPv4 address. + ''; + }; + + bindPort = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + Depending on the local firewall/NAT rules, you might need to force + Miredo to use a fixed UDP port and or IPv4 address. + ''; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.miredo = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "Teredo IPv6 Tunneling Daemon"; + serviceConfig = { + Restart = "always"; + RestartSec = "5s"; + ExecStart = "${cfg.package}/bin/miredo -c ${miredoConf} -p ${pidFile} -f"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/mjpg-streamer.nix b/nixpkgs/nixos/modules/services/networking/mjpg-streamer.nix new file mode 100644 index 000000000000..dbc35e2e71c0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mjpg-streamer.nix @@ -0,0 +1,80 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.mjpg-streamer; + +in { + + options = { + + services.mjpg-streamer = { + + enable = mkEnableOption "mjpg-streamer webcam streamer"; + + inputPlugin = mkOption { + type = types.str; + default = "input_uvc.so"; + description = '' + Input plugin. See plugins documentation for more information. + ''; + }; + + outputPlugin = mkOption { + type = types.str; + default = "output_http.so -w @www@ -n -p 5050"; + description = '' + Output plugin. <literal>@www@</literal> is substituted for default mjpg-streamer www directory. + See plugins documentation for more information. + ''; + }; + + user = mkOption { + type = types.str; + default = "mjpg-streamer"; + description = "mjpg-streamer user name."; + }; + + group = mkOption { + type = types.str; + default = "video"; + description = "mjpg-streamer group name."; + }; + + }; + + }; + + config = mkIf cfg.enable { + + users.users = optionalAttrs (cfg.user == "mjpg-streamer") { + mjpg-streamer = { + uid = config.ids.uids.mjpg-streamer; + group = cfg.group; + }; + }; + + systemd.services.mjpg-streamer = { + description = "mjpg-streamer webcam streamer"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + Restart = "on-failure"; + RestartSec = 1; + }; + + script = '' + IPLUGIN="${cfg.inputPlugin}" + OPLUGIN="${cfg.outputPlugin}" + OPLUGIN="''${OPLUGIN//@www@/${pkgs.mjpg-streamer}/share/mjpg-streamer/www}" + exec ${pkgs.mjpg-streamer}/bin/mjpg_streamer -i "$IPLUGIN" -o "$OPLUGIN" + ''; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/monero.nix b/nixpkgs/nixos/modules/services/networking/monero.nix new file mode 100644 index 000000000000..97af29978397 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/monero.nix @@ -0,0 +1,238 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.monero; + dataDir = "/var/lib/monero"; + + listToConf = option: list: + concatMapStrings (value: "${option}=${value}\n") list; + + login = (cfg.rpc.user != null && cfg.rpc.password != null); + + configFile = with cfg; pkgs.writeText "monero.conf" '' + log-file=/dev/stdout + data-dir=${dataDir} + + ${optionalString mining.enable '' + start-mining=${mining.address} + mining-threads=${toString mining.threads} + ''} + + rpc-bind-ip=${rpc.address} + rpc-bind-port=${toString rpc.port} + ${optionalString login '' + rpc-login=${rpc.user}:${rpc.password} + ''} + ${optionalString rpc.restricted '' + restricted-rpc=1 + ''} + + limit-rate-up=${toString limits.upload} + limit-rate-down=${toString limits.download} + max-concurrency=${toString limits.threads} + block-sync-size=${toString limits.syncSize} + + ${listToConf "add-peer" extraNodes} + ${listToConf "add-priority-node" priorityNodes} + ${listToConf "add-exclusive-node" exclusiveNodes} + + ${extraConfig} + ''; + +in + +{ + + ###### interface + + options = { + + services.monero = { + + enable = mkEnableOption "Monero node daemon"; + + mining.enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to mine moneroj. + ''; + }; + + mining.address = mkOption { + type = types.str; + default = ""; + description = '' + Monero address where to send mining rewards. + ''; + }; + + mining.threads = mkOption { + type = types.addCheck types.int (x: x>=0); + default = 0; + description = '' + Number of threads used for mining. + Set to <literal>0</literal> to use all available. + ''; + }; + + rpc.user = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + User name for RPC connections. + ''; + }; + + rpc.password = mkOption { + type = types.str; + default = null; + description = '' + Password for RPC connections. + ''; + }; + + rpc.address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + IP address the RPC server will bind to. + ''; + }; + + rpc.port = mkOption { + type = types.int; + default = 18081; + description = '' + Port the RPC server will bind to. + ''; + }; + + rpc.restricted = mkOption { + type = types.bool; + default = false; + description = '' + Whether to restrict RPC to view only commands. + ''; + }; + + limits.upload = mkOption { + type = types.addCheck types.int (x: x>=-1); + default = -1; + description = '' + Limit of the upload rate in kB/s. + Set to <literal>-1</literal> to leave unlimited. + ''; + }; + + limits.download = mkOption { + type = types.addCheck types.int (x: x>=-1); + default = -1; + description = '' + Limit of the download rate in kB/s. + Set to <literal>-1</literal> to leave unlimited. + ''; + }; + + limits.threads = mkOption { + type = types.addCheck types.int (x: x>=0); + default = 0; + description = '' + Maximum number of threads used for a parallel job. + Set to <literal>0</literal> to leave unlimited. + ''; + }; + + limits.syncSize = mkOption { + type = types.addCheck types.int (x: x>=0); + default = 0; + description = '' + Maximum number of blocks to sync at once. + Set to <literal>0</literal> for adaptive. + ''; + }; + + extraNodes = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of additional peer IP addresses to add to the local list. + ''; + }; + + priorityNodes = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of peer IP addresses to connect to and + attempt to keep the connection open. + ''; + }; + + exclusiveNodes = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of peer IP addresses to connect to *only*. + If given the other peer options will be ignored. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to be added verbatim to monerod configuration. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.users.monero = { + uid = config.ids.uids.monero; + description = "Monero daemon user"; + home = dataDir; + createHome = true; + }; + + users.groups.monero = { + gid = config.ids.gids.monero; + }; + + systemd.services.monero = { + description = "monero daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = "monero"; + Group = "monero"; + ExecStart = "${pkgs.monero}/bin/monerod --config-file=${configFile} --non-interactive"; + Restart = "always"; + SuccessExitStatus = [ 0 1 ]; + }; + }; + + assertions = singleton { + assertion = cfg.mining.enable -> cfg.mining.address != ""; + message = '' + You need a Monero address to receive mining rewards: + specify one using option monero.mining.address. + ''; + }; + + }; + + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; + +} + diff --git a/nixpkgs/nixos/modules/services/networking/morty.nix b/nixpkgs/nixos/modules/services/networking/morty.nix new file mode 100644 index 000000000000..e3a6444c1163 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/morty.nix @@ -0,0 +1,97 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.morty; + +in + +{ + + ###### interface + + options = { + + services.morty = { + + enable = mkEnableOption + "Morty proxy server. See https://github.com/asciimoo/morty"; + + ipv6 = mkOption { + type = types.bool; + default = true; + description = "Allow IPv6 HTTP requests?"; + defaultText = "Allow IPv6 HTTP requests."; + }; + + key = mkOption { + type = types.str; + default = ""; + description = "HMAC url validation key (hexadecimal encoded). + Leave blank to disable. Without validation key, anyone can + submit proxy requests. Leave blank to disable."; + defaultText = "No HMAC url validation. Generate with echo -n somevalue | openssl dgst -sha1 -hmac somekey"; + }; + + timeout = mkOption { + type = types.int; + default = 2; + description = "Request timeout in seconds."; + defaultText = "A resource now gets 2 seconds to respond."; + }; + + package = mkOption { + type = types.package; + default = pkgs.morty; + defaultText = "pkgs.morty"; + description = "morty package to use."; + }; + + port = mkOption { + type = types.int; + default = 3000; + description = "Listing port"; + }; + + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "The address on which the service listens"; + defaultText = "127.0.0.1 (localhost)"; + }; + + }; + + }; + + ###### Service definition + + config = mkIf config.services.morty.enable { + + users.users.morty = + { description = "Morty user"; + createHome = true; + home = "/var/lib/morty"; + isSystemUser = true; + }; + + systemd.services.morty = + { + description = "Morty sanitizing proxy server."; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "morty"; + ExecStart = ''${cfg.package}/bin/morty \ + -listen ${cfg.listenAddress}:${toString cfg.port} \ + ${optionalString cfg.ipv6 "-ipv6"} \ + ${optionalString (cfg.key != "") "-key " + cfg.key} \ + ''; + }; + }; + environment.systemPackages = [ cfg.package ]; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/mosquitto.nix b/nixpkgs/nixos/modules/services/networking/mosquitto.nix new file mode 100644 index 000000000000..d2feb93e2b72 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mosquitto.nix @@ -0,0 +1,231 @@ +{ config, lib, pkgs, ...}: + +with lib; + +let + cfg = config.services.mosquitto; + + listenerConf = optionalString cfg.ssl.enable '' + listener ${toString cfg.ssl.port} ${cfg.ssl.host} + cafile ${cfg.ssl.cafile} + certfile ${cfg.ssl.certfile} + keyfile ${cfg.ssl.keyfile} + ''; + + passwordConf = optionalString cfg.checkPasswords '' + password_file ${cfg.dataDir}/passwd + ''; + + mosquittoConf = pkgs.writeText "mosquitto.conf" '' + acl_file ${aclFile} + persistence true + allow_anonymous ${boolToString cfg.allowAnonymous} + bind_address ${cfg.host} + port ${toString cfg.port} + ${passwordConf} + ${listenerConf} + ${cfg.extraConf} + ''; + + userAcl = (concatStringsSep "\n\n" (mapAttrsToList (n: c: + "user ${n}\n" + (concatStringsSep "\n" c.acl)) cfg.users + )); + + aclFile = pkgs.writeText "mosquitto.acl" '' + ${cfg.aclExtraConf} + ${userAcl} + ''; + +in + +{ + + ###### Interface + + options = { + services.mosquitto = { + enable = mkEnableOption "the MQTT Mosquitto broker"; + + host = mkOption { + default = "127.0.0.1"; + example = "0.0.0.0"; + type = types.str; + description = '' + Host to listen on without SSL. + ''; + }; + + port = mkOption { + default = 1883; + example = 1883; + type = types.int; + description = '' + Port on which to listen without SSL. + ''; + }; + + ssl = { + enable = mkEnableOption "SSL listener"; + + cafile = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to PEM encoded CA certificates."; + }; + + certfile = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to PEM encoded server certificate."; + }; + + keyfile = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to PEM encoded server key."; + }; + + host = mkOption { + default = "0.0.0.0"; + example = "localhost"; + type = types.str; + description = '' + Host to listen on with SSL. + ''; + }; + + port = mkOption { + default = 8883; + example = 8883; + type = types.int; + description = '' + Port on which to listen with SSL. + ''; + }; + }; + + dataDir = mkOption { + default = "/var/lib/mosquitto"; + type = types.path; + description = '' + The data directory. + ''; + }; + + users = mkOption { + type = types.attrsOf (types.submodule { + options = { + password = mkOption { + type = with types; uniq (nullOr str); + default = null; + description = '' + Specifies the (clear text) password for the MQTT User. + ''; + }; + + hashedPassword = mkOption { + type = with types; uniq (nullOr str); + default = null; + description = '' + Specifies the hashed password for the MQTT User. + <option>hashedPassword</option> overrides <option>password</option>. + To generate hashed password install <literal>mosquitto</literal> + package and use <literal>mosquitto_passwd</literal>. + ''; + }; + + acl = mkOption { + type = types.listOf types.str; + example = [ "topic read A/B" "topic A/#" ]; + description = '' + Control client access to topics on the broker. + ''; + }; + }; + }); + example = { john = { password = "123456"; acl = [ "topic readwrite john/#" ]; }; }; + description = '' + A set of users and their passwords and ACLs. + ''; + }; + + allowAnonymous = mkOption { + default = false; + type = types.bool; + description = '' + Allow clients to connect without authentication. + ''; + }; + + checkPasswords = mkOption { + default = false; + example = true; + type = types.bool; + description = '' + Refuse connection when clients provide incorrect passwords. + ''; + }; + + extraConf = mkOption { + default = ""; + type = types.lines; + description = '' + Extra config to append to `mosquitto.conf` file. + ''; + }; + + aclExtraConf = mkOption { + default = ""; + type = types.lines; + description = '' + Extra config to prepend to the ACL file. + ''; + }; + + }; + }; + + + ###### Implementation + + config = mkIf cfg.enable { + + systemd.services.mosquitto = { + description = "Mosquitto MQTT Broker Daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + Type = "notify"; + NotifyAccess = "main"; + User = "mosquitto"; + Group = "mosquitto"; + RuntimeDirectory = "mosquitto"; + WorkingDirectory = cfg.dataDir; + Restart = "on-failure"; + ExecStart = "${pkgs.mosquitto}/bin/mosquitto -c ${mosquittoConf}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + preStart = '' + rm -f ${cfg.dataDir}/passwd + touch ${cfg.dataDir}/passwd + '' + concatStringsSep "\n" ( + mapAttrsToList (n: c: + if c.hashedPassword != null then + "echo '${n}:${c.hashedPassword}' >> ${cfg.dataDir}/passwd" + else optionalString (c.password != null) + "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} '${c.password}'" + ) cfg.users); + }; + + users.users.mosquitto = { + description = "Mosquitto MQTT Broker Daemon owner"; + group = "mosquitto"; + uid = config.ids.uids.mosquitto; + home = cfg.dataDir; + createHome = true; + }; + + users.groups.mosquitto.gid = config.ids.gids.mosquitto; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/mstpd.nix b/nixpkgs/nixos/modules/services/networking/mstpd.nix new file mode 100644 index 000000000000..5d1fc4a65427 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mstpd.nix @@ -0,0 +1,33 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.mstpd; +in +with lib; +{ + options.services.mstpd = { + + enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable the multiple spanning tree protocol daemon. + ''; + }; + + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.mstpd ]; + + systemd.services.mstpd = { + description = "Multiple Spanning Tree Protocol Daemon"; + wantedBy = [ "network.target" ]; + unitConfig.ConditionCapability = "CAP_NET_ADMIN"; + serviceConfig = { + Type = "forking"; + ExecStart = "@${pkgs.mstpd}/bin/mstpd mstpd"; + PIDFile = "/run/mstpd.pid"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/mtprotoproxy.nix b/nixpkgs/nixos/modules/services/networking/mtprotoproxy.nix new file mode 100644 index 000000000000..d896f227b82c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mtprotoproxy.nix @@ -0,0 +1,110 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.mtprotoproxy; + + configOpts = { + PORT = cfg.port; + USERS = cfg.users; + SECURE_ONLY = cfg.secureOnly; + } // lib.optionalAttrs (cfg.adTag != null) { AD_TAG = cfg.adTag; } + // cfg.extraConfig; + + convertOption = opt: + if isString opt || isInt opt then + builtins.toJSON opt + else if isBool opt then + if opt then "True" else "False" + else if isList opt then + "[" + concatMapStringsSep "," convertOption opt + "]" + else if isAttrs opt then + "{" + concatStringsSep "," (mapAttrsToList (name: opt: "${builtins.toJSON name}: ${convertOption opt}") opt) + "}" + else + throw "Invalid option type"; + + configFile = pkgs.writeText "config.py" (concatStringsSep "\n" (mapAttrsToList (name: opt: "${name} = ${convertOption opt}") configOpts)); + +in + +{ + + ###### interface + + options = { + + services.mtprotoproxy = { + + enable = mkEnableOption "mtprotoproxy"; + + port = mkOption { + type = types.int; + default = 3256; + description = '' + TCP port to accept mtproto connections on. + ''; + }; + + users = mkOption { + type = types.attrsOf types.str; + example = { + tg = "00000000000000000000000000000000"; + tg2 = "0123456789abcdef0123456789abcdef"; + }; + description = '' + Allowed users and their secrets. A secret is a 32 characters long hex string. + ''; + }; + + secureOnly = mkOption { + type = types.bool; + default = true; + description = '' + Don't allow users to connect in non-secure mode (without random padding). + ''; + }; + + adTag = mkOption { + type = types.nullOr types.str; + default = null; + # Taken from mtproxyproto's repo. + example = "3c09c680b76ee91a4c25ad51f742267d"; + description = '' + Tag for advertising that can be obtained from @MTProxybot. + ''; + }; + + extraConfig = mkOption { + type = types.attrs; + default = {}; + example = { + STATS_PRINT_PERIOD = 600; + }; + description = '' + Extra configuration options for mtprotoproxy. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.mtprotoproxy = { + description = "MTProto Proxy Daemon"; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${pkgs.mtprotoproxy}/bin/mtprotoproxy ${configFile}"; + DynamicUser = true; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix new file mode 100644 index 000000000000..cc98414257ca --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mullvad-vpn.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.mullvad-vpn; +in +with lib; +{ + options.services.mullvad-vpn.enable = mkOption { + type = types.bool; + default = false; + description = '' + This option enables Mullvad VPN daemon. + ''; + }; + + config = mkIf cfg.enable { + boot.kernelModules = [ "tun" ]; + + systemd.services.mullvad-daemon = { + description = "Mullvad VPN daemon"; + wantedBy = [ "multi-user.target" ]; + wants = [ "network.target" ]; + after = [ + "network-online.target" + "NetworkManager.service" + "systemd-resolved.service" + ]; + path = [ + pkgs.iproute + # Needed for ping + "/run/wrappers" + ]; + serviceConfig = { + StartLimitBurst = 5; + StartLimitIntervalSec = 20; + ExecStart = "${pkgs.mullvad-vpn}/bin/mullvad-daemon -v --disable-stdout-timestamps"; + Restart = "always"; + RestartSec = 1; + }; + }; + }; + + meta.maintainers = [ maintainers.xfix ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/murmur.nix b/nixpkgs/nixos/modules/services/networking/murmur.nix new file mode 100644 index 000000000000..3054ae1b201f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/murmur.nix @@ -0,0 +1,270 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.murmur; + forking = cfg.logFile != null; + configFile = pkgs.writeText "murmurd.ini" '' + database=/var/lib/murmur/murmur.sqlite + dbDriver=QSQLITE + + autobanAttempts=${toString cfg.autobanAttempts} + autobanTimeframe=${toString cfg.autobanTimeframe} + autobanTime=${toString cfg.autobanTime} + + logfile=${optionalString (cfg.logFile != null) cfg.logFile} + ${optionalString forking "pidfile=/run/murmur/murmurd.pid"} + + welcometext="${cfg.welcometext}" + port=${toString cfg.port} + + ${if cfg.hostName == "" then "" else "host="+cfg.hostName} + ${if cfg.password == "" then "" else "serverpassword="+cfg.password} + + bandwidth=${toString cfg.bandwidth} + users=${toString cfg.users} + + textmessagelength=${toString cfg.textMsgLength} + imagemessagelength=${toString cfg.imgMsgLength} + allowhtml=${boolToString cfg.allowHtml} + logdays=${toString cfg.logDays} + bonjour=${boolToString cfg.bonjour} + sendversion=${boolToString cfg.sendVersion} + + ${if cfg.registerName == "" then "" else "registerName="+cfg.registerName} + ${if cfg.registerPassword == "" then "" else "registerPassword="+cfg.registerPassword} + ${if cfg.registerUrl == "" then "" else "registerUrl="+cfg.registerUrl} + ${if cfg.registerHostname == "" then "" else "registerHostname="+cfg.registerHostname} + + certrequired=${boolToString cfg.clientCertRequired} + ${if cfg.sslCert == "" then "" else "sslCert="+cfg.sslCert} + ${if cfg.sslKey == "" then "" else "sslKey="+cfg.sslKey} + ${if cfg.sslCa == "" then "" else "sslCA="+cfg.sslCa} + + ${cfg.extraConfig} + ''; +in +{ + imports = [ + (mkRenamedOptionModule [ "services" "murmur" "welcome" ] [ "services" "murmur" "welcometext" ]) + (mkRemovedOptionModule [ "services" "murmur" "pidfile" ] "Hardcoded to /run/murmur/murmurd.pid now") + ]; + + options = { + services.murmur = { + enable = mkOption { + type = types.bool; + default = false; + description = "If enabled, start the Murmur Mumble server."; + }; + + autobanAttempts = mkOption { + type = types.int; + default = 10; + description = '' + Number of attempts a client is allowed to make in + <literal>autobanTimeframe</literal> seconds, before being + banned for <literal>autobanTime</literal>. + ''; + }; + + autobanTimeframe = mkOption { + type = types.int; + default = 120; + description = '' + Timeframe in which a client can connect without being banned + for repeated attempts (in seconds). + ''; + }; + + autobanTime = mkOption { + type = types.int; + default = 300; + description = "The amount of time an IP ban lasts (in seconds)."; + }; + + logFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/log/murmur/murmurd.log"; + description = "Path to the log file for Murmur daemon. Empty means log to journald."; + }; + + welcometext = mkOption { + type = types.str; + default = ""; + description = "Welcome message for connected clients."; + }; + + port = mkOption { + type = types.int; + default = 64738; + description = "Ports to bind to (UDP and TCP)."; + }; + + hostName = mkOption { + type = types.str; + default = ""; + description = "Host to bind to. Defaults binding on all addresses."; + }; + + password = mkOption { + type = types.str; + default = ""; + description = "Required password to join server, if specified."; + }; + + bandwidth = mkOption { + type = types.int; + default = 72000; + description = '' + Maximum bandwidth (in bits per second) that clients may send + speech at. + ''; + }; + + users = mkOption { + type = types.int; + default = 100; + description = "Maximum number of concurrent clients allowed."; + }; + + textMsgLength = mkOption { + type = types.int; + default = 5000; + description = "Max length of text messages. Set 0 for no limit."; + }; + + imgMsgLength = mkOption { + type = types.int; + default = 131072; + description = "Max length of image messages. Set 0 for no limit."; + }; + + allowHtml = mkOption { + type = types.bool; + default = true; + description = '' + Allow HTML in client messages, comments, and channel + descriptions. + ''; + }; + + logDays = mkOption { + type = types.int; + default = 31; + description = '' + How long to store RPC logs for in the database. Set 0 to + keep logs forever, or -1 to disable DB logging. + ''; + }; + + bonjour = mkOption { + type = types.bool; + default = false; + description = '' + Enable Bonjour auto-discovery, which allows clients over + your LAN to automatically discover Murmur servers. + ''; + }; + + sendVersion = mkOption { + type = types.bool; + default = true; + description = "Send Murmur version in UDP response."; + }; + + registerName = mkOption { + type = types.str; + default = ""; + description = '' + Public server registration name, and also the name of the + Root channel. Even if you don't publicly register your + server, you probably still want to set this. + ''; + }; + + registerPassword = mkOption { + type = types.str; + default = ""; + description = '' + Public server registry password, used authenticate your + server to the registry to prevent impersonation; required for + subsequent registry updates. + ''; + }; + + registerUrl = mkOption { + type = types.str; + default = ""; + description = "URL website for your server."; + }; + + registerHostname = mkOption { + type = types.str; + default = ""; + description = '' + DNS hostname where your server can be reached. This is only + needed if you want your server to be accessed by its + hostname and not IP - but the name *must* resolve on the + internet properly. + ''; + }; + + clientCertRequired = mkOption { + type = types.bool; + default = false; + description = "Require clients to authenticate via certificates."; + }; + + sslCert = mkOption { + type = types.str; + default = ""; + description = "Path to your SSL certificate."; + }; + + sslKey = mkOption { + type = types.str; + default = ""; + description = "Path to your SSL key."; + }; + + sslCa = mkOption { + type = types.str; + default = ""; + description = "Path to your SSL CA certificate."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Extra configuration to put into murmur.ini."; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.murmur = { + description = "Murmur Service user"; + home = "/var/lib/murmur"; + createHome = true; + uid = config.ids.uids.murmur; + }; + + systemd.services.murmur = { + description = "Murmur Chat Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target "]; + + serviceConfig = { + # murmurd doesn't fork when logging to the console. + Type = if forking then "forking" else "simple"; + PIDFile = mkIf forking "/run/murmur/murmurd.pid"; + RuntimeDirectory = mkIf forking "murmur"; + User = "murmur"; + ExecStart = "${pkgs.murmur}/bin/murmurd -ini ${configFile}"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/mxisd.nix b/nixpkgs/nixos/modules/services/networking/mxisd.nix new file mode 100644 index 000000000000..482d6ff456b1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mxisd.nix @@ -0,0 +1,127 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + isMa1sd = + package: + lib.hasPrefix "ma1sd" package.name; + + isMxisd = + package: + lib.hasPrefix "mxisd" package.name; + + cfg = config.services.mxisd; + + server = optionalAttrs (cfg.server.name != null) { inherit (cfg.server) name; } + // optionalAttrs (cfg.server.port != null) { inherit (cfg.server) port; }; + + baseConfig = { + matrix.domain = cfg.matrix.domain; + key.path = "${cfg.dataDir}/signing.key"; + storage = { + provider.sqlite.database = if isMa1sd cfg.package + then "${cfg.dataDir}/ma1sd.db" + else "${cfg.dataDir}/mxisd.db"; + }; + } // optionalAttrs (server != {}) { inherit server; }; + + # merges baseConfig and extraConfig into a single file + fullConfig = recursiveUpdate baseConfig cfg.extraConfig; + + configFile = if isMa1sd cfg.package + then pkgs.writeText "ma1sd-config.yaml" (builtins.toJSON fullConfig) + else pkgs.writeText "mxisd-config.yaml" (builtins.toJSON fullConfig); + +in { + options = { + services.mxisd = { + enable = mkEnableOption "matrix federated identity server"; + + package = mkOption { + type = types.package; + default = pkgs.mxisd; + defaultText = "pkgs.mxisd"; + description = "The mxisd/ma1sd package to use"; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/mxisd"; + description = "Where data mxisd/ma1sd uses resides"; + }; + + extraConfig = mkOption { + type = types.attrs; + default = {}; + description = "Extra options merged into the mxisd/ma1sd configuration"; + }; + + matrix = { + + domain = mkOption { + type = types.str; + description = '' + the domain of the matrix homeserver + ''; + }; + + }; + + server = { + + name = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Public hostname of mxisd/ma1sd, if different from the Matrix domain. + ''; + }; + + port = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + HTTP port to listen on (unencrypted) + ''; + }; + + }; + + }; + }; + + config = mkIf cfg.enable { + users.users.mxisd = + { + group = "mxisd"; + home = cfg.dataDir; + createHome = true; + shell = "${pkgs.bash}/bin/bash"; + uid = config.ids.uids.mxisd; + }; + + users.groups.mxisd = + { + gid = config.ids.gids.mxisd; + }; + + systemd.services.mxisd = { + description = "a federated identity server for the matrix ecosystem"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = let + executable = if isMa1sd cfg.package then "ma1sd" else "mxisd"; + in { + Type = "simple"; + User = "mxisd"; + Group = "mxisd"; + ExecStart = "${cfg.package}/bin/${executable} -c ${configFile}"; + WorkingDirectory = cfg.dataDir; + Restart = "on-failure"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/namecoind.nix b/nixpkgs/nixos/modules/services/networking/namecoind.nix new file mode 100644 index 000000000000..6ca99e1321bd --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/namecoind.nix @@ -0,0 +1,199 @@ + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.namecoind; + dataDir = "/var/lib/namecoind"; + useSSL = (cfg.rpc.certificate != null) && (cfg.rpc.key != null); + useRPC = (cfg.rpc.user != null) && (cfg.rpc.password != null); + + listToConf = option: list: + concatMapStrings (value :"${option}=${value}\n") list; + + configFile = pkgs.writeText "namecoin.conf" ('' + server=1 + daemon=0 + txindex=1 + txprevcache=1 + walletpath=${cfg.wallet} + gen=${if cfg.generate then "1" else "0"} + ${listToConf "addnode" cfg.extraNodes} + ${listToConf "connect" cfg.trustedNodes} + '' + optionalString useRPC '' + rpcbind=${cfg.rpc.address} + rpcport=${toString cfg.rpc.port} + rpcuser=${cfg.rpc.user} + rpcpassword=${cfg.rpc.password} + ${listToConf "rpcallowip" cfg.rpc.allowFrom} + '' + optionalString useSSL '' + rpcssl=1 + rpcsslcertificatechainfile=${cfg.rpc.certificate} + rpcsslprivatekeyfile=${cfg.rpc.key} + rpcsslciphers=TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH + ''); + +in + +{ + + ###### interface + + options = { + + services.namecoind = { + + enable = mkEnableOption "namecoind, Namecoin client"; + + wallet = mkOption { + type = types.path; + default = "${dataDir}/wallet.dat"; + description = '' + Wallet file. The ownership of the file has to be + namecoin:namecoin, and the permissions must be 0640. + ''; + }; + + generate = mkOption { + type = types.bool; + default = false; + description = '' + Whether to generate (mine) Namecoins. + ''; + }; + + extraNodes = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of additional peer IP addresses to connect to. + ''; + }; + + trustedNodes = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + List of the only peer IP addresses to connect to. If specified + no other connection will be made. + ''; + }; + + rpc.user = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + User name for RPC connections. + ''; + }; + + rpc.password = mkOption { + type = types.str; + default = null; + description = '' + Password for RPC connections. + ''; + }; + + rpc.address = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + IP address the RPC server will bind to. + ''; + }; + + rpc.port = mkOption { + type = types.int; + default = 8332; + description = '' + Port the RPC server will bind to. + ''; + }; + + rpc.certificate = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/lib/namecoind/server.cert"; + description = '' + Certificate file for securing RPC connections. + ''; + }; + + rpc.key = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/lib/namecoind/server.pem"; + description = '' + Key file for securing RPC connections. + ''; + }; + + + rpc.allowFrom = mkOption { + type = types.listOf types.str; + default = [ "127.0.0.1" ]; + description = '' + List of IP address ranges allowed to use the RPC API. + Wiledcards (*) can be user to specify a range. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.users.namecoin = { + uid = config.ids.uids.namecoin; + description = "Namecoin daemon user"; + home = dataDir; + createHome = true; + }; + + users.groups.namecoin = { + gid = config.ids.gids.namecoin; + }; + + systemd.services.namecoind = { + description = "Namecoind daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = "namecoin"; + Group = "namecoin"; + ExecStart = "${pkgs.namecoind}/bin/namecoind -conf=${configFile} -datadir=${dataDir} -printtoconsole"; + ExecStop = "${pkgs.coreutils}/bin/kill -KILL $MAINPID"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Nice = "10"; + PrivateTmp = true; + TimeoutStopSec = "60s"; + TimeoutStartSec = "2s"; + Restart = "always"; + StartLimitInterval = "120s"; + StartLimitBurst = "5"; + }; + + preStart = optionalString (cfg.wallet != "${dataDir}/wallet.dat") '' + # check wallet file permissions + if [ "$(stat --printf '%u' ${cfg.wallet})" != "${toString config.ids.uids.namecoin}" \ + -o "$(stat --printf '%g' ${cfg.wallet})" != "${toString config.ids.gids.namecoin}" \ + -o "$(stat --printf '%a' ${cfg.wallet})" != "640" ]; then + echo "ERROR: bad ownership or rights on ${cfg.wallet}" >&2 + exit 1 + fi + ''; + + }; + + }; + + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; + +} diff --git a/nixpkgs/nixos/modules/services/networking/nat.nix b/nixpkgs/nixos/modules/services/networking/nat.nix new file mode 100644 index 000000000000..21ae9eb8b6d4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nat.nix @@ -0,0 +1,290 @@ +# This module enables Network Address Translation (NAT). +# XXX: todo: support multiple upstream links +# see http://yesican.chsoft.biz/lartc/MultihomedLinuxNetworking.html + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.networking.nat; + + dest = if cfg.externalIP == null then "-j MASQUERADE" else "-j SNAT --to-source ${cfg.externalIP}"; + + helpers = import ./helpers.nix { inherit config lib; }; + + flushNat = '' + ${helpers} + ip46tables -w -t nat -D PREROUTING -j nixos-nat-pre 2>/dev/null|| true + ip46tables -w -t nat -F nixos-nat-pre 2>/dev/null || true + ip46tables -w -t nat -X nixos-nat-pre 2>/dev/null || true + ip46tables -w -t nat -D POSTROUTING -j nixos-nat-post 2>/dev/null || true + ip46tables -w -t nat -F nixos-nat-post 2>/dev/null || true + ip46tables -w -t nat -X nixos-nat-post 2>/dev/null || true + ip46tables -w -t nat -D OUTPUT -j nixos-nat-out 2>/dev/null || true + ip46tables -w -t nat -F nixos-nat-out 2>/dev/null || true + ip46tables -w -t nat -X nixos-nat-out 2>/dev/null || true + + ${cfg.extraStopCommands} + ''; + + setupNat = '' + ${helpers} + # Create subchain where we store rules + ip46tables -w -t nat -N nixos-nat-pre + ip46tables -w -t nat -N nixos-nat-post + ip46tables -w -t nat -N nixos-nat-out + + # We can't match on incoming interface in POSTROUTING, so + # mark packets coming from the internal interfaces. + ${concatMapStrings (iface: '' + iptables -w -t nat -A nixos-nat-pre \ + -i '${iface}' -j MARK --set-mark 1 + '') cfg.internalInterfaces} + + # NAT the marked packets. + ${optionalString (cfg.internalInterfaces != []) '' + iptables -w -t nat -A nixos-nat-post -m mark --mark 1 \ + ${optionalString (cfg.externalInterface != null) "-o ${cfg.externalInterface}"} ${dest} + ''} + + # NAT packets coming from the internal IPs. + ${concatMapStrings (range: '' + iptables -w -t nat -A nixos-nat-post \ + -s '${range}' ${optionalString (cfg.externalInterface != null) "-o ${cfg.externalInterface}"} ${dest} + '') cfg.internalIPs} + + # NAT from external ports to internal ports. + ${concatMapStrings (fwd: '' + iptables -w -t nat -A nixos-nat-pre \ + -i ${toString cfg.externalInterface} -p ${fwd.proto} \ + --dport ${builtins.toString fwd.sourcePort} \ + -j DNAT --to-destination ${fwd.destination} + + ${concatMapStrings (loopbackip: + let + m = builtins.match "([0-9.]+):([0-9-]+)" fwd.destination; + destinationIP = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else elemAt m 0; + destinationPorts = if (m == null) then throw "bad ip:ports `${fwd.destination}'" else builtins.replaceStrings ["-"] [":"] (elemAt m 1); + in '' + # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from the host itself + iptables -w -t nat -A nixos-nat-out \ + -d ${loopbackip} -p ${fwd.proto} \ + --dport ${builtins.toString fwd.sourcePort} \ + -j DNAT --to-destination ${fwd.destination} + + # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from other hosts behind NAT + iptables -w -t nat -A nixos-nat-pre \ + -d ${loopbackip} -p ${fwd.proto} \ + --dport ${builtins.toString fwd.sourcePort} \ + -j DNAT --to-destination ${fwd.destination} + + iptables -w -t nat -A nixos-nat-post \ + -d ${destinationIP} -p ${fwd.proto} \ + --dport ${destinationPorts} \ + -j SNAT --to-source ${loopbackip} + '') fwd.loopbackIPs} + '') cfg.forwardPorts} + + ${optionalString (cfg.dmzHost != null) '' + iptables -w -t nat -A nixos-nat-pre \ + -i ${toString cfg.externalInterface} -j DNAT \ + --to-destination ${cfg.dmzHost} + ''} + + ${cfg.extraCommands} + + # Append our chains to the nat tables + ip46tables -w -t nat -A PREROUTING -j nixos-nat-pre + ip46tables -w -t nat -A POSTROUTING -j nixos-nat-post + ip46tables -w -t nat -A OUTPUT -j nixos-nat-out + ''; + +in + +{ + + ###### interface + + options = { + + networking.nat.enable = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to enable Network Address Translation (NAT). + ''; + }; + + networking.nat.internalInterfaces = mkOption { + type = types.listOf types.str; + default = []; + example = [ "eth0" ]; + description = + '' + The interfaces for which to perform NAT. Packets coming from + these interface and destined for the external interface will + be rewritten. + ''; + }; + + networking.nat.internalIPs = mkOption { + type = types.listOf types.str; + default = []; + example = [ "192.168.1.0/24" ]; + description = + '' + The IP address ranges for which to perform NAT. Packets + coming from these addresses (on any interface) and destined + for the external interface will be rewritten. + ''; + }; + + networking.nat.externalInterface = mkOption { + type = types.nullOr types.str; + default = null; + example = "eth1"; + description = + '' + The name of the external network interface. + ''; + }; + + networking.nat.externalIP = mkOption { + type = types.nullOr types.str; + default = null; + example = "203.0.113.123"; + description = + '' + The public IP address to which packets from the local + network are to be rewritten. If this is left empty, the + IP address associated with the external interface will be + used. + ''; + }; + + networking.nat.forwardPorts = mkOption { + type = with types; listOf (submodule { + options = { + sourcePort = mkOption { + type = types.either types.int (types.strMatching "[[:digit:]]+:[[:digit:]]+"); + example = 8080; + description = "Source port of the external interface; to specify a port range, use a string with a colon (e.g. \"60000:61000\")"; + }; + + destination = mkOption { + type = types.str; + example = "10.0.0.1:80"; + description = "Forward connection to destination ip:port; to specify a port range, use ip:start-end"; + }; + + proto = mkOption { + type = types.str; + default = "tcp"; + example = "udp"; + description = "Protocol of forwarded connection"; + }; + + loopbackIPs = mkOption { + type = types.listOf types.str; + default = []; + example = literalExample ''[ "55.1.2.3" ]''; + description = "Public IPs for NAT reflection; for connections to `loopbackip:sourcePort' from the host itself and from other hosts behind NAT"; + }; + }; + }); + default = []; + example = [ { sourcePort = 8080; destination = "10.0.0.1:80"; proto = "tcp"; } ]; + description = + '' + List of forwarded ports from the external interface to + internal destinations by using DNAT. + ''; + }; + + networking.nat.dmzHost = mkOption { + type = types.nullOr types.str; + default = null; + example = "10.0.0.1"; + description = + '' + The local IP address to which all traffic that does not match any + forwarding rule is forwarded. + ''; + }; + + networking.nat.extraCommands = mkOption { + type = types.lines; + default = ""; + example = "iptables -A INPUT -p icmp -j ACCEPT"; + description = + '' + Additional shell commands executed as part of the nat + initialisation script. + ''; + }; + + networking.nat.extraStopCommands = mkOption { + type = types.lines; + default = ""; + example = "iptables -D INPUT -p icmp -j ACCEPT || true"; + description = + '' + Additional shell commands executed as part of the nat + teardown script. + ''; + }; + + }; + + + ###### implementation + + config = mkMerge [ + { networking.firewall.extraCommands = mkBefore flushNat; } + (mkIf config.networking.nat.enable { + + assertions = [ + { assertion = (cfg.dmzHost != null) -> (cfg.externalInterface != null); + message = "networking.nat.dmzHost requires networking.nat.externalInterface"; + } + { assertion = (cfg.forwardPorts != []) -> (cfg.externalInterface != null); + message = "networking.nat.forwardPorts requires networking.nat.externalInterface"; + } + ]; + + environment.systemPackages = [ pkgs.iptables ]; + + boot = { + kernelModules = [ "nf_nat_ftp" ]; + kernel.sysctl = { + "net.ipv4.conf.all.forwarding" = mkOverride 99 true; + "net.ipv4.conf.default.forwarding" = mkOverride 99 true; + }; + }; + + networking.firewall = mkIf config.networking.firewall.enable { + extraCommands = setupNat; + extraStopCommands = flushNat; + }; + + systemd.services = mkIf (!config.networking.firewall.enable) { nat = { + description = "Network Address Translation"; + wantedBy = [ "network.target" ]; + after = [ "network-pre.target" "systemd-modules-load.service" ]; + path = [ pkgs.iptables ]; + unitConfig.ConditionCapability = "CAP_NET_ADMIN"; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = flushNat + setupNat; + + postStop = flushNat; + }; }; + }) + ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/ndppd.nix b/nixpkgs/nixos/modules/services/networking/ndppd.nix new file mode 100644 index 000000000000..77e979a8a424 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ndppd.nix @@ -0,0 +1,189 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.ndppd; + + render = s: f: concatStringsSep "\n" (mapAttrsToList f s); + prefer = a: b: if a != null then a else b; + + ndppdConf = prefer cfg.configFile (pkgs.writeText "ndppd.conf" '' + route-ttl ${toString cfg.routeTTL} + ${render cfg.proxies (proxyInterfaceName: proxy: '' + proxy ${prefer proxy.interface proxyInterfaceName} { + router ${boolToString proxy.router} + timeout ${toString proxy.timeout} + ttl ${toString proxy.ttl} + ${render proxy.rules (ruleNetworkName: rule: '' + rule ${prefer rule.network ruleNetworkName} { + ${rule.method}${if rule.method == "iface" then " ${rule.interface}" else ""} + }'')} + }'')} + ''); + + proxy = types.submodule { + options = { + interface = mkOption { + type = types.nullOr types.str; + description = '' + Listen for any Neighbor Solicitation messages on this interface, + and respond to them according to a set of rules. + Defaults to the name of the attrset. + ''; + default = null; + }; + router = mkOption { + type = types.bool; + description = '' + Turns on or off the router flag for Neighbor Advertisement Messages. + ''; + default = true; + }; + timeout = mkOption { + type = types.int; + description = '' + Controls how long to wait for a Neighbor Advertisment Message before + invalidating the entry, in milliseconds. + ''; + default = 500; + }; + ttl = mkOption { + type = types.int; + description = '' + Controls how long a valid or invalid entry remains in the cache, in + milliseconds. + ''; + default = 30000; + }; + rules = mkOption { + type = types.attrsOf rule; + description = '' + This is a rule that the target address is to match against. If no netmask + is provided, /128 is assumed. You may have several rule sections, and the + addresses may or may not overlap. + ''; + default = {}; + }; + }; + }; + + rule = types.submodule { + options = { + network = mkOption { + type = types.nullOr types.str; + description = '' + This is the target address is to match against. If no netmask + is provided, /128 is assumed. The addresses of serveral rules + may or may not overlap. + Defaults to the name of the attrset. + ''; + default = null; + }; + method = mkOption { + type = types.enum [ "static" "iface" "auto" ]; + description = '' + static: Immediately answer any Neighbor Solicitation Messages + (if they match the IP rule). + iface: Forward the Neighbor Solicitation Message through the specified + interface and only respond if a matching Neighbor Advertisement + Message is received. + auto: Same as iface, but instead of manually specifying the outgoing + interface, check for a matching route in /proc/net/ipv6_route. + ''; + default = "auto"; + }; + interface = mkOption { + type = types.nullOr types.str; + description = "Interface to use when method is iface."; + default = null; + }; + }; + }; + +in { + options.services.ndppd = { + enable = mkEnableOption "daemon that proxies NDP (Neighbor Discovery Protocol) messages between interfaces"; + interface = mkOption { + type = types.nullOr types.str; + description = '' + Interface which is on link-level with router. + (Legacy option, use services.ndppd.proxies.<interface>.rules.<network> instead) + ''; + default = null; + example = "eth0"; + }; + network = mkOption { + type = types.nullOr types.str; + description = '' + Network that we proxy. + (Legacy option, use services.ndppd.proxies.<interface>.rules.<network> instead) + ''; + default = null; + example = "1111::/64"; + }; + configFile = mkOption { + type = types.nullOr types.path; + description = "Path to configuration file."; + default = null; + }; + routeTTL = mkOption { + type = types.int; + description = '' + This tells 'ndppd' how often to reload the route file /proc/net/ipv6_route, + in milliseconds. + ''; + default = 30000; + }; + proxies = mkOption { + type = types.attrsOf proxy; + description = '' + This sets up a listener, that will listen for any Neighbor Solicitation + messages, and respond to them according to a set of rules. + ''; + default = {}; + example = literalExample '' + { + eth0.rules."1111::/64" = {}; + } + ''; + }; + }; + + config = mkIf cfg.enable { + warnings = mkIf (cfg.interface != null && cfg.network != null) [ '' + The options services.ndppd.interface and services.ndppd.network will probably be removed soon, + please use services.ndppd.proxies.<interface>.rules.<network> instead. + '' ]; + + services.ndppd.proxies = mkIf (cfg.interface != null && cfg.network != null) { + ${cfg.interface}.rules.${cfg.network} = {}; + }; + + systemd.services.ndppd = { + description = "NDP Proxy Daemon"; + documentation = [ "man:ndppd(1)" "man:ndppd.conf(5)" ]; + after = [ "network-pre.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${pkgs.ndppd}/bin/ndppd -c ${ndppdConf}"; + + # Sandboxing + CapabilityBoundingSet = "CAP_NET_RAW CAP_NET_ADMIN"; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictAddressFamilies = "AF_INET6 AF_PACKET AF_NETLINK"; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix new file mode 100644 index 000000000000..cc789897b29f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix @@ -0,0 +1,491 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.networking.networkmanager; + + basePackages = with pkgs; [ + crda + modemmanager + networkmanager + networkmanager-fortisslvpn + networkmanager-iodine + networkmanager-l2tp + networkmanager-openconnect + networkmanager-openvpn + networkmanager-vpnc + ] ++ optional (!delegateWireless && !enableIwd) wpa_supplicant; + + delegateWireless = config.networking.wireless.enable == true && cfg.unmanaged != []; + + enableIwd = cfg.wifi.backend == "iwd"; + + configFile = pkgs.writeText "NetworkManager.conf" '' + [main] + plugins=keyfile + dhcp=${cfg.dhcp} + dns=${cfg.dns} + # If resolvconf is disabled that means that resolv.conf is managed by some other module. + rc-manager=${if config.networking.resolvconf.enable then "resolvconf" else "unmanaged"} + + [keyfile] + ${optionalString (cfg.unmanaged != []) + ''unmanaged-devices=${lib.concatStringsSep ";" cfg.unmanaged}''} + + [logging] + level=${cfg.logLevel} + audit=${lib.boolToString config.security.audit.enable} + + [connection] + ipv6.ip6-privacy=2 + ethernet.cloned-mac-address=${cfg.ethernet.macAddress} + wifi.cloned-mac-address=${cfg.wifi.macAddress} + ${optionalString (cfg.wifi.powersave != null) + ''wifi.powersave=${if cfg.wifi.powersave then "3" else "2"}''} + + [device] + wifi.scan-rand-mac-address=${if cfg.wifi.scanRandMacAddress then "yes" else "no"} + wifi.backend=${cfg.wifi.backend} + + ${cfg.extraConfig} + ''; + + /* + [network-manager] + Identity=unix-group:networkmanager + Action=org.freedesktop.NetworkManager.* + ResultAny=yes + ResultInactive=no + ResultActive=yes + + [modem-manager] + Identity=unix-group:networkmanager + Action=org.freedesktop.ModemManager* + ResultAny=yes + ResultInactive=no + ResultActive=yes + */ + polkitConf = '' + polkit.addRule(function(action, subject) { + if ( + subject.isInGroup("networkmanager") + && (action.id.indexOf("org.freedesktop.NetworkManager.") == 0 + || action.id.indexOf("org.freedesktop.ModemManager") == 0 + )) + { return polkit.Result.YES; } + }); + ''; + + ns = xs: pkgs.writeText "nameservers" ( + concatStrings (map (s: "nameserver ${s}\n") xs) + ); + + overrideNameserversScript = pkgs.writeScript "02overridedns" '' + #!/bin/sh + PATH=${with pkgs; makeBinPath [ gnused gnugrep coreutils ]} + tmp=$(mktemp) + sed '/nameserver /d' /etc/resolv.conf > $tmp + grep 'nameserver ' /etc/resolv.conf | \ + grep -vf ${ns (cfg.appendNameservers ++ cfg.insertNameservers)} > $tmp.ns + cat $tmp ${ns cfg.insertNameservers} $tmp.ns ${ns cfg.appendNameservers} > /etc/resolv.conf + rm -f $tmp $tmp.ns + ''; + + dispatcherTypesSubdirMap = { + basic = ""; + pre-up = "pre-up.d/"; + pre-down = "pre-down.d/"; + }; + + macAddressOpt = mkOption { + type = types.either types.str (types.enum ["permanent" "preserve" "random" "stable"]); + default = "preserve"; + example = "00:11:22:33:44:55"; + description = '' + Set the MAC address of the interface. + <variablelist> + <varlistentry> + <term>"XX:XX:XX:XX:XX:XX"</term> + <listitem><para>MAC address of the interface</para></listitem> + </varlistentry> + <varlistentry> + <term><literal>"permanent"</literal></term> + <listitem><para>Use the permanent MAC address of the device</para></listitem> + </varlistentry> + <varlistentry> + <term><literal>"preserve"</literal></term> + <listitem><para>Don’t change the MAC address of the device upon activation</para></listitem> + </varlistentry> + <varlistentry> + <term><literal>"random"</literal></term> + <listitem><para>Generate a randomized value upon each connect</para></listitem> + </varlistentry> + <varlistentry> + <term><literal>"stable"</literal></term> + <listitem><para>Generate a stable, hashed MAC address</para></listitem> + </varlistentry> + </variablelist> + ''; + }; + +in { + + meta = { + maintainers = teams.freedesktop.members; + }; + + ###### interface + + options = { + + networking.networkmanager = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to use NetworkManager to obtain an IP address and other + configuration for all network interfaces that are not manually + configured. If enabled, a group <literal>networkmanager</literal> + will be created. Add all users that should have permission + to change network settings to this group. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Configuration appended to the generated NetworkManager.conf. + Refer to + <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html"> + https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html + </link> + or + <citerefentry> + <refentrytitle>NetworkManager.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + for more information. + ''; + }; + + unmanaged = mkOption { + type = types.listOf types.str; + default = []; + description = '' + List of interfaces that will not be managed by NetworkManager. + Interface name can be specified here, but if you need more fidelity, + refer to + <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec"> + https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html#device-spec + </link> + or the "Device List Format" Appendix of + <citerefentry> + <refentrytitle>NetworkManager.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry>. + ''; + }; + + packages = mkOption { + type = types.listOf types.package; + default = [ ]; + description = '' + Extra packages that provide NetworkManager plugins. + ''; + apply = list: basePackages ++ list; + }; + + dhcp = mkOption { + type = types.enum [ "dhclient" "dhcpcd" "internal" ]; + default = "internal"; + description = '' + Which program (or internal library) should be used for DHCP. + ''; + }; + + logLevel = mkOption { + type = types.enum [ "OFF" "ERR" "WARN" "INFO" "DEBUG" "TRACE" ]; + default = "WARN"; + description = '' + Set the default logging verbosity level. + ''; + }; + + appendNameservers = mkOption { + type = types.listOf types.str; + default = []; + description = '' + A list of name servers that should be appended + to the ones configured in NetworkManager or received by DHCP. + ''; + }; + + insertNameservers = mkOption { + type = types.listOf types.str; + default = []; + description = '' + A list of name servers that should be inserted before + the ones configured in NetworkManager or received by DHCP. + ''; + }; + + ethernet.macAddress = macAddressOpt; + + wifi = { + macAddress = macAddressOpt; + + backend = mkOption { + type = types.enum [ "wpa_supplicant" "iwd" ]; + default = "wpa_supplicant"; + description = '' + Specify the Wi-Fi backend used for the device. + Currently supported are <option>wpa_supplicant</option> or <option>iwd</option> (experimental). + ''; + }; + + powersave = mkOption { + type = types.nullOr types.bool; + default = null; + description = '' + Whether to enable Wi-Fi power saving. + ''; + }; + + scanRandMacAddress = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable MAC address randomization of a Wi-Fi device + during scanning. + ''; + }; + }; + + dns = mkOption { + type = types.enum [ "default" "dnsmasq" "unbound" "systemd-resolved" "none" ]; + default = "default"; + description = '' + Set the DNS (<literal>resolv.conf</literal>) processing mode. + </para> + <para> + A description of these modes can be found in the main section of + <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html"> + https://developer.gnome.org/NetworkManager/stable/NetworkManager.conf.html + </link> + or in + <citerefentry> + <refentrytitle>NetworkManager.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry>. + ''; + }; + + dispatcherScripts = mkOption { + type = types.listOf (types.submodule { + options = { + source = mkOption { + type = types.path; + description = '' + Path to the hook script. + ''; + }; + + type = mkOption { + type = types.enum (attrNames dispatcherTypesSubdirMap); + default = "basic"; + description = '' + Dispatcher hook type. Look up the hooks described at + <link xlink:href="https://developer.gnome.org/NetworkManager/stable/NetworkManager.html">https://developer.gnome.org/NetworkManager/stable/NetworkManager.html</link> + and choose the type depending on the output folder. + You should then filter the event type (e.g., "up"/"down") from within your script. + ''; + }; + }; + }); + default = []; + example = literalExample '' + [ { + source = pkgs.writeText "upHook" ''' + + if [ "$2" != "up" ]; then + logger "exit: event $2 != up" + exit + fi + + # coreutils and iproute are in PATH too + logger "Device $DEVICE_IFACE coming up" + '''; + type = "basic"; + } ]''; + description = '' + A list of scripts which will be executed in response to network events. + ''; + }; + + enableStrongSwan = mkOption { + type = types.bool; + default = false; + description = '' + Enable the StrongSwan plugin. + </para><para> + If you enable this option the + <literal>networkmanager_strongswan</literal> plugin will be added to + the <option>networking.networkmanager.packages</option> option + so you don't need to to that yourself. + ''; + }; + }; + }; + + imports = [ + (mkRenamedOptionModule [ "networking" "networkmanager" "useDnsmasq" ] [ "networking" "networkmanager" "dns" ]) + (mkRemovedOptionModule ["networking" "networkmanager" "dynamicHosts"] '' + This option was removed because allowing (multiple) regular users to + override host entries affecting the whole system opens up a huge attack + vector. There seem to be very rare cases where this might be useful. + Consider setting system-wide host entries using networking.hosts, provide + them via the DNS server in your network, or use environment.etc + to add a file into /etc/NetworkManager/dnsmasq.d reconfiguring hostsdir. + '') + ]; + + + ###### implementation + + config = mkIf cfg.enable { + + assertions = [ + { assertion = config.networking.wireless.enable == true -> cfg.unmanaged != []; + message = '' + You can not use networking.networkmanager with networking.wireless. + Except if you mark some interfaces as <literal>unmanaged</literal> by NetworkManager. + ''; + } + ]; + + environment.etc = with pkgs; { + "NetworkManager/NetworkManager.conf".source = configFile; + + "NetworkManager/VPN/nm-openvpn-service.name".source = + "${networkmanager-openvpn}/lib/NetworkManager/VPN/nm-openvpn-service.name"; + + "NetworkManager/VPN/nm-vpnc-service.name".source = + "${networkmanager-vpnc}/lib/NetworkManager/VPN/nm-vpnc-service.name"; + + "NetworkManager/VPN/nm-openconnect-service.name".source = + "${networkmanager-openconnect}/lib/NetworkManager/VPN/nm-openconnect-service.name"; + + "NetworkManager/VPN/nm-fortisslvpn-service.name".source = + "${networkmanager-fortisslvpn}/lib/NetworkManager/VPN/nm-fortisslvpn-service.name"; + + "NetworkManager/VPN/nm-l2tp-service.name".source = + "${networkmanager-l2tp}/lib/NetworkManager/VPN/nm-l2tp-service.name"; + + "NetworkManager/VPN/nm-iodine-service.name".source = + "${networkmanager-iodine}/lib/NetworkManager/VPN/nm-iodine-service.name"; + } + // optionalAttrs (cfg.appendNameservers != [] || cfg.insertNameservers != []) + { + "NetworkManager/dispatcher.d/02overridedns".source = overrideNameserversScript; + } + // optionalAttrs cfg.enableStrongSwan + { + "NetworkManager/VPN/nm-strongswan-service.name".source = + "${pkgs.networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name"; + } + // listToAttrs (lib.imap1 (i: s: + { + name = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}"; + value = { mode = "0544"; inherit (s) source; }; + }) cfg.dispatcherScripts); + + environment.systemPackages = cfg.packages; + + users.groups = { + networkmanager.gid = config.ids.gids.networkmanager; + nm-openvpn.gid = config.ids.gids.nm-openvpn; + }; + + users.users = { + nm-openvpn = { + uid = config.ids.uids.nm-openvpn; + extraGroups = [ "networkmanager" ]; + }; + nm-iodine = { + isSystemUser = true; + group = "networkmanager"; + }; + }; + + systemd.packages = cfg.packages; + + systemd.tmpfiles.rules = [ + "d /etc/NetworkManager/system-connections 0700 root root -" + "d /etc/ipsec.d 0700 root root -" + "d /var/lib/NetworkManager-fortisslvpn 0700 root root -" + + "d /var/lib/dhclient 0755 root root -" + "d /var/lib/misc 0755 root root -" # for dnsmasq.leases + ]; + + systemd.services.NetworkManager = { + wantedBy = [ "network.target" ]; + restartTriggers = [ configFile ]; + + aliases = [ "dbus-org.freedesktop.NetworkManager.service" ]; + + serviceConfig = { + StateDirectory = "NetworkManager"; + StateDirectoryMode = 755; # not sure if this really needs to be 755 + }; + }; + + systemd.services.NetworkManager-wait-online = { + wantedBy = [ "network-online.target" ]; + }; + + systemd.services.ModemManager.aliases = [ "dbus-org.freedesktop.ModemManager1.service" ]; + + # override unit as recommended by upstream - see https://github.com/NixOS/nixpkgs/issues/88089 + # TODO: keep an eye on modem-manager releases as this will eventually be added to the upstream unit + systemd.services.ModemManager.serviceConfig.ExecStart = [ + "" + "${pkgs.modemmanager}/sbin/ModemManager --filter-policy=STRICT" + ]; + + systemd.services.NetworkManager-dispatcher = { + wantedBy = [ "network.target" ]; + restartTriggers = [ configFile ]; + + # useful binaries for user-specified hooks + path = [ pkgs.iproute pkgs.utillinux pkgs.coreutils ]; + aliases = [ "dbus-org.freedesktop.nm-dispatcher.service" ]; + }; + + # Turn off NixOS' network management when networking is managed entirely by NetworkManager + networking = mkMerge [ + (mkIf (!delegateWireless) { + useDHCP = false; + }) + + (mkIf cfg.enableStrongSwan { + networkmanager.packages = [ pkgs.networkmanager_strongswan ]; + }) + + (mkIf enableIwd { + wireless.iwd.enable = true; + }) + ]; + + security.polkit.extraConfig = polkitConf; + + services.dbus.packages = cfg.packages + ++ optional cfg.enableStrongSwan pkgs.strongswanNM + ++ optional (cfg.dns == "dnsmasq") pkgs.dnsmasq; + + services.udev.packages = cfg.packages; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nftables.nix b/nixpkgs/nixos/modules/services/networking/nftables.nix new file mode 100644 index 000000000000..ec9d9753cfe2 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nftables.nix @@ -0,0 +1,136 @@ +{ config, pkgs, lib, ... }: +with lib; +let + cfg = config.networking.nftables; +in +{ + ###### interface + + options = { + networking.nftables.enable = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to enable nftables. nftables is a Linux-based packet + filtering framework intended to replace frameworks like iptables. + + This conflicts with the standard networking firewall, so make sure to + disable it before using nftables. + + Note that if you have Docker enabled you will not be able to use + nftables without intervention. Docker uses iptables internally to + setup NAT for containers. This module disables the ip_tables kernel + module, however Docker automatically loads the module. Please see [1] + for more information. + + There are other programs that use iptables internally too, such as + libvirt. + + [1]: https://github.com/NixOS/nixpkgs/issues/24318#issuecomment-289216273 + ''; + }; + networking.nftables.ruleset = mkOption { + type = types.lines; + example = '' + # Check out https://wiki.nftables.org/ for better documentation. + # Table for both IPv4 and IPv6. + table inet filter { + # Block all incomming connections traffic except SSH and "ping". + chain input { + type filter hook input priority 0; + + # accept any localhost traffic + iifname lo accept + + # accept traffic originated from us + ct state {established, related} accept + + # ICMP + # routers may also want: mld-listener-query, nd-router-solicit + ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept + ip protocol icmp icmp type { destination-unreachable, router-advertisement, time-exceeded, parameter-problem } accept + + # allow "ping" + ip6 nexthdr icmpv6 icmpv6 type echo-request accept + ip protocol icmp icmp type echo-request accept + + # accept SSH connections (required for a server) + tcp dport 22 accept + + # count and drop any other traffic + counter drop + } + + # Allow all outgoing connections. + chain output { + type filter hook output priority 0; + accept + } + + chain forward { + type filter hook forward priority 0; + accept + } + } + ''; + description = + '' + The ruleset to be used with nftables. Should be in a format that + can be loaded using "/bin/nft -f". The ruleset is updated atomically. + ''; + }; + networking.nftables.rulesetFile = mkOption { + type = types.path; + default = pkgs.writeTextFile { + name = "nftables-rules"; + text = cfg.ruleset; + }; + description = + '' + The ruleset file to be used with nftables. Should be in a format that + can be loaded using "nft -f". The ruleset is updated atomically. + ''; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + assertions = [{ + assertion = config.networking.firewall.enable == false; + message = "You can not use nftables with services.networking.firewall."; + }]; + boot.blacklistedKernelModules = [ "ip_tables" ]; + environment.systemPackages = [ pkgs.nftables ]; + systemd.services.nftables = { + description = "nftables firewall"; + before = [ "network-pre.target" ]; + wants = [ "network-pre.target" ]; + wantedBy = [ "multi-user.target" ]; + reloadIfChanged = true; + serviceConfig = let + rulesScript = pkgs.writeScript "nftables-rules" '' + #! ${pkgs.nftables}/bin/nft -f + flush ruleset + include "${cfg.rulesetFile}" + ''; + checkScript = pkgs.writeScript "nftables-check" '' + #! ${pkgs.runtimeShell} -e + if $(${pkgs.kmod}/bin/lsmod | grep -q ip_tables); then + echo "Unload ip_tables before using nftables!" 1>&2 + exit 1 + else + ${rulesScript} + fi + ''; + in { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = checkScript; + ExecReload = checkScript; + ExecStop = "${pkgs.nftables}/bin/nft flush ruleset"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix new file mode 100644 index 000000000000..6523f4b8b9e0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix @@ -0,0 +1,131 @@ +{ lib, ...}: +{ options = { + proto = lib.mkOption { + type = lib.types.enum [ "h2" "http/1.1" ]; + default = "http/1.1"; + description = '' + This option configures the protocol the backend server expects + to use. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + tls = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + This option determines whether nghttpx will negotiate its + connection with a backend server using TLS or not. The burden + is on the backend server to provide the TLS certificate! + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + sni = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + Override the TLS SNI field value. This value (in nghttpx) + defaults to the host value of the backend configuration. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + fall = lib.mkOption { + type = lib.types.int; + default = 0; + description = '' + If nghttpx cannot connect to the backend N times in a row, the + backend is assumed to be offline and is excluded from load + balancing. If N is 0 the backend is never excluded from load + balancing. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + rise = lib.mkOption { + type = lib.types.int; + default = 0; + description = '' + If the backend is excluded from load balancing, nghttpx will + periodically attempt to make a connection to the backend. If + the connection is successful N times in a row the backend is + re-included in load balancing. If N is 0 a backend is never + reconsidered for load balancing once it falls. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + affinity = lib.mkOption { + type = lib.types.enum [ "ip" "none" ]; + default = "none"; + description = '' + If "ip" is given, client IP based session affinity is + enabled. If "none" is given, session affinity is disabled. + + Session affinity is enabled (by nghttpx) per-backend + pattern. If at least one backend has a non-"none" affinity, + then session affinity is enabled for all backend servers + sharing the same pattern. + + It is advised to set affinity on all backends explicitly if + session affinity is desired. The session affinity may break if + one of the backend gets unreachable, or backend settings are + reloaded or replaced by API. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + dns = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Name resolution of a backends host name is done at start up, + or configuration reload. If "dns" is true, name resolution + takes place dynamically. + + This is useful if a backends address changes frequently. If + "dns" is true, name resolution of a backend's host name at + start up, or configuration reload is skipped. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + redirect-if-not-tls = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + If true, a backend match requires the frontend connection be + TLS encrypted. If it is not, nghttpx responds to the request + with a 308 status code and https URI the client should use + instead in the Location header. + + The port number in the redirect URI is 443 by default and can + be changed using 'services.nghttpx.redirect-https-port' + option. + + If at least one backend has "redirect-if-not-tls" set to true, + this feature is enabled for all backend servers with the same + pattern. It is advised to set "redirect-if-no-tls" parameter + to all backends explicitly if this feature is desired. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/backend-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/backend-submodule.nix new file mode 100644 index 000000000000..eb559e926e76 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nghttpx/backend-submodule.nix @@ -0,0 +1,50 @@ +{ lib, ... }: +{ options = { + server = lib.mkOption { + type = + lib.types.either + (lib.types.submodule (import ./server-options.nix)) + (lib.types.path); + example = { + host = "127.0.0.1"; + port = 8888; + }; + default = { + host = "127.0.0.1"; + port = 80; + }; + description = '' + Backend server location specified as either a host:port pair + or a unix domain docket. + ''; + }; + + patterns = lib.mkOption { + type = lib.types.listOf lib.types.str; + example = [ + "*.host.net/v1/" + "host.org/v2/mypath" + "/somepath" + ]; + default = []; + description = '' + List of nghttpx backend patterns. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more information on the pattern syntax and nghttpxs behavior. + ''; + }; + + params = lib.mkOption { + type = lib.types.nullOr (lib.types.submodule (import ./backend-params-submodule.nix)); + example = { + proto = "h2"; + tls = true; + }; + default = null; + description = '' + Parameters to configure a backend. + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix new file mode 100644 index 000000000000..881a2670f5db --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix @@ -0,0 +1,118 @@ +{config, pkgs, lib, ...}: +let + cfg = config.services.nghttpx; + + # renderHost :: Either ServerOptions Path -> String + renderHost = server: + if builtins.isString server + then "unix://${server}" + else "${server.host},${builtins.toString server.port}"; + + # Filter out submodule parameters whose value is null or false or is + # the key _module. + # + # filterParams :: ParamsSubmodule -> ParamsSubmodule + filterParams = p: + lib.filterAttrs + (n: v: ("_module" != n) && (null != v) && (false != v)) + (lib.optionalAttrs (null != p) p); + + # renderBackend :: BackendSubmodule -> String + renderBackend = backend: + let + host = renderHost backend.server; + patterns = lib.concatStringsSep ":" backend.patterns; + + # Render a set of backend parameters, this is somewhat + # complicated because nghttpx backend patterns can be entirely + # omitted and the params may be given as a mixed collection of + # 'key=val' pairs or atoms (e.g: 'proto=h2;tls') + params = + lib.mapAttrsToList + (n: v: + if builtins.isBool v + then n + else if builtins.isString v + then "${n}=${v}" + else "${n}=${builtins.toString v}") + (filterParams backend.params); + + # NB: params are delimited by a ";" which is the same delimiter + # to separate the host;[pattern];[params] sections of a backend + sections = + builtins.filter (e: "" != e) ([ + host + patterns + ]++params); + formattedSections = lib.concatStringsSep ";" sections; + in + "backend=${formattedSections}"; + + # renderFrontend :: FrontendSubmodule -> String + renderFrontend = frontend: + let + host = renderHost frontend.server; + params0 = + lib.mapAttrsToList + (n: v: if builtins.isBool v then n else v) + (filterParams frontend.params); + + # NB: nghttpx doesn't accept "tls", you must omit "no-tls" for + # the default behavior of turning on TLS. + params1 = lib.remove "tls" params0; + + sections = [ host] ++ params1; + formattedSections = lib.concatStringsSep ";" sections; + in + "frontend=${formattedSections}"; + + configurationFile = pkgs.writeText "nghttpx.conf" '' + ${lib.optionalString (null != cfg.tls) ("private-key-file="+cfg.tls.key)} + ${lib.optionalString (null != cfg.tls) ("certificate-file="+cfg.tls.crt)} + + user=nghttpx + + ${lib.concatMapStringsSep "\n" renderFrontend cfg.frontends} + ${lib.concatMapStringsSep "\n" renderBackend cfg.backends} + + backlog=${builtins.toString cfg.backlog} + backend-address-family=${cfg.backend-address-family} + + workers=${builtins.toString cfg.workers} + rlimit-nofile=${builtins.toString cfg.rlimit-nofile} + + ${lib.optionalString cfg.single-thread "single-thread=yes"} + ${lib.optionalString cfg.single-process "single-process=yes"} + + ${cfg.extraConfig} + ''; +in +{ imports = [ + ./nghttpx-options.nix + ]; + + config = lib.mkIf cfg.enable { + + users.groups.nghttpx = { }; + users.users.nghttpx = { + group = config.users.groups.nghttpx.name; + isSystemUser = true; + }; + + + systemd.services = { + nghttpx = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + script = '' + ${pkgs.nghttp2}/bin/nghttpx --conf=${configurationFile} + ''; + + serviceConfig = { + Restart = "on-failure"; + RestartSec = 60; + }; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix new file mode 100644 index 000000000000..33c8572bd14f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix @@ -0,0 +1,64 @@ +{ lib, ...}: +{ options = { + tls = lib.mkOption { + type = lib.types.enum [ "tls" "no-tls" ]; + default = "tls"; + description = '' + Enable or disable TLS. If true (enabled) the key and + certificate must be configured for nghttpx. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + + sni-fwd = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + When performing a match to select a backend server, SNI host + name received from the client is used instead of the request + host. See --backend option about the pattern match. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + + api = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable API access for this frontend. This enables you to + dynamically modify nghttpx at run-time therefore this feature + is disabled by default and should be turned on with care. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + + healthmon = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Make this frontend a health monitor endpoint. Any request + received on this frontend is responded to with a 200 OK. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + + proxyproto = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Accept PROXY protocol version 1 on frontend connection. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-submodule.nix new file mode 100644 index 000000000000..887ef4502131 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nghttpx/frontend-submodule.nix @@ -0,0 +1,36 @@ +{ lib, ... }: +{ options = { + server = lib.mkOption { + type = + lib.types.either + (lib.types.submodule (import ./server-options.nix)) + (lib.types.path); + example = { + host = "127.0.0.1"; + port = 8888; + }; + default = { + host = "127.0.0.1"; + port = 80; + }; + description = '' + Frontend server interface binding specification as either a + host:port pair or a unix domain docket. + + NB: a host of "*" listens on all interfaces and includes IPv6 + addresses. + ''; + }; + + params = lib.mkOption { + type = lib.types.nullOr (lib.types.submodule (import ./frontend-params-submodule.nix)); + example = { + tls = "tls"; + }; + default = null; + description = '' + Parameters to configure a backend. + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/nghttpx-options.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/nghttpx-options.nix new file mode 100644 index 000000000000..51f1d081b971 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nghttpx/nghttpx-options.nix @@ -0,0 +1,142 @@ +{ lib, ... }: +{ options.services.nghttpx = { + enable = lib.mkEnableOption "nghttpx"; + + frontends = lib.mkOption { + type = lib.types.listOf (lib.types.submodule (import ./frontend-submodule.nix)); + description = '' + A list of frontend listener specifications. + ''; + example = [ + { server = { + host = "*"; + port = 80; + }; + + params = { + tls = "no-tls"; + }; + } + ]; + }; + + backends = lib.mkOption { + type = lib.types.listOf (lib.types.submodule (import ./backend-submodule.nix)); + description = '' + A list of backend specifications. + ''; + example = [ + { server = { + host = "172.16.0.22"; + port = 8443; + }; + patterns = [ "/" ]; + params = { + proto = "http/1.1"; + redirect-if-not-tls = true; + }; + } + ]; + }; + + tls = lib.mkOption { + type = lib.types.nullOr (lib.types.submodule (import ./tls-submodule.nix)); + default = null; + description = '' + TLS certificate and key paths. Note that this does not enable + TLS for a frontend listener, to do so, a frontend + specification must set <literal>params.tls</literal> to true. + ''; + example = { + key = "/etc/ssl/keys/server.key"; + crt = "/etc/ssl/certs/server.crt"; + }; + }; + + extraConfig = lib.mkOption { + type = lib.types.lines; + default = ""; + description = '' + Extra configuration options to be appended to the generated + configuration file. + ''; + }; + + single-process = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Run this program in a single process mode for debugging + purpose. Without this option, nghttpx creates at least 2 + processes: master and worker processes. If this option is + used, master and worker are unified into a single + process. nghttpx still spawns additional process if neverbleed + is used. In the single process mode, the signal handling + feature is disabled. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--single-process + ''; + }; + + backlog = lib.mkOption { + type = lib.types.int; + default = 65536; + description = '' + Listen backlog size. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--backlog + ''; + }; + + backend-address-family = lib.mkOption { + type = lib.types.enum [ + "auto" + "IPv4" + "IPv6" + ]; + default = "auto"; + description = '' + Specify address family of backend connections. If "auto" is + given, both IPv4 and IPv6 are considered. If "IPv4" is given, + only IPv4 address is considered. If "IPv6" is given, only IPv6 + address is considered. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--backend-address-family + ''; + }; + + workers = lib.mkOption { + type = lib.types.int; + default = 1; + description = '' + Set the number of worker threads. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-n + ''; + }; + + single-thread = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Run everything in one thread inside the worker process. This + feature is provided for better debugging experience, or for + the platforms which lack thread support. If threading is + disabled, this option is always enabled. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--single-thread + ''; + }; + + rlimit-nofile = lib.mkOption { + type = lib.types.int; + default = 0; + description = '' + Set maximum number of open files (RLIMIT_NOFILE) to <N>. If 0 + is given, nghttpx does not set the limit. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--rlimit-nofile + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/server-options.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/server-options.nix new file mode 100644 index 000000000000..ef23bfd793c5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nghttpx/server-options.nix @@ -0,0 +1,18 @@ +{ lib, ... }: +{ options = { + host = lib.mkOption { + type = lib.types.str; + example = "127.0.0.1"; + description = '' + Server host address. + ''; + }; + port = lib.mkOption { + type = lib.types.int; + example = 5088; + description = '' + Server host port. + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nghttpx/tls-submodule.nix b/nixpkgs/nixos/modules/services/networking/nghttpx/tls-submodule.nix new file mode 100644 index 000000000000..8f3cdaae2c81 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nghttpx/tls-submodule.nix @@ -0,0 +1,21 @@ +{lib, ...}: +{ options = { + key = lib.mkOption { + type = lib.types.str; + example = "/etc/ssl/keys/mykeyfile.key"; + default = "/etc/ssl/keys/server.key"; + description = '' + Path to the TLS key file. + ''; + }; + + crt = lib.mkOption { + type = lib.types.str; + example = "/etc/ssl/certs/mycert.crt"; + default = "/etc/ssl/certs/server.crt"; + description = '' + Path to the TLS certificate file. + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/ngircd.nix b/nixpkgs/nixos/modules/services/networking/ngircd.nix new file mode 100644 index 000000000000..4b2fa7795922 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ngircd.nix @@ -0,0 +1,59 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.ngircd; + + configFile = pkgs.stdenv.mkDerivation { + name = "ngircd.conf"; + + text = cfg.config; + + preferLocalBuild = true; + + buildCommand = '' + echo -n "$text" > $out + ${cfg.package}/sbin/ngircd --config $out --configtest + ''; + }; +in { + options = { + services.ngircd = { + enable = mkEnableOption "the ngircd IRC server"; + + config = mkOption { + description = "The ngircd configuration (see ngircd.conf(5))."; + + type = types.lines; + }; + + package = mkOption { + description = "The ngircd package."; + + type = types.package; + + default = pkgs.ngircd; + defaultText = "pkgs.ngircd"; + }; + }; + }; + + config = mkIf cfg.enable { + #!!! TODO: Use ExecReload (see https://github.com/NixOS/nixpkgs/issues/1988) + systemd.services.ngircd = { + description = "The ngircd IRC server"; + + wantedBy = [ "multi-user.target" ]; + + serviceConfig.ExecStart = "${cfg.package}/sbin/ngircd --config ${configFile} --nodaemon"; + + serviceConfig.User = "ngircd"; + }; + + users.users.ngircd = { + uid = config.ids.uids.ngircd; + description = "ngircd user."; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nix-serve.nix b/nixpkgs/nixos/modules/services/networking/nix-serve.nix new file mode 100644 index 000000000000..347d87b3f385 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nix-serve.nix @@ -0,0 +1,81 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.nix-serve; +in +{ + options = { + services.nix-serve = { + enable = mkEnableOption "nix-serve, the standalone Nix binary cache server"; + + port = mkOption { + type = types.int; + default = 5000; + description = '' + Port number where nix-serve will listen on. + ''; + }; + + bindAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + IP address where nix-serve will bind its listening socket. + ''; + }; + + secretKeyFile = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The path to the file used for signing derivation data. + Generate with: + + ``` + nix-store --generate-binary-cache-key key-name secret-key-file public-key-file + ``` + + Make sure user `nix-serve` has read access to the private key file. + + For more details see <citerefentry><refentrytitle>nix-store</refentrytitle><manvolnum>1</manvolnum></citerefentry>. + ''; + }; + + extraParams = mkOption { + type = types.separatedString " "; + default = ""; + description = '' + Extra command line parameters for nix-serve. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.nix-serve = { + description = "nix-serve binary cache server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + path = [ config.nix.package.out pkgs.bzip2.bin ]; + environment.NIX_REMOTE = "daemon"; + environment.NIX_SECRET_KEY_FILE = cfg.secretKeyFile; + + serviceConfig = { + Restart = "always"; + RestartSec = "5s"; + ExecStart = "${pkgs.nix-serve}/bin/nix-serve " + + "--listen ${cfg.bindAddress}:${toString cfg.port} ${cfg.extraParams}"; + User = "nix-serve"; + Group = "nogroup"; + }; + }; + + users.users.nix-serve = { + description = "Nix-serve user"; + uid = config.ids.uids.nix-serve; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nix-store-gcs-proxy.nix b/nixpkgs/nixos/modules/services/networking/nix-store-gcs-proxy.nix new file mode 100644 index 000000000000..3f2ce5bca4da --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nix-store-gcs-proxy.nix @@ -0,0 +1,75 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + opts = { name, config, ... }: { + options = { + enable = mkOption { + default = true; + type = types.bool; + example = true; + description = "Whether to enable proxy for this bucket"; + }; + bucketName = mkOption { + type = types.str; + default = name; + example = "my-bucket-name"; + description = "Name of Google storage bucket"; + }; + address = mkOption { + type = types.str; + example = "localhost:3000"; + description = "The address of the proxy."; + }; + }; + }; + enabledProxies = lib.filterAttrs (n: v: v.enable) config.services.nix-store-gcs-proxy; + mapProxies = function: lib.mkMerge (lib.mapAttrsToList function enabledProxies); +in +{ + options.services.nix-store-gcs-proxy = mkOption { + type = types.attrsOf (types.submodule opts); + default = {}; + description = '' + An attribute set describing an HTTP to GCS proxy that allows us to use GCS + bucket via HTTP protocol. + ''; + }; + + config.systemd.services = mapProxies (name: cfg: { + "nix-store-gcs-proxy-${name}" = { + description = "A HTTP nix store that proxies requests to Google Storage"; + wantedBy = ["multi-user.target"]; + + serviceConfig = { + RestartSec = 5; + StartLimitInterval = 10; + ExecStart = '' + ${pkgs.nix-store-gcs-proxy}/bin/nix-store-gcs-proxy \ + --bucket-name ${cfg.bucketName} \ + --addr ${cfg.address} + ''; + + DynamicUser = true; + + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateUsers = true; + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + + NoNewPrivileges = true; + LockPersonality = true; + RestrictRealtime = true; + }; + }; + }); + + meta.maintainers = [ maintainers.mrkkrp ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/nixops-dns.nix b/nixpkgs/nixos/modules/services/networking/nixops-dns.nix new file mode 100644 index 000000000000..2bb1263b7fa2 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nixops-dns.nix @@ -0,0 +1,79 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + pkg = pkgs.nixops-dns; + cfg = config.services.nixops-dns; +in + +{ + options = { + services.nixops-dns = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the nixops-dns resolution + of NixOps virtual machines via dnsmasq and fake domain name. + ''; + }; + + user = mkOption { + type = types.str; + description = '' + The user the nixops-dns daemon should run as. + This should be the user, which is also used for nixops and + have the .nixops directory in its home. + ''; + }; + + domain = mkOption { + type = types.str; + description = '' + Fake domain name to resolve to NixOps virtual machines. + + For example "ops" will resolve "vm.ops". + ''; + example = "ops"; + default = "ops"; + }; + + dnsmasq = mkOption { + type = types.bool; + default = true; + description = '' + Enable dnsmasq forwarding to nixops-dns. This allows to use + nixops-dns for `services.nixops-dns.domain` resolution + while forwarding the rest of the queries to original resolvers. + ''; + }; + + }; + }; + + config = mkIf cfg.enable { + systemd.services.nixops-dns = { + description = "nixops-dns: DNS server for resolving NixOps machines"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "simple"; + User = cfg.user; + ExecStart="${pkg}/bin/nixops-dns --domain=.${cfg.domain}"; + }; + }; + + services.dnsmasq = mkIf cfg.dnsmasq { + enable = true; + resolveLocalQueries = true; + servers = [ + "/${cfg.domain}/127.0.0.1#5300" + ]; + extraConfig = '' + bind-interfaces + listen-address=127.0.0.1 + ''; + }; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix b/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix new file mode 100644 index 000000000000..cc061bf6e3b9 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix @@ -0,0 +1,234 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (pkgs) nntp-proxy; + + proxyUser = "nntp-proxy"; + + cfg = config.services.nntp-proxy; + + configBool = b: if b then "TRUE" else "FALSE"; + + confFile = pkgs.writeText "nntp-proxy.conf" '' + nntp_server: + { + # NNTP Server host and port address + server = "${cfg.upstreamServer}"; + port = ${toString cfg.upstreamPort}; + # NNTP username + username = "${cfg.upstreamUser}"; + # NNTP password in clear text + password = "${cfg.upstreamPassword}"; + # Maximum number of connections allowed by the NNTP + max_connections = ${toString cfg.upstreamMaxConnections}; + }; + + proxy: + { + # Local address and port to bind to + bind_ip = "${cfg.listenAddress}"; + bind_port = ${toString cfg.port}; + + # SSL key and cert file + ssl_key = "${cfg.sslKey}"; + ssl_cert = "${cfg.sslCert}"; + + # prohibit users from posting + prohibit_posting = ${configBool cfg.prohibitPosting}; + # Verbose levels: ERROR, WARNING, NOTICE, INFO, DEBUG + verbose = "${toUpper cfg.verbosity}"; + # Password is made with: 'mkpasswd -m sha-512 <password>' + users = (${concatStringsSep ",\n" (mapAttrsToList (username: userConfig: + '' + { + username = "${username}"; + password = "${userConfig.passwordHash}"; + max_connections = ${toString userConfig.maxConnections}; + } + '') cfg.users)}); + }; + ''; + +in + +{ + + ###### interface + + options = { + + services.nntp-proxy = { + enable = mkEnableOption "NNTP-Proxy"; + + upstreamServer = mkOption { + type = types.str; + default = ""; + example = "ssl-eu.astraweb.com"; + description = '' + Upstream server address + ''; + }; + + upstreamPort = mkOption { + type = types.int; + default = 563; + description = '' + Upstream server port + ''; + }; + + upstreamMaxConnections = mkOption { + type = types.int; + default = 20; + description = '' + Upstream server maximum allowed concurrent connections + ''; + }; + + upstreamUser = mkOption { + type = types.str; + default = ""; + description = '' + Upstream server username + ''; + }; + + upstreamPassword = mkOption { + type = types.str; + default = ""; + description = '' + Upstream server password + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + example = "[::]"; + description = '' + Proxy listen address (IPv6 literal addresses need to be enclosed in "[" and "]" characters) + ''; + }; + + port = mkOption { + type = types.int; + default = 5555; + description = '' + Proxy listen port + ''; + }; + + sslKey = mkOption { + type = types.str; + default = "key.pem"; + example = "/path/to/your/key.file"; + description = '' + Proxy ssl key path + ''; + }; + + sslCert = mkOption { + type = types.str; + default = "cert.pem"; + example = "/path/to/your/cert.file"; + description = '' + Proxy ssl certificate path + ''; + }; + + prohibitPosting = mkOption { + type = types.bool; + default = true; + description = '' + Whether to prohibit posting to the upstream server + ''; + }; + + verbosity = mkOption { + type = types.enum [ "error" "warning" "notice" "info" "debug" ]; + default = "info"; + example = "error"; + description = '' + Verbosity level + ''; + }; + + users = mkOption { + type = types.attrsOf (types.submodule { + options = { + username = mkOption { + type = types.str; + default = null; + description = '' + Username + ''; + }; + + passwordHash = mkOption { + type = types.str; + default = null; + example = "$6$GtzE7FrpE$wwuVgFYU.TZH4Rz.Snjxk9XGua89IeVwPQ/fEUD8eujr40q5Y021yhn0aNcsQ2Ifw.BLclyzvzgegopgKcneL0"; + description = '' + SHA-512 password hash (can be generated by + <code>mkpasswd -m sha-512 <password></code>) + ''; + }; + + maxConnections = mkOption { + type = types.int; + default = 1; + description = '' + Maximum number of concurrent connections to the proxy for this user + ''; + }; + }; + }); + description = '' + NNTP-Proxy user configuration + ''; + + default = {}; + example = literalExample '' + "user1" = { + passwordHash = "$6$1l0t5Kn2Dk$appzivc./9l/kjq57eg5UCsBKlcfyCr0zNWYNerKoPsI1d7eAwiT0SVsOVx/CTgaBNT/u4fi2vN.iGlPfv1ek0"; + maxConnections = 5; + }; + "anotheruser" = { + passwordHash = "$6$6lwEsWB.TmsS$W7m1riUx4QrA8pKJz8hvff0dnF1NwtZXgdjmGqA1Dx2MDPj07tI9GNcb0SWlMglE.2/hBgynDdAd/XqqtRqVQ0"; + maxConnections = 7; + }; + ''; + }; + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + users.users.${proxyUser} = + { uid = config.ids.uids.nntp-proxy; + description = "NNTP-Proxy daemon user"; + }; + + systemd.services.nntp-proxy = { + description = "NNTP proxy"; + after = [ "network.target" "nss-lookup.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { User="${proxyUser}"; }; + serviceConfig.ExecStart = "${nntp-proxy}/bin/nntp-proxy ${confFile}"; + preStart = '' + if [ ! \( -f ${cfg.sslCert} -a -f ${cfg.sslKey} \) ]; then + ${pkgs.openssl.bin}/bin/openssl req -subj '/CN=AutoGeneratedCert/O=NixOS Service/C=US' \ + -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout ${cfg.sslKey} -out ${cfg.sslCert}; + fi + ''; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/nsd.nix b/nixpkgs/nixos/modules/services/networking/nsd.nix new file mode 100644 index 000000000000..6e3eed0c5570 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nsd.nix @@ -0,0 +1,980 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.nsd; + + username = "nsd"; + stateDir = "/var/lib/nsd"; + pidFile = stateDir + "/var/nsd.pid"; + + # build nsd with the options needed for the given config + nsdPkg = pkgs.nsd.override { + configFile = "${configFile}/nsd.conf"; + + bind8Stats = cfg.bind8Stats; + ipv6 = cfg.ipv6; + ratelimit = cfg.ratelimit.enable; + rootServer = cfg.rootServer; + zoneStats = length (collect (x: (x.zoneStats or null) != null) cfg.zones) > 0; + }; + + mkZoneFileName = name: if name == "." then "root" else name; + + nsdEnv = pkgs.buildEnv { + name = "nsd-env"; + + paths = [ configFile ] + ++ mapAttrsToList (name: zone: writeZoneData name zone.data) zoneConfigs; + + postBuild = '' + echo "checking zone files" + cd $out/zones + + for zoneFile in *; do + echo "|- checking zone '$out/zones/$zoneFile'" + ${nsdPkg}/sbin/nsd-checkzone "$zoneFile" "$zoneFile" || { + if grep -q \\\\\\$ "$zoneFile"; then + echo zone "$zoneFile" contains escaped dollar signes \\\$ + echo Escaping them is not needed any more. Please make shure \ + to unescape them where they prefix a variable name + fi + + exit 1 + } + done + + echo "checking configuration file" + ${nsdPkg}/sbin/nsd-checkconf $out/nsd.conf + ''; + }; + + writeZoneData = name: text: pkgs.writeTextFile { + name = "nsd-zone-${mkZoneFileName name}"; + inherit text; + destination = "/zones/${mkZoneFileName name}"; + }; + + + # options are ordered alphanumerically by the nixos option name + configFile = pkgs.writeTextDir "nsd.conf" '' + server: + chroot: "${stateDir}" + username: ${username} + + # The directory for zonefile: files. The daemon chdirs here. + zonesdir: "${stateDir}" + + # the list of dynamically added zones. + database: "${stateDir}/var/nsd.db" + pidfile: "${pidFile}" + xfrdfile: "${stateDir}/var/xfrd.state" + xfrdir: "${stateDir}/tmp" + zonelistfile: "${stateDir}/var/zone.list" + + # interfaces + ${forEach " ip-address: " cfg.interfaces} + + ip-freebind: ${yesOrNo cfg.ipFreebind} + hide-version: ${yesOrNo cfg.hideVersion} + identity: "${cfg.identity}" + ip-transparent: ${yesOrNo cfg.ipTransparent} + do-ip4: ${yesOrNo cfg.ipv4} + ipv4-edns-size: ${toString cfg.ipv4EDNSSize} + do-ip6: ${yesOrNo cfg.ipv6} + ipv6-edns-size: ${toString cfg.ipv6EDNSSize} + log-time-ascii: ${yesOrNo cfg.logTimeAscii} + ${maybeString "nsid: " cfg.nsid} + port: ${toString cfg.port} + reuseport: ${yesOrNo cfg.reuseport} + round-robin: ${yesOrNo cfg.roundRobin} + server-count: ${toString cfg.serverCount} + ${maybeToString "statistics: " cfg.statistics} + tcp-count: ${toString cfg.tcpCount} + tcp-query-count: ${toString cfg.tcpQueryCount} + tcp-timeout: ${toString cfg.tcpTimeout} + verbosity: ${toString cfg.verbosity} + ${maybeString "version: " cfg.version} + xfrd-reload-timeout: ${toString cfg.xfrdReloadTimeout} + zonefiles-check: ${yesOrNo cfg.zonefilesCheck} + + ${maybeString "rrl-ipv4-prefix-length: " cfg.ratelimit.ipv4PrefixLength} + ${maybeString "rrl-ipv6-prefix-length: " cfg.ratelimit.ipv6PrefixLength} + rrl-ratelimit: ${toString cfg.ratelimit.ratelimit} + ${maybeString "rrl-slip: " cfg.ratelimit.slip} + rrl-size: ${toString cfg.ratelimit.size} + rrl-whitelist-ratelimit: ${toString cfg.ratelimit.whitelistRatelimit} + + ${keyConfigFile} + + remote-control: + control-enable: ${yesOrNo cfg.remoteControl.enable} + control-key-file: "${cfg.remoteControl.controlKeyFile}" + control-cert-file: "${cfg.remoteControl.controlCertFile}" + ${forEach " control-interface: " cfg.remoteControl.interfaces} + control-port: ${toString cfg.remoteControl.port} + server-key-file: "${cfg.remoteControl.serverKeyFile}" + server-cert-file: "${cfg.remoteControl.serverCertFile}" + + ${concatStrings (mapAttrsToList zoneConfigFile zoneConfigs)} + + ${cfg.extraConfig} + ''; + + yesOrNo = b: if b then "yes" else "no"; + maybeString = prefix: x: if x == null then "" else ''${prefix} "${x}"''; + maybeToString = prefix: x: if x == null then "" else ''${prefix} ${toString x}''; + forEach = pre: l: concatMapStrings (x: pre + x + "\n") l; + + + keyConfigFile = concatStrings (mapAttrsToList (keyName: keyOptions: '' + key: + name: "${keyName}" + algorithm: "${keyOptions.algorithm}" + include: "${stateDir}/private/${keyName}" + '') cfg.keys); + + copyKeys = concatStrings (mapAttrsToList (keyName: keyOptions: '' + secret=$(cat "${keyOptions.keyFile}") + dest="${stateDir}/private/${keyName}" + echo " secret: \"$secret\"" > "$dest" + chown ${username}:${username} "$dest" + chmod 0400 "$dest" + '') cfg.keys); + + + # options are ordered alphanumerically by the nixos option name + zoneConfigFile = name: zone: '' + zone: + name: "${name}" + zonefile: "${stateDir}/zones/${mkZoneFileName name}" + ${maybeString "outgoing-interface: " zone.outgoingInterface} + ${forEach " rrl-whitelist: " zone.rrlWhitelist} + ${maybeString "zonestats: " zone.zoneStats} + + ${maybeToString "max-refresh-time: " zone.maxRefreshSecs} + ${maybeToString "min-refresh-time: " zone.minRefreshSecs} + ${maybeToString "max-retry-time: " zone.maxRetrySecs} + ${maybeToString "min-retry-time: " zone.minRetrySecs} + + allow-axfr-fallback: ${yesOrNo zone.allowAXFRFallback} + ${forEach " allow-notify: " zone.allowNotify} + ${forEach " request-xfr: " zone.requestXFR} + + ${forEach " notify: " zone.notify} + notify-retry: ${toString zone.notifyRetry} + ${forEach " provide-xfr: " zone.provideXFR} + ''; + + zoneConfigs = zoneConfigs' {} "" { children = cfg.zones; }; + + zoneConfigs' = parent: name: zone: + if !(zone ? children) || zone.children == null || zone.children == { } + # leaf -> actual zone + then listToAttrs [ (nameValuePair name (parent // zone)) ] + + # fork -> pattern + else zipAttrsWith (name: head) ( + mapAttrsToList (name: child: zoneConfigs' (parent // zone // { children = {}; }) name child) + zone.children + ); + + # fighting infinite recursion + zoneOptions = zoneOptionsRaw // childConfig zoneOptions1 true; + zoneOptions1 = zoneOptionsRaw // childConfig zoneOptions2 false; + zoneOptions2 = zoneOptionsRaw // childConfig zoneOptions3 false; + zoneOptions3 = zoneOptionsRaw // childConfig zoneOptions4 false; + zoneOptions4 = zoneOptionsRaw // childConfig zoneOptions5 false; + zoneOptions5 = zoneOptionsRaw // childConfig zoneOptions6 false; + zoneOptions6 = zoneOptionsRaw // childConfig null false; + + childConfig = x: v: { options.children = { type = types.attrsOf x; visible = v; }; }; + + # options are ordered alphanumerically + zoneOptionsRaw = types.submodule { + options = { + + allowAXFRFallback = mkOption { + type = types.bool; + default = true; + description = '' + If NSD as secondary server should be allowed to AXFR if the primary + server does not allow IXFR. + ''; + }; + + allowNotify = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "192.0.2.0/24 NOKEY" "10.0.0.1-10.0.0.5 my_tsig_key_name" + "10.0.3.4&255.255.0.0 BLOCKED" + ]; + description = '' + Listed primary servers are allowed to notify this secondary server. + <screen><![CDATA[ + Format: <ip> <key-name | NOKEY | BLOCKED> + + <ip> either a plain IPv4/IPv6 address or range. Valid patters for ranges: + * 10.0.0.0/24 # via subnet size + * 10.0.0.0&255.255.255.0 # via subnet mask + * 10.0.0.1-10.0.0.254 # via range + + A optional port number could be added with a '@': + * 2001:1234::1@1234 + + <key-name | NOKEY | BLOCKED> + * <key-name> will use the specified TSIG key + * NOKEY no TSIG signature is required + * BLOCKED notifies from non-listed or blocked IPs will be ignored + * ]]></screen> + ''; + }; + + children = mkOption { + default = {}; + description = '' + Children zones inherit all options of their parents. Attributes + defined in a child will overwrite the ones of its parent. Only + leaf zones will be actually served. This way it's possible to + define maybe zones which share most attributes without + duplicating everything. This mechanism replaces nsd's patterns + in a save and functional way. + ''; + }; + + data = mkOption { + type = types.lines; + default = ""; + example = ""; + description = '' + The actual zone data. This is the content of your zone file. + Use imports or pkgs.lib.readFile if you don't want this data in your config file. + ''; + }; + + dnssec = mkEnableOption "DNSSEC"; + + dnssecPolicy = { + algorithm = mkOption { + type = types.str; + default = "RSASHA256"; + description = "Which algorithm to use for DNSSEC"; + }; + keyttl = mkOption { + type = types.str; + default = "1h"; + description = "TTL for dnssec records"; + }; + coverage = mkOption { + type = types.str; + default = "1y"; + description = '' + The length of time to ensure that keys will be correct; no action will be taken to create new keys to be activated after this time. + ''; + }; + zsk = mkOption { + type = keyPolicy; + default = { keySize = 2048; + prePublish = "1w"; + postPublish = "1w"; + rollPeriod = "1mo"; + }; + description = "Key policy for zone signing keys"; + }; + ksk = mkOption { + type = keyPolicy; + default = { keySize = 4096; + prePublish = "1mo"; + postPublish = "1mo"; + rollPeriod = "0"; + }; + description = "Key policy for key signing keys"; + }; + }; + + maxRefreshSecs = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Limit refresh time for secondary zones. This is the timer which + checks to see if the zone has to be refetched when it expires. + Normally the value from the SOA record is used, but this option + restricts that value. + ''; + }; + + minRefreshSecs = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Limit refresh time for secondary zones. + ''; + }; + + maxRetrySecs = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Limit retry time for secondary zones. This is the timeout after + a failed fetch attempt for the zone. Normally the value from + the SOA record is used, but this option restricts that value. + ''; + }; + + minRetrySecs = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Limit retry time for secondary zones. + ''; + }; + + + notify = mkOption { + type = types.listOf types.str; + default = []; + example = [ "10.0.0.1@3721 my_key" "::5 NOKEY" ]; + description = '' + This primary server will notify all given secondary servers about + zone changes. + <screen><![CDATA[ + Format: <ip> <key-name | NOKEY> + + <ip> a plain IPv4/IPv6 address with on optional port number (ip@port) + + <key-name | NOKEY> + * <key-name> sign notifies with the specified key + * NOKEY don't sign notifies + ]]></screen> + ''; + }; + + notifyRetry = mkOption { + type = types.int; + default = 5; + description = '' + Specifies the number of retries for failed notifies. Set this along with notify. + ''; + }; + + outgoingInterface = mkOption { + type = types.nullOr types.str; + default = null; + example = "2000::1@1234"; + description = '' + This address will be used for zone-transfere requests if configured + as a secondary server or notifications in case of a primary server. + Supply either a plain IPv4 or IPv6 address with an optional port + number (ip@port). + ''; + }; + + provideXFR = mkOption { + type = types.listOf types.str; + default = []; + example = [ "192.0.2.0/24 NOKEY" "192.0.2.0/24 my_tsig_key_name" ]; + description = '' + Allow these IPs and TSIG to transfer zones, addr TSIG|NOKEY|BLOCKED + address range 192.0.2.0/24, 1.2.3.4&255.255.0.0, 3.0.2.20-3.0.2.40 + ''; + }; + + requestXFR = mkOption { + type = types.listOf types.str; + default = []; + example = []; + description = '' + Format: <code>[AXFR|UDP] <ip-address> <key-name | NOKEY></code> + ''; + }; + + rrlWhitelist = mkOption { + type = with types; listOf (enum [ "nxdomain" "error" "referral" "any" "rrsig" "wildcard" "nodata" "dnskey" "positive" "all" ]); + default = []; + description = '' + Whitelists the given rrl-types. + ''; + }; + + zoneStats = mkOption { + type = types.nullOr types.str; + default = null; + example = "%s"; + description = '' + When set to something distinct to null NSD is able to collect + statistics per zone. All statistics of this zone(s) will be added + to the group specified by this given name. Use "%s" to use the zones + name as the group. The groups are output from nsd-control stats + and stats_noreset. + ''; + }; + }; + }; + + keyPolicy = types.submodule { + options = { + keySize = mkOption { + type = types.int; + description = "Key size in bits"; + }; + prePublish = mkOption { + type = types.str; + description = "How long in advance to publish new keys"; + }; + postPublish = mkOption { + type = types.str; + description = "How long after deactivation to keep a key in the zone"; + }; + rollPeriod = mkOption { + type = types.str; + description = "How frequently to change keys"; + }; + }; + }; + + dnssecZones = (filterAttrs (n: v: if v ? dnssec then v.dnssec else false) zoneConfigs); + + dnssec = dnssecZones != {}; + + dnssecTools = pkgs.bind.override { enablePython = true; }; + + signZones = optionalString dnssec '' + mkdir -p ${stateDir}/dnssec + chown ${username}:${username} ${stateDir}/dnssec + chmod 0600 ${stateDir}/dnssec + + ${concatStrings (mapAttrsToList signZone dnssecZones)} + ''; + signZone = name: zone: '' + ${dnssecTools}/bin/dnssec-keymgr -g ${dnssecTools}/bin/dnssec-keygen -s ${dnssecTools}/bin/dnssec-settime -K ${stateDir}/dnssec -c ${policyFile name zone.dnssecPolicy} ${name} + ${dnssecTools}/bin/dnssec-signzone -S -K ${stateDir}/dnssec -o ${name} -O full -N date ${stateDir}/zones/${name} + ${nsdPkg}/sbin/nsd-checkzone ${name} ${stateDir}/zones/${name}.signed && mv -v ${stateDir}/zones/${name}.signed ${stateDir}/zones/${name} + ''; + policyFile = name: policy: pkgs.writeText "${name}.policy" '' + zone ${name} { + algorithm ${policy.algorithm}; + key-size zsk ${toString policy.zsk.keySize}; + key-size ksk ${toString policy.ksk.keySize}; + keyttl ${policy.keyttl}; + pre-publish zsk ${policy.zsk.prePublish}; + pre-publish ksk ${policy.ksk.prePublish}; + post-publish zsk ${policy.zsk.postPublish}; + post-publish ksk ${policy.ksk.postPublish}; + roll-period zsk ${policy.zsk.rollPeriod}; + roll-period ksk ${policy.ksk.rollPeriod}; + coverage ${policy.coverage}; + }; + ''; +in +{ + # options are ordered alphanumerically + options.services.nsd = { + + enable = mkEnableOption "NSD authoritative DNS server"; + + bind8Stats = mkEnableOption "BIND8 like statistics"; + + dnssecInterval = mkOption { + type = types.str; + default = "1h"; + description = '' + How often to check whether dnssec key rollover is required + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra nsd config. + ''; + }; + + hideVersion = mkOption { + type = types.bool; + default = true; + description = '' + Whether NSD should answer VERSION.BIND and VERSION.SERVER CHAOS class queries. + ''; + }; + + identity = mkOption { + type = types.str; + default = "unidentified server"; + description = '' + Identify the server (CH TXT ID.SERVER entry). + ''; + }; + + interfaces = mkOption { + type = types.listOf types.str; + default = [ "127.0.0.0" "::1" ]; + description = '' + What addresses the server should listen to. + ''; + }; + + ipFreebind = mkOption { + type = types.bool; + default = false; + description = '' + Whether to bind to nonlocal addresses and interfaces that are down. + Similar to ip-transparent. + ''; + }; + + ipTransparent = mkOption { + type = types.bool; + default = false; + description = '' + Allow binding to non local addresses. + ''; + }; + + ipv4 = mkOption { + type = types.bool; + default = true; + description = '' + Whether to listen on IPv4 connections. + ''; + }; + + ipv4EDNSSize = mkOption { + type = types.int; + default = 4096; + description = '' + Preferred EDNS buffer size for IPv4. + ''; + }; + + ipv6 = mkOption { + type = types.bool; + default = true; + description = '' + Whether to listen on IPv6 connections. + ''; + }; + + ipv6EDNSSize = mkOption { + type = types.int; + default = 4096; + description = '' + Preferred EDNS buffer size for IPv6. + ''; + }; + + logTimeAscii = mkOption { + type = types.bool; + default = true; + description = '' + Log time in ascii, if false then in unix epoch seconds. + ''; + }; + + nsid = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + NSID identity (hex string, or "ascii_somestring"). + ''; + }; + + port = mkOption { + type = types.int; + default = 53; + description = '' + Port the service should bind do. + ''; + }; + + reuseport = mkOption { + type = types.bool; + default = pkgs.stdenv.isLinux; + description = '' + Whether to enable SO_REUSEPORT on all used sockets. This lets multiple + processes bind to the same port. This speeds up operation especially + if the server count is greater than one and makes fast restarts less + prone to fail + ''; + }; + + rootServer = mkOption { + type = types.bool; + default = false; + description = '' + Whether this server will be a root server (a DNS root server, you + usually don't want that). + ''; + }; + + roundRobin = mkEnableOption "round robin rotation of records"; + + serverCount = mkOption { + type = types.int; + default = 1; + description = '' + Number of NSD servers to fork. Put the number of CPUs to use here. + ''; + }; + + statistics = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Statistics are produced every number of seconds. Prints to log. + If null no statistics are logged. + ''; + }; + + tcpCount = mkOption { + type = types.int; + default = 100; + description = '' + Maximum number of concurrent TCP connections per server. + ''; + }; + + tcpQueryCount = mkOption { + type = types.int; + default = 0; + description = '' + Maximum number of queries served on a single TCP connection. + 0 means no maximum. + ''; + }; + + tcpTimeout = mkOption { + type = types.int; + default = 120; + description = '' + TCP timeout in seconds. + ''; + }; + + verbosity = mkOption { + type = types.int; + default = 0; + description = '' + Verbosity level. + ''; + }; + + version = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The version string replied for CH TXT version.server and version.bind + queries. Will use the compiled package version on null. + See hideVersion for enabling/disabling this responses. + ''; + }; + + xfrdReloadTimeout = mkOption { + type = types.int; + default = 1; + description = '' + Number of seconds between reloads triggered by xfrd. + ''; + }; + + zonefilesCheck = mkOption { + type = types.bool; + default = true; + description = '' + Whether to check mtime of all zone files on start and sighup. + ''; + }; + + + keys = mkOption { + type = types.attrsOf (types.submodule { + options = { + + algorithm = mkOption { + type = types.str; + default = "hmac-sha256"; + description = '' + Authentication algorithm for this key. + ''; + }; + + keyFile = mkOption { + type = types.path; + description = '' + Path to the file which contains the actual base64 encoded + key. The key will be copied into "${stateDir}/private" before + NSD starts. The copied file is only accessibly by the NSD + user. + ''; + }; + + }; + }); + default = {}; + example = literalExample '' + { "tsig.example.org" = { + algorithm = "hmac-md5"; + keyFile = "/path/to/my/key"; + }; + } + ''; + description = '' + Define your TSIG keys here. + ''; + }; + + + ratelimit = { + + enable = mkEnableOption "ratelimit capabilities"; + + ipv4PrefixLength = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + IPv4 prefix length. Addresses are grouped by netblock. + ''; + }; + + ipv6PrefixLength = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + IPv6 prefix length. Addresses are grouped by netblock. + ''; + }; + + ratelimit = mkOption { + type = types.int; + default = 200; + description = '' + Max qps allowed from any query source. + 0 means unlimited. With an verbosity of 2 blocked and + unblocked subnets will be logged. + ''; + }; + + slip = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Number of packets that get discarded before replying a SLIP response. + 0 disables SLIP responses. 1 will make every response a SLIP response. + ''; + }; + + size = mkOption { + type = types.int; + default = 1000000; + description = '' + Size of the hashtable. More buckets use more memory but lower + the chance of hash hash collisions. + ''; + }; + + whitelistRatelimit = mkOption { + type = types.int; + default = 2000; + description = '' + Max qps allowed from whitelisted sources. + 0 means unlimited. Set the rrl-whitelist option for specific + queries to apply this limit instead of the default to them. + ''; + }; + + }; + + + remoteControl = { + + enable = mkEnableOption "remote control via nsd-control"; + + controlCertFile = mkOption { + type = types.path; + default = "/etc/nsd/nsd_control.pem"; + description = '' + Path to the client certificate signed with the server certificate. + This file is used by nsd-control and generated by nsd-control-setup. + ''; + }; + + controlKeyFile = mkOption { + type = types.path; + default = "/etc/nsd/nsd_control.key"; + description = '' + Path to the client private key, which is used by nsd-control + but not by the server. This file is generated by nsd-control-setup. + ''; + }; + + interfaces = mkOption { + type = types.listOf types.str; + default = [ "127.0.0.1" "::1" ]; + description = '' + Which interfaces NSD should bind to for remote control. + ''; + }; + + port = mkOption { + type = types.int; + default = 8952; + description = '' + Port number for remote control operations (uses TLS over TCP). + ''; + }; + + serverCertFile = mkOption { + type = types.path; + default = "/etc/nsd/nsd_server.pem"; + description = '' + Path to the server self signed certificate, which is used by the server + but and by nsd-control. This file is generated by nsd-control-setup. + ''; + }; + + serverKeyFile = mkOption { + type = types.path; + default = "/etc/nsd/nsd_server.key"; + description = '' + Path to the server private key, which is used by the server + but not by nsd-control. This file is generated by nsd-control-setup. + ''; + }; + + }; + + zones = mkOption { + type = types.attrsOf zoneOptions; + default = {}; + example = literalExample '' + { "serverGroup1" = { + provideXFR = [ "10.1.2.3 NOKEY" ]; + children = { + "example.com." = { + data = ''' + $ORIGIN example.com. + $TTL 86400 + @ IN SOA a.ns.example.com. admin.example.com. ( + ... + '''; + }; + "example.org." = { + data = ''' + $ORIGIN example.org. + $TTL 86400 + @ IN SOA a.ns.example.com. admin.example.com. ( + ... + '''; + }; + }; + }; + + "example.net." = { + provideXFR = [ "10.3.2.1 NOKEY" ]; + data = ''' + ... + '''; + }; + } + ''; + description = '' + Define your zones here. Zones can cascade other zones and therefore + inherit settings from parent zones. Look at the definition of + children to learn about inheritance and child zones. + The given example will define 3 zones (example.(com|org|net).). Both + example.com. and example.org. inherit their configuration from + serverGroup1. + ''; + }; + }; + + config = mkIf cfg.enable { + + assertions = singleton { + assertion = zoneConfigs ? "." -> cfg.rootServer; + message = "You have a root zone configured. If this is really what you " + + "want, please enable 'services.nsd.rootServer'."; + }; + + environment.systemPackages = [ nsdPkg ]; + + users.groups.${username}.gid = config.ids.gids.nsd; + + users.users.${username} = { + description = "NSD service user"; + home = stateDir; + createHome = true; + uid = config.ids.uids.nsd; + group = username; + }; + + systemd.services.nsd = { + description = "NSD authoritative only domain name service"; + + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = "${nsdPkg}/sbin/nsd -d -c ${nsdEnv}/nsd.conf"; + StandardError = "null"; + PIDFile = pidFile; + Restart = "always"; + RestartSec = "4s"; + StartLimitBurst = 4; + StartLimitInterval = "5min"; + }; + + preStart = '' + rm -Rf "${stateDir}/private/" + rm -Rf "${stateDir}/tmp/" + + mkdir -m 0700 -p "${stateDir}/private" + mkdir -m 0700 -p "${stateDir}/tmp" + mkdir -m 0700 -p "${stateDir}/var" + + cat > "${stateDir}/don't touch anything in here" << EOF + Everything in this directory except NSD's state in var and dnssec + is automatically generated and will be purged and redeployed by + the nsd.service pre-start script. + EOF + + chown ${username}:${username} -R "${stateDir}/private" + chown ${username}:${username} -R "${stateDir}/tmp" + chown ${username}:${username} -R "${stateDir}/var" + + rm -rf "${stateDir}/zones" + cp -rL "${nsdEnv}/zones" "${stateDir}/zones" + + ${copyKeys} + ''; + }; + + systemd.timers.nsd-dnssec = mkIf dnssec { + description = "Automatic DNSSEC key rollover"; + + wantedBy = [ "nsd.service" ]; + + timerConfig = { + OnActiveSec = cfg.dnssecInterval; + OnUnitActiveSec = cfg.dnssecInterval; + }; + }; + + systemd.services.nsd-dnssec = mkIf dnssec { + description = "DNSSEC key rollover"; + + wantedBy = [ "nsd.service" ]; + before = [ "nsd.service" ]; + + script = signZones; + + postStop = '' + /run/current-system/systemd/bin/systemctl kill -s SIGHUP nsd.service + ''; + }; + + }; + + meta.maintainers = with lib.maintainers; [ hrdinka ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/ntopng.nix b/nixpkgs/nixos/modules/services/networking/ntopng.nix new file mode 100644 index 000000000000..c15257117137 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ntopng.nix @@ -0,0 +1,116 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.ntopng; + redisCfg = config.services.redis; + + configFile = if cfg.configText != "" then + pkgs.writeText "ntopng.conf" '' + ${cfg.configText} + '' + else + pkgs.writeText "ntopng.conf" '' + ${concatStringsSep " " (map (e: "--interface=" + e) cfg.interfaces)} + --http-port=${toString cfg.http-port} + --redis=localhost:${toString redisCfg.port} + ${cfg.extraConfig} + ''; + +in + +{ + + options = { + + services.ntopng = { + + enable = mkOption { + default = false; + type = types.bool; + description = '' + Enable ntopng, a high-speed web-based traffic analysis and flow + collection tool. + + With the default configuration, ntopng monitors all network + interfaces and displays its findings at http://localhost:${toString + cfg.http-port}. Default username and password is admin/admin. + + See the ntopng(8) manual page and http://www.ntop.org/products/ntop/ + for more info. + + Note that enabling ntopng will also enable redis (key-value + database server) for persistent data storage. + ''; + }; + + interfaces = mkOption { + default = [ "any" ]; + example = [ "eth0" "wlan0" ]; + type = types.listOf types.str; + description = '' + List of interfaces to monitor. Use "any" to monitor all interfaces. + ''; + }; + + http-port = mkOption { + default = 3000; + type = types.int; + description = '' + Sets the HTTP port of the embedded web server. + ''; + }; + + configText = mkOption { + default = ""; + example = '' + --interface=any + --http-port=3000 + --disable-login + ''; + type = types.lines; + description = '' + Overridable configuration file contents to use for ntopng. By + default, use the contents automatically generated by NixOS. + ''; + }; + + extraConfig = mkOption { + default = ""; + type = types.lines; + description = '' + Configuration lines that will be appended to the generated ntopng + configuration file. Note that this mechanism does not work when the + manual <option>configText</option> option is used. + ''; + }; + + }; + + }; + + config = mkIf cfg.enable { + + # ntopng uses redis for data storage + services.redis.enable = true; + + # nice to have manual page and ntopng command in PATH + environment.systemPackages = [ pkgs.ntopng ]; + + systemd.services.ntopng = { + description = "Ntopng Network Monitor"; + requires = [ "redis.service" ]; + after = [ "network.target" "redis.service" ]; + wantedBy = [ "multi-user.target" ]; + preStart = "mkdir -p /var/lib/ntopng/"; + serviceConfig.ExecStart = "${pkgs.ntopng}/bin/ntopng ${configFile}"; + unitConfig.Documentation = "man:ntopng(8)"; + }; + + # ntopng drops priveleges to user "nobody" and that user is already defined + # in users-groups.nix. + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix new file mode 100644 index 000000000000..b7e4c89a155c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ntp/chrony.nix @@ -0,0 +1,125 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.chrony; + + stateDir = "/var/lib/chrony"; + keyFile = "${stateDir}/chrony.keys"; + + configFile = pkgs.writeText "chrony.conf" '' + ${concatMapStringsSep "\n" (server: "server " + server + " iburst") cfg.servers} + + ${optionalString + (cfg.initstepslew.enabled && (cfg.servers != [])) + "initstepslew ${toString cfg.initstepslew.threshold} ${concatStringsSep " " cfg.servers}" + } + + driftfile ${stateDir}/chrony.drift + keyfile ${keyFile} + + ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"} + + ${cfg.extraConfig} + ''; + + chronyFlags = "-n -m -u chrony -f ${configFile} ${toString cfg.extraFlags}"; +in +{ + options = { + services.chrony = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to synchronise your machine's time using chrony. + Make sure you disable NTP if you enable this service. + ''; + }; + + servers = mkOption { + default = config.networking.timeServers; + description = '' + The set of NTP servers from which to synchronise. + ''; + }; + + initstepslew = mkOption { + default = { + enabled = true; + threshold = 1000; # by default, same threshold as 'ntpd -g' (1000s) + }; + description = '' + Allow chronyd to make a rapid measurement of the system clock error at + boot time, and to correct the system clock by stepping before normal + operation begins. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration directives that should be added to + <literal>chrony.conf</literal> + ''; + }; + + extraFlags = mkOption { + default = []; + example = [ "-s" ]; + type = types.listOf types.str; + description = "Extra flags passed to the chronyd command."; + }; + }; + }; + + config = mkIf cfg.enable { + meta.maintainers = with lib.maintainers; [ thoughtpolice ]; + + environment.systemPackages = [ pkgs.chrony ]; + + users.groups.chrony.gid = config.ids.gids.chrony; + + users.users.chrony = + { uid = config.ids.uids.chrony; + group = "chrony"; + description = "chrony daemon user"; + home = stateDir; + }; + + services.timesyncd.enable = mkForce false; + + systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "chronyd.service"; }; + + systemd.tmpfiles.rules = [ + "d ${stateDir} 0755 chrony chrony - -" + "f ${keyFile} 0640 chrony chrony -" + ]; + + systemd.services.chronyd = + { description = "chrony NTP daemon"; + + wantedBy = [ "multi-user.target" ]; + wants = [ "time-sync.target" ]; + before = [ "time-sync.target" ]; + after = [ "network.target" ]; + conflicts = [ "ntpd.service" "systemd-timesyncd.service" ]; + + path = [ pkgs.chrony ]; + + unitConfig.ConditionCapability = "CAP_SYS_TIME"; + serviceConfig = + { Type = "simple"; + ExecStart = "${pkgs.chrony}/bin/chronyd ${chronyFlags}"; + + ProtectHome = "yes"; + ProtectSystem = "full"; + PrivateTmp = "yes"; + StateDirectory = "chrony"; + }; + + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix b/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix new file mode 100644 index 000000000000..51398851adc6 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ntp/ntpd.nix @@ -0,0 +1,148 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (pkgs) ntp; + + cfg = config.services.ntp; + + stateDir = "/var/lib/ntp"; + + ntpUser = "ntp"; + + configFile = pkgs.writeText "ntp.conf" '' + driftfile ${stateDir}/ntp.drift + + restrict default ${toString cfg.restrictDefault} + restrict -6 default ${toString cfg.restrictDefault} + restrict source ${toString cfg.restrictSource} + + restrict 127.0.0.1 + restrict -6 ::1 + + ${toString (map (server: "server " + server + " iburst\n") cfg.servers)} + + ${cfg.extraConfig} + ''; + + ntpFlags = "-c ${configFile} -u ${ntpUser}:nogroup ${toString cfg.extraFlags}"; + +in + +{ + + ###### interface + + options = { + + services.ntp = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to synchronise your machine's time using ntpd, as a peer in + the NTP network. + </para> + <para> + Disables <literal>systemd.timesyncd</literal> if enabled. + ''; + }; + + restrictDefault = mkOption { + type = types.listOf types.str; + description = '' + The restriction flags to be set by default. + </para> + <para> + The default flags prevent external hosts from using ntpd as a DDoS + reflector, setting system time, and querying OS/ntpd version. As + recommended in section 6.5.1.1.3, answer "No" of + http://support.ntp.org/bin/view/Support/AccessRestrictions + ''; + default = [ "limited" "kod" "nomodify" "notrap" "noquery" "nopeer" ]; + }; + + restrictSource = mkOption { + type = types.listOf types.str; + description = '' + The restriction flags to be set on source. + </para> + <para> + The default flags allow peers to be added by ntpd from configured + pool(s), but not by other means. + ''; + default = [ "limited" "kod" "nomodify" "notrap" "noquery" ]; + }; + + servers = mkOption { + default = config.networking.timeServers; + description = '' + The set of NTP servers from which to synchronise. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + fudge 127.127.1.0 stratum 10 + ''; + description = '' + Additional text appended to <filename>ntp.conf</filename>. + ''; + }; + + extraFlags = mkOption { + type = types.listOf types.str; + description = "Extra flags passed to the ntpd command."; + example = literalExample ''[ "--interface=eth0" ]''; + default = []; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.services.ntp.enable { + meta.maintainers = with lib.maintainers; [ thoughtpolice ]; + + # Make tools such as ntpq available in the system path. + environment.systemPackages = [ pkgs.ntp ]; + services.timesyncd.enable = mkForce false; + + systemd.services.systemd-timedated.environment = { SYSTEMD_TIMEDATED_NTP_SERVICES = "ntpd.service"; }; + + users.users.${ntpUser} = + { uid = config.ids.uids.ntp; + description = "NTP daemon user"; + home = stateDir; + }; + + systemd.services.ntpd = + { description = "NTP Daemon"; + + wantedBy = [ "multi-user.target" ]; + wants = [ "time-sync.target" ]; + before = [ "time-sync.target" ]; + + preStart = + '' + mkdir -m 0755 -p ${stateDir} + chown ${ntpUser} ${stateDir} + ''; + + serviceConfig = { + ExecStart = "@${ntp}/bin/ntpd ntpd -g ${ntpFlags}"; + Type = "forking"; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix b/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix new file mode 100644 index 000000000000..67a04d48d308 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ntp/openntpd.nix @@ -0,0 +1,82 @@ +{ pkgs, lib, config, options, ... }: + +with lib; + +let + cfg = config.services.openntpd; + + package = pkgs.openntpd_nixos; + + configFile = '' + ${concatStringsSep "\n" (map (s: "server ${s}") cfg.servers)} + ${cfg.extraConfig} + ''; + + pidFile = "/run/openntpd.pid"; + +in +{ + ###### interface + + options.services.openntpd = { + enable = mkEnableOption "OpenNTP time synchronization server"; + + servers = mkOption { + default = config.services.ntp.servers; + type = types.listOf types.str; + inherit (options.services.ntp.servers) description; + }; + + extraConfig = mkOption { + type = with types; lines; + default = ""; + example = '' + listen on 127.0.0.1 + listen on ::1 + ''; + description = '' + Additional text appended to <filename>openntpd.conf</filename>. + ''; + }; + + extraOptions = mkOption { + type = with types; separatedString " "; + default = ""; + example = "-s"; + description = '' + Extra options used when launching openntpd. + ''; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + meta.maintainers = with lib.maintainers; [ thoughtpolice ]; + services.timesyncd.enable = mkForce false; + + # Add ntpctl to the environment for status checking + environment.systemPackages = [ package ]; + + environment.etc."ntpd.conf".text = configFile; + + users.users.ntp = { + uid = config.ids.uids.ntp; + description = "OpenNTP daemon user"; + home = "/var/empty"; + }; + + systemd.services.openntpd = { + description = "OpenNTP Server"; + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" "time-sync.target" ]; + before = [ "time-sync.target" ]; + after = [ "dnsmasq.service" "bind.service" "network-online.target" ]; + serviceConfig = { + ExecStart = "${package}/sbin/ntpd -p ${pidFile} ${cfg.extraOptions}"; + Type = "forking"; + PIDFile = pidFile; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nullidentdmod.nix b/nixpkgs/nixos/modules/services/networking/nullidentdmod.nix new file mode 100644 index 000000000000..b0d338a27941 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nullidentdmod.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, ... }: with lib; let + cfg = config.services.nullidentdmod; + +in { + options.services.nullidentdmod = with types; { + enable = mkEnableOption "the nullidentdmod identd daemon"; + + userid = mkOption { + type = nullOr str; + description = "User ID to return. Set to null to return a random string each time."; + default = null; + example = "alice"; + }; + }; + + config = mkIf cfg.enable { + systemd.sockets.nullidentdmod = { + description = "Socket for identd (NullidentdMod)"; + listenStreams = [ "113" ]; + socketConfig.Accept = true; + wantedBy = [ "sockets.target" ]; + }; + + systemd.services."nullidentdmod@" = { + description = "NullidentdMod service"; + serviceConfig = { + DynamicUser = true; + ExecStart = "${pkgs.nullidentdmod}/bin/nullidentdmod${optionalString (cfg.userid != null) " ${cfg.userid}"}"; + StandardInput = "socket"; + StandardOutput = "socket"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/nylon.nix b/nixpkgs/nixos/modules/services/networking/nylon.nix new file mode 100644 index 000000000000..7c171281a926 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nylon.nix @@ -0,0 +1,166 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.nylon; + + homeDir = "/var/lib/nylon"; + + configFile = cfg: pkgs.writeText "nylon-${cfg.name}.conf" '' + [General] + No-Simultaneous-Conn=${toString cfg.nrConnections} + Log=${if cfg.logging then "1" else "0"} + Verbose=${if cfg.verbosity then "1" else "0"} + + [Server] + Binding-Interface=${cfg.acceptInterface} + Connecting-Interface=${cfg.bindInterface} + Port=${toString cfg.port} + Allow-IP=${concatStringsSep " " cfg.allowedIPRanges} + Deny-IP=${concatStringsSep " " cfg.deniedIPRanges} + ''; + + nylonOpts = { name, ... }: { + + options = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enables nylon as a running service upon activation. + ''; + }; + + name = mkOption { + type = types.str; + default = ""; + description = "The name of this nylon instance."; + }; + + nrConnections = mkOption { + type = types.int; + default = 10; + description = '' + The number of allowed simultaneous connections to the daemon, default 10. + ''; + }; + + logging = mkOption { + type = types.bool; + default = false; + description = '' + Enable logging, default is no logging. + ''; + }; + + verbosity = mkOption { + type = types.bool; + default = false; + description = '' + Enable verbose output, default is to not be verbose. + ''; + }; + + acceptInterface = mkOption { + type = types.str; + default = "lo"; + description = '' + Tell nylon which interface to listen for client requests on, default is "lo". + ''; + }; + + bindInterface = mkOption { + type = types.str; + default = "enp3s0f0"; + description = '' + Tell nylon which interface to use as an uplink, default is "enp3s0f0". + ''; + }; + + port = mkOption { + type = types.int; + default = 1080; + description = '' + What port to listen for client requests, default is 1080. + ''; + }; + + allowedIPRanges = mkOption { + type = with types; listOf str; + default = [ "192.168.0.0/16" "127.0.0.1/8" "172.16.0.1/12" "10.0.0.0/8" ]; + description = '' + Allowed client IP ranges are evaluated first, defaults to ARIN IPv4 private ranges: + [ "192.168.0.0/16" "127.0.0.0/8" "172.16.0.0/12" "10.0.0.0/8" ] + ''; + }; + + deniedIPRanges = mkOption { + type = with types; listOf str; + default = [ "0.0.0.0/0" ]; + description = '' + Denied client IP ranges, these gets evaluated after the allowed IP ranges, defaults to all IPv4 addresses: + [ "0.0.0.0/0" ] + To block all other access than the allowed. + ''; + }; + }; + config = { name = mkDefault name; }; + }; + + mkNamedNylon = cfg: { + "nylon-${cfg.name}" = { + description = "Nylon, a lightweight SOCKS proxy server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = + { + User = "nylon"; + Group = "nylon"; + WorkingDirectory = homeDir; + ExecStart = "${pkgs.nylon}/bin/nylon -f -c ${configFile cfg}"; + }; + }; + }; + + anyNylons = collect (p: p ? enable) cfg; + enabledNylons = filter (p: p.enable == true) anyNylons; + nylonUnits = map (nylon: mkNamedNylon nylon) enabledNylons; + +in + +{ + + ###### interface + + options = { + + services.nylon = mkOption { + default = {}; + description = "Collection of named nylon instances"; + type = with types; loaOf (submodule nylonOpts); + internal = true; + }; + + }; + + ###### implementation + + config = mkIf (length(enabledNylons) > 0) { + + users.users.nylon = { + group = "nylon"; + description = "Nylon SOCKS Proxy"; + home = homeDir; + createHome = true; + uid = config.ids.uids.nylon; + }; + + users.groups.nylon.gid = config.ids.gids.nylon; + + systemd.services = fold (a: b: a // b) {} nylonUnits; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/ocserv.nix b/nixpkgs/nixos/modules/services/networking/ocserv.nix new file mode 100644 index 000000000000..dc26ffeafeef --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ocserv.nix @@ -0,0 +1,99 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + + cfg = config.services.ocserv; + +in + +{ + options.services.ocserv = { + enable = mkEnableOption "ocserv"; + + config = mkOption { + type = types.lines; + + description = '' + Configuration content to start an OCServ server. + + For a full configuration reference,please refer to the online documentation + (https://ocserv.gitlab.io/www/manual.html), the openconnect + recipes (https://github.com/openconnect/recipes) or `man ocserv`. + ''; + + example = '' + # configuration examples from $out/doc without explanatory comments. + # for a full reference please look at the installed man pages. + auth = "plain[passwd=./sample.passwd]" + tcp-port = 443 + udp-port = 443 + run-as-user = nobody + run-as-group = nogroup + socket-file = /run/ocserv-socket + server-cert = certs/server-cert.pem + server-key = certs/server-key.pem + keepalive = 32400 + dpd = 90 + mobile-dpd = 1800 + switch-to-tcp-timeout = 25 + try-mtu-discovery = false + cert-user-oid = 0.9.2342.19200300.100.1.1 + tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-VERS-SSL3.0" + auth-timeout = 240 + min-reauth-time = 300 + max-ban-score = 80 + ban-reset-time = 1200 + cookie-timeout = 300 + deny-roaming = false + rekey-time = 172800 + rekey-method = ssl + use-occtl = true + pid-file = /run/ocserv.pid + device = vpns + predictable-ips = true + default-domain = example.com + ipv4-network = 192.168.1.0 + ipv4-netmask = 255.255.255.0 + dns = 192.168.1.2 + ping-leases = false + route = 10.10.10.0/255.255.255.0 + route = 192.168.0.0/255.255.0.0 + no-route = 192.168.5.0/255.255.255.0 + cisco-client-compat = true + dtls-legacy = true + + [vhost:www.example.com] + auth = "certificate" + ca-cert = certs/ca.pem + server-cert = certs/server-cert-secp521r1.pem + server-key = cersts/certs/server-key-secp521r1.pem + ipv4-network = 192.168.2.0 + ipv4-netmask = 255.255.255.0 + cert-user-oid = 0.9.2342.19200300.100.1.1 + ''; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.ocserv ]; + environment.etc."ocserv/ocserv.conf".text = cfg.config; + + security.pam.services.ocserv = {}; + + systemd.services.ocserv = { + description = "OpenConnect SSL VPN server"; + documentation = [ "man:ocserv(8)" ]; + after = [ "dbus.service" "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + PrivateTmp = true; + PIDFile = "/run/ocserv.pid"; + ExecStart = "${pkgs.ocserv}/bin/ocserv --foreground --pid-file /run/ocesrv.pid --config /etc/ocserv/ocserv.conf"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/ofono.nix b/nixpkgs/nixos/modules/services/networking/ofono.nix new file mode 100644 index 000000000000..40ef9433de0f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ofono.nix @@ -0,0 +1,44 @@ +# Ofono daemon. +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.ofono; + + plugin_path = + lib.concatMapStringsSep ":" + (plugin: "${plugin}/lib/ofono/plugins") + cfg.plugins + ; + +in + +{ + ###### interface + options = { + services.ofono = { + enable = mkEnableOption "Ofono"; + + plugins = mkOption { + type = types.listOf types.package; + default = []; + example = literalExample "[ pkgs.modem-manager-gui ]"; + description = '' + The list of plugins to install. + ''; + }; + }; + }; + + ###### implementation + config = mkIf cfg.enable { + services.dbus.packages = [ pkgs.ofono ]; + + systemd.packages = [ pkgs.ofono ]; + + systemd.services.ofono.environment.OFONO_PLUGIN_PATH = mkIf (cfg.plugins != []) plugin_path; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/oidentd.nix b/nixpkgs/nixos/modules/services/networking/oidentd.nix new file mode 100644 index 000000000000..feb84806ba99 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/oidentd.nix @@ -0,0 +1,44 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + + ###### interface + + options = { + + services.oidentd.enable = mkOption { + default = false; + type = types.bool; + description = '' + Whether to enable ‘oidentd’, an implementation of the Ident + protocol (RFC 1413). It allows remote systems to identify the + name of the user associated with a TCP connection. + ''; + }; + + }; + + + ###### implementation + + config = mkIf config.services.oidentd.enable { + systemd.services.oidentd = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig.Type = "forking"; + script = "${pkgs.oidentd}/sbin/oidentd -u oidentd -g nogroup"; + }; + + users.users.oidentd = { + description = "Ident Protocol daemon user"; + group = "oidentd"; + uid = config.ids.uids.oidentd; + }; + + users.groups.oidentd.gid = config.ids.gids.oidentd; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/openfire.nix b/nixpkgs/nixos/modules/services/networking/openfire.nix new file mode 100644 index 000000000000..fe0499d52323 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/openfire.nix @@ -0,0 +1,56 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + ###### interface + + options = { + + services.openfire = { + + enable = mkEnableOption "OpenFire XMPP server"; + + usePostgreSQL = mkOption { + type = types.bool; + default = true; + description = " + Whether you use PostgreSQL service for your storage back-end. + "; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.services.openfire.enable { + + assertions = singleton + { assertion = !(config.services.openfire.usePostgreSQL -> config.services.postgresql.enable); + message = "OpenFire configured to use PostgreSQL but services.postgresql.enable is not enabled."; + }; + + systemd.services.openfire = { + description = "OpenFire XMPP server"; + wantedBy = [ "multi-user.target" ]; + after = [ "networking.target" ] ++ + optional config.services.openfire.usePostgreSQL "postgresql.service"; + path = with pkgs; [ jre openfire coreutils which gnugrep gawk gnused ]; + script = '' + export HOME=/tmp + mkdir /var/log/openfire || true + mkdir /etc/openfire || true + for i in ${pkgs.openfire}/conf.inst/*; do + if ! test -f /etc/openfire/$(basename $i); then + cp $i /etc/openfire/ + fi + done + openfire start + ''; # */ + }; + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/openvpn.nix b/nixpkgs/nixos/modules/services/networking/openvpn.nix new file mode 100644 index 000000000000..dcd7e9e5fa4c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/openvpn.nix @@ -0,0 +1,219 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.openvpn; + + inherit (pkgs) openvpn; + + makeOpenVPNJob = cfg: name: + let + + path = (getAttr "openvpn-${name}" config.systemd.services).path; + + upScript = '' + #! /bin/sh + export PATH=${path} + + # For convenience in client scripts, extract the remote domain + # name and name server. + for var in ''${!foreign_option_*}; do + x=(''${!var}) + if [ "''${x[0]}" = dhcp-option ]; then + if [ "''${x[1]}" = DOMAIN ]; then domain="''${x[2]}" + elif [ "''${x[1]}" = DNS ]; then nameserver="''${x[2]}" + fi + fi + done + + ${cfg.up} + ${optionalString cfg.updateResolvConf + "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"} + ''; + + downScript = '' + #! /bin/sh + export PATH=${path} + ${optionalString cfg.updateResolvConf + "${pkgs.update-resolv-conf}/libexec/openvpn/update-resolv-conf"} + ${cfg.down} + ''; + + configFile = pkgs.writeText "openvpn-config-${name}" + '' + errors-to-stderr + ${optionalString (cfg.up != "" || cfg.down != "" || cfg.updateResolvConf) "script-security 2"} + ${cfg.config} + ${optionalString (cfg.up != "" || cfg.updateResolvConf) + "up ${pkgs.writeScript "openvpn-${name}-up" upScript}"} + ${optionalString (cfg.down != "" || cfg.updateResolvConf) + "down ${pkgs.writeScript "openvpn-${name}-down" downScript}"} + ${optionalString (cfg.authUserPass != null) + "auth-user-pass ${pkgs.writeText "openvpn-credentials-${name}" '' + ${cfg.authUserPass.username} + ${cfg.authUserPass.password} + ''}"} + ''; + + in { + description = "OpenVPN instance ‘${name}’"; + + wantedBy = optional cfg.autoStart "multi-user.target"; + after = [ "network.target" ]; + + path = [ pkgs.iptables pkgs.iproute pkgs.nettools ]; + + serviceConfig.ExecStart = "@${openvpn}/sbin/openvpn openvpn --suppress-timestamps --config ${configFile}"; + serviceConfig.Restart = "always"; + serviceConfig.Type = "notify"; + }; + +in + +{ + imports = [ + (mkRemovedOptionModule [ "services" "openvpn" "enable" ] "") + ]; + + ###### interface + + options = { + + services.openvpn.servers = mkOption { + default = {}; + + example = literalExample '' + { + server = { + config = ''' + # Simplest server configuration: https://community.openvpn.net/openvpn/wiki/StaticKeyMiniHowto + # server : + dev tun + ifconfig 10.8.0.1 10.8.0.2 + secret /root/static.key + '''; + up = "ip route add ..."; + down = "ip route del ..."; + }; + + client = { + config = ''' + client + remote vpn.example.org + dev tun + proto tcp-client + port 8080 + ca /root/.vpn/ca.crt + cert /root/.vpn/alice.crt + key /root/.vpn/alice.key + '''; + up = "echo nameserver $nameserver | ''${pkgs.openresolv}/sbin/resolvconf -m 0 -a $dev"; + down = "''${pkgs.openresolv}/sbin/resolvconf -d $dev"; + }; + } + ''; + + description = '' + Each attribute of this option defines a systemd service that + runs an OpenVPN instance. These can be OpenVPN servers or + clients. The name of each systemd service is + <literal>openvpn-<replaceable>name</replaceable>.service</literal>, + where <replaceable>name</replaceable> is the corresponding + attribute name. + ''; + + type = with types; attrsOf (submodule { + + options = { + + config = mkOption { + type = types.lines; + description = '' + Configuration of this OpenVPN instance. See + <citerefentry><refentrytitle>openvpn</refentrytitle><manvolnum>8</manvolnum></citerefentry> + for details. + + To import an external config file, use the following definition: + <literal>config = "config /path/to/config.ovpn"</literal> + ''; + }; + + up = mkOption { + default = ""; + type = types.lines; + description = '' + Shell commands executed when the instance is starting. + ''; + }; + + down = mkOption { + default = ""; + type = types.lines; + description = '' + Shell commands executed when the instance is shutting down. + ''; + }; + + autoStart = mkOption { + default = true; + type = types.bool; + description = "Whether this OpenVPN instance should be started automatically."; + }; + + updateResolvConf = mkOption { + default = false; + type = types.bool; + description = '' + Use the script from the update-resolv-conf package to automatically + update resolv.conf with the DNS information provided by openvpn. The + script will be run after the "up" commands and before the "down" commands. + ''; + }; + + authUserPass = mkOption { + default = null; + description = '' + This option can be used to store the username / password credentials + with the "auth-user-pass" authentication method. + + WARNING: Using this option will put the credentials WORLD-READABLE in the Nix store! + ''; + type = types.nullOr (types.submodule { + + options = { + username = mkOption { + description = "The username to store inside the credentials file."; + type = types.str; + }; + + password = mkOption { + description = "The password to store inside the credentials file."; + type = types.str; + }; + }; + }); + }; + }; + + }); + + }; + + }; + + + ###### implementation + + config = mkIf (cfg.servers != {}) { + + systemd.services = listToAttrs (mapAttrsFlatten (name: value: nameValuePair "openvpn-${name}" (makeOpenVPNJob value name)) cfg.servers); + + environment.systemPackages = [ openvpn ]; + + boot.kernelModules = [ "tun" ]; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/ostinato.nix b/nixpkgs/nixos/modules/services/networking/ostinato.nix new file mode 100644 index 000000000000..5e8cce5b89aa --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ostinato.nix @@ -0,0 +1,104 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + pkg = pkgs.ostinato; + cfg = config.services.ostinato; + configFile = pkgs.writeText "drone.ini" '' + [General] + RateAccuracy=${cfg.rateAccuracy} + + [RpcServer] + Address=${cfg.rpcServer.address} + + [PortList] + Include=${concatStringsSep "," cfg.portList.include} + Exclude=${concatStringsSep "," cfg.portList.exclude} + ''; + +in +{ + + ###### interface + + options = { + + services.ostinato = { + + enable = mkEnableOption "Ostinato agent-controller (Drone)"; + + port = mkOption { + type = types.int; + default = 7878; + description = '' + Port to listen on. + ''; + }; + + rateAccuracy = mkOption { + type = types.enum [ "High" "Low" ]; + default = "High"; + description = '' + To ensure that the actual transmit rate is as close as possible to + the configured transmit rate, Drone runs a busy-wait loop. + While this provides the maximum accuracy possible, the CPU + utilization is 100% while the transmit is on. You can however, + sacrifice the accuracy to reduce the CPU load. + ''; + }; + + rpcServer = { + address = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + By default, the Drone RPC server will listen on all interfaces and + local IPv4 adresses for incoming connections from clients. Specify + a single IPv4 or IPv6 address if you want to restrict that. + To listen on any IPv6 address, use :: + ''; + }; + }; + + portList = { + include = mkOption { + type = types.listOf types.str; + default = []; + example = ''[ "eth*" "lo*" ]''; + description = '' + For a port to pass the filter and appear on the port list managed + by drone, it be allowed by this include list. + ''; + }; + exclude = mkOption { + type = types.listOf types.str; + default = []; + example = ''[ "usbmon*" "eth0" ]''; + description = '' + A list of ports does not appear on the port list managed by drone. + ''; + }; + }; + + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkg ]; + + systemd.services.drone = { + description = "Ostinato agent-controller"; + wantedBy = [ "multi-user.target" ]; + script = '' + ${pkg}/bin/drone ${toString cfg.port} ${configFile} + ''; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/owamp.nix b/nixpkgs/nixos/modules/services/networking/owamp.nix new file mode 100644 index 000000000000..637ed618b893 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/owamp.nix @@ -0,0 +1,45 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.owamp; +in +{ + + ###### interface + + options = { + services.owamp.enable = mkEnableOption ''Enable OWAMP server''; + }; + + + ###### implementation + + config = mkIf cfg.enable { + users.users.owamp = { + group = "owamp"; + description = "Owamp daemon"; + isSystemUser = true; + }; + + users.groups.owamp = { }; + + systemd.services.owamp = { + description = "Owamp server"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart="${pkgs.owamp}/bin/owampd -R /run/owamp -d /run/owamp -v -Z "; + PrivateTmp = true; + Restart = "always"; + Type="simple"; + User = "owamp"; + Group = "owamp"; + RuntimeDirectory = "owamp"; + StateDirectory = "owamp"; + AmbientCapabilities = "cap_net_bind_service"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix b/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix new file mode 100644 index 000000000000..6ff181377fcc --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix @@ -0,0 +1,224 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + dataDir = "/var/lib/pdns-recursor"; + username = "pdns-recursor"; + + cfg = config.services.pdns-recursor; + + oneOrMore = type: with types; either type (listOf type); + valueType = with types; oneOf [ int str bool path ]; + configType = with types; attrsOf (nullOr (oneOrMore valueType)); + + toBool = val: if val then "yes" else "no"; + serialize = val: with types; + if str.check val then val + else if int.check val then toString val + else if path.check val then toString val + else if bool.check val then toBool val + else if builtins.isList val then (concatMapStringsSep "," serialize val) + else ""; + + configFile = pkgs.writeText "recursor.conf" + (concatStringsSep "\n" + (flip mapAttrsToList cfg.settings + (name: val: "${name}=${serialize val}"))); + + mkDefaultAttrs = mapAttrs (n: v: mkDefault v); + +in { + options.services.pdns-recursor = { + enable = mkEnableOption "PowerDNS Recursor, a recursive DNS server"; + + dns.address = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + IP address Recursor DNS server will bind to. + ''; + }; + + dns.port = mkOption { + type = types.int; + default = 53; + description = '' + Port number Recursor DNS server will bind to. + ''; + }; + + dns.allowFrom = mkOption { + type = types.listOf types.str; + default = [ "10.0.0.0/8" "172.16.0.0/12" "192.168.0.0/16" ]; + example = [ "0.0.0.0/0" ]; + description = '' + IP address ranges of clients allowed to make DNS queries. + ''; + }; + + api.address = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + IP address Recursor REST API server will bind to. + ''; + }; + + api.port = mkOption { + type = types.int; + default = 8082; + description = '' + Port number Recursor REST API server will bind to. + ''; + }; + + api.allowFrom = mkOption { + type = types.listOf types.str; + default = [ "0.0.0.0/0" ]; + description = '' + IP address ranges of clients allowed to make API requests. + ''; + }; + + exportHosts = mkOption { + type = types.bool; + default = false; + description = '' + Whether to export names and IP addresses defined in /etc/hosts. + ''; + }; + + forwardZones = mkOption { + type = types.attrs; + default = {}; + description = '' + DNS zones to be forwarded to other authoritative servers. + ''; + }; + + forwardZonesRecurse = mkOption { + type = types.attrs; + example = { eth = "127.0.0.1:5353"; }; + default = {}; + description = '' + DNS zones to be forwarded to other recursive servers. + ''; + }; + + dnssecValidation = mkOption { + type = types.enum ["off" "process-no-validate" "process" "log-fail" "validate"]; + default = "validate"; + description = '' + Controls the level of DNSSEC processing done by the PowerDNS Recursor. + See https://doc.powerdns.com/md/recursor/dnssec/ for a detailed explanation. + ''; + }; + + serveRFC1918 = mkOption { + type = types.bool; + default = true; + description = '' + Whether to directly resolve the RFC1918 reverse-mapping domains: + <literal>10.in-addr.arpa</literal>, + <literal>168.192.in-addr.arpa</literal>, + <literal>16-31.172.in-addr.arpa</literal> + This saves load on the AS112 servers. + ''; + }; + + settings = mkOption { + type = configType; + default = { }; + example = literalExample '' + { + loglevel = 8; + log-common-errors = true; + } + ''; + description = '' + PowerDNS Recursor settings. Use this option to configure Recursor + settings not exposed in a NixOS option or to bypass one. + See the full documentation at + <link xlink:href="https://doc.powerdns.com/recursor/settings.html"/> + for the available options. + ''; + }; + + luaConfig = mkOption { + type = types.lines; + default = ""; + description = '' + The content Lua configuration file for PowerDNS Recursor. See + <link xlink:href="https://doc.powerdns.com/recursor/lua-config/index.html"/>. + ''; + }; + }; + + config = mkIf cfg.enable { + + services.pdns-recursor.settings = mkDefaultAttrs { + local-address = cfg.dns.address; + local-port = cfg.dns.port; + allow-from = cfg.dns.allowFrom; + + webserver-address = cfg.api.address; + webserver-port = cfg.api.port; + webserver-allow-from = cfg.api.allowFrom; + + forward-zones = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZones; + forward-zones-recurse = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZonesRecurse; + export-etc-hosts = cfg.exportHosts; + dnssec = cfg.dnssecValidation; + serve-rfc1918 = cfg.serveRFC1918; + lua-config-file = pkgs.writeText "recursor.lua" cfg.luaConfig; + + log-timestamp = false; + disable-syslog = true; + }; + + users.users.${username} = { + home = dataDir; + createHome = true; + uid = config.ids.uids.pdns-recursor; + description = "PowerDNS Recursor daemon user"; + }; + + systemd.services.pdns-recursor = { + unitConfig.Documentation = "man:pdns_recursor(1) man:rec_control(1)"; + description = "PowerDNS recursive server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + User = username; + Restart ="on-failure"; + RestartSec = "5"; + PrivateTmp = true; + PrivateDevices = true; + AmbientCapabilities = "cap_net_bind_service"; + ExecStart = ''${pkgs.pdns-recursor}/bin/pdns_recursor \ + --config-dir=${dataDir} \ + --socket-dir=${dataDir} + ''; + }; + + preStart = '' + # Link configuration file into recursor home directory + configPath=${dataDir}/recursor.conf + if [ "$(realpath $configPath)" != "${configFile}" ]; then + rm -f $configPath + ln -s ${configFile} $configPath + fi + ''; + }; + }; + + imports = [ + (mkRemovedOptionModule [ "services" "pdns-recursor" "extraConfig" ] + "To change extra Recursor settings use services.pdns-recursor.settings instead.") + ]; + + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; + +} diff --git a/nixpkgs/nixos/modules/services/networking/pdnsd.nix b/nixpkgs/nixos/modules/services/networking/pdnsd.nix new file mode 100644 index 000000000000..24b5bbc5104e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/pdnsd.nix @@ -0,0 +1,91 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.pdnsd; + pdnsd = pkgs.pdnsd; + pdnsdUser = "pdnsd"; + pdnsdGroup = "pdnsd"; + pdnsdConf = pkgs.writeText "pdnsd.conf" + '' + global { + run_as=${pdnsdUser}; + cache_dir="${cfg.cacheDir}"; + ${cfg.globalConfig} + } + + server { + ${cfg.serverConfig} + } + ${cfg.extraConfig} + ''; +in + +{ options = + { services.pdnsd = + { enable = mkEnableOption "pdnsd"; + + cacheDir = mkOption { + type = types.str; + default = "/var/cache/pdnsd"; + description = "Directory holding the pdnsd cache"; + }; + + globalConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Global configuration that should be added to the global directory + of <literal>pdnsd.conf</literal>. + ''; + }; + + serverConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Server configuration that should be added to the server directory + of <literal>pdnsd.conf</literal>. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra configuration directives that should be added to + <literal>pdnsd.conf</literal>. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.${pdnsdUser} = { + uid = config.ids.uids.pdnsd; + group = pdnsdGroup; + description = "pdnsd user"; + }; + + users.groups.${pdnsdGroup} = { + gid = config.ids.gids.pdnsd; + }; + + systemd.services.pdnsd = + { wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + preStart = + '' + mkdir -p "${cfg.cacheDir}" + touch "${cfg.cacheDir}/pdnsd.cache" + chown -R ${pdnsdUser}:${pdnsdGroup} "${cfg.cacheDir}" + ''; + description = "pdnsd"; + serviceConfig = + { + ExecStart = "${pdnsd}/bin/pdnsd -c ${pdnsdConf}"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/pixiecore.nix b/nixpkgs/nixos/modules/services/networking/pixiecore.nix new file mode 100644 index 000000000000..85aa40784af8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/pixiecore.nix @@ -0,0 +1,134 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.pixiecore; +in +{ + meta.maintainers = with maintainers; [ bbigras danderson ]; + + options = { + services.pixiecore = { + enable = mkEnableOption "Pixiecore"; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports (67, 69 UDP and 4011, 'port', 'statusPort' TCP) in the firewall for Pixiecore. + ''; + }; + + mode = mkOption { + description = "Which mode to use"; + default = "boot"; + type = types.enum [ "api" "boot" ]; + }; + + debug = mkOption { + type = types.bool; + default = false; + description = "Log more things that aren't directly related to booting a recognized client"; + }; + + dhcpNoBind = mkOption { + type = types.bool; + default = false; + description = "Handle DHCP traffic without binding to the DHCP server port"; + }; + + kernel = mkOption { + type = types.str or types.path; + default = ""; + description = "Kernel path. Ignored unless mode is set to 'boot'"; + }; + + initrd = mkOption { + type = types.str or types.path; + default = ""; + description = "Initrd path. Ignored unless mode is set to 'boot'"; + }; + + cmdLine = mkOption { + type = types.str; + default = ""; + description = "Kernel commandline arguments. Ignored unless mode is set to 'boot'"; + }; + + listen = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "IPv4 address to listen on"; + }; + + port = mkOption { + type = types.port; + default = 80; + description = "Port to listen on for HTTP"; + }; + + statusPort = mkOption { + type = types.port; + default = 80; + description = "HTTP port for status information (can be the same as --port)"; + }; + + apiServer = mkOption { + type = types.str; + example = "localhost:8080"; + description = "host:port to connect to the API. Ignored unless mode is set to 'api'"; + }; + + extraArguments = mkOption { + type = types.listOf types.str; + default = []; + description = "Additional command line arguments to pass to Pixiecore"; + }; + }; + }; + + config = mkIf cfg.enable { + users.groups.pixiecore = {}; + users.users.pixiecore = { + description = "Pixiecore daemon user"; + group = "pixiecore"; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ 4011 cfg.port cfg.statusPort ]; + allowedUDPPorts = [ 67 69 ]; + }; + + systemd.services.pixiecore = { + description = "Pixiecore server"; + after = [ "network.target"]; + wants = [ "network.target"]; + wantedBy = [ "multi-user.target"]; + serviceConfig = { + User = "pixiecore"; + Restart = "always"; + AmbientCapabilities = [ "cap_net_bind_service" ] ++ optional cfg.dhcpNoBind "cap_net_raw"; + ExecStart = + let + argString = + if cfg.mode == "boot" + then [ "boot" cfg.kernel ] + ++ optional (cfg.initrd != "") cfg.initrd + ++ optionals (cfg.cmdLine != "") [ "--cmdline" cfg.cmdLine ] + else [ "api" cfg.apiServer ]; + in + '' + ${pkgs.pixiecore}/bin/pixiecore \ + ${lib.escapeShellArgs argString} \ + ${optionalString cfg.debug "--debug"} \ + ${optionalString cfg.dhcpNoBind "--dhcp-no-bind"} \ + --listen-addr ${lib.escapeShellArg cfg.listen} \ + --port ${toString cfg.port} \ + --status-port ${toString cfg.statusPort} \ + ${escapeShellArgs cfg.extraArguments} + ''; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/polipo.nix b/nixpkgs/nixos/modules/services/networking/polipo.nix new file mode 100644 index 000000000000..1ff9388346b6 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/polipo.nix @@ -0,0 +1,112 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.polipo; + + polipoConfig = pkgs.writeText "polipo.conf" '' + proxyAddress = ${cfg.proxyAddress} + proxyPort = ${toString cfg.proxyPort} + allowedClients = ${concatStringsSep ", " cfg.allowedClients} + ${optionalString (cfg.parentProxy != "") "parentProxy = ${cfg.parentProxy}" } + ${optionalString (cfg.socksParentProxy != "") "socksParentProxy = ${cfg.socksParentProxy}" } + ${config.services.polipo.extraConfig} + ''; + +in + +{ + + options = { + + services.polipo = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to run the polipo caching web proxy."; + }; + + proxyAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "IP address on which Polipo will listen."; + }; + + proxyPort = mkOption { + type = types.int; + default = 8123; + description = "TCP port on which Polipo will listen."; + }; + + allowedClients = mkOption { + type = types.listOf types.str; + default = [ "127.0.0.1" "::1" ]; + example = [ "127.0.0.1" "::1" "134.157.168.0/24" "2001:660:116::/48" ]; + description = '' + List of IP addresses or network addresses that may connect to Polipo. + ''; + }; + + parentProxy = mkOption { + type = types.str; + default = ""; + example = "localhost:8124"; + description = '' + Hostname and port number of an HTTP parent proxy; + it should have the form ‘host:port’. + ''; + }; + + socksParentProxy = mkOption { + type = types.str; + default = ""; + example = "localhost:9050"; + description = '' + Hostname and port number of an SOCKS parent proxy; + it should have the form ‘host:port’. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Polio configuration. Contents will be added + verbatim to the configuration file. + ''; + }; + + }; + + }; + + config = mkIf cfg.enable { + + users.users.polipo = + { uid = config.ids.uids.polipo; + description = "Polipo caching proxy user"; + home = "/var/cache/polipo"; + createHome = true; + }; + + users.groups.polipo = + { gid = config.ids.gids.polipo; + members = [ "polipo" ]; + }; + + systemd.services.polipo = { + description = "caching web proxy"; + after = [ "network.target" "nss-lookup.target" ]; + wantedBy = [ "multi-user.target"]; + serviceConfig = { + ExecStart = "${pkgs.polipo}/bin/polipo -c ${polipoConfig}"; + User = "polipo"; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/powerdns.nix b/nixpkgs/nixos/modules/services/networking/powerdns.nix new file mode 100644 index 000000000000..ba05e15389f6 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/powerdns.nix @@ -0,0 +1,49 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.powerdns; + configDir = pkgs.writeTextDir "pdns.conf" "${cfg.extraConfig}"; +in { + options = { + services.powerdns = { + enable = mkEnableOption "Powerdns domain name server"; + + extraConfig = mkOption { + type = types.lines; + default = "launch=bind"; + description = '' + Extra lines to be added verbatim to pdns.conf. + Powerdns will chroot to /var/lib/powerdns. + So any file, powerdns is supposed to be read, + should be in /var/lib/powerdns and needs to specified + relative to the chroot. + ''; + }; + }; + }; + + config = mkIf config.services.powerdns.enable { + systemd.services.pdns = { + unitConfig.Documentation = "man:pdns_server(1) man:pdns_control(1)"; + description = "Powerdns name server"; + wantedBy = [ "multi-user.target" ]; + after = ["network.target" "mysql.service" "postgresql.service" "openldap.service"]; + + serviceConfig = { + Restart="on-failure"; + RestartSec="1"; + StartLimitInterval="0"; + PrivateDevices=true; + CapabilityBoundingSet="CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT"; + NoNewPrivileges=true; + ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /var/lib/powerdns"; + ExecStart = "${pkgs.powerdns}/bin/pdns_server --setuid=nobody --setgid=nogroup --chroot=/var/lib/powerdns --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}"; + ProtectSystem="full"; + ProtectHome=true; + RestrictAddressFamilies="AF_UNIX AF_INET AF_INET6"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/pppd.nix b/nixpkgs/nixos/modules/services/networking/pppd.nix new file mode 100644 index 000000000000..c1cbdb461765 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/pppd.nix @@ -0,0 +1,136 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.pppd; +in +{ + meta = { + maintainers = with maintainers; [ danderson ]; + }; + + options = { + services.pppd = { + enable = mkEnableOption "pppd"; + + package = mkOption { + default = pkgs.ppp; + defaultText = "pkgs.ppp"; + type = types.package; + description = "pppd package to use."; + }; + + peers = mkOption { + default = {}; + description = "pppd peers."; + type = types.attrsOf (types.submodule ( + { name, ... }: + { + options = { + name = mkOption { + type = types.str; + default = name; + example = "dialup"; + description = "Name of the PPP peer."; + }; + + enable = mkOption { + type = types.bool; + default = true; + example = false; + description = "Whether to enable this PPP peer."; + }; + + autostart = mkOption { + type = types.bool; + default = true; + example = false; + description = "Whether the PPP session is automatically started at boot time."; + }; + + config = mkOption { + type = types.lines; + default = ""; + description = "pppd configuration for this peer, see the pppd(8) man page."; + }; + }; + })); + }; + }; + }; + + config = let + enabledConfigs = filter (f: f.enable) (attrValues cfg.peers); + + mkEtc = peerCfg: { + name = "ppp/peers/${peerCfg.name}"; + value.text = peerCfg.config; + }; + + mkSystemd = peerCfg: { + name = "pppd-${peerCfg.name}"; + value = { + restartTriggers = [ config.environment.etc."ppp/peers/${peerCfg.name}".source ]; + before = [ "network.target" ]; + wants = [ "network.target" ]; + after = [ "network-pre.target" ]; + environment = { + # pppd likes to write directly into /var/run. This is rude + # on a modern system, so we use libredirect to transparently + # move those files into /run/pppd. + LD_PRELOAD = "${pkgs.libredirect}/lib/libredirect.so"; + NIX_REDIRECTS = "/var/run=/run/pppd"; + }; + serviceConfig = { + ExecStart = "${getBin cfg.package}/sbin/pppd call ${peerCfg.name} nodetach nolog"; + Restart = "always"; + RestartSec = 5; + + AmbientCapabilities = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN"; + CapabilityBoundingSet = "CAP_SYS_TTY_CONFIG CAP_NET_ADMIN CAP_NET_RAW CAP_SYS_ADMIN"; + KeyringMode = "private"; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateMounts = true; + PrivateTmp = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelModules = true; + # pppd can be configured to tweak kernel settings. + ProtectKernelTunables = false; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = "AF_PACKET AF_UNIX AF_PPPOX AF_ATMPVC AF_ATMSVC AF_INET AF_INET6 AF_IPX"; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SecureBits = "no-setuid-fixup-locked noroot-locked"; + SystemCallFilter = "@system-service"; + SystemCallArchitectures = "native"; + + # All pppd instances on a system must share a runtime + # directory in order for PPP multilink to work correctly. So + # we give all instances the same /run/pppd directory to store + # things in. + # + # For the same reason, we can't set PrivateUsers=true, because + # all instances need to run as the same user to access the + # multilink database. + RuntimeDirectory = "pppd"; + RuntimeDirectoryPreserve = true; + }; + wantedBy = mkIf peerCfg.autostart [ "multi-user.target" ]; + }; + }; + + etcFiles = listToAttrs (map mkEtc enabledConfigs); + systemdConfigs = listToAttrs (map mkSystemd enabledConfigs); + + in mkIf cfg.enable { + environment.etc = etcFiles; + systemd.services = systemdConfigs; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/pptpd.nix b/nixpkgs/nixos/modules/services/networking/pptpd.nix new file mode 100644 index 000000000000..3e7753b9dd35 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/pptpd.nix @@ -0,0 +1,124 @@ +{ config, pkgs, lib, ... }: + +with lib; + +{ + options = { + services.pptpd = { + enable = mkEnableOption "pptpd, the Point-to-Point Tunneling Protocol daemon"; + + serverIp = mkOption { + type = types.str; + description = "The server-side IP address."; + default = "10.124.124.1"; + }; + + clientIpRange = mkOption { + type = types.str; + description = "The range from which client IPs are drawn."; + default = "10.124.124.2-11"; + }; + + maxClients = mkOption { + type = types.int; + description = "The maximum number of simultaneous connections."; + default = 10; + }; + + extraPptpdOptions = mkOption { + type = types.lines; + description = "Adds extra lines to the pptpd configuration file."; + default = ""; + }; + + extraPppdOptions = mkOption { + type = types.lines; + description = "Adds extra lines to the pppd options file."; + default = ""; + example = '' + ms-dns 8.8.8.8 + ms-dns 8.8.4.4 + ''; + }; + }; + }; + + config = mkIf config.services.pptpd.enable { + systemd.services.pptpd = let + cfg = config.services.pptpd; + + pptpd-conf = pkgs.writeText "pptpd.conf" '' + # Inspired from pptpd-1.4.0/samples/pptpd.conf + ppp ${ppp-pptpd-wrapped}/bin/pppd + option ${pppd-options} + pidfile /run/pptpd.pid + localip ${cfg.serverIp} + remoteip ${cfg.clientIpRange} + connections ${toString cfg.maxClients} # (Will get harmless warning if inconsistent with IP range) + + # Extra + ${cfg.extraPptpdOptions} + ''; + + pppd-options = pkgs.writeText "ppp-options-pptpd.conf" '' + # From: cat pptpd-1.4.0/samples/options.pptpd | grep -v ^# | grep -v ^$ + name pptpd + refuse-pap + refuse-chap + refuse-mschap + require-mschap-v2 + require-mppe-128 + proxyarp + lock + nobsdcomp + novj + novjccomp + nologfd + + # Extra: + ${cfg.extraPppdOptions} + ''; + + ppp-pptpd-wrapped = pkgs.stdenv.mkDerivation { + name = "ppp-pptpd-wrapped"; + phases = [ "installPhase" ]; + buildInputs = with pkgs; [ makeWrapper ]; + installPhase = '' + mkdir -p $out/bin + makeWrapper ${pkgs.ppp}/bin/pppd $out/bin/pppd \ + --set LD_PRELOAD "${pkgs.libredirect}/lib/libredirect.so" \ + --set NIX_REDIRECTS "/etc/ppp=/etc/ppp-pptpd" + ''; + }; + in { + description = "pptpd server"; + + requires = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + preStart = '' + mkdir -p -m 700 /etc/ppp-pptpd + + secrets="/etc/ppp-pptpd/chap-secrets" + + [ -f "$secrets" ] || cat > "$secrets" << EOF + # From: pptpd-1.4.0/samples/chap-secrets + # Secrets for authentication using CHAP + # client server secret IP addresses + #username pptpd password * + EOF + + chown root.root "$secrets" + chmod 600 "$secrets" + ''; + + serviceConfig = { + ExecStart = "${pkgs.pptpd}/bin/pptpd --conf ${pptpd-conf}"; + KillMode = "process"; + Restart = "on-success"; + Type = "forking"; + PIDFile = "/run/pptpd.pid"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/prayer.nix b/nixpkgs/nixos/modules/services/networking/prayer.nix new file mode 100644 index 000000000000..f04dac01d9b8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/prayer.nix @@ -0,0 +1,89 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (pkgs) prayer; + + cfg = config.services.prayer; + + stateDir = "/var/lib/prayer"; + + prayerUser = "prayer"; + prayerGroup = "prayer"; + + prayerExtraCfg = pkgs.writeText "extraprayer.cf" '' + prefix = "${prayer}" + var_prefix = "${stateDir}" + prayer_user = "${prayerUser}" + prayer_group = "${prayerGroup}" + sendmail_path = "/run/wrappers/bin/sendmail" + + use_http_port ${cfg.port} + + ${cfg.extraConfig} + ''; + + prayerCfg = pkgs.runCommand "prayer.cf" { preferLocalBuild = true; } '' + # We have to remove the http_port 80, or it will start a server there + cat ${prayer}/etc/prayer.cf | grep -v http_port > $out + cat ${prayerExtraCfg} >> $out + ''; + +in + +{ + + ###### interface + + options = { + + services.prayer = { + + enable = mkEnableOption "the prayer webmail http server"; + + port = mkOption { + default = "2080"; + description = '' + Port the prayer http server is listening to. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = "" ; + description = '' + Extra configuration. Contents will be added verbatim to the configuration file. + ''; + }; + }; + + }; + + + ###### implementation + + config = mkIf config.services.prayer.enable { + environment.systemPackages = [ prayer ]; + + users.users.${prayerUser} = + { uid = config.ids.uids.prayer; + description = "Prayer daemon user"; + home = stateDir; + }; + + users.groups.${prayerGroup} = + { gid = config.ids.gids.prayer; }; + + systemd.services.prayer = { + wantedBy = [ "multi-user.target" ]; + serviceConfig.Type = "forking"; + preStart = '' + mkdir -m 0755 -p ${stateDir} + chown ${prayerUser}.${prayerGroup} ${stateDir} + ''; + script = "${prayer}/sbin/prayer --config-file=${prayerCfg}"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/privoxy.nix b/nixpkgs/nixos/modules/services/networking/privoxy.nix new file mode 100644 index 000000000000..1f41c720adf5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/privoxy.nix @@ -0,0 +1,114 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (pkgs) privoxy; + + cfg = config.services.privoxy; + + confFile = pkgs.writeText "privoxy.conf" '' + user-manual ${privoxy}/share/doc/privoxy/user-manual + confdir ${privoxy}/etc/ + listen-address ${cfg.listenAddress} + enable-edit-actions ${if (cfg.enableEditActions == true) then "1" else "0"} + ${concatMapStrings (f: "actionsfile ${f}\n") cfg.actionsFiles} + ${concatMapStrings (f: "filterfile ${f}\n") cfg.filterFiles} + ${cfg.extraConfig} + ''; + +in + +{ + + ###### interface + + options = { + + services.privoxy = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the Privoxy non-caching filtering proxy. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "127.0.0.1:8118"; + description = '' + Address the proxy server is listening to. + ''; + }; + + actionsFiles = mkOption { + type = types.listOf types.str; + example = [ "match-all.action" "default.action" "/etc/privoxy/user.action" ]; + default = [ "match-all.action" "default.action" ]; + description = '' + List of paths to Privoxy action files. + These paths may either be absolute or relative to the privoxy configuration directory. + ''; + }; + + filterFiles = mkOption { + type = types.listOf types.str; + example = [ "default.filter" "/etc/privoxy/user.filter" ]; + default = [ "default.filter" ]; + description = '' + List of paths to Privoxy filter files. + These paths may either be absolute or relative to the privoxy configuration directory. + ''; + }; + + enableEditActions = mkOption { + type = types.bool; + default = false; + description = '' + Whether or not the web-based actions file editor may be used. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = "" ; + description = '' + Extra configuration. Contents will be added verbatim to the configuration file. + ''; + }; + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + users.users.privoxy = { + isSystemUser = true; + home = "/var/empty"; + group = "privoxy"; + }; + + users.groups.privoxy = {}; + + systemd.services.privoxy = { + description = "Filtering web proxy"; + after = [ "network.target" "nss-lookup.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig.ExecStart = "${privoxy}/bin/privoxy --no-daemon --user privoxy ${confFile}"; + + serviceConfig.PrivateDevices = true; + serviceConfig.PrivateTmp = true; + serviceConfig.ProtectHome = true; + serviceConfig.ProtectSystem = "full"; + }; + + }; + + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; + +} diff --git a/nixpkgs/nixos/modules/services/networking/prosody.nix b/nixpkgs/nixos/modules/services/networking/prosody.nix new file mode 100644 index 000000000000..cdd341c9fb62 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/prosody.nix @@ -0,0 +1,882 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.prosody; + + sslOpts = { ... }: { + + options = { + + key = mkOption { + type = types.path; + description = "Path to the key file."; + }; + + # TODO: rename to certificate to match the prosody config + cert = mkOption { + type = types.path; + description = "Path to the certificate file."; + }; + + extraOptions = mkOption { + type = types.attrs; + default = {}; + description = "Extra SSL configuration options."; + }; + + }; + }; + + discoOpts = { + options = { + url = mkOption { + type = types.str; + description = "URL of the endpoint you want to make discoverable"; + }; + description = mkOption { + type = types.str; + description = "A short description of the endpoint you want to advertise"; + }; + }; + }; + + moduleOpts = { + # Required for compliance with https://compliance.conversations.im/about/ + roster = mkOption { + type = types.bool; + default = true; + description = "Allow users to have a roster"; + }; + + saslauth = mkOption { + type = types.bool; + default = true; + description = "Authentication for clients and servers. Recommended if you want to log in."; + }; + + tls = mkOption { + type = types.bool; + default = true; + description = "Add support for secure TLS on c2s/s2s connections"; + }; + + dialback = mkOption { + type = types.bool; + default = true; + description = "s2s dialback support"; + }; + + disco = mkOption { + type = types.bool; + default = true; + description = "Service discovery"; + }; + + # Not essential, but recommended + carbons = mkOption { + type = types.bool; + default = true; + description = "Keep multiple clients in sync"; + }; + + csi = mkOption { + type = types.bool; + default = true; + description = "Implements the CSI protocol that allows clients to report their active/inactive state to the server"; + }; + + cloud_notify = mkOption { + type = types.bool; + default = true; + description = "Push notifications to inform users of new messages or other pertinent information even when they have no XMPP clients online"; + }; + + pep = mkOption { + type = types.bool; + default = true; + description = "Enables users to publish their mood, activity, playing music and more"; + }; + + private = mkOption { + type = types.bool; + default = true; + description = "Private XML storage (for room bookmarks, etc.)"; + }; + + blocklist = mkOption { + type = types.bool; + default = true; + description = "Allow users to block communications with other users"; + }; + + vcard = mkOption { + type = types.bool; + default = false; + description = "Allow users to set vCards"; + }; + + vcard_legacy = mkOption { + type = types.bool; + default = true; + description = "Converts users profiles and Avatars between old and new formats"; + }; + + bookmarks = mkOption { + type = types.bool; + default = true; + description = "Allows interop between older clients that use XEP-0048: Bookmarks in its 1.0 version and recent clients which use it in PEP"; + }; + + # Nice to have + version = mkOption { + type = types.bool; + default = true; + description = "Replies to server version requests"; + }; + + uptime = mkOption { + type = types.bool; + default = true; + description = "Report how long server has been running"; + }; + + time = mkOption { + type = types.bool; + default = true; + description = "Let others know the time here on this server"; + }; + + ping = mkOption { + type = types.bool; + default = true; + description = "Replies to XMPP pings with pongs"; + }; + + register = mkOption { + type = types.bool; + default = true; + description = "Allow users to register on this server using a client and change passwords"; + }; + + mam = mkOption { + type = types.bool; + default = true; + description = "Store messages in an archive and allow users to access it"; + }; + + smacks = mkOption { + type = types.bool; + default = true; + description = "Allow a client to resume a disconnected session, and prevent message loss"; + }; + + # Admin interfaces + admin_adhoc = mkOption { + type = types.bool; + default = true; + description = "Allows administration via an XMPP client that supports ad-hoc commands"; + }; + + http_files = mkOption { + type = types.bool; + default = true; + description = "Serve static files from a directory over HTTP"; + }; + + proxy65 = mkOption { + type = types.bool; + default = true; + description = "Enables a file transfer proxy service which clients behind NAT can use"; + }; + + admin_telnet = mkOption { + type = types.bool; + default = false; + description = "Opens telnet console interface on localhost port 5582"; + }; + + # HTTP modules + bosh = mkOption { + type = types.bool; + default = false; + description = "Enable BOSH clients, aka 'Jabber over HTTP'"; + }; + + websocket = mkOption { + type = types.bool; + default = false; + description = "Enable WebSocket support"; + }; + + # Other specific functionality + limits = mkOption { + type = types.bool; + default = false; + description = "Enable bandwidth limiting for XMPP connections"; + }; + + groups = mkOption { + type = types.bool; + default = false; + description = "Shared roster support"; + }; + + server_contact_info = mkOption { + type = types.bool; + default = false; + description = "Publish contact information for this service"; + }; + + announce = mkOption { + type = types.bool; + default = false; + description = "Send announcement to all online users"; + }; + + welcome = mkOption { + type = types.bool; + default = false; + description = "Welcome users who register accounts"; + }; + + watchregistrations = mkOption { + type = types.bool; + default = false; + description = "Alert admins of registrations"; + }; + + motd = mkOption { + type = types.bool; + default = false; + description = "Send a message to users when they log in"; + }; + + legacyauth = mkOption { + type = types.bool; + default = false; + description = "Legacy authentication. Only used by some old clients and bots"; + }; + }; + + toLua = x: + if builtins.isString x then ''"${x}"'' + else if builtins.isBool x then (if x == true then "true" else "false") + else if builtins.isInt x then toString x + else if builtins.isList x then ''{ ${lib.concatStringsSep ", " (map (n: toLua n) x) } }'' + else throw "Invalid Lua value"; + + createSSLOptsStr = o: '' + ssl = { + cafile = "/etc/ssl/certs/ca-bundle.crt"; + key = "${o.key}"; + certificate = "${o.cert}"; + ${concatStringsSep "\n" (mapAttrsToList (name: value: "${name} = ${toLua value};") o.extraOptions)} + }; + ''; + + mucOpts = { ... }: { + options = { + domain = mkOption { + type = types.str; + description = "Domain name of the MUC"; + }; + name = mkOption { + type = types.str; + description = "The name to return in service discovery responses for the MUC service itself"; + default = "Prosody Chatrooms"; + }; + restrictRoomCreation = mkOption { + type = types.enum [ true false "admin" "local" ]; + default = false; + description = "Restrict room creation to server admins"; + }; + maxHistoryMessages = mkOption { + type = types.int; + default = 20; + description = "Specifies a limit on what each room can be configured to keep"; + }; + roomLocking = mkOption { + type = types.bool; + default = true; + description = '' + Enables room locking, which means that a room must be + configured before it can be used. Locked rooms are invisible + and cannot be entered by anyone but the creator + ''; + }; + roomLockTimeout = mkOption { + type = types.int; + default = 300; + description = '' + Timout after which the room is destroyed or unlocked if not + configured, in seconds + ''; + }; + tombstones = mkOption { + type = types.bool; + default = true; + description = '' + When a room is destroyed, it leaves behind a tombstone which + prevents the room being entered or recreated. It also allows + anyone who was not in the room at the time it was destroyed + to learn about it, and to update their bookmarks. Tombstones + prevents the case where someone could recreate a previously + semi-anonymous room in order to learn the real JIDs of those + who often join there. + ''; + }; + tombstoneExpiry = mkOption { + type = types.int; + default = 2678400; + description = '' + This settings controls how long a tombstone is considered + valid. It defaults to 31 days. After this time, the room in + question can be created again. + ''; + }; + + vcard_muc = mkOption { + type = types.bool; + default = true; + description = "Adds the ability to set vCard for Multi User Chat rooms"; + }; + + # Extra parameters. Defaulting to prosody default values. + # Adding them explicitly to make them visible from the options + # documentation. + # + # See https://prosody.im/doc/modules/mod_muc for more details. + roomDefaultPublic = mkOption { + type = types.bool; + default = true; + description = "If set, the MUC rooms will be public by default."; + }; + roomDefaultMembersOnly = mkOption { + type = types.bool; + default = false; + description = "If set, the MUC rooms will only be accessible to the members by default."; + }; + roomDefaultModerated = mkOption { + type = types.bool; + default = false; + description = "If set, the MUC rooms will be moderated by default."; + }; + roomDefaultPublicJids = mkOption { + type = types.bool; + default = false; + description = "If set, the MUC rooms will display the public JIDs by default."; + }; + roomDefaultChangeSubject = mkOption { + type = types.bool; + default = false; + description = "If set, the rooms will display the public JIDs by default."; + }; + roomDefaultHistoryLength = mkOption { + type = types.int; + default = 20; + description = "Number of history message sent to participants by default."; + }; + roomDefaultLanguage = mkOption { + type = types.str; + default = "en"; + description = "Default room language."; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Additional MUC specific configuration"; + }; + }; + }; + + uploadHttpOpts = { ... }: { + options = { + domain = mkOption { + type = types.nullOr types.str; + description = "Domain name for the http-upload service"; + }; + uploadFileSizeLimit = mkOption { + type = types.str; + default = "50 * 1024 * 1024"; + description = "Maximum file size, in bytes. Defaults to 50MB."; + }; + uploadExpireAfter = mkOption { + type = types.str; + default = "60 * 60 * 24 * 7"; + description = "Max age of a file before it gets deleted, in seconds."; + }; + userQuota = mkOption { + type = types.nullOr types.int; + default = null; + example = 1234; + description = '' + Maximum size of all uploaded files per user, in bytes. There + will be no quota if this option is set to null. + ''; + }; + httpUploadPath = mkOption { + type = types.str; + description = '' + Directory where the uploaded files will be stored. By + default, uploaded files are put in a sub-directory of the + default Prosody storage path (usually /var/lib/prosody). + ''; + default = "/var/lib/prosody"; + }; + }; + }; + + vHostOpts = { ... }: { + + options = { + + # TODO: require attribute + domain = mkOption { + type = types.str; + description = "Domain name"; + }; + + enabled = mkOption { + type = types.bool; + default = false; + description = "Whether to enable the virtual host"; + }; + + ssl = mkOption { + type = types.nullOr (types.submodule sslOpts); + default = null; + description = "Paths to SSL files"; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Additional virtual host specific configuration"; + }; + + }; + + }; + +in + +{ + + ###### interface + + options = { + + services.prosody = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable the prosody server"; + }; + + xmppComplianceSuite = mkOption { + type = types.bool; + default = true; + description = '' + The XEP-0423 defines a set of recommended XEPs to implement + for a server. It's generally a good idea to implement this + set of extensions if you want to provide your users with a + good XMPP experience. + + This NixOS module aims to provide a "advanced server" + experience as per defined in the XEP-0423[1] specification. + + Setting this option to true will prevent you from building a + NixOS configuration which won't comply with this standard. + You can explicitely decide to ignore this standard if you + know what you are doing by setting this option to false. + + [1] https://xmpp.org/extensions/xep-0423.html + ''; + }; + + package = mkOption { + type = types.package; + description = "Prosody package to use"; + default = pkgs.prosody; + defaultText = "pkgs.prosody"; + example = literalExample '' + pkgs.prosody.override { + withExtraLibs = [ pkgs.luaPackages.lpty ]; + withCommunityModules = [ "auth_external" ]; + }; + ''; + }; + + dataDir = mkOption { + type = types.path; + description = "Directory where Prosody stores its data"; + default = "/var/lib/prosody"; + }; + + disco_items = mkOption { + type = types.listOf (types.submodule discoOpts); + default = []; + description = "List of discoverable items you want to advertise."; + }; + + user = mkOption { + type = types.str; + default = "prosody"; + description = "User account under which prosody runs."; + }; + + group = mkOption { + type = types.str; + default = "prosody"; + description = "Group account under which prosody runs."; + }; + + allowRegistration = mkOption { + type = types.bool; + default = false; + description = "Allow account creation"; + }; + + # HTTP server-related options + httpPorts = mkOption { + type = types.listOf types.int; + description = "Listening HTTP ports list for this service."; + default = [ 5280 ]; + }; + + httpInterfaces = mkOption { + type = types.listOf types.str; + default = [ "*" "::" ]; + description = "Interfaces on which the HTTP server will listen on."; + }; + + httpsPorts = mkOption { + type = types.listOf types.int; + description = "Listening HTTPS ports list for this service."; + default = [ 5281 ]; + }; + + httpsInterfaces = mkOption { + type = types.listOf types.str; + default = [ "*" "::" ]; + description = "Interfaces on which the HTTPS server will listen on."; + }; + + c2sRequireEncryption = mkOption { + type = types.bool; + default = true; + description = '' + Force clients to use encrypted connections? This option will + prevent clients from authenticating unless they are using encryption. + ''; + }; + + s2sRequireEncryption = mkOption { + type = types.bool; + default = true; + description = '' + Force servers to use encrypted connections? This option will + prevent servers from authenticating unless they are using encryption. + Note that this is different from authentication. + ''; + }; + + s2sSecureAuth = mkOption { + type = types.bool; + default = false; + description = '' + Force certificate authentication for server-to-server connections? + This provides ideal security, but requires servers you communicate + with to support encryption AND present valid, trusted certificates. + For more information see https://prosody.im/doc/s2s#security + ''; + }; + + s2sInsecureDomains = mkOption { + type = types.listOf types.str; + default = []; + example = [ "insecure.example.com" ]; + description = '' + Some servers have invalid or self-signed certificates. You can list + remote domains here that will not be required to authenticate using + certificates. They will be authenticated using DNS instead, even + when s2s_secure_auth is enabled. + ''; + }; + + s2sSecureDomains = mkOption { + type = types.listOf types.str; + default = []; + example = [ "jabber.org" ]; + description = '' + Even if you leave s2s_secure_auth disabled, you can still require valid + certificates for some domains by specifying a list here. + ''; + }; + + + modules = moduleOpts; + + extraModules = mkOption { + type = types.listOf types.str; + default = []; + description = "Enable custom modules"; + }; + + extraPluginPaths = mkOption { + type = types.listOf types.path; + default = []; + description = "Addtional path in which to look find plugins/modules"; + }; + + uploadHttp = mkOption { + description = '' + Configures the Prosody builtin HTTP server to handle user uploads. + ''; + type = types.nullOr (types.submodule uploadHttpOpts); + default = null; + example = { + domain = "uploads.my-xmpp-example-host.org"; + }; + }; + + muc = mkOption { + type = types.listOf (types.submodule mucOpts); + default = [ ]; + example = [ { + domain = "conference.my-xmpp-example-host.org"; + } ]; + description = "Multi User Chat (MUC) configuration"; + }; + + virtualHosts = mkOption { + + description = "Define the virtual hosts"; + + type = with types; loaOf (submodule vHostOpts); + + example = { + myhost = { + domain = "my-xmpp-example-host.org"; + enabled = true; + }; + }; + + default = { + localhost = { + domain = "localhost"; + enabled = true; + }; + }; + + }; + + ssl = mkOption { + type = types.nullOr (types.submodule sslOpts); + default = null; + description = "Paths to SSL files"; + }; + + admins = mkOption { + type = types.listOf types.str; + default = []; + example = [ "admin1@example.com" "admin2@example.com" ]; + description = "List of administrators of the current host"; + }; + + authentication = mkOption { + type = types.enum [ "internal_plain" "internal_hashed" "cyrus" "anonymous" ]; + default = "internal_hashed"; + example = "internal_plain"; + description = "Authentication mechanism used for logins."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Additional prosody configuration"; + }; + + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + assertions = let + genericErrMsg = '' + + Having a server not XEP-0423-compliant might make your XMPP + experience terrible. See the NixOS manual for further + informations. + + If you know what you're doing, you can disable this warning by + setting config.services.prosody.xmppComplianceSuite to false. + ''; + errors = [ + { assertion = (builtins.length cfg.muc > 0) || !cfg.xmppComplianceSuite; + message = '' + You need to setup at least a MUC domain to comply with + XEP-0423. + '' + genericErrMsg;} + { assertion = cfg.uploadHttp != null || !cfg.xmppComplianceSuite; + message = '' + You need to setup the uploadHttp module through + config.services.prosody.uploadHttp to comply with + XEP-0423. + '' + genericErrMsg;} + ]; + in errors; + + environment.systemPackages = [ cfg.package ]; + + environment.etc."prosody/prosody.cfg.lua".text = + let + httpDiscoItems = if (cfg.uploadHttp != null) + then [{ url = cfg.uploadHttp.domain; description = "HTTP upload endpoint";}] + else []; + mucDiscoItems = builtins.foldl' + (acc: muc: [{ url = muc.domain; description = "${muc.domain} MUC endpoint";}] ++ acc) + [] + cfg.muc; + discoItems = cfg.disco_items ++ httpDiscoItems ++ mucDiscoItems; + in '' + + pidfile = "/run/prosody/prosody.pid" + + log = "*syslog" + + data_path = "${cfg.dataDir}" + plugin_paths = { + ${lib.concatStringsSep ", " (map (n: "\"${n}\"") cfg.extraPluginPaths) } + } + + ${ optionalString (cfg.ssl != null) (createSSLOptsStr cfg.ssl) } + + admins = ${toLua cfg.admins} + + -- we already build with libevent, so we can just enable it for a more performant server + use_libevent = true + + modules_enabled = { + + ${ lib.concatStringsSep "\n " (lib.mapAttrsToList + (name: val: optionalString val "${toLua name};") + cfg.modules) } + ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.package.communityModules)} + ${ lib.concatStringsSep "\n" (map (x: "${toLua x};") cfg.extraModules)} + }; + + disco_items = { + ${ lib.concatStringsSep "\n" (builtins.map (x: ''{ "${x.url}", "${x.description}"};'') discoItems)} + }; + + allow_registration = ${toLua cfg.allowRegistration} + + c2s_require_encryption = ${toLua cfg.c2sRequireEncryption} + + s2s_require_encryption = ${toLua cfg.s2sRequireEncryption} + + s2s_secure_auth = ${toLua cfg.s2sSecureAuth} + + s2s_insecure_domains = ${toLua cfg.s2sInsecureDomains} + + s2s_secure_domains = ${toLua cfg.s2sSecureDomains} + + authentication = ${toLua cfg.authentication} + + http_interfaces = ${toLua cfg.httpInterfaces} + + https_interfaces = ${toLua cfg.httpsInterfaces} + + http_ports = ${toLua cfg.httpPorts} + + https_ports = ${toLua cfg.httpsPorts} + + ${ cfg.extraConfig } + + ${lib.concatMapStrings (muc: '' + Component ${toLua muc.domain} "muc" + modules_enabled = { "muc_mam"; ${optionalString muc.vcard_muc ''"vcard_muc";'' } } + name = ${toLua muc.name} + restrict_room_creation = ${toLua muc.restrictRoomCreation} + max_history_messages = ${toLua muc.maxHistoryMessages} + muc_room_locking = ${toLua muc.roomLocking} + muc_room_lock_timeout = ${toLua muc.roomLockTimeout} + muc_tombstones = ${toLua muc.tombstones} + muc_tombstone_expiry = ${toLua muc.tombstoneExpiry} + muc_room_default_public = ${toLua muc.roomDefaultPublic} + muc_room_default_members_only = ${toLua muc.roomDefaultMembersOnly} + muc_room_default_moderated = ${toLua muc.roomDefaultModerated} + muc_room_default_public_jids = ${toLua muc.roomDefaultPublicJids} + muc_room_default_change_subject = ${toLua muc.roomDefaultChangeSubject} + muc_room_default_history_length = ${toLua muc.roomDefaultHistoryLength} + muc_room_default_language = ${toLua muc.roomDefaultLanguage} + ${ muc.extraConfig } + '') cfg.muc} + + ${ lib.optionalString (cfg.uploadHttp != null) '' + Component ${toLua cfg.uploadHttp.domain} "http_upload" + http_upload_file_size_limit = ${cfg.uploadHttp.uploadFileSizeLimit} + http_upload_expire_after = ${cfg.uploadHttp.uploadExpireAfter} + ${lib.optionalString (cfg.uploadHttp.userQuota != null) "http_upload_quota = ${toLua cfg.uploadHttp.userQuota}"} + http_upload_path = ${toLua cfg.uploadHttp.httpUploadPath} + ''} + + ${ lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: '' + VirtualHost "${v.domain}" + enabled = ${boolToString v.enabled}; + ${ optionalString (v.ssl != null) (createSSLOptsStr v.ssl) } + ${ v.extraConfig } + '') cfg.virtualHosts) } + ''; + + users.users.prosody = mkIf (cfg.user == "prosody") { + uid = config.ids.uids.prosody; + description = "Prosody user"; + createHome = true; + inherit (cfg) group; + home = "${cfg.dataDir}"; + }; + + users.groups.prosody = mkIf (cfg.group == "prosody") { + gid = config.ids.gids.prosody; + }; + + systemd.services.prosody = { + description = "Prosody XMPP server"; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ config.environment.etc."prosody/prosody.cfg.lua".source ]; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + Type = "forking"; + RuntimeDirectory = [ "prosody" ]; + PIDFile = "/run/prosody/prosody.pid"; + ExecStart = "${cfg.package}/bin/prosodyctl start"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + + MemoryDenyWriteExecute = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + }; + }; + + }; + meta.doc = ./prosody.xml; +} diff --git a/nixpkgs/nixos/modules/services/networking/prosody.xml b/nixpkgs/nixos/modules/services/networking/prosody.xml new file mode 100644 index 000000000000..7859cb1578b7 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/prosody.xml @@ -0,0 +1,88 @@ +<chapter xmlns="http://docbook.org/ns/docbook" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xi="http://www.w3.org/2001/XInclude" + version="5.0" + xml:id="module-services-prosody"> + <title>Prosody</title> + <para> + <link xlink:href="https://prosody.im/">Prosody</link> is an open-source, modern XMPP server. + </para> + <section xml:id="module-services-prosody-basic-usage"> + <title>Basic usage</title> + + <para> + A common struggle for most XMPP newcomers is to find the right set + of XMPP Extensions (XEPs) to setup. Forget to activate a few of + those and your XMPP experience might turn into a nightmare! + </para> + + <para> + The XMPP community tackles this problem by creating a meta-XEP + listing a decent set of XEPs you should implement. This meta-XEP + is issued every year, the 2020 edition being + <link xlink:href="https://xmpp.org/extensions/xep-0423.html">XEP-0423</link>. + </para> + <para> + The NixOS Prosody module will implement most of these recommendend XEPs out of + the box. That being said, two components still require some + manual configuration: the + <link xlink:href="https://xmpp.org/extensions/xep-0045.html">Multi User Chat (MUC)</link> + and the <link xlink:href="https://xmpp.org/extensions/xep-0363.html">HTTP File Upload</link> ones. + You'll need to create a DNS subdomain for each of those. The current convention is to name your + MUC endpoint <literal>conference.example.org</literal> and your HTTP upload domain <literal>upload.example.org</literal>. + </para> + <para> + A good configuration to start with, including a + <link xlink:href="https://xmpp.org/extensions/xep-0045.html">Multi User Chat (MUC)</link> + endpoint as well as a <link xlink:href="https://xmpp.org/extensions/xep-0363.html">HTTP File Upload</link> + endpoint will look like this: + <programlisting> +services.prosody = { + <link linkend="opt-services.prosody.enable">enable</link> = true; + <link linkend="opt-services.prosody.admins">admins</link> = [ "root@example.org" ]; + <link linkend="opt-services.prosody.ssl.cert">ssl.cert</link> = "/var/lib/acme/example.org/fullchain.pem"; + <link linkend="opt-services.prosody.ssl.key">ssl.key</link> = "/var/lib/acme/example.org/key.pem"; + <link linkend="opt-services.prosody.virtualHosts">virtualHosts</link>."example.org" = { + <link linkend="opt-services.prosody.virtualHosts._name__.enabled">enabled</link> = true; + <link linkend="opt-services.prosody.virtualHosts._name__.domain">domain</link> = "example.org"; + <link linkend="opt-services.prosody.virtualHosts._name__.ssl.cert">ssl.cert</link> = "/var/lib/acme/example.org/fullchain.pem"; + <link linkend="opt-services.prosody.virtualHosts._name__.ssl.key">ssl.key</link> = "/var/lib/acme/example.org/key.pem"; + }; + <link linkend="opt-services.prosody.muc">muc</link> = [ { + <link linkend="opt-services.prosody.muc">domain</link> = "conference.example.org"; + } ]; + <link linkend="opt-services.prosody.uploadHttp">uploadHttp</link> = { + <link linkend="opt-services.prosody.uploadHttp.domain">domain</link> = "upload.example.org"; + }; +};</programlisting> + </para> + </section> + <section xml:id="module-services-prosody-letsencrypt"> + <title>Let's Encrypt Configuration</title> + <para> + As you can see in the code snippet from the + <link linkend="module-services-prosody-basic-usage">previous section</link>, + you'll need a single TLS certificate covering your main endpoint, + the MUC one as well as the HTTP Upload one. We can generate such a + certificate by leveraging the ACME + <link linkend="opt-security.acme.certs._name_.extraDomains">extraDomains</link> module option. + </para> + <para> + Provided the setup detailed in the previous section, you'll need the following acme configuration to generate + a TLS certificate for the three endponits: + <programlisting> +security.acme = { + <link linkend="opt-security.acme.email">email</link> = "root@example.org"; + <link linkend="opt-security.acme.acceptTerms">acceptTerms</link> = true; + <link linkend="opt-security.acme.certs">certs</link> = { + "example.org" = { + <link linkend="opt-security.acme.certs._name_.webroot">webroot</link> = "/var/www/example.org"; + <link linkend="opt-security.acme.certs._name_.email">email</link> = "root@example.org"; + <link linkend="opt-security.acme.certs._name_.extraDomains">extraDomains."conference.example.org"</link> = null; + <link linkend="opt-security.acme.certs._name_.extraDomains">extraDomains."upload.example.org"</link> = null; + }; + }; +};</programlisting> + </para> +</section> +</chapter> diff --git a/nixpkgs/nixos/modules/services/networking/quagga.nix b/nixpkgs/nixos/modules/services/networking/quagga.nix new file mode 100644 index 000000000000..5acdd5af8f8f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/quagga.nix @@ -0,0 +1,185 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.quagga; + + services = [ "babel" "bgp" "isis" "ospf6" "ospf" "pim" "rip" "ripng" ]; + allServices = services ++ [ "zebra" ]; + + isEnabled = service: cfg.${service}.enable; + + daemonName = service: if service == "zebra" then service else "${service}d"; + + configFile = service: + let + scfg = cfg.${service}; + in + if scfg.configFile != null then scfg.configFile + else pkgs.writeText "${daemonName service}.conf" + '' + ! Quagga ${daemonName service} configuration + ! + hostname ${config.networking.hostName} + log syslog + service password-encryption + ! + ${scfg.config} + ! + end + ''; + + serviceOptions = service: + { + enable = mkEnableOption "the Quagga ${toUpper service} routing protocol"; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/etc/quagga/${daemonName service}.conf"; + description = '' + Configuration file to use for Quagga ${daemonName service}. + By default the NixOS generated files are used. + ''; + }; + + config = mkOption { + type = types.lines; + default = ""; + example = + let + examples = { + rip = '' + router rip + network 10.0.0.0/8 + ''; + + ospf = '' + router ospf + network 10.0.0.0/8 area 0 + ''; + + bgp = '' + router bgp 65001 + neighbor 10.0.0.1 remote-as 65001 + ''; + }; + in + examples.${service} or ""; + description = '' + ${daemonName service} configuration statements. + ''; + }; + + vtyListenAddress = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + Address to bind to for the VTY interface. + ''; + }; + + vtyListenPort = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + TCP Port to bind to for the VTY interface. + ''; + }; + }; + +in + +{ + + ###### interface + imports = [ + { + options.services.quagga = { + zebra = (serviceOptions "zebra") // { + enable = mkOption { + type = types.bool; + default = any isEnabled services; + description = '' + Whether to enable the Zebra routing manager. + + The Zebra routing manager is automatically enabled + if any routing protocols are configured. + ''; + }; + }; + }; + } + { options.services.quagga = (genAttrs services serviceOptions); } + ]; + + ###### implementation + + config = mkIf (any isEnabled allServices) { + + environment.systemPackages = [ + pkgs.quagga # for the vtysh tool + ]; + + users.users.quagga = { + description = "Quagga daemon user"; + isSystemUser = true; + group = "quagga"; + }; + + users.groups = { + quagga = {}; + # Members of the quaggavty group can use vtysh to inspect the Quagga daemons + quaggavty = { members = [ "quagga" ]; }; + }; + + systemd.services = + let + quaggaService = service: + let + scfg = cfg.${service}; + daemon = daemonName service; + in + nameValuePair daemon ({ + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ (configFile service) ]; + + serviceConfig = { + Type = "forking"; + PIDFile = "/run/quagga/${daemon}.pid"; + ExecStart = "@${pkgs.quagga}/libexec/quagga/${daemon} ${daemon} -d -f ${configFile service}" + + optionalString (scfg.vtyListenAddress != "") " -A ${scfg.vtyListenAddress}" + + optionalString (scfg.vtyListenPort != null) " -P ${toString scfg.vtyListenPort}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Restart = "on-abort"; + }; + } // ( + if service == "zebra" then + { + description = "Quagga Zebra routing manager"; + unitConfig.Documentation = "man:zebra(8)"; + after = [ "network.target" ]; + preStart = '' + install -m 0755 -o quagga -g quagga -d /run/quagga + + ${pkgs.iproute}/bin/ip route flush proto zebra + ''; + } + else + { + description = "Quagga ${toUpper service} routing daemon"; + unitConfig.Documentation = "man:${daemon}(8) man:zebra(8)"; + bindsTo = [ "zebra.service" ]; + after = [ "network.target" "zebra.service" ]; + } + )); + in + listToAttrs (map quaggaService (filter isEnabled allServices)); + + }; + + meta.maintainers = with lib.maintainers; [ tavyc ]; + +} diff --git a/nixpkgs/nixos/modules/services/networking/quassel.nix b/nixpkgs/nixos/modules/services/networking/quassel.nix new file mode 100644 index 000000000000..da723ec86adf --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/quassel.nix @@ -0,0 +1,132 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.quassel; + quassel = cfg.package; + user = if cfg.user != null then cfg.user else "quassel"; +in + +{ + + ###### interface + + options = { + + services.quassel = { + + enable = mkEnableOption "the Quassel IRC client daemon"; + + certificateFile = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to the certificate used for SSL connections with clients. + ''; + }; + + requireSSL = mkOption { + type = types.bool; + default = false; + description = '' + Require SSL for connections from clients. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.quasselDaemon; + defaultText = "pkgs.quasselDaemon"; + description = '' + The package of the quassel daemon. + ''; + example = literalExample "pkgs.quasselDaemon"; + }; + + interfaces = mkOption { + default = [ "127.0.0.1" ]; + description = '' + The interfaces the Quassel daemon will be listening to. If `[ 127.0.0.1 ]', + only clients on the local host can connect to it; if `[ 0.0.0.0 ]', clients + can access it from any network interface. + ''; + }; + + portNumber = mkOption { + default = 4242; + description = '' + The port number the Quassel daemon will be listening to. + ''; + }; + + dataDir = mkOption { + default = ''/home/${user}/.config/quassel-irc.org''; + description = '' + The directory holding configuration files, the SQlite database and the SSL Cert. + ''; + }; + + user = mkOption { + default = null; + description = '' + The existing user the Quassel daemon should run as. If left empty, a default "quassel" user will be created. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + assertions = [ + { assertion = cfg.requireSSL -> cfg.certificateFile != null; + message = "Quassel needs a certificate file in order to require SSL"; + }]; + + users.users = optionalAttrs (cfg.user == null) { + quassel = { + name = "quassel"; + description = "Quassel IRC client daemon"; + group = "quassel"; + uid = config.ids.uids.quassel; + }; + }; + + users.groups = optionalAttrs (cfg.user == null) { + quassel = { + name = "quassel"; + gid = config.ids.gids.quassel; + }; + }; + + systemd.tmpfiles.rules = [ + "d '${cfg.dataDir}' - ${user} - - -" + ]; + + systemd.services.quassel = + { description = "Quassel IRC client daemon"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ] ++ optional config.services.postgresql.enable "postgresql.service" + ++ optional config.services.mysql.enable "mysql.service"; + + serviceConfig = + { + ExecStart = concatStringsSep " " ([ + "${quassel}/bin/quasselcore" + "--listen=${concatStringsSep "," cfg.interfaces}" + "--port=${toString cfg.portNumber}" + "--configdir=${cfg.dataDir}" + ] ++ optional cfg.requireSSL "--require-ssl" + ++ optional (cfg.certificateFile != null) "--ssl-cert=${cfg.certificateFile}"); + User = user; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/quicktun.nix b/nixpkgs/nixos/modules/services/networking/quicktun.nix new file mode 100644 index 000000000000..fb783c836464 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/quicktun.nix @@ -0,0 +1,118 @@ +{ config, pkgs, lib, ... }: + +let + + cfg = config.services.quicktun; + +in + +with lib; + +{ + options = { + + services.quicktun = mkOption { + default = { }; + description = "QuickTun tunnels"; + type = types.attrsOf (types.submodule { + options = { + tunMode = mkOption { + type = types.int; + default = 0; + example = 1; + description = ""; + }; + + remoteAddress = mkOption { + type = types.str; + example = "tunnel.example.com"; + description = ""; + }; + + localAddress = mkOption { + type = types.str; + example = "0.0.0.0"; + description = ""; + }; + + localPort = mkOption { + type = types.int; + default = 2998; + description = ""; + }; + + remotePort = mkOption { + type = types.int; + default = 2998; + description = ""; + }; + + remoteFloat = mkOption { + type = types.int; + default = 0; + description = ""; + }; + + protocol = mkOption { + type = types.str; + default = "nacltai"; + description = ""; + }; + + privateKey = mkOption { + type = types.str; + description = ""; + }; + + publicKey = mkOption { + type = types.str; + description = ""; + }; + + timeWindow = mkOption { + type = types.int; + default = 5; + description = ""; + }; + + upScript = mkOption { + type = types.lines; + default = ""; + description = ""; + }; + }; + }); + }; + + }; + + config = mkIf (cfg != []) { + systemd.services = fold (a: b: a // b) {} ( + mapAttrsToList (name: qtcfg: { + "quicktun-${name}" = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + environment = { + INTERFACE = name; + TUN_MODE = toString qtcfg.tunMode; + REMOTE_ADDRESS = qtcfg.remoteAddress; + LOCAL_ADDRESS = qtcfg.localAddress; + LOCAL_PORT = toString qtcfg.localPort; + REMOTE_PORT = toString qtcfg.remotePort; + REMOTE_FLOAT = toString qtcfg.remoteFloat; + PRIVATE_KEY = qtcfg.privateKey; + PUBLIC_KEY = qtcfg.publicKey; + TIME_WINDOW = toString qtcfg.timeWindow; + TUN_UP_SCRIPT = pkgs.writeScript "quicktun-${name}-up.sh" qtcfg.upScript; + SUID = "nobody"; + }; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.quicktun}/bin/quicktun.${qtcfg.protocol}"; + }; + }; + }) cfg + ); + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/quorum.nix b/nixpkgs/nixos/modules/services/networking/quorum.nix new file mode 100644 index 000000000000..2f612c9db686 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/quorum.nix @@ -0,0 +1,229 @@ +{ config, pkgs, lib, ... }: +let + + inherit (lib) mkEnableOption mkIf mkOption literalExample types optionalString; + + cfg = config.services.quorum; + dataDir = "/var/lib/quorum"; + genesisFile = pkgs.writeText "genesis.json" (builtins.toJSON cfg.genesis); + staticNodesFile = pkgs.writeText "static-nodes.json" (builtins.toJSON cfg.staticNodes); + +in { + options = { + + services.quorum = { + enable = mkEnableOption "Quorum blockchain daemon"; + + user = mkOption { + type = types.str; + default = "quorum"; + description = "The user as which to run quorum."; + }; + + group = mkOption { + type = types.str; + default = cfg.user; + description = "The group as which to run quorum."; + }; + + port = mkOption { + type = types.port; + default = 21000; + description = "Override the default port on which to listen for connections."; + }; + + nodekeyFile = mkOption { + type = types.path; + default = "${dataDir}/nodekey"; + description = "Path to the nodekey."; + }; + + staticNodes = mkOption { + type = types.listOf types.str; + default = []; + example = [ "enode://dd333ec28f0a8910c92eb4d336461eea1c20803eed9cf2c056557f986e720f8e693605bba2f4e8f289b1162e5ac7c80c914c7178130711e393ca76abc1d92f57@0.0.0.0:30303?discport=0" ]; + description = "List of validator nodes."; + }; + + privateconfig = mkOption { + type = types.str; + default = "ignore"; + description = "Configuration of privacy transaction manager."; + }; + + syncmode = mkOption { + type = types.enum [ "fast" "full" "light" ]; + default = "full"; + description = "Blockchain sync mode."; + }; + + blockperiod = mkOption { + type = types.int; + default = 5; + description = "Default minimum difference between two consecutive block's timestamps in seconds."; + }; + + permissioned = mkOption { + type = types.bool; + default = true; + description = "Allow only a defined list of nodes to connect."; + }; + + rpc = { + enable = mkOption { + type = types.bool; + default = true; + description = "Enable RPC interface."; + }; + + address = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "Listening address for RPC connections."; + }; + + port = mkOption { + type = types.port; + default = 22004; + description = "Override the default port on which to listen for RPC connections."; + }; + + api = mkOption { + type = types.str; + default = "admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul"; + description = "API's offered over the HTTP-RPC interface."; + }; + }; + + ws = { + enable = mkOption { + type = types.bool; + default = true; + description = "Enable WS-RPC interface."; + }; + + address = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "Listening address for WS-RPC connections."; + }; + + port = mkOption { + type = types.port; + default = 8546; + description = "Override the default port on which to listen for WS-RPC connections."; + }; + + api = mkOption { + type = types.str; + default = "admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul"; + description = "API's offered over the WS-RPC interface."; + }; + + origins = mkOption { + type = types.str; + default = "*"; + description = "Origins from which to accept websockets requests"; + }; + }; + + genesis = mkOption { + type = types.nullOr types.attrs; + default = null; + example = literalExample '' { + alloc = { + a47385db68718bdcbddc2d2bb7c54018066ec111 = { + balance = "1000000000000000000000000000"; + }; + }; + coinbase = "0x0000000000000000000000000000000000000000"; + config = { + byzantiumBlock = 4; + chainId = 494702925; + eip150Block = 2; + eip155Block = 3; + eip158Block = 3; + homesteadBlock = 1; + isQuorum = true; + istanbul = { + epoch = 30000; + policy = 0; + }; + }; + difficulty = "0x1"; + extraData = "0x0000000000000000000000000000000000000000000000000000000000000000f85ad59438f0508111273d8e482f49410ca4078afc86a961b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0"; + gasLimit = "0x2FEFD800"; + mixHash = "0x63746963616c2062797a616e74696e65201111756c7420746f6c6572616e6365"; + nonce = "0x0"; + parentHash = "0x0000000000000000000000000000000000000000000000000000000000000000"; + timestamp = "0x00"; + }''; + description = "Blockchain genesis settings."; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.quorum ]; + systemd.tmpfiles.rules = [ + "d '${dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -" + ]; + systemd.services.quorum = { + description = "Quorum daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + environment = { + PRIVATE_CONFIG = "${cfg.privateconfig}"; + }; + preStart = '' + if [ ! -d ${dataDir}/geth ]; then + if [ ! -d ${dataDir}/keystore ]; then + echo ERROR: You need to create a wallet before initializing your genesis file, run: + echo # su -s /bin/sh - quorum + echo $ geth --datadir ${dataDir} account new + echo and configure your genesis file accordingly. + exit 1; + fi + ln -s ${staticNodesFile} ${dataDir}/static-nodes.json + ${pkgs.quorum}/bin/geth --datadir ${dataDir} init ${genesisFile} + fi + ''; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + ExecStart = ''${pkgs.quorum}/bin/geth \ + --nodiscover \ + --verbosity 5 \ + --nodekey ${cfg.nodekeyFile} \ + --istanbul.blockperiod ${toString cfg.blockperiod} \ + --syncmode ${cfg.syncmode} \ + ${optionalString (cfg.permissioned) + "--permissioned"} \ + --mine --minerthreads 1 \ + ${optionalString (cfg.rpc.enable) + "--rpc --rpcaddr ${cfg.rpc.address} --rpcport ${toString cfg.rpc.port} --rpcapi ${cfg.rpc.api}"} \ + ${optionalString (cfg.ws.enable) + "--ws --wsaddr ${cfg.ws.address} --wsport ${toString cfg.ws.port} --wsapi ${cfg.ws.api} --wsorigins ${cfg.ws.origins}"} \ + --emitcheckpoints \ + --datadir ${dataDir} \ + --port ${toString cfg.port}''; + Restart = "on-failure"; + + # Hardening measures + PrivateTmp = "true"; + ProtectSystem = "full"; + NoNewPrivileges = "true"; + PrivateDevices = "true"; + MemoryDenyWriteExecute = "true"; + }; + }; + users.users.${cfg.user} = { + name = cfg.user; + group = cfg.group; + description = "Quorum daemon user"; + home = dataDir; + isSystemUser = true; + }; + users.groups.${cfg.group} = {}; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/racoon.nix b/nixpkgs/nixos/modules/services/networking/racoon.nix new file mode 100644 index 000000000000..328f4cb1497f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/racoon.nix @@ -0,0 +1,45 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.racoon; +in { + options.services.racoon = { + enable = mkEnableOption "racoon"; + + config = mkOption { + description = "Contents of racoon configuration file."; + default = ""; + type = types.str; + }; + + configPath = mkOption { + description = "Location of racoon config if config is not provided."; + default = "/etc/racoon/racoon.conf"; + type = types.path; + }; + }; + + config = mkIf cfg.enable { + systemd.services.racoon = { + description = "Racoon Daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + ExecStart = "${pkgs.ipsecTools}/bin/racoon -f ${ + if (cfg.config != "") then pkgs.writeText "racoon.conf" cfg.config + else cfg.configPath + }"; + ExecReload = "${pkgs.ipsecTools}/bin/racoonctl reload-config"; + PIDFile = "/run/racoon.pid"; + Type = "forking"; + Restart = "always"; + }; + preStart = '' + rm /run/racoon.pid || true + mkdir -p /var/racoon + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/radicale.nix b/nixpkgs/nixos/modules/services/networking/radicale.nix new file mode 100644 index 000000000000..30bf22586f86 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/radicale.nix @@ -0,0 +1,89 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.radicale; + + confFile = pkgs.writeText "radicale.conf" cfg.config; + + # This enables us to default to version 2 while still not breaking configurations of people with version 1 + defaultPackage = if versionAtLeast config.system.stateVersion "17.09" then { + pkg = pkgs.radicale2; + text = "pkgs.radicale2"; + } else { + pkg = pkgs.radicale1; + text = "pkgs.radicale1"; + }; +in + +{ + + options = { + services.radicale.enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable Radicale CalDAV and CardDAV server. + ''; + }; + + services.radicale.package = mkOption { + type = types.package; + default = defaultPackage.pkg; + defaultText = defaultPackage.text; + description = '' + Radicale package to use. This defaults to version 1.x if + <literal>system.stateVersion < 17.09</literal> and version 2.x + otherwise. + ''; + }; + + services.radicale.config = mkOption { + type = types.str; + default = ""; + description = '' + Radicale configuration, this will set the service + configuration file. + ''; + }; + + services.radicale.extraArgs = mkOption { + type = types.listOf types.str; + default = []; + description = "Extra arguments passed to the Radicale daemon."; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + users.users.radicale = + { uid = config.ids.uids.radicale; + description = "radicale user"; + home = "/var/lib/radicale"; + createHome = true; + }; + + users.groups.radicale = + { gid = config.ids.gids.radicale; }; + + systemd.services.radicale = { + description = "A Simple Calendar and Contact Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = concatStringsSep " " ([ + "${cfg.package}/bin/radicale" "-C" confFile + ] ++ ( + map escapeShellArg cfg.extraArgs + )); + User = "radicale"; + Group = "radicale"; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ aneeshusa infinisil ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/radvd.nix b/nixpkgs/nixos/modules/services/networking/radvd.nix new file mode 100644 index 000000000000..f4b00c9b356e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/radvd.nix @@ -0,0 +1,73 @@ +# Module for the IPv6 Router Advertisement Daemon. + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.radvd; + + confFile = pkgs.writeText "radvd.conf" cfg.config; + +in + +{ + + ###### interface + + options = { + + services.radvd.enable = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to enable the Router Advertisement Daemon + (<command>radvd</command>), which provides link-local + advertisements of IPv6 router addresses and prefixes using + the Neighbor Discovery Protocol (NDP). This enables + stateless address autoconfiguration in IPv6 clients on the + network. + ''; + }; + + services.radvd.config = mkOption { + example = + '' + interface eth0 { + AdvSendAdvert on; + prefix 2001:db8:1234:5678::/64 { }; + }; + ''; + description = + '' + The contents of the radvd configuration file. + ''; + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.users.radvd = + { uid = config.ids.uids.radvd; + description = "Router Advertisement Daemon User"; + }; + + systemd.services.radvd = + { description = "IPv6 Router Advertisement Daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = + { ExecStart = "@${pkgs.radvd}/bin/radvd radvd -n -u radvd -C ${confFile}"; + Restart = "always"; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/rdnssd.nix b/nixpkgs/nixos/modules/services/networking/rdnssd.nix new file mode 100644 index 000000000000..469504c43172 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/rdnssd.nix @@ -0,0 +1,80 @@ +# Module for rdnssd, a daemon that configures DNS servers in +# /etc/resolv/conf from IPv6 RDNSS advertisements. + +{ config, lib, pkgs, ... }: + +with lib; +let + mergeHook = pkgs.writeScript "rdnssd-merge-hook" '' + #! ${pkgs.runtimeShell} -e + ${pkgs.openresolv}/bin/resolvconf -u + ''; +in +{ + + ###### interface + + options = { + + services.rdnssd.enable = mkOption { + type = types.bool; + default = false; + #default = config.networking.enableIPv6; + description = + '' + Whether to enable the RDNSS daemon + (<command>rdnssd</command>), which configures DNS servers in + <filename>/etc/resolv.conf</filename> from RDNSS + advertisements sent by IPv6 routers. + ''; + }; + + }; + + + ###### implementation + + config = mkIf config.services.rdnssd.enable { + + assertions = [{ + assertion = config.networking.resolvconf.enable; + message = "rdnssd needs resolvconf to work (probably something sets up a static resolv.conf)"; + }]; + + systemd.services.rdnssd = { + description = "RDNSS daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + preStart = '' + # Create the proper run directory + mkdir -p /run/rdnssd + touch /run/rdnssd/resolv.conf + chown -R rdnssd /run/rdnssd + + # Link the resolvconf interfaces to rdnssd + rm -f /run/resolvconf/interfaces/rdnssd + ln -s /run/rdnssd/resolv.conf /run/resolvconf/interfaces/rdnssd + ${mergeHook} + ''; + + postStop = '' + rm -f /run/resolvconf/interfaces/rdnssd + ${mergeHook} + ''; + + serviceConfig = { + ExecStart = "@${pkgs.ndisc6}/bin/rdnssd rdnssd -p /run/rdnssd/rdnssd.pid -r /run/rdnssd/resolv.conf -u rdnssd -H ${mergeHook}"; + Type = "forking"; + PIDFile = "/run/rdnssd/rdnssd.pid"; + }; + }; + + users.users.rdnssd = { + description = "RDNSSD Daemon User"; + uid = config.ids.uids.rdnssd; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/redsocks.nix b/nixpkgs/nixos/modules/services/networking/redsocks.nix new file mode 100644 index 000000000000..8481f9debf39 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/redsocks.nix @@ -0,0 +1,272 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.redsocks; +in +{ + ##### interface + options = { + services.redsocks = { + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable redsocks."; + }; + + log_debug = mkOption { + type = types.bool; + default = false; + description = "Log connection progress."; + }; + + log_info = mkOption { + type = types.bool; + default = false; + description = "Log start and end of client sessions."; + }; + + log = mkOption { + type = types.str; + default = "stderr"; + description = + '' + Where to send logs. + + Possible values are: + - stderr + - file:/path/to/file + - syslog:FACILITY where FACILITY is any of "daemon", "local0", + etc. + ''; + }; + + chroot = mkOption { + type = with types; nullOr str; + default = null; + description = + '' + Chroot under which to run redsocks. Log file is opened before + chroot, but if logging to syslog /etc/localtime may be required. + ''; + }; + + redsocks = mkOption { + description = + '' + Local port to proxy associations to be performed. + + The example shows how to configure a proxy to handle port 80 as HTTP + relay, and all other ports as HTTP connect. + ''; + example = [ + { port = 23456; proxy = "1.2.3.4:8080"; type = "http-relay"; + redirectCondition = "--dport 80"; + doNotRedirect = [ "-d 1.2.0.0/16" ]; + } + { port = 23457; proxy = "1.2.3.4:8080"; type = "http-connect"; + redirectCondition = true; + doNotRedirect = [ "-d 1.2.0.0/16" ]; + } + ]; + type = types.listOf (types.submodule { options = { + ip = mkOption { + type = types.str; + default = "127.0.0.1"; + description = + '' + IP on which redsocks should listen. Defaults to 127.0.0.1 for + security reasons. + ''; + }; + + port = mkOption { + type = types.int; + default = 12345; + description = "Port on which redsocks should listen."; + }; + + proxy = mkOption { + type = types.str; + description = + '' + Proxy through which redsocks should forward incoming traffic. + Example: "example.org:8080" + ''; + }; + + type = mkOption { + type = types.enum [ "socks4" "socks5" "http-connect" "http-relay" ]; + description = "Type of proxy."; + }; + + login = mkOption { + type = with types; nullOr str; + default = null; + description = "Login to send to proxy."; + }; + + password = mkOption { + type = with types; nullOr str; + default = null; + description = + '' + Password to send to proxy. WARNING, this will end up + world-readable in the store! Awaiting + https://github.com/NixOS/nix/issues/8 to be able to fix. + ''; + }; + + disclose_src = mkOption { + type = types.enum [ "false" "X-Forwarded-For" "Forwarded_ip" + "Forwarded_ipport" ]; + default = "false"; + description = + '' + Way to disclose client IP to the proxy. + - "false": do not disclose + http-connect supports the following ways: + - "X-Forwarded-For": add header "X-Forwarded-For: IP" + - "Forwarded_ip": add header "Forwarded: for=IP" (see RFC7239) + - "Forwarded_ipport": add header 'Forwarded: for="IP:port"' + ''; + }; + + redirectInternetOnly = mkOption { + type = types.bool; + default = true; + description = "Exclude all non-globally-routable IPs from redsocks"; + }; + + doNotRedirect = mkOption { + type = with types; listOf str; + default = []; + description = + '' + Iptables filters that if matched will get the packet off of + redsocks. + ''; + example = [ "-d 1.2.3.4" ]; + }; + + redirectCondition = mkOption { + type = with types; either bool str; + default = false; + description = + '' + Conditions to make outbound packets go through this redsocks + instance. + + If set to false, no packet will be forwarded. If set to true, + all packets will be forwarded (except packets excluded by + redirectInternetOnly). + + If set to a string, this is an iptables filter that will be + matched against packets before getting them into redsocks. For + example, setting it to "--dport 80" will only send + packets to port 80 to redsocks. Note "-p tcp" is always + implicitly added, as udp can only be proxied through redudp or + the like. + ''; + }; + };}); + }; + + # TODO: Add support for redudp and dnstc + }; + }; + + ##### implementation + config = let + redsocks_blocks = concatMapStrings (block: + let proxy = splitString ":" block.proxy; in + '' + redsocks { + local_ip = ${block.ip}; + local_port = ${toString block.port}; + + ip = ${elemAt proxy 0}; + port = ${elemAt proxy 1}; + type = ${block.type}; + + ${optionalString (block.login != null) "login = \"${block.login}\";"} + ${optionalString (block.password != null) "password = \"${block.password}\";"} + + disclose_src = ${block.disclose_src}; + } + '') cfg.redsocks; + configfile = pkgs.writeText "redsocks.conf" + '' + base { + log_debug = ${if cfg.log_debug then "on" else "off" }; + log_info = ${if cfg.log_info then "on" else "off" }; + log = ${cfg.log}; + + daemon = off; + redirector = iptables; + + user = redsocks; + group = redsocks; + ${optionalString (cfg.chroot != null) "chroot = ${cfg.chroot};"} + } + + ${redsocks_blocks} + ''; + internetOnly = [ # TODO: add ipv6-equivalent + "-d 0.0.0.0/8" + "-d 10.0.0.0/8" + "-d 127.0.0.0/8" + "-d 169.254.0.0/16" + "-d 172.16.0.0/12" + "-d 192.168.0.0/16" + "-d 224.168.0.0/4" + "-d 240.168.0.0/4" + ]; + redCond = block: + optionalString (isString block.redirectCondition) block.redirectCondition; + iptables = concatImapStrings (idx: block: + let chain = "REDSOCKS${toString idx}"; doNotRedirect = + concatMapStringsSep "\n" + (f: "ip46tables -t nat -A ${chain} ${f} -j RETURN 2>/dev/null || true") + (block.doNotRedirect ++ (optionals block.redirectInternetOnly internetOnly)); + in + optionalString (block.redirectCondition != false) + '' + ip46tables -t nat -F ${chain} 2>/dev/null || true + ip46tables -t nat -N ${chain} 2>/dev/null || true + ${doNotRedirect} + ip46tables -t nat -A ${chain} -p tcp -j REDIRECT --to-ports ${toString block.port} + + # TODO: show errors, when it will be easily possible by a switch to + # iptables-restore + ip46tables -t nat -A OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true + '' + ) cfg.redsocks; + in + mkIf cfg.enable { + users.groups.redsocks = {}; + users.users.redsocks = { + description = "Redsocks daemon"; + group = "redsocks"; + isSystemUser = true; + }; + + systemd.services.redsocks = { + description = "Redsocks"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "${pkgs.redsocks}/bin/redsocks -c ${configfile}"; + }; + + networking.firewall.extraCommands = iptables; + + networking.firewall.extraStopCommands = + concatImapStringsSep "\n" (idx: block: + let chain = "REDSOCKS${toString idx}"; in + optionalString (block.redirectCondition != false) + "ip46tables -t nat -D OUTPUT -p tcp ${redCond block} -j ${chain} 2>/dev/null || true" + ) cfg.redsocks; + }; + + meta.maintainers = with lib.maintainers; [ ekleog ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/resilio.nix b/nixpkgs/nixos/modules/services/networking/resilio.nix new file mode 100644 index 000000000000..e74e03fc0b07 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/resilio.nix @@ -0,0 +1,263 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.resilio; + + resilioSync = pkgs.resilio-sync; + + sharedFoldersRecord = map (entry: { + secret = entry.secret; + dir = entry.directory; + + use_relay_server = entry.useRelayServer; + use_tracker = entry.useTracker; + use_dht = entry.useDHT; + + search_lan = entry.searchLAN; + use_sync_trash = entry.useSyncTrash; + known_hosts = entry.knownHosts; + }) cfg.sharedFolders; + + configFile = pkgs.writeText "config.json" (builtins.toJSON ({ + device_name = cfg.deviceName; + storage_path = cfg.storagePath; + listening_port = cfg.listeningPort; + use_gui = false; + check_for_updates = cfg.checkForUpdates; + use_upnp = cfg.useUpnp; + download_limit = cfg.downloadLimit; + upload_limit = cfg.uploadLimit; + lan_encrypt_data = cfg.encryptLAN; + } // optionalAttrs cfg.enableWebUI { + webui = { listen = "${cfg.httpListenAddr}:${toString cfg.httpListenPort}"; } // + (optionalAttrs (cfg.httpLogin != "") { login = cfg.httpLogin; }) // + (optionalAttrs (cfg.httpPass != "") { password = cfg.httpPass; }) // + (optionalAttrs (cfg.apiKey != "") { api_key = cfg.apiKey; }) // + (optionalAttrs (cfg.directoryRoot != "") { directory_root = cfg.directoryRoot; }); + } // optionalAttrs (sharedFoldersRecord != []) { + shared_folders = sharedFoldersRecord; + })); + +in +{ + options = { + services.resilio = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + If enabled, start the Resilio Sync daemon. Once enabled, you can + interact with the service through the Web UI, or configure it in your + NixOS configuration. + ''; + }; + + deviceName = mkOption { + type = types.str; + example = "Voltron"; + default = config.networking.hostName; + description = '' + Name of the Resilio Sync device. + ''; + }; + + listeningPort = mkOption { + type = types.int; + default = 0; + example = 44444; + description = '' + Listening port. Defaults to 0 which randomizes the port. + ''; + }; + + checkForUpdates = mkOption { + type = types.bool; + default = true; + description = '' + Determines whether to check for updates and alert the user + about them in the UI. + ''; + }; + + useUpnp = mkOption { + type = types.bool; + default = true; + description = '' + Use Universal Plug-n-Play (UPnP) + ''; + }; + + downloadLimit = mkOption { + type = types.int; + default = 0; + example = 1024; + description = '' + Download speed limit. 0 is unlimited (default). + ''; + }; + + uploadLimit = mkOption { + type = types.int; + default = 0; + example = 1024; + description = '' + Upload speed limit. 0 is unlimited (default). + ''; + }; + + httpListenAddr = mkOption { + type = types.str; + default = "0.0.0.0"; + example = "1.2.3.4"; + description = '' + HTTP address to bind to. + ''; + }; + + httpListenPort = mkOption { + type = types.int; + default = 9000; + description = '' + HTTP port to bind on. + ''; + }; + + httpLogin = mkOption { + type = types.str; + example = "allyourbase"; + default = ""; + description = '' + HTTP web login username. + ''; + }; + + httpPass = mkOption { + type = types.str; + example = "arebelongtous"; + default = ""; + description = '' + HTTP web login password. + ''; + }; + + encryptLAN = mkOption { + type = types.bool; + default = true; + description = "Encrypt LAN data."; + }; + + enableWebUI = mkOption { + type = types.bool; + default = false; + description = '' + Enable Web UI for administration. Bound to the specified + <literal>httpListenAddress</literal> and + <literal>httpListenPort</literal>. + ''; + }; + + storagePath = mkOption { + type = types.path; + default = "/var/lib/resilio-sync/"; + description = '' + Where BitTorrent Sync will store it's database files (containing + things like username info and licenses). Generally, you should not + need to ever change this. + ''; + }; + + apiKey = mkOption { + type = types.str; + default = ""; + description = "API key, which enables the developer API."; + }; + + directoryRoot = mkOption { + type = types.str; + default = ""; + example = "/media"; + description = "Default directory to add folders in the web UI."; + }; + + sharedFolders = mkOption { + default = []; + example = + [ { secret = "AHMYFPCQAHBM7LQPFXQ7WV6Y42IGUXJ5Y"; + directory = "/home/user/sync_test"; + useRelayServer = true; + useTracker = true; + useDHT = false; + searchLAN = true; + useSyncTrash = true; + knownHosts = [ + "192.168.1.2:4444" + "192.168.1.3:4444" + ]; + } + ]; + description = '' + Shared folder list. If enabled, web UI must be + disabled. Secrets can be generated using <literal>rslsync + --generate-secret</literal>. Note that this secret will be + put inside the Nix store, so it is realistically not very + secret. + + If you would like to be able to modify the contents of this + directories, it is recommended that you make your user a + member of the <literal>resilio</literal> group. + + Directories in this list should be in the + <literal>resilio</literal> group, and that group must have + write access to the directory. It is also recommended that + <literal>chmod g+s</literal> is applied to the directory + so that any sub directories created will also belong to + the <literal>resilio</literal> group. Also, + <literal>setfacl -d -m group:resilio:rwx</literal> and + <literal>setfacl -m group:resilio:rwx</literal> should also + be applied so that the sub directories are writable by + the group. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = + [ { assertion = cfg.deviceName != ""; + message = "Device name cannot be empty."; + } + { assertion = cfg.enableWebUI -> cfg.sharedFolders == []; + message = "If using shared folders, the web UI cannot be enabled."; + } + { assertion = cfg.apiKey != "" -> cfg.enableWebUI; + message = "If you're using an API key, you must enable the web server."; + } + ]; + + users.users.rslsync = { + description = "Resilio Sync Service user"; + home = cfg.storagePath; + createHome = true; + uid = config.ids.uids.rslsync; + group = "rslsync"; + }; + + users.groups.rslsync = {}; + + systemd.services.resilio = with pkgs; { + description = "Resilio Sync Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + Restart = "on-abort"; + UMask = "0002"; + User = "rslsync"; + ExecStart = '' + ${resilioSync}/bin/rslsync --nodaemon --config ${configFile} + ''; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/rpcbind.nix b/nixpkgs/nixos/modules/services/networking/rpcbind.nix new file mode 100644 index 000000000000..0a5df6987092 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/rpcbind.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + + ###### interface + + options = { + + services.rpcbind = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable `rpcbind', an ONC RPC directory service + notably used by NFS and NIS, and which can be queried + using the rpcinfo(1) command. `rpcbind` is a replacement for + `portmap`. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.services.rpcbind.enable { + environment.systemPackages = [ pkgs.rpcbind ]; + + systemd.packages = [ pkgs.rpcbind ]; + + systemd.services.rpcbind = { + wantedBy = [ "multi-user.target" ]; + }; + + users.users.rpc = { + group = "nogroup"; + uid = config.ids.uids.rpc; + }; + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/rxe.nix b/nixpkgs/nixos/modules/services/networking/rxe.nix new file mode 100644 index 000000000000..c7d174a00de2 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/rxe.nix @@ -0,0 +1,52 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.networking.rxe; + +in { + ###### interface + + options = { + networking.rxe = { + enable = mkEnableOption "RDMA over converged ethernet"; + interfaces = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "eth0" ]; + description = '' + Enable RDMA on the listed interfaces. The corresponding virtual + RDMA interfaces will be named rxe_<interface>. + UDP port 4791 must be open on the respective ethernet interfaces. + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.rxe = { + description = "RoCE interfaces"; + + wantedBy = [ "multi-user.target" ]; + after = [ "systemd-modules-load.service" "network-online.target" ]; + wants = [ "network-pre.target" ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = map ( x: + "${pkgs.iproute}/bin/rdma link add rxe_${x} type rxe netdev ${x}" + ) cfg.interfaces; + + ExecStop = map ( x: + "${pkgs.iproute}/bin/rdma link delete rxe_${x}" + ) cfg.interfaces; + }; + }; + }; +} + diff --git a/nixpkgs/nixos/modules/services/networking/sabnzbd.nix b/nixpkgs/nixos/modules/services/networking/sabnzbd.nix new file mode 100644 index 000000000000..ff5aef7d1cb4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/sabnzbd.nix @@ -0,0 +1,67 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.sabnzbd; + inherit (pkgs) sabnzbd; + +in + +{ + + ###### interface + + options = { + services.sabnzbd = { + enable = mkEnableOption "the sabnzbd server"; + + configFile = mkOption { + default = "/var/lib/sabnzbd/sabnzbd.ini"; + description = "Path to config file."; + }; + + user = mkOption { + default = "sabnzbd"; + description = "User to run the service as"; + }; + + group = mkOption { + default = "sabnzbd"; + description = "Group to run the service as"; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.users.sabnzbd = { + uid = config.ids.uids.sabnzbd; + group = "sabnzbd"; + description = "sabnzbd user"; + home = "/var/lib/sabnzbd/"; + createHome = true; + }; + + users.groups.sabnzbd = { + gid = config.ids.gids.sabnzbd; + }; + + systemd.services.sabnzbd = { + description = "sabnzbd server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + Type = "forking"; + GuessMainPID = "no"; + User = "${cfg.user}"; + Group = "${cfg.group}"; + ExecStart = "${sabnzbd}/bin/sabnzbd -d -f ${cfg.configFile}"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/searx.nix b/nixpkgs/nixos/modules/services/networking/searx.nix new file mode 100644 index 000000000000..60fb3d5d6d44 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/searx.nix @@ -0,0 +1,80 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.searx; + + configFile = cfg.configFile; + +in + +{ + + ###### interface + + options = { + + services.searx = { + + enable = mkEnableOption + "the searx server. See https://github.com/asciimoo/searx"; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = " + The path of the Searx server configuration file. If no file + is specified, a default file is used (default config file has + debug mode enabled). + "; + }; + + package = mkOption { + type = types.package; + default = pkgs.searx; + defaultText = "pkgs.searx"; + description = "searx package to use."; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.services.searx.enable { + + users.users.searx = + { uid = config.ids.uids.searx; + description = "Searx user"; + createHome = true; + home = "/var/lib/searx"; + }; + + users.groups.searx = + { gid = config.ids.gids.searx; + }; + + systemd.services.searx = + { + description = "Searx server, the meta search engine."; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "searx"; + ExecStart = "${cfg.package}/bin/searx-run"; + }; + } // (optionalAttrs (configFile != null) { + environment.SEARX_SETTINGS_PATH = configFile; + }); + + environment.systemPackages = [ cfg.package ]; + + }; + + meta.maintainers = with lib.maintainers; [ rnhmjoj ]; + +} diff --git a/nixpkgs/nixos/modules/services/networking/seeks.nix b/nixpkgs/nixos/modules/services/networking/seeks.nix new file mode 100644 index 000000000000..40729225b6d0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/seeks.nix @@ -0,0 +1,75 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.seeks; + + confDir = cfg.confDir; + + seeks = pkgs.seeks.override { seeks_confDir = confDir; }; + +in + +{ + + ###### interface + + options = { + + services.seeks = { + + enable = mkOption { + default = false; + type = types.bool; + description = " + Whether to enable the Seeks server. + "; + }; + + confDir = mkOption { + default = ""; + type = types.str; + description = " + The Seeks server configuration. If it is not specified, + a default configuration is used. + "; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.services.seeks.enable { + + users.users.seeks = + { uid = config.ids.uids.seeks; + description = "Seeks user"; + createHome = true; + home = "/var/lib/seeks"; + }; + + users.groups.seeks = + { gid = config.ids.gids.seeks; + }; + + systemd.services.seeks = + { + description = "Seeks server, the p2p search engine."; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = "seeks"; + ExecStart = "${seeks}/bin/seeks"; + }; + }; + + environment.systemPackages = [ seeks ]; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/shadowsocks.nix b/nixpkgs/nixos/modules/services/networking/shadowsocks.nix new file mode 100644 index 000000000000..af12db590f00 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/shadowsocks.nix @@ -0,0 +1,112 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.shadowsocks; + + opts = { + server = cfg.localAddress; + server_port = cfg.port; + method = cfg.encryptionMethod; + mode = cfg.mode; + user = "nobody"; + fast_open = true; + } // optionalAttrs (cfg.password != null) { password = cfg.password; }; + + configFile = pkgs.writeText "shadowsocks.json" (builtins.toJSON opts); + +in + +{ + + ###### interface + + options = { + + services.shadowsocks = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run shadowsocks-libev shadowsocks server. + ''; + }; + + localAddress = mkOption { + type = types.coercedTo types.str singleton (types.listOf types.str); + default = [ "[::0]" "0.0.0.0" ]; + description = '' + Local addresses to which the server binds. + ''; + }; + + port = mkOption { + type = types.int; + default = 8388; + description = '' + Port which the server uses. + ''; + }; + + password = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Password for connecting clients. + ''; + }; + + passwordFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Password file with a password for connecting clients. + ''; + }; + + mode = mkOption { + type = types.enum [ "tcp_only" "tcp_and_udp" "udp_only" ]; + default = "tcp_and_udp"; + description = '' + Relay protocols. + ''; + }; + + encryptionMethod = mkOption { + type = types.str; + default = "chacha20-ietf-poly1305"; + description = '' + Encryption method. See <link xlink:href="https://github.com/shadowsocks/shadowsocks-org/wiki/AEAD-Ciphers"/>. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + assertions = singleton + { assertion = cfg.password == null || cfg.passwordFile == null; + message = "Cannot use both password and passwordFile for shadowsocks-libev"; + }; + + systemd.services.shadowsocks-libev = { + description = "shadowsocks-libev Daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.shadowsocks-libev ] ++ optional (cfg.passwordFile != null) pkgs.jq; + serviceConfig.PrivateTmp = true; + script = '' + ${optionalString (cfg.passwordFile != null) '' + cat ${configFile} | jq --arg password "$(cat "${cfg.passwordFile}")" '. + { password: $password }' > /tmp/shadowsocks.json + ''} + exec ss-server -c ${if cfg.passwordFile != null then "/tmp/shadowsocks.json" else configFile} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/shairport-sync.nix b/nixpkgs/nixos/modules/services/networking/shairport-sync.nix new file mode 100644 index 000000000000..b4b86a2d55be --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/shairport-sync.nix @@ -0,0 +1,83 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.shairport-sync; + +in + +{ + + ###### interface + + options = { + + services.shairport-sync = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable the shairport-sync daemon. + + Running with a local system-wide or remote pulseaudio server + is recommended. + ''; + }; + + arguments = mkOption { + default = "-v -o pa"; + description = '' + Arguments to pass to the daemon. Defaults to a local pulseaudio + server. + ''; + }; + + user = mkOption { + default = "shairport"; + description = '' + User account name under which to run shairport-sync. The account + will be created. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf config.services.shairport-sync.enable { + + services.avahi.enable = true; + services.avahi.publish.enable = true; + services.avahi.publish.userServices = true; + + users.users.${cfg.user} = + { description = "Shairport user"; + isSystemUser = true; + createHome = true; + home = "/var/lib/shairport-sync"; + extraGroups = [ "audio" ] ++ optional config.hardware.pulseaudio.enable "pulse"; + }; + + systemd.services.shairport-sync = + { + description = "shairport-sync"; + after = [ "network.target" "avahi-daemon.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + User = cfg.user; + ExecStart = "${pkgs.shairport-sync}/bin/shairport-sync ${cfg.arguments}"; + RuntimeDirectory = "shairport-sync"; + }; + }; + + environment.systemPackages = [ pkgs.shairport-sync ]; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/shorewall.nix b/nixpkgs/nixos/modules/services/networking/shorewall.nix new file mode 100644 index 000000000000..16383be2530f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/shorewall.nix @@ -0,0 +1,70 @@ +{ config, lib, pkgs, ... }: +let + types = lib.types; + cfg = config.services.shorewall; +in { + options = { + services.shorewall = { + enable = lib.mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable Shorewall IPv4 Firewall. + <warning> + <para> + Enabling this service WILL disable the existing NixOS + firewall! Default firewall rules provided by packages are not + considered at the moment. + </para> + </warning> + ''; + }; + package = lib.mkOption { + type = types.package; + default = pkgs.shorewall; + defaultText = "pkgs.shorewall"; + description = "The shorewall package to use."; + }; + configs = lib.mkOption { + type = types.attrsOf types.lines; + default = {}; + description = '' + This option defines the Shorewall configs. + The attribute name defines the name of the config, + and the attribute value defines the content of the config. + ''; + apply = lib.mapAttrs (name: text: pkgs.writeText "${name}" text); + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.firewall.enable = false; + systemd.services.shorewall = { + description = "Shorewall IPv4 Firewall"; + after = [ "ipset.target" ]; + before = [ "network-pre.target" ]; + wants = [ "network-pre.target" ]; + wantedBy = [ "multi-user.target" ]; + reloadIfChanged = true; + restartTriggers = lib.attrValues cfg.configs; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + ExecStart = "${cfg.package}/bin/shorewall start"; + ExecReload = "${cfg.package}/bin/shorewall reload"; + ExecStop = "${cfg.package}/bin/shorewall stop"; + }; + preStart = '' + install -D -d -m 750 /var/lib/shorewall + install -D -d -m 755 /var/lock/subsys + touch /var/log/shorewall.log + chown 750 /var/log/shorewall.log + ''; + }; + environment = { + etc = lib.mapAttrs' (name: conf: lib.nameValuePair "shorewall/${name}" {source=conf;}) cfg.configs; + systemPackages = [ cfg.package ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/shorewall6.nix b/nixpkgs/nixos/modules/services/networking/shorewall6.nix new file mode 100644 index 000000000000..e081aedc6c34 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/shorewall6.nix @@ -0,0 +1,70 @@ +{ config, lib, pkgs, ... }: +let + types = lib.types; + cfg = config.services.shorewall6; +in { + options = { + services.shorewall6 = { + enable = lib.mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable Shorewall IPv6 Firewall. + <warning> + <para> + Enabling this service WILL disable the existing NixOS + firewall! Default firewall rules provided by packages are not + considered at the moment. + </para> + </warning> + ''; + }; + package = lib.mkOption { + type = types.package; + default = pkgs.shorewall; + defaultText = "pkgs.shorewall"; + description = "The shorewall package to use."; + }; + configs = lib.mkOption { + type = types.attrsOf types.lines; + default = {}; + description = '' + This option defines the Shorewall configs. + The attribute name defines the name of the config, + and the attribute value defines the content of the config. + ''; + apply = lib.mapAttrs (name: text: pkgs.writeText "${name}" text); + }; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.firewall.enable = false; + systemd.services.shorewall6 = { + description = "Shorewall IPv6 Firewall"; + after = [ "ipset.target" ]; + before = [ "network-pre.target" ]; + wants = [ "network-pre.target" ]; + wantedBy = [ "multi-user.target" ]; + reloadIfChanged = true; + restartTriggers = lib.attrValues cfg.configs; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + ExecStart = "${cfg.package}/bin/shorewall6 start"; + ExecReload = "${cfg.package}/bin/shorewall6 reload"; + ExecStop = "${cfg.package}/bin/shorewall6 stop"; + }; + preStart = '' + install -D -d -m 750 /var/lib/shorewall6 + install -D -d -m 755 /var/lock/subsys + touch /var/log/shorewall6.log + chown 750 /var/log/shorewall6.log + ''; + }; + environment = { + etc = lib.mapAttrs' (name: conf: lib.nameValuePair "shorewall6/${name}" {source=conf;}) cfg.configs; + systemPackages = [ cfg.package ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/shout.nix b/nixpkgs/nixos/modules/services/networking/shout.nix new file mode 100644 index 000000000000..a808a7f39d05 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/shout.nix @@ -0,0 +1,113 @@ +{ pkgs, lib, config, ... }: + +with lib; + +let + cfg = config.services.shout; + shoutHome = "/var/lib/shout"; + + defaultConfig = pkgs.runCommand "config.js" { preferLocalBuild = true; } '' + EDITOR=true ${pkgs.shout}/bin/shout config --home $PWD + mv config.js $out + ''; + + finalConfigFile = if (cfg.configFile != null) then cfg.configFile else '' + var _ = require('${pkgs.shout}/lib/node_modules/shout/node_modules/lodash') + + module.exports = _.merge( + {}, + require('${defaultConfig}'), + ${builtins.toJSON cfg.config} + ) + ''; + +in { + options.services.shout = { + enable = mkEnableOption "Shout web IRC client"; + + private = mkOption { + type = types.bool; + default = false; + description = '' + Make your shout instance private. You will need to configure user + accounts by adding entries in <filename>${shoutHome}/users</filename>. + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "IP interface to listen on for http connections."; + }; + + port = mkOption { + type = types.int; + default = 9000; + description = "TCP port to listen on for http connections."; + }; + + configFile = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + Contents of Shout's <filename>config.js</filename> file. + + Used for backward compatibility, recommended way is now to use + the <literal>config</literal> option. + + Documentation: http://shout-irc.com/docs/server/configuration.html + ''; + }; + + config = mkOption { + default = {}; + type = types.attrs; + example = { + displayNetwork = false; + defaults = { + name = "Your Network"; + host = "localhost"; + port = 6697; + }; + }; + description = '' + Shout <filename>config.js</filename> contents as attribute set (will be + converted to JSON to generate the configuration file). + + The options defined here will be merged to the default configuration file. + + Documentation: http://shout-irc.com/docs/server/configuration.html + ''; + }; + }; + + config = mkIf cfg.enable { + users.users.shout = { + uid = config.ids.uids.shout; + description = "Shout daemon user"; + home = shoutHome; + createHome = true; + }; + + systemd.services.shout = { + description = "Shout web IRC client"; + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + preStart = "ln -sf ${pkgs.writeText "config.js" finalConfigFile} ${shoutHome}/config.js"; + script = concatStringsSep " " [ + "${pkgs.shout}/bin/shout" + (if cfg.private then "--private" else "--public") + "--port" (toString cfg.port) + "--host" (toString cfg.listenAddress) + "--home" shoutHome + ]; + serviceConfig = { + User = "shout"; + ProtectHome = "true"; + ProtectSystem = "full"; + PrivateTmp = "true"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/skydns.nix b/nixpkgs/nixos/modules/services/networking/skydns.nix new file mode 100644 index 000000000000..e79d6de92644 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/skydns.nix @@ -0,0 +1,92 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + cfg = config.services.skydns; + +in { + options.services.skydns = { + enable = mkEnableOption "skydns service"; + + etcd = { + machines = mkOption { + default = [ "http://127.0.0.1:2379" ]; + type = types.listOf types.str; + description = "Skydns list of etcd endpoints to connect to."; + }; + + tlsKey = mkOption { + default = null; + type = types.nullOr types.path; + description = "Skydns path of TLS client certificate - private key."; + }; + + tlsPem = mkOption { + default = null; + type = types.nullOr types.path; + description = "Skydns path of TLS client certificate - public key."; + }; + + caCert = mkOption { + default = null; + type = types.nullOr types.path; + description = "Skydns path of TLS certificate authority public key."; + }; + }; + + address = mkOption { + default = "0.0.0.0:53"; + type = types.str; + description = "Skydns address to bind to."; + }; + + domain = mkOption { + default = "skydns.local."; + type = types.str; + description = "Skydns default domain if not specified by etcd config."; + }; + + nameservers = mkOption { + default = map (n: n + ":53") config.networking.nameservers; + type = types.listOf types.str; + description = "Skydns list of nameservers to forward DNS requests to when not authoritative for a domain."; + example = ["8.8.8.8:53" "8.8.4.4:53"]; + }; + + package = mkOption { + default = pkgs.skydns; + defaultText = "pkgs.skydns"; + type = types.package; + description = "Skydns package to use."; + }; + + extraConfig = mkOption { + default = {}; + type = types.attrsOf types.str; + description = "Skydns attribute set of extra config options passed as environemnt variables."; + }; + }; + + config = mkIf (cfg.enable) { + systemd.services.skydns = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "etcd.service" ]; + description = "Skydns Service"; + environment = { + ETCD_MACHINES = concatStringsSep "," cfg.etcd.machines; + ETCD_TLSKEY = cfg.etcd.tlsKey; + ETCD_TLSPEM = cfg.etcd.tlsPem; + ETCD_CACERT = cfg.etcd.caCert; + SKYDNS_ADDR = cfg.address; + SKYDNS_DOMAIN = cfg.domain; + SKYDNS_NAMESERVERS = concatStringsSep "," cfg.nameservers; + }; + serviceConfig = { + ExecStart = "${cfg.package}/bin/skydns"; + }; + }; + + environment.systemPackages = [ cfg.package ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/smartdns.nix b/nixpkgs/nixos/modules/services/networking/smartdns.nix new file mode 100644 index 000000000000..f1888af70416 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/smartdns.nix @@ -0,0 +1,61 @@ +{ lib, pkgs, config, ... }: + +with lib; + +let + inherit (lib.types) attrsOf coercedTo listOf oneOf str int bool; + cfg = config.services.smartdns; + + confFile = pkgs.writeText "smartdns.conf" (with generators; + toKeyValue { + mkKeyValue = mkKeyValueDefault { + mkValueString = v: + if isBool v then + if v then "yes" else "no" + else + mkValueStringDefault { } v; + } " "; + listsAsDuplicateKeys = + true; # Allowing duplications because we need to deal with multiple entries with the same key. + } cfg.settings); +in { + options.services.smartdns = { + enable = mkEnableOption "SmartDNS DNS server"; + + bindPort = mkOption { + type = types.port; + default = 53; + description = "DNS listening port number."; + }; + + settings = mkOption { + type = + let atom = oneOf [ str int bool ]; + in attrsOf (coercedTo atom toList (listOf atom)); + example = literalExample '' + { + bind = ":5353 -no-rule -group example"; + cache-size = 4096; + server-tls = [ "8.8.8.8:853" "1.1.1.1:853" ]; + server-https = "https://cloudflare-dns.com/dns-query -exclude-default-group"; + prefetch-domain = true; + speed-check-mode = "ping,tcp:80"; + }; + ''; + description = '' + A set that will be generated into configuration file, see the <link xlink:href="https://github.com/pymumu/smartdns/blob/master/ReadMe_en.md#configuration-parameter">SmartDNS README</link> for details of configuration parameters. + You could override the options here like <option>services.smartdns.bindPort</option> by writing <literal>settings.bind = ":5353 -no-rule -group example";</literal>. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + services.smartdns.settings.bind = mkDefault ":${toString cfg.bindPort}"; + + systemd.packages = [ pkgs.smartdns ]; + systemd.services.smartdns.wantedBy = [ "multi-user.target" ]; + environment.etc."smartdns/smartdns.conf".source = confFile; + environment.etc."default/smartdns".source = + "${pkgs.smartdns}/etc/default/smartdns"; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/smokeping.nix b/nixpkgs/nixos/modules/services/networking/smokeping.nix new file mode 100644 index 000000000000..37ee2a803890 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/smokeping.nix @@ -0,0 +1,320 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + + cfg = config.services.smokeping; + smokepingHome = "/var/lib/smokeping"; + smokepingPidDir = "/run"; + configFile = + if cfg.config == null + then + '' + *** General *** + cgiurl = ${cfg.cgiUrl} + contact = ${cfg.ownerEmail} + datadir = ${smokepingHome}/data + imgcache = ${smokepingHome}/cache + imgurl = ${cfg.imgUrl} + linkstyle = ${cfg.linkStyle} + ${lib.optionalString (cfg.mailHost != "") "mailhost = ${cfg.mailHost}"} + owner = ${cfg.owner} + pagedir = ${smokepingHome}/cache + piddir = ${smokepingPidDir} + ${lib.optionalString (cfg.sendmail != null) "sendmail = ${cfg.sendmail}"} + smokemail = ${cfg.smokeMailTemplate} + *** Presentation *** + template = ${cfg.presentationTemplate} + ${cfg.presentationConfig} + *** Alerts *** + ${cfg.alertConfig} + *** Database *** + ${cfg.databaseConfig} + *** Probes *** + ${cfg.probeConfig} + *** Targets *** + ${cfg.targetConfig} + ${cfg.extraConfig} + '' + else + cfg.config; + + configPath = pkgs.writeText "smokeping.conf" configFile; + cgiHome = pkgs.writeScript "smokeping.fcgi" '' + #!${pkgs.bash}/bin/bash + ${cfg.package}/bin/smokeping_cgi ${configPath} + ''; +in + +{ + options = { + services.smokeping = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable the smokeping service"; + }; + alertConfig = mkOption { + type = types.lines; + default = '' + to = root@localhost + from = smokeping@localhost + ''; + example = literalExample '' + to = alertee@address.somewhere + from = smokealert@company.xy + + +someloss + type = loss + # in percent + pattern = >0%,*12*,>0%,*12*,>0% + comment = loss 3 times in a row; + ''; + description = "Configuration for alerts."; + }; + cgiUrl = mkOption { + type = types.str; + default = "http://${cfg.hostName}:${toString cfg.port}/smokeping.cgi"; + defaultText = "http://\${hostName}:\${toString port}/smokeping.cgi"; + example = "https://somewhere.example.com/smokeping.cgi"; + description = "URL to the smokeping cgi."; + }; + config = mkOption { + type = types.nullOr types.lines; + default = null; + description = "Full smokeping config supplied by the user. Overrides " + + "and replaces any other configuration supplied."; + }; + databaseConfig = mkOption { + type = types.lines; + default = '' + step = 300 + pings = 20 + # consfn mrhb steps total + AVERAGE 0.5 1 1008 + AVERAGE 0.5 12 4320 + MIN 0.5 12 4320 + MAX 0.5 12 4320 + AVERAGE 0.5 144 720 + MAX 0.5 144 720 + MIN 0.5 144 720 + + ''; + example = literalExample '' + # near constant pings. + step = 30 + pings = 20 + # consfn mrhb steps total + AVERAGE 0.5 1 10080 + AVERAGE 0.5 12 43200 + MIN 0.5 12 43200 + MAX 0.5 12 43200 + AVERAGE 0.5 144 7200 + MAX 0.5 144 7200 + MIN 0.5 144 7200 + ''; + description = ''Configure the ping frequency and retention of the rrd files. + Once set, changing the interval will require deletion or migration of all + the collected data.''; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Any additional customization not already included."; + }; + hostName = mkOption { + type = types.str; + default = config.networking.hostName; + example = "somewhere.example.com"; + description = "DNS name for the urls generated in the cgi."; + }; + imgUrl = mkOption { + type = types.str; + default = "http://${cfg.hostName}:${toString cfg.port}/cache"; + defaultText = "http://\${hostName}:\${toString port}/cache"; + example = "https://somewhere.example.com/cache"; + description = "Base url for images generated in the cgi."; + }; + linkStyle = mkOption { + type = types.enum ["original" "absolute" "relative"]; + default = "relative"; + example = "absolute"; + description = "DNS name for the urls generated in the cgi."; + }; + mailHost = mkOption { + type = types.str; + default = ""; + example = "localhost"; + description = "Use this SMTP server to send alerts"; + }; + owner = mkOption { + type = types.str; + default = "nobody"; + example = "Joe Admin"; + description = "Real name of the owner of the instance"; + }; + ownerEmail = mkOption { + type = types.str; + default = "no-reply@${cfg.hostName}"; + example = "no-reply@yourdomain.com"; + description = "Email contact for owner"; + }; + package = mkOption { + type = types.package; + default = pkgs.smokeping; + defaultText = "pkgs.smokeping"; + description = "Specify a custom smokeping package"; + }; + port = mkOption { + type = types.int; + default = 8081; + example = 8081; + description = "TCP port to use for the web server."; + }; + presentationConfig = mkOption { + type = types.lines; + default = '' + + charts + menu = Charts + title = The most interesting destinations + ++ stddev + sorter = StdDev(entries=>4) + title = Top Standard Deviation + menu = Std Deviation + format = Standard Deviation %f + ++ max + sorter = Max(entries=>5) + title = Top Max Roundtrip Time + menu = by Max + format = Max Roundtrip Time %f seconds + ++ loss + sorter = Loss(entries=>5) + title = Top Packet Loss + menu = Loss + format = Packets Lost %f + ++ median + sorter = Median(entries=>5) + title = Top Median Roundtrip Time + menu = by Median + format = Median RTT %f seconds + + overview + width = 600 + height = 50 + range = 10h + + detail + width = 600 + height = 200 + unison_tolerance = 2 + "Last 3 Hours" 3h + "Last 30 Hours" 30h + "Last 10 Days" 10d + "Last 360 Days" 360d + ''; + description = "presentation graph style"; + }; + presentationTemplate = mkOption { + type = types.str; + default = "${pkgs.smokeping}/etc/basepage.html.dist"; + description = "Default page layout for the web UI."; + }; + probeConfig = mkOption { + type = types.lines; + default = '' + + FPing + binary = ${config.security.wrapperDir}/fping + ''; + description = "Probe configuration"; + }; + sendmail = mkOption { + type = types.nullOr types.path; + default = null; + example = "/run/wrappers/bin/sendmail"; + description = "Use this sendmail compatible script to deliver alerts"; + }; + smokeMailTemplate = mkOption { + type = types.str; + default = "${cfg.package}/etc/smokemail.dist"; + description = "Specify the smokemail template for alerts."; + }; + targetConfig = mkOption { + type = types.lines; + default = '' + probe = FPing + menu = Top + title = Network Latency Grapher + remark = Welcome to the SmokePing website of xxx Company. \ + Here you will learn all about the latency of our network. + + Local + menu = Local + title = Local Network + ++ LocalMachine + menu = Local Machine + title = This host + host = localhost + ''; + description = "Target configuration"; + }; + user = mkOption { + type = types.str; + default = "smokeping"; + description = "User that runs smokeping and (optionally) thttpd"; + }; + webService = mkOption { + type = types.bool; + default = true; + description = "Enable a smokeping web interface"; + }; + }; + + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = !(cfg.sendmail != null && cfg.mailHost != ""); + message = "services.smokeping: sendmail and Mailhost cannot both be enabled."; + } + ]; + security.wrappers = { + fping.source = "${pkgs.fping}/bin/fping"; + fping6.source = "${pkgs.fping}/bin/fping6"; + }; + environment.systemPackages = [ pkgs.fping ]; + users.users.${cfg.user} = { + isNormalUser = false; + isSystemUser = true; + uid = config.ids.uids.smokeping; + description = "smokeping daemon user"; + home = smokepingHome; + createHome = true; + }; + systemd.services.smokeping = { + wantedBy = [ "multi-user.target"]; + serviceConfig = { + User = cfg.user; + Restart = "on-failure"; + }; + preStart = '' + mkdir -m 0755 -p ${smokepingHome}/cache ${smokepingHome}/data + rm -f ${smokepingHome}/cropper + ln -s ${cfg.package}/htdocs/cropper ${smokepingHome}/cropper + rm -f ${smokepingHome}/smokeping.fcgi + ln -s ${cgiHome} ${smokepingHome}/smokeping.fcgi + ${cfg.package}/bin/smokeping --check --config=${configPath} + ${cfg.package}/bin/smokeping --static --config=${configPath} + ''; + script = ''${cfg.package}/bin/smokeping --config=${configPath} --nodaemon''; + }; + systemd.services.thttpd = mkIf cfg.webService { + wantedBy = [ "multi-user.target"]; + requires = [ "smokeping.service"]; + partOf = [ "smokeping.service"]; + path = with pkgs; [ bash rrdtool smokeping thttpd ]; + script = ''thttpd -u ${cfg.user} -c "**.fcgi" -d ${smokepingHome} -p ${builtins.toString cfg.port} -D -nos''; + serviceConfig.Restart = "always"; + }; + }; + + meta.maintainers = with lib.maintainers; [ erictapen ]; +} + diff --git a/nixpkgs/nixos/modules/services/networking/sniproxy.nix b/nixpkgs/nixos/modules/services/networking/sniproxy.nix new file mode 100644 index 000000000000..0345c12d3afe --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/sniproxy.nix @@ -0,0 +1,99 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + + cfg = config.services.sniproxy; + + configFile = pkgs.writeText "sniproxy.conf" '' + user ${cfg.user} + pidfile /run/sniproxy.pid + ${cfg.config} + ''; + +in +{ + options = { + services.sniproxy = { + enable = mkEnableOption "sniproxy server"; + + user = mkOption { + type = types.str; + default = "sniproxy"; + description = "User account under which sniproxy runs."; + }; + + group = mkOption { + type = types.str; + default = "sniproxy"; + description = "Group under which sniproxy runs."; + }; + + config = mkOption { + type = types.lines; + default = ""; + description = "sniproxy.conf configuration excluding the daemon username and pid file."; + example = literalExample '' + error_log { + filename /var/log/sniproxy/error.log + } + access_log { + filename /var/log/sniproxy/access.log + } + listen 443 { + proto tls + } + table { + example.com 192.0.2.10 + example.net 192.0.2.20 + } + ''; + }; + + logDir = mkOption { + type = types.str; + default = "/var/log/sniproxy/"; + description = "Location of the log directory for sniproxy."; + }; + + }; + + }; + + config = mkIf cfg.enable { + systemd.services.sniproxy = { + description = "sniproxy server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + test -d ${cfg.logDir} || { + echo "Creating initial log directory for sniproxy in ${cfg.logDir}" + mkdir -p ${cfg.logDir} + chmod 640 ${cfg.logDir} + } + chown -R ${cfg.user}:${cfg.group} ${cfg.logDir} + ''; + + serviceConfig = { + Type = "forking"; + ExecStart = "${pkgs.sniproxy}/bin/sniproxy -c ${configFile}"; + Restart = "always"; + }; + }; + + users.users = mkIf (cfg.user == "sniproxy") { + sniproxy = { + group = cfg.group; + uid = config.ids.uids.sniproxy; + }; + }; + + users.groups = mkIf (cfg.group == "sniproxy") { + sniproxy = { + gid = config.ids.gids.sniproxy; + }; + }; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/softether.nix b/nixpkgs/nixos/modules/services/networking/softether.nix new file mode 100644 index 000000000000..2dc73d81b258 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/softether.nix @@ -0,0 +1,163 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.softether; + + package = cfg.package.override { dataDir = cfg.dataDir; }; + +in +{ + + ###### interface + + options = { + + services.softether = { + + enable = mkEnableOption "SoftEther VPN services"; + + package = mkOption { + type = types.package; + default = pkgs.softether; + defaultText = "pkgs.softether"; + description = '' + softether derivation to use. + ''; + }; + + vpnserver.enable = mkEnableOption "SoftEther VPN Server"; + + vpnbridge.enable = mkEnableOption "SoftEther VPN Bridge"; + + vpnclient = { + enable = mkEnableOption "SoftEther VPN Client"; + up = mkOption { + type = types.lines; + default = ""; + description = '' + Shell commands executed when the Virtual Network Adapter(s) is/are starting. + ''; + }; + down = mkOption { + type = types.lines; + default = ""; + description = '' + Shell commands executed when the Virtual Network Adapter(s) is/are shutting down. + ''; + }; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/softether"; + description = '' + Data directory for SoftEther VPN. + ''; + }; + + }; + + }; + + ###### implementation + + config = mkIf cfg.enable ( + + mkMerge [{ + environment.systemPackages = [ package ]; + + systemd.services.softether-init = { + description = "SoftEther VPN services initial task"; + wantedBy = [ "network.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = false; + }; + script = '' + for d in vpnserver vpnbridge vpnclient vpncmd; do + if ! test -e ${cfg.dataDir}/$d; then + ${pkgs.coreutils}/bin/mkdir -m0700 -p ${cfg.dataDir}/$d + install -m0600 ${package}${cfg.dataDir}/$d/hamcore.se2 ${cfg.dataDir}/$d/hamcore.se2 + fi + done + rm -rf ${cfg.dataDir}/vpncmd/vpncmd + ln -s ${package}${cfg.dataDir}/vpncmd/vpncmd ${cfg.dataDir}/vpncmd/vpncmd + ''; + }; + } + + (mkIf (cfg.vpnserver.enable) { + systemd.services.vpnserver = { + description = "SoftEther VPN Server"; + after = [ "softether-init.service" ]; + requires = [ "softether-init.service" ]; + wantedBy = [ "network.target" ]; + serviceConfig = { + Type = "forking"; + ExecStart = "${package}/bin/vpnserver start"; + ExecStop = "${package}/bin/vpnserver stop"; + }; + preStart = '' + rm -rf ${cfg.dataDir}/vpnserver/vpnserver + ln -s ${package}${cfg.dataDir}/vpnserver/vpnserver ${cfg.dataDir}/vpnserver/vpnserver + ''; + postStop = '' + rm -rf ${cfg.dataDir}/vpnserver/vpnserver + ''; + }; + }) + + (mkIf (cfg.vpnbridge.enable) { + systemd.services.vpnbridge = { + description = "SoftEther VPN Bridge"; + after = [ "softether-init.service" ]; + requires = [ "softether-init.service" ]; + wantedBy = [ "network.target" ]; + serviceConfig = { + Type = "forking"; + ExecStart = "${package}/bin/vpnbridge start"; + ExecStop = "${package}/bin/vpnbridge stop"; + }; + preStart = '' + rm -rf ${cfg.dataDir}/vpnbridge/vpnbridge + ln -s ${package}${cfg.dataDir}/vpnbridge/vpnbridge ${cfg.dataDir}/vpnbridge/vpnbridge + ''; + postStop = '' + rm -rf ${cfg.dataDir}/vpnbridge/vpnbridge + ''; + }; + }) + + (mkIf (cfg.vpnclient.enable) { + systemd.services.vpnclient = { + description = "SoftEther VPN Client"; + after = [ "softether-init.service" ]; + requires = [ "softether-init.service" ]; + wantedBy = [ "network.target" ]; + serviceConfig = { + Type = "forking"; + ExecStart = "${package}/bin/vpnclient start"; + ExecStop = "${package}/bin/vpnclient stop"; + }; + preStart = '' + rm -rf ${cfg.dataDir}/vpnclient/vpnclient + ln -s ${package}${cfg.dataDir}/vpnclient/vpnclient ${cfg.dataDir}/vpnclient/vpnclient + ''; + postStart = '' + sleep 1 + ${cfg.vpnclient.up} + ''; + postStop = '' + rm -rf ${cfg.dataDir}/vpnclient/vpnclient + sleep 1 + ${cfg.vpnclient.down} + ''; + }; + boot.kernelModules = [ "tun" ]; + }) + + ]); + +} diff --git a/nixpkgs/nixos/modules/services/networking/spacecookie.nix b/nixpkgs/nixos/modules/services/networking/spacecookie.nix new file mode 100644 index 000000000000..c4d06df6ad4a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/spacecookie.nix @@ -0,0 +1,83 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.spacecookie; + configFile = pkgs.writeText "spacecookie.json" (lib.generators.toJSON {} { + inherit (cfg) hostname port root; + }); +in { + + options = { + + services.spacecookie = { + + enable = mkEnableOption "spacecookie"; + + hostname = mkOption { + type = types.str; + default = "localhost"; + description = "The hostname the service is reachable via. Clients will use this hostname for further requests after loading the initial gopher menu."; + }; + + port = mkOption { + type = types.port; + default = 70; + description = "Port the gopher service should be exposed on."; + }; + + root = mkOption { + type = types.path; + default = "/srv/gopher"; + description = "The root directory spacecookie serves via gopher."; + }; + }; + }; + + config = mkIf cfg.enable { + + systemd.sockets.spacecookie = { + description = "Socket for the Spacecookie Gopher Server"; + wantedBy = [ "sockets.target" ]; + listenStreams = [ "[::]:${toString cfg.port}" ]; + socketConfig = { + BindIPv6Only = "both"; + }; + }; + + systemd.services.spacecookie = { + description = "Spacecookie Gopher Server"; + wantedBy = [ "multi-user.target" ]; + requires = [ "spacecookie.socket" ]; + + serviceConfig = { + Type = "notify"; + ExecStart = "${pkgs.haskellPackages.spacecookie}/bin/spacecookie ${configFile}"; + FileDescriptorStoreMax = 1; + + DynamicUser = true; + + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateUsers = true; + + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + + CapabilityBoundingSet = ""; + NoNewPrivileges = true; + LockPersonality = true; + RestrictRealtime = true; + + # AF_UNIX for communication with systemd + # AF_INET replaced by BindIPv6Only=both + RestrictAddressFamilies = "AF_UNIX AF_INET6"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/spiped.nix b/nixpkgs/nixos/modules/services/networking/spiped.nix new file mode 100644 index 000000000000..e60d9abf42a6 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/spiped.nix @@ -0,0 +1,220 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.spiped; +in +{ + options = { + services.spiped = { + enable = mkOption { + type = types.bool; + default = false; + description = "Enable the spiped service module."; + }; + + config = mkOption { + type = types.attrsOf (types.submodule ( + { + options = { + encrypt = mkOption { + type = types.bool; + default = false; + description = '' + Take unencrypted connections from the + <literal>source</literal> socket and send encrypted + connections to the <literal>target</literal> socket. + ''; + }; + + decrypt = mkOption { + type = types.bool; + default = false; + description = '' + Take encrypted connections from the + <literal>source</literal> socket and send unencrypted + connections to the <literal>target</literal> socket. + ''; + }; + + source = mkOption { + type = types.str; + description = '' + Address on which spiped should listen for incoming + connections. Must be in one of the following formats: + <literal>/absolute/path/to/unix/socket</literal>, + <literal>host.name:port</literal>, + <literal>[ip.v4.ad.dr]:port</literal> or + <literal>[ipv6::addr]:port</literal> - note that + hostnames are resolved when spiped is launched and are + not re-resolved later; thus if DNS entries change + spiped will continue to connect to the expired + address. + ''; + }; + + target = mkOption { + type = types.str; + description = "Address to which spiped should connect."; + }; + + keyfile = mkOption { + type = types.path; + description = '' + Name of a file containing the spiped key. As the + daemon runs as the <literal>spiped</literal> user, the + key file must be somewhere owned by that user. By + default, we recommend putting the keys for any spipe + services in <literal>/var/lib/spiped</literal>. + ''; + }; + + timeout = mkOption { + type = types.int; + default = 5; + description = '' + Timeout, in seconds, after which an attempt to connect to + the target or a protocol handshake will be aborted (and the + connection dropped) if not completed + ''; + }; + + maxConns = mkOption { + type = types.int; + default = 100; + description = '' + Limit on the number of simultaneous connections allowed. + ''; + }; + + waitForDNS = mkOption { + type = types.bool; + default = false; + description = '' + Wait for DNS. Normally when <literal>spiped</literal> is + launched it resolves addresses and binds to its source + socket before the parent process returns; with this option + it will daemonize first and retry failed DNS lookups until + they succeed. This allows <literal>spiped</literal> to + launch even if DNS isn't set up yet, but at the expense of + losing the guarantee that once <literal>spiped</literal> has + finished launching it will be ready to create pipes. + ''; + }; + + disableKeepalives = mkOption { + type = types.bool; + default = false; + description = "Disable transport layer keep-alives."; + }; + + weakHandshake = mkOption { + type = types.bool; + default = false; + description = '' + Use fast/weak handshaking: This reduces the CPU time spent + in the initial connection setup, at the expense of losing + perfect forward secrecy. + ''; + }; + + resolveRefresh = mkOption { + type = types.int; + default = 60; + description = '' + Resolution refresh time for the target socket, in seconds. + ''; + }; + + disableReresolution = mkOption { + type = types.bool; + default = false; + description = "Disable target address re-resolution."; + }; + }; + } + )); + + default = {}; + + example = literalExample '' + { + pipe1 = + { keyfile = "/var/lib/spiped/pipe1.key"; + encrypt = true; + source = "localhost:6000"; + target = "endpoint.example.com:7000"; + }; + pipe2 = + { keyfile = "/var/lib/spiped/pipe2.key"; + decrypt = true; + source = "0.0.0.0:7000"; + target = "localhost:3000"; + }; + } + ''; + + description = '' + Configuration for a secure pipe daemon. The daemon can be + started, stopped, or examined using + <literal>systemctl</literal>, under the name + <literal>spiped@foo</literal>. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = mapAttrsToList (name: c: { + assertion = (c.encrypt -> !c.decrypt) || (c.decrypt -> c.encrypt); + message = "A pipe must either encrypt or decrypt"; + }) cfg.config; + + users.groups.spiped.gid = config.ids.gids.spiped; + users.users.spiped = { + description = "Secure Pipe Service user"; + group = "spiped"; + uid = config.ids.uids.spiped; + }; + + systemd.services."spiped@" = { + description = "Secure pipe '%i'"; + after = [ "network.target" ]; + + serviceConfig = { + Restart = "always"; + User = "spiped"; + PermissionsStartOnly = true; + }; + + preStart = '' + cd /var/lib/spiped + chmod -R 0660 * + chown -R spiped:spiped * + ''; + scriptArgs = "%i"; + script = "exec ${pkgs.spiped}/bin/spiped -F `cat /etc/spiped/$1.spec`"; + }; + + system.activationScripts.spiped = optionalString (cfg.config != {}) + "mkdir -p /var/lib/spiped"; + + # Setup spiped config files + environment.etc = mapAttrs' (name: cfg: nameValuePair "spiped/${name}.spec" + { text = concatStringsSep " " + [ (if cfg.encrypt then "-e" else "-d") # Mode + "-s ${cfg.source}" # Source + "-t ${cfg.target}" # Target + "-k ${cfg.keyfile}" # Keyfile + "-n ${toString cfg.maxConns}" # Max number of conns + "-o ${toString cfg.timeout}" # Timeout + (optionalString cfg.waitForDNS "-D") # Wait for DNS + (optionalString cfg.weakHandshake "-f") # No PFS + (optionalString cfg.disableKeepalives "-j") # Keepalives + (if cfg.disableReresolution then "-R" + else "-r ${toString cfg.resolveRefresh}") + ]; + }) cfg.config; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/squid.nix b/nixpkgs/nixos/modules/services/networking/squid.nix new file mode 100644 index 000000000000..9d063b92aa1e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/squid.nix @@ -0,0 +1,168 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.squid; + + + squidConfig = pkgs.writeText "squid.conf" + (if cfg.configText != null then cfg.configText else + '' + # + # Recommended minimum configuration (3.5): + # + + # Example rule allowing access from your local networks. + # Adapt to list your (internal) IP networks from where browsing + # should be allowed + acl localnet src 10.0.0.0/8 # RFC 1918 possible internal network + acl localnet src 172.16.0.0/12 # RFC 1918 possible internal network + acl localnet src 192.168.0.0/16 # RFC 1918 possible internal network + acl localnet src 169.254.0.0/16 # RFC 3927 link-local (directly plugged) machines + acl localnet src fc00::/7 # RFC 4193 local private network range + acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines + + acl SSL_ports port 443 # https + acl Safe_ports port 80 # http + acl Safe_ports port 21 # ftp + acl Safe_ports port 443 # https + acl Safe_ports port 70 # gopher + acl Safe_ports port 210 # wais + acl Safe_ports port 1025-65535 # unregistered ports + acl Safe_ports port 280 # http-mgmt + acl Safe_ports port 488 # gss-http + acl Safe_ports port 591 # filemaker + acl Safe_ports port 777 # multiling http + acl CONNECT method CONNECT + + # + # Recommended minimum Access Permission configuration: + # + # Deny requests to certain unsafe ports + http_access deny !Safe_ports + + # Deny CONNECT to other than secure SSL ports + http_access deny CONNECT !SSL_ports + + # Only allow cachemgr access from localhost + http_access allow localhost manager + http_access deny manager + + # We strongly recommend the following be uncommented to protect innocent + # web applications running on the proxy server who think the only + # one who can access services on "localhost" is a local user + http_access deny to_localhost + + # Application logs to syslog, access and store logs have specific files + cache_log syslog + access_log stdio:/var/log/squid/access.log + cache_store_log stdio:/var/log/squid/store.log + + # Required by systemd service + pid_filename /run/squid.pid + + # Run as user and group squid + cache_effective_user squid squid + + # + # INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS + # + ${cfg.extraConfig} + + # Example rule allowing access from your local networks. + # Adapt localnet in the ACL section to list your (internal) IP networks + # from where browsing should be allowed + http_access allow localnet + http_access allow localhost + + # And finally deny all other access to this proxy + http_access deny all + + # Squid normally listens to port 3128 + http_port ${toString cfg.proxyPort} + + # Leave coredumps in the first cache dir + coredump_dir /var/cache/squid + + # + # Add any of your own refresh_pattern entries above these. + # + refresh_pattern ^ftp: 1440 20% 10080 + refresh_pattern ^gopher: 1440 0% 1440 + refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 + refresh_pattern . 0 20% 4320 + ''); + +in + +{ + + options = { + + services.squid = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to run squid web proxy."; + }; + + proxyPort = mkOption { + type = types.int; + default = 3128; + description = "TCP port on which squid will listen."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Squid configuration. Contents will be added + verbatim to the configuration file. + ''; + }; + + configText = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + Verbatim contents of squid.conf. If null (default), use the + autogenerated file from NixOS instead. + ''; + }; + + }; + + }; + + config = mkIf cfg.enable { + + users.users.squid = { + isSystemUser = true; + group = "squid"; + home = "/var/cache/squid"; + createHome = true; + }; + + users.groups.squid = {}; + + systemd.services.squid = { + description = "Squid caching web proxy"; + after = [ "network.target" "nss-lookup.target" ]; + wantedBy = [ "multi-user.target"]; + preStart = '' + mkdir -p "/var/log/squid" + chown squid:squid "/var/log/squid" + ''; + serviceConfig = { + Type="forking"; + PIDFile="/run/squid.pid"; + ExecStart = "${pkgs.squid}/bin/squid -YCs -f ${squidConfig}"; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix new file mode 100644 index 000000000000..41d0584080e4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix @@ -0,0 +1,183 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (pkgs) lsh; + + cfg = config.services.lshd; + +in + +{ + + ###### interface + + options = { + + services.lshd = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the GNU lshd SSH2 daemon, which allows + secure remote login. + ''; + }; + + portNumber = mkOption { + default = 22; + description = '' + The port on which to listen for connections. + ''; + }; + + interfaces = mkOption { + default = []; + description = '' + List of network interfaces where listening for connections. + When providing the empty list, `[]', lshd listens on all + network interfaces. + ''; + example = [ "localhost" "1.2.3.4:443" ]; + }; + + hostKey = mkOption { + default = "/etc/lsh/host-key"; + description = '' + Path to the server's private key. Note that this key must + have been created, e.g., using "lsh-keygen --server | + lsh-writekey --server", so that you can run lshd. + ''; + }; + + syslog = mkOption { + type = types.bool; + default = true; + description = ''Whether to enable syslog output.''; + }; + + passwordAuthentication = mkOption { + type = types.bool; + default = true; + description = ''Whether to enable password authentication.''; + }; + + publicKeyAuthentication = mkOption { + type = types.bool; + default = true; + description = ''Whether to enable public key authentication.''; + }; + + rootLogin = mkOption { + type = types.bool; + default = false; + description = ''Whether to enable remote root login.''; + }; + + loginShell = mkOption { + default = null; + description = '' + If non-null, override the default login shell with the + specified value. + ''; + example = "/nix/store/xyz-bash-10.0/bin/bash10"; + }; + + srpKeyExchange = mkOption { + default = false; + description = '' + Whether to enable SRP key exchange and user authentication. + ''; + }; + + tcpForwarding = mkOption { + type = types.bool; + default = true; + description = ''Whether to enable TCP/IP forwarding.''; + }; + + x11Forwarding = mkOption { + type = types.bool; + default = true; + description = ''Whether to enable X11 forwarding.''; + }; + + subsystems = mkOption { + description = '' + List of subsystem-path pairs, where the head of the pair + denotes the subsystem name, and the tail denotes the path to + an executable implementing it. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + services.lshd.subsystems = [ ["sftp" "${pkgs.lsh}/sbin/sftp-server"] ]; + + systemd.services.lshd = { + description = "GNU lshd SSH2 daemon"; + + after = [ "network.target" ]; + + wantedBy = [ "multi-user.target" ]; + + environment = { + LD_LIBRARY_PATH = config.system.nssModules.path; + }; + + preStart = '' + test -d /etc/lsh || mkdir -m 0755 -p /etc/lsh + test -d /var/spool/lsh || mkdir -m 0755 -p /var/spool/lsh + + if ! test -f /var/spool/lsh/yarrow-seed-file + then + # XXX: It would be nice to provide feedback to the + # user when this fails, so that they can retry it + # manually. + ${lsh}/bin/lsh-make-seed --sloppy \ + -o /var/spool/lsh/yarrow-seed-file + fi + + if ! test -f "${cfg.hostKey}" + then + ${lsh}/bin/lsh-keygen --server | \ + ${lsh}/bin/lsh-writekey --server -o "${cfg.hostKey}" + fi + ''; + + script = with cfg; '' + ${lsh}/sbin/lshd --daemonic \ + --password-helper="${lsh}/sbin/lsh-pam-checkpw" \ + -p ${toString portNumber} \ + ${if interfaces == [] then "" + else (concatStrings (map (i: "--interface=\"${i}\"") + interfaces))} \ + -h "${hostKey}" \ + ${if !syslog then "--no-syslog" else ""} \ + ${if passwordAuthentication then "--password" else "--no-password" } \ + ${if publicKeyAuthentication then "--publickey" else "--no-publickey" } \ + ${if rootLogin then "--root-login" else "--no-root-login" } \ + ${if loginShell != null then "--login-shell=\"${loginShell}\"" else "" } \ + ${if srpKeyExchange then "--srp-keyexchange" else "--no-srp-keyexchange" } \ + ${if !tcpForwarding then "--no-tcpip-forward" else "--tcpip-forward"} \ + ${if x11Forwarding then "--x11-forward" else "--no-x11-forward" } \ + --subsystems=${concatStringsSep "," + (map (pair: (head pair) + "=" + + (head (tail pair))) + subsystems)} + ''; + }; + + security.pam.services.lshd = {}; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix new file mode 100644 index 000000000000..a294bbfba0aa --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix @@ -0,0 +1,557 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + # The splicing information needed for nativeBuildInputs isn't available + # on the derivations likely to be used as `cfgc.package`. + # This middle-ground solution ensures *an* sshd can do their basic validation + # on the configuration. + validationPackage = if pkgs.stdenv.buildPlatform == pkgs.stdenv.hostPlatform + then cfgc.package + else pkgs.buildPackages.openssh; + + sshconf = pkgs.runCommand "sshd.conf-validated" { nativeBuildInputs = [ validationPackage ]; } '' + cat >$out <<EOL + ${cfg.extraConfig} + EOL + + ssh-keygen -q -f mock-hostkey -N "" + sshd -t -f $out -h mock-hostkey + ''; + + cfg = config.services.openssh; + cfgc = config.programs.ssh; + + nssModulesPath = config.system.nssModules.path; + + userOptions = { + + options.openssh.authorizedKeys = { + keys = mkOption { + type = types.listOf types.str; + default = []; + description = '' + A list of verbatim OpenSSH public keys that should be added to the + user's authorized keys. The keys are added to a file that the SSH + daemon reads in addition to the the user's authorized_keys file. + You can combine the <literal>keys</literal> and + <literal>keyFiles</literal> options. + Warning: If you are using <literal>NixOps</literal> then don't use this + option since it will replace the key required for deployment via ssh. + ''; + }; + + keyFiles = mkOption { + type = types.listOf types.path; + default = []; + description = '' + A list of files each containing one OpenSSH public key that should be + added to the user's authorized keys. The contents of the files are + read at build time and added to a file that the SSH daemon reads in + addition to the the user's authorized_keys file. You can combine the + <literal>keyFiles</literal> and <literal>keys</literal> options. + ''; + }; + }; + + }; + + authKeysFiles = let + mkAuthKeyFile = u: nameValuePair "ssh/authorized_keys.d/${u.name}" { + mode = "0444"; + source = pkgs.writeText "${u.name}-authorized_keys" '' + ${concatStringsSep "\n" u.openssh.authorizedKeys.keys} + ${concatMapStrings (f: readFile f + "\n") u.openssh.authorizedKeys.keyFiles} + ''; + }; + usersWithKeys = attrValues (flip filterAttrs config.users.users (n: u: + length u.openssh.authorizedKeys.keys != 0 || length u.openssh.authorizedKeys.keyFiles != 0 + )); + in listToAttrs (map mkAuthKeyFile usersWithKeys); + +in + +{ + imports = [ + (mkAliasOptionModule [ "services" "sshd" "enable" ] [ "services" "openssh" "enable" ]) + (mkAliasOptionModule [ "services" "openssh" "knownHosts" ] [ "programs" "ssh" "knownHosts" ]) + ]; + + ###### interface + + options = { + + services.openssh = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the OpenSSH secure shell daemon, which + allows secure remote logins. + ''; + }; + + startWhenNeeded = mkOption { + type = types.bool; + default = false; + description = '' + If set, <command>sshd</command> is socket-activated; that + is, instead of having it permanently running as a daemon, + systemd will start an instance for each incoming connection. + ''; + }; + + forwardX11 = mkOption { + type = types.bool; + default = false; + description = '' + Whether to allow X11 connections to be forwarded. + ''; + }; + + allowSFTP = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable the SFTP subsystem in the SSH daemon. This + enables the use of commands such as <command>sftp</command> and + <command>sshfs</command>. + ''; + }; + + sftpFlags = mkOption { + type = with types; listOf str; + default = []; + example = [ "-f AUTHPRIV" "-l INFO" ]; + description = '' + Commandline flags to add to sftp-server. + ''; + }; + + permitRootLogin = mkOption { + default = "prohibit-password"; + type = types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"]; + description = '' + Whether the root user can login using ssh. + ''; + }; + + gatewayPorts = mkOption { + type = types.str; + default = "no"; + description = '' + Specifies whether remote hosts are allowed to connect to + ports forwarded for the client. See + <citerefentry><refentrytitle>sshd_config</refentrytitle> + <manvolnum>5</manvolnum></citerefentry>. + ''; + }; + + ports = mkOption { + type = types.listOf types.port; + default = [22]; + description = '' + Specifies on which ports the SSH daemon listens. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = true; + description = '' + Whether to automatically open the specified ports in the firewall. + ''; + }; + + listenAddresses = mkOption { + type = with types; listOf (submodule { + options = { + addr = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Host, IPv4 or IPv6 address to listen to. + ''; + }; + port = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Port to listen to. + ''; + }; + }; + }); + default = []; + example = [ { addr = "192.168.3.1"; port = 22; } { addr = "0.0.0.0"; port = 64022; } ]; + description = '' + List of addresses and ports to listen on (ListenAddress directive + in config). If port is not specified for address sshd will listen + on all ports specified by <literal>ports</literal> option. + NOTE: this will override default listening on all local addresses and port 22. + NOTE: setting this option won't automatically enable given ports + in firewall configuration. + ''; + }; + + passwordAuthentication = mkOption { + type = types.bool; + default = true; + description = '' + Specifies whether password authentication is allowed. + ''; + }; + + challengeResponseAuthentication = mkOption { + type = types.bool; + default = true; + description = '' + Specifies whether challenge/response authentication is allowed. + ''; + }; + + hostKeys = mkOption { + type = types.listOf types.attrs; + default = + [ { type = "rsa"; bits = 4096; path = "/etc/ssh/ssh_host_rsa_key"; } + { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; } + ]; + example = + [ { type = "rsa"; bits = 4096; path = "/etc/ssh/ssh_host_rsa_key"; rounds = 100; openSSHFormat = true; } + { type = "ed25519"; path = "/etc/ssh/ssh_host_ed25519_key"; rounds = 100; comment = "key comment"; } + ]; + description = '' + NixOS can automatically generate SSH host keys. This option + specifies the path, type and size of each key. See + <citerefentry><refentrytitle>ssh-keygen</refentrytitle> + <manvolnum>1</manvolnum></citerefentry> for supported types + and sizes. + ''; + }; + + authorizedKeysFiles = mkOption { + type = types.listOf types.str; + default = []; + description = "Files from which authorized keys are read."; + }; + + authorizedKeysCommand = mkOption { + type = types.str; + default = "none"; + description = '' + Specifies a program to be used to look up the user's public + keys. The program must be owned by root, not writable by group + or others and specified by an absolute path. + ''; + }; + + authorizedKeysCommandUser = mkOption { + type = types.str; + default = "nobody"; + description = '' + Specifies the user under whose account the AuthorizedKeysCommand + is run. It is recommended to use a dedicated user that has no + other role on the host than running authorized keys commands. + ''; + }; + + kexAlgorithms = mkOption { + type = types.listOf types.str; + default = [ + "curve25519-sha256@libssh.org" + "diffie-hellman-group-exchange-sha256" + ]; + description = '' + Allowed key exchange algorithms + </para> + <para> + Defaults to recommended settings from both + <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" /> + and + <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" /> + ''; + }; + + ciphers = mkOption { + type = types.listOf types.str; + default = [ + "chacha20-poly1305@openssh.com" + "aes256-gcm@openssh.com" + "aes128-gcm@openssh.com" + "aes256-ctr" + "aes192-ctr" + "aes128-ctr" + ]; + description = '' + Allowed ciphers + </para> + <para> + Defaults to recommended settings from both + <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" /> + and + <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" /> + ''; + }; + + macs = mkOption { + type = types.listOf types.str; + default = [ + "hmac-sha2-512-etm@openssh.com" + "hmac-sha2-256-etm@openssh.com" + "umac-128-etm@openssh.com" + "hmac-sha2-512" + "hmac-sha2-256" + "umac-128@openssh.com" + ]; + description = '' + Allowed MACs + </para> + <para> + Defaults to recommended settings from both + <link xlink:href="https://stribika.github.io/2015/01/04/secure-secure-shell.html" /> + and + <link xlink:href="https://wiki.mozilla.org/Security/Guidelines/OpenSSH#Modern_.28OpenSSH_6.7.2B.29" /> + ''; + }; + + logLevel = mkOption { + type = types.enum [ "QUIET" "FATAL" "ERROR" "INFO" "VERBOSE" "DEBUG" "DEBUG1" "DEBUG2" "DEBUG3" ]; + default = "VERBOSE"; + description = '' + Gives the verbosity level that is used when logging messages from sshd(8). The possible values are: + QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is VERBOSE. DEBUG and DEBUG1 + are equivalent. DEBUG2 and DEBUG3 each specify higher levels of debugging output. Logging with a DEBUG level + violates the privacy of users and is not recommended. + + LogLevel VERBOSE logs user's key fingerprint on login. + Needed to have a clear audit track of which key was used to log in. + ''; + }; + + useDns = mkOption { + type = types.bool; + default = false; + description = '' + Specifies whether sshd(8) should look up the remote host name, and to check that the resolved host name for + the remote IP address maps back to the very same IP address. + If this option is set to no (the default) then only addresses and not host names may be used in + ~/.ssh/authorized_keys from and sshd_config Match Host directives. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Verbatim contents of <filename>sshd_config</filename>."; + }; + + moduliFile = mkOption { + example = "/etc/my-local-ssh-moduli;"; + type = types.path; + description = '' + Path to <literal>moduli</literal> file to install in + <literal>/etc/ssh/moduli</literal>. If this option is unset, then + the <literal>moduli</literal> file shipped with OpenSSH will be used. + ''; + }; + + strictModes = mkOption { + type = types.bool; + default = true; + description = '' + Whether sshd should check file modes and ownership of directories + ''; + }; + }; + + users.users = mkOption { + type = with types; loaOf (submodule userOptions); + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.users = { + sshd = { + isSystemUser = true; + description = "SSH privilege separation user"; + }; + } // (optionalAttrs (cfg.authorizedKeysCommand != null) { + ${cfg.authorizedKeysCommandUser} = {}; + }); + + services.openssh.moduliFile = mkDefault "${cfgc.package}/etc/ssh/moduli"; + + environment.etc = authKeysFiles // + { "ssh/moduli".source = cfg.moduliFile; + "ssh/sshd_config".source = sshconf; + }; + + systemd = + let + service = + { description = "SSH Daemon"; + wantedBy = optional (!cfg.startWhenNeeded) "multi-user.target"; + after = [ "network.target" ]; + stopIfChanged = false; + path = [ cfgc.package pkgs.gawk ]; + environment.LD_LIBRARY_PATH = nssModulesPath; + + restartTriggers = optionals (!cfg.startWhenNeeded) [ + config.environment.etc."ssh/sshd_config".source + ]; + + preStart = + '' + # Make sure we don't write to stdout, since in case of + # socket activation, it goes to the remote side (#19589). + exec >&2 + + mkdir -m 0755 -p /etc/ssh + + ${flip concatMapStrings cfg.hostKeys (k: '' + if ! [ -f "${k.path}" ]; then + ssh-keygen \ + -t "${k.type}" \ + ${if k ? bits then "-b ${toString k.bits}" else ""} \ + ${if k ? rounds then "-a ${toString k.rounds}" else ""} \ + ${if k ? comment then "-C '${k.comment}'" else ""} \ + ${if k ? openSSHFormat && k.openSSHFormat then "-o" else ""} \ + -f "${k.path}" \ + -N "" + fi + '')} + ''; + + serviceConfig = + { ExecStart = + (optionalString cfg.startWhenNeeded "-") + + "${cfgc.package}/bin/sshd " + (optionalString cfg.startWhenNeeded "-i ") + + "-f /etc/ssh/sshd_config"; + KillMode = "process"; + } // (if cfg.startWhenNeeded then { + StandardInput = "socket"; + StandardError = "journal"; + } else { + Restart = "always"; + Type = "simple"; + }); + + }; + in + + if cfg.startWhenNeeded then { + + sockets.sshd = + { description = "SSH Socket"; + wantedBy = [ "sockets.target" ]; + socketConfig.ListenStream = if cfg.listenAddresses != [] then + map (l: "${l.addr}:${toString (if l.port != null then l.port else 22)}") cfg.listenAddresses + else + cfg.ports; + socketConfig.Accept = true; + }; + + services."sshd@" = service; + + } else { + + services.sshd = service; + + }; + + networking.firewall.allowedTCPPorts = if cfg.openFirewall then cfg.ports else []; + + security.pam.services.sshd = + { startSession = true; + showMotd = true; + unixAuth = cfg.passwordAuthentication; + }; + + # These values are merged with the ones defined externally, see: + # https://github.com/NixOS/nixpkgs/pull/10155 + # https://github.com/NixOS/nixpkgs/pull/41745 + services.openssh.authorizedKeysFiles = + [ ".ssh/authorized_keys" ".ssh/authorized_keys2" "/etc/ssh/authorized_keys.d/%u" ]; + + services.openssh.extraConfig = mkOrder 0 + '' + UsePAM yes + + AddressFamily ${if config.networking.enableIPv6 then "any" else "inet"} + ${concatMapStrings (port: '' + Port ${toString port} + '') cfg.ports} + + ${concatMapStrings ({ port, addr, ... }: '' + ListenAddress ${addr}${if port != null then ":" + toString port else ""} + '') cfg.listenAddresses} + + ${optionalString cfgc.setXAuthLocation '' + XAuthLocation ${pkgs.xorg.xauth}/bin/xauth + ''} + + ${if cfg.forwardX11 then '' + X11Forwarding yes + '' else '' + X11Forwarding no + ''} + + ${optionalString cfg.allowSFTP '' + Subsystem sftp ${cfgc.package}/libexec/sftp-server ${concatStringsSep " " cfg.sftpFlags} + ''} + + PermitRootLogin ${cfg.permitRootLogin} + GatewayPorts ${cfg.gatewayPorts} + PasswordAuthentication ${if cfg.passwordAuthentication then "yes" else "no"} + ChallengeResponseAuthentication ${if cfg.challengeResponseAuthentication then "yes" else "no"} + + PrintMotd no # handled by pam_motd + + ${optionalString (cfg.authorizedKeysCommand != null) '' + AuthorizedKeysCommand ${cfg.authorizedKeysCommand} + AuthorizedKeysCommandUser ${cfg.authorizedKeysCommandUser} + ''} + AuthorizedKeysFile ${toString cfg.authorizedKeysFiles} + ${optionalString (cfg.authorizedKeysCommand != "none") '' + AuthorizedKeysCommand ${cfg.authorizedKeysCommand} + AuthorizedKeysCommandUser ${cfg.authorizedKeysCommandUser} + ''} + + ${flip concatMapStrings cfg.hostKeys (k: '' + HostKey ${k.path} + '')} + + KexAlgorithms ${concatStringsSep "," cfg.kexAlgorithms} + Ciphers ${concatStringsSep "," cfg.ciphers} + MACs ${concatStringsSep "," cfg.macs} + + LogLevel ${cfg.logLevel} + + ${if cfg.useDns then '' + UseDNS yes + '' else '' + UseDNS no + ''} + + StrictModes ${if cfg.strictModes then "yes" else "no"} + + ''; + + assertions = [{ assertion = if cfg.forwardX11 then cfgc.setXAuthLocation else true; + message = "cannot enable X11 forwarding without setting xauth location";}] + ++ forEach cfg.listenAddresses ({ addr, ... }: { + assertion = addr != null; + message = "addr must be specified in each listenAddresses entry"; + }); + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/sslh.nix b/nixpkgs/nixos/modules/services/networking/sslh.nix new file mode 100644 index 000000000000..c4fa370a5fef --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/sslh.nix @@ -0,0 +1,160 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.sslh; + user = "sslh"; + configFile = pkgs.writeText "sslh.conf" '' + verbose: ${boolToString cfg.verbose}; + foreground: true; + inetd: false; + numeric: false; + transparent: ${boolToString cfg.transparent}; + timeout: "${toString cfg.timeout}"; + + listen: + ( + { host: "${cfg.listenAddress}"; port: "${toString cfg.port}"; } + ); + + ${cfg.appendConfig} + ''; + defaultAppendConfig = '' + protocols: + ( + { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, + { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; }, + { name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; }, + { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, + { name: "ssl"; host: "localhost"; port: "443"; probe: "builtin"; }, + { name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; } + ); + ''; +in +{ + options = { + services.sslh = { + enable = mkEnableOption "sslh"; + + verbose = mkOption { + type = types.bool; + default = false; + description = "Verbose logs."; + }; + + timeout = mkOption { + type = types.int; + default = 2; + description = "Timeout in seconds."; + }; + + transparent = mkOption { + type = types.bool; + default = false; + description = "Will the services behind sslh (Apache, sshd and so on) see the external IP and ports as if the external world connected directly to them"; + }; + + listenAddress = mkOption { + type = types.str; + default = "0.0.0.0"; + description = "Listening address or hostname."; + }; + + port = mkOption { + type = types.int; + default = 443; + description = "Listening port."; + }; + + appendConfig = mkOption { + type = types.str; + default = defaultAppendConfig; + description = "Verbatim configuration file."; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.enable { + systemd.services.sslh = { + description = "Applicative Protocol Multiplexer (e.g. share SSH and HTTPS on the same port)"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + DynamicUser = true; + User = "sslh"; + PermissionsStartOnly = true; + Restart = "always"; + RestartSec = "1s"; + ExecStart = "${pkgs.sslh}/bin/sslh -F${configFile}"; + KillMode = "process"; + AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_SETGID CAP_SETUID"; + PrivateTmp = true; + PrivateDevices = true; + ProtectSystem = "full"; + ProtectHome = true; + }; + }; + }) + + # code from https://github.com/yrutschle/sslh#transparent-proxy-support + # the only difference is using iptables mark 0x2 instead of 0x1 to avoid conflicts with nixos/nat module + (mkIf (cfg.enable && cfg.transparent) { + # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination + boot.kernel.sysctl."net.ipv4.conf.default.route_localnet" = 1; + boot.kernel.sysctl."net.ipv4.conf.all.route_localnet" = 1; + + systemd.services.sslh = let + iptablesCommands = [ + # DROP martian packets as they would have been if route_localnet was zero + # Note: packets not leaving the server aren't affected by this, thus sslh will still work + { table = "raw"; command = "PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP"; } + { table = "mangle"; command = "POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP"; } + # Mark all connections made by ssl for special treatment (here sslh is run as user ${user}) + { table = "nat"; command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; } + # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark) + { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; } + ]; + ip6tablesCommands = [ + { table = "raw"; command = "PREROUTING ! -i lo -d ::1/128 -j DROP"; } + { table = "mangle"; command = "POSTROUTING ! -o lo -s ::1/128 -j DROP"; } + { table = "nat"; command = "OUTPUT -m owner --uid-owner ${user} -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x02/0x0f"; } + { table = "mangle"; command = "OUTPUT ! -o lo -p tcp -m connmark --mark 0x02/0x0f -j CONNMARK --restore-mark --mask 0x0f"; } + ]; + in { + path = [ pkgs.iptables pkgs.iproute pkgs.procps ]; + + preStart = '' + # Cleanup old iptables entries which might be still there + ${concatMapStringsSep "\n" ({table, command}: "while iptables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") iptablesCommands} + ${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -A ${command}" ) iptablesCommands} + + # Configure routing for those marked packets + ip rule add fwmark 0x2 lookup 100 + ip route add local 0.0.0.0/0 dev lo table 100 + + '' + optionalString config.networking.enableIPv6 '' + ${concatMapStringsSep "\n" ({table, command}: "while ip6tables -w -t ${table} -D ${command} 2>/dev/null; do echo; done") ip6tablesCommands} + ${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -A ${command}" ) ip6tablesCommands} + + ip -6 rule add fwmark 0x2 lookup 100 + ip -6 route add local ::/0 dev lo table 100 + ''; + + postStop = '' + ${concatMapStringsSep "\n" ({table, command}: "iptables -w -t ${table} -D ${command}") iptablesCommands} + + ip rule del fwmark 0x2 lookup 100 + ip route del local 0.0.0.0/0 dev lo table 100 + '' + optionalString config.networking.enableIPv6 '' + ${concatMapStringsSep "\n" ({table, command}: "ip6tables -w -t ${table} -D ${command}") ip6tablesCommands} + + ip -6 rule del fwmark 0x2 lookup 100 + ip -6 route del local ::/0 dev lo table 100 + ''; + }; + }) + ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix new file mode 100644 index 000000000000..0fec3ef00ad9 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix @@ -0,0 +1,84 @@ +{ config, lib, pkgs, ... }: + +with lib; +with (import ./param-lib.nix lib); + +let + cfg = config.services.strongswan-swanctl; + swanctlParams = import ./swanctl-params.nix lib; +in { + options.services.strongswan-swanctl = { + enable = mkEnableOption "strongswan-swanctl service"; + + package = mkOption { + type = types.package; + default = pkgs.strongswan; + defaultText = "pkgs.strongswan"; + description = '' + The strongswan derivation to use. + ''; + }; + + strongswan.extraConfig = mkOption { + type = types.str; + default = ""; + description = '' + Contents of the <literal>strongswan.conf</literal> file. + ''; + }; + + swanctl = paramsToOptions swanctlParams; + }; + + config = mkIf cfg.enable { + + assertions = [ + { assertion = !config.services.strongswan.enable; + message = "cannot enable both services.strongswan and services.strongswan-swanctl. Choose either one."; + } + ]; + + environment.etc."swanctl/swanctl.conf".text = + paramsToConf cfg.swanctl swanctlParams; + + # The swanctl command complains when the following directories don't exist: + # See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctldirectory + system.activationScripts.strongswan-swanctl-etc = stringAfter ["etc"] '' + mkdir -p '/etc/swanctl/x509' # Trusted X.509 end entity certificates + mkdir -p '/etc/swanctl/x509ca' # Trusted X.509 Certificate Authority certificates + mkdir -p '/etc/swanctl/x509ocsp' + mkdir -p '/etc/swanctl/x509aa' # Trusted X.509 Attribute Authority certificates + mkdir -p '/etc/swanctl/x509ac' # Attribute Certificates + mkdir -p '/etc/swanctl/x509crl' # Certificate Revocation Lists + mkdir -p '/etc/swanctl/pubkey' # Raw public keys + mkdir -p '/etc/swanctl/private' # Private keys in any format + mkdir -p '/etc/swanctl/rsa' # PKCS#1 encoded RSA private keys + mkdir -p '/etc/swanctl/ecdsa' # Plain ECDSA private keys + mkdir -p '/etc/swanctl/bliss' + mkdir -p '/etc/swanctl/pkcs8' # PKCS#8 encoded private keys of any type + mkdir -p '/etc/swanctl/pkcs12' # PKCS#12 containers + ''; + + systemd.services.strongswan-swanctl = { + description = "strongSwan IPsec IKEv1/IKEv2 daemon using swanctl"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + path = with pkgs; [ kmod iproute iptables utillinux ]; + environment = { + STRONGSWAN_CONF = pkgs.writeTextFile { + name = "strongswan.conf"; + text = cfg.strongswan.extraConfig; + }; + SWANCTL_DIR = "/etc/swanctl"; + }; + restartTriggers = [ config.environment.etc."swanctl/swanctl.conf".source ]; + serviceConfig = { + ExecStart = "${cfg.package}/sbin/charon-systemd"; + Type = "notify"; + ExecStartPost = "${cfg.package}/sbin/swanctl --load-all --noprompt"; + ExecReload = "${cfg.package}/sbin/swanctl --reload"; + Restart = "on-abnormal"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix new file mode 100644 index 000000000000..dfdfc50d8ae2 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix @@ -0,0 +1,162 @@ +# In the following context a parameter is an attribute set that +# contains a NixOS option and a render function. It also contains the +# attribute: '_type = "param"' so we can distinguish it from other +# sets. +# +# The render function is used to convert the value of the option to a +# snippet of strongswan.conf. Most parameters simply render their +# value to a string. For example, take the following parameter: +# +# threads = mkIntParam 10 "Threads to use for request handling."; +# +# When a users defines the corresponding option as for example: +# +# services.strongswan-swanctl.strongswan.threads = 32; +# +# It will get rendered to the following snippet in strongswan.conf: +# +# threads = 32 +# +# Some parameters however need to be able to change the attribute +# name. For example, take the following parameter: +# +# id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") "..."; +# +# A user can define the corresponding option as for example: +# +# id = { +# "foo" = "bar"; +# "baz" = "qux"; +# }; +# +# This will get rendered to the following snippet: +# +# foo-id = bar +# baz-id = qux +# +# For this reason the render function is not simply a function from +# value -> string but a function from a value to an attribute set: +# { "${name}" = string }. This allows parameters to change the attribute +# name like in the previous example. + +lib : + +with lib; +with (import ./param-lib.nix lib); + +rec { + mkParamOfType = type : strongswanDefault : description : { + _type = "param"; + option = mkOption { + type = types.nullOr type; + default = null; + description = documentDefault description strongswanDefault; + }; + render = single toString; + }; + + documentDefault = description : strongswanDefault : + if strongswanDefault == null + then description + else description + '' + </para><para> + StrongSwan default: <literal><![CDATA[${builtins.toJSON strongswanDefault}]]></literal> + ''; + + single = f: name: value: { ${name} = f value; }; + + mkStrParam = mkParamOfType types.str; + mkOptionalStrParam = mkStrParam null; + + mkEnumParam = values : mkParamOfType (types.enum values); + + mkIntParam = mkParamOfType types.int; + mkOptionalIntParam = mkIntParam null; + + # We should have floats in Nix... + mkFloatParam = mkStrParam; + + # TODO: Check for hex format: + mkHexParam = mkStrParam; + mkOptionalHexParam = mkOptionalStrParam; + + # TODO: Check for duration format: + mkDurationParam = mkStrParam; + mkOptionalDurationParam = mkOptionalStrParam; + + mkYesNoParam = strongswanDefault : description : { + _type = "param"; + option = mkOption { + type = types.nullOr types.bool; + default = null; + description = documentDefault description strongswanDefault; + }; + render = single (b: if b then "yes" else "no"); + }; + yes = true; + no = false; + + mkSpaceSepListParam = mkSepListParam " "; + mkCommaSepListParam = mkSepListParam ","; + + mkSepListParam = sep : strongswanDefault : description : { + _type = "param"; + option = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + description = documentDefault description strongswanDefault; + }; + render = single (value: concatStringsSep sep value); + }; + + mkAttrsOfParams = params : + mkAttrsOf params (types.submodule {options = paramsToOptions params;}); + + mkAttrsOfParam = param : + mkAttrsOf param param.option.type; + + mkAttrsOf = param : option : description : { + _type = "param"; + option = mkOption { + type = types.attrsOf option; + default = {}; + inherit description; + }; + render = single (attrs: + (paramsToRenderedStrings attrs + (mapAttrs (_n: _v: param) attrs))); + }; + + mkPrefixedAttrsOfParams = params : + mkPrefixedAttrsOf params (types.submodule {options = paramsToOptions params;}); + + mkPrefixedAttrsOfParam = param : + mkPrefixedAttrsOf param param.option.type; + + mkPrefixedAttrsOf = p : option : description : { + _type = "param"; + option = mkOption { + type = types.attrsOf option; + default = {}; + inherit description; + }; + render = prefix: attrs: + let prefixedAttrs = mapAttrs' (name: nameValuePair "${prefix}-${name}") attrs; + in paramsToRenderedStrings prefixedAttrs + (mapAttrs (_n: _v: p) prefixedAttrs); + }; + + mkPostfixedAttrsOfParams = params : description : { + _type = "param"; + option = mkOption { + type = types.attrsOf (types.submodule {options = paramsToOptions params;}); + default = {}; + inherit description; + }; + render = postfix: attrs: + let postfixedAttrs = mapAttrs' (name: nameValuePair "${name}-${postfix}") attrs; + in paramsToRenderedStrings postfixedAttrs + (mapAttrs (_n: _v: params) postfixedAttrs); + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix new file mode 100644 index 000000000000..2bbb39a76049 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix @@ -0,0 +1,82 @@ +lib : + +with lib; + +rec { + paramsToConf = cfg : ps : mkConf 0 (paramsToRenderedStrings cfg ps); + + # mkConf takes an indentation level (which usually starts at 0) and a nested + # attribute set of strings and will render that set to a strongswan.conf style + # configuration format. For example: + # + # mkConf 0 {a = "1"; b = { c = { "foo" = "2"; "bar" = "3"; }; d = "4";};} => '' + # a = 1 + # b { + # c { + # foo = 2 + # bar = 3 + # } + # d = 4 + # }'' + mkConf = indent : ps : + concatMapStringsSep "\n" + (name: + let value = ps.${name}; + indentation = replicate indent " "; + in + indentation + ( + if isAttrs value + then "${name} {\n" + + mkConf (indent + 2) value + "\n" + + indentation + "}" + else "${name} = ${value}" + ) + ) + (attrNames ps); + + replicate = n : c : concatStrings (builtins.genList (_x : c) n); + + # `paramsToRenderedStrings cfg ps` converts the NixOS configuration `cfg` + # (typically the "config" argument of a NixOS module) and the set of + # parameters `ps` (an attribute set where the values are constructed using the + # parameter constructors in ./param-constructors.nix) to a nested attribute + # set of strings (rendered parameters). + paramsToRenderedStrings = cfg : ps : + filterEmptySets ( + (mapParamsRecursive (path: name: param: + let value = attrByPath path null cfg; + in optionalAttrs (value != null) (param.render name value) + ) ps)); + + filterEmptySets = set : filterAttrs (n: v: (v != null)) (mapAttrs (name: value: + if isAttrs value + then let value' = filterEmptySets value; + in if value' == {} + then null + else value' + else value + ) set); + + # Recursively map over every parameter in the given attribute set. + mapParamsRecursive = mapAttrsRecursiveCond' (as: (!(as ? _type && as._type == "param"))); + + mapAttrsRecursiveCond' = cond: f: set: + let + recurse = path: set: + let + g = + name: value: + if isAttrs value && cond value + then { ${name} = recurse (path ++ [name]) value; } + else f (path ++ [name]) name value; + in mapAttrs'' g set; + in recurse [] set; + + mapAttrs'' = f: set: + foldl' (a: b: a // b) {} (map (attr: f attr set.${attr}) (attrNames set)); + + # Extract the options from the given set of parameters. + paramsToOptions = ps : + mapParamsRecursive (_path: name: param: { ${name} = param.option; }) ps; + +} diff --git a/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix new file mode 100644 index 000000000000..808cb863a9cf --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix @@ -0,0 +1,1300 @@ +# See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctlconf +# +# When strongSwan is upgraded please update the parameters in this file. You can +# see which parameters should be deleted, changed or added by diffing +# swanctl.opt: +# +# git clone https://github.com/strongswan/strongswan.git +# cd strongswan +# git diff 5.7.2..5.8.0 src/swanctl/swanctl.opt + +lib: with (import ./param-constructors.nix lib); + +let + certParams = { + file = mkOptionalStrParam '' + Absolute path to the certificate to load. Passed as-is to the daemon, so + it must be readable by it. + </para><para> + Configure either this or <option>handle</option>, but not both, in one section. + ''; + + handle = mkOptionalHexParam '' + Hex-encoded CKA_ID or handle of the certificate on a token or TPM, + respectively. + </para><para> + Configure either this or <option>file</option>, but not both, in one section. + ''; + + slot = mkOptionalIntParam '' + Optional slot number of the token that stores the certificate. + ''; + + module = mkOptionalStrParam '' + Optional PKCS#11 module name. + ''; + }; +in { + authorities = mkAttrsOfParams ({ + + cacert = mkOptionalStrParam '' + The certificates may use a relative path from the swanctl + <literal>x509ca</literal> directory or an absolute path. + </para><para> + Configure one of <option>cacert</option>, + <option>file</option>, or + <option>handle</option> per section. + ''; + + cert_uri_base = mkOptionalStrParam '' + Defines the base URI for the Hash and URL feature supported by + IKEv2. Instead of exchanging complete certificates, IKEv2 allows one to + send an URI that resolves to the DER encoded certificate. The certificate + URIs are built by appending the SHA1 hash of the DER encoded certificates + to this base URI. + ''; + + crl_uris = mkCommaSepListParam [] '' + List of CRL distribution points (ldap, http, or file URI). + ''; + + ocsp_uris = mkCommaSepListParam [] '' + List of OCSP URIs. + ''; + + } // certParams) '' + Section defining complementary attributes of certification authorities, each + in its own subsection with an arbitrary yet unique name + ''; + + connections = mkAttrsOfParams { + + version = mkIntParam 0 '' + IKE major version to use for connection. + <itemizedlist> + <listitem><para>1 uses IKEv1 aka ISAKMP,</para></listitem> + <listitem><para>2 uses IKEv2.</para></listitem> + <listitem><para>A connection using the default of 0 accepts both IKEv1 and IKEv2 as + responder, and initiates the connection actively with IKEv2.</para></listitem> + </itemizedlist> + ''; + + local_addrs = mkCommaSepListParam [] '' + Local address(es) to use for IKE communication. Takes + single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges. + </para><para> + As initiator, the first non-range/non-subnet is used to initiate the + connection from. As responder, the local destination address must match at + least to one of the specified addresses, subnets or ranges. + </para><para> + If FQDNs are assigned they are resolved every time a configuration lookup + is done. If DNS resolution times out, the lookup is delayed for that time. + ''; + + remote_addrs = mkCommaSepListParam [] '' + Remote address(es) to use for IKE communication. Takes + single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges. + </para><para> + As initiator, the first non-range/non-subnet is used to initiate the + connection to. As responder, the initiator source address must match at + least to one of the specified addresses, subnets or ranges. + </para><para> + If FQDNs are assigned they are resolved every time a configuration lookup + is done. If DNS resolution times out, the lookup is delayed for that time. + To initiate a connection, at least one specific address or DNS name must + be specified. + ''; + + local_port = mkIntParam 500 '' + Local UDP port for IKE communication. By default the port of the socket + backend is used, which is usually <literal>500</literal>. If port + <literal>500</literal> is used, automatic IKE port floating to port + <literal>4500</literal> is used to work around NAT issues. + </para><para> + Using a non-default local IKE port requires support from the socket + backend in use (socket-dynamic). + ''; + + remote_port = mkIntParam 500 '' + Remote UDP port for IKE communication. If the default of port + <literal>500</literal> is used, automatic IKE port floating to port + <literal>4500</literal> is used to work around NAT issues. + ''; + + proposals = mkCommaSepListParam ["default"] '' + A proposal is a set of algorithms. For non-AEAD algorithms, this includes + for IKE an encryption algorithm, an integrity algorithm, a pseudo random + function and a Diffie-Hellman group. For AEAD algorithms, instead of + encryption and integrity algorithms, a combined algorithm is used. + </para><para> + In IKEv2, multiple algorithms of the same kind can be specified in a + single proposal, from which one gets selected. In IKEv1, only one + algorithm per kind is allowed per proposal, more algorithms get implicitly + stripped. Use multiple proposals to offer different algorithms + combinations in IKEv1. + </para><para> + Algorithm keywords get separated using dashes. Multiple proposals may be + specified in a list. The special value <literal>default</literal> forms a + default proposal of supported algorithms considered safe, and is usually a + good choice for interoperability. + ''; + + vips = mkCommaSepListParam [] '' + List of virtual IPs to request in IKEv2 configuration payloads or IKEv1 + Mode Config. The wildcard addresses <literal>0.0.0.0</literal> and + <literal>::</literal> request an arbitrary address, specific addresses may + be defined. The responder may return a different address, though, or none + at all. + ''; + + aggressive = mkYesNoParam no '' + Enables Aggressive Mode instead of Main Mode with Identity + Protection. Aggressive Mode is considered less secure, because the ID and + HASH payloads are exchanged unprotected. This allows a passive attacker to + snoop peer identities, and even worse, start dictionary attacks on the + Preshared Key. + ''; + + pull = mkYesNoParam yes '' + If the default of yes is used, Mode Config works in pull mode, where the + initiator actively requests a virtual IP. With no, push mode is used, + where the responder pushes down a virtual IP to the initiating peer. + </para><para> + Push mode is currently supported for IKEv1, but not in IKEv2. It is used + by a few implementations only, pull mode is recommended. + ''; + + dscp = mkStrParam "000000" '' + Differentiated Services Field Codepoint to set on outgoing IKE packets for + this connection. The value is a six digit binary encoded string specifying + the Codepoint to set, as defined in RFC 2474. + ''; + + encap = mkYesNoParam no '' + To enforce UDP encapsulation of ESP packets, the IKE daemon can fake the + NAT detection payloads. This makes the peer believe that NAT takes place + on the path, forcing it to encapsulate ESP packets in UDP. + </para><para> + Usually this is not required, but it can help to work around connectivity + issues with too restrictive intermediary firewalls. + ''; + + mobike = mkYesNoParam yes '' + Enables MOBIKE on IKEv2 connections. MOBIKE is enabled by default on IKEv2 + connections, and allows mobility of clients and multi-homing on servers by + migrating active IPsec tunnels. + </para><para> + Usually keeping MOBIKE enabled is unproblematic, as it is not used if the + peer does not indicate support for it. However, due to the design of + MOBIKE, IKEv2 always floats to port 4500 starting from the second + exchange. Some implementations don't like this behavior, hence it can be + disabled. + ''; + + dpd_delay = mkDurationParam "0s" '' + Interval to check the liveness of a peer actively using IKEv2 + INFORMATIONAL exchanges or IKEv1 R_U_THERE messages. Active DPD checking + is only enforced if no IKE or ESP/AH packet has been received for the + configured DPD delay. + ''; + + dpd_timeout = mkDurationParam "0s" '' + Charon by default uses the normal retransmission mechanism and timeouts to + check the liveness of a peer, as all messages are used for liveness + checking. For compatibility reasons, with IKEv1 a custom interval may be + specified; this option has no effect on connections using IKEv2. + ''; + + fragmentation = mkEnumParam ["yes" "accept" "force" "no"] "yes" '' + Use IKE fragmentation (proprietary IKEv1 extension or RFC 7383 IKEv2 + fragmentation). Acceptable values are <literal>yes</literal> (the default + since 5.5.1), <literal>accept</literal> (since versions:5.5.3), + <literal>force</literal> and <literal>no</literal>. + <itemizedlist> + <listitem><para>If set to <literal>yes</literal>, and the peer + supports it, oversized IKE messages will be sent in fragments.</para></listitem> + <listitem><para>If set to + <literal>accept</literal>, support for fragmentation is announced to the peer but the daemon + does not send its own messages in fragments.</para></listitem> + <listitem><para>If set to <literal>force</literal> (only + supported for IKEv1) the initial IKE message will already be fragmented if + required.</para></listitem> + <listitem><para>Finally, setting the option to <literal>no</literal> will disable announcing + support for this feature.</para></listitem> + </itemizedlist> + </para><para> + Note that fragmented IKE messages sent by a peer are always processed + irrespective of the value of this option (even when set to no). + ''; + + childless = mkEnumParam [ "allow" "force" "never" ] "allow" '' + Use childless IKE_SA initiation (RFC 6023) for IKEv2. Acceptable values + are <literal>allow</literal> (the default), <literal>force</literal> and + <literal>never</literal>. If set to <literal>allow</literal>, responders + will accept childless IKE_SAs (as indicated via notify in the IKE_SA_INIT + response) while initiators continue to create regular IKE_SAs with the + first CHILD_SA created during IKE_AUTH, unless the IKE_SA is initiated + explicitly without any children (which will fail if the responder does not + support or has disabled this extension). If set to + <literal>force</literal>, only childless initiation is accepted and the + first CHILD_SA is created with a separate CREATE_CHILD_SA exchange + (e.g. to use an independent DH exchange for all CHILD_SAs). Finally, + setting the option to <literal>never</literal> disables support for + childless IKE_SAs as responder. + ''; + + send_certreq = mkYesNoParam yes '' + Send certificate request payloads to offer trusted root CA certificates to + the peer. Certificate requests help the peer to choose an appropriate + certificate/private key for authentication and are enabled by default. + Disabling certificate requests can be useful if too many trusted root CA + certificates are installed, as each certificate request increases the size + of the initial IKE packets. + ''; + + send_cert = mkEnumParam ["always" "never" "ifasked" ] "ifasked" '' + Send certificate payloads when using certificate authentication. + <itemizedlist> + <listitem><para>With the default of <literal>ifasked</literal> the daemon sends + certificate payloads only if certificate requests have been received.</para></listitem> + <listitem><para><literal>never</literal> disables sending of certificate payloads + altogether,</para></listitem> + <listitem><para><literal>always</literal> causes certificate payloads to be sent + unconditionally whenever certificate authentication is used.</para></listitem> + </itemizedlist> + ''; + + ppk_id = mkOptionalStrParam '' + String identifying the Postquantum Preshared Key (PPK) to be used. + ''; + + ppk_required = mkYesNoParam no '' + Whether a Postquantum Preshared Key (PPK) is required for this connection. + ''; + + keyingtries = mkIntParam 1 '' + Number of retransmission sequences to perform during initial + connect. Instead of giving up initiation after the first retransmission + sequence with the default value of <literal>1</literal>, additional + sequences may be started according to the configured value. A value of + <literal>0</literal> initiates a new sequence until the connection + establishes or fails with a permanent error. + ''; + + unique = mkEnumParam ["no" "never" "keep" "replace"] "no" '' + Connection uniqueness policy to enforce. To avoid multiple connections + from the same user, a uniqueness policy can be enforced. + </para><para> + <itemizedlist> + <listitem><para> + The value <literal>never</literal> does never enforce such a policy, even + if a peer included INITIAL_CONTACT notification messages, + </para></listitem> + <listitem><para> + whereas <literal>no</literal> replaces existing connections for the same + identity if a new one has the INITIAL_CONTACT notify. + </para></listitem> + <listitem><para> + <literal>keep</literal> rejects new connection attempts if the same user + already has an active connection, + </para></listitem> + <listitem><para> + <literal>replace</literal> deletes any existing connection if a new one + for the same user gets established. + </para></listitem> + </itemizedlist> + To compare connections for uniqueness, the remote IKE identity is used. If + EAP or XAuth authentication is involved, the EAP-Identity or XAuth + username is used to enforce the uniqueness policy instead. + </para><para> + On initiators this setting specifies whether an INITIAL_CONTACT notify is + sent during IKE_AUTH if no existing connection is found with the remote + peer (determined by the identities of the first authentication + round). Unless set to <literal>never</literal> the client will send a notify. + ''; + + reauth_time = mkDurationParam "0s" '' + Time to schedule IKE reauthentication. IKE reauthentication recreates the + IKE/ISAKMP SA from scratch and re-evaluates the credentials. In asymmetric + configurations (with EAP or configuration payloads) it might not be + possible to actively reauthenticate as responder. The IKEv2 + reauthentication lifetime negotiation can instruct the client to perform + reauthentication. + </para><para> + Reauthentication is disabled by default. Enabling it usually may lead to + small connection interruptions, as strongSwan uses a break-before-make + policy with IKEv2 to avoid any conflicts with associated tunnel resources. + ''; + + rekey_time = mkDurationParam "4h" '' + IKE rekeying refreshes key material using a Diffie-Hellman exchange, but + does not re-check associated credentials. It is supported in IKEv2 only, + IKEv1 performs a reauthentication procedure instead. + </para><para> + With the default value IKE rekeying is scheduled every 4 hours, minus the + configured rand_time. If a reauth_time is configured, rekey_time defaults + to zero, disabling rekeying; explicitly set both to enforce rekeying and + reauthentication. + ''; + + over_time = mkOptionalDurationParam '' + Hard IKE_SA lifetime if rekey/reauth does not complete, as time. To avoid + having an IKE/ISAKMP kept alive if IKE reauthentication or rekeying fails + perpetually, a maximum hard lifetime may be specified. If the IKE_SA fails + to rekey or reauthenticate within the specified time, the IKE_SA gets + closed. + </para><para> + In contrast to CHILD_SA rekeying, over_time is relative in time to the + rekey_time and reauth_time values, as it applies to both. + </para><para> + The default is 10% of the longer of <option>rekey_time</option> and + <option>reauth_time</option>. + ''; + + rand_time = mkOptionalDurationParam '' + Time range from which to choose a random value to subtract from + rekey/reauth times. To avoid having both peers initiating the rekey/reauth + procedure simultaneously, a random time gets subtracted from the + rekey/reauth times. + </para><para> + The default is equal to the configured <option>over_time</option>. + ''; + + pools = mkCommaSepListParam [] '' + List of named IP pools to allocate virtual IP addresses + and other configuration attributes from. Each name references a pool by + name from either the pools section or an external pool. + ''; + + if_id_in = mkStrParam "0" '' + XFRM interface ID set on inbound policies/SA, can be overridden by child + config, see there for details. + ''; + + if_id_out = mkStrParam "0" '' + XFRM interface ID set on outbound policies/SA, can be overridden by child + config, see there for details. + ''; + + mediation = mkYesNoParam no '' + Whether this connection is a mediation connection, that is, whether this + connection is used to mediate other connections using the IKEv2 Mediation + Extension. Mediation connections create no CHILD_SA. + ''; + + mediated_by = mkOptionalStrParam '' + The name of the connection to mediate this connection through. If given, + the connection will be mediated through the named mediation + connection. The mediation connection must have mediation enabled. + ''; + + mediation_peer = mkOptionalStrParam '' + Identity under which the peer is registered at the mediation server, that + is, the IKE identity the other end of this connection uses as its local + identity on its connection to the mediation server. This is the identity + we request the mediation server to mediate us with. Only relevant on + connections that set mediated_by. If it is not given, the remote IKE + identity of the first authentication round of this connection will be + used. + ''; + + local = mkPrefixedAttrsOfParams { + + round = mkIntParam 0 '' + Optional numeric identifier by which authentication rounds are + sorted. If not specified rounds are ordered by their position in the + config file/vici message. + ''; + + certs = mkCommaSepListParam [] '' + List of certificate candidates to use for + authentication. The certificates may use a relative path from the + swanctl <literal>x509</literal> directory or an absolute path. + </para><para> + The certificate used for authentication is selected based on the + received certificate request payloads. If no appropriate CA can be + located, the first certificate is used. + ''; + + cert = mkPostfixedAttrsOfParams certParams '' + Section for a certificate candidate to use for + authentication. Certificates in certs are transmitted as binary blobs, + these sections offer more flexibility. + ''; + + pubkeys = mkCommaSepListParam [] '' + List of raw public key candidates to use for + authentication. The public keys may use a relative path from the swanctl + <literal>pubkey</literal> directory or an absolute path. + </para><para> + Even though multiple local public keys could be defined in principle, + only the first public key in the list is used for authentication. + ''; + + auth = mkStrParam "pubkey" '' + Authentication to perform locally. + <itemizedlist> + <listitem><para> + The default <literal>pubkey</literal> uses public key authentication + using a private key associated to a usable certificate. + </para></listitem> + <listitem><para> + <literal>psk</literal> uses pre-shared key authentication. + </para></listitem> + <listitem><para> + The IKEv1 specific <literal>xauth</literal> is used for XAuth or Hybrid + authentication, + </para></listitem> + <listitem><para> + while the IKEv2 specific <literal>eap</literal> keyword defines EAP + authentication. + </para></listitem> + <listitem><para> + For <literal>xauth</literal>, a specific backend name may be appended, + separated by a dash. The appropriate <literal>xauth</literal> backend is + selected to perform the XAuth exchange. For traditional XAuth, the + <literal>xauth</literal> method is usually defined in the second + authentication round following an initial <literal>pubkey</literal> (or + <literal>psk</literal>) round. Using <literal>xauth</literal> in the + first round performs Hybrid Mode client authentication. + </para></listitem> + <listitem><para> + For <literal>eap</literal>, a specific EAP method name may be appended, separated by a + dash. An EAP module implementing the appropriate method is selected to + perform the EAP conversation. + </para></listitem> + <listitem><para> + Since 5.4.0, if both peers support RFC 7427 ("Signature Authentication + in IKEv2") specific hash algorithms to be used during IKEv2 + authentication may be configured. To do so use <literal>ike:</literal> + followed by a trust chain signature scheme constraint (see description + of the <option>remote</option> section's <option>auth</option> + keyword). For example, with <literal>ike:pubkey-sha384-sha256</literal> + a public key signature scheme with either SHA-384 or SHA-256 would get + used for authentication, in that order and depending on the hash + algorithms supported by the peer. If no specific hash algorithms are + configured, the default is to prefer an algorithm that matches or + exceeds the strength of the signature key. If no constraints with + <literal>ike:</literal> prefix are configured any signature scheme + constraint (without <literal>ike:</literal> prefix) will also apply to + IKEv2 authentication, unless this is disabled in + <literal>strongswan.conf</literal>. To use RSASSA-PSS signatures use + <literal>rsa/pss</literal> instead of <literal>pubkey</literal> or + <literal>rsa</literal> as in e.g. + <literal>ike:rsa/pss-sha256</literal>. If <literal>pubkey</literal> or + <literal>rsa</literal> constraints are configured RSASSA-PSS signatures + will only be used if enabled in <literal>strongswan.conf</literal>(5). + </para></listitem> + </itemizedlist> + ''; + + id = mkOptionalStrParam '' + IKE identity to use for authentication round. When using certificate + authentication, the IKE identity must be contained in the certificate, + either as subject or as subjectAltName. + ''; + + eap_id = mkOptionalStrParam '' + Client EAP-Identity to use in EAP-Identity exchange and the EAP method. + ''; + + aaa_id = mkOptionalStrParam '' + Server side EAP-Identity to expect in the EAP method. Some EAP methods, + such as EAP-TLS, use an identity for the server to perform mutual + authentication. This identity may differ from the IKE identity, + especially when EAP authentication is delegated from the IKE responder + to an AAA backend. + </para><para> + For EAP-(T)TLS, this defines the identity for which the server must + provide a certificate in the TLS exchange. + ''; + + xauth_id = mkOptionalStrParam '' + Client XAuth username used in the XAuth exchange. + ''; + + } '' + Section for a local authentication round. A local authentication round + defines the rules how authentication is performed for the local + peer. Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple + Authentication or IKEv1 XAuth. + </para><para> + Each round is defined in a section having <literal>local</literal> as + prefix, and an optional unique suffix. To define a single authentication + round, the suffix may be omitted. + ''; + + remote = mkPrefixedAttrsOfParams { + + round = mkIntParam 0 '' + Optional numeric identifier by which authentication rounds are + sorted. If not specified rounds are ordered by their position in the + config file/vici message. + ''; + + id = mkStrParam "%any" '' + IKE identity to expect for authentication round. When using certificate + authentication, the IKE identity must be contained in the certificate, + either as subject or as subjectAltName. + ''; + + eap_id = mkOptionalStrParam '' + Identity to use as peer identity during EAP authentication. If set to + <literal>%any</literal> the EAP-Identity method will be used to ask the + client for an EAP identity. + ''; + + groups = mkCommaSepListParam [] '' + Authorization group memberships to require. The peer + must prove membership to at least one of the specified groups. Group + membership can be certified by different means, for example by + appropriate Attribute Certificates or by an AAA backend involved in the + authentication. + ''; + + cert_policy = mkCommaSepListParam [] '' + List of certificate policy OIDs the peer's certificate + must have. OIDs are specified using the numerical dotted representation. + ''; + + certs = mkCommaSepListParam [] '' + List of certificates to accept for authentication. The certificates may + use a relative path from the swanctl <literal>x509</literal> directory + or an absolute path. + ''; + + cert = mkPostfixedAttrsOfParams certParams '' + Section for a certificate candidate to use for + authentication. Certificates in certs are transmitted as binary blobs, + these sections offer more flexibility. + ''; + + cacerts = mkCommaSepListParam [] '' + List of CA certificates to accept for + authentication. The certificates may use a relative path from the + swanctl <literal>x509ca</literal> directory or an absolute path. + ''; + + cacert = mkPostfixedAttrsOfParams certParams '' + Section for a CA certificate to accept for authentication. Certificates + in cacerts are transmitted as binary blobs, these sections offer more + flexibility. + ''; + + pubkeys = mkCommaSepListParam [] '' + List of raw public keys to accept for + authentication. The public keys may use a relative path from the swanctl + <literal>pubkey</literal> directory or an absolute path. + ''; + + revocation = mkEnumParam ["strict" "ifuri" "relaxed"] "relaxed" '' + Certificate revocation policy for CRL or OCSP revocation. + <itemizedlist> + <listitem><para> + A <literal>strict</literal> revocation policy fails if no revocation information is + available, i.e. the certificate is not known to be unrevoked. + </para></listitem> + <listitem><para> + <literal>ifuri</literal> fails only if a CRL/OCSP URI is available, but certificate + revocation checking fails, i.e. there should be revocation information + available, but it could not be obtained. + </para></listitem> + <listitem><para> + The default revocation policy <literal>relaxed</literal> fails only if a certificate is + revoked, i.e. it is explicitly known that it is bad. + </para></listitem> + </itemizedlist> + ''; + + auth = mkStrParam "pubkey" '' + Authentication to expect from remote. See the <option>local</option> + section's <option>auth</option> keyword description about the details of + supported mechanisms. + </para><para> + Since 5.4.0, to require a trustchain public key strength for the remote + side, specify the key type followed by the minimum strength in bits (for + example <literal>ecdsa-384</literal> or + <literal>rsa-2048-ecdsa-256</literal>). To limit the acceptable set of + hashing algorithms for trustchain validation, append hash algorithms to + pubkey or a key strength definition (for example + <literal>pubkey-sha256-sha512</literal>, + <literal>rsa-2048-sha256-sha384-sha512</literal> or + <literal>rsa-2048-sha256-ecdsa-256-sha256-sha384</literal>). + Unless disabled in <literal>strongswan.conf</literal>, or explicit IKEv2 + signature constraints are configured (refer to the description of the + <option>local</option> section's <option>auth</option> keyword for + details), such key types and hash algorithms are also applied as + constraints against IKEv2 signature authentication schemes used by the + remote side. To require RSASSA-PSS signatures use + <literal>rsa/pss</literal> instead of <literal>pubkey</literal> or + <literal>rsa</literal> as in e.g. <literal>rsa/pss-sha256</literal>. If + <literal>pubkey</literal> or <literal>rsa</literal> constraints are + configured RSASSA-PSS signatures will only be accepted if enabled in + <literal>strongswan.conf</literal>(5). + </para><para> + To specify trust chain constraints for EAP-(T)TLS, append a colon to the + EAP method, followed by the key type/size and hash algorithm as + discussed above (e.g. <literal>eap-tls:ecdsa-384-sha384</literal>). + ''; + + } '' + Section for a remote authentication round. A remote authentication round + defines the constraints how the peers must authenticate to use this + connection. Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple + Authentication or IKEv1 XAuth. + </para><para> + Each round is defined in a section having <literal>remote</literal> as + prefix, and an optional unique suffix. To define a single authentication + round, the suffix may be omitted. + ''; + + children = mkAttrsOfParams { + ah_proposals = mkCommaSepListParam [] '' + AH proposals to offer for the CHILD_SA. A proposal is a set of + algorithms. For AH, this includes an integrity algorithm and an optional + Diffie-Hellman group. If a DH group is specified, CHILD_SA/Quick Mode + rekeying and initial negotiation uses a separate Diffie-Hellman exchange + using the specified group (refer to esp_proposals for details). + </para><para> + In IKEv2, multiple algorithms of the same kind can be specified in a + single proposal, from which one gets selected. In IKEv1, only one + algorithm per kind is allowed per proposal, more algorithms get + implicitly stripped. Use multiple proposals to offer different algorithms + combinations in IKEv1. + </para><para> + Algorithm keywords get separated using dashes. Multiple proposals may be + specified in a list. The special value <literal>default</literal> forms + a default proposal of supported algorithms considered safe, and is + usually a good choice for interoperability. By default no AH proposals + are included, instead ESP is proposed. + ''; + + esp_proposals = mkCommaSepListParam ["default"] '' + ESP proposals to offer for the CHILD_SA. A proposal is a set of + algorithms. For ESP non-AEAD proposals, this includes an integrity + algorithm, an encryption algorithm, an optional Diffie-Hellman group and + an optional Extended Sequence Number Mode indicator. For AEAD proposals, + a combined mode algorithm is used instead of the separate + encryption/integrity algorithms. + </para><para> + If a DH group is specified, CHILD_SA/Quick Mode rekeying and initial + negotiation use a separate Diffie-Hellman exchange using the specified + group. However, for IKEv2, the keys of the CHILD_SA created implicitly + with the IKE_SA will always be derived from the IKE_SA's key material. So + any DH group specified here will only apply when the CHILD_SA is later + rekeyed or is created with a separate CREATE_CHILD_SA exchange. A + proposal mismatch might, therefore, not immediately be noticed when the + SA is established, but may later cause rekeying to fail. + </para><para> + Extended Sequence Number support may be indicated with the + <literal>esn</literal> and <literal>noesn</literal> values, both may be + included to indicate support for both modes. If omitted, + <literal>noesn</literal> is assumed. + </para><para> + In IKEv2, multiple algorithms of the same kind can be specified in a + single proposal, from which one gets selected. In IKEv1, only one + algorithm per kind is allowed per proposal, more algorithms get + implicitly stripped. Use multiple proposals to offer different algorithms + combinations in IKEv1. + </para><para> + Algorithm keywords get separated using dashes. Multiple proposals may be + specified as a list. The special value <literal>default</literal> forms + a default proposal of supported algorithms considered safe, and is + usually a good choice for interoperability. If no algorithms are + specified for AH nor ESP, the default set of algorithms for ESP is + included. + ''; + + sha256_96 = mkYesNoParam no '' + HMAC-SHA-256 is used with 128-bit truncation with IPsec. For + compatibility with implementations that incorrectly use 96-bit truncation + this option may be enabled to configure the shorter truncation length in + the kernel. This is not negotiated, so this only works with peers that + use the incorrect truncation length (or have this option enabled). + ''; + + local_ts = mkCommaSepListParam ["dynamic"] '' + List of local traffic selectors to include in CHILD_SA. Each selector is + a CIDR subnet definition, followed by an optional proto/port + selector. The special value <literal>dynamic</literal> may be used + instead of a subnet definition, which gets replaced by the tunnel outer + address or the virtual IP, if negotiated. This is the default. + </para><para> + A protocol/port selector is surrounded by opening and closing square + brackets. Between these brackets, a numeric or getservent(3) protocol + name may be specified. After the optional protocol restriction, an + optional port restriction may be specified, separated by a slash. The + port restriction may be numeric, a getservent(3) service name, or the + special value <literal>opaque</literal> for RFC 4301 OPAQUE + selectors. Port ranges may be specified as well, none of the kernel + backends currently support port ranges, though. + </para><para> + When IKEv1 is used only the first selector is interpreted, except if the + Cisco Unity extension plugin is used. This is due to a limitation of the + IKEv1 protocol, which only allows a single pair of selectors per + CHILD_SA. So to tunnel traffic matched by several pairs of selectors when + using IKEv1 several children (CHILD_SAs) have to be defined that cover + the selectors. The IKE daemon uses traffic selector narrowing for IKEv1, + the same way it is standardized and implemented for IKEv2. However, this + may lead to problems with other implementations. To avoid that, configure + identical selectors in such scenarios. + ''; + + remote_ts = mkCommaSepListParam ["dynamic"] '' + List of remote selectors to include in CHILD_SA. See + <option>local_ts</option> for a description of the selector syntax. + ''; + + rekey_time = mkDurationParam "1h" '' + Time to schedule CHILD_SA rekeying. CHILD_SA rekeying refreshes key + material, optionally using a Diffie-Hellman exchange if a group is + specified in the proposal. To avoid rekey collisions initiated by both + ends simultaneously, a value in the range of <option>rand_time</option> + gets subtracted to form the effective soft lifetime. + </para><para> + By default CHILD_SA rekeying is scheduled every hour, minus + <option>rand_time</option>. + ''; + + life_time = mkOptionalDurationParam '' + Maximum lifetime before CHILD_SA gets closed. Usually this hard lifetime + is never reached, because the CHILD_SA gets rekeyed before. If that fails + for whatever reason, this limit closes the CHILD_SA. The default is 10% + more than the <option>rekey_time</option>. + ''; + + rand_time = mkOptionalDurationParam '' + Time range from which to choose a random value to subtract from + <option>rekey_time</option>. The default is the difference between + <option>life_time</option> and <option>rekey_time</option>. + ''; + + rekey_bytes = mkIntParam 0 '' + Number of bytes processed before initiating CHILD_SA rekeying. CHILD_SA + rekeying refreshes key material, optionally using a Diffie-Hellman + exchange if a group is specified in the proposal. + </para><para> + To avoid rekey collisions initiated by both ends simultaneously, a value + in the range of <option>rand_bytes</option> gets subtracted to form the + effective soft volume limit. + </para><para> + Volume based CHILD_SA rekeying is disabled by default. + ''; + + life_bytes = mkOptionalIntParam '' + Maximum bytes processed before CHILD_SA gets closed. Usually this hard + volume limit is never reached, because the CHILD_SA gets rekeyed + before. If that fails for whatever reason, this limit closes the + CHILD_SA. The default is 10% more than <option>rekey_bytes</option>. + ''; + + rand_bytes = mkOptionalIntParam '' + Byte range from which to choose a random value to subtract from + <option>rekey_bytes</option>. The default is the difference between + <option>life_bytes</option> and <option>rekey_bytes</option>. + ''; + + rekey_packets = mkIntParam 0 '' + Number of packets processed before initiating CHILD_SA rekeying. CHILD_SA + rekeying refreshes key material, optionally using a Diffie-Hellman + exchange if a group is specified in the proposal. + </para><para> + To avoid rekey collisions initiated by both ends simultaneously, a value + in the range of <option>rand_packets</option> gets subtracted to form + the effective soft packet count limit. + </para><para> + Packet count based CHILD_SA rekeying is disabled by default. + ''; + + life_packets = mkOptionalIntParam '' + Maximum number of packets processed before CHILD_SA gets closed. Usually + this hard packets limit is never reached, because the CHILD_SA gets + rekeyed before. If that fails for whatever reason, this limit closes the + CHILD_SA. + </para><para> + The default is 10% more than <option>rekey_bytes</option>. + ''; + + rand_packets = mkOptionalIntParam '' + Packet range from which to choose a random value to subtract from + <option>rekey_packets</option>. The default is the difference between + <option>life_packets</option> and <option>rekey_packets</option>. + ''; + + updown = mkOptionalStrParam '' + Updown script to invoke on CHILD_SA up and down events. + ''; + + hostaccess = mkYesNoParam no '' + Hostaccess variable to pass to <literal>updown</literal> script. + ''; + + mode = mkEnumParam [ "tunnel" + "transport" + "transport_proxy" + "beet" + "pass" + "drop" + ] "tunnel" '' + IPsec Mode to establish CHILD_SA with. + <itemizedlist> + <listitem><para> + <literal>tunnel</literal> negotiates the CHILD_SA in IPsec Tunnel Mode, + </para></listitem> + <listitem><para> + whereas <literal>transport</literal> uses IPsec Transport Mode. + </para></listitem> + <listitem><para> + <literal>transport_proxy</literal> signifying the special Mobile IPv6 + Transport Proxy Mode. + </para></listitem> + <listitem><para> + <literal>beet</literal> is the Bound End to End Tunnel mixture mode, + working with fixed inner addresses without the need to include them in + each packet. + </para></listitem> + <listitem><para> + Both <literal>transport</literal> and <literal>beet</literal> modes are + subject to mode negotiation; <literal>tunnel</literal> mode is + negotiated if the preferred mode is not available. + </para></listitem> + <listitem><para> + <literal>pass</literal> and <literal>drop</literal> are used to install + shunt policies which explicitly bypass the defined traffic from IPsec + processing or drop it, respectively. + </para></listitem> + </itemizedlist> + ''; + + policies = mkYesNoParam yes '' + Whether to install IPsec policies or not. Disabling this can be useful in + some scenarios e.g. MIPv6, where policies are not managed by the IKE + daemon. Since 5.3.3. + ''; + + policies_fwd_out = mkYesNoParam no '' + Whether to install outbound FWD IPsec policies or not. Enabling this is + required in case there is a drop policy that would match and block + forwarded traffic for this CHILD_SA. Since 5.5.1. + ''; + + dpd_action = mkEnumParam ["clear" "trap" "restart"] "clear" '' + Action to perform for this CHILD_SA on DPD timeout. The default clear + closes the CHILD_SA and does not take further action. trap installs a + trap policy, which will catch matching traffic and tries to re-negotiate + the tunnel on-demand. restart immediately tries to re-negotiate the + CHILD_SA under a fresh IKE_SA. + ''; + + ipcomp = mkYesNoParam no '' + Enable IPComp compression before encryption. If enabled, IKE tries to + negotiate IPComp compression to compress ESP payload data prior to + encryption. + ''; + + inactivity = mkDurationParam "0s" '' + Timeout before closing CHILD_SA after inactivity. If no traffic has been + processed in either direction for the configured timeout, the CHILD_SA + gets closed due to inactivity. The default value of 0 disables inactivity + checks. + ''; + + reqid = mkIntParam 0 '' + Fixed reqid to use for this CHILD_SA. This might be helpful in some + scenarios, but works only if each CHILD_SA configuration is instantiated + not more than once. The default of 0 uses dynamic reqids, allocated + incrementally. + ''; + + priority = mkIntParam 0 '' + Optional fixed priority for IPsec policies. This could be useful to + install high-priority drop policies. The default of 0 uses dynamically + calculated priorities based on the size of the traffic selectors. + ''; + + interface = mkOptionalStrParam '' + Optional interface name to restrict outbound IPsec policies. + ''; + + mark_in = mkStrParam "0/0x00000000" '' + Netfilter mark and mask for input traffic. On Linux, Netfilter may + require marks on each packet to match an SA/policy having that option + set. This allows installing duplicate policies and enables Netfilter + rules to select specific SAs/policies for incoming traffic. Note that + inbound marks are only set on policies, by default, unless + <option>mark_in_sa</option> is enabled. The special value + <literal>%unique</literal> sets a unique mark on each CHILD_SA instance, + beyond that the value <literal>%unique-dir</literal> assigns a different + unique mark for each + </para><para> + An additional mask may be appended to the mark, separated by + <literal>/</literal>. The default mask if omitted is + <literal>0xffffffff</literal>. + ''; + + mark_in_sa = mkYesNoParam no '' + Whether to set <option>mark_in</option> on the inbound SA. By default, + the inbound mark is only set on the inbound policy. The tuple destination + address, protocol and SPI is unique and the mark is not required to find + the correct SA, allowing to mark traffic after decryption instead (where + more specific selectors may be used) to match different policies. Marking + packets before decryption is still possible, even if no mark is set on + the SA. + ''; + + mark_out = mkStrParam "0/0x00000000" '' + Netfilter mark and mask for output traffic. On Linux, Netfilter may + require marks on each packet to match a policy/SA having that option + set. This allows installing duplicate policies and enables Netfilter + rules to select specific policies/SAs for outgoing traffic. The special + value <literal>%unique</literal> sets a unique mark on each CHILD_SA + instance, beyond that the value <literal>%unique-dir</literal> assigns a + different unique mark for each CHILD_SA direction (in/out). + </para><para> + An additional mask may be appended to the mark, separated by + <literal>/</literal>. The default mask if omitted is + <literal>0xffffffff</literal>. + ''; + + set_mark_in = mkStrParam "0/0x00000000" '' + Netfilter mark applied to packets after the inbound IPsec SA processed + them. This way it's not necessary to mark packets via Netfilter before + decryption or right afterwards to match policies or process them + differently (e.g. via policy routing). + + An additional mask may be appended to the mark, separated by + <literal>/</literal>. The default mask if omitted is 0xffffffff. The + special value <literal>%same</literal> uses the value (but not the mask) + from <option>mark_in</option> as mark value, which can be fixed, + <literal>%unique</literal> or <literal>%unique-dir</literal>. + + Setting marks in XFRM input requires Linux 4.19 or higher. + ''; + + set_mark_out = mkStrParam "0/0x00000000" '' + Netfilter mark applied to packets after the outbound IPsec SA processed + them. This allows processing ESP packets differently than the original + traffic (e.g. via policy routing). + + An additional mask may be appended to the mark, separated by + <literal>/</literal>. The default mask if omitted is 0xffffffff. The + special value <literal>%same</literal> uses the value (but not the mask) + from <option>mark_out</option> as mark value, which can be fixed, + <literal>%unique_</literal> or <literal>%unique-dir</literal>. + + Setting marks in XFRM output is supported since Linux 4.14. Setting a + mask requires at least Linux 4.19. + ''; + + if_id_in = mkStrParam "0" '' + XFRM interface ID set on inbound policies/SA. This allows installing + duplicate policies/SAs and associates them with an interface with the + same ID. The special value <literal>%unique</literal> sets a unique + interface ID on each CHILD_SA instance, beyond that the value + <literal>%unique-dir</literal> assigns a different unique interface ID + for each CHILD_SA direction (in/out). + ''; + + if_id_out = mkStrParam "0" '' + XFRM interface ID set on outbound policies/SA. This allows installing + duplicate policies/SAs and associates them with an interface with the + same ID. The special value <literal>%unique</literal> sets a unique + interface ID on each CHILD_SA instance, beyond that the value + <literal>%unique-dir</literal> assigns a different unique interface ID + for each CHILD_SA direction (in/out). + + The daemon will not install routes for CHILD_SAs that have this option set. + ''; + + tfc_padding = mkParamOfType (with lib.types; either int (enum ["mtu"])) 0 '' + Pads ESP packets with additional data to have a consistent ESP packet + size for improved Traffic Flow Confidentiality. The padding defines the + minimum size of all ESP packets sent. The default value of + <literal>0</literal> disables TFC padding, the special value + <literal>mtu</literal> adds TFC padding to create a packet size equal to + the Path Maximum Transfer Unit. + ''; + + replay_window = mkIntParam 32 '' + IPsec replay window to configure for this CHILD_SA. Larger values than + the default of <literal>32</literal> are supported using the Netlink + backend only, a value of <literal>0</literal> disables IPsec replay + protection. + ''; + + hw_offload = mkEnumParam ["yes" "no" "auto"] "no" '' + Enable hardware offload for this CHILD_SA, if supported by the IPsec + implementation. The value <literal>yes</literal> enforces offloading + and the installation will fail if it's not supported by either kernel or + device. The value <literal>auto</literal> enables offloading, if it's + supported, but the installation does not fail otherwise. + ''; + + copy_df = mkYesNoParam yes '' + Whether to copy the DF bit to the outer IPv4 header in tunnel mode. This + effectively disables Path MTU discovery (PMTUD). Controlling this + behavior is not supported by all kernel interfaces. + ''; + + copy_ecn = mkYesNoParam yes '' + Whether to copy the ECN (Explicit Congestion Notification) header field + to/from the outer IP header in tunnel mode. Controlling this behavior is + not supported by all kernel interfaces. + ''; + + copy_dscp = mkEnumParam [ "out" "in" "yes" "no" ] "out" '' + Whether to copy the DSCP (Differentiated Services Field Codepoint) + header field to/from the outer IP header in tunnel mode. The value + <literal>out</literal> only copies the field from the inner to the outer + header, the value <literal>in</literal> does the opposite and only + copies the field from the outer to the inner header when decapsulating, + the value <literal>yes</literal> copies the field in both directions, + and the value <literal>no</literal> disables copying the field + altogether. Setting this to <literal>yes</literal> or + <literal>in</literal> could allow an attacker to adversely affect other + traffic at the receiver, which is why the default is + <literal>out</literal>. Controlling this behavior is not supported by + all kernel interfaces. + ''; + + start_action = mkEnumParam ["none" "trap" "start"] "none" '' + Action to perform after loading the configuration. + <itemizedlist> + <listitem><para> + The default of <literal>none</literal> loads the connection only, which + then can be manually initiated or used as a responder configuration. + </para></listitem> + <listitem><para> + The value <literal>trap</literal> installs a trap policy, which triggers + the tunnel as soon as matching traffic has been detected. + </para></listitem> + <listitem><para> + The value <literal>start</literal> initiates the connection actively. + </para></listitem> + </itemizedlist> + When unloading or replacing a CHILD_SA configuration having a + <option>start_action</option> different from <literal>none</literal>, + the inverse action is performed. Configurations with + <literal>start</literal> get closed, while such with + <literal>trap</literal> get uninstalled. + ''; + + close_action = mkEnumParam ["none" "trap" "start"] "none" '' + Action to perform after a CHILD_SA gets closed by the peer. + <itemizedlist> + <listitem><para> + The default of <literal>none</literal> does not take any action, + </para></listitem> + <listitem><para> + <literal>trap</literal> installs a trap policy for the CHILD_SA. + </para></listitem> + <listitem><para> + <literal>start</literal> tries to re-create the CHILD_SA. + </para></listitem> + </itemizedlist> + </para><para> + <option>close_action</option> does not provide any guarantee that the + CHILD_SA is kept alive. It acts on explicit close messages only, but not + on negotiation failures. Use trap policies to reliably re-create failed + CHILD_SAs. + ''; + + } '' + CHILD_SA configuration sub-section. Each connection definition may have + one or more sections in its <option>children</option> subsection. The + section name defines the name of the CHILD_SA configuration, which must be + unique within the connection (denoted <child> below). + ''; + } '' + Section defining IKE connection configurations, each in its own subsection + with an arbitrary yet unique name + ''; + + secrets = let + mkEapXauthParams = mkPrefixedAttrsOfParams { + secret = mkOptionalStrParam '' + Value of the EAP/XAuth secret. It may either be an ASCII string, a hex + encoded string if it has a 0x prefix or a Base64 encoded string if it + has a 0s prefix in its value. + ''; + + id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") '' + Identity the EAP/XAuth secret belongs to. Multiple unique identities may + be specified, each having an <literal>id</literal> prefix, if a secret + is shared between multiple users. + ''; + + } '' + EAP secret section for a specific secret. Each EAP secret is defined in a + unique section having the <literal>eap</literal> prefix. EAP secrets are + used for XAuth authentication as well. + ''; + + in { + + eap = mkEapXauthParams; + xauth = mkEapXauthParams; + + ntlm = mkPrefixedAttrsOfParams { + secret = mkOptionalStrParam '' + Value of the NTLM secret, which is the NT Hash of the actual secret, + that is, MD4(UTF-16LE(secret)). The resulting 16-byte value may either + be given as a hex encoded string with a 0x prefix or as a Base64 encoded + string with a 0s prefix. + ''; + + id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") '' + Identity the NTLM secret belongs to. Multiple unique identities may be + specified, each having an id prefix, if a secret is shared between + multiple users. + ''; + } '' + NTLM secret section for a specific secret. Each NTLM secret is defined in + a unique section having the <literal>ntlm</literal> prefix. NTLM secrets + may only be used for EAP-MSCHAPv2 authentication. + ''; + + ike = mkPrefixedAttrsOfParams { + secret = mkOptionalStrParam '' + Value of the IKE preshared secret. It may either be an ASCII string, a + hex encoded string if it has a 0x prefix or a Base64 encoded string if + it has a 0s prefix in its value. + ''; + + id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") '' + IKE identity the IKE preshared secret belongs to. Multiple unique + identities may be specified, each having an <literal>id</literal> + prefix, if a secret is shared between multiple peers. + ''; + } '' + IKE preshared secret section for a specific secret. Each IKE PSK is + defined in a unique section having the <literal>ike</literal> prefix. + ''; + + ppk = mkPrefixedAttrsOfParams { + secret = mkOptionalStrParam '' + Value of the PPK. It may either be an ASCII string, a hex encoded string + if it has a <literal>0x</literal> prefix or a Base64 encoded string if + it has a <literal>0s</literal> prefix in its value. Should have at least + 256 bits of entropy for 128-bit security. + ''; + + id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") '' + PPK identity the PPK belongs to. Multiple unique identities may be + specified, each having an <literal>id</literal> prefix, if a secret is + shared between multiple peers. + ''; + } '' + Postquantum Preshared Key (PPK) section for a specific secret. Each PPK is + defined in a unique section having the <literal>ppk</literal> prefix. + ''; + + private = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the private folder for which this passphrase should be used. + ''; + + secret = mkOptionalStrParam '' + Value of decryption passphrase for private key. + ''; + } '' + Private key decryption passphrase for a key in the + <literal>private</literal> folder. + ''; + + rsa = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the <literal>rsa</literal> folder for which this passphrase + should be used. + ''; + secret = mkOptionalStrParam '' + Value of decryption passphrase for RSA key. + ''; + } '' + Private key decryption passphrase for a key in the <literal>rsa</literal> + folder. + ''; + + ecdsa = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the <literal>ecdsa</literal> folder for which this + passphrase should be used. + ''; + secret = mkOptionalStrParam '' + Value of decryption passphrase for ECDSA key. + ''; + } '' + Private key decryption passphrase for a key in the + <literal>ecdsa</literal> folder. + ''; + + pkcs8 = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the <literal>pkcs8</literal> folder for which this + passphrase should be used. + ''; + secret = mkOptionalStrParam '' + Value of decryption passphrase for PKCS#8 key. + ''; + } '' + Private key decryption passphrase for a key in the + <literal>pkcs8</literal> folder. + ''; + + pkcs12 = mkPrefixedAttrsOfParams { + file = mkOptionalStrParam '' + File name in the <literal>pkcs12</literal> folder for which this + passphrase should be used. + ''; + secret = mkOptionalStrParam '' + Value of decryption passphrase for PKCS#12 container. + ''; + } '' + PKCS#12 decryption passphrase for a container in the + <literal>pkcs12</literal> folder. + ''; + + token = mkPrefixedAttrsOfParams { + handle = mkOptionalHexParam '' + Hex-encoded CKA_ID or handle of the private key on the token or TPM, + respectively. + ''; + + slot = mkOptionalIntParam '' + Optional slot number to access the token. + ''; + + module = mkOptionalStrParam '' + Optional PKCS#11 module name to access the token. + ''; + + pin = mkOptionalStrParam '' + Optional PIN required to access the key on the token. If none is + provided the user is prompted during an interactive + <literal>--load-creds</literal> call. + ''; + } ''Definition for a private key that's stored on a token/smartcard/TPM.''; + + }; + + pools = mkAttrsOfParams { + addrs = mkOptionalStrParam '' + Subnet or range defining addresses allocated in pool. Accepts a single + CIDR subnet defining the pool to allocate addresses from or an address + range (<from>-<to>). Pools must be unique and non-overlapping. + ''; + + dns = mkCommaSepListParam [] "Address or CIDR subnets"; + nbns = mkCommaSepListParam [] "Address or CIDR subnets"; + dhcp = mkCommaSepListParam [] "Address or CIDR subnets"; + netmask = mkCommaSepListParam [] "Address or CIDR subnets"; + server = mkCommaSepListParam [] "Address or CIDR subnets"; + subnet = mkCommaSepListParam [] "Address or CIDR subnets"; + split_include = mkCommaSepListParam [] "Address or CIDR subnets"; + split_exclude = mkCommaSepListParam [] "Address or CIDR subnets"; + } '' + Section defining named pools. Named pools may be referenced by connections + with the pools option to assign virtual IPs and other configuration + attributes. Each pool must have a unique name (denoted <name> below). + ''; +} diff --git a/nixpkgs/nixos/modules/services/networking/strongswan.nix b/nixpkgs/nixos/modules/services/networking/strongswan.nix new file mode 100644 index 000000000000..13a1a897c5ed --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/strongswan.nix @@ -0,0 +1,170 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (builtins) toFile; + inherit (lib) concatMapStringsSep concatStringsSep mapAttrsToList + mkIf mkEnableOption mkOption types literalExample; + + cfg = config.services.strongswan; + + ipsecSecrets = secrets: toFile "ipsec.secrets" ( + concatMapStringsSep "\n" (f: "include ${f}") secrets + ); + + ipsecConf = {setup, connections, ca}: + let + # https://wiki.strongswan.org/projects/strongswan/wiki/IpsecConf + makeSections = type: sections: concatStringsSep "\n\n" ( + mapAttrsToList (sec: attrs: + "${type} ${sec}\n" + + (concatStringsSep "\n" ( mapAttrsToList (k: v: " ${k}=${v}") attrs )) + ) sections + ); + setupConf = makeSections "config" { inherit setup; }; + connectionsConf = makeSections "conn" connections; + caConf = makeSections "ca" ca; + + in + builtins.toFile "ipsec.conf" '' + ${setupConf} + ${connectionsConf} + ${caConf} + ''; + + strongswanConf = {setup, connections, ca, secretsFile, managePlugins, enabledPlugins}: toFile "strongswan.conf" '' + charon { + ${if managePlugins then "load_modular = no" else ""} + ${if managePlugins then ("load = " + (concatStringsSep " " enabledPlugins)) else ""} + plugins { + stroke { + secrets_file = ${secretsFile} + } + } + } + + starter { + config_file = ${ipsecConf { inherit setup connections ca; }} + } + ''; + +in +{ + options.services.strongswan = { + enable = mkEnableOption "strongSwan"; + + secrets = mkOption { + type = types.listOf types.str; + default = []; + example = [ "/run/keys/ipsec-foo.secret" ]; + description = '' + A list of paths to IPSec secret files. These + files will be included into the main ipsec.secrets file with + the <literal>include</literal> directive. It is safer if these + paths are absolute. + ''; + }; + + setup = mkOption { + type = types.attrsOf types.str; + default = {}; + example = { cachecrls = "yes"; strictcrlpolicy = "yes"; }; + description = '' + A set of options for the ‘config setup’ section of the + <filename>ipsec.conf</filename> file. Defines general + configuration parameters. + ''; + }; + + connections = mkOption { + type = types.attrsOf (types.attrsOf types.str); + default = {}; + example = literalExample '' + { + "%default" = { + keyexchange = "ikev2"; + keyingtries = "1"; + }; + roadwarrior = { + auto = "add"; + leftcert = "/run/keys/moonCert.pem"; + leftid = "@moon.strongswan.org"; + leftsubnet = "10.1.0.0/16"; + right = "%any"; + }; + } + ''; + description = '' + A set of connections and their options for the ‘conn xxx’ + sections of the <filename>ipsec.conf</filename> file. + ''; + }; + + ca = mkOption { + type = types.attrsOf (types.attrsOf types.str); + default = {}; + example = { + strongswan = { + auto = "add"; + cacert = "/run/keys/strongswanCert.pem"; + crluri = "http://crl2.strongswan.org/strongswan.crl"; + }; + }; + description = '' + A set of CAs (certification authorities) and their options for + the ‘ca xxx’ sections of the <filename>ipsec.conf</filename> + file. + ''; + }; + + managePlugins = mkOption { + type = types.bool; + default = false; + description = '' + If set to true, this option will disable automatic plugin loading and + then tell strongSwan to enable the plugins specified in the + <option>enabledPlugins</option> option. + ''; + }; + + enabledPlugins = mkOption { + type = types.listOf types.str; + default = []; + description = '' + A list of additional plugins to enable if + <option>managePlugins</option> is true. + ''; + }; + }; + + + config = with cfg; + let + secretsFile = ipsecSecrets cfg.secrets; + in + mkIf enable + { + + # here we should use the default strongswan ipsec.secrets and + # append to it (default one is empty so not a pb for now) + environment.etc."ipsec.secrets".source = secretsFile; + + systemd.services.strongswan = { + description = "strongSwan IPSec Service"; + wantedBy = [ "multi-user.target" ]; + path = with pkgs; [ kmod iproute iptables utillinux ]; # XXX Linux + after = [ "network-online.target" ]; + environment = { + STRONGSWAN_CONF = strongswanConf { inherit setup connections ca secretsFile managePlugins enabledPlugins; }; + }; + serviceConfig = { + ExecStart = "${pkgs.strongswan}/sbin/ipsec start --nofork"; + }; + preStart = '' + # with 'nopeerdns' setting, ppp writes into this folder + mkdir -m 700 -p /etc/ppp + ''; + }; + }; +} + diff --git a/nixpkgs/nixos/modules/services/networking/stubby.nix b/nixpkgs/nixos/modules/services/networking/stubby.nix new file mode 100644 index 000000000000..c5e0f929a126 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/stubby.nix @@ -0,0 +1,217 @@ +{ config, lib, pkgs, ...}: + +with lib; + +let + cfg = config.services.stubby; + + fallbacks = concatMapStringsSep "\n " (x: "- ${x}") cfg.fallbackProtocols; + listeners = concatMapStringsSep "\n " (x: "- ${x}") cfg.listenAddresses; + + # By default, the recursive resolvers maintained by the getdns + # project itself are enabled. More information about both getdns's servers, + # as well as third party options for upstream resolvers, can be found here: + # https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Test+Servers + # + # You can override these values by supplying a yaml-formatted array of your + # preferred upstream resolvers in the following format: + # + # 106 # - address_data: IPv4 or IPv6 address of the upstream + # port: Port for UDP/TCP (default is 53) + # tls_auth_name: Authentication domain name checked against the server + # certificate + # tls_pubkey_pinset: An SPKI pinset verified against the keys in the server + # certificate + # - digest: Only "sha256" is currently supported + # value: Base64 encoded value of the sha256 fingerprint of the public + # key + # tls_port: Port for TLS (default is 853) + + defaultUpstream = '' + - address_data: 145.100.185.15 + tls_auth_name: "dnsovertls.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4= + - address_data: 145.100.185.16 + tls_auth_name: "dnsovertls1.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA= + - address_data: 185.49.141.37 + tls_auth_name: "getdnsapi.net" + tls_pubkey_pinset: + - digest: "sha256" + value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q= + - address_data: 2001:610:1:40ba:145:100:185:15 + tls_auth_name: "dnsovertls.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: 62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4= + - address_data: 2001:610:1:40ba:145:100:185:16 + tls_auth_name: "dnsovertls1.sinodun.com" + tls_pubkey_pinset: + - digest: "sha256" + value: cE2ecALeE5B+urJhDrJlVFmf38cJLAvqekONvjvpqUA= + - address_data: 2a04:b900:0:100::38 + tls_auth_name: "getdnsapi.net" + tls_pubkey_pinset: + - digest: "sha256" + value: foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9Q= + ''; + + # Resolution type is not changeable here because it is required per the + # stubby documentation: + # + # "resolution_type: Work in stub mode only (not recursive mode) - required for Stubby + # operation." + # + # https://dnsprivacy.org/wiki/display/DP/Configuring+Stubby + + confFile = pkgs.writeText "stubby.yml" '' + resolution_type: GETDNS_RESOLUTION_STUB + dns_transport_list: + ${fallbacks} + appdata_dir: "/var/cache/stubby" + tls_authentication: ${cfg.authenticationMode} + tls_query_padding_blocksize: ${toString cfg.queryPaddingBlocksize} + edns_client_subnet_private: ${if cfg.subnetPrivate then "1" else "0"} + idle_timeout: ${toString cfg.idleTimeout} + listen_addresses: + ${listeners} + round_robin_upstreams: ${if cfg.roundRobinUpstreams then "1" else "0"} + ${cfg.extraConfig} + upstream_recursive_servers: + ${cfg.upstreamServers} + ''; +in + +{ + options = { + services.stubby = { + + enable = mkEnableOption "Stubby DNS resolver"; + + fallbackProtocols = mkOption { + default = [ "GETDNS_TRANSPORT_TLS" ]; + type = with types; listOf (enum [ + "GETDNS_TRANSPORT_TLS" + "GETDNS_TRANSPORT_TCP" + "GETDNS_TRANSPORT_UDP" + ]); + description = '' + Ordered list composed of one or more transport protocols. + Strict mode should only use <literal>GETDNS_TRANSPORT_TLS</literal>. + Other options are <literal>GETDNS_TRANSPORT_UDP</literal> and + <literal>GETDNS_TRANSPORT_TCP</literal>. + ''; + }; + + authenticationMode = mkOption { + default = "GETDNS_AUTHENTICATION_REQUIRED"; + type = types.enum [ + "GETDNS_AUTHENTICATION_REQUIRED" + "GETDNS_AUTHENTICATION_NONE" + ]; + description = '' + Selects the Strict or Opportunistic usage profile. + For strict, set to <literal>GETDNS_AUTHENTICATION_REQUIRED</literal>. + for opportunistic, use <literal>GETDNS_AUTHENTICATION_NONE</literal>. + ''; + }; + + queryPaddingBlocksize = mkOption { + default = 128; + type = types.int; + description = '' + EDNS0 option to pad the size of the DNS query to the given blocksize. + ''; + }; + + subnetPrivate = mkOption { + default = true; + type = types.bool; + description = '' + EDNS0 option for ECS client privacy. Default is + <literal>true</literal>. If set, this option prevents the client + subnet from being sent to authoritative nameservers. + ''; + }; + + idleTimeout = mkOption { + default = 10000; + type = types.int; + description = "EDNS0 option for keepalive idle timeout expressed in + milliseconds."; + }; + + listenAddresses = mkOption { + default = [ "127.0.0.1" "0::1" ]; + type = with types; listOf str; + description = '' + Sets the listen address for the stubby daemon. + Uses port 53 by default. + Ise IP@port to specify a different port. + ''; + }; + + roundRobinUpstreams = mkOption { + default = true; + type = types.bool; + description = '' + Instructs stubby to distribute queries across all available name + servers. Default is <literal>true</literal>. Set to + <literal>false</literal> in order to use the first available. + ''; + }; + + upstreamServers = mkOption { + default = defaultUpstream; + type = types.lines; + description = '' + Replace default upstreams. See <citerefentry><refentrytitle>stubby + </refentrytitle><manvolnum>1</manvolnum></citerefentry> for an + example of the entry formatting. In Strict mode, at least one of the + following settings must be supplied for each nameserver: + <literal>tls_auth_name</literal> or + <literal>tls_pubkey_pinset</literal>. + ''; + }; + + debugLogging = mkOption { + default = false; + type = types.bool; + description = "Enable or disable debug level logging."; + }; + + extraConfig = mkOption { + default = ""; + type = types.lines; + description = '' + Add additional configuration options. see <citerefentry> + <refentrytitle>stubby</refentrytitle><manvolnum>1</manvolnum> + </citerefentry>for more options. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.stubby ]; + systemd.services.stubby = { + description = "Stubby local DNS resolver"; + after = [ "network.target" ]; + before = [ "nss-lookup.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "notify"; + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; + ExecStart = "${pkgs.stubby}/bin/stubby -C ${confFile} ${optionalString cfg.debugLogging "-l"}"; + DynamicUser = true; + CacheDirectory = "stubby"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/stunnel.nix b/nixpkgs/nixos/modules/services/networking/stunnel.nix new file mode 100644 index 000000000000..ab51bba2f6ac --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/stunnel.nix @@ -0,0 +1,234 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.stunnel; + yesNo = val: if val then "yes" else "no"; + + verifyChainPathAssert = n: c: { + assertion = c.verifyHostname == null || (c.verifyChain || c.verifyPeer); + message = "stunnel: \"${n}\" client configuration - hostname verification " + + "is not possible without either verifyChain or verifyPeer enabled"; + }; + + serverConfig = { + options = { + accept = mkOption { + type = types.int; + description = "On which port stunnel should listen for incoming TLS connections."; + }; + + connect = mkOption { + type = types.int; + description = "To which port the decrypted connection should be forwarded."; + }; + + cert = mkOption { + type = types.path; + description = "File containing both the private and public keys."; + }; + }; + }; + + clientConfig = { + options = { + accept = mkOption { + type = types.str; + description = "IP:Port on which connections should be accepted."; + }; + + connect = mkOption { + type = types.str; + description = "IP:Port destination to connect to."; + }; + + verifyChain = mkOption { + type = types.bool; + default = true; + description = "Check if the provided certificate has a valid certificate chain (against CAPath)."; + }; + + verifyPeer = mkOption { + type = types.bool; + default = false; + description = "Check if the provided certificate is contained in CAPath."; + }; + + CAPath = mkOption { + type = types.nullOr types.path; + default = null; + description = "Path to a directory containing certificates to validate against."; + }; + + CAFile = mkOption { + type = types.nullOr types.path; + default = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; + description = "Path to a file containing certificates to validate against."; + }; + + verifyHostname = mkOption { + type = with types; nullOr str; + default = null; + description = "If set, stunnel checks if the provided certificate is valid for the given hostname."; + }; + }; + }; + + +in + +{ + + ###### interface + + options = { + + services.stunnel = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Whether to enable the stunnel TLS tunneling service."; + }; + + user = mkOption { + type = with types; nullOr str; + default = "nobody"; + description = "The user under which stunnel runs."; + }; + + group = mkOption { + type = with types; nullOr str; + default = "nogroup"; + description = "The group under which stunnel runs."; + }; + + logLevel = mkOption { + type = types.enum [ "emerg" "alert" "crit" "err" "warning" "notice" "info" "debug" ]; + default = "info"; + description = "Verbosity of stunnel output."; + }; + + fipsMode = mkOption { + type = types.bool; + default = false; + description = "Enable FIPS 140-2 mode required for compliance."; + }; + + enableInsecureSSLv3 = mkOption { + type = types.bool; + default = false; + description = "Enable support for the insecure SSLv3 protocol."; + }; + + + servers = mkOption { + description = "Define the server configuations."; + type = with types; attrsOf (submodule serverConfig); + example = { + fancyWebserver = { + enable = true; + accept = 443; + connect = 8080; + cert = "/path/to/pem/file"; + }; + }; + default = { }; + }; + + clients = mkOption { + description = "Define the client configurations."; + type = with types; attrsOf (submodule clientConfig); + example = { + foobar = { + accept = "0.0.0.0:8080"; + connect = "nixos.org:443"; + verifyChain = false; + }; + }; + default = { }; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + assertions = concatLists [ + (singleton { + assertion = (length (attrValues cfg.servers) != 0) || ((length (attrValues cfg.clients)) != 0); + message = "stunnel: At least one server- or client-configuration has to be present."; + }) + + (mapAttrsToList verifyChainPathAssert cfg.clients) + ]; + + environment.systemPackages = [ pkgs.stunnel ]; + + environment.etc."stunnel.cfg".text = '' + ${ if cfg.user != null then "setuid = ${cfg.user}" else "" } + ${ if cfg.group != null then "setgid = ${cfg.group}" else "" } + + debug = ${cfg.logLevel} + + ${ optionalString cfg.fipsMode "fips = yes" } + ${ optionalString cfg.enableInsecureSSLv3 "options = -NO_SSLv3" } + + ; ----- SERVER CONFIGURATIONS ----- + ${ lib.concatStringsSep "\n" + (lib.mapAttrsToList + (n: v: '' + [${n}] + accept = ${toString v.accept} + connect = ${toString v.connect} + cert = ${v.cert} + + '') + cfg.servers) + } + + ; ----- CLIENT CONFIGURATIONS ----- + ${ lib.concatStringsSep "\n" + (lib.mapAttrsToList + (n: v: '' + [${n}] + client = yes + accept = ${v.accept} + connect = ${v.connect} + verifyChain = ${yesNo v.verifyChain} + verifyPeer = ${yesNo v.verifyPeer} + ${optionalString (v.CAPath != null) "CApath = ${v.CAPath}"} + ${optionalString (v.CAFile != null) "CAFile = ${v.CAFile}"} + ${optionalString (v.verifyHostname != null) "checkHost = ${v.verifyHostname}"} + OCSPaia = yes + + '') + cfg.clients) + } + ''; + + systemd.services.stunnel = { + description = "stunnel TLS tunneling service"; + after = [ "network.target" ]; + wants = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + restartTriggers = [ config.environment.etc."stunnel.cfg".source ]; + serviceConfig = { + ExecStart = "${pkgs.stunnel}/bin/stunnel ${config.environment.etc."stunnel.cfg".source}"; + Type = "forking"; + }; + }; + + meta.maintainers = with maintainers; [ + # Server side + lschuermann + # Client side + das_j + ]; + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/supplicant.nix b/nixpkgs/nixos/modules/services/networking/supplicant.nix new file mode 100644 index 000000000000..b5b9989ce186 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/supplicant.nix @@ -0,0 +1,249 @@ +{ config, lib, utils, pkgs, ... }: + +with lib; + +let + + cfg = config.networking.supplicant; + + # We must escape interfaces due to the systemd interpretation + subsystemDevice = interface: + "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device"; + + serviceName = iface: "supplicant-${if (iface=="WLAN") then "wlan@" else ( + if (iface=="LAN") then "lan@" else ( + if (iface=="DBUS") then "dbus" + else (replaceChars [" "] ["-"] iface)))}"; + + # TODO: Use proper privilege separation for wpa_supplicant + supplicantService = iface: suppl: + let + deps = (if (iface=="WLAN"||iface=="LAN") then ["sys-subsystem-net-devices-%i.device"] else ( + if (iface=="DBUS") then ["dbus.service"] + else (map subsystemDevice (splitString " " iface)))) + ++ optional (suppl.bridge!="") (subsystemDevice suppl.bridge); + + ifaceArg = concatStringsSep " -N " (map (i: "-i${i}") (splitString " " iface)); + driverArg = optionalString (suppl.driver != null) "-D${suppl.driver}"; + bridgeArg = optionalString (suppl.bridge!="") "-b${suppl.bridge}"; + confFileArg = optionalString (suppl.configFile.path!=null) "-c${suppl.configFile.path}"; + extraConfFile = pkgs.writeText "supplicant-extra-conf-${replaceChars [" "] ["-"] iface}" '' + ${optionalString suppl.userControlled.enable "ctrl_interface=DIR=${suppl.userControlled.socketDir} GROUP=${suppl.userControlled.group}"} + ${optionalString suppl.configFile.writable "update_config=1"} + ${suppl.extraConf} + ''; + in + { description = "Supplicant ${iface}${optionalString (iface=="WLAN"||iface=="LAN") " %I"}"; + wantedBy = [ "multi-user.target" ] ++ deps; + wants = [ "network.target" ]; + bindsTo = deps; + after = deps; + before = [ "network.target" ]; + + path = [ pkgs.coreutils ]; + + preStart = '' + ${optionalString (suppl.configFile.path!=null) '' + touch -a ${suppl.configFile.path} + chmod 600 ${suppl.configFile.path} + ''} + ${optionalString suppl.userControlled.enable '' + if ! test -e ${suppl.userControlled.socketDir}; then + mkdir -m 0770 -p ${suppl.userControlled.socketDir} + chgrp ${suppl.userControlled.group} ${suppl.userControlled.socketDir} + fi + + if test "$(stat --printf '%G' ${suppl.userControlled.socketDir})" != "${suppl.userControlled.group}"; then + echo "ERROR: bad ownership on ${suppl.userControlled.socketDir}" >&2 + exit 1 + fi + ''} + ''; + + serviceConfig.ExecStart = "${pkgs.wpa_supplicant}/bin/wpa_supplicant -s ${driverArg} ${confFileArg} -I${extraConfFile} ${bridgeArg} ${suppl.extraCmdArgs} ${if (iface=="WLAN"||iface=="LAN") then "-i%I" else (if (iface=="DBUS") then "-u" else ifaceArg)}"; + + }; + + +in + +{ + + ###### interface + + options = { + + networking.supplicant = mkOption { + type = with types; attrsOf (submodule { + options = { + + configFile = { + + path = mkOption { + type = types.nullOr types.path; + default = null; + example = literalExample "/etc/wpa_supplicant.conf"; + description = '' + External <literal>wpa_supplicant.conf</literal> configuration file. + The configuration options defined declaratively within <literal>networking.supplicant</literal> have + precedence over options defined in <literal>configFile</literal>. + ''; + }; + + writable = mkOption { + type = types.bool; + default = false; + description = '' + Whether the configuration file at <literal>configFile.path</literal> should be written to by + <literal>wpa_supplicant</literal>. + ''; + }; + + }; + + extraConf = mkOption { + type = types.lines; + default = ""; + example = '' + ap_scan=1 + device_name=My-NixOS-Device + device_type=1-0050F204-1 + driver_param=use_p2p_group_interface=1 + disable_scan_offload=1 + p2p_listen_reg_class=81 + p2p_listen_channel=1 + p2p_oper_reg_class=81 + p2p_oper_channel=1 + manufacturer=NixOS + model_name=NixOS_Unstable + model_number=2015 + ''; + description = '' + Configuration options for <literal>wpa_supplicant.conf</literal>. + Options defined here have precedence over options in <literal>configFile</literal>. + NOTE: Do not write sensitive data into <literal>extraConf</literal> as it will + be world-readable in the <literal>nix-store</literal>. For sensitive information + use the <literal>configFile</literal> instead. + ''; + }; + + extraCmdArgs = mkOption { + type = types.str; + default = ""; + example = "-e/run/wpa_supplicant/entropy.bin"; + description = + "Command line arguments to add when executing <literal>wpa_supplicant</literal>."; + }; + + driver = mkOption { + type = types.nullOr types.str; + default = "nl80211,wext"; + description = "Force a specific wpa_supplicant driver."; + }; + + bridge = mkOption { + type = types.str; + default = ""; + description = "Name of the bridge interface that wpa_supplicant should listen at."; + }; + + userControlled = { + + enable = mkOption { + type = types.bool; + default = false; + description = '' + Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli. + This is useful for laptop users that switch networks a lot and don't want + to depend on a large package such as NetworkManager just to pick nearby + access points. + ''; + }; + + socketDir = mkOption { + type = types.str; + default = "/run/wpa_supplicant"; + description = "Directory of sockets for controlling wpa_supplicant."; + }; + + group = mkOption { + type = types.str; + default = "wheel"; + example = "network"; + description = "Members of this group can control wpa_supplicant."; + }; + + }; + }; + }); + + default = { }; + + example = literalExample '' + { "wlan0 wlan1" = { + configFile.path = "/etc/wpa_supplicant.conf"; + userControlled.group = "network"; + extraConf = ''' + ap_scan=1 + p2p_disabled=1 + '''; + extraCmdArgs = "-u -W"; + bridge = "br0"; + }; + } + ''; + + description = '' + Interfaces for which to start <command>wpa_supplicant</command>. + The supplicant is used to scan for and associate with wireless networks, + or to authenticate with 802.1x capable network switches. + + The value of this option is an attribute set. Each attribute configures a + <command>wpa_supplicant</command> service, where the attribute name specifies + the name of the interface that <command>wpa_supplicant</command> operates on. + The attribute name can be a space separated list of interfaces. + The attribute names <literal>WLAN</literal>, <literal>LAN</literal> and <literal>DBUS</literal> + have a special meaning. <literal>WLAN</literal> and <literal>LAN</literal> are + configurations for universal <command>wpa_supplicant</command> service that is + started for each WLAN interface or for each LAN interface, respectively. + <literal>DBUS</literal> defines a device-unrelated <command>wpa_supplicant</command> + service that can be accessed through <literal>D-Bus</literal>. + ''; + + }; + + }; + + + ###### implementation + + config = mkIf (cfg != {}) { + + environment.systemPackages = [ pkgs.wpa_supplicant ]; + + services.dbus.packages = [ pkgs.wpa_supplicant ]; + + systemd.services = mapAttrs' (n: v: nameValuePair (serviceName n) (supplicantService n v)) cfg; + + services.udev.packages = [ + (pkgs.writeTextFile { + name = "99-zzz-60-supplicant.rules"; + destination = "/etc/udev/rules.d/99-zzz-60-supplicant.rules"; + text = '' + ${flip (concatMapStringsSep "\n") (filter (n: n!="WLAN" && n!="LAN" && n!="DBUS") (attrNames cfg)) (iface: + flip (concatMapStringsSep "\n") (splitString " " iface) (i: '' + ACTION=="add", SUBSYSTEM=="net", ENV{INTERFACE}=="${i}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="supplicant-${replaceChars [" "] ["-"] iface}.service", TAG+="SUPPLICANT_ASSIGNED"''))} + + ${optionalString (hasAttr "WLAN" cfg) '' + ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-wlan@$result.service" + ''} + ${optionalString (hasAttr "LAN" cfg) '' + ACTION=="add", SUBSYSTEM=="net", ENV{DEVTYPE}=="lan", TAG!="SUPPLICANT_ASSIGNED", TAG+="systemd", PROGRAM="${pkgs.systemd}/bin/systemd-escape -p %E{INTERFACE}", ENV{SYSTEMD_WANTS}+="supplicant-lan@$result.service" + ''} + ''; + })]; + + }; + +} + diff --git a/nixpkgs/nixos/modules/services/networking/supybot.nix b/nixpkgs/nixos/modules/services/networking/supybot.nix new file mode 100644 index 000000000000..dc9fb31ffd0b --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/supybot.nix @@ -0,0 +1,161 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.supybot; + isStateDirHome = hasPrefix "/home/" cfg.stateDir; + isStateDirVar = cfg.stateDir == "/var/lib/supybot"; + pyEnv = pkgs.python3.withPackages (p: [ p.limnoria ] ++ (cfg.extraPackages p)); +in +{ + options = { + + services.supybot = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Enable Supybot, an IRC bot (also known as Limnoria)."; + }; + + stateDir = mkOption { + type = types.path; + default = if versionAtLeast config.system.stateVersion "20.09" + then "/var/lib/supybot" + else "/home/supybot"; + defaultText = "/var/lib/supybot"; + description = "The root directory, logs and plugins are stored here"; + }; + + configFile = mkOption { + type = types.path; + description = '' + Path to initial supybot config file. This can be generated by + running supybot-wizard. + + Note: all paths should include the full path to the stateDir + directory (backup conf data logs logs/plugins plugins tmp web). + ''; + }; + + plugins = mkOption { + type = types.attrsOf types.path; + default = {}; + description = '' + Attribute set of additional plugins that will be symlinked to the + <filename>plugin</filename> subdirectory. + + Please note that you still need to add the plugins to the config + file (or with <literal>!load</literal>) using their attribute name. + ''; + example = literalExample '' + let + plugins = pkgs.fetchzip { + url = "https://github.com/ProgVal/Supybot-plugins/archive/57c2450c.zip"; + sha256 = "077snf84ibnva3sbpzdfpfma6hcdw7dflwnhg6pw7mgnf0nd84qd"; + }; + in + { + Wikipedia = "''${plugins}/Wikipedia"; + Decide = ./supy-decide; + } + ''; + }; + + extraPackages = mkOption { + default = p: []; + description = '' + Extra Python packages available to supybot plugins. The + value must be a function which receives the attrset defined + in <varname>python3Packages</varname> as the sole argument. + ''; + example = literalExample ''p: [ p.lxml p.requests ]''; + }; + + }; + + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.python3Packages.limnoria ]; + + users.users.supybot = { + uid = config.ids.uids.supybot; + group = "supybot"; + description = "Supybot IRC bot user"; + home = cfg.stateDir; + isSystemUser = true; + }; + + users.groups.supybot = { + gid = config.ids.gids.supybot; + }; + + systemd.services.supybot = { + description = "Supybot, an IRC bot"; + documentation = [ "https://limnoria.readthedocs.io/" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + # This needs to be created afresh every time + rm -f '${cfg.stateDir}/supybot.cfg.bak' + ''; + + serviceConfig = { + ExecStart = "${pyEnv}/bin/supybot ${cfg.stateDir}/supybot.cfg"; + PIDFile = "/run/supybot.pid"; + User = "supybot"; + Group = "supybot"; + UMask = "0007"; + Restart = "on-abort"; + StartLimitInterval = "5m"; + StartLimitBurst = "1"; + + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + RestrictNamespaces = true; + RestrictRealtime = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RemoveIPC = true; + ProtectHostname = true; + CapabilityBoundingSet = ""; + ProtectSystem = "full"; + } + // optionalAttrs isStateDirVar { + StateDirectory = "supybot"; + ProtectSystem = "strict"; + } + // optionalAttrs (!isStateDirHome) { + ProtectHome = true; + }; + }; + + systemd.tmpfiles.rules = [ + "d '${cfg.stateDir}' 0700 supybot supybot - -" + "d '${cfg.stateDir}/backup' 0750 supybot supybot - -" + "d '${cfg.stateDir}/conf' 0750 supybot supybot - -" + "d '${cfg.stateDir}/data' 0750 supybot supybot - -" + "d '${cfg.stateDir}/plugins' 0750 supybot supybot - -" + "d '${cfg.stateDir}/logs' 0750 supybot supybot - -" + "d '${cfg.stateDir}/logs/plugins' 0750 supybot supybot - -" + "d '${cfg.stateDir}/tmp' 0750 supybot supybot - -" + "d '${cfg.stateDir}/web' 0750 supybot supybot - -" + "L '${cfg.stateDir}/supybot.cfg' - - - - ${cfg.configFile}" + ] + ++ (flip mapAttrsToList cfg.plugins (name: dest: + "L+ '${cfg.stateDir}/plugins/${name}' - - - - ${dest}" + )); + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/syncplay.nix b/nixpkgs/nixos/modules/services/networking/syncplay.nix new file mode 100644 index 000000000000..e3147c10502c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/syncplay.nix @@ -0,0 +1,80 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.syncplay; + + cmdArgs = + [ "--port" cfg.port ] + ++ optionals (cfg.salt != null) [ "--salt" cfg.salt ] + ++ optionals (cfg.certDir != null) [ "--tls" cfg.certDir ]; + +in +{ + options = { + services.syncplay = { + enable = mkOption { + type = types.bool; + default = false; + description = "If enabled, start the Syncplay server."; + }; + + port = mkOption { + type = types.int; + default = 8999; + description = '' + TCP port to bind to. + ''; + }; + + salt = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Salt to allow room operator passwords generated by this server + instance to still work when the server is restarted. + ''; + }; + + certDir = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + TLS certificates directory to use for encryption. See + <link xlink:href="https://github.com/Syncplay/syncplay/wiki/TLS-support"/>. + ''; + }; + + user = mkOption { + type = types.str; + default = "nobody"; + description = '' + User to use when running Syncplay. + ''; + }; + + group = mkOption { + type = types.str; + default = "nogroup"; + description = '' + Group to use when running Syncplay. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services.syncplay = { + description = "Syncplay Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target "]; + + serviceConfig = { + ExecStart = "${pkgs.syncplay}/bin/syncplay-server ${escapeShellArgs cmdArgs}"; + User = cfg.user; + Group = cfg.group; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/syncthing-relay.nix b/nixpkgs/nixos/modules/services/networking/syncthing-relay.nix new file mode 100644 index 000000000000..f5ca63e78930 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/syncthing-relay.nix @@ -0,0 +1,121 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.syncthing.relay; + + dataDirectory = "/var/lib/syncthing-relay"; + + relayOptions = + [ + "--keys=${dataDirectory}" + "--listen=${cfg.listenAddress}:${toString cfg.port}" + "--status-srv=${cfg.statusListenAddress}:${toString cfg.statusPort}" + "--provided-by=${escapeShellArg cfg.providedBy}" + ] + ++ optional (cfg.pools != null) "--pools=${escapeShellArg (concatStringsSep "," cfg.pools)}" + ++ optional (cfg.globalRateBps != null) "--global-rate=${toString cfg.globalRateBps}" + ++ optional (cfg.perSessionRateBps != null) "--per-session-rate=${toString cfg.perSessionRateBps}" + ++ cfg.extraOptions; +in { + ###### interface + + options.services.syncthing.relay = { + enable = mkEnableOption "Syncthing relay service"; + + listenAddress = mkOption { + type = types.str; + default = ""; + example = "1.2.3.4"; + description = '' + Address to listen on for relay traffic. + ''; + }; + + port = mkOption { + type = types.port; + default = 22067; + description = '' + Port to listen on for relay traffic. This port should be added to + <literal>networking.firewall.allowedTCPPorts</literal>. + ''; + }; + + statusListenAddress = mkOption { + type = types.str; + default = ""; + example = "1.2.3.4"; + description = '' + Address to listen on for serving the relay status API. + ''; + }; + + statusPort = mkOption { + type = types.port; + default = 22070; + description = '' + Port to listen on for serving the relay status API. This port should be + added to <literal>networking.firewall.allowedTCPPorts</literal>. + ''; + }; + + pools = mkOption { + type = types.nullOr (types.listOf types.str); + default = null; + description = '' + Relay pools to join. If null, uses the default global pool. + ''; + }; + + providedBy = mkOption { + type = types.str; + default = ""; + description = '' + Human-readable description of the provider of the relay (you). + ''; + }; + + globalRateBps = mkOption { + type = types.nullOr types.ints.positive; + default = null; + description = '' + Global bandwidth rate limit in bytes per second. + ''; + }; + + perSessionRateBps = mkOption { + type = types.nullOr types.ints.positive; + default = null; + description = '' + Per session bandwidth rate limit in bytes per second. + ''; + }; + + extraOptions = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra command line arguments to pass to strelaysrv. + ''; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + systemd.services.syncthing-relay = { + description = "Syncthing relay service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + DynamicUser = true; + StateDirectory = baseNameOf dataDirectory; + + Restart = "on-failure"; + ExecStart = "${pkgs.syncthing-relay}/bin/strelaysrv ${concatStringsSep " " relayOptions}"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/syncthing.nix b/nixpkgs/nixos/modules/services/networking/syncthing.nix new file mode 100644 index 000000000000..e717d78feed5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix @@ -0,0 +1,528 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.syncthing; + defaultUser = "syncthing"; + + devices = mapAttrsToList (name: device: { + deviceID = device.id; + inherit (device) name addresses introducer; + }) cfg.declarative.devices; + + folders = mapAttrsToList ( _: folder: { + inherit (folder) path id label type; + devices = map (device: { deviceId = cfg.declarative.devices.${device}.id; }) folder.devices; + rescanIntervalS = folder.rescanInterval; + fsWatcherEnabled = folder.watch; + fsWatcherDelayS = folder.watchDelay; + ignorePerms = folder.ignorePerms; + versioning = folder.versioning; + }) (filterAttrs ( + _: folder: + folder.enable + ) cfg.declarative.folders); + + # get the api key by parsing the config.xml + getApiKey = pkgs.writers.writeDash "getAPIKey" '' + ${pkgs.libxml2}/bin/xmllint \ + --xpath 'string(configuration/gui/apikey)'\ + ${cfg.configDir}/config.xml + ''; + + updateConfig = pkgs.writers.writeDash "merge-syncthing-config" '' + set -efu + # wait for syncthing port to open + until ${pkgs.curl}/bin/curl -Ss ${cfg.guiAddress} -o /dev/null; do + sleep 1 + done + + API_KEY=$(${getApiKey}) + OLD_CFG=$(${pkgs.curl}/bin/curl -Ss \ + -H "X-API-Key: $API_KEY" \ + ${cfg.guiAddress}/rest/system/config) + + # generate the new config by merging with the nixos config options + NEW_CFG=$(echo "$OLD_CFG" | ${pkgs.jq}/bin/jq -s '.[] as $in | $in * { + "devices": (${builtins.toJSON devices}${optionalString (! cfg.declarative.overrideDevices) " + $in.devices"}), + "folders": (${builtins.toJSON folders}${optionalString (! cfg.declarative.overrideFolders) " + $in.folders"}) + }') + + # POST the new config to syncthing + echo "$NEW_CFG" | ${pkgs.curl}/bin/curl -Ss \ + -H "X-API-Key: $API_KEY" \ + ${cfg.guiAddress}/rest/system/config -d @- + + # restart syncthing after sending the new config + ${pkgs.curl}/bin/curl -Ss \ + -H "X-API-Key: $API_KEY" \ + -X POST \ + ${cfg.guiAddress}/rest/system/restart + ''; +in { + ###### interface + options = { + services.syncthing = { + + enable = mkEnableOption '' + Syncthing - the self-hosted open-source alternative + to Dropbox and Bittorrent Sync. Initial interface will be + available on http://127.0.0.1:8384/. + ''; + + declarative = { + cert = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to users cert.pem file, will be copied into the syncthing's + <literal>configDir</literal> + ''; + }; + + key = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to users key.pem file, will be copied into the syncthing's + <literal>configDir</literal> + ''; + }; + + overrideDevices = mkOption { + type = types.bool; + default = true; + description = '' + Whether to delete the devices which are not configured via the + <literal>declarative.devices</literal> option. + If set to false, devices added via the webinterface will + persist but will have to be deleted manually. + ''; + }; + + devices = mkOption { + default = {}; + description = '' + Peers/devices which syncthing should communicate with. + ''; + example = { + bigbox = { + id = "7CFNTQM-IMTJBHJ-3UWRDIU-ZGQJFR6-VCXZ3NB-XUH3KZO-N52ITXR-LAIYUAU"; + addresses = [ "tcp://192.168.0.10:51820" ]; + }; + }; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + + name = mkOption { + type = types.str; + default = name; + description = '' + Name of the device + ''; + }; + + addresses = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The addresses used to connect to the device. + If this is let empty, dynamic configuration is attempted + ''; + }; + + id = mkOption { + type = types.str; + description = '' + The id of the other peer, this is mandatory. It's documented at + https://docs.syncthing.net/dev/device-ids.html + ''; + }; + + introducer = mkOption { + type = types.bool; + default = false; + description = '' + If the device should act as an introducer and be allowed + to add folders on this computer. + ''; + }; + + }; + })); + }; + + overrideFolders = mkOption { + type = types.bool; + default = true; + description = '' + Whether to delete the folders which are not configured via the + <literal>declarative.folders</literal> option. + If set to false, folders added via the webinterface will persist + but will have to be deleted manually. + ''; + }; + + folders = mkOption { + default = {}; + description = '' + folders which should be shared by syncthing. + ''; + example = literalExample '' + { + "/home/user/sync" = { + id = "syncme"; + devices = [ "bigbox" ]; + }; + } + ''; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + + enable = mkOption { + type = types.bool; + default = true; + description = '' + share this folder. + This option is useful when you want to define all folders + in one place, but not every machine should share all folders. + ''; + }; + + path = mkOption { + type = types.str; + default = name; + description = '' + The path to the folder which should be shared. + ''; + }; + + id = mkOption { + type = types.str; + default = name; + description = '' + The id of the folder. Must be the same on all devices. + ''; + }; + + label = mkOption { + type = types.str; + default = name; + description = '' + The label of the folder. + ''; + }; + + devices = mkOption { + type = types.listOf types.str; + default = []; + description = '' + The devices this folder should be shared with. Must be defined + in the <literal>declarative.devices</literal> attribute. + ''; + }; + + versioning = mkOption { + default = null; + description = '' + How to keep changed/deleted files with syncthing. + There are 4 different types of versioning with different parameters. + See https://docs.syncthing.net/users/versioning.html + ''; + example = [ + { + versioning = { + type = "simple"; + params.keep = "10"; + }; + } + { + versioning = { + type = "trashcan"; + params.cleanoutDays = "1000"; + }; + } + { + versioning = { + type = "staggered"; + params = { + cleanInterval = "3600"; + maxAge = "31536000"; + versionsPath = "/syncthing/backup"; + }; + }; + } + { + versioning = { + type = "external"; + params.versionsPath = pkgs.writers.writeBash "backup" '' + folderpath="$1" + filepath="$2" + rm -rf "$folderpath/$filepath" + ''; + }; + } + ]; + type = with types; nullOr (submodule { + options = { + type = mkOption { + type = enum [ "external" "simple" "staggered" "trashcan" ]; + description = '' + Type of versioning. + See https://docs.syncthing.net/users/versioning.html + ''; + }; + params = mkOption { + type = attrsOf (either str path); + description = '' + Parameters for versioning. Structure depends on versioning.type. + See https://docs.syncthing.net/users/versioning.html + ''; + }; + }; + }); + }; + + + + rescanInterval = mkOption { + type = types.int; + default = 3600; + description = '' + How often the folders should be rescaned for changes. + ''; + }; + + type = mkOption { + type = types.enum [ "sendreceive" "sendonly" "receiveonly" ]; + default = "sendreceive"; + description = '' + Whether to send only changes from this folder, only receive them + or propagate both. + ''; + }; + + watch = mkOption { + type = types.bool; + default = true; + description = '' + Whether the folder should be watched for changes by inotify. + ''; + }; + + watchDelay = mkOption { + type = types.int; + default = 10; + description = '' + The delay after an inotify event is triggered. + ''; + }; + + ignorePerms = mkOption { + type = types.bool; + default = true; + description = '' + Whether to propagate permission changes. + ''; + }; + + }; + })); + }; + }; + + guiAddress = mkOption { + type = types.str; + default = "127.0.0.1:8384"; + description = '' + Address to serve the GUI. + ''; + }; + + systemService = mkOption { + type = types.bool; + default = true; + description = "Auto launch Syncthing as a system service."; + }; + + user = mkOption { + type = types.str; + default = defaultUser; + description = '' + Syncthing will be run under this user (user will be created if it doesn't exist. + This can be your user name). + ''; + }; + + group = mkOption { + type = types.str; + default = defaultUser; + description = '' + Syncthing will be run under this group (group will not be created if it doesn't exist. + This can be your user name). + ''; + }; + + all_proxy = mkOption { + type = with types; nullOr str; + default = null; + example = "socks5://address.com:1234"; + description = '' + Overwrites all_proxy environment variable for the syncthing process to + the given value. This is normaly used to let relay client connect + through SOCKS5 proxy server. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/syncthing"; + description = '' + Path where synced directories will exist. + ''; + }; + + configDir = mkOption { + type = types.path; + description = '' + Path where the settings and keys will exist. + ''; + default = + let + nixos = config.system.stateVersion; + cond = versionAtLeast nixos "19.03"; + in cfg.dataDir + (optionalString cond "/.config/syncthing"); + }; + + openDefaultPorts = mkOption { + type = types.bool; + default = false; + example = literalExample "true"; + description = '' + Open the default ports in the firewall: + - TCP 22000 for transfers + - UDP 21027 for discovery + If multiple users are running syncthing on this machine, you will need to manually open a set of ports for each instance and leave this disabled. + Alternatively, if are running only a single instance on this machine using the default ports, enable this. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.syncthing; + defaultText = "pkgs.syncthing"; + example = literalExample "pkgs.syncthing"; + description = '' + Syncthing package to use. + ''; + }; + }; + }; + + imports = [ + (mkRemovedOptionModule ["services" "syncthing" "useInotify"] '' + This option was removed because syncthing now has the inotify functionality included under the name "fswatcher". + It can be enabled on a per-folder basis through the webinterface. + '') + ]; + + ###### implementation + + config = mkIf cfg.enable { + + networking.firewall = mkIf cfg.openDefaultPorts { + allowedTCPPorts = [ 22000 ]; + allowedUDPPorts = [ 21027 ]; + }; + + systemd.packages = [ pkgs.syncthing ]; + + users.users = mkIf (cfg.systemService && cfg.user == defaultUser) { + ${defaultUser} = + { group = cfg.group; + home = cfg.dataDir; + createHome = true; + uid = config.ids.uids.syncthing; + description = "Syncthing daemon user"; + }; + }; + + users.groups = mkIf (cfg.systemService && cfg.group == defaultUser) { + ${defaultUser}.gid = + config.ids.gids.syncthing; + }; + + systemd.services = { + syncthing = mkIf cfg.systemService { + description = "Syncthing service"; + after = [ "network.target" ]; + environment = { + STNORESTART = "yes"; + STNOUPGRADE = "yes"; + inherit (cfg) all_proxy; + } // config.networking.proxy.envVars; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Restart = "on-failure"; + SuccessExitStatus = "2 3 4"; + RestartForceExitStatus="3 4"; + User = cfg.user; + Group = cfg.group; + ExecStartPre = mkIf (cfg.declarative.cert != null || cfg.declarative.key != null) + "+${pkgs.writers.writeBash "syncthing-copy-keys" '' + install -dm700 -o ${cfg.user} -g ${cfg.group} ${cfg.configDir} + ${optionalString (cfg.declarative.cert != null) '' + install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.cert} ${cfg.configDir}/cert.pem + ''} + ${optionalString (cfg.declarative.key != null) '' + install -Dm400 -o ${cfg.user} -g ${cfg.group} ${toString cfg.declarative.key} ${cfg.configDir}/key.pem + ''} + ''}" + ; + ExecStart = '' + ${cfg.package}/bin/syncthing \ + -no-browser \ + -gui-address=${cfg.guiAddress} \ + -home=${cfg.configDir} + ''; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + CapabilityBoundingSet = [ + "~CAP_SYS_PTRACE" "~CAP_SYS_ADMIN" + "~CAP_SETGID" "~CAP_SETUID" "~CAP_SETPCAP" + "~CAP_SYS_TIME" "~CAP_KILL" + ]; + }; + }; + syncthing-init = mkIf ( + cfg.declarative.devices != {} || cfg.declarative.folders != {} + ) { + after = [ "syncthing.service" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = cfg.user; + RemainAfterExit = true; + Type = "oneshot"; + ExecStart = updateConfig; + }; + }; + + syncthing-resume = { + wantedBy = [ "suspend.target" ]; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/tailscale.nix b/nixpkgs/nixos/modules/services/networking/tailscale.nix new file mode 100644 index 000000000000..4d6aeb75ebd1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tailscale.nix @@ -0,0 +1,49 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.tailscale; +in { + meta.maintainers = with maintainers; [ danderson mbaillie ]; + + options.services.tailscale = { + enable = mkEnableOption "Tailscale client daemon"; + + port = mkOption { + type = types.port; + default = 41641; + description = "The port to listen on for tunnel traffic (0=autoselect)."; + }; + }; + + config = mkIf cfg.enable { + systemd.services.tailscale = { + description = "Tailscale client daemon"; + + after = [ "network-pre.target" ]; + wants = [ "network-pre.target" ]; + wantedBy = [ "multi-user.target" ]; + + unitConfig = { + StartLimitIntervalSec = 0; + StartLimitBurst = 0; + }; + + serviceConfig = { + ExecStart = + "${pkgs.tailscale}/bin/tailscaled --port ${toString cfg.port}"; + + RuntimeDirectory = "tailscale"; + RuntimeDirectoryMode = 755; + + StateDirectory = "tailscale"; + StateDirectoryMode = 750; + + CacheDirectory = "tailscale"; + CacheDirectoryMode = 750; + + Restart = "on-failure"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/tcpcrypt.nix b/nixpkgs/nixos/modules/services/networking/tcpcrypt.nix new file mode 100644 index 000000000000..5a91054e1668 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tcpcrypt.nix @@ -0,0 +1,80 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.networking.tcpcrypt; + +in + +{ + + ###### interface + + options = { + + networking.tcpcrypt.enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable opportunistic TCP encryption. If the other end + speaks Tcpcrypt, then your traffic will be encrypted; otherwise + it will be sent in clear text. Thus, Tcpcrypt alone provides no + guarantees -- it is best effort. If, however, a Tcpcrypt + connection is successful and any attackers that exist are + passive, then Tcpcrypt guarantees privacy. + ''; + }; + }; + + config = mkIf cfg.enable { + + users.users.tcpcryptd = { + uid = config.ids.uids.tcpcryptd; + description = "tcpcrypt daemon user"; + }; + + systemd.services.tcpcrypt = { + description = "tcpcrypt"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + path = [ pkgs.iptables pkgs.tcpcrypt pkgs.procps ]; + + preStart = '' + mkdir -p /run/tcpcryptd + chown tcpcryptd /run/tcpcryptd + sysctl -n net.ipv4.tcp_ecn > /run/tcpcryptd/pre-tcpcrypt-ecn-state + sysctl -w net.ipv4.tcp_ecn=0 + + iptables -t raw -N nixos-tcpcrypt + iptables -t raw -A nixos-tcpcrypt -p tcp -m mark --mark 0x0/0x10 -j NFQUEUE --queue-num 666 + iptables -t raw -I PREROUTING -j nixos-tcpcrypt + + iptables -t mangle -N nixos-tcpcrypt + iptables -t mangle -A nixos-tcpcrypt -p tcp -m mark --mark 0x0/0x10 -j NFQUEUE --queue-num 666 + iptables -t mangle -I POSTROUTING -j nixos-tcpcrypt + ''; + + script = "tcpcryptd -x 0x10"; + + postStop = '' + if [ -f /run/tcpcryptd/pre-tcpcrypt-ecn-state ]; then + sysctl -w net.ipv4.tcp_ecn=$(cat /run/tcpcryptd/pre-tcpcrypt-ecn-state) + fi + + iptables -t mangle -D POSTROUTING -j nixos-tcpcrypt || true + iptables -t raw -D PREROUTING -j nixos-tcpcrypt || true + + iptables -t raw -F nixos-tcpcrypt || true + iptables -t raw -X nixos-tcpcrypt || true + + iptables -t mangle -F nixos-tcpcrypt || true + iptables -t mangle -X nixos-tcpcrypt || true + ''; + }; + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/teamspeak3.nix b/nixpkgs/nixos/modules/services/networking/teamspeak3.nix new file mode 100644 index 000000000000..fadb32dcd777 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/teamspeak3.nix @@ -0,0 +1,142 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + ts3 = pkgs.teamspeak_server; + cfg = config.services.teamspeak3; + user = "teamspeak"; + group = "teamspeak"; +in + +{ + + ###### interface + + options = { + + services.teamspeak3 = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run the Teamspeak3 voice communication server daemon. + ''; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/teamspeak3-server"; + description = '' + Directory to store TS3 database and other state/data files. + ''; + }; + + logPath = mkOption { + type = types.path; + default = "/var/log/teamspeak3-server/"; + description = '' + Directory to store log files in. + ''; + }; + + voiceIP = mkOption { + type = types.nullOr types.str; + default = null; + example = "0.0.0.0"; + description = '' + IP on which the server instance will listen for incoming voice connections. Defaults to any IP. + ''; + }; + + defaultVoicePort = mkOption { + type = types.int; + default = 9987; + description = '' + Default UDP port for clients to connect to virtual servers - used for first virtual server, subsequent ones will open on incrementing port numbers by default. + ''; + }; + + fileTransferIP = mkOption { + type = types.nullOr types.str; + default = null; + example = "0.0.0.0"; + description = '' + IP on which the server instance will listen for incoming file transfer connections. Defaults to any IP. + ''; + }; + + fileTransferPort = mkOption { + type = types.int; + default = 30033; + description = '' + TCP port opened for file transfers. + ''; + }; + + queryIP = mkOption { + type = types.nullOr types.str; + default = null; + example = "0.0.0.0"; + description = '' + IP on which the server instance will listen for incoming ServerQuery connections. Defaults to any IP. + ''; + }; + + queryPort = mkOption { + type = types.int; + default = 10011; + description = '' + TCP port opened for ServerQuery connections. + ''; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + users.users.teamspeak = { + description = "Teamspeak3 voice communication server daemon"; + group = group; + uid = config.ids.uids.teamspeak; + home = cfg.dataDir; + createHome = true; + }; + + users.groups.teamspeak = { + gid = config.ids.gids.teamspeak; + }; + + systemd.tmpfiles.rules = [ + "d '${cfg.logPath}' - ${user} ${group} - -" + ]; + + systemd.services.teamspeak3-server = { + description = "Teamspeak3 voice communication server daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = '' + ${ts3}/bin/ts3server \ + dbsqlpath=${ts3}/lib/teamspeak/sql/ logpath=${cfg.logPath} \ + ${optionalString (cfg.voiceIP != null) "voice_ip=${cfg.voiceIP}"} \ + default_voice_port=${toString cfg.defaultVoicePort} \ + ${optionalString (cfg.fileTransferIP != null) "filetransfer_ip=${cfg.fileTransferIP}"} \ + filetransfer_port=${toString cfg.fileTransferPort} \ + ${optionalString (cfg.queryIP != null) "query_ip=${cfg.queryIP}"} \ + query_port=${toString cfg.queryPort} license_accepted=1 + ''; + WorkingDirectory = cfg.dataDir; + User = user; + Group = group; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ arobyn ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/tedicross.nix b/nixpkgs/nixos/modules/services/networking/tedicross.nix new file mode 100644 index 000000000000..0716975f594a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tedicross.nix @@ -0,0 +1,100 @@ +{ config, pkgs, lib, ... }: + +with lib; + +let + dataDir = "/var/lib/tedicross"; + cfg = config.services.tedicross; + configJSON = pkgs.writeText "tedicross-settings.json" (builtins.toJSON cfg.config); + configYAML = pkgs.runCommand "tedicross-settings.yaml" { preferLocalBuild = true; } '' + ${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out + ''; + +in { + options = { + services.tedicross = { + enable = mkEnableOption "the TediCross Telegram-Discord bridge service"; + + config = mkOption { + type = types.attrs; + # from https://github.com/TediCross/TediCross/blob/master/example.settings.yaml + example = literalExample '' + { + telegram = { + useFirstNameInsteadOfUsername = false; + colonAfterSenderName = false; + skipOldMessages = true; + sendEmojiWithStickers = true; + }; + discord = { + useNickname = false; + skipOldMessages = true; + displayTelegramReplies = "embed"; + replyLength = 100; + }; + bridges = [ + { + name = "Default bridge"; + direction = "both"; + telegram = { + chatId = -123456789; + relayJoinMessages = true; + relayLeaveMessages = true; + sendUsernames = true; + ignoreCommands = true; + }; + discord = { + serverId = "DISCORD_SERVER_ID"; + channelId = "DISCORD_CHANNEL_ID"; + relayJoinMessages = true; + relayLeaveMessages = true; + sendUsernames = true; + crossDeleteOnTelegram = true; + }; + } + ]; + + debug = false; + } + ''; + description = '' + <filename>settings.yaml</filename> configuration as a Nix attribute set. + Secret tokens should be specified using <option>environmentFile</option> + instead of this world-readable file. + ''; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + File containing environment variables to be passed to the TediCross service, + in which secret tokens can be specified securely using the + <literal>TELEGRAM_BOT_TOKEN</literal> and <literal>DISCORD_BOT_TOKEN</literal> + keys. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + # from https://github.com/TediCross/TediCross/blob/master/guides/autostart/Linux.md + systemd.services.tedicross = { + description = "TediCross Telegram-Discord bridge service"; + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.nodePackages.tedicross}/bin/tedicross --config='${configYAML}' --data-dir='${dataDir}'"; + Restart = "always"; + DynamicUser = true; + StateDirectory = baseNameOf dataDir; + EnvironmentFile = cfg.environmentFile; + }; + }; + }; + + meta.maintainers = with maintainers; [ pacien ]; +} + diff --git a/nixpkgs/nixos/modules/services/networking/tftpd.nix b/nixpkgs/nixos/modules/services/networking/tftpd.nix new file mode 100644 index 000000000000..c9c0a2b321d5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tftpd.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + + ###### interface + + options = { + + services.tftpd.enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable tftpd, a Trivial File Transfer Protocol server. + The server will be run as an xinetd service. + ''; + }; + + services.tftpd.path = mkOption { + type = types.path; + default = "/srv/tftp"; + description = '' + Where the tftp server files are stored. + ''; + }; + + }; + + + ###### implementation + + config = mkIf config.services.tftpd.enable { + + services.xinetd.enable = true; + + services.xinetd.services = singleton + { name = "tftp"; + protocol = "udp"; + server = "${pkgs.netkittftp}/sbin/in.tftpd"; + serverArgs = "${config.services.tftpd.path}"; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/thelounge.nix b/nixpkgs/nixos/modules/services/networking/thelounge.nix new file mode 100644 index 000000000000..a1b06703484b --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/thelounge.nix @@ -0,0 +1,75 @@ +{ pkgs, lib, config, ... }: + +with lib; + +let + cfg = config.services.thelounge; + dataDir = "/var/lib/thelounge"; + configJsData = "module.exports = " + builtins.toJSON ( + { private = cfg.private; port = cfg.port; } // cfg.extraConfig + ); +in { + options.services.thelounge = { + enable = mkEnableOption "The Lounge web IRC client"; + + private = mkOption { + type = types.bool; + default = false; + description = '' + Make your The Lounge instance private. You will need to configure user + accounts by using the (<command>thelounge</command>) command or by adding + entries in <filename>${dataDir}/users</filename>. You might need to restart + The Lounge after making changes to the state directory. + ''; + }; + + port = mkOption { + type = types.port; + default = 9000; + description = "TCP port to listen on for http connections."; + }; + + extraConfig = mkOption { + default = {}; + type = types.attrs; + example = literalExample ''{ + reverseProxy = true; + defaults = { + name = "Your Network"; + host = "localhost"; + port = 6697; + }; + }''; + description = '' + The Lounge's <filename>config.js</filename> contents as attribute set (will be + converted to JSON to generate the configuration file). + + The options defined here will be merged to the default configuration file. + Note: In case of duplicate configuration, options from <option>extraConfig</option> have priority. + + Documentation: <link xlink:href="https://thelounge.chat/docs/server/configuration" /> + ''; + }; + }; + + config = mkIf cfg.enable { + users.users.thelounge = { + description = "thelounge service user"; + group = "thelounge"; + isSystemUser = true; + }; + users.groups.thelounge = {}; + systemd.services.thelounge = { + description = "The Lounge web IRC client"; + wantedBy = [ "multi-user.target" ]; + preStart = "ln -sf ${pkgs.writeText "config.js" configJsData} ${dataDir}/config.js"; + serviceConfig = { + User = "thelounge"; + StateDirectory = baseNameOf dataDir; + ExecStart = "${pkgs.thelounge}/bin/thelounge start"; + }; + }; + + environment.systemPackages = [ pkgs.thelounge ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/tinc.nix b/nixpkgs/nixos/modules/services/networking/tinc.nix new file mode 100644 index 000000000000..e98aafc20937 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tinc.nix @@ -0,0 +1,212 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.tinc; + +in + +{ + + ###### interface + + options = { + + services.tinc = { + + networks = mkOption { + default = { }; + type = with types; attrsOf (submodule { + options = { + + extraConfig = mkOption { + default = ""; + type = types.lines; + description = '' + Extra lines to add to the tinc service configuration file. + ''; + }; + + name = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The name of the node which is used as an identifier when communicating + with the remote nodes in the mesh. If null then the hostname of the system + is used to derive a name (note that tinc may replace non-alphanumeric characters in + hostnames by underscores). + ''; + }; + + ed25519PrivateKeyFile = mkOption { + default = null; + type = types.nullOr types.path; + description = '' + Path of the private ed25519 keyfile. + ''; + }; + + debugLevel = mkOption { + default = 0; + type = types.addCheck types.int (l: l >= 0 && l <= 5); + description = '' + The amount of debugging information to add to the log. 0 means little + logging while 5 is the most logging. <command>man tincd</command> for + more details. + ''; + }; + + hosts = mkOption { + default = { }; + type = types.attrsOf types.lines; + description = '' + The name of the host in the network as well as the configuration for that host. + This name should only contain alphanumerics and underscores. + ''; + }; + + interfaceType = mkOption { + default = "tun"; + type = types.enum [ "tun" "tap" ]; + description = '' + The type of virtual interface used for the network connection + ''; + }; + + listenAddress = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The ip address to listen on for incoming connections. + ''; + }; + + bindToAddress = mkOption { + default = null; + type = types.nullOr types.str; + description = '' + The ip address to bind to (both listen on and send packets from). + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.tinc_pre; + defaultText = "pkgs.tinc_pre"; + description = '' + The package to use for the tinc daemon's binary. + ''; + }; + + chroot = mkOption { + default = true; + type = types.bool; + description = '' + Change process root directory to the directory where the config file is located (/etc/tinc/netname/), for added security. + The chroot is performed after all the initialization is done, after writing pid files and opening network sockets. + + Note that tinc can't run scripts anymore (such as tinc-down or host-up), unless it is setup to be runnable inside chroot environment. + ''; + }; + }; + }); + + description = '' + Defines the tinc networks which will be started. + Each network invokes a different daemon. + ''; + }; + }; + + }; + + + ###### implementation + + config = mkIf (cfg.networks != { }) { + + environment.etc = fold (a: b: a // b) { } + (flip mapAttrsToList cfg.networks (network: data: + flip mapAttrs' data.hosts (host: text: nameValuePair + ("tinc/${network}/hosts/${host}") + ({ mode = "0644"; user = "tinc.${network}"; inherit text; }) + ) // { + "tinc/${network}/tinc.conf" = { + mode = "0444"; + text = '' + Name = ${if data.name == null then "$HOST" else data.name} + DeviceType = ${data.interfaceType} + ${optionalString (data.ed25519PrivateKeyFile != null) "Ed25519PrivateKeyFile = ${data.ed25519PrivateKeyFile}"} + ${optionalString (data.listenAddress != null) "ListenAddress = ${data.listenAddress}"} + ${optionalString (data.bindToAddress != null) "BindToAddress = ${data.bindToAddress}"} + Interface = tinc.${network} + ${data.extraConfig} + ''; + }; + } + )); + + systemd.services = flip mapAttrs' cfg.networks (network: data: nameValuePair + ("tinc.${network}") + ({ + description = "Tinc Daemon - ${network}"; + wantedBy = [ "multi-user.target" ]; + path = [ data.package ]; + restartTriggers = [ config.environment.etc."tinc/${network}/tinc.conf".source ]; + serviceConfig = { + Type = "simple"; + Restart = "always"; + RestartSec = "3"; + ExecStart = "${data.package}/bin/tincd -D -U tinc.${network} -n ${network} ${optionalString (data.chroot) "-R"} --pidfile /run/tinc.${network}.pid -d ${toString data.debugLevel}"; + }; + preStart = '' + mkdir -p /etc/tinc/${network}/hosts + chown tinc.${network} /etc/tinc/${network}/hosts + mkdir -p /etc/tinc/${network}/invitations + chown tinc.${network} /etc/tinc/${network}/invitations + + # Determine how we should generate our keys + if type tinc >/dev/null 2>&1; then + # Tinc 1.1+ uses the tinc helper application for key generation + ${if data.ed25519PrivateKeyFile != null then " # Keyfile managed by nix" else '' + # Prefer ED25519 keys (only in 1.1+) + [ -f "/etc/tinc/${network}/ed25519_key.priv" ] || tinc -n ${network} generate-ed25519-keys + ''} + # Otherwise use RSA keys + [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tinc -n ${network} generate-rsa-keys 4096 + else + # Tinc 1.0 uses the tincd application + [ -f "/etc/tinc/${network}/rsa_key.priv" ] || tincd -n ${network} -K 4096 + fi + ''; + }) + ); + + environment.systemPackages = let + cli-wrappers = pkgs.stdenv.mkDerivation { + name = "tinc-cli-wrappers"; + buildInputs = [ pkgs.makeWrapper ]; + buildCommand = '' + mkdir -p $out/bin + ${concatStringsSep "\n" (mapAttrsToList (network: data: + optionalString (versionAtLeast data.package.version "1.1pre") '' + makeWrapper ${data.package}/bin/tinc "$out/bin/tinc.${network}" \ + --add-flags "--pidfile=/run/tinc.${network}.pid" \ + --add-flags "--config=/etc/tinc/${network}" + '') cfg.networks)} + ''; + }; + in [ cli-wrappers ]; + + users.users = flip mapAttrs' cfg.networks (network: _: + nameValuePair ("tinc.${network}") ({ + description = "Tinc daemon user for ${network}"; + isSystemUser = true; + }) + ); + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/tinydns.nix b/nixpkgs/nixos/modules/services/networking/tinydns.nix new file mode 100644 index 000000000000..79507b2ebcdd --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tinydns.nix @@ -0,0 +1,55 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + ###### interface + + options = { + services.tinydns = { + enable = mkOption { + default = false; + type = types.bool; + description = "Whether to run the tinydns dns server"; + }; + + data = mkOption { + type = types.lines; + default = ""; + description = "The DNS data to serve, in the format described by tinydns-data(8)"; + }; + + ip = mkOption { + default = "0.0.0.0"; + type = types.str; + description = "IP address on which to listen for connections"; + }; + }; + }; + + ###### implementation + + config = mkIf config.services.tinydns.enable { + environment.systemPackages = [ pkgs.djbdns ]; + + users.users.tinydns.isSystemUser = true; + + systemd.services.tinydns = { + description = "djbdns tinydns server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + path = with pkgs; [ daemontools djbdns ]; + preStart = '' + rm -rf /var/lib/tinydns + tinydns-conf tinydns tinydns /var/lib/tinydns ${config.services.tinydns.ip} + cd /var/lib/tinydns/root/ + ln -sf ${pkgs.writeText "tinydns-data" config.services.tinydns.data} data + tinydns-data + ''; + script = '' + cd /var/lib/tinydns + exec ./run + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix b/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix new file mode 100644 index 000000000000..f88e34827d00 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix @@ -0,0 +1,79 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + home = "/var/lib/tox-bootstrapd"; + PIDFile = "${home}/pid"; + + pkg = pkgs.libtoxcore; + cfg = config.services.toxBootstrapd; + cfgFile = builtins.toFile "tox-bootstrapd.conf" + '' + port = ${toString cfg.port} + keys_file_path = "${home}/keys" + pid_file_path = "${PIDFile}" + ${cfg.extraConfig} + ''; +in +{ + options = + { services.toxBootstrapd = + { enable = mkOption { + type = types.bool; + default = false; + description = + '' + Whether to enable the Tox DHT bootstrap daemon. + ''; + }; + + port = mkOption { + type = types.int; + default = 33445; + description = "Listening port (UDP)."; + }; + + keysFile = mkOption { + type = types.str; + default = "${home}/keys"; + description = "Node key file."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = + '' + Configuration for bootstrap daemon. + See <link xlink:href="https://github.com/irungentoo/toxcore/blob/master/other/bootstrap_daemon/tox-bootstrapd.conf"/> + and <link xlink:href="http://wiki.tox.im/Nodes"/>. + ''; + }; + }; + + }; + + config = mkIf config.services.toxBootstrapd.enable { + + users.users.tox-bootstrapd = + { uid = config.ids.uids.tox-bootstrapd; + description = "Tox bootstrap daemon user"; + inherit home; + createHome = true; + }; + + systemd.services.tox-bootstrapd = { + description = "Tox DHT bootstrap daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = + { ExecStart = "${pkg}/bin/tox-bootstrapd --config=${cfgFile}"; + Type = "forking"; + inherit PIDFile; + User = "tox-bootstrapd"; + }; + }; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/tox-node.nix b/nixpkgs/nixos/modules/services/networking/tox-node.nix new file mode 100644 index 000000000000..c24e7fd12850 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tox-node.nix @@ -0,0 +1,95 @@ +{ lib, pkgs, config, ... }: + +with lib; + +let + pkg = pkgs.tox-node; + cfg = config.services.tox-node; + homeDir = "/var/lib/tox-node"; + + configFile = let + # fetchurl should be switched to getting this file from tox-node.src once + # the dpkg directory is in a release + src = pkgs.fetchurl { + url = "https://raw.githubusercontent.com/tox-rs/tox-node/master/dpkg/config.yml"; + sha256 = "1431wzpzm786mcvyzk1rp7ar418n45dr75hdggxvlm7pkpam31xa"; + }; + confJSON = pkgs.writeText "config.json" ( + builtins.toJSON { + log-type = cfg.logType; + keys-file = cfg.keysFile; + udp-address = cfg.udpAddress; + tcp-addresses = cfg.tcpAddresses; + tcp-connections-limit = cfg.tcpConnectionLimit; + lan-discovery = cfg.lanDiscovery; + threads = cfg.threads; + motd = cfg.motd; + } + ); + in with pkgs; runCommand "config.yml" {} '' + ${remarshal}/bin/remarshal -if yaml -of json ${src} -o src.json + ${jq}/bin/jq -s '(.[0] | with_entries( select(.key == "bootstrap-nodes"))) * .[1]' src.json ${confJSON} > $out + ''; + +in { + options.services.tox-node = { + enable = mkEnableOption "Tox Node service"; + + logType = mkOption { + type = types.enum [ "Stderr" "Stdout" "Syslog" "None" ]; + default = "Stderr"; + description = "Logging implementation."; + }; + keysFile = mkOption { + type = types.str; + default = "${homeDir}/keys"; + description = "Path to the file where DHT keys are stored."; + }; + udpAddress = mkOption { + type = types.str; + default = "0.0.0.0:33445"; + description = "UDP address to run DHT node."; + }; + tcpAddresses = mkOption { + type = types.listOf types.str; + default = [ "0.0.0.0:33445" ]; + description = "TCP addresses to run TCP relay."; + }; + tcpConnectionLimit = mkOption { + type = types.int; + default = 8192; + description = "Maximum number of active TCP connections relay can hold"; + }; + lanDiscovery = mkOption { + type = types.bool; + default = true; + description = "Enable local network discovery."; + }; + threads = mkOption { + type = types.int; + default = 1; + description = "Number of threads for execution"; + }; + motd = mkOption { + type = types.str; + default = "Hi from tox-rs! I'm up {{uptime}}. TCP: incoming {{tcp_packets_in}}, outgoing {{tcp_packets_out}}, UDP: incoming {{udp_packets_in}}, outgoing {{udp_packets_out}}"; + description = "Message of the day"; + }; + }; + + config = lib.mkIf cfg.enable { + systemd.services.tox-node = { + description = "Tox Node"; + + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + ExecStart = "${pkg}/bin/tox-node config ${configFile}"; + StateDirectory = "tox-node"; + DynamicUser = true; + Restart = "always"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/toxvpn.nix b/nixpkgs/nixos/modules/services/networking/toxvpn.nix new file mode 100644 index 000000000000..9e97faeebc1e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/toxvpn.nix @@ -0,0 +1,68 @@ +{ config, pkgs, lib, ... }: + +with lib; + +{ + options = { + services.toxvpn = { + enable = mkEnableOption "toxvpn running on startup"; + + localip = mkOption { + type = types.str; + default = "10.123.123.1"; + description = "your ip on the vpn"; + }; + + port = mkOption { + type = types.int; + default = 33445; + description = "udp port for toxcore, port-forward to help with connectivity if you run many nodes behind one NAT"; + }; + + auto_add_peers = mkOption { + type = types.listOf types.str; + default = []; + example = ''[ "toxid1" "toxid2" ]''; + description = "peers to automatically connect to on startup"; + }; + }; + }; + + config = mkIf config.services.toxvpn.enable { + systemd.services.toxvpn = { + description = "toxvpn daemon"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + preStart = '' + mkdir -p /run/toxvpn || true + chown toxvpn /run/toxvpn + ''; + + path = [ pkgs.toxvpn ]; + + script = '' + exec toxvpn -i ${config.services.toxvpn.localip} -l /run/toxvpn/control -u toxvpn -p ${toString config.services.toxvpn.port} ${lib.concatMapStringsSep " " (x: "-a ${x}") config.services.toxvpn.auto_add_peers} + ''; + + serviceConfig = { + KillMode = "process"; + Restart = "on-success"; + Type = "notify"; + }; + + restartIfChanged = false; # Likely to be used for remote admin + }; + + environment.systemPackages = [ pkgs.toxvpn ]; + + users.users = { + toxvpn = { + uid = config.ids.uids.toxvpn; + home = "/var/lib/toxvpn"; + createHome = true; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/trickster.nix b/nixpkgs/nixos/modules/services/networking/trickster.nix new file mode 100644 index 000000000000..8760dd5a9382 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/trickster.nix @@ -0,0 +1,112 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.trickster; +in +{ + + options = { + services.trickster = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enable Trickster. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.trickster; + defaultText = "pkgs.trickster"; + description = '' + Package that should be used for trickster. + ''; + }; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Path to configuration file. + ''; + }; + + instance-id = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Instance ID for when running multiple processes (default null). + ''; + }; + + log-level = mkOption { + type = types.str; + default = "info"; + description = '' + Level of Logging to use (debug, info, warn, error) (default "info"). + ''; + }; + + metrics-port = mkOption { + type = types.port; + default = 8082; + description = '' + Port that the /metrics endpoint will listen on. + ''; + }; + + origin = mkOption { + type = types.str; + default = "http://prometheus:9090"; + description = '' + URL to the Prometheus Origin. Enter it like you would in grafana, e.g., http://prometheus:9090 (default http://prometheus:9090). + ''; + }; + + profiler-port = mkOption { + type = types.nullOr types.port; + default = null; + description = '' + Port that the /debug/pprof endpoint will listen on. + ''; + }; + + proxy-port = mkOption { + type = types.port; + default = 9090; + description = '' + Port that the Proxy server will listen on. + ''; + }; + + }; + }; + + config = mkIf cfg.enable { + systemd.services.trickster = { + description = "Dashboard Accelerator for Prometheus"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + DynamicUser = true; + ExecStart = '' + ${cfg.package}/bin/trickster \ + -log-level ${cfg.log-level} \ + -metrics-port ${toString cfg.metrics-port} \ + -origin ${cfg.origin} \ + -proxy-port ${toString cfg.proxy-port} \ + ${optionalString (cfg.configFile != null) "-config ${cfg.configFile}"} \ + ${optionalString (cfg.profiler-port != null) "-profiler-port ${cfg.profiler-port}"} \ + ${optionalString (cfg.instance-id != null) "-instance-id ${cfg.instance-id}"} + ''; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Restart = "always"; + }; + }; + + }; +} + diff --git a/nixpkgs/nixos/modules/services/networking/tvheadend.nix b/nixpkgs/nixos/modules/services/networking/tvheadend.nix new file mode 100644 index 000000000000..ccf879996631 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tvheadend.nix @@ -0,0 +1,61 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.tvheadend; + pidFile = "${config.users.users.tvheadend.home}/tvheadend.pid"; +in + +{ + options = { + services.tvheadend = { + enable = mkEnableOption "Tvheadend"; + httpPort = mkOption { + type = types.int; + default = 9981; + description = "Port to bind HTTP to."; + }; + + htspPort = mkOption { + type = types.int; + default = 9982; + description = "Port to bind HTSP to."; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.tvheadend = { + description = "Tvheadend Service user"; + home = "/var/lib/tvheadend"; + createHome = true; + uid = config.ids.uids.tvheadend; + }; + + systemd.services.tvheadend = { + description = "Tvheadend TV streaming server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "forking"; + PIDFile = pidFile; + Restart = "always"; + RestartSec = 5; + User = "tvheadend"; + Group = "video"; + ExecStart = '' + ${pkgs.tvheadend}/bin/tvheadend \ + --http_port ${toString cfg.httpPort} \ + --htsp_port ${toString cfg.htspPort} \ + -f \ + -C \ + -p ${pidFile} \ + -u tvheadend \ + -g video + ''; + ExecStop = "${pkgs.coreutils}/bin/rm ${pidFile}"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/unbound.nix b/nixpkgs/nixos/modules/services/networking/unbound.nix new file mode 100644 index 000000000000..baed83591e1e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/unbound.nix @@ -0,0 +1,148 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.unbound; + + stateDir = "/var/lib/unbound"; + + access = concatMapStringsSep "\n " (x: "access-control: ${x} allow") cfg.allowedAccess; + + interfaces = concatMapStringsSep "\n " (x: "interface: ${x}") cfg.interfaces; + + isLocalAddress = x: substring 0 3 x == "::1" || substring 0 9 x == "127.0.0.1"; + + forward = + optionalString (any isLocalAddress cfg.forwardAddresses) '' + do-not-query-localhost: no + '' + + optionalString (cfg.forwardAddresses != []) '' + forward-zone: + name: . + '' + + concatMapStringsSep "\n" (x: " forward-addr: ${x}") cfg.forwardAddresses; + + rootTrustAnchorFile = "${stateDir}/root.key"; + + trustAnchor = optionalString cfg.enableRootTrustAnchor + "auto-trust-anchor-file: ${rootTrustAnchorFile}"; + + confFile = pkgs.writeText "unbound.conf" '' + server: + directory: "${stateDir}" + username: unbound + chroot: "${stateDir}" + pidfile: "" + ${interfaces} + ${access} + ${trustAnchor} + ${cfg.extraConfig} + ${forward} + ''; + +in + +{ + + ###### interface + + options = { + services.unbound = { + + enable = mkEnableOption "Unbound domain name server"; + + package = mkOption { + type = types.package; + default = pkgs.unbound; + defaultText = "pkgs.unbound"; + description = "The unbound package to use"; + }; + + allowedAccess = mkOption { + default = [ "127.0.0.0/24" ]; + type = types.listOf types.str; + description = "What networks are allowed to use unbound as a resolver."; + }; + + interfaces = mkOption { + default = [ "127.0.0.1" ] ++ optional config.networking.enableIPv6 "::1"; + type = types.listOf types.str; + description = "What addresses the server should listen on."; + }; + + forwardAddresses = mkOption { + default = [ ]; + type = types.listOf types.str; + description = "What servers to forward queries to."; + }; + + enableRootTrustAnchor = mkOption { + default = true; + type = types.bool; + description = "Use and update root trust anchor for DNSSEC validation."; + }; + + extraConfig = mkOption { + default = ""; + type = types.lines; + description = '' + Extra unbound config. See + <citerefentry><refentrytitle>unbound.conf</refentrytitle><manvolnum>8 + </manvolnum></citerefentry>. + ''; + }; + + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + environment.systemPackages = [ cfg.package ]; + + users.users.unbound = { + description = "unbound daemon user"; + isSystemUser = true; + }; + + networking.resolvconf.useLocalResolver = mkDefault true; + + systemd.services.unbound = { + description = "Unbound recursive Domain Name Server"; + after = [ "network.target" ]; + before = [ "nss-lookup.target" ]; + wants = [ "nss-lookup.target" ]; + wantedBy = [ "multi-user.target" ]; + + preStart = '' + mkdir -m 0755 -p ${stateDir}/dev/ + cp ${confFile} ${stateDir}/unbound.conf + ${optionalString cfg.enableRootTrustAnchor '' + ${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!" + chown unbound ${stateDir} ${rootTrustAnchorFile} + ''} + touch ${stateDir}/dev/random + ${pkgs.utillinux}/bin/mount --bind -n /dev/urandom ${stateDir}/dev/random + ''; + + serviceConfig = { + ExecStart = "${cfg.package}/bin/unbound -d -c ${stateDir}/unbound.conf"; + ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random"; + + ProtectSystem = true; + ProtectHome = true; + PrivateDevices = true; + Restart = "always"; + RestartSec = "5s"; + }; + }; + + # If networkmanager is enabled, ask it to interface with unbound. + networking.networkmanager.dns = "unbound"; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/unifi.nix b/nixpkgs/nixos/modules/services/networking/unifi.nix new file mode 100644 index 000000000000..4bdfa8143dce --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/unifi.nix @@ -0,0 +1,180 @@ +{ config, lib, pkgs, utils, ... }: +with lib; +let + cfg = config.services.unifi; + stateDir = "/var/lib/unifi"; + cmd = '' + @${cfg.jrePackage}/bin/java java \ + ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \ + ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \ + -jar ${stateDir}/lib/ace.jar + ''; + mountPoints = [ + { + what = "${cfg.unifiPackage}/dl"; + where = "${stateDir}/dl"; + } + { + what = "${cfg.unifiPackage}/lib"; + where = "${stateDir}/lib"; + } + { + what = "${cfg.mongodbPackage}/bin"; + where = "${stateDir}/bin"; + } + { + what = "${cfg.dataDir}"; + where = "${stateDir}/data"; + } + ]; + systemdMountPoints = map (m: "${utils.escapeSystemdPath m.where}.mount") mountPoints; +in +{ + + options = { + + services.unifi.enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether or not to enable the unifi controller service. + ''; + }; + + services.unifi.jrePackage = mkOption { + type = types.package; + default = pkgs.jre8; + defaultText = "pkgs.jre8"; + description = '' + The JRE package to use. Check the release notes to ensure it is supported. + ''; + }; + + services.unifi.unifiPackage = mkOption { + type = types.package; + default = pkgs.unifiLTS; + defaultText = "pkgs.unifiLTS"; + description = '' + The unifi package to use. + ''; + }; + + services.unifi.mongodbPackage = mkOption { + type = types.package; + default = pkgs.mongodb; + defaultText = "pkgs.mongodb"; + description = '' + The mongodb package to use. + ''; + }; + + services.unifi.dataDir = mkOption { + type = types.str; + default = "${stateDir}/data"; + description = '' + Where to store the database and other data. + + This directory will be bind-mounted to ${stateDir}/data as part of the service startup. + ''; + }; + + services.unifi.openPorts = mkOption { + type = types.bool; + default = true; + description = '' + Whether or not to open the minimum required ports on the firewall. + + This is necessary to allow firmware upgrades and device discovery to + work. For remote login, you should additionally open (or forward) port + 8443. + ''; + }; + + services.unifi.initialJavaHeapSize = mkOption { + type = types.nullOr types.int; + default = null; + example = 1024; + description = '' + Set the initial heap size for the JVM in MB. If this option isn't set, the + JVM will decide this value at runtime. + ''; + }; + + services.unifi.maximumJavaHeapSize = mkOption { + type = types.nullOr types.int; + default = null; + example = 4096; + description = '' + Set the maximimum heap size for the JVM in MB. If this option isn't set, the + JVM will decide this value at runtime. + ''; + }; + + }; + + config = mkIf cfg.enable { + + users.users.unifi = { + uid = config.ids.uids.unifi; + description = "UniFi controller daemon user"; + home = "${stateDir}"; + }; + + networking.firewall = mkIf cfg.openPorts { + # https://help.ubnt.com/hc/en-us/articles/218506997 + allowedTCPPorts = [ + 8080 # Port for UAP to inform controller. + 8880 # Port for HTTP portal redirect, if guest portal is enabled. + 8843 # Port for HTTPS portal redirect, ditto. + 6789 # Port for UniFi mobile speed test. + ]; + allowedUDPPorts = [ + 3478 # UDP port used for STUN. + 10001 # UDP port used for device discovery. + ]; + }; + + # We must create the binary directories as bind mounts instead of symlinks + # This is because the controller resolves all symlinks to absolute paths + # to be used as the working directory. + systemd.mounts = map ({ what, where }: { + bindsTo = [ "unifi.service" ]; + partOf = [ "unifi.service" ]; + unitConfig.RequiresMountsFor = stateDir; + options = "bind"; + what = what; + where = where; + }) mountPoints; + + systemd.tmpfiles.rules = [ + "d '${stateDir}' 0700 unifi - - -" + "d '${stateDir}/data' 0700 unifi - - -" + "d '${stateDir}/webapps' 0700 unifi - - -" + "L+ '${stateDir}/webapps/ROOT' - - - - ${cfg.unifiPackage}/webapps/ROOT" + ]; + + systemd.services.unifi = { + description = "UniFi controller daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ] ++ systemdMountPoints; + partOf = systemdMountPoints; + bindsTo = systemdMountPoints; + unitConfig.RequiresMountsFor = stateDir; + # This a HACK to fix missing dependencies of dynamic libs extracted from jars + environment.LD_LIBRARY_PATH = with pkgs.stdenv; "${cc.cc.lib}/lib"; + + serviceConfig = { + Type = "simple"; + ExecStart = "${(removeSuffix "\n" cmd)} start"; + ExecStop = "${(removeSuffix "\n" cmd)} stop"; + Restart = "on-failure"; + User = "unifi"; + UMask = "0077"; + WorkingDirectory = "${stateDir}"; + }; + }; + + }; + + meta.maintainers = with lib.maintainers; [ erictapen ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/v2ray.nix b/nixpkgs/nixos/modules/services/networking/v2ray.nix new file mode 100644 index 000000000000..6a924a16449a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/v2ray.nix @@ -0,0 +1,87 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + options = { + + services.v2ray = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to run v2ray server. + + Either <literal>configFile</literal> or <literal>config</literal> must be specified. + ''; + }; + + configFile = mkOption { + type = types.nullOr types.str; + default = null; + example = "/etc/v2ray/config.json"; + description = '' + The absolute path to the configuration file. + + Either <literal>configFile</literal> or <literal>config</literal> must be specified. + + See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>. + ''; + }; + + config = mkOption { + type = types.nullOr (types.attrsOf types.unspecified); + default = null; + example = { + inbounds = [{ + port = 1080; + listen = "127.0.0.1"; + protocol = "http"; + }]; + outbounds = [{ + protocol = "freedom"; + }]; + }; + description = '' + The configuration object. + + Either `configFile` or `config` must be specified. + + See <link xlink:href="https://v2ray.com/en/configuration/overview.html"/>. + ''; + }; + }; + + }; + + config = let + cfg = config.services.v2ray; + configFile = if cfg.configFile != null + then cfg.configFile + else pkgs.writeTextFile { + name = "v2ray.json"; + text = builtins.toJSON cfg.config; + checkPhase = '' + ${pkgs.v2ray}/bin/v2ray -test -config $out + ''; + }; + + in mkIf cfg.enable { + assertions = [ + { + assertion = (cfg.configFile == null) != (cfg.config == null); + message = "Either but not both `configFile` and `config` should be specified for v2ray."; + } + ]; + + systemd.services.v2ray = { + description = "v2ray Daemon"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.v2ray ]; + script = '' + exec v2ray -config ${configFile} + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/vsftpd.nix b/nixpkgs/nixos/modules/services/networking/vsftpd.nix new file mode 100644 index 000000000000..c57994533c17 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/vsftpd.nix @@ -0,0 +1,326 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + /* minimal secure setup: + + enable = true; + forceLocalLoginsSSL = true; + forceLocalDataSSL = true; + userlistDeny = false; + localUsers = true; + userlist = ["non-root-user" "other-non-root-user"]; + rsaCertFile = "/var/vsftpd/vsftpd.pem"; + + */ + + cfg = config.services.vsftpd; + + inherit (pkgs) vsftpd; + + yesNoOption = nixosName: vsftpdName: default: description: { + cfgText = "${vsftpdName}=${if getAttr nixosName cfg then "YES" else "NO"}"; + + nixosOption = { + type = types.bool; + name = nixosName; + value = mkOption { + inherit description default; + type = types.bool; + }; + }; + }; + + optionDescription = [ + (yesNoOption "allowWriteableChroot" "allow_writeable_chroot" false '' + Allow the use of writeable root inside chroot(). + '') + (yesNoOption "virtualUseLocalPrivs" "virtual_use_local_privs" false '' + If enabled, virtual users will use the same privileges as local + users. By default, virtual users will use the same privileges as + anonymous users, which tends to be more restrictive (especially + in terms of write access). + '') + (yesNoOption "anonymousUser" "anonymous_enable" false '' + Whether to enable the anonymous FTP user. + '') + (yesNoOption "anonymousUserNoPassword" "no_anon_password" false '' + Whether to disable the password for the anonymous FTP user. + '') + (yesNoOption "localUsers" "local_enable" false '' + Whether to enable FTP for local users. + '') + (yesNoOption "writeEnable" "write_enable" false '' + Whether any write activity is permitted to users. + '') + (yesNoOption "anonymousUploadEnable" "anon_upload_enable" false '' + Whether any uploads are permitted to anonymous users. + '') + (yesNoOption "anonymousMkdirEnable" "anon_mkdir_write_enable" false '' + Whether any uploads are permitted to anonymous users. + '') + (yesNoOption "chrootlocalUser" "chroot_local_user" false '' + Whether local users are confined to their home directory. + '') + (yesNoOption "userlistEnable" "userlist_enable" false '' + Whether users are included. + '') + (yesNoOption "userlistDeny" "userlist_deny" false '' + Specifies whether <option>userlistFile</option> is a list of user + names to allow or deny access. + The default <literal>false</literal> means whitelist/allow. + '') + (yesNoOption "forceLocalLoginsSSL" "force_local_logins_ssl" false '' + Only applies if <option>sslEnable</option> is true. Non anonymous (local) users + must use a secure SSL connection to send a password. + '') + (yesNoOption "forceLocalDataSSL" "force_local_data_ssl" false '' + Only applies if <option>sslEnable</option> is true. Non anonymous (local) users + must use a secure SSL connection for sending/receiving data on data connection. + '') + (yesNoOption "portPromiscuous" "port_promiscuous" false '' + Set to YES if you want to disable the PORT security check that ensures that + outgoing data connections can only connect to the client. Only enable if you + know what you are doing! + '') + (yesNoOption "ssl_tlsv1" "ssl_tlsv1" true '' + Only applies if <option>ssl_enable</option> is activated. If + enabled, this option will permit TLS v1 protocol connections. + TLS v1 connections are preferred. + '') + (yesNoOption "ssl_sslv2" "ssl_sslv2" false '' + Only applies if <option>ssl_enable</option> is activated. If + enabled, this option will permit SSL v2 protocol connections. + TLS v1 connections are preferred. + '') + (yesNoOption "ssl_sslv3" "ssl_sslv3" false '' + Only applies if <option>ssl_enable</option> is activated. If + enabled, this option will permit SSL v3 protocol connections. + TLS v1 connections are preferred. + '') + ]; + + configFile = pkgs.writeText "vsftpd.conf" + '' + ${concatMapStrings (x: "${x.cfgText}\n") optionDescription} + ${optionalString (cfg.rsaCertFile != null) '' + ssl_enable=YES + rsa_cert_file=${cfg.rsaCertFile} + ''} + ${optionalString (cfg.rsaKeyFile != null) '' + rsa_private_key_file=${cfg.rsaKeyFile} + ''} + ${optionalString (cfg.userlistFile != null) '' + userlist_file=${cfg.userlistFile} + ''} + background=YES + listen=NO + listen_ipv6=YES + nopriv_user=vsftpd + secure_chroot_dir=/var/empty + ${optionalString (cfg.localRoot != null) '' + local_root=${cfg.localRoot} + ''} + syslog_enable=YES + ${optionalString (pkgs.stdenv.hostPlatform.system == "x86_64-linux") '' + seccomp_sandbox=NO + ''} + anon_umask=${cfg.anonymousUmask} + ${optionalString cfg.anonymousUser '' + anon_root=${cfg.anonymousUserHome} + ''} + ${optionalString cfg.enableVirtualUsers '' + guest_enable=YES + guest_username=vsftpd + ''} + pam_service_name=vsftpd + ${cfg.extraConfig} + ''; + +in + +{ + + ###### interface + + options = { + + services.vsftpd = { + + enable = mkEnableOption "vsftpd"; + + userlist = mkOption { + default = []; + description = "See <option>userlistFile</option>."; + }; + + userlistFile = mkOption { + type = types.path; + default = pkgs.writeText "userlist" (concatMapStrings (x: "${x}\n") cfg.userlist); + defaultText = "pkgs.writeText \"userlist\" (concatMapStrings (x: \"\${x}\n\") cfg.userlist)"; + description = '' + Newline separated list of names to be allowed/denied if <option>userlistEnable</option> + is <literal>true</literal>. Meaning see <option>userlistDeny</option>. + + The default is a file containing the users from <option>userlist</option>. + + If explicitely set to null userlist_file will not be set in vsftpd's config file. + ''; + }; + + enableVirtualUsers = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enable the <literal>pam_userdb</literal>-based + virtual user system + ''; + }; + + userDbPath = mkOption { + type = types.nullOr types.str; + example = "/etc/vsftpd/userDb"; + default = null; + description = '' + Only applies if <option>enableVirtualUsers</option> is true. + Path pointing to the <literal>pam_userdb</literal> user + database used by vsftpd to authenticate the virtual users. + + This user list should be stored in the Berkeley DB database + format. + + To generate a new user database, create a text file, add + your users using the following format: + <programlisting> + user1 + password1 + user2 + password2 + </programlisting> + + You can then install <literal>pkgs.db</literal> to generate + the Berkeley DB using + <programlisting> + db_load -T -t hash -f logins.txt userDb.db + </programlisting> + + Caution: <literal>pam_userdb</literal> will automatically + append a <literal>.db</literal> suffix to the filename you + provide though this option. This option shouldn't include + this filetype suffix. + ''; + }; + + localRoot = mkOption { + type = types.nullOr types.str; + default = null; + example = "/var/www/$USER"; + description = '' + This option represents a directory which vsftpd will try to + change into after a local (i.e. non- anonymous) login. + + Failure is silently ignored. + ''; + }; + + anonymousUserHome = mkOption { + type = types.path; + default = "/home/ftp/"; + description = '' + Directory to consider the HOME of the anonymous user. + ''; + }; + + rsaCertFile = mkOption { + type = types.nullOr types.path; + default = null; + description = "RSA certificate file."; + }; + + rsaKeyFile = mkOption { + type = types.nullOr types.path; + default = null; + description = "RSA private key file."; + }; + + anonymousUmask = mkOption { + type = types.str; + default = "077"; + example = "002"; + description = "Anonymous write umask."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = "ftpd_banner=Hello"; + description = "Extra configuration to add at the bottom of the generated configuration file."; + }; + + } // (listToAttrs (catAttrs "nixosOption" optionDescription)); + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + assertions = [ + { assertion = + (cfg.forceLocalLoginsSSL -> cfg.rsaCertFile != null) + && (cfg.forceLocalDataSSL -> cfg.rsaCertFile != null); + message = "vsftpd: If forceLocalLoginsSSL or forceLocalDataSSL is true then a rsaCertFile must be provided!"; + } + { + assertion = (cfg.enableVirtualUsers -> cfg.userDbPath != null) + && (cfg.enableVirtualUsers -> cfg.localUsers != null); + message = "vsftpd: If enableVirtualUsers is true, you need to setup both the userDbPath and localUsers options."; + }]; + + users.users = { + "vsftpd" = { + uid = config.ids.uids.vsftpd; + description = "VSFTPD user"; + home = if cfg.localRoot != null + then cfg.localRoot # <= Necessary for virtual users. + else "/homeless-shelter"; + }; + } // optionalAttrs cfg.anonymousUser { + "ftp" = { name = "ftp"; + uid = config.ids.uids.ftp; + group = "ftp"; + description = "Anonymous FTP user"; + home = cfg.anonymousUserHome; + }; + }; + + users.groups.ftp.gid = config.ids.gids.ftp; + + # If you really have to access root via FTP use mkOverride or userlistDeny + # = false and whitelist root + services.vsftpd.userlist = if cfg.userlistDeny then ["root"] else []; + + systemd = { + tmpfiles.rules = optional cfg.anonymousUser + #Type Path Mode User Gr Age Arg + "d '${builtins.toString cfg.anonymousUserHome}' 0555 'ftp' 'ftp' - -"; + services.vsftpd = { + description = "Vsftpd Server"; + + wantedBy = [ "multi-user.target" ]; + + serviceConfig.ExecStart = "@${vsftpd}/sbin/vsftpd vsftpd ${configFile}"; + serviceConfig.Restart = "always"; + serviceConfig.Type = "forking"; + }; + }; + + security.pam.services.vsftpd.text = mkIf (cfg.enableVirtualUsers && cfg.userDbPath != null)'' + auth required pam_userdb.so db=${cfg.userDbPath} + account required pam_userdb.so db=${cfg.userDbPath} + ''; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/wakeonlan.nix b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix new file mode 100644 index 000000000000..ebfba263cd8f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/wakeonlan.nix @@ -0,0 +1,56 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + interfaces = config.services.wakeonlan.interfaces; + + ethtool = "${pkgs.ethtool}/sbin/ethtool"; + + passwordParameter = password : if (password == "") then "" else + "sopass ${password}"; + + methodParameter = {method, password} : + if method == "magicpacket" then "wol g" + else if method == "password" then "wol s so ${passwordParameter password}" + else throw "Wake-On-Lan method not supported"; + + line = { interface, method ? "magicpacket", password ? "" }: '' + ${ethtool} -s ${interface} ${methodParameter {inherit method password;}} + ''; + + concatStrings = fold (x: y: x + y) ""; + lines = concatStrings (map (l: line l) interfaces); + +in +{ + + ###### interface + + options = { + + services.wakeonlan.interfaces = mkOption { + default = [ ]; + example = [ + { + interface = "eth0"; + method = "password"; + password = "00:11:22:33:44:55"; + } + ]; + description = '' + Interfaces where to enable Wake-On-LAN, and how. Two methods available: + "magicpacket" and "password". The password has the shape of six bytes + in hexadecimal separated by a colon each. For more information, + check the ethtool manual. + ''; + }; + + }; + + + ###### implementation + + config.powerManagement.powerDownCommands = lines; + +} diff --git a/nixpkgs/nixos/modules/services/networking/websockify.nix b/nixpkgs/nixos/modules/services/networking/websockify.nix new file mode 100644 index 000000000000..d9177df65bd6 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/websockify.nix @@ -0,0 +1,54 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let cfg = config.services.networking.websockify; in { + options = { + services.networking.websockify = { + enable = mkOption { + description = "Whether to enable websockify to forward websocket connections to TCP connections."; + + default = false; + + type = types.bool; + }; + + sslCert = mkOption { + description = "Path to the SSL certificate."; + type = types.path; + }; + + sslKey = mkOption { + description = "Path to the SSL key."; + default = cfg.sslCert; + defaultText = "config.services.networking.websockify.sslCert"; + type = types.path; + }; + + portMap = mkOption { + description = "Ports to map by default."; + default = {}; + type = types.attrsOf types.int; + }; + }; + }; + + config = mkIf cfg.enable { + systemd.services."websockify@" = { + description = "Service to forward websocket connections to TCP connections (from port:to port %I)"; + script = '' + IFS=':' read -a array <<< "$1" + ${pkgs.pythonPackages.websockify}/bin/websockify --ssl-only \ + --cert=${cfg.sslCert} --key=${cfg.sslKey} 0.0.0.0:''${array[0]} 0.0.0.0:''${array[1]} + ''; + scriptArgs = "%i"; + }; + + systemd.targets.default-websockify = { + description = "Target to start all default websockify@ services"; + unitConfig.X-StopOnReconfiguration = true; + wants = mapAttrsToList (name: value: "websockify@${name}:${toString value}.service") cfg.portMap; + wantedBy = [ "multi-user.target" ]; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/wg-quick.nix b/nixpkgs/nixos/modules/services/networking/wg-quick.nix new file mode 100644 index 000000000000..ff1bdeed9f48 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/wg-quick.nix @@ -0,0 +1,312 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.networking.wg-quick; + + kernel = config.boot.kernelPackages; + + # interface options + + interfaceOpts = { ... }: { + options = { + address = mkOption { + example = [ "192.168.2.1/24" ]; + default = []; + type = with types; listOf str; + description = "The IP addresses of the interface."; + }; + + dns = mkOption { + example = [ "192.168.2.2" ]; + default = []; + type = with types; listOf str; + description = "The IP addresses of DNS servers to configure."; + }; + + privateKey = mkOption { + example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk="; + type = with types; nullOr str; + default = null; + description = '' + Base64 private key generated by wg genkey. + + Warning: Consider using privateKeyFile instead if you do not + want to store the key in the world-readable Nix store. + ''; + }; + + privateKeyFile = mkOption { + example = "/private/wireguard_key"; + type = with types; nullOr str; + default = null; + description = '' + Private key file as generated by wg genkey. + ''; + }; + + listenPort = mkOption { + default = null; + type = with types; nullOr int; + example = 51820; + description = '' + 16-bit port for listening. Optional; if not specified, + automatically generated based on interface name. + ''; + }; + + preUp = mkOption { + example = literalExample '' + ${pkgs.iproute}/bin/ip netns add foo + ''; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = '' + Commands called at the start of the interface setup. + ''; + }; + + preDown = mkOption { + example = literalExample '' + ${pkgs.iproute}/bin/ip netns del foo + ''; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = '' + Command called before the interface is taken down. + ''; + }; + + postUp = mkOption { + example = literalExample '' + ${pkgs.iproute}/bin/ip netns add foo + ''; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = '' + Commands called after the interface setup. + ''; + }; + + postDown = mkOption { + example = literalExample '' + ${pkgs.iproute}/bin/ip netns del foo + ''; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = '' + Command called after the interface is taken down. + ''; + }; + + table = mkOption { + example = "main"; + default = null; + type = with types; nullOr str; + description = '' + The kernel routing table to add this interface's + associated routes to. Setting this is useful for e.g. policy routing + ("ip rule") or virtual routing and forwarding ("ip vrf"). Both numeric + table IDs and table names (/etc/rt_tables) can be used. Defaults to + "main". + ''; + }; + + mtu = mkOption { + example = 1248; + default = null; + type = with types; nullOr int; + description = '' + If not specified, the MTU is automatically determined + from the endpoint addresses or the system default route, which is usually + a sane choice. However, to manually specify an MTU to override this + automatic discovery, this value may be specified explicitly. + ''; + }; + + peers = mkOption { + default = []; + description = "Peers linked to the interface."; + type = with types; listOf (submodule peerOpts); + }; + }; + }; + + # peer options + + peerOpts = { + options = { + publicKey = mkOption { + example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="; + type = types.str; + description = "The base64 public key the peer."; + }; + + presharedKey = mkOption { + default = null; + example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I="; + type = with types; nullOr str; + description = '' + Base64 preshared key generated by wg genpsk. Optional, + and may be omitted. This option adds an additional layer of + symmetric-key cryptography to be mixed into the already existing + public-key cryptography, for post-quantum resistance. + + Warning: Consider using presharedKeyFile instead if you do not + want to store the key in the world-readable Nix store. + ''; + }; + + presharedKeyFile = mkOption { + default = null; + example = "/private/wireguard_psk"; + type = with types; nullOr str; + description = '' + File pointing to preshared key as generated by wg pensk. Optional, + and may be omitted. This option adds an additional layer of + symmetric-key cryptography to be mixed into the already existing + public-key cryptography, for post-quantum resistance. + ''; + }; + + allowedIPs = mkOption { + example = [ "10.192.122.3/32" "10.192.124.1/24" ]; + type = with types; listOf str; + description = ''List of IP (v4 or v6) addresses with CIDR masks from + which this peer is allowed to send incoming traffic and to which + outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may + be specified for matching all IPv4 addresses, and ::/0 may be specified + for matching all IPv6 addresses.''; + }; + + endpoint = mkOption { + default = null; + example = "demo.wireguard.io:12913"; + type = with types; nullOr str; + description = ''Endpoint IP or hostname of the peer, followed by a colon, + and then a port number of the peer.''; + }; + + persistentKeepalive = mkOption { + default = null; + type = with types; nullOr int; + example = 25; + description = ''This is optional and is by default off, because most + users will not need it. It represents, in seconds, between 1 and 65535 + inclusive, how often to send an authenticated empty packet to the peer, + for the purpose of keeping a stateful firewall or NAT mapping valid + persistently. For example, if the interface very rarely sends traffic, + but it might at anytime receive traffic from a peer, and it is behind + NAT, the interface might benefit from having a persistent keepalive + interval of 25 seconds; however, most users will not need this.''; + }; + }; + }; + + writeScriptFile = name: text: ((pkgs.writeShellScriptBin name text) + "/bin/${name}"); + + generateUnit = name: values: + assert assertMsg ((values.privateKey != null) != (values.privateKeyFile != null)) "Only one of privateKey or privateKeyFile may be set"; + let + preUpFile = if values.preUp != "" then writeScriptFile "preUp.sh" values.preUp else null; + postUp = + optional (values.privateKeyFile != null) "wg set ${name} private-key <(cat ${values.privateKeyFile})" ++ + (concatMap (peer: optional (peer.presharedKeyFile != null) "wg set ${name} peer ${peer.publicKey} preshared-key <(cat ${peer.presharedKeyFile})") values.peers) ++ + optional (values.postUp != null) values.postUp; + postUpFile = if postUp != [] then writeScriptFile "postUp.sh" (concatMapStringsSep "\n" (line: line) postUp) else null; + preDownFile = if values.preDown != "" then writeScriptFile "preDown.sh" values.preDown else null; + postDownFile = if values.postDown != "" then writeScriptFile "postDown.sh" values.postDown else null; + configDir = pkgs.writeTextFile { + name = "config-${name}"; + executable = false; + destination = "/${name}.conf"; + text = + '' + [interface] + ${concatMapStringsSep "\n" (address: + "Address = ${address}" + ) values.address} + ${concatMapStringsSep "\n" (dns: + "DNS = ${dns}" + ) values.dns} + '' + + optionalString (values.table != null) "Table = ${values.table}\n" + + optionalString (values.mtu != null) "MTU = ${toString values.mtu}\n" + + optionalString (values.privateKey != null) "PrivateKey = ${values.privateKey}\n" + + optionalString (values.listenPort != null) "ListenPort = ${toString values.listenPort}\n" + + optionalString (preUpFile != null) "PreUp = ${preUpFile}\n" + + optionalString (postUpFile != null) "PostUp = ${postUpFile}\n" + + optionalString (preDownFile != null) "PreDown = ${preDownFile}\n" + + optionalString (postDownFile != null) "PostDown = ${postDownFile}\n" + + concatMapStringsSep "\n" (peer: + assert assertMsg (!((peer.presharedKeyFile != null) && (peer.presharedKey != null))) "Only one of presharedKey or presharedKeyFile may be set"; + "[Peer]\n" + + "PublicKey = ${peer.publicKey}\n" + + optionalString (peer.presharedKey != null) "PresharedKey = ${peer.presharedKey}\n" + + optionalString (peer.endpoint != null) "Endpoint = ${peer.endpoint}\n" + + optionalString (peer.persistentKeepalive != null) "PersistentKeepalive = ${toString peer.persistentKeepalive}\n" + + optionalString (peer.allowedIPs != []) "AllowedIPs = ${concatStringsSep "," peer.allowedIPs}\n" + ) values.peers; + }; + configPath = "${configDir}/${name}.conf"; + in + nameValuePair "wg-quick-${name}" + { + description = "wg-quick WireGuard Tunnel - ${name}"; + requires = [ "network-online.target" ]; + after = [ "network.target" "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + environment.DEVICE = name; + path = [ pkgs.kmod pkgs.wireguard-tools ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = '' + ${optionalString (!config.boot.isContainer) "modprobe wireguard"} + wg-quick up ${configPath} + ''; + + preStop = '' + wg-quick down ${configPath} + ''; + }; +in { + + ###### interface + + options = { + networking.wg-quick = { + interfaces = mkOption { + description = "Wireguard interfaces."; + default = {}; + example = { + wg0 = { + address = [ "192.168.20.4/24" ]; + privateKey = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk="; + peers = [ + { allowedIPs = [ "192.168.20.1/32" ]; + publicKey = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="; + endpoint = "demo.wireguard.io:12913"; } + ]; + }; + }; + type = with types; attrsOf (submodule interfaceOpts); + }; + }; + }; + + + ###### implementation + + config = mkIf (cfg.interfaces != {}) { + boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard; + environment.systemPackages = [ pkgs.wireguard-tools ]; + # This is forced to false for now because the default "--validmark" rpfilter we apply on reverse path filtering + # breaks the wg-quick routing because wireguard packets leave with a fwmark from wireguard. + networking.firewall.checkReversePath = false; + systemd.services = mapAttrs' generateUnit cfg.interfaces; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/wicd.nix b/nixpkgs/nixos/modules/services/networking/wicd.nix new file mode 100644 index 000000000000..aa10a50f876a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/wicd.nix @@ -0,0 +1,40 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + + ###### interface + + options = { + + networking.wicd.enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to start <command>wicd</command>. Wired and + wireless network configurations can then be managed by + wicd-client. + ''; + }; + }; + + + ###### implementation + + config = mkIf config.networking.wicd.enable { + + environment.systemPackages = [pkgs.wicd]; + + systemd.services.wicd = { + after = [ "network-pre.target" ]; + before = [ "network.target" ]; + wants = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + script = "${pkgs.wicd}/sbin/wicd -f"; + }; + + services.dbus.enable = true; + services.dbus.packages = [pkgs.wicd]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/wireguard.nix b/nixpkgs/nixos/modules/services/networking/wireguard.nix new file mode 100644 index 000000000000..e8f83f6dd8bf --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/wireguard.nix @@ -0,0 +1,452 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.networking.wireguard; + + kernel = config.boot.kernelPackages; + + # interface options + + interfaceOpts = { ... }: { + + options = { + + ips = mkOption { + example = [ "192.168.2.1/24" ]; + default = []; + type = with types; listOf str; + description = "The IP addresses of the interface."; + }; + + privateKey = mkOption { + example = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk="; + type = with types; nullOr str; + default = null; + description = '' + Base64 private key generated by <command>wg genkey</command>. + + Warning: Consider using privateKeyFile instead if you do not + want to store the key in the world-readable Nix store. + ''; + }; + + generatePrivateKeyFile = mkOption { + default = false; + type = types.bool; + description = '' + Automatically generate a private key with + <command>wg genkey</command>, at the privateKeyFile location. + ''; + }; + + privateKeyFile = mkOption { + example = "/private/wireguard_key"; + type = with types; nullOr str; + default = null; + description = '' + Private key file as generated by <command>wg genkey</command>. + ''; + }; + + listenPort = mkOption { + default = null; + type = with types; nullOr int; + example = 51820; + description = '' + 16-bit port for listening. Optional; if not specified, + automatically generated based on interface name. + ''; + }; + + preSetup = mkOption { + example = literalExample '' + ${pkgs.iproute}/bin/ip netns add foo + ''; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = '' + Commands called at the start of the interface setup. + ''; + }; + + postSetup = mkOption { + example = literalExample '' + printf "nameserver 10.200.100.1" | ${pkgs.openresolv}/bin/resolvconf -a wg0 -m 0 + ''; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = "Commands called at the end of the interface setup."; + }; + + postShutdown = mkOption { + example = literalExample "${pkgs.openresolv}/bin/resolvconf -d wg0"; + default = ""; + type = with types; coercedTo (listOf str) (concatStringsSep "\n") lines; + description = "Commands called after shutting down the interface."; + }; + + table = mkOption { + default = "main"; + type = types.str; + description = ''The kernel routing table to add this interface's + associated routes to. Setting this is useful for e.g. policy routing + ("ip rule") or virtual routing and forwarding ("ip vrf"). Both numeric + table IDs and table names (/etc/rt_tables) can be used. Defaults to + "main".''; + }; + + peers = mkOption { + default = []; + description = "Peers linked to the interface."; + type = with types; listOf (submodule peerOpts); + }; + + allowedIPsAsRoutes = mkOption { + example = false; + default = true; + type = types.bool; + description = '' + Determines whether to add allowed IPs as routes or not. + ''; + }; + + socketNamespace = mkOption { + default = null; + type = with types; nullOr str; + example = "container"; + description = ''The pre-existing network namespace in which the + WireGuard interface is created, and which retains the socket even if the + interface is moved via <option>interfaceNamespace</option>. When + <literal>null</literal>, the interface is created in the init namespace. + See <link + xlink:href="https://www.wireguard.com/netns/">documentation</link>. + ''; + }; + + interfaceNamespace = mkOption { + default = null; + type = with types; nullOr str; + example = "init"; + description = ''The pre-existing network namespace the WireGuard + interface is moved to. The special value <literal>init</literal> means + the init namespace. When <literal>null</literal>, the interface is not + moved. + See <link + xlink:href="https://www.wireguard.com/netns/">documentation</link>. + ''; + }; + }; + + }; + + # peer options + + peerOpts = { + + options = { + + publicKey = mkOption { + example = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="; + type = types.str; + description = "The base64 public key of the peer."; + }; + + presharedKey = mkOption { + default = null; + example = "rVXs/Ni9tu3oDBLS4hOyAUAa1qTWVA3loR8eL20os3I="; + type = with types; nullOr str; + description = '' + Base64 preshared key generated by <command>wg genpsk</command>. + Optional, and may be omitted. This option adds an additional layer of + symmetric-key cryptography to be mixed into the already existing + public-key cryptography, for post-quantum resistance. + + Warning: Consider using presharedKeyFile instead if you do not + want to store the key in the world-readable Nix store. + ''; + }; + + presharedKeyFile = mkOption { + default = null; + example = "/private/wireguard_psk"; + type = with types; nullOr str; + description = '' + File pointing to preshared key as generated by <command>wg pensk</command>. + Optional, and may be omitted. This option adds an additional layer of + symmetric-key cryptography to be mixed into the already existing + public-key cryptography, for post-quantum resistance. + ''; + }; + + allowedIPs = mkOption { + example = [ "10.192.122.3/32" "10.192.124.1/24" ]; + type = with types; listOf str; + description = ''List of IP (v4 or v6) addresses with CIDR masks from + which this peer is allowed to send incoming traffic and to which + outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may + be specified for matching all IPv4 addresses, and ::/0 may be specified + for matching all IPv6 addresses.''; + }; + + endpoint = mkOption { + default = null; + example = "demo.wireguard.io:12913"; + type = with types; nullOr str; + description = ''Endpoint IP or hostname of the peer, followed by a colon, + and then a port number of the peer.''; + }; + + persistentKeepalive = mkOption { + default = null; + type = with types; nullOr int; + example = 25; + description = ''This is optional and is by default off, because most + users will not need it. It represents, in seconds, between 1 and 65535 + inclusive, how often to send an authenticated empty packet to the peer, + for the purpose of keeping a stateful firewall or NAT mapping valid + persistently. For example, if the interface very rarely sends traffic, + but it might at anytime receive traffic from a peer, and it is behind + NAT, the interface might benefit from having a persistent keepalive + interval of 25 seconds; however, most users will not need this.''; + }; + + }; + + }; + + + generatePathUnit = name: values: + assert (values.privateKey == null); + assert (values.privateKeyFile != null); + nameValuePair "wireguard-${name}" + { + description = "WireGuard Tunnel - ${name} - Private Key"; + requiredBy = [ "wireguard-${name}.service" ]; + before = [ "wireguard-${name}.service" ]; + pathConfig.PathExists = values.privateKeyFile; + }; + + generateKeyServiceUnit = name: values: + assert values.generatePrivateKeyFile; + nameValuePair "wireguard-${name}-key" + { + description = "WireGuard Tunnel - ${name} - Key Generator"; + wantedBy = [ "wireguard-${name}.service" ]; + requiredBy = [ "wireguard-${name}.service" ]; + before = [ "wireguard-${name}.service" ]; + path = with pkgs; [ wireguard ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = '' + mkdir --mode 0644 -p "${dirOf values.privateKeyFile}" + if [ ! -f "${values.privateKeyFile}" ]; then + touch "${values.privateKeyFile}" + chmod 0600 "${values.privateKeyFile}" + wg genkey > "${values.privateKeyFile}" + chmod 0400 "${values.privateKeyFile}" + fi + ''; + }; + + generatePeerUnit = { interfaceName, interfaceCfg, peer }: + let + keyToUnitName = replaceChars + [ "/" "-" " " "+" "=" ] + [ "-" "\\x2d" "\\x20" "\\x2b" "\\x3d" ]; + unitName = keyToUnitName peer.publicKey; + psk = + if peer.presharedKey != null + then pkgs.writeText "wg-psk" peer.presharedKey + else peer.presharedKeyFile; + src = interfaceCfg.socketNamespace; + dst = interfaceCfg.interfaceNamespace; + ip = nsWrap "ip" src dst; + wg = nsWrap "wg" src dst; + in nameValuePair "wireguard-${interfaceName}-peer-${unitName}" + { + description = "WireGuard Peer - ${interfaceName} - ${peer.publicKey}"; + requires = [ "wireguard-${interfaceName}.service" ]; + after = [ "wireguard-${interfaceName}.service" ]; + wantedBy = [ "multi-user.target" "wireguard-${interfaceName}.service" ]; + environment.DEVICE = interfaceName; + environment.WG_ENDPOINT_RESOLUTION_RETRIES = "infinity"; + path = with pkgs; [ iproute wireguard-tools ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = let + wg_setup = "${wg} set ${interfaceName} peer ${peer.publicKey}" + + optionalString (psk != null) " preshared-key ${psk}" + + optionalString (peer.endpoint != null) " endpoint ${peer.endpoint}" + + optionalString (peer.persistentKeepalive != null) " persistent-keepalive ${toString peer.persistentKeepalive}" + + optionalString (peer.allowedIPs != []) " allowed-ips ${concatStringsSep "," peer.allowedIPs}"; + route_setup = + optionalString interfaceCfg.allowedIPsAsRoutes + (concatMapStringsSep "\n" + (allowedIP: + "${ip} route replace ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}" + ) peer.allowedIPs); + in '' + ${wg_setup} + ${route_setup} + ''; + + postStop = let + route_destroy = optionalString interfaceCfg.allowedIPsAsRoutes + (concatMapStringsSep "\n" + (allowedIP: + "${ip} route delete ${allowedIP} dev ${interfaceName} table ${interfaceCfg.table}" + ) peer.allowedIPs); + in '' + ${wg} set ${interfaceName} peer ${peer.publicKey} remove + ${route_destroy} + ''; + }; + + generateInterfaceUnit = name: values: + # exactly one way to specify the private key must be set + #assert (values.privateKey != null) != (values.privateKeyFile != null); + let privKey = if values.privateKeyFile != null then values.privateKeyFile else pkgs.writeText "wg-key" values.privateKey; + src = values.socketNamespace; + dst = values.interfaceNamespace; + ipPreMove = nsWrap "ip" src null; + ipPostMove = nsWrap "ip" src dst; + wg = nsWrap "wg" src dst; + ns = if dst == "init" then "1" else dst; + + in + nameValuePair "wireguard-${name}" + { + description = "WireGuard Tunnel - ${name}"; + requires = [ "network-online.target" ]; + after = [ "network.target" "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + environment.DEVICE = name; + path = with pkgs; [ kmod iproute wireguard-tools ]; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + + script = '' + ${optionalString (!config.boot.isContainer) "modprobe wireguard || true"} + + ${values.preSetup} + + ${ipPreMove} link add dev ${name} type wireguard + ${optionalString (values.interfaceNamespace != null && values.interfaceNamespace != values.socketNamespace) "${ipPreMove} link set ${name} netns ${ns}"} + + ${concatMapStringsSep "\n" (ip: + "${ipPostMove} address add ${ip} dev ${name}" + ) values.ips} + + ${wg} set ${name} private-key ${privKey} ${ + optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"} + + ${ipPostMove} link set up dev ${name} + + ${values.postSetup} + ''; + + postStop = '' + ${ipPostMove} link del dev ${name} + ${values.postShutdown} + ''; + }; + + nsWrap = cmd: src: dst: + let + nsList = filter (ns: ns != null) [ src dst ]; + ns = last nsList; + in + if (length nsList > 0 && ns != "init") then "ip netns exec ${ns} ${cmd}" else cmd; +in + +{ + + ###### interface + + options = { + + networking.wireguard = { + + enable = mkOption { + description = "Whether to enable WireGuard."; + type = types.bool; + # 2019-05-25: Backwards compatibility. + default = cfg.interfaces != {}; + example = true; + }; + + interfaces = mkOption { + description = "WireGuard interfaces."; + default = {}; + example = { + wg0 = { + ips = [ "192.168.20.4/24" ]; + privateKey = "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk="; + peers = [ + { allowedIPs = [ "192.168.20.1/32" ]; + publicKey = "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg="; + endpoint = "demo.wireguard.io:12913"; } + ]; + }; + }; + type = with types; attrsOf (submodule interfaceOpts); + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable (let + all_peers = flatten + (mapAttrsToList (interfaceName: interfaceCfg: + map (peer: { inherit interfaceName interfaceCfg peer;}) interfaceCfg.peers + ) cfg.interfaces); + in { + + assertions = (attrValues ( + mapAttrs (name: value: { + assertion = (value.privateKey != null) != (value.privateKeyFile != null); + message = "Either networking.wireguard.interfaces.${name}.privateKey or networking.wireguard.interfaces.${name}.privateKeyFile must be set."; + }) cfg.interfaces)) + ++ (attrValues ( + mapAttrs (name: value: { + assertion = value.generatePrivateKeyFile -> (value.privateKey == null); + message = "networking.wireguard.interfaces.${name}.generatePrivateKeyFile must not be set if networking.wireguard.interfaces.${name}.privateKey is set."; + }) cfg.interfaces)) + ++ map ({ interfaceName, peer, ... }: { + assertion = (peer.presharedKey == null) || (peer.presharedKeyFile == null); + message = "networking.wireguard.interfaces.${interfaceName} peer «${peer.publicKey}» has both presharedKey and presharedKeyFile set, but only one can be used."; + }) all_peers; + + boot.extraModulePackages = optional (versionOlder kernel.kernel.version "5.6") kernel.wireguard; + environment.systemPackages = [ pkgs.wireguard-tools ]; + + systemd.services = + (mapAttrs' generateInterfaceUnit cfg.interfaces) + // (listToAttrs (map generatePeerUnit all_peers)) + // (mapAttrs' generateKeyServiceUnit + (filterAttrs (name: value: value.generatePrivateKeyFile) cfg.interfaces)); + + systemd.paths = mapAttrs' generatePathUnit + (filterAttrs (name: value: value.privateKeyFile != null) cfg.interfaces); + + }); + +} diff --git a/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix new file mode 100644 index 000000000000..a7dea95056a0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix @@ -0,0 +1,266 @@ +{ config, lib, pkgs, utils, ... }: + +with lib; + +let + cfg = config.networking.wireless; + configFile = if cfg.networks != {} then pkgs.writeText "wpa_supplicant.conf" '' + ${optionalString cfg.userControlled.enable '' + ctrl_interface=DIR=/run/wpa_supplicant GROUP=${cfg.userControlled.group} + update_config=1''} + ${cfg.extraConfig} + ${concatStringsSep "\n" (mapAttrsToList (ssid: config: with config; let + key = if psk != null + then ''"${psk}"'' + else pskRaw; + baseAuth = if key != null + then ''psk=${key}'' + else ''key_mgmt=NONE''; + in '' + network={ + ssid="${ssid}" + ${optionalString (priority != null) ''priority=${toString priority}''} + ${optionalString hidden "scan_ssid=1"} + ${if (auth != null) then auth else baseAuth} + ${extraConfig} + } + '') cfg.networks)} + '' else "/etc/wpa_supplicant.conf"; +in { + options = { + networking.wireless = { + enable = mkEnableOption "wpa_supplicant"; + + interfaces = mkOption { + type = types.listOf types.str; + default = []; + example = [ "wlan0" "wlan1" ]; + description = '' + The interfaces <command>wpa_supplicant</command> will use. If empty, it will + automatically use all wireless interfaces. + ''; + }; + + driver = mkOption { + type = types.str; + default = "nl80211,wext"; + description = "Force a specific wpa_supplicant driver."; + }; + + networks = mkOption { + type = types.attrsOf (types.submodule { + options = { + psk = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The network's pre-shared key in plaintext defaulting + to being a network without any authentication. + + Be aware that these will be written to the nix store + in plaintext! + + Mutually exclusive with <varname>pskRaw</varname>. + ''; + }; + + pskRaw = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + The network's pre-shared key in hex defaulting + to being a network without any authentication. + + Mutually exclusive with <varname>psk</varname>. + ''; + }; + + auth = mkOption { + type = types.nullOr types.str; + default = null; + example = '' + key_mgmt=WPA-EAP + eap=PEAP + identity="user@example.com" + password="secret" + ''; + description = '' + Use this option to configure advanced authentication methods like EAP. + See + <citerefentry> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + for example configurations. + + Mutually exclusive with <varname>psk</varname> and <varname>pskRaw</varname>. + ''; + }; + + hidden = mkOption { + type = types.bool; + default = false; + description = '' + Set this to <literal>true</literal> if the SSID of the network is hidden. + ''; + example = literalExample '' + { echelon = { + hidden = true; + psk = "abcdefgh"; + }; + } + ''; + }; + + priority = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + By default, all networks will get same priority group (0). If some of the + networks are more desirable, this field can be used to change the order in + which wpa_supplicant goes through the networks when selecting a BSS. The + priority groups will be iterated in decreasing priority (i.e., the larger the + priority value, the sooner the network is matched against the scan results). + Within each priority group, networks will be selected based on security + policy, signal strength, etc. + ''; + }; + + extraConfig = mkOption { + type = types.str; + default = ""; + example = '' + bssid_blacklist=02:11:22:33:44:55 02:22:aa:44:55:66 + ''; + description = '' + Extra configuration lines appended to the network block. + See + <citerefentry> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + for available options. + ''; + }; + + }; + }); + description = '' + The network definitions to automatically connect to when + <command>wpa_supplicant</command> is running. If this + parameter is left empty wpa_supplicant will use + /etc/wpa_supplicant.conf as the configuration file. + ''; + default = {}; + example = literalExample '' + { echelon = { # SSID with no spaces or special characters + psk = "abcdefgh"; + }; + "echelon's AP" = { # SSID with spaces and/or special characters + psk = "ijklmnop"; + }; + "free.wifi" = {}; # Public wireless network + } + ''; + }; + + userControlled = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Allow normal users to control wpa_supplicant through wpa_gui or wpa_cli. + This is useful for laptop users that switch networks a lot and don't want + to depend on a large package such as NetworkManager just to pick nearby + access points. + + When using a declarative network specification you cannot persist any + settings via wpa_gui or wpa_cli. + ''; + }; + + group = mkOption { + type = types.str; + default = "wheel"; + example = "network"; + description = "Members of this group can control wpa_supplicant."; + }; + }; + extraConfig = mkOption { + type = types.str; + default = ""; + example = '' + p2p_disabled=1 + ''; + description = '' + Extra lines appended to the configuration file. + See + <citerefentry> + <refentrytitle>wpa_supplicant.conf</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + for available options. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = flip mapAttrsToList cfg.networks (name: cfg: { + assertion = with cfg; count (x: x != null) [ psk pskRaw auth ] <= 1; + message = ''options networking.wireless."${name}".{psk,pskRaw,auth} are mutually exclusive''; + }); + + environment.systemPackages = [ pkgs.wpa_supplicant ]; + + services.dbus.packages = [ pkgs.wpa_supplicant ]; + services.udev.packages = [ pkgs.crda ]; + + # FIXME: start a separate wpa_supplicant instance per interface. + systemd.services.wpa_supplicant = let + ifaces = cfg.interfaces; + deviceUnit = interface: [ "sys-subsystem-net-devices-${utils.escapeSystemdPath interface}.device" ]; + in { + description = "WPA Supplicant"; + + after = lib.concatMap deviceUnit ifaces; + before = [ "network.target" ]; + wants = [ "network.target" ]; + requires = lib.concatMap deviceUnit ifaces; + wantedBy = [ "multi-user.target" ]; + stopIfChanged = false; + + path = [ pkgs.wpa_supplicant ]; + + script = '' + iface_args="-s -u -D${cfg.driver} -c ${configFile}" + ${if ifaces == [] then '' + for i in $(cd /sys/class/net && echo *); do + DEVTYPE= + UEVENT_PATH=/sys/class/net/$i/uevent + if [ -e "$UEVENT_PATH" ]; then + source "$UEVENT_PATH" + if [ "$DEVTYPE" = "wlan" -o -e /sys/class/net/$i/wireless ]; then + args+="''${args:+ -N} -i$i $iface_args" + fi + fi + done + '' else '' + args="${concatMapStringsSep " -N " (i: "-i${i} $iface_args") ifaces}" + ''} + exec wpa_supplicant $args + ''; + }; + + powerManagement.resumeCommands = '' + /run/current-system/systemd/bin/systemctl try-restart wpa_supplicant + ''; + + # Restart wpa_supplicant when a wlan device appears or disappears. + services.udev.extraRules = '' + ACTION=="add|remove", SUBSYSTEM=="net", ENV{DEVTYPE}=="wlan", RUN+="/run/current-system/systemd/bin/systemctl try-restart wpa_supplicant.service" + ''; + }; + + meta.maintainers = with lib.maintainers; [ globin ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/xandikos.nix b/nixpkgs/nixos/modules/services/networking/xandikos.nix new file mode 100644 index 000000000000..87c029156b9e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/xandikos.nix @@ -0,0 +1,148 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.xandikos; +in +{ + + options = { + services.xandikos = { + enable = mkEnableOption "Xandikos CalDAV and CardDAV server"; + + package = mkOption { + type = types.package; + default = pkgs.xandikos; + defaultText = "pkgs.xandikos"; + description = "The Xandikos package to use."; + }; + + address = mkOption { + type = types.str; + default = "localhost"; + description = '' + The IP address on which Xandikos will listen. + By default listens on localhost. + ''; + }; + + port = mkOption { + type = types.port; + default = 8080; + description = "The port of the Xandikos web application"; + }; + + routePrefix = mkOption { + type = types.str; + default = "/"; + description = '' + Path to Xandikos. + Useful when Xandikos is behind a reverse proxy. + ''; + }; + + extraOptions = mkOption { + default = []; + type = types.listOf types.str; + example = literalExample '' + [ "--autocreate" + "--defaults" + "--current-user-principal user" + "--dump-dav-xml" + ] + ''; + description = '' + Extra command line arguments to pass to xandikos. + ''; + }; + + nginx = mkOption { + default = {}; + description = '' + Configuration for nginx reverse proxy. + ''; + + type = types.submodule { + options = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Configure the nginx reverse proxy settings. + ''; + }; + + hostName = mkOption { + type = types.str; + description = '' + The hostname use to setup the virtualhost configuration + ''; + }; + }; + }; + }; + + }; + + }; + + config = mkIf cfg.enable ( + mkMerge [ + { + meta.maintainers = [ lib.maintainers."0x4A6F" ]; + + systemd.services.xandikos = { + description = "A Simple Calendar and Contact Server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = "xandikos"; + Group = "xandikos"; + DynamicUser = "yes"; + RuntimeDirectory = "xandikos"; + StateDirectory = "xandikos"; + StateDirectoryMode = "0700"; + PrivateDevices = true; + # Sandboxing + CapabilityBoundingSet = "CAP_NET_RAW CAP_NET_ADMIN"; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictAddressFamilies = "AF_INET AF_INET6 AF_UNIX AF_PACKET AF_NETLINK"; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + ExecStart = '' + ${cfg.package}/bin/xandikos \ + --directory /var/lib/xandikos \ + --listen_address ${cfg.address} \ + --port ${toString cfg.port} \ + --route-prefix ${cfg.routePrefix} \ + ${lib.concatStringsSep " " cfg.extraOptions} + ''; + }; + }; + } + + ( + mkIf cfg.nginx.enable { + services.nginx = { + enable = true; + virtualHosts."${cfg.nginx.hostName}" = { + locations."/" = { + proxyPass = "http://${cfg.address}:${toString cfg.port}/"; + }; + }; + }; + } + ) + ] + ); +} diff --git a/nixpkgs/nixos/modules/services/networking/xinetd.nix b/nixpkgs/nixos/modules/services/networking/xinetd.nix new file mode 100644 index 000000000000..2f527ab156aa --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/xinetd.nix @@ -0,0 +1,147 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.xinetd; + + configFile = pkgs.writeText "xinetd.conf" + '' + defaults + { + log_type = SYSLOG daemon info + log_on_failure = HOST + log_on_success = PID HOST DURATION EXIT + ${cfg.extraDefaults} + } + + ${concatMapStrings makeService cfg.services} + ''; + + makeService = srv: + '' + service ${srv.name} + { + protocol = ${srv.protocol} + ${optionalString srv.unlisted "type = UNLISTED"} + ${optionalString (srv.flags != "") "flags = ${srv.flags}"} + socket_type = ${if srv.protocol == "udp" then "dgram" else "stream"} + ${if srv.port != 0 then "port = ${toString srv.port}" else ""} + wait = ${if srv.protocol == "udp" then "yes" else "no"} + user = ${srv.user} + server = ${srv.server} + ${optionalString (srv.serverArgs != "") "server_args = ${srv.serverArgs}"} + ${srv.extraConfig} + } + ''; + +in + +{ + + ###### interface + + options = { + + services.xinetd.enable = mkEnableOption "the xinetd super-server daemon"; + + services.xinetd.extraDefaults = mkOption { + default = ""; + type = types.lines; + description = '' + Additional configuration lines added to the default section of xinetd's configuration. + ''; + }; + + services.xinetd.services = mkOption { + default = []; + description = '' + A list of services provided by xinetd. + ''; + + type = with types; listOf (submodule ({ + + options = { + + name = mkOption { + type = types.str; + example = "login"; + description = "Name of the service."; + }; + + protocol = mkOption { + type = types.str; + default = "tcp"; + description = + "Protocol of the service. Usually <literal>tcp</literal> or <literal>udp</literal>."; + }; + + port = mkOption { + type = types.int; + default = 0; + example = 123; + description = "Port number of the service."; + }; + + user = mkOption { + type = types.str; + default = "nobody"; + description = "User account for the service"; + }; + + server = mkOption { + type = types.str; + example = "/foo/bin/ftpd"; + description = "Path of the program that implements the service."; + }; + + serverArgs = mkOption { + type = types.separatedString " "; + default = ""; + description = "Command-line arguments for the server program."; + }; + + flags = mkOption { + type = types.str; + default = ""; + description = ""; + }; + + unlisted = mkOption { + type = types.bool; + default = false; + description = '' + Whether this server is listed in + <filename>/etc/services</filename>. If so, the port + number can be omitted. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = "Extra configuration-lines added to the section of the service."; + }; + + }; + + })); + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + systemd.services.xinetd = { + description = "xinetd server"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.xinetd ]; + script = "exec xinetd -syslog daemon -dontfork -stayalive -f ${configFile}"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/xl2tpd.nix b/nixpkgs/nixos/modules/services/networking/xl2tpd.nix new file mode 100644 index 000000000000..7dbe51422d96 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/xl2tpd.nix @@ -0,0 +1,143 @@ +{ config, pkgs, lib, ... }: + +with lib; + +{ + options = { + services.xl2tpd = { + enable = mkEnableOption "xl2tpd, the Layer 2 Tunnelling Protocol Daemon"; + + serverIp = mkOption { + type = types.str; + description = "The server-side IP address."; + default = "10.125.125.1"; + }; + + clientIpRange = mkOption { + type = types.str; + description = "The range from which client IPs are drawn."; + default = "10.125.125.2-11"; + }; + + extraXl2tpOptions = mkOption { + type = types.lines; + description = "Adds extra lines to the xl2tpd configuration file."; + default = ""; + }; + + extraPppdOptions = mkOption { + type = types.lines; + description = "Adds extra lines to the pppd options file."; + default = ""; + example = '' + ms-dns 8.8.8.8 + ms-dns 8.8.4.4 + ''; + }; + }; + }; + + config = mkIf config.services.xl2tpd.enable { + systemd.services.xl2tpd = let + cfg = config.services.xl2tpd; + + # Config files from https://help.ubuntu.com/community/L2TPServer + xl2tpd-conf = pkgs.writeText "xl2tpd.conf" '' + [global] + ipsec saref = no + + [lns default] + local ip = ${cfg.serverIp} + ip range = ${cfg.clientIpRange} + pppoptfile = ${pppd-options} + length bit = yes + + ; Extra + ${cfg.extraXl2tpOptions} + ''; + + pppd-options = pkgs.writeText "ppp-options-xl2tpd.conf" '' + refuse-pap + refuse-chap + refuse-mschap + require-mschap-v2 + # require-mppe-128 + asyncmap 0 + auth + crtscts + idle 1800 + mtu 1200 + mru 1200 + lock + hide-password + local + # debug + name xl2tpd + # proxyarp + lcp-echo-interval 30 + lcp-echo-failure 4 + + # Extra: + ${cfg.extraPppdOptions} + ''; + + xl2tpd-ppp-wrapped = pkgs.stdenv.mkDerivation { + name = "xl2tpd-ppp-wrapped"; + phases = [ "installPhase" ]; + buildInputs = with pkgs; [ makeWrapper ]; + installPhase = '' + mkdir -p $out/bin + + makeWrapper ${pkgs.ppp}/sbin/pppd $out/bin/pppd \ + --set LD_PRELOAD "${pkgs.libredirect}/lib/libredirect.so" \ + --set NIX_REDIRECTS "/etc/ppp=/etc/xl2tpd/ppp" + + makeWrapper ${pkgs.xl2tpd}/bin/xl2tpd $out/bin/xl2tpd \ + --set LD_PRELOAD "${pkgs.libredirect}/lib/libredirect.so" \ + --set NIX_REDIRECTS "${pkgs.ppp}/sbin/pppd=$out/bin/pppd" + ''; + }; + in { + description = "xl2tpd server"; + + requires = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + preStart = '' + mkdir -p -m 700 /etc/xl2tpd + + pushd /etc/xl2tpd > /dev/null + + mkdir -p -m 700 ppp + + [ -f ppp/chap-secrets ] || cat > ppp/chap-secrets << EOF + # Secrets for authentication using CHAP + # client server secret IP addresses + #username xl2tpd password * + EOF + + chown root.root ppp/chap-secrets + chmod 600 ppp/chap-secrets + + # The documentation says this file should be present but doesn't explain why and things work even if not there: + [ -f l2tp-secrets ] || (echo -n "* * "; ${pkgs.apg}/bin/apg -n 1 -m 32 -x 32 -a 1 -M LCN) > l2tp-secrets + chown root.root l2tp-secrets + chmod 600 l2tp-secrets + + popd > /dev/null + + mkdir -p /run/xl2tpd + chown root.root /run/xl2tpd + chmod 700 /run/xl2tpd + ''; + + serviceConfig = { + ExecStart = "${xl2tpd-ppp-wrapped}/bin/xl2tpd -D -c ${xl2tpd-conf} -s /etc/xl2tpd/l2tp-secrets -p /run/xl2tpd/pid -C /run/xl2tpd/control"; + KillMode = "process"; + Restart = "on-success"; + Type = "simple"; + PIDFile = "/run/xl2tpd/pid"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/xrdp.nix b/nixpkgs/nixos/modules/services/networking/xrdp.nix new file mode 100644 index 000000000000..b7dd1c5d99dd --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/xrdp.nix @@ -0,0 +1,171 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.xrdp; + confDir = pkgs.runCommand "xrdp.conf" { preferLocalBuild = true; } '' + mkdir $out + + cp ${cfg.package}/etc/xrdp/{km-*,xrdp,sesman,xrdp_keyboard}.ini $out + + cat > $out/startwm.sh <<EOF + #!/bin/sh + . /etc/profile + ${cfg.defaultWindowManager} + EOF + chmod +x $out/startwm.sh + + substituteInPlace $out/xrdp.ini \ + --replace "#rsakeys_ini=" "rsakeys_ini=/run/xrdp/rsakeys.ini" \ + --replace "certificate=" "certificate=${cfg.sslCert}" \ + --replace "key_file=" "key_file=${cfg.sslKey}" \ + --replace LogFile=xrdp.log LogFile=/dev/null \ + --replace EnableSyslog=true EnableSyslog=false + + substituteInPlace $out/sesman.ini \ + --replace LogFile=xrdp-sesman.log LogFile=/dev/null \ + --replace EnableSyslog=1 EnableSyslog=0 + + # Ensure that clipboard works for non-ASCII characters + sed -i -e '/.*SessionVariables.*/ a\ + LANG=${config.i18n.defaultLocale}\ + LOCALE_ARCHIVE=${config.i18n.glibcLocales}/lib/locale/locale-archive + ' $out/sesman.ini + ''; +in +{ + + ###### interface + + options = { + + services.xrdp = { + + enable = mkEnableOption "xrdp, the Remote Desktop Protocol server"; + + package = mkOption { + type = types.package; + default = pkgs.xrdp; + defaultText = "pkgs.xrdp"; + description = '' + The package to use for the xrdp daemon's binary. + ''; + }; + + port = mkOption { + type = types.int; + default = 3389; + description = '' + Specifies on which port the xrdp daemon listens. + ''; + }; + + sslKey = mkOption { + type = types.str; + default = "/etc/xrdp/key.pem"; + example = "/path/to/your/key.pem"; + description = '' + ssl private key path + A self-signed certificate will be generated if file not exists. + ''; + }; + + sslCert = mkOption { + type = types.str; + default = "/etc/xrdp/cert.pem"; + example = "/path/to/your/cert.pem"; + description = '' + ssl certificate path + A self-signed certificate will be generated if file not exists. + ''; + }; + + defaultWindowManager = mkOption { + type = types.str; + default = "xterm"; + example = "xfce4-session"; + description = '' + The script to run when user log in, usually a window manager, e.g. "icewm", "xfce4-session" + This is per-user overridable, if file ~/startwm.sh exists it will be used instead. + ''; + }; + + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + # xrdp can run X11 program even if "services.xserver.enable = false" + xdg = { + autostart.enable = true; + menus.enable = true; + mime.enable = true; + icons.enable = true; + }; + + fonts.enableDefaultFonts = mkDefault true; + + systemd = { + services.xrdp = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "xrdp daemon"; + requires = [ "xrdp-sesman.service" ]; + preStart = '' + # prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp) + mkdir -p /tmp/.xrdp || true + chown xrdp:xrdp /tmp/.xrdp + chmod 3777 /tmp/.xrdp + + # generate a self-signed certificate + if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then + mkdir -p $(dirname ${cfg.sslCert}) || true + mkdir -p $(dirname ${cfg.sslKey}) || true + ${pkgs.openssl.bin}/bin/openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \ + -subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \ + -config ${cfg.package}/share/xrdp/openssl.conf \ + -keyout ${cfg.sslKey} -out ${cfg.sslCert} + chown root:xrdp ${cfg.sslKey} ${cfg.sslCert} + chmod 440 ${cfg.sslKey} ${cfg.sslCert} + fi + if [ ! -s /run/xrdp/rsakeys.ini ]; then + mkdir -p /run/xrdp + ${cfg.package}/bin/xrdp-keygen xrdp /run/xrdp/rsakeys.ini + fi + ''; + serviceConfig = { + User = "xrdp"; + Group = "xrdp"; + PermissionsStartOnly = true; + ExecStart = "${cfg.package}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${confDir}/xrdp.ini"; + }; + }; + + services.xrdp-sesman = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + description = "xrdp session manager"; + restartIfChanged = false; # do not restart on "nixos-rebuild switch". like "display-manager", it can have many interactive programs as children + serviceConfig = { + ExecStart = "${cfg.package}/bin/xrdp-sesman --nodaemon --config ${confDir}/sesman.ini"; + ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; + }; + }; + + }; + + users.users.xrdp = { + description = "xrdp daemon user"; + isSystemUser = true; + group = "xrdp"; + }; + users.groups.xrdp = {}; + + security.pam.services.xrdp-sesman = { allowNullPassword = true; startSession = true; }; + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/yggdrasil.nix b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix new file mode 100644 index 000000000000..0fe9a200a1b6 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/yggdrasil.nix @@ -0,0 +1,199 @@ +{ config, lib, pkgs, ... }: +with lib; +let + keysPath = "/var/lib/yggdrasil/keys.json"; + + cfg = config.services.yggdrasil; + configProvided = cfg.config != { }; + configFileProvided = cfg.configFile != null; + +in { + options = with types; { + services.yggdrasil = { + enable = mkEnableOption "the yggdrasil system service"; + + config = mkOption { + type = attrs; + default = {}; + example = { + Peers = [ + "tcp://aa.bb.cc.dd:eeeee" + "tcp://[aaaa:bbbb:cccc:dddd::eeee]:fffff" + ]; + Listen = [ + "tcp://0.0.0.0:xxxxx" + ]; + }; + description = '' + Configuration for yggdrasil, as a Nix attribute set. + + Warning: this is stored in the WORLD-READABLE Nix store! + Therefore, it is not appropriate for private keys. If you + wish to specify the keys, use <option>configFile</option>. + + If the <option>persistentKeys</option> is enabled then the + keys that are generated during activation will override + those in <option>config</option> or + <option>configFile</option>. + + If no keys are specified then ephemeral keys are generated + and the Yggdrasil interface will have a random IPv6 address + each time the service is started, this is the default. + + If both <option>configFile</option> and <option>config</option> + are supplied, they will be combined, with values from + <option>configFile</option> taking precedence. + + You can use the command <code>nix-shell -p yggdrasil --run + "yggdrasil -genconf"</code> to generate default + configuration values with documentation. + ''; + }; + + configFile = mkOption { + type = nullOr path; + default = null; + example = "/run/keys/yggdrasil.conf"; + description = '' + A file which contains JSON configuration for yggdrasil. + See the <option>config</option> option for more information. + ''; + }; + + group = mkOption { + type = types.str; + default = "root"; + example = "wheel"; + description = "Group to grant acces to the Yggdrasil control socket."; + }; + + openMulticastPort = mkOption { + type = bool; + default = false; + description = '' + Whether to open the UDP port used for multicast peer + discovery. The NixOS firewall blocks link-local + communication, so in order to make local peering work you + will also need to set <code>LinkLocalTCPPort</code> in your + yggdrasil configuration (<option>config</option> or + <option>configFile</option>) to a port number other than 0, + and then add that port to + <option>networking.firewall.allowedTCPPorts</option>. + ''; + }; + + denyDhcpcdInterfaces = mkOption { + type = listOf str; + default = []; + example = [ "tap*" ]; + description = '' + Disable the DHCP client for any interface whose name matches + any of the shell glob patterns in this list. Use this + option to prevent the DHCP client from broadcasting requests + on the yggdrasil network. It is only necessary to do so + when yggdrasil is running in TAP mode, because TUN + interfaces do not support broadcasting. + ''; + }; + + package = mkOption { + type = package; + default = pkgs.yggdrasil; + defaultText = "pkgs.yggdrasil"; + description = "Yggdrasil package to use."; + }; + + persistentKeys = mkEnableOption '' + If enabled then keys will be generated once and Yggdrasil + will retain the same IPv6 address when the service is + restarted. Keys are stored at ${keysPath}. + ''; + + }; + }; + + config = mkIf cfg.enable (let binYggdrasil = cfg.package + "/bin/yggdrasil"; + in { + assertions = [{ + assertion = config.networking.enableIPv6; + message = "networking.enableIPv6 must be true for yggdrasil to work"; + }]; + + system.activationScripts.yggdrasil = mkIf cfg.persistentKeys '' + if [ ! -e ${keysPath} ] + then + mkdir -p ${builtins.dirOf keysPath} + ${binYggdrasil} -genconf -json \ + | ${pkgs.jq}/bin/jq \ + 'to_entries|map(select(.key|endswith("Key")))|from_entries' \ + > ${keysPath} + chmod 600 ${keysPath} + fi + ''; + + systemd.services.yggdrasil = { + description = "Yggdrasil Network Service"; + bindsTo = [ "network-online.target" ]; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + + preStart = + (if configProvided || configFileProvided || cfg.persistentKeys then + "echo " + + + (lib.optionalString configProvided + "'${builtins.toJSON cfg.config}'") + + (lib.optionalString configFileProvided "$(cat ${cfg.configFile})") + + (lib.optionalString cfg.persistentKeys "$(cat ${keysPath})") + + " | ${pkgs.jq}/bin/jq -s add | ${binYggdrasil} -normaliseconf -useconf" + else + "${binYggdrasil} -genconf") + " > /run/yggdrasil/yggdrasil.conf"; + + serviceConfig = { + ExecStart = + "${binYggdrasil} -useconffile /run/yggdrasil/yggdrasil.conf"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + Restart = "always"; + + Group = cfg.group; + RuntimeDirectory = "yggdrasil"; + RuntimeDirectoryMode = "0750"; + BindReadOnlyPaths = lib.optional configFileProvided cfg.configFile + ++ lib.optional cfg.persistentKeys keysPath; + + # TODO: as of yggdrasil 0.3.8 and systemd 243, yggdrasil fails + # to set up the network adapter when DynamicUser is set. See + # github.com/yggdrasil-network/yggdrasil-go/issues/557. The + # following options are implied by DynamicUser according to + # the systemd.exec documentation, and can be removed if the + # upstream issue is fixed and DynamicUser is set to true: + PrivateTmp = true; + RemoveIPC = true; + NoNewPrivileges = true; + ProtectSystem = "strict"; + RestrictSUIDSGID = true; + # End of list of options implied by DynamicUser. + + AmbientCapabilities = "CAP_NET_ADMIN"; + CapabilityBoundingSet = "CAP_NET_ADMIN"; + MemoryDenyWriteExecute = true; + ProtectControlGroups = true; + ProtectHome = "tmpfs"; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK"; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @resources"; + }; + }; + + networking.dhcpcd.denyInterfaces = cfg.denyDhcpcdInterfaces; + networking.firewall.allowedUDPPorts = mkIf cfg.openMulticastPort [ 9001 ]; + + # Make yggdrasilctl available on the command line. + environment.systemPackages = [ cfg.package ]; + }); + meta.maintainers = with lib.maintainers; [ gazally ehmry ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/zerobin.nix b/nixpkgs/nixos/modules/services/networking/zerobin.nix new file mode 100644 index 000000000000..78de246a816f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/zerobin.nix @@ -0,0 +1,102 @@ +{ config, pkgs, lib, ... }: +with lib; +let + cfg = config.services.zerobin; + + zerobin_config = pkgs.writeText "zerobin-config.py" '' + PASTE_FILES_ROOT = "${cfg.dataDir}" + ${cfg.extraConfig} + ''; + +in + { + options = { + services.zerobin = { + enable = mkEnableOption "0bin"; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/zerobin"; + description = '' + Path to the 0bin data directory + ''; + }; + + user = mkOption { + type = types.str; + default = "zerobin"; + description = '' + The user 0bin should run as + ''; + }; + + group = mkOption { + type = types.str; + default = "zerobin"; + description = '' + The group 0bin should run as + ''; + }; + + listenPort = mkOption { + type = types.int; + default = 8000; + example = 1357; + description = '' + The port zerobin should listen on + ''; + }; + + listenAddress = mkOption { + type = types.str; + default = "localhost"; + example = "127.0.0.1"; + description = '' + The address zerobin should listen to + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + MENU = ( + ('Home', '/'), + ) + COMPRESSED_STATIC_FILE = True + ''; + description = '' + Extra configuration to be appended to the 0bin config file + (see https://0bin.readthedocs.org/en/latest/en/options.html) + ''; + }; + }; + }; + + config = mkIf (cfg.enable) { + users.users.${cfg.user} = + if cfg.user == "zerobin" then { + isSystemUser = true; + group = cfg.group; + home = cfg.dataDir; + createHome = true; + } + else {}; + users.groups.${cfg.group} = {}; + + systemd.services.zerobin = { + enable = true; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig.ExecStart = "${pkgs.pythonPackages.zerobin}/bin/zerobin ${cfg.listenAddress} ${toString cfg.listenPort} false ${cfg.user} ${cfg.group} ${zerobin_config}"; + serviceConfig.PrivateTmp="yes"; + serviceConfig.User = cfg.user; + serviceConfig.Group = cfg.group; + preStart = '' + mkdir -p ${cfg.dataDir} + chown ${cfg.user} ${cfg.dataDir} + ''; + }; + }; + } + diff --git a/nixpkgs/nixos/modules/services/networking/zeronet.nix b/nixpkgs/nixos/modules/services/networking/zeronet.nix new file mode 100644 index 000000000000..f354a9d42c79 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/zeronet.nix @@ -0,0 +1,96 @@ +{ config, lib, pkgs, ... }: + +let + inherit (lib) generators literalExample mkEnableOption mkIf mkOption recursiveUpdate types; + cfg = config.services.zeronet; + dataDir = "/var/lib/zeronet"; + configFile = pkgs.writeText "zeronet.conf" (generators.toINI {} (recursiveUpdate defaultSettings cfg.settings)); + + defaultSettings = { + global = { + data_dir = dataDir; + log_dir = dataDir; + ui_port = cfg.port; + fileserver_port = cfg.fileserverPort; + tor = if !cfg.tor then "disable" else if cfg.torAlways then "always" else "enable"; + }; + }; +in with lib; { + options.services.zeronet = { + enable = mkEnableOption "zeronet"; + + settings = mkOption { + type = with types; attrsOf (oneOf [ str int bool (listOf str) ]); + default = {}; + example = literalExample "global.tor = enable;"; + + description = '' + <filename>zeronet.conf</filename> configuration. Refer to + <link xlink:href="https://zeronet.readthedocs.io/en/latest/faq/#is-it-possible-to-use-a-configuration-file"/> + for details on supported values; + ''; + }; + + port = mkOption { + type = types.int; + default = 43110; + example = 43110; + description = "Optional zeronet web UI port."; + }; + + fileserverPort = mkOption { + # Not optional: when absent zeronet tries to write one to the + # read-only config file and crashes + type = types.int; + default = 12261; + example = 12261; + description = "Zeronet fileserver port."; + }; + + tor = mkOption { + type = types.bool; + default = false; + description = "Use TOR for zeronet traffic where possible."; + }; + + torAlways = mkOption { + type = types.bool; + default = false; + description = "Use TOR for all zeronet traffic."; + }; + }; + + config = mkIf cfg.enable { + services.tor = mkIf cfg.tor { + enable = true; + controlPort = 9051; + + extraConfig = '' + CacheDirectoryGroupReadable 1 + CookieAuthentication 1 + CookieAuthFileGroupReadable 1 + ''; + }; + + systemd.services.zeronet = { + description = "zeronet"; + after = [ "network.target" (optionalString cfg.tor "tor.service") ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = "zeronet"; + DynamicUser = true; + StateDirectory = "zeronet"; + SupplementaryGroups = mkIf cfg.tor [ "tor" ]; + ExecStart = "${pkgs.zeronet}/bin/zeronet --config_file ${configFile}"; + }; + }; + }; + + imports = [ + (mkRemovedOptionModule [ "services" "zeronet" "dataDir" ] "Zeronet will store data by default in /var/lib/zeronet") + (mkRemovedOptionModule [ "services" "zeronet" "logDir" ] "Zeronet will log by default in /var/lib/zeronet") + ]; + + meta.maintainers = with maintainers; [ chiiruno ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/zerotierone.nix b/nixpkgs/nixos/modules/services/networking/zerotierone.nix new file mode 100644 index 000000000000..cf39ed065a76 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/zerotierone.nix @@ -0,0 +1,82 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.zerotierone; +in +{ + options.services.zerotierone.enable = mkEnableOption "ZeroTierOne"; + + options.services.zerotierone.joinNetworks = mkOption { + default = []; + example = [ "a8a2c3c10c1a68de" ]; + type = types.listOf types.str; + description = '' + List of ZeroTier Network IDs to join on startup + ''; + }; + + options.services.zerotierone.port = mkOption { + default = 9993; + example = 9993; + type = types.int; + description = '' + Network port used by ZeroTier. + ''; + }; + + options.services.zerotierone.package = mkOption { + default = pkgs.zerotierone; + defaultText = "pkgs.zerotierone"; + type = types.package; + description = '' + ZeroTier One package to use. + ''; + }; + + config = mkIf cfg.enable { + systemd.services.zerotierone = { + description = "ZeroTierOne"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + wants = [ "network-online.target" ]; + + path = [ cfg.package ]; + + preStart = '' + mkdir -p /var/lib/zerotier-one/networks.d + chmod 700 /var/lib/zerotier-one + chown -R root:root /var/lib/zerotier-one + '' + (concatMapStrings (netId: '' + touch "/var/lib/zerotier-one/networks.d/${netId}.conf" + '') cfg.joinNetworks); + serviceConfig = { + ExecStart = "${cfg.package}/bin/zerotier-one -p${toString cfg.port}"; + Restart = "always"; + KillMode = "process"; + TimeoutStopSec = 5; + }; + }; + + # ZeroTier does not issue DHCP leases, but some strangers might... + networking.dhcpcd.denyInterfaces = [ "zt*" ]; + + # ZeroTier receives UDP transmissions + networking.firewall.allowedUDPPorts = [ cfg.port ]; + + environment.systemPackages = [ cfg.package ]; + + # Prevent systemd from potentially changing the MAC address + systemd.network.links."50-zerotier" = { + matchConfig = { + OriginalName = "zt*"; + }; + linkConfig = { + AutoNegotiation = false; + MACAddressPolicy = "none"; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/znc/default.nix b/nixpkgs/nixos/modules/services/networking/znc/default.nix new file mode 100644 index 000000000000..a7315896c506 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/znc/default.nix @@ -0,0 +1,308 @@ +{ 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: optionals (value.${subname} != null) ([ + "<${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 (oneOf [ int bool str ]); + zncAttr = attrsOf (nullOr zncConf); + zncAll = oneOf [ 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 + <link xlink:href="https://wiki.znc.in/Configuration"/> 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 = lib.getVersion pkgs.znc; + 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 = optionalAttrs (cfg.user == defaultUser) { + ${defaultUser} = + { description = "ZNC server daemon owner"; + group = defaultUser; + uid = config.ids.uids.znc; + home = cfg.dataDir; + createHome = true; + }; + }; + + users.groups = optionalAttrs (cfg.user == defaultUser) { + ${defaultUser} = + { gid = config.ids.gids.znc; + members = [ defaultUser ]; + }; + }; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/znc/options.nix b/nixpkgs/nixos/modules/services/networking/znc/options.nix new file mode 100644 index 000000000000..048dbd738630 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/znc/options.nix @@ -0,0 +1,270 @@ +{ 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; + URIPrefix = c.uriPrefix; + }; + 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 ]; + }; + extraConfig = 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 ...";`. + '') + ]; +} |