diff options
Diffstat (limited to 'nixpkgs/nixos/modules/services/networking')
198 files changed, 30493 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/services/networking/amuled.nix b/nixpkgs/nixos/modules/services/networking/amuled.nix new file mode 100644 index 000000000000..57f02542eafd --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/amuled.nix @@ -0,0 +1,76 @@ +{ 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 { + 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..53829bf18863 --- /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.string; + 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.string; + 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.string; + 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 = [ "local-fs.target" "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..a098a155e991 --- /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.string; + example = "socks-peer"; + description = "Name of the local AutoSSH session"; + }; + user = mkOption { + type = types.string; + 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.string; + 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..ddcfe3d77e2f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/avahi-daemon.nix @@ -0,0 +1,281 @@ +{ 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; + + 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..3dfd80f6ff52 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/babeld.nix @@ -0,0 +1,100 @@ +{ 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 = mkOption { + default = false; + description = '' + Whether to run 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..06af4dbcca4e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/bind.nix @@ -0,0 +1,207 @@ +{ 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 = mkOption { + default = false; + description = " + Whether to enable BIND domain name server. + "; + }; + + cacheNetworks = mkOption { + default = ["127.0.0.0/24"]; + description = " + What networks are allowed to use us as a resolver. + "; + }; + + 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 = singleton + { name = 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..d3501636b41d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/bitcoind.nix @@ -0,0 +1,195 @@ +{ 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.altcoins.bitcoind; + defaultText = "pkgs.altcoins.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"; + + # Permission for preStart + PermissionsStartOnly = "true"; + }; + }; + users.users.${cfg.user} = { + name = cfg.user; + group = cfg.group; + description = "Bitcoin daemon user"; + home = cfg.dataDir; + }; + 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..274b36171608 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/bitlbee.nix @@ -0,0 +1,194 @@ +{ 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 { + 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 = singleton { + name = "bitlbee"; + uid = bitlbeeUid; + description = "BitlBee user"; + home = "/var/lib/bitlbee"; + createHome = true; + }; + + users.groups = singleton { + name = "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..e3aba063f87b --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/charybdis.nix @@ -0,0 +1,109 @@ +{ 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.string; + description = '' + Charybdis IRC daemon configuration file. + ''; + }; + + statedir = mkOption { + type = types.string; + default = "/var/lib/charybdis"; + description = '' + Location of the state directory of charybdis. + ''; + }; + + user = mkOption { + type = types.string; + default = "ircd"; + description = '' + Charybdis IRC daemon user. + ''; + }; + + group = mkOption { + type = types.string; + 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 = singleton { + name = cfg.user; + description = "Charybdis IRC daemon user"; + uid = config.ids.uids.ircd; + group = cfg.group; + }; + + users.groups = singleton { + name = 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/chrony.nix b/nixpkgs/nixos/modules/services/networking/chrony.nix new file mode 100644 index 000000000000..77f702577000 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/chrony.nix @@ -0,0 +1,129 @@ +{ 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) cfg.servers} + + ${optionalString + (cfg.initstepslew.enabled && (cfg.servers != [])) + "initstepslew ${toString cfg.initstepslew.threshold} ${concatStringsSep " " cfg.initstepslew.servers}" + } + + driftfile ${stateDir}/chrony.drift + keyfile ${keyFile} + + ${optionalString (!config.time.hardwareClockInLocalTime) "rtconutc"} + + ${cfg.extraConfig} + ''; + + chronyFlags = "-m -u chrony -f ${configFile} ${toString cfg.extraFlags}"; +in +{ + options = { + services.chrony = { + enable = mkOption { + 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) + servers = cfg.servers; + }; + 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 { + environment.systemPackages = [ pkgs.chrony ]; + + users.groups = singleton + { name = "chrony"; + gid = config.ids.gids.chrony; + }; + + users.users = singleton + { name = "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.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 ]; + + preStart = '' + mkdir -m 0755 -p ${stateDir} + touch ${keyFile} + chmod 0640 ${keyFile} + chown chrony:chrony ${stateDir} ${keyFile} + ''; + + unitConfig.ConditionCapability = "CAP_SYS_TIME"; + serviceConfig = + { Type = "forking"; + ExecStart = "${pkgs.chrony}/bin/chronyd ${chronyFlags}"; + + ProtectHome = "yes"; + ProtectSystem = "full"; + PrivateTmp = "yes"; + + }; + + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/cjdns.nix b/nixpkgs/nixos/modules/services/networking/cjdns.nix new file mode 100644 index 000000000000..3fb85b16cbe2 --- /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 = import (pkgs.runCommand "cjdns-hosts" {} + # Generate a builder that produces an output usable as a Nix string value + '' + exec >$out + echo \'\' + ${concatStringsSep "\n" (mapAttrsToList (k: v: + optionalString (v.hostname != "") + "echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}") + (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo))} + echo \'\' + ''); + + 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 = { + "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 = { + "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.extraHosts = 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..4e4e3104c3a8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/cntlm.nix @@ -0,0 +1,126 @@ +{ 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 = mkOption { + default = false; + description = '' + Whether to enable the cntlm, which start 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..c3ca6fbe725e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/connman.nix @@ -0,0 +1,128 @@ +{ config, lib, pkgs, ... }: + +with pkgs; +with lib; + +let + cfg = config.networking.connman; + configFile = pkgs.writeText "connman.conf" '' + [General] + NetworkInterfaceBlacklist=${concatStringsSep "," cfg.networkInterfaceBlacklist} + + ${cfg.extraConfig} + ''; +in { + + ###### interface + + options = { + + networking.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 string; + default = [ "vmnet" "vboxnet" "virbr" "ifb" "ve" ]; + description = '' + Default blacklisted interfaces, this includes NixOS containers interfaces (ve). + ''; + }; + + extraFlags = mkOption { + type = with types; listOf string; + default = [ ]; + example = [ "--nodnsproxy" ]; + description = '' + Extra flags to pass to connmand + ''; + }; + + }; + + }; + + ###### implementation + + config = mkIf cfg.enable { + + assertions = [{ + assertion = !config.networking.useDHCP; + message = "You can not use services.networking.connman with services.networking.useDHCP"; + }{ + assertion = config.networking.wireless.enable; + message = "You must use services.networking.connman with services.networking.wireless"; + }{ + assertion = !config.networking.networkmanager.enable; + message = "You can not use services.networking.connman with services.networking.networkmanager"; + }]; + + environment.systemPackages = [ connman ]; + + systemd.services."connman" = { + description = "Connection service"; + wantedBy = [ "multi-user.target" ]; + after = [ "syslog.target" ]; + serviceConfig = { + Type = "dbus"; + BusName = "net.connman"; + Restart = "on-failure"; + ExecStart = "${pkgs.connman}/sbin/connmand --config=${configFile} --nodaemon ${toString 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 = "${pkgs.connman}/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 = "${pkgs.connman}/sbin/connman-vpnd -n"; + User = "root"; + SystemdService = "connman-vpn.service"; + }; + }; + + networking = { + useDHCP = false; + wireless.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..f080f12eaccd --- /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}/bin/consul consul agent -config-dir /etc/consul.d" + + concatMapStrings (n: " -config-file ${n}") configFiles; + ExecReload = "${cfg.package.bin}/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}/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}/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/coturn.nix b/nixpkgs/nixos/modules/services/networking/coturn.nix new file mode 100644 index 000000000000..c430ce5af92a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/coturn.nix @@ -0,0 +1,336 @@ +{ 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 = [ + { name = "turnserver"; + uid = config.ids.uids.turnserver; + description = "coturn TURN server user"; + } ]; + users.groups = [ + { name = "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..04ce5ca3a874 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ddclient.nix @@ -0,0 +1,201 @@ +{ 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; + +{ + + ###### 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..7b2786034552 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dhcpcd.nix @@ -0,0 +1,202 @@ +{ 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)) + ++ concatLists (attrValues (mapAttrs (n: v: v.interfaces) 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}"} + + ${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. + ${config.systemd.package}/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. + ''; + }; + + }; + + + ###### 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" ]; + + # 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 -w --quiet ${optionalString cfg.persistent "--persistent"} --config ${dhcpcdConf}"; + ExecReload = "${dhcpcd}/sbin/dhcpcd --rebind"; + Restart = "always"; + }; + }; + + environment.systemPackages = [ dhcpcd ]; + + environment.etc = + [ { source = exitHook; + target = "dhcpcd.exit-hook"; + } + ]; + + powerManagement.resumeCommands = mkIf config.systemd.services.dhcpcd.enable + '' + # Tell dhcpcd to rebind its interfaces if it's running. + ${config.systemd.package}/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..0b2063bc4246 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dhcpd.nix @@ -0,0 +1,210 @@ +{ 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 + +{ + + ###### 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..5051fc916d96 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnscache.nix @@ -0,0 +1,106 @@ +{ 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 = { + "@" = ["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 = {}; + + 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/dnschain.nix b/nixpkgs/nixos/modules/services/networking/dnschain.nix new file mode 100644 index 000000000000..0c2add424bac --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnschain.nix @@ -0,0 +1,177 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfgs = config.services; + cfg = cfgs.dnschain; + + dataDir = "/var/lib/dnschain"; + username = "dnschain"; + + configFile = pkgs.writeText "dnschain.conf" '' + [log] + level = info + + [dns] + host = ${cfg.dns.address} + port = ${toString cfg.dns.port} + oldDNSMethod = NO_OLD_DNS + externalIP = ${cfg.dns.externalAddress} + + [http] + host = ${cfg.api.hostname} + port = ${toString cfg.api.port} + tlsPort = ${toString cfg.api.tlsPort} + + ${cfg.extraConfig} + ''; + +in + +{ + + ###### interface + + options = { + + services.dnschain = { + + enable = mkEnableOption '' + DNSChain, a blockchain based DNS + HTTP server. + To resolve .bit domains set <literal>services.namecoind.enable = true;</literal> + and an RPC username/password. + ''; + + dns.address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = '' + The IP address the DNSChain resolver will bind to. + Leave this unchanged if you do not wish to directly expose the resolver. + ''; + }; + + dns.externalAddress = mkOption { + type = types.str; + default = cfg.dns.address; + description = '' + The IP address used by clients to reach the resolver and the value of + the <literal>namecoin.dns</literal> record. Set this in case the bind address + is not the actual IP address (e.g. the machine is behind a NAT). + ''; + }; + + dns.port = mkOption { + type = types.int; + default = 5333; + description = '' + The port the DNSChain resolver will bind to. + ''; + }; + + api.hostname = mkOption { + type = types.str; + default = "0.0.0.0"; + description = '' + The hostname (or IP address) the DNSChain API server will bind to. + ''; + }; + + api.port = mkOption { + type = types.int; + default = 8080; + description = '' + The port the DNSChain API server (HTTP) will bind to. + ''; + }; + + api.tlsPort = mkOption { + type = types.int; + default = 4433; + description = '' + The port the DNSChain API server (HTTPS) will bind to. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + example = '' + [log] + level = debug + ''; + description = '' + Additional options that will be appended to the configuration file. + ''; + }; + + }; + + services.dnsmasq.resolveDNSChainQueries = mkOption { + type = types.bool; + default = false; + description = '' + Resolve <literal>.bit</literal> top-level domains using DNSChain and namecoin. + ''; + }; + + services.pdns-recursor.resolveDNSChainQueries = mkOption { + type = types.bool; + default = false; + description = '' + Resolve <literal>.bit</literal> top-level domains using DNSChain and namecoin. + ''; + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + services.dnsmasq.servers = optionals cfgs.dnsmasq.resolveDNSChainQueries + [ "/.bit/127.0.0.1#${toString cfg.dns.port}" + "/.dns/127.0.0.1#${toString cfg.dns.port}" + ]; + + services.pdns-recursor.forwardZones = mkIf cfgs.pdns-recursor.resolveDNSChainQueries + { bit = "127.0.0.1:${toString cfg.dns.port}"; + dns = "127.0.0.1:${toString cfg.dns.port}"; + }; + + users.users = singleton { + name = username; + description = "DNSChain daemon user"; + home = dataDir; + createHome = true; + uid = config.ids.uids.dnschain; + extraGroups = optional cfgs.namecoind.enable "namecoin"; + }; + + systemd.services.dnschain = { + description = "DNSChain daemon"; + after = optional cfgs.namecoind.enable "namecoind.target"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = "dnschain"; + Restart = "on-failure"; + ExecStart = "${pkgs.nodePackages.dnschain}/bin/dnschain"; + }; + + preStart = '' + # Link configuration file into dnschain home directory + configPath=${dataDir}/.dnschain/dnschain.conf + mkdir -p ${dataDir}/.dnschain + if [ "$(realpath $configPath)" != "${configFile}" ]; then + rm -f $configPath + ln -s ${configFile} $configPath + fi + ''; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy.nix b/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy.nix new file mode 100644 index 000000000000..8edcf925dbfa --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy.nix @@ -0,0 +1,328 @@ +{ config, lib, pkgs, ... }: +with lib; + +let + cfg = config.services.dnscrypt-proxy; + + stateDirectory = "/var/lib/dnscrypt-proxy"; + + # The minisign public key used to sign the upstream resolver list. + # This is somewhat more flexible than preloading the key as an + # embedded string. + upstreamResolverListPubKey = pkgs.fetchurl { + url = https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/minisign.pub; + sha256 = "18lnp8qr6ghfc2sd46nn1rhcpr324fqlvgsp4zaigw396cd7vnnh"; + }; + + # Internal flag indicating whether the upstream resolver list is used. + useUpstreamResolverList = cfg.customResolver == null; + + # The final local address. + localAddress = "${cfg.localAddress}:${toString cfg.localPort}"; + + # The final resolvers list path. + resolverList = "${stateDirectory}/dnscrypt-resolvers.csv"; + + # Build daemon command line + + resolverArgs = + if (cfg.customResolver == null) + then + [ "-L ${resolverList}" + "-R ${cfg.resolverName}" + ] + else with cfg.customResolver; + [ "-N ${name}" + "-k ${key}" + "-r ${address}:${toString port}" + ]; + + daemonArgs = + [ "-a ${localAddress}" ] + ++ resolverArgs + ++ cfg.extraArgs; +in + +{ + meta = { + maintainers = with maintainers; [ joachifm ]; + doc = ./dnscrypt-proxy.xml; + }; + + options = { + # Before adding another option, consider whether it could + # equally well be passed via extraArgs. + + services.dnscrypt-proxy = { + enable = mkOption { + default = false; + type = types.bool; + description = "Whether to enable the DNSCrypt client proxy"; + }; + + localAddress = mkOption { + default = "127.0.0.1"; + type = types.str; + description = '' + Listen for DNS queries to relay on this address. The only reason to + change this from its default value is to proxy queries on behalf + of other machines (typically on the local network). + ''; + }; + + localPort = mkOption { + default = 53; + type = types.int; + description = '' + Listen for DNS queries to relay on this port. The default value + assumes that the DNSCrypt proxy should relay DNS queries directly. + When running as a forwarder for another DNS client, set this option + to a different value; otherwise leave the default. + ''; + }; + + resolverName = mkOption { + default = "random"; + example = "dnscrypt.eu-nl"; + type = types.nullOr types.str; + description = '' + The name of the DNSCrypt resolver to use, taken from + <filename>${resolverList}</filename>. The default is to + pick a random non-logging resolver that supports DNSSEC. + ''; + }; + + customResolver = mkOption { + default = null; + description = '' + Use an unlisted resolver (e.g., a private DNSCrypt provider). For + advanced users only. If specified, this option takes precedence. + ''; + type = types.nullOr (types.submodule ({ ... }: { options = { + address = mkOption { + type = types.str; + description = "IP address"; + example = "208.67.220.220"; + }; + + port = mkOption { + type = types.int; + description = "Port"; + default = 443; + }; + + name = mkOption { + type = types.str; + description = "Fully qualified domain name"; + example = "2.dnscrypt-cert.example.com"; + }; + + key = mkOption { + type = types.str; + description = "Public key"; + example = "B735:1140:206F:225D:3E2B:D822:D7FD:691E:A1C3:3CC8:D666:8D0C:BE04:BFAB:CA43:FB79"; + }; + }; })); + }; + + extraArgs = mkOption { + default = []; + type = types.listOf types.str; + description = '' + Additional command-line arguments passed verbatim to the daemon. + See <citerefentry><refentrytitle>dnscrypt-proxy</refentrytitle> + <manvolnum>8</manvolnum></citerefentry> for details. + ''; + example = [ "-X libdcplugin_example_cache.so,--min-ttl=60" ]; + }; + }; + }; + + config = mkIf cfg.enable (mkMerge [{ + assertions = [ + { assertion = (cfg.customResolver != null) || (cfg.resolverName != null); + message = "please configure upstream DNSCrypt resolver"; + } + ]; + + # make man 8 dnscrypt-proxy work + environment.systemPackages = [ pkgs.dnscrypt-proxy ]; + + users.users.dnscrypt-proxy = { + description = "dnscrypt-proxy daemon user"; + isSystemUser = true; + group = "dnscrypt-proxy"; + }; + users.groups.dnscrypt-proxy = {}; + + systemd.sockets.dnscrypt-proxy = { + description = "dnscrypt-proxy listening socket"; + documentation = [ "man:dnscrypt-proxy(8)" ]; + + wantedBy = [ "sockets.target" ]; + + socketConfig = { + ListenStream = localAddress; + ListenDatagram = localAddress; + }; + }; + + systemd.services.dnscrypt-proxy = { + description = "dnscrypt-proxy daemon"; + documentation = [ "man:dnscrypt-proxy(8)" ]; + + before = [ "nss-lookup.target" ]; + after = [ "network.target" ]; + requires = [ "dnscrypt-proxy.socket "]; + + serviceConfig = { + NonBlocking = "true"; + ExecStart = "${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy ${toString daemonArgs}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + + User = "dnscrypt-proxy"; + + PrivateTmp = true; + PrivateDevices = true; + ProtectHome = true; + }; + }; + } + + (mkIf config.security.apparmor.enable { + systemd.services.dnscrypt-proxy.after = [ "apparmor.service" ]; + + security.apparmor.profiles = singleton (pkgs.writeText "apparmor-dnscrypt-proxy" '' + ${pkgs.dnscrypt-proxy}/bin/dnscrypt-proxy { + /dev/null rw, + /dev/random r, + /dev/urandom r, + + /etc/passwd r, + /etc/group r, + ${config.environment.etc."nsswitch.conf".source} r, + + ${getLib pkgs.glibc}/lib/*.so mr, + ${pkgs.tzdata}/share/zoneinfo/** r, + + network inet stream, + network inet6 stream, + network inet dgram, + network inet6 dgram, + + ${getLib pkgs.dnscrypt-proxy}/lib/dnscrypt-proxy/libdcplugin*.so mr, + + ${getLib pkgs.gcc.cc}/lib/libssp.so.* mr, + ${getLib pkgs.libsodium}/lib/libsodium.so.* mr, + ${getLib pkgs.systemd}/lib/libsystemd.so.* mr, + ${getLib pkgs.utillinuxMinimal.out}/lib/libmount.so.* mr, + ${getLib pkgs.utillinuxMinimal.out}/lib/libblkid.so.* mr, + ${getLib pkgs.utillinuxMinimal.out}/lib/libuuid.so.* mr, + ${getLib pkgs.xz}/lib/liblzma.so.* mr, + ${getLib pkgs.libgcrypt}/lib/libgcrypt.so.* mr, + ${getLib pkgs.libgpgerror}/lib/libgpg-error.so.* mr, + ${getLib pkgs.libcap}/lib/libcap.so.* mr, + ${getLib pkgs.lz4}/lib/liblz4.so.* mr, + ${getLib pkgs.attr}/lib/libattr.so.* mr, # */ + + ${resolverList} r, + + /run/systemd/notify rw, + } + ''); + }) + + (mkIf useUpstreamResolverList { + systemd.services.init-dnscrypt-proxy-statedir = { + description = "Initialize dnscrypt-proxy state directory"; + + wantedBy = [ "dnscrypt-proxy.service" ]; + before = [ "dnscrypt-proxy.service" ]; + + script = '' + mkdir -pv ${stateDirectory} + chown -c dnscrypt-proxy:dnscrypt-proxy ${stateDirectory} + cp -uv \ + ${pkgs.dnscrypt-proxy}/share/dnscrypt-proxy/dnscrypt-resolvers.csv \ + ${stateDirectory} + ''; + + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + + systemd.services.update-dnscrypt-resolvers = { + description = "Update list of DNSCrypt resolvers"; + + requires = [ "init-dnscrypt-proxy-statedir.service" ]; + after = [ "init-dnscrypt-proxy-statedir.service" ]; + + path = with pkgs; [ curl diffutils dnscrypt-proxy minisign ]; + script = '' + cd ${stateDirectory} + domain=raw.githubusercontent.com + get="curl -fSs --resolve $domain:443:$(hostip -r 8.8.8.8 $domain | head -1)" + $get -o dnscrypt-resolvers.csv.tmp \ + https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv + $get -o dnscrypt-resolvers.csv.minisig.tmp \ + https://$domain/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv.minisig + mv dnscrypt-resolvers.csv.minisig{.tmp,} + if ! minisign -q -V -p ${upstreamResolverListPubKey} \ + -m dnscrypt-resolvers.csv.tmp -x dnscrypt-resolvers.csv.minisig ; then + echo "failed to verify resolver list!" >&2 + exit 1 + fi + [[ -f dnscrypt-resolvers.csv ]] && mv dnscrypt-resolvers.csv{,.old} + mv dnscrypt-resolvers.csv{.tmp,} + if cmp dnscrypt-resolvers.csv{,.old} ; then + echo "no change" + else + echo "resolver list updated" + fi + ''; + + serviceConfig = { + PrivateTmp = true; + PrivateDevices = true; + ProtectHome = true; + ProtectSystem = "strict"; + ReadWritePaths = "${dirOf stateDirectory} ${stateDirectory}"; + SystemCallFilter = "~@mount"; + }; + }; + + systemd.timers.update-dnscrypt-resolvers = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = "5min"; + OnUnitActiveSec = "6h"; + }; + }; + }) + ]); + + imports = [ + (mkRenamedOptionModule [ "services" "dnscrypt-proxy" "port" ] [ "services" "dnscrypt-proxy" "localPort" ]) + + (mkChangedOptionModule + [ "services" "dnscrypt-proxy" "tcpOnly" ] + [ "services" "dnscrypt-proxy" "extraArgs" ] + (config: + let val = getAttrFromPath [ "services" "dnscrypt-proxy" "tcpOnly" ] config; in + optional val "-T")) + + (mkChangedOptionModule + [ "services" "dnscrypt-proxy" "ephemeralKeys" ] + [ "services" "dnscrypt-proxy" "extraArgs" ] + (config: + let val = getAttrFromPath [ "services" "dnscrypt-proxy" "ephemeralKeys" ] config; in + optional val "-E")) + + (mkRemovedOptionModule [ "services" "dnscrypt-proxy" "resolverList" ] '' + The current resolver listing from upstream is always used + unless a custom resolver is specified. + '') + ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy.xml b/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy.xml new file mode 100644 index 000000000000..afc7880392a1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnscrypt-proxy.xml @@ -0,0 +1,66 @@ +<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="sec-dnscrypt-proxy"> + <title>DNSCrypt client proxy</title> + <para> + The DNSCrypt client proxy relays DNS queries to a DNSCrypt enabled upstream + resolver. The traffic between the client and the upstream resolver is + encrypted and authenticated, mitigating the risk of MITM attacks, DNS + poisoning attacks, and third-party snooping (assuming the upstream is + trustworthy). + </para> + <sect1 xml:id="sec-dnscrypt-proxy-configuration"> + <title>Basic configuration</title> + + <para> + To enable the client proxy, set +<programlisting> +<xref linkend="opt-services.dnscrypt-proxy.enable"/> = true; +</programlisting> + </para> + + <para> + Enabling the client proxy does not alter the system nameserver; to relay + local queries, prepend <literal>127.0.0.1</literal> to + <option>networking.nameservers</option>. + </para> + </sect1> + <sect1 xml:id="sec-dnscrypt-proxy-forwarder"> + <title>As a forwarder for another DNS client</title> + + <para> + To run the DNSCrypt proxy client as a forwarder for another DNS client, + change the default proxy listening port to a non-standard value and point + the other client to it: +<programlisting> +<xref linkend="opt-services.dnscrypt-proxy.localPort"/> = 43; +</programlisting> + </para> + + <sect2 xml:id="sec-dnscrypt-proxy-forwarder-dsnmasq"> + <title>dnsmasq</title> + <para> +<programlisting> +{ + <xref linkend="opt-services.dnsmasq.enable"/> = true; + <xref linkend="opt-services.dnsmasq.servers"/> = [ "127.0.0.1#43" ]; +} +</programlisting> + </para> + </sect2> + + <sect2 xml:id="sec-dnscrypt-proxy-forwarder-unbound"> + <title>unbound</title> + <para> +<programlisting> +{ + <xref linkend="opt-services.unbound.enable"/> = true; + <xref linkend="opt-services.unbound.forwardAddresses"/> = [ "127.0.0.1@43" ]; +} +</programlisting> + </para> + </sect2> + </sect1> +</chapter> 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..bf13d5c6f5fe --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnscrypt-wrapper.nix @@ -0,0 +1,199 @@ +{ config, lib, pkgs, ... }: +with lib; + +let + cfg = config.services.dnscrypt-wrapper; + dataDir = "/var/lib/dnscrypt-wrapper"; + + daemonArgs = with cfg; [ + "--listen-address=${address}:${toString port}" + "--resolver-address=${upstream.address}:${toString upstream.port}" + "--provider-name=${providerName}" + "--provider-publickey-file=public.key" + "--provider-secretkey-file=secret.key" + "--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=public.key \ + --provider-secretkey-file=secret.key \ + --cert-file-expire-days=${toString cfg.keys.expiration} + } + + cd ${dataDir} + + # generate provider keypair (first run only) + if [ ! -f public.key ] || [ ! -f secret.key ]; 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 + ''; + +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>. + ''; + }; + + 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; + }; + 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-proxy 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; + }; + }; + + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/dnsdist.nix b/nixpkgs/nixos/modules/services/networking/dnsdist.nix new file mode 100644 index 000000000000..12eee136e639 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnsdist.nix @@ -0,0 +1,61 @@ +{ 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"; + PrivateTmp=true; + PrivateDevices=true; + CapabilityBoundingSet="CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID"; + ExecStart = "${pkgs.dnsdist}/bin/dnsdist --supervised --disable-syslog --config ${configFile}"; + ProtectSystem="full"; + 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..714a5903bff1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/dnsmasq.nix @@ -0,0 +1,129 @@ +{ 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 = singleton { + name = "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..6a38f85c48a2 --- /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") (singleton + { name = "ejabberd"; + group = cfg.group; + home = cfg.spoolDir; + createHome = true; + uid = config.ids.uids.ejabberd; + }); + + users.groups = optionalAttrs (cfg.group == "ejabberd") (singleton + { name = "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/eternal-terminal.nix b/nixpkgs/nixos/modules/services/networking/eternal-terminal.nix new file mode 100644 index 000000000000..be7337ece7e4 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/eternal-terminal.nix @@ -0,0 +1,89 @@ +{ 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. + ''; + }; + + 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"; + }; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/fakeroute.nix b/nixpkgs/nixos/modules/services/networking/fakeroute.nix new file mode 100644 index 000000000000..82a9fb729d84 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/fakeroute.nix @@ -0,0 +1,63 @@ +{ 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}"; + }; + }; + + }; + +} 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..4ea891262e56 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/firewall.nix @@ -0,0 +1,579 @@ +/* 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 = + '' + # Helper command to manipulate both the IPv4 and IPv6 tables. + ip46tables() { + iptables -w "$@" + ${optionalString config.networking.enableIPv6 '' + ip6tables -w "$@" + ''} + } + ''; + + 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. + ''; + }; + + 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 = [ pkgs.iptables ] ++ 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 = [ { 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 = [ pkgs.iptables ] ++ 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..dd2f6454e954 --- /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.bin; + defaultText = "pkgs.flannel.bin"; + }; + + 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}/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..9c51b88ef677 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/flashpolicyd.nix @@ -0,0 +1,84 @@ +{ 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 { + 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..e192b70c129c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/freeradius.nix @@ -0,0 +1,72 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.freeradius; + + freeradiusService = cfg: + { + description = "FreeRadius server"; + wantedBy = ["multi-user.target"]; + after = ["network-online.target"]; + wants = ["network-online.target"]; + preStart = '' + ${pkgs.freeradius}/bin/radiusd -C -d ${cfg.configDir} -l stdout + ''; + + serviceConfig = { + ExecStart = "${pkgs.freeradius}/bin/radiusd -f -d ${cfg.configDir} -l stdout -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. + ''; + }; + + }; + +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; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/gale.nix b/nixpkgs/nixos/modules/services/networking/gale.nix new file mode 100644 index 000000000000..7083d87c4073 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gale.nix @@ -0,0 +1,182 @@ +{ 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 = [{ + name = 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..c0020349ec74 --- /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 = if cfg.user != "git" then {} else singleton + { name = "git"; + uid = config.ids.uids.git; + description = "Git daemon user"; + }; + + users.groups = if cfg.group != "git" then {} else singleton + { name = "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..178a832c166e --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gnunet.nix @@ -0,0 +1,158 @@ +{ 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 { + 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 { + default = 1024; + description = '' + Maximum file system usage (in MiB) for file sharing. + ''; + }; + }; + + udp = { + port = mkOption { + default = 2086; # assigned by IANA + description = '' + The UDP port for use by GNUnet. + ''; + }; + }; + + tcp = { + port = mkOption { + default = 2086; # assigned by IANA + description = '' + The TCP port for use by GNUnet. + ''; + }; + }; + + load = { + maxNetDownBandwidth = mkOption { + default = 50000; + description = '' + Maximum bandwidth usage (in bits per second) for GNUnet + when downloading data. + ''; + }; + + maxNetUpBandwidth = mkOption { + default = 50000; + description = '' + Maximum bandwidth usage (in bits per second) for GNUnet + when downloading data. + ''; + }; + + hardNetUpBandwidth = mkOption { + 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 { + 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/gogoclient.nix b/nixpkgs/nixos/modules/services/networking/gogoclient.nix new file mode 100644 index 000000000000..9d16f0efb435 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gogoclient.nix @@ -0,0 +1,84 @@ +{ 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 { + default = true; + description = '' + Whether to automatically start the tunnel. + ''; + }; + + username = mkOption { + default = ""; + description = '' + Your Gateway6 login name, if any. + ''; + }; + + password = mkOption { + default = ""; + type = types.string; + 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..3ef3548e0a08 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/gvpe.nix @@ -0,0 +1,128 @@ +# 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 = mkOption { + default = false; + description = '' + Whether to run 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..20e57e4626ef --- /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 = singleton { + name = hansUser; + description = "Hans daemon user"; + }; + }; + + 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..0438d0bf8d86 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/haproxy.nix @@ -0,0 +1,62 @@ +{ config, lib, pkgs, ... }: +let + cfg = config.services.haproxy; + haproxyCfg = pkgs.writeText "haproxy.conf" 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. + ''; + }; + + 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."; + }]; + + systemd.services.haproxy = { + description = "HAProxy"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/haproxy.pid"; + ExecStartPre = "${pkgs.haproxy}/sbin/haproxy -c -q -f ${haproxyCfg}"; + ExecStart = "${pkgs.haproxy}/sbin/haproxy -D -f ${haproxyCfg} -p /run/haproxy.pid"; + ExecReload = "-${pkgs.bash}/bin/bash -c \"exec ${pkgs.haproxy}/sbin/haproxy -D -f ${haproxyCfg} -p /run/haproxy.pid -sf $MAINPID\""; + }; + }; + + environment.systemPackages = [ pkgs.haproxy ]; + + users.users.haproxy = { + group = "haproxy"; + uid = config.ids.uids.haproxy; + }; + + users.groups.haproxy.gid = config.ids.uids.haproxy; + }; +} 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..54a5bed2563f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/hostapd.nix @@ -0,0 +1,182 @@ +{ 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} + + # logging (debug level) + logger_syslog=-1 + logger_syslog_level=2 + logger_stdout=-1 + logger_stdout_level=2 + + 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 { + 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 { + 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.string; + description = '' + Which driver <command>hostapd</command> will use. + Most applications will probably use the default. + ''; + }; + + ssid = mkOption { + default = "nixos"; + example = "mySpecialSSID"; + type = types.string; + 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.string; + description = '' + Members of this group can control <command>hostapd</command>. + ''; + }; + + wpa = mkOption { + 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.string; + 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! + ''; + }; + + 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 ]; + + 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..0c6602e7f8ab --- /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..f2be417738ee --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/i2pd.nix @@ -0,0 +1,680 @@ +{ 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 && proto ? name) 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 + +{ + + ###### 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; loaOf (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; loaOf (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..344f84374bbd --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/iodine.nix @@ -0,0 +1,150 @@ +# NixOS module for iodine, ip over dns daemon + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.iodine; + + iodinedUser = "iodined"; + +in +{ + + ### 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 = "Domain or Subdomain of server running iodined"; + example = "tunnel.mydomain.com"; + }; + + relay = mkOption { + type = types.str; + default = ""; + description = "DNS server to use as a 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 = "File that contains 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 != "") "< \"${cfg.passwordFile}\""} ${cfg.relay} ${cfg.server}"; + serviceConfig = { + RestartSec = "30s"; + Restart = "always"; + }; + }; + 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 != "") "< \"${cfg.server.passwordFile}\""} ${cfg.server.ip} ${cfg.server.domain}"; + }; + }; + + users.users = singleton { + name = 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..2bd898edf897 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ircd-hybrid/default.nix @@ -0,0 +1,131 @@ +{ 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 = mkOption { + default = false; + description = " + Enable 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 = singleton + { name = "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..18ed20e28886 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/iwd.nix @@ -0,0 +1,33 @@ +{ 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" ]; + + systemd.tmpfiles.rules = [ + "d /var/lib/iwd 0700 root root -" + ]; + }; + + meta.maintainers = with lib.maintainers; [ mic92 ]; +} 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..85f52be8a6ac --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/keybase.nix @@ -0,0 +1,42 @@ +{ 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 { + + systemd.user.services.keybase = { + description = "Keybase service"; + unitConfig.ConditionUser = "!@system"; + serviceConfig = { + ExecStart = '' + ${pkgs.keybase}/bin/keybase service --auto-forked + ''; + 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..40c38254a57c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/kippo.nix @@ -0,0 +1,118 @@ +# 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 +rec { + 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.string; + description = ''Hostname for kippo to present to SSH login''; + }; + varPath = mkOption { + default = "/var/lib/kippo"; + type = types.string; + description = ''Path of read/write files needed for operation and configuration.''; + }; + logPath = mkOption { + default = "/var/log/kippo"; + type = types.string; + description = ''Path of log files needed for operation and configuration.''; + }; + pidPath = mkOption { + default = "/run/kippo"; + type = types.string; + 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 = singleton { + name = "kippo"; + description = "kippo web server privilege separation user"; + uid = 108; # why does config.ids.uids.kippo give an error? + }; + users.groups = singleton { name = "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..1cc1dd3f2f62 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/knot.nix @@ -0,0 +1,95 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.knot; + + configFile = pkgs.writeText "knot.conf" cfg.extraConfig; + socketFile = "/run/knot/knot.sock"; + + knotConfCheck = file: pkgs.runCommand "knot-config-checked" + { buildInputs = [ cfg.package ]; } '' + ln -s ${configFile} $out + knotc --config=${configFile} conf-check + ''; + + 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 + ''; + }; + + 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; + description = '' + Which Knot DNS package to use + ''; + }; + }; + }; + + config = mkIf config.services.knot.enable { + 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=${knotConfCheck 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; + DynamicUser = "yes"; + 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..fc516c01230a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/kresd.nix @@ -0,0 +1,136 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.kresd; + package = pkgs.knot-resolver; + + configFile = pkgs.writeText "kresd.conf" cfg.extraConfig; +in + +{ + meta.maintainers = [ maintainers.vcunat /* upstream developer */ ]; + + ###### 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/kresd/control</literal> + and give commands interactively to kresd. + ''; + }; + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra lines to be added verbatim to the generated configuration file. + ''; + }; + cacheDir = mkOption { + type = types.path; + default = "/var/cache/kresd"; + description = '' + Directory for caches. They are intended to survive reboots. + ''; + }; + interfaces = mkOption { + type = with types; listOf str; + default = [ "::1" "127.0.0.1" ]; + description = '' + What addresses the server should listen on. (UDP+TCP 53) + ''; + }; + listenTLS = mkOption { + type = with types; listOf str; + default = []; + example = [ "198.51.100.1:853" "[2001:db8::1]:853" "853" ]; + description = '' + Addresses on which kresd should provide DNS over TLS (see RFC 7858). + For detailed syntax see ListenStream in man systemd.socket. + ''; + }; + # TODO: perhaps options for more common stuff like cache size or forwarding + }; + + ###### implementation + config = mkIf cfg.enable { + environment.etc."kresd.conf".source = configFile; # not required + + users.users = singleton + { name = "kresd"; + uid = config.ids.uids.kresd; + group = "kresd"; + description = "Knot-resolver daemon user"; + }; + users.groups = singleton + { name = "kresd"; + gid = config.ids.gids.kresd; + }; + + systemd.sockets.kresd = rec { + wantedBy = [ "sockets.target" ]; + before = wantedBy; + listenStreams = map + # Syntax depends on being IPv6 or IPv4. + (iface: if elem ":" (stringToCharacters iface) then "[${iface}]:53" else "${iface}:53") + cfg.interfaces; + socketConfig = { + ListenDatagram = listenStreams; + FreeBind = true; + FileDescriptorName = "dns"; + }; + }; + + systemd.sockets.kresd-tls = mkIf (cfg.listenTLS != []) rec { + wantedBy = [ "sockets.target" ]; + before = wantedBy; + partOf = [ "kresd.socket" ]; + listenStreams = cfg.listenTLS; + socketConfig = { + FileDescriptorName = "tls"; + FreeBind = true; + Service = "kresd.service"; + }; + }; + + systemd.sockets.kresd-control = rec { + wantedBy = [ "sockets.target" ]; + before = wantedBy; + partOf = [ "kresd.socket" ]; + listenStreams = [ "/run/kresd/control" ]; + socketConfig = { + FileDescriptorName = "control"; + Service = "kresd.service"; + SocketMode = "0660"; # only root user/group may connect and control kresd + }; + }; + + systemd.tmpfiles.rules = [ "d '${cfg.cacheDir}' 0770 kresd kresd - -" ]; + + systemd.services.kresd = { + description = "Knot-resolver daemon"; + + serviceConfig = { + User = "kresd"; + Type = "notify"; + WorkingDirectory = cfg.cacheDir; + Restart = "on-failure"; + Sockets = [ "kresd.socket" "kresd-control.socket" ] + ++ optional (cfg.listenTLS != []) "kresd-tls.socket"; + }; + + # Trust anchor goes from dns-root-data by default. + script = '' + exec '${package}/bin/kresd' --config '${configFile}' --forks=1 + ''; + + requires = [ "kresd.socket" ]; + }; + }; +} 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..406626a8a343 --- /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" "local-fs.target" ]; + + serviceConfig = { + Type = "forking"; + ExecStart = "${pkgs.logmein-hamachi}/bin/hamachid"; + }; + }; + + environment.systemPackages = [ pkgs.logmein-hamachi ]; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/mailpile.nix b/nixpkgs/nixos/modules/services/networking/mailpile.nix new file mode 100644 index 000000000000..c42d3d5a44cb --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mailpile.nix @@ -0,0 +1,76 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.mailpile; + + hostname = cfg.hostname; + port = cfg.port; + +in + +{ + + ###### interface + + options = { + + services.mailpile = { + enable = mkOption { + default = false; + description = " + Whether to enable 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..1fd63348c16c --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/matterbridge.nix @@ -0,0 +1,118 @@ +{ 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 = optional (cfg.user == "matterbridge") + { name = "matterbridge"; + group = "matterbridge"; + }; + + users.groups = optional (cfg.group == "matterbridge") + { name = "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}/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..ed0c1044a570 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/minidlna.nix @@ -0,0 +1,113 @@ +# 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.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.config = mkOption { + type = types.lines; + description = "The contents of MiniDLNA's configuration file."; + }; + }; + + ###### implementation + config = mkIf cfg.enable { + services.minidlna.config = + '' + port=${toString port} + friendly_name=${config.networking.hostName} MiniDLNA + db_dir=/var/cache/minidlna + log_level=${cfg.loglevel} + inotify=yes + ${concatMapStrings (dir: '' + media_dir=${dir} + '') cfg.mediaDirs} + ''; + + 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" "local-fs.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..e0a6c112e3cb --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mjpg-streamer.nix @@ -0,0 +1,79 @@ +{ 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 = optional (cfg.user == "mjpg-streamer") { + name = "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..831e4d60d8da --- /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 '' + restrict-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 = singleton { + name = "monero"; + uid = config.ids.uids.monero; + description = "Monero daemon user"; + home = dataDir; + createHome = true; + }; + + users.groups = singleton { + name = "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. + ''; + }; + + }; + +} + diff --git a/nixpkgs/nixos/modules/services/networking/morty.nix b/nixpkgs/nixos/modules/services/networking/morty.nix new file mode 100644 index 000000000000..cc81e27e9399 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/morty.nix @@ -0,0 +1,96 @@ +{ 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.string; + 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.string; + 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"; + }; + + 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..1d49c137723c --- /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.string; + 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.string; + 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.string; + 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..24bf33815da8 --- /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/murmur.nix b/nixpkgs/nixos/modules/services/networking/murmur.nix new file mode 100644 index 000000000000..7ac4d0c6419d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/murmur.nix @@ -0,0 +1,265 @@ +{ 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 +{ + 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 mumur.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..02e89f441b34 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/mxisd.nix @@ -0,0 +1,116 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + 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 = "${cfg.dataDir}/mxisd.db"; + }; + } // optionalAttrs (server != {}) { inherit server; }; + + # merges baseConfig and extraConfig into a single file + fullConfig = recursiveUpdate baseConfig cfg.extraConfig; + + configFile = pkgs.writeText "mxisd-config.yaml" (builtins.toJSON fullConfig); + +in { + options = { + services.mxisd = { + enable = mkEnableOption "mxisd matrix federated identity server"; + + package = mkOption { + type = types.package; + default = pkgs.mxisd; + defaultText = "pkgs.mxisd"; + description = "The mxisd package to use"; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/mxisd"; + description = "Where data mxisd uses resides"; + }; + + extraConfig = mkOption { + type = types.attrs; + default = {}; + description = "Extra options merged into the mxisd 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, 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 = [ + { + name = "mxisd"; + group = "mxisd"; + home = cfg.dataDir; + createHome = true; + shell = "${pkgs.bash}/bin/bash"; + uid = config.ids.uids.mxisd; + } + ]; + + users.groups = [ + { + name = "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 = { + Type = "simple"; + User = "mxisd"; + Group = "mxisd"; + ExecStart = "${cfg.package}/bin/mxisd -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..a569ca87e262 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/namecoind.nix @@ -0,0 +1,204 @@ + +{ 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 { + + services.dnschain.extraConfig = '' + [namecoin] + config = ${configFile} + ''; + + users.users = singleton { + name = "namecoin"; + uid = config.ids.uids.namecoin; + description = "Namecoin daemon user"; + home = dataDir; + createHome = true; + }; + + users.groups = singleton { + name = "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.altcoins.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 + ''; + + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/nat.nix b/nixpkgs/nixos/modules/services/networking/nat.nix new file mode 100644 index 000000000000..89d8590093dd --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nat.nix @@ -0,0 +1,282 @@ +# 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}"; + + flushNat = '' + iptables -w -t nat -D PREROUTING -j nixos-nat-pre 2>/dev/null|| true + iptables -w -t nat -F nixos-nat-pre 2>/dev/null || true + iptables -w -t nat -X nixos-nat-pre 2>/dev/null || true + iptables -w -t nat -D POSTROUTING -j nixos-nat-post 2>/dev/null || true + iptables -w -t nat -F nixos-nat-post 2>/dev/null || true + iptables -w -t nat -X nixos-nat-post 2>/dev/null || true + + ${cfg.extraStopCommands} + ''; + + setupNat = '' + # Create subchain where we store rules + iptables -w -t nat -N nixos-nat-pre + iptables -w -t nat -N nixos-nat-post + + # We can't match on incoming interface in POSTROUTING, so + # mark packets coming from the external 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 elemAt m 1; + in '' + # Allow connections to ${loopbackip}:${toString fwd.sourcePort} from the host itself + iptables -w -t nat -A OUTPUT \ + -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 + iptables -w -t nat -A PREROUTING -j nixos-nat-pre + iptables -w -t nat -A POSTROUTING -j nixos-nat-post + ''; + +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..ba17f1ba825a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ndppd.nix @@ -0,0 +1,167 @@ +{ 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 = { "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}"; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/networkmanager.nix b/nixpkgs/nixos/modules/services/networking/networkmanager.nix new file mode 100644 index 000000000000..551636a33d25 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/networkmanager.nix @@ -0,0 +1,511 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.networking.networkmanager; + + dynamicHostsEnabled = + cfg.dynamicHosts.enable && cfg.dynamicHosts.hostsDirs != {}; + + # /var/lib/misc is for dnsmasq.leases. + stateDirs = "/var/lib/NetworkManager /var/lib/dhclient /var/lib/misc"; + + 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} + + [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"} + + ${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 { + + ###### 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.string; + 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>. + ''; + }; + + # Ugly hack for using the correct gnome3 packageSet + basePackages = mkOption { + type = types.attrsOf types.package; + default = { inherit (pkgs) + networkmanager modemmanager wpa_supplicant crda + networkmanager-openvpn networkmanager-vpnc + networkmanager-openconnect networkmanager-fortisslvpn + networkmanager-l2tp networkmanager-iodine; }; + internal = true; + }; + + packages = mkOption { + type = types.listOf types.path; + default = [ ]; + description = '' + Extra packages that provide NetworkManager plugins. + ''; + apply = list: (attrValues cfg.basePackages) ++ list; + }; + + dhcp = mkOption { + type = types.enum [ "dhclient" "dhcpcd" "internal" ]; + default = "dhclient"; + 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; + + 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" + 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. + ''; + }; + + dynamicHosts = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Enabling this option requires the + <option>networking.networkmanager.dns</option> option to be + set to <literal>dnsmasq</literal>. If enabled, the directories + defined by the + <option>networking.networkmanager.dynamicHosts.hostsDirs</option> + option will be set up when the service starts. The dnsmasq instance + managed by NetworkManager will then watch those directories for + hosts files (see the <literal>--hostsdir</literal> option of + dnsmasq). This way a non-privileged user can add or override DNS + entries on the local system (depending on what hosts directories + that are configured).. + ''; + }; + hostsDirs = mkOption { + type = with types; attrsOf (submodule { + options = { + user = mkOption { + type = types.str; + default = "root"; + description = '' + The user that will own the hosts directory. + ''; + }; + group = mkOption { + type = types.str; + default = "root"; + description = '' + The group that will own the hosts directory. + ''; + }; + }; + }); + default = {}; + description = '' + Defines a set of directories (relative to + <literal>/run/NetworkManager/hostdirs</literal>) that dnsmasq will + watch for hosts files. + ''; + }; + }; + }; + }; + + + ###### implementation + + config = mkIf cfg.enable { + + assertions = [ + { assertion = config.networking.wireless.enable == false; + message = "You can not use networking.networkmanager with networking.wireless"; + } + { assertion = !dynamicHostsEnabled || (dynamicHostsEnabled && cfg.dns == "dnsmasq"); + message = '' + To use networking.networkmanager.dynamicHosts you also need to set + networking.networkmanager.dns = "dnsmasq" + ''; + } + ]; + + environment.etc = with cfg.basePackages; [ + { source = configFile; + target = "NetworkManager/NetworkManager.conf"; + } + { source = "${networkmanager-openvpn}/lib/NetworkManager/VPN/nm-openvpn-service.name"; + target = "NetworkManager/VPN/nm-openvpn-service.name"; + } + { source = "${networkmanager-vpnc}/lib/NetworkManager/VPN/nm-vpnc-service.name"; + target = "NetworkManager/VPN/nm-vpnc-service.name"; + } + { source = "${networkmanager-openconnect}/lib/NetworkManager/VPN/nm-openconnect-service.name"; + target = "NetworkManager/VPN/nm-openconnect-service.name"; + } + { source = "${networkmanager-fortisslvpn}/lib/NetworkManager/VPN/nm-fortisslvpn-service.name"; + target = "NetworkManager/VPN/nm-fortisslvpn-service.name"; + } + { source = "${networkmanager-l2tp}/lib/NetworkManager/VPN/nm-l2tp-service.name"; + target = "NetworkManager/VPN/nm-l2tp-service.name"; + } + { source = "${networkmanager-iodine}/lib/NetworkManager/VPN/nm-iodine-service.name"; + target = "NetworkManager/VPN/nm-iodine-service.name"; + } + ] ++ optional (cfg.appendNameservers != [] || cfg.insertNameservers != []) + { source = overrideNameserversScript; + target = "NetworkManager/dispatcher.d/02overridedns"; + } + ++ lib.imap1 (i: s: { + inherit (s) source; + target = "NetworkManager/dispatcher.d/${dispatcherTypesSubdirMap.${s.type}}03userscript${lib.fixedWidthNumber 4 i}"; + mode = "0544"; + }) cfg.dispatcherScripts + ++ optional dynamicHostsEnabled + { target = "NetworkManager/dnsmasq.d/dyndns.conf"; + text = concatMapStrings (n: '' + hostsdir=/run/NetworkManager/hostsdirs/${n} + '') (attrNames cfg.dynamicHosts.hostsDirs); + } + ++ optional cfg.enableStrongSwan + { source = "${pkgs.networkmanager_strongswan}/lib/NetworkManager/VPN/nm-strongswan-service.name"; + target = "NetworkManager/VPN/nm-strongswan-service.name"; + }; + + environment.systemPackages = cfg.packages; + + users.groups = [{ + name = "networkmanager"; + gid = config.ids.gids.networkmanager; + } + { + name = "nm-openvpn"; + gid = config.ids.gids.nm-openvpn; + }]; + users.users = [{ + name = "nm-openvpn"; + uid = config.ids.uids.nm-openvpn; + extraGroups = [ "networkmanager" ]; + } + { + name = "nm-iodine"; + isSystemUser = true; + group = "networkmanager"; + }]; + + systemd.packages = cfg.packages; + + systemd.services."NetworkManager" = { + wantedBy = [ "network.target" ]; + restartTriggers = [ configFile ]; + + preStart = '' + mkdir -m 700 -p /etc/NetworkManager/system-connections + mkdir -m 700 -p /etc/ipsec.d + mkdir -m 755 -p ${stateDirs} + ''; + }; + + systemd.services.NetworkManager-wait-online = { + wantedBy = [ "network-online.target" ]; + }; + + systemd.services.nm-setup-hostsdirs = mkIf dynamicHostsEnabled { + wantedBy = [ "NetworkManager.service" ]; + before = [ "NetworkManager.service" ]; + partOf = [ "NetworkManager.service" ]; + script = concatStrings (mapAttrsToList (n: d: '' + mkdir -p "/run/NetworkManager/hostsdirs/${n}" + chown "${d.user}:${d.group}" "/run/NetworkManager/hostsdirs/${n}" + chmod 0775 "/run/NetworkManager/hostsdirs/${n}" + '') cfg.dynamicHosts.hostsDirs); + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + + systemd.services."NetworkManager-dispatcher" = { + wantedBy = [ "network.target" ]; + restartTriggers = [ configFile ]; + + # useful binaries for user-specified hooks + path = [ pkgs.iproute pkgs.utillinux pkgs.coreutils ]; + }; + + # Turn off NixOS' network management + networking = { + useDHCP = false; + # use mkDefault to trigger the assertion about the conflict above + wireless.enable = mkDefault false; + }; + + security.polkit.extraConfig = polkitConf; + + networking.networkmanager.packages = + mkIf cfg.enableStrongSwan [ pkgs.networkmanager_strongswan ]; + + services.dbus.packages = + optional cfg.enableStrongSwan pkgs.strongswanNM ++ cfg.packages; + + 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..ad7c013a5449 --- /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 icmp 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..d6e1906e3881 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nghttpx/default.nix @@ -0,0 +1,117 @@ +{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; + }; + + + 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..ca458d089dcc --- /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.string; + 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.string; + 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/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..d24d6f77a491 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nntp-proxy.nix @@ -0,0 +1,235 @@ +{ 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 = singleton + { name = 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..8b918dab86dd --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/nsd.nix @@ -0,0 +1,985 @@ +{ 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.str; + 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.str; + 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 = singleton { + name = username; + gid = config.ids.gids.nsd; + }; + + users.users = singleton { + name = 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 = [ "keys.target" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + wants = [ "keys.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 = '' + ${pkgs.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/ntpd.nix b/nixpkgs/nixos/modules/services/networking/ntpd.nix new file mode 100644 index 000000000000..588d1c6edb07 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ntpd.nix @@ -0,0 +1,134 @@ +{ 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)} + ''; + + ntpFlags = "-c ${configFile} -u ${ntpUser}:nogroup ${toString cfg.extraFlags}"; + +in + +{ + + ###### interface + + options = { + + services.ntp = { + + enable = mkOption { + 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. + ''; + }; + + 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 { + + # 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 = singleton + { name = 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/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..b061ce34ed2c --- /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.string; + default = "lo"; + description = '' + Tell nylon which interface to listen for client requests on, default is "lo". + ''; + }; + + bindInterface = mkOption { + type = types.string; + 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 string; + 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 string; + 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/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..4059eb3db83d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/openfire.nix @@ -0,0 +1,60 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + ###### interface + + options = { + + services.openfire = { + + enable = mkOption { + default = false; + description = " + Whether to enable OpenFire XMPP server. + "; + }; + + usePostgreSQL = mkOption { + 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/openntpd.nix b/nixpkgs/nixos/modules/services/networking/openntpd.nix new file mode 100644 index 000000000000..57638ebc9c01 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/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; string; + default = ""; + example = "-s"; + description = '' + Extra options used when launching openntpd. + ''; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + services.timesyncd.enable = mkForce false; + + # Add ntpctl to the environment for status checking + environment.systemPackages = [ package ]; + + environment.etc."ntpd.conf".text = configFile; + + users.users = singleton { + name = "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/openvpn.nix b/nixpkgs/nixos/modules/services/networking/openvpn.nix new file mode 100644 index 000000000000..f47122ee70bf --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/openvpn.nix @@ -0,0 +1,216 @@ +{ 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 + +{ + + ###### 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.string; + }; + + password = mkOption { + description = "The password to store inside the credentials file."; + type = types.string; + }; + }; + }); + }; + }; + + }); + + }; + + }; + + + ###### 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..13f784dc53c1 --- /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.string; + 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.string; + 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..821a0258f4be --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/owamp.nix @@ -0,0 +1,47 @@ +{ 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 = singleton { + name = "owamp"; + group = "owamp"; + description = "Owamp daemon"; + }; + + users.groups = singleton { + name = "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..d07deb9dcc67 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/pdns-recursor.nix @@ -0,0 +1,168 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + dataDir = "/var/lib/pdns-recursor"; + username = "pdns-recursor"; + + cfg = config.services.pdns-recursor; + zones = mapAttrsToList (zone: uri: "${zone}.=${uri}") cfg.forwardZones; + + configFile = pkgs.writeText "recursor.conf" '' + local-address=${cfg.dns.address} + local-port=${toString cfg.dns.port} + allow-from=${concatStringsSep "," cfg.dns.allowFrom} + + webserver-address=${cfg.api.address} + webserver-port=${toString cfg.api.port} + webserver-allow-from=${concatStringsSep "," cfg.api.allowFrom} + + forward-zones=${concatStringsSep "," zones} + export-etc-hosts=${if cfg.exportHosts then "yes" else "no"} + dnssec=${cfg.dnssecValidation} + serve-rfc1918=${if cfg.serveRFC1918 then "yes" else "no"} + + ${cfg.extraConfig} + ''; + +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; + example = { eth = "127.0.0.1:5353"; }; + default = {}; + description = '' + DNS zones to be forwarded to other 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. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Extra options to be appended to the configuration file. + ''; + }; + }; + + config = mkIf cfg.enable { + + 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} \ + --disable-syslog + ''; + }; + + 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 + ''; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/pdnsd.nix b/nixpkgs/nixos/modules/services/networking/pdnsd.nix new file mode 100644 index 000000000000..f5b174dd7b7b --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/pdnsd.nix @@ -0,0 +1,93 @@ +{ 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 = singleton { + name = pdnsdUser; + uid = config.ids.uids.pdnsd; + group = pdnsdGroup; + description = "pdnsd user"; + }; + + users.groups = singleton { + name = 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/polipo.nix b/nixpkgs/nixos/modules/services/networking/polipo.nix new file mode 100644 index 000000000000..529115a1c6e1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/polipo.nix @@ -0,0 +1,114 @@ +{ 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.string; + 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.string; + 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.string; + 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 = singleton + { name = "polipo"; + uid = config.ids.uids.polipo; + description = "Polipo caching proxy user"; + home = "/var/cache/polipo"; + createHome = true; + }; + + users.groups = singleton + { name = "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"; + }; + }; + + }; + +} \ No newline at end of file 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/pptpd.nix b/nixpkgs/nixos/modules/services/networking/pptpd.nix new file mode 100644 index 000000000000..d8b9e8f8341a --- /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.string; + description = "The server-side IP address."; + default = "10.124.124.1"; + }; + + clientIpRange = mkOption { + type = types.string; + 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..c936417e68cb --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/prayer.nix @@ -0,0 +1,97 @@ +{ 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 = mkOption { + default = false; + description = '' + Whether to run 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 = singleton + { name = prayerUser; + uid = config.ids.uids.prayer; + description = "Prayer daemon user"; + home = stateDir; + }; + + users.groups = singleton + { name = 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..49ca839a2c37 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/privoxy.nix @@ -0,0 +1,112 @@ +{ 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"; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/prosody.nix b/nixpkgs/nixos/modules/services/networking/prosody.nix new file mode 100644 index 000000000000..40bd9015b1eb --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/prosody.nix @@ -0,0 +1,530 @@ +{ 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."; + }; + + }; + }; + + moduleOpts = { + # Generally required + 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"; + }; + + 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 = true; + description = "Allow users to set vCards"; + }; + + # 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 = false; + description = "Store messages in an archive and allow users to access it"; + }; + + # Admin interfaces + admin_adhoc = mkOption { + type = types.bool; + default = true; + description = "Allows administration via an XMPP client that supports ad-hoc commands"; + }; + + 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"; + }; + + http_files = mkOption { + type = types.bool; + default = false; + description = "Serve static files from a directory over HTTP"; + }; + + # 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"; + }; + + proxy65 = mkOption { + type = types.bool; + default = false; + description = "Enables a file transfer proxy service which clients behind NAT can use"; + }; + + }; + + 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)} + }; + ''; + + 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"; + }; + + 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.string; + description = "Directory where Prosody stores its data"; + default = "/var/lib/prosody"; + }; + + 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"; + }; + + 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"; + }; + + 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 { + + environment.systemPackages = [ cfg.package ]; + + environment.etc."prosody/prosody.cfg.lua".text = '' + + 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)} + }; + + 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} + + ${ cfg.extraConfig } + + ${ 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"; + }; + }; + + }; + +} 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..b495b3948fb5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/quassel.nix @@ -0,0 +1,133 @@ +{ 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 = mkOption { + default = false; + description = '' + Whether to run 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 = mkIf (cfg.user == null) [ + { name = "quassel"; + description = "Quassel IRC client daemon"; + group = "quassel"; + uid = config.ids.uids.quassel; + }]; + + users.groups = mkIf (cfg.user == null) [ + { 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..5bcf923f909c --- /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/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..d6fabbcd4700 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/radicale.nix @@ -0,0 +1,92 @@ +{ 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.string; + default = ""; + description = '' + Radicale configuration, this will set the service + configuration file. + ''; + }; + + services.radicale.extraArgs = mkOption { + type = types.listOf types.string; + default = []; + description = "Extra arguments passed to the Radicale daemon."; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ cfg.package ]; + + users.users = singleton + { name = "radicale"; + uid = config.ids.uids.radicale; + description = "radicale user"; + home = "/var/lib/radicale"; + createHome = true; + }; + + users.groups = singleton + { name = "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..020faa34922a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/radvd.nix @@ -0,0 +1,72 @@ +# 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 { + 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..bccab805beeb --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/rdnssd.nix @@ -0,0 +1,79 @@ +# 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 { + 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..ee7f82ac7bee --- /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 = [ { name = "rslsync"; } ]; + + systemd.services.resilio = with pkgs; { + description = "Resilio Sync Service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" "local-fs.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..a6a069ec50c0 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/rxe.nix @@ -0,0 +1,63 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.networking.rxe; + + runRxeCmd = cmd: ifcs: + concatStrings ( map (x: "${pkgs.rdma-core}/bin/rxe_cfg -n ${cmd} ${x};") ifcs); + + startScript = pkgs.writeShellScriptBin "rxe-start" '' + ${pkgs.rdma-core}/bin/rxe_cfg -n start + ${runRxeCmd "add" cfg.interfaces} + ${pkgs.rdma-core}/bin/rxe_cfg + ''; + + stopScript = pkgs.writeShellScriptBin "rxe-stop" '' + ${runRxeCmd "remove" cfg.interfaces } + ${pkgs.rdma-core}/bin/rxe_cfg -n stop + ''; + +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 rxe0 ... rxeN where the ordering + will be as they are named in the list. UDP port 4791 must be + open on the respective ethernet interfaces. + ''; + }; + }; + }; + + ###### implementation + + config = mkIf cfg.enable { + + systemd.services.rxe = { + path = with pkgs; [ kmod rdma-core ]; + 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 = "${startScript}/bin/rxe-start"; + ExecStop = "${stopScript}/bin/rxe-stop"; + }; + }; + }; +} + diff --git a/nixpkgs/nixos/modules/services/networking/sabnzbd.nix b/nixpkgs/nixos/modules/services/networking/sabnzbd.nix new file mode 100644 index 000000000000..62b24d4377f8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/sabnzbd.nix @@ -0,0 +1,69 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.sabnzbd; + inherit (pkgs) sabnzbd; + +in + +{ + + ###### interface + + options = { + services.sabnzbd = { + enable = mkOption { + default = false; + description = "Whether to enable 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..9412d0ef8a62 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/searx.nix @@ -0,0 +1,78 @@ +{ 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 ]; + + }; + +} 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..68e005ab81da --- /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 { + 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 = singleton + { name = 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/shout.nix b/nixpkgs/nixos/modules/services/networking/shout.nix new file mode 100644 index 000000000000..f511a9af2562 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/shout.nix @@ -0,0 +1,114 @@ +{ 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.string; + 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 = singleton { + name = "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..6ad18bb22403 --- /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}/bin/skydns"; + }; + }; + + environment.systemPackages = [ cfg.package ]; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/smokeping.nix b/nixpkgs/nixos/modules/services/networking/smokeping.nix new file mode 100644 index 000000000000..c41d0edaf17f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/smokeping.nix @@ -0,0 +1,316 @@ +{ 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.string; + 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.string; + default = "http://${cfg.hostName}:${builtins.toString cfg.port}/smokeping.cgi"; + example = "https://somewhere.example.com/smokeping.cgi"; + description = "URL to the smokeping cgi."; + }; + config = mkOption { + type = types.nullOr types.string; + default = null; + description = "Full smokeping config supplied by the user. Overrides " + + "and replaces any other configuration supplied."; + }; + databaseConfig = mkOption { + type = types.string; + 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.string; + default = config.networking.hostName; + example = "somewhere.example.com"; + description = "DNS name for the urls generated in the cgi."; + }; + imgUrl = mkOption { + type = types.string; + default = "http://${cfg.hostName}:${builtins.toString cfg.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.string; + default = ""; + example = "localhost"; + description = "Use this SMTP server to send alerts"; + }; + owner = mkOption { + type = types.string; + default = "nobody"; + example = "Joe Admin"; + description = "Real name of the owner of the instance"; + }; + ownerEmail = mkOption { + type = types.string; + 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.string; + 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.string; + default = "${pkgs.smokeping}/etc/basepage.html.dist"; + description = "Default page layout for the web UI."; + }; + probeConfig = mkOption { + type = types.string; + 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.string; + default = "${cfg.package}/etc/smokemail.dist"; + description = "Specify the smokemail template for alerts."; + }; + targetConfig = mkOption { + type = types.string; + 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.string; + 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 = singleton { + name = 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 + cp ${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"; + }; + }; +} + 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..0046dcd366fa --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/softether.nix @@ -0,0 +1,165 @@ +{ 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.string; + 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"; + after = [ "keys.target" ]; + wants = [ "keys.target" ]; + 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/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..eca599afb33b --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ssh/lshd.nix @@ -0,0 +1,176 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (pkgs) lsh; + + cfg = config.services.lshd; + +in + +{ + + ###### interface + + options = { + + services.lshd = { + + enable = mkOption { + 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 { + default = true; + description = ''Whether to enable syslog output.''; + }; + + passwordAuthentication = mkOption { + default = true; + description = ''Whether to enable password authentication.''; + }; + + publicKeyAuthentication = mkOption { + default = true; + description = ''Whether to enable public key authentication.''; + }; + + rootLogin = mkOption { + 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 { + default = true; + description = ''Whether to enable TCP/IP forwarding.''; + }; + + x11Forwarding = mkOption { + 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..91fc7d72bc6d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/ssh/sshd.nix @@ -0,0 +1,512 @@ +{ 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 -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 + +{ + + ###### 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."; + }; + + 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. + ''; + }; + + }; + + users.users = mkOption { + type = with types; loaOf (submodule userOptions); + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + users.users.sshd = + { isSystemUser = true; + description = "SSH privilege separation user"; + }; + + 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 + + AuthorizedKeysFile ${toString cfg.authorizedKeysFiles} + + ${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 + ''} + + ''; + + 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..0222e8ce8b58 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/sslh.nix @@ -0,0 +1,165 @@ +{ 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 { + users.users.${user} = { + description = "sslh daemon user"; + isSystemUser = true; + }; + + 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 = { + User = user; + Group = "nogroup"; + 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..817b5ec55f78 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/strongswan-swanctl/module.nix @@ -0,0 +1,85 @@ +{ 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" "keys.target" ]; + wants = [ "keys.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..95a174122d04 --- /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..193ad27f035a --- /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..41b69039ba7a --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/strongswan.nix @@ -0,0 +1,169 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (builtins) toFile; + inherit (lib) concatMapStringsSep concatStringsSep mapAttrsToList + mkIf mkEnableOption mkOption types; + + 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 = { + "%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 + wants = [ "keys.target" ]; + after = [ "network-online.target" "keys.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..3fbf6eb60e9d --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/stubby.nix @@ -0,0 +1,214 @@ +{ 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} + 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 = '' + Add additional 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 = { + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; + ExecStart = "${pkgs.stubby}/bin/stubby -C ${confFile} ${optionalString cfg.debugLogging "-l"}"; + DynamicUser = true; + }; + }; + }; +} diff --git a/nixpkgs/nixos/modules/services/networking/stunnel.nix b/nixpkgs/nixos/modules/services/networking/stunnel.nix new file mode 100644 index 000000000000..89a14966eca7 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/stunnel.nix @@ -0,0 +1,221 @@ +{ 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.string; + description = "IP:Port on which connections should be accepted."; + }; + + connect = mkOption { + type = types.string; + 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.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 string; + 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 string; + default = "nobody"; + description = "The user under which stunnel runs."; + }; + + group = mkOption { + type = with types; nullOr string; + 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.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"; + }; + }; + + }; + +} diff --git a/nixpkgs/nixos/modules/services/networking/supplicant.nix b/nixpkgs/nixos/modules/services/networking/supplicant.nix new file mode 100644 index 000000000000..35c1e649e2e1 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/supplicant.nix @@ -0,0 +1,251 @@ +{ 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" ]; + # Receive restart event after resume + partOf = [ "post-resume.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..64eb11068329 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/supybot.nix @@ -0,0 +1,88 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.supybot; + +in + +{ + + options = { + + services.supybot = { + + enable = mkOption { + default = false; + description = "Enable Supybot, an IRC bot"; + }; + + stateDir = mkOption { + # Setting this to /var/lib/supybot caused useradd to fail + default = "/home/supybot"; + description = "The root directory, logs and plugins are stored here"; + }; + + configFile = mkOption { + type = types.path; + description = '' + Path to a 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). + ''; + }; + + }; + + }; + + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.pythonPackages.limnoria ]; + + users.users = singleton { + name = "supybot"; + uid = config.ids.uids.supybot; + group = "supybot"; + description = "Supybot IRC bot user"; + home = cfg.stateDir; + createHome = true; + }; + + users.groups.supybot = { + name = "supybot"; + gid = config.ids.gids.supybot; + }; + + systemd.services.supybot = { + description = "Supybot, an IRC bot"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.pythonPackages.limnoria ]; + preStart = '' + cd ${cfg.stateDir} + mkdir -p backup conf data plugins logs/plugins tmp web + ln -sf ${cfg.configFile} supybot.cfg + # This needs to be created afresh every time + rm -f supybot.cfg.bak + ''; + + serviceConfig = { + ExecStart = "${pkgs.pythonPackages.limnoria}/bin/supybot ${cfg.stateDir}/supybot.cfg"; + PIDFile = "/run/supybot.pid"; + User = "supybot"; + Group = "supybot"; + UMask = "0007"; + Restart = "on-abort"; + StartLimitInterval = "5m"; + StartLimitBurst = "1"; + }; + }; + + }; +} 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..126f5b7b527b --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/syncthing.nix @@ -0,0 +1,444 @@ +{ 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; + }) (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 ({ config, ... }: { + options = { + + name = mkOption { + type = types.str; + default = config._module.args.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 = { + "/home/user/sync" = { + id = "syncme"; + devices = [ "bigbox" ]; + }; + }; + type = types.attrsOf (types.submodule ({ config, ... }: { + 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 = config._module.args.name; + description = '' + The path to the folder which should be shared. + ''; + }; + + id = mkOption { + type = types.str; + default = config._module.args.name; + description = '' + The id of the folder. Must be the same on all devices. + ''; + }; + + label = mkOption { + type = types.str; + default = config._module.args.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. + ''; + }; + + 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} + ''; + }; + }; + 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/tcpcrypt.nix b/nixpkgs/nixos/modules/services/networking/tcpcrypt.nix new file mode 100644 index 000000000000..a0ccb9950094 --- /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 { + 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 = singleton { + name = "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..b1d23372955e --- /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"; + }; + users.groups.thelounge = {}; + systemd.services.thelounge = { + description = "The Lounge web IRC client"; + wantedBy = [ "multi-user.target" ]; + environment = { THELOUNGE_HOME = dataDir; }; + 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..7d5db71601ef --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tinydns.nix @@ -0,0 +1,54 @@ +{ 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 = {}; + + systemd.services.tinydns = { + description = "djbdns tinydns server"; + wantedBy = [ "multi-user.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..1d3492151690 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/tox-bootstrapd.nix @@ -0,0 +1,80 @@ +{ 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 = singleton + { name = "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..7830dfb1834c --- /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.string; + 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.string; + default = []; + example = ''[ "toxid1" "toxid2" ]''; + description = "peers to automacally 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/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..3cf82e8839bb --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/unbound.nix @@ -0,0 +1,141 @@ +{ 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"; + + 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 = [ pkgs.unbound ]; + + 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 '' + ${pkgs.unbound}/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 = "${pkgs.unbound}/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..6239c88b7e41 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/unifi.nix @@ -0,0 +1,188 @@ +{ 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 = [ + "e '${stateDir}' 0700 unifi - - -" + "d '${stateDir}/data' 0700 unifi - - -" + ]; + + 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"; + + preStart = '' + # Create the volatile webapps + rm -rf "${stateDir}/webapps" + mkdir -p "${stateDir}/webapps" + ln -s "${cfg.unifiPackage}/webapps/ROOT" "${stateDir}/webapps/ROOT" + ''; + + postStop = '' + rm -rf "${stateDir}/webapps" + ''; + + serviceConfig = { + Type = "simple"; + ExecStart = "${(removeSuffix "\n" cmd)} start"; + ExecStop = "${(removeSuffix "\n" cmd)} stop"; + User = "unifi"; + UMask = "0077"; + WorkingDirectory = "${stateDir}"; + }; + }; + + }; + + meta.maintainers = with lib.maintainers; [ erictapen ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/vsftpd.nix b/nixpkgs/nixos/modules/services/networking/vsftpd.nix new file mode 100644 index 000000000000..31e1e65fa9ca --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/vsftpd.nix @@ -0,0 +1,235 @@ +{ 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 "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 '' '') + (yesNoOption "ssl_sslv2" "ssl_sslv2" false '' '') + (yesNoOption "ssl_sslv3" "ssl_sslv3" false '' '') + ]; + + 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=YES + nopriv_user=vsftpd + secure_chroot_dir=/var/empty + 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} + ''} + ${cfg.extraConfig} + ''; + +in + +{ + + ###### interface + + options = { + + services.vsftpd = { + + enable = mkOption { + default = false; + description = "Whether to enable the vsftpd FTP server."; + }; + + 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. + ''; + }; + + 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.string; + 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 = singleton + { 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!"; + }; + + users.users = + [ { name = "vsftpd"; + uid = config.ids.uids.vsftpd; + description = "VSFTPD user"; + home = "/homeless-shelter"; + } + ] ++ optional cfg.anonymousUser + { 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.services.vsftpd = + { description = "Vsftpd Server"; + + wantedBy = [ "multi-user.target" ]; + + preStart = + optionalString cfg.anonymousUser + '' + mkdir -p -m 555 ${cfg.anonymousUserHome} + chown -R ftp:ftp ${cfg.anonymousUserHome} + ''; + + serviceConfig.ExecStart = "@${vsftpd}/sbin/vsftpd vsftpd ${configFile}"; + serviceConfig.Restart = "always"; + serviceConfig.Type = "forking"; + }; + + }; + +} 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..4b76350ecf8a --- /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..b770d47d269e --- /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 = [ 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..03c6bd28aaba --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/wicd.nix @@ -0,0 +1,39 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + + ###### interface + + options = { + + networking.wicd.enable = mkOption { + 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..4176da2c8cb8 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/wireguard.nix @@ -0,0 +1,408 @@ +{ 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. + ''; + }; + }; + + }; + + # 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 <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; + 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 != false) + (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 != false) + (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; + 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} + + ip link add dev ${name} type wireguard + + ${concatMapStringsSep "\n" (ip: + "ip address add ${ip} dev ${name}" + ) values.ips} + + wg set ${name} private-key ${privKey} ${ + optionalString (values.listenPort != null) " listen-port ${toString values.listenPort}"} + + ip link set up dev ${name} + + ${values.postSetup} + ''; + + postStop = '' + ip link del dev ${name} + ${values.postShutdown} + ''; + }; + +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}.generatePrivateKey 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 = [ 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..63e59e7c8fac --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/wpa_supplicant.nix @@ -0,0 +1,252 @@ +{ 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. + ''; + }; + + 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 = { + psk = "abcdefgh"; + }; + "free.wifi" = {}; + } + ''; + }; + + 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 = '' + ${if ifaces == [] then '' + for i in $(cd /sys/class/net && echo *); do + DEVTYPE= + source /sys/class/net/$i/uevent + if [ "$DEVTYPE" = "wlan" -o -e /sys/class/net/$i/wireless ]; then + ifaces="$ifaces''${ifaces:+ -N} -i$i" + fi + done + '' else '' + ifaces="${concatStringsSep " -N " (map (i: "-i${i}") ifaces)}" + ''} + exec wpa_supplicant -s -u -D${cfg.driver} -c ${configFile} $ifaces + ''; + }; + + powerManagement.resumeCommands = '' + ${config.systemd.package}/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+="${config.systemd.package}/bin/systemctl try-restart wpa_supplicant.service" + ''; + }; + + meta.maintainers = with lib.maintainers; [ globin ]; +} diff --git a/nixpkgs/nixos/modules/services/networking/xinetd.nix b/nixpkgs/nixos/modules/services/networking/xinetd.nix new file mode 100644 index 000000000000..2d7cd5cebb48 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/xinetd.nix @@ -0,0 +1,152 @@ +{ 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 = mkOption { + default = false; + description = '' + Whether to enable the xinetd super-server daemon. + ''; + }; + + services.xinetd.extraDefaults = mkOption { + default = ""; + type = types.string; + 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.string; + example = "login"; + description = "Name of the service."; + }; + + protocol = mkOption { + type = types.string; + 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.string; + default = "nobody"; + description = "User account for the service"; + }; + + server = mkOption { + type = types.string; + example = "/foo/bin/ftpd"; + description = "Path of the program that implements the service."; + }; + + serverArgs = mkOption { + type = types.string; + default = ""; + description = "Command-line arguments for the server program."; + }; + + flags = mkOption { + type = types.string; + 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..d0a3ed7bb5e0 --- /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.string; + description = "The server-side IP address."; + default = "10.125.125.1"; + }; + + clientIpRange = mkOption { + type = types.string; + 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/zerobin.nix b/nixpkgs/nixos/modules/services/networking/zerobin.nix new file mode 100644 index 000000000000..06ccd7032e6c --- /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..f4988a902685 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/zeronet.nix @@ -0,0 +1,122 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.services.zeronet; + + zConfFile = pkgs.writeTextFile { + name = "zeronet.conf"; + + text = '' + [global] + data_dir = ${cfg.dataDir} + log_dir = ${cfg.logDir} + '' + lib.optionalString (cfg.port != null) '' + ui_port = ${toString cfg.port} + '' + lib.optionalString (cfg.fileserverPort != null) '' + fileserver_port = ${toString cfg.fileserverPort} + '' + lib.optionalString (cfg.torAlways) '' + tor = always + '' + cfg.extraConfig; + }; +in with lib; { + options.services.zeronet = { + enable = mkEnableOption "zeronet"; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/zeronet"; + example = "/home/okina/zeronet"; + description = "Path to the zeronet data directory."; + }; + + logDir = mkOption { + type = types.path; + default = "/var/log/zeronet"; + example = "/home/okina/zeronet/log"; + description = "Path to the zeronet log directory."; + }; + + port = mkOption { + type = types.nullOr types.int; + default = null; + 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."; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + + description = '' + Extra configuration. Contents will be added verbatim to the + configuration file at the end. + ''; + }; + }; + + config = mkIf cfg.enable { + services.tor = mkIf cfg.tor { + enable = true; + controlPort = 9051; + extraConfig = '' + CacheDirectoryGroupReadable 1 + CookieAuthentication 1 + CookieAuthFileGroupReadable 1 + ''; + }; + + systemd.tmpfiles.rules = [ + "d '${cfg.dataDir}' 750 zeronet zeronet - -" + "d '${cfg.logDir}' 750 zeronet zeronet - -" + ]; + + systemd.services.zeronet = { + description = "zeronet"; + after = [ "network.target" (optionalString cfg.tor "tor.service") ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + PrivateTmp = "yes"; + User = "zeronet"; + Group = "zeronet"; + ExecStart = "${pkgs.zeronet}/bin/zeronet --config_file ${zConfFile}"; + }; + }; + + users = { + groups.zeronet.gid = config.ids.gids.zeronet; + + users.zeronet = { + description = "zeronet service user"; + home = cfg.dataDir; + createHome = true; + group = "zeronet"; + extraGroups = mkIf cfg.tor [ "tor" ]; + uid = config.ids.uids.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..764af3846fe5 --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/zerotierone.nix @@ -0,0 +1,67 @@ +{ 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"; + path = [ cfg.package ]; + bindsTo = [ "network-online.target" ]; + after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + 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"; + }; + }; + + # 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 ]; + }; +} 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..05f97bfa539f --- /dev/null +++ b/nixpkgs/nixos/modules/services/networking/znc/default.nix @@ -0,0 +1,306 @@ +{ config, lib, pkgs, ...}: + +with lib; + +let + + cfg = config.services.znc; + + defaultUser = "znc"; + + modules = pkgs.buildEnv { + name = "znc-modules"; + paths = cfg.modulePackages; + }; + + listenerPorts = concatMap (l: optional (l ? Port) l.Port) + (attrValues (cfg.config.Listener or {})); + + # Converts the config option to a string + semanticString = let + + sortedAttrs = set: sort (l: r: + if l == "extraConfig" then false # Always put extraConfig last + else if isAttrs set.${l} == isAttrs set.${r} then l < r + else isAttrs set.${r} # Attrsets should be last, makes for a nice config + # This last case occurs when any side (but not both) is an attrset + # The order of these is correct when the attrset is on the right + # which we're just returning + ) (attrNames set); + + # Specifies an attrset that encodes the value according to its type + encode = name: value: { + null = []; + bool = [ "${name} = ${boolToString value}" ]; + int = [ "${name} = ${toString value}" ]; + + # extraConfig should be inserted verbatim + string = [ (if name == "extraConfig" then value else "${name} = ${value}") ]; + + # Values like `Foo = [ "bar" "baz" ];` should be transformed into + # Foo=bar + # Foo=baz + list = concatMap (encode name) value; + + # Values like `Foo = { bar = { Baz = "baz"; Qux = "qux"; Florps = null; }; };` should be transmed into + # <Foo bar> + # Baz=baz + # Qux=qux + # </Foo> + set = concatMap (subname: 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 = (builtins.parseDrvName pkgs.znc.name).version; + Listener.l.Port = mkDefault 5000; + Listener.l.SSL = mkDefault true; + }; + }; + + networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall listenerPorts; + + systemd.services.znc = { + description = "ZNC Server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + serviceConfig = { + User = cfg.user; + Group = cfg.group; + Restart = "always"; + ExecStart = "${pkgs.znc}/bin/znc --foreground --datadir ${cfg.dataDir} ${escapeShellArgs cfg.extraFlags}"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID"; + }; + preStart = '' + mkdir -p ${cfg.dataDir}/configs + + # If mutable, regenerate conf file every time. + ${optionalString (!cfg.mutable) '' + echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated." + rm -f ${cfg.dataDir}/configs/znc.conf + ''} + + # Ensure essential files exist. + if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then + echo "No znc.conf file found in ${cfg.dataDir}. Creating one now." + cp --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf + chmod u+rw ${cfg.dataDir}/configs/znc.conf + chown ${cfg.user} ${cfg.dataDir}/configs/znc.conf + fi + + if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then + echo "No znc.pem file found in ${cfg.dataDir}. Creating one now." + ${pkgs.znc}/bin/znc --makepem --datadir ${cfg.dataDir} + fi + + # Symlink modules + rm ${cfg.dataDir}/modules || true + ln -fs ${modules}/lib/znc ${cfg.dataDir}/modules + ''; + }; + + users.users = optional (cfg.user == defaultUser) + { name = defaultUser; + description = "ZNC server daemon owner"; + group = defaultUser; + uid = config.ids.uids.znc; + home = cfg.dataDir; + createHome = true; + }; + + users.groups = optional (cfg.user == defaultUser) + { name = defaultUser; + gid = config.ids.gids.znc; + members = [ defaultUser ]; + }; + + }; +} 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 ...";`. + '') + ]; +} |