From cd6374e641420ade3603f0fd23fe9afac5c443bc Mon Sep 17 00:00:00 2001 From: tv Date: Wed, 8 Apr 2015 01:07:01 +0200 Subject: I Like To Move It --- modules/tv/base-cac-CentOS-7-64bit.nix | 27 ++ modules/tv/base.nix | 17 + modules/tv/ejabberd.nix | 856 +++++++++++++++++++++++++++++++++ modules/tv/exim-retiolum.nix | 126 +++++ modules/tv/exim-smarthost.nix | 474 ++++++++++++++++++ modules/tv/nginx.nix | 30 ++ modules/tv/retiolum.nix | 228 +++++++++ modules/tv/sanitize.nix | 8 + modules/tv/synaptics.nix | 14 + modules/tv/tools.nix | 101 ++++ modules/tv/urxvt.nix | 24 + modules/tv/users.nix | 221 +++++++++ modules/tv/xserver.nix | 40 ++ 13 files changed, 2166 insertions(+) create mode 100644 modules/tv/base-cac-CentOS-7-64bit.nix create mode 100644 modules/tv/base.nix create mode 100644 modules/tv/ejabberd.nix create mode 100644 modules/tv/exim-retiolum.nix create mode 100644 modules/tv/exim-smarthost.nix create mode 100644 modules/tv/nginx.nix create mode 100644 modules/tv/retiolum.nix create mode 100644 modules/tv/sanitize.nix create mode 100644 modules/tv/synaptics.nix create mode 100644 modules/tv/tools.nix create mode 100644 modules/tv/urxvt.nix create mode 100644 modules/tv/users.nix create mode 100644 modules/tv/xserver.nix (limited to 'modules/tv') diff --git a/modules/tv/base-cac-CentOS-7-64bit.nix b/modules/tv/base-cac-CentOS-7-64bit.nix new file mode 100644 index 00000000..42ab481b --- /dev/null +++ b/modules/tv/base-cac-CentOS-7-64bit.nix @@ -0,0 +1,27 @@ +{ config, pkgs, ... }: + +{ + boot.loader.grub.device = "/dev/sda"; + boot.loader.grub.enable = true; + boot.loader.grub.version = 2; + + boot.initrd.availableKernelModules = [ + "ata_piix" + "vmw_pvscsi" + ]; + + fileSystems."/" = { + device = "/dev/centos/root"; + fsType = "xfs"; + }; + + fileSystems."/boot" = { + device = "/dev/sda1"; + fsType = "xfs"; + }; + + swapDevices = [ + { device = "/dev/centos/swap"; } + ]; +} + diff --git a/modules/tv/base.nix b/modules/tv/base.nix new file mode 100644 index 00000000..76c8b897 --- /dev/null +++ b/modules/tv/base.nix @@ -0,0 +1,17 @@ +{ config, pkgs, ... }: + +{ + time.timeZone = "Europe/Berlin"; + + nix.maxJobs = 8; + nix.useChroot = true; + # TODO check if both are required: + nix.chrootDirs = [ "/etc/protocols" pkgs.iana_etc.outPath ]; + + nix.trustedBinaryCaches = [ + "https://cache.nixos.org" + "http://cache.nixos.org" + "http://hydra.nixos.org" + ]; + +} diff --git a/modules/tv/ejabberd.nix b/modules/tv/ejabberd.nix new file mode 100644 index 00000000..e836d2cd --- /dev/null +++ b/modules/tv/ejabberd.nix @@ -0,0 +1,856 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + inherit (pkgs) ejabberd writeScript writeScriptBin utillinux; + inherit (lib) makeSearchPath; + + cfg = config.services.ejabberd-cd; + + + +in + +{ + + ####### interface + + options = { + + services.ejabberd-cd = { + + enable = mkOption { + default = false; + description = "Whether to enable ejabberd server"; + }; + + config = mkOption { + type = types.string; + default = ""; + description = '' + TODO + ''; + }; + + user = mkOption { + type = types.string; + default = "ejabberd"; + description = '' + TODO + ''; + }; + + group = mkOption { + type = types.string; + default = "ejabberd"; + description = '' + TODO + ''; + }; + + + # spoolDir = mkOption { + # default = "/var/lib/ejabberd"; + # description = "Location of the spooldir of ejabberd"; + # }; + + # logsDir = mkOption { + # default = "/var/log/ejabberd"; + # description = "Location of the logfile directory of ejabberd"; + # }; + + # confDir = mkOption { + # default = "/var/ejabberd"; + # description = "Location of the config directory of ejabberd"; + # }; + + # virtualHosts = mkOption { + # default = "\"localhost\""; + # description = "Virtualhosts that ejabberd should host. Hostnames are surrounded with doublequotes and separated by commas"; + # }; + + # loadDumps = mkOption { + # default = []; + # description = "Configuration dump that should be loaded on the first startup"; + # example = literalExample "[ ./myejabberd.dump ]"; + # }; + + # config + }; + + }; + + + ####### implementation + + config = + let + my-ejabberdctl = writeScriptBin "ejabberdctl" '' + #! /bin/sh + set -euf + exec env \ + SPOOLDIR=/var/ejabberd \ + EJABBERD_CONFIG_PATH=/etc/ejabberd.cfg \ + ${ejabberd}/bin/ejabberdctl \ + --logs /var/ejabberd \ + "$@" + ''; + in + mkIf cfg.enable { + #environment.systemPackages = [ pkgs.ejabberd ]; + + environment = { + etc."ejabberd.cfg".text = '' + %%% + %%% ejabberd configuration file + %%% + %%%' + + %%% The parameters used in this configuration file are explained in more detail + %%% in the ejabberd Installation and Operation Guide. + %%% Please consult the Guide in case of doubts, it is included with + %%% your copy of ejabberd, and is also available online at + %%% http://www.process-one.net/en/ejabberd/docs/ + + %%% This configuration file contains Erlang terms. + %%% In case you want to understand the syntax, here are the concepts: + %%% + %%% - The character to comment a line is % + %%% + %%% - Each term ends in a dot, for example: + %%% override_global. + %%% + %%% - A tuple has a fixed definition, its elements are + %%% enclosed in {}, and separated with commas: + %%% {loglevel, 4}. + %%% + %%% - A list can have as many elements as you want, + %%% and is enclosed in [], for example: + %%% [http_poll, web_admin, tls] + %%% + %%% - A keyword of ejabberd is a word in lowercase. + %%% Strings are enclosed in "" and can contain spaces, dots, ... + %%% {language, "en"}. + %%% {ldap_rootdn, "dc=example,dc=com"}. + %%% + %%% - This term includes a tuple, a keyword, a list, and two strings: + %%% {hosts, ["jabber.example.net", "im.example.com"]}. + %%% + + + %%%. ======================= + %%%' OVERRIDE STORED OPTIONS + + %% + %% Override the old values stored in the database. + %% + + %% + %% Override global options (shared by all ejabberd nodes in a cluster). + %% + %%override_global. + + %% + %% Override local options (specific for this particular ejabberd node). + %% + %%override_local. + + %% + %% Remove the Access Control Lists before new ones are added. + %% + %%override_acls. + + + %%%. ========= + %%%' DEBUGGING + + %% + %% loglevel: Verbosity of log files generated by ejabberd. + %% 0: No ejabberd log at all (not recommended) + %% 1: Critical + %% 2: Error + %% 3: Warning + %% 4: Info + %% 5: Debug + %% + {loglevel, 3}. + + %% + %% watchdog_admins: Only useful for developers: if an ejabberd process + %% consumes a lot of memory, send live notifications to these XMPP + %% accounts. + %% + %%{watchdog_admins, ["bob@example.com"]}. + + + %%%. ================ + %%%' SERVED HOSTNAMES + + %% + %% hosts: Domains served by ejabberd. + %% You can define one or several, for example: + %% {hosts, ["example.net", "example.com", "example.org"]}. + %% + {hosts, ["jabber.viljetic.de"]}. + + %% + %% route_subdomains: Delegate subdomains to other XMPP servers. + %% For example, if this ejabberd serves example.org and you want + %% to allow communication with an XMPP server called im.example.org. + %% + %%{route_subdomains, s2s}. + + + %%%. =============== + %%%' LISTENING PORTS + + %% + %% listen: The ports ejabberd will listen on, which service each is handled + %% by and what options to start it with. + %% + {listen, + [ + + {5222, ejabberd_c2s, [ + + %% + %% If TLS is compiled in and you installed a SSL + %% certificate, specify the full path to the + %% file and uncomment this line: + %% + starttls, + {certfile, "/etc/nixos/secrets/ejabberd.cd.retiolum.pem"}, + + {access, c2s}, + {shaper, c2s_shaper}, + {max_stanza_size, 65536} + ]}, + + {5269, ejabberd_s2s_in, [ + {shaper, s2s_shaper}, + {max_stanza_size, 131072} + ]}, + + %% + %% ejabberd_service: Interact with external components (transports, ...) + %% + %%{8888, ejabberd_service, [ + %% {access, all}, + %% {shaper_rule, fast}, + %% {ip, {127, 0, 0, 1}}, + %% {hosts, ["icq.example.org", "sms.example.org"], + %% [{password, "secret"}] + %% } + %% ]}, + + %% + %% ejabberd_stun: Handles STUN Binding requests + %% + %%{{3478, udp}, ejabberd_stun, []}, + + {5280, ejabberd_http, [ + %%{request_handlers, + %% [ + %% {["pub", "archive"], mod_http_fileserver} + %% ]}, + captcha, + http_bind, + http_poll, + %%register, + web_admin + ]} + + ]}. + + %% + %% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. + %% Allowed values are: false optional required required_trusted + %% You must specify a certificate file. + %% + {s2s_use_starttls, required}. + + %% + %% s2s_certfile: Specify a certificate file. + %% + {s2s_certfile, "/etc/nixos/secrets/ejabberd.cd.retiolum.pem"}. + + %% + %% domain_certfile: Specify a different certificate for each served hostname. + %% + %%{domain_certfile, "example.org", "/path/to/example_org.pem"}. + %%{domain_certfile, "example.com", "/path/to/example_com.pem"}. + + %% + %% S2S whitelist or blacklist + %% + %% Default s2s policy for undefined hosts. + %% + %%{s2s_default_policy, allow}. + + %% + %% Allow or deny communication with specific servers. + %% + %%{{s2s_host, "goodhost.org"}, allow}. + %%{{s2s_host, "badhost.org"}, deny}. + + %% + %% Outgoing S2S options + %% + %% Preferred address families (which to try first) and connect timeout + %% in milliseconds. + %% + %%{outgoing_s2s_options, [ipv4, ipv6], 10000}. + + + %%%. ============== + %%%' AUTHENTICATION + + %% + %% auth_method: Method used to authenticate the users. + %% The default method is the internal. + %% If you want to use a different method, + %% comment this line and enable the correct ones. + %% + {auth_method, internal}. + %% + %% Store the plain passwords or hashed for SCRAM: + %%{auth_password_format, plain}. + %%{auth_password_format, scram}. + %% + %% Define the FQDN if ejabberd doesn't detect it: + %%{fqdn, "server3.example.com"}. + + %% + %% Authentication using external script + %% Make sure the script is executable by ejabberd. + %% + %%{auth_method, external}. + %{extauth_program, "$ {ejabberd-auth}"}. + + %% + %% Authentication using ODBC + %% Remember to setup a database in the next section. + %% + %%{auth_method, odbc}. + + %% + %% Authentication using PAM + %% + %%{auth_method, pam}. + %%{pam_service, "pamservicename"}. + + %% + %% Authentication using LDAP + %% + %%{auth_method, ldap}. + %% + %% List of LDAP servers: + %%{ldap_servers, ["localhost"]}. + %% + %% Encryption of connection to LDAP servers: + %%{ldap_encrypt, none}. + %%{ldap_encrypt, tls}. + %% + %% Port to connect to on LDAP servers: + %%{ldap_port, 389}. + %%{ldap_port, 636}. + %% + %% LDAP manager: + %%{ldap_rootdn, "dc=example,dc=com"}. + %% + %% Password of LDAP manager: + %%{ldap_password, "******"}. + %% + %% Search base of LDAP directory: + %%{ldap_base, "dc=example,dc=com"}. + %% + %% LDAP attribute that holds user ID: + %%{ldap_uids, [{"mail", "%u@mail.example.org"}]}. + %% + %% LDAP filter: + %%{ldap_filter, "(objectClass=shadowAccount)"}. + + %% + %% Anonymous login support: + %% auth_method: anonymous + %% anonymous_protocol: sasl_anon | login_anon | both + %% allow_multiple_connections: true | false + %% + %%{host_config, "public.example.org", [{auth_method, anonymous}, + %% {allow_multiple_connections, false}, + %% {anonymous_protocol, sasl_anon}]}. + %% + %% To use both anonymous and internal authentication: + %% + %%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}. + + + %%%. ============== + %%%' DATABASE SETUP + + %% ejabberd by default uses the internal Mnesia database, + %% so you do not necessarily need this section. + %% This section provides configuration examples in case + %% you want to use other database backends. + %% Please consult the ejabberd Guide for details on database creation. + + %% + %% MySQL server: + %% + %%{odbc_server, {mysql, "server", "database", "username", "password"}}. + %% + %% If you want to specify the port: + %%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}. + + %% + %% PostgreSQL server: + %% + %%{odbc_server, {pgsql, "server", "database", "username", "password"}}. + %% + %% If you want to specify the port: + %%{odbc_server, {pgsql, "server", 1234, "database", "username", "password"}}. + %% + %% If you use PostgreSQL, have a large database, and need a + %% faster but inexact replacement for "select count(*) from users" + %% + %%{pgsql_users_number_estimate, true}. + + %% + %% ODBC compatible or MSSQL server: + %% + %%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}. + + %% + %% Number of connections to open to the database for each virtual host + %% + %%{odbc_pool_size, 10}. + + %% + %% Interval to make a dummy SQL request to keep the connections to the + %% database alive. Specify in seconds: for example 28800 means 8 hours + %% + %%{odbc_keepalive_interval, undefined}. + + + %%%. =============== + %%%' TRAFFIC SHAPERS + + %% + %% The "normal" shaper limits traffic speed to 1000 B/s + %% + {shaper, normal, {maxrate, 1000}}. + + %% + %% The "fast" shaper limits traffic speed to 50000 B/s + %% + {shaper, fast, {maxrate, 50000}}. + + %% + %% This option specifies the maximum number of elements in the queue + %% of the FSM. Refer to the documentation for details. + %% + {max_fsm_queue, 1000}. + + + %%%. ==================== + %%%' ACCESS CONTROL LISTS + + %% + %% The 'admin' ACL grants administrative privileges to XMPP accounts. + %% You can put here as many accounts as you want. + %% + %%{acl, admin, {user, "aleksey", "localhost"}}. + %%{acl, admin, {user, "ermine", "example.org"}}. + + %% + %% Blocked users + %% + %%{acl, blocked, {user, "baduser", "example.org"}}. + %%{acl, blocked, {user, "test"}}. + + %% + %% Local users: don't modify this line. + %% + {acl, local, {user_regexp, ""}}. + + %% + %% More examples of ACLs + %% + %%{acl, jabberorg, {server, "jabber.org"}}. + %%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. + %%{acl, test, {user_regexp, "^test"}}. + %%{acl, test, {user_glob, "test*"}}. + + %% + %% Define specific ACLs in a virtual host. + %% + %%{host_config, "localhost", + %% [ + %% {acl, admin, {user, "bob-local", "localhost"}} + %% ] + %%}. + + + %%%. ============ + %%%' ACCESS RULES + + %% Maximum number of simultaneous sessions allowed for a single user: + {access, max_user_sessions, [{10, all}]}. + + %% Maximum number of offline messages that users can have: + {access, max_user_offline_messages, [{5000, admin}, {100, all}]}. + + %% This rule allows access only for local users: + {access, local, [{allow, local}]}. + + %% Only non-blocked users can use c2s connections: + {access, c2s, [{deny, blocked}, + {allow, all}]}. + + %% For C2S connections, all users except admins use the "normal" shaper + {access, c2s_shaper, [{none, admin}, + {normal, all}]}. + + %% All S2S connections use the "fast" shaper + {access, s2s_shaper, [{fast, all}]}. + + %% Only admins can send announcement messages: + {access, announce, [{allow, admin}]}. + + %% Only admins can use the configuration interface: + {access, configure, [{allow, admin}]}. + + %% Admins of this server are also admins of the MUC service: + {access, muc_admin, [{allow, admin}]}. + + %% Only accounts of the local ejabberd server can create rooms: + {access, muc_create, [{allow, local}]}. + + %% All users are allowed to use the MUC service: + {access, muc, [{allow, all}]}. + + %% Only accounts on the local ejabberd server can create Pubsub nodes: + {access, pubsub_createnode, [{allow, local}]}. + + %% In-band registration allows registration of any possible username. + %% To disable in-band registration, replace 'allow' with 'deny'. + {access, register, [{allow, all}]}. + + %% By default the frequency of account registrations from the same IP + %% is limited to 1 account every 10 minutes. To disable, specify: infinity + %%{registration_timeout, 600}. + + %% + %% Define specific Access Rules in a virtual host. + %% + %%{host_config, "localhost", + %% [ + %% {access, c2s, [{allow, admin}, {deny, all}]}, + %% {access, register, [{deny, all}]} + %% ] + %%}. + + + %%%. ================ + %%%' DEFAULT LANGUAGE + + %% + %% language: Default language used for server messages. + %% + {language, "en"}. + + %% + %% Set a different default language in a virtual host. + %% + %%{host_config, "localhost", + %% [{language, "ru"}] + %%}. + + + %%%. ======= + %%%' CAPTCHA + + %% + %% Full path to a script that generates the image. + %% + %%{captcha_cmd, "/lib/ejabberd/priv/bin/captcha.sh"}. + + %% + %% Host for the URL and port where ejabberd listens for CAPTCHA requests. + %% + %%{captcha_host, "example.org:5280"}. + + %% + %% Limit CAPTCHA calls per minute for JID/IP to avoid DoS. + %% + %%{captcha_limit, 5}. + + %%%. ======= + %%%' MODULES + + %% + %% Modules enabled in all ejabberd virtual hosts. + %% + {modules, + [ + {mod_adhoc, []}, + {mod_announce, [{access, announce}]}, % recommends mod_adhoc + {mod_blocking,[]}, % requires mod_privacy + {mod_caps, []}, + {mod_configure,[]}, % requires mod_adhoc + {mod_disco, []}, + %%{mod_echo, [{host, "echo.localhost"}]}, + {mod_irc, []}, + {mod_http_bind, []}, + %%{mod_http_fileserver, [ + %% {docroot, "/var/www"}, + %% {accesslog, "/var/log/ejabberd/access.log"} + %% ]}, + {mod_last, []}, + {mod_muc, [ + %%{host, "conference.@HOST@"}, + {access, muc}, + {access_create, muc_create}, + {access_persistent, muc_create}, + {access_admin, muc_admin} + ]}, + %%{mod_muc_log,[]}, + {mod_offline, [{access_max_user_messages, max_user_offline_messages}]}, + {mod_ping, []}, + %%{mod_pres_counter,[{count, 5}, {interval, 60}]}, + {mod_privacy, []}, + {mod_private, []}, + %%{mod_proxy65,[]}, + {mod_pubsub, [ + {access_createnode, pubsub_createnode}, + {ignore_pep_from_offline, true}, % reduces resource comsumption, but XEP incompliant + %%{ignore_pep_from_offline, false}, % XEP compliant, but increases resource comsumption + {last_item_cache, false}, + {plugins, ["flat", "hometree", "pep"]} % pep requires mod_caps + ]}, + {mod_register, [ + %% + %% Protect In-Band account registrations with CAPTCHA. + %% + %%{captcha_protected, true}, + + %% + %% Set the minimum informational entropy for passwords. + %% + %%{password_strength, 32}, + + %% + %% After successful registration, the user receives + %% a message with this subject and body. + %% + {welcome_message, {"Welcome!", + "Hi.\nWelcome to this XMPP server."}}, + + %% + %% When a user registers, send a notification to + %% these XMPP accounts. + %% + %%{registration_watchers, ["admin1@example.org"]}, + + %% + %% Only clients in the server machine can register accounts + %% + {ip_access, [{allow, "127.0.0.0/8"}, + {deny, "0.0.0.0/0"}]}, + + %% + %% Local c2s or remote s2s users cannot register accounts + %% + %%{access_from, deny}, + + {access, register} + ]}, + %%{mod_register_web, [ + %% + %% When a user registers, send a notification to + %% these XMPP accounts. + %% + %%{registration_watchers, ["admin1@example.org"]} + %% ]}, + {mod_roster, []}, + %%{mod_service_log,[]}, + {mod_shared_roster,[]}, + {mod_stats, []}, + {mod_time, []}, + {mod_vcard, []}, + {mod_version, []} + ]}. + + %% + %% Enable modules with custom options in a specific virtual host + %% + %%{host_config, "localhost", + %% [{{add, modules}, + %% [ + %% {mod_echo, [{host, "mirror.localhost"}]} + %% ] + %% } + %% ]}. + + + %%%. + %%%' + + %%% $Id$ + + %%% Local Variables: + %%% mode: erlang + %%% End: + %%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: + ''; + # TODO properly configured wrapper + systemPackages = [ my-ejabberdctl ]; + }; + #exim_user = ${cfg.user} + #exim_group = ${cfg.group} + #exim_path = /var/setuid-wrappers/exim + #spool_directory = ${cfg.spoolDir} + #${cfg.config} + + users.extraUsers = singleton { + name = "ejabberd"; + description = "TODO"; + uid = 405222; + group = "ejabberd"; + home = "/var/ejabberd"; + createHome = true; + }; + + users.extraGroups = singleton { + name = "ejabberd"; + gid = 405222; + }; + + #security.setuidPrograms = [ "exim" ]; + + systemd.services.ejabberd = { + description = "ejabberd XMPP Daemon"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + reloadIfChanged = true; + serviceConfig = { + ExecStart = "${my-ejabberdctl}/bin/ejabberdctl start"; + ExecStop = "${my-ejabberdctl}/bin/ejabberdctl stop"; + ExecReload = "${my-ejabberdctl}/bin/ejabberdctl restart"; + Type = "oneshot"; + RemainAfterExit = "yes"; + RestartSec = 5; + LimitNOFILE = 16000; + User = "ejabberd"; + Group = "ejabberd"; + }; + }; + + systemd.services.ejabberd-prepare = { + description = "ejabberd XMPP Preparetion Service"; + requiredBy = [ "ejabberd.service" ]; + serviceConfig = { + Type = "oneshot"; + RestartSec = 5; + ExecStart = "${writeScript "ejabberd-prepare" + '' + #! /bin/sh + set -euf + chown ejabberd: /etc/nixos/secrets/ejabberd.cd.retiolum.pem + '' + }"; + }; + }; + + + + }; + + #config = mkIf cfg.enable { + # environment.systemPackages = [ pkgs.ejabberd ]; + + # jobs.ejabberd = + # { description = "EJabberd server"; + + # startOn = "started network-interfaces"; + # stopOn = "stopping network-interfaces"; + + # environment = { + # PATH = "$PATH:${pkgs.ejabberd}/sbin:${pkgs.ejabberd}/bin:${pkgs.coreutils}/bin:${pkgs.bash}/bin:${pkgs.gnused}/bin"; + # }; + + # preStart = + # '' + # PATH="$PATH:${pkgs.ejabberd}/sbin:${pkgs.ejabberd}/bin:${pkgs.coreutils}/bin:${pkgs.bash}/bin:${pkgs.gnused}/bin"; + # + # # Initialise state data + # mkdir -p ${cfg.logsDir} + + # if ! test -d ${cfg.spoolDir} + # then + # initialize=1 + # cp -av ${pkgs.ejabberd}/var/lib/ejabberd /var/lib + # fi + + # #if ! test -d ${cfg.confDir} + # #then + # # mkdir -p ${cfg.confDir} + # # cp ${pkgs.ejabberd}/etc/ejabberd/* ${cfg.confDir} + # # sed -e 's|{hosts, \["localhost"\]}.|{hosts, \[${cfg.virtualHosts}\]}.|' ${pkgs.ejabberd}/etc/ejabberd/ejabberd.cfg > ${cfg.confDir}/ejabberd.cfg + # #fi + # mkdir -p ${cfg.confDir} + + + # ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} start + + # ${if cfg.loadDumps == [] then "" else + # '' + # if [ "$initialize" = "1" ] + # then + # # Wait until the ejabberd server is available for use + # count=0 + # while ! ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} status + # do + # if [ $count -eq 30 ] + # then + # echo "Tried 30 times, giving up..." + # exit 1 + # fi + + # echo "Ejabberd daemon not yet started. Waiting for 1 second..." + # count=$((count++)) + # sleep 1 + # done + + # ${concatMapStrings (dump: + # '' + # echo "Importing dump: ${dump}" + + # if [ -f ${dump} ] + # then + # ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load ${dump} + # elif [ -d ${dump} ] + # then + # for i in ${dump}/ejabberd-dump/* + # do + # ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load $i + # done + # fi + # '') cfg.loadDumps} + # fi + # ''} + # ''; + + # postStop = + # '' + # ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} stop + # ''; + # }; + + # security.pam.services.ejabberd = {}; + + #}; + +} diff --git a/modules/tv/exim-retiolum.nix b/modules/tv/exim-retiolum.nix new file mode 100644 index 00000000..e80358fc --- /dev/null +++ b/modules/tv/exim-retiolum.nix @@ -0,0 +1,126 @@ +{ config, pkgs, ... }: + +{ + services.exim = + # This configuration makes only sense for retiolum-enabled hosts. + # TODO modular configuration + assert config.services.retiolum.enable; + let + # TODO get the hostname from config.services.retiolum. + retiolumHostname = "${config.networking.hostName}.retiolum"; + in + { enable = true; + config = '' + primary_hostname = ${retiolumHostname} + domainlist local_domains = @ : localhost + domainlist relay_to_domains = *.retiolum + hostlist relay_from_hosts = <; 127.0.0.1 ; ::1 + + acl_smtp_rcpt = acl_check_rcpt + acl_smtp_data = acl_check_data + + host_lookup = * + rfc1413_hosts = * + rfc1413_query_timeout = 5s + + log_file_path = syslog + syslog_timestamp = false + syslog_duplication = false + + begin acl + + acl_check_rcpt: + accept hosts = : + control = dkim_disable_verify + + deny message = Restricted characters in address + domains = +local_domains + local_parts = ^[.] : ^.*[@%!/|] + + deny message = Restricted characters in address + domains = !+local_domains + local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./ + + accept local_parts = postmaster + domains = +local_domains + + #accept + # hosts = *.retiolum + # domains = *.retiolum + # control = dkim_disable_verify + + #require verify = sender + + accept hosts = +relay_from_hosts + control = submission + control = dkim_disable_verify + + accept authenticated = * + control = submission + control = dkim_disable_verify + + require message = relay not permitted + domains = +local_domains : +relay_to_domains + + require verify = recipient + + accept + + + acl_check_data: + accept + + + begin routers + + retiolum: + driver = manualroute + domains = ! ${retiolumHostname} : *.retiolum + transport = remote_smtp + route_list = ^.* $0 byname + no_more + + nonlocal: + debug_print = "R: nonlocal for $local_part@$domain" + driver = redirect + domains = ! +local_domains + allow_fail + data = :fail: Mailing to remote domains not supported + no_more + + local_user: + # debug_print = "R: local_user for $local_part@$domain" + driver = accept + check_local_user + # local_part_suffix = +* : -* + # local_part_suffix_optional + transport = home_maildir + cannot_route_message = Unknown user + + + begin transports + + remote_smtp: + driver = smtp + + home_maildir: + driver = appendfile + maildir_format + directory = $home/Maildir + directory_mode = 0700 + delivery_date_add + envelope_to_add + return_path_add + # group = mail + # mode = 0660 + + begin retry + *.retiolum * F,42d,1m + * * F,2h,15m; G,16h,1h,1.5; F,4d,6h + + begin rewrite + + begin authenticators + ''; + }; +} diff --git a/modules/tv/exim-smarthost.nix b/modules/tv/exim-smarthost.nix new file mode 100644 index 00000000..a4c47b39 --- /dev/null +++ b/modules/tv/exim-smarthost.nix @@ -0,0 +1,474 @@ +{ config, pkgs, ... }: + +let + inherit (builtins) toFile; + inherit (pkgs.lib.attrsets) mapAttrs; + inherit (pkgs.lib.strings) concatMapStringsSep; +in + +{ + services.exim = + let + retiolumHostname = "${config.networking.hostName}.retiolum"; + + internet-aliases = [ + { from = "tomislav@viljetic.de"; to = "tv@wu.retiolum"; } + + # (mindestens) lisp-stammtisch und elli haben die: + { from = "tv@viljetic.de"; to = "tv@wu.retiolum"; } + + { from = "tv@destroy.dyn.shackspace.de"; to = "tv@wu.retiolum"; } + + { from = "mirko@viljetic.de"; to = "mv@cd.retiolum"; } + + # TODO killme (wo wird die benutzt?) + { from = "tv@cd.retiolum"; to = "tv@wu.retiolum"; } + + { from = "postmaster@krebsco.de"; to = "tv@wu.retiolum"; } + ]; + + system-aliases = [ + { from = "mailer-daemon"; to = "postmaster"; } + { from = "postmaster"; to = "root"; } + { from = "nobody"; to = "root"; } + { from = "hostmaster"; to = "root"; } + { from = "usenet"; to = "root"; } + { from = "news"; to = "root"; } + { from = "webmaster"; to = "root"; } + { from = "www"; to = "root"; } + { from = "ftp"; to = "root"; } + { from = "abuse"; to = "root"; } + { from = "noc"; to = "root"; } + { from = "security"; to = "root"; } + { from = "root"; to = "tv"; } + { from = "mirko"; to = "mv"; } + ]; + + to-lsearch = concatMapStringsSep "\n" ({ from, to }: "${from}: ${to}"); + lsearch = + mapAttrs (name: set: toFile name (to-lsearch set)) { + inherit internet-aliases; + inherit system-aliases; + }; + in + { + enable = true; + config = + '' + primary_hostname = ${retiolumHostname} + + # HOST_REDIR contains the real destinations for "local_domains". + #HOST_REDIR = /etc/exim4/host_redirect + + + # Domains not listed in local_domains need to be deliverable remotely. + # XXX We abuse local_domains to mean "domains, we're the gateway for". + domainlist local_domains = @ : localhost + #: viljetic.de : SHACK_REDIR_HOSTNAME + domainlist relay_to_domains = + hostlist relay_from_hosts = <; 127.0.0.1 ; ::1 ; 10.243.13.37 + + acl_smtp_rcpt = acl_check_rcpt + acl_smtp_data = acl_check_data + + # av_scanner = clamd:/tmp/clamd + # spamd_address = 127.0.0.1 783 + + # tls_advertise_hosts = * + # tls_certificate = /etc/ssl/exim.crt + # tls_privatekey = /etc/ssl/exim.pem + # (debian) tls_verify_certificates (to check client certs) + + # daemon_smtp_ports = 25 : 465 : 587 + # tls_on_connect_ports = 465 + + # qualify_domain defaults to primary_hostname + # qualify_recipient defaults to qualify_domain + + # allow_domain_literals + + never_users = root + + host_lookup = * + + # ident callbacks for all incoming SMTP calls + rfc1413_hosts = * + rfc1413_query_timeout = 5s + + # sender_unqualified_hosts = + # recipient_unqualified_hosts = + + # percent_hack_domains = + + # arch & debian + #ignore_bounce_errors_after = 2d + #timeout_frozen_after = 7d + # debian + #smtp_banner = $smtp_active_hostname ESMTP Exim $version_number $tod_full + #freeze_tell = postmaster + #trusted_users = uucp + # arch + #split_spool_directory = true + + log_selector = -queue_run +address_rewrite +all_parents +queue_time + log_file_path = syslog + syslog_timestamp = false + syslog_duplication = false + + begin acl + + acl_check_rcpt: + # Accept if the source is local SMTP (i.e. not over TCP/IP). + # We do this by testing for an empty sending host field. + accept hosts = : + # arch & debian: + control = dkim_disable_verify + + deny message = Restricted characters in address + domains = +local_domains + local_parts = ^[.] : ^.*[@%!/|] + + deny message = Restricted characters in address + domains = !+local_domains + local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./ + + accept local_parts = postmaster + domains = +local_domains + + ## feature RETIOLUM_MAIL + #accept + # hosts = *.retiolum + # domains = *.retiolum + # control = dkim_disable_verify + + #require verify = sender + + accept hosts = +relay_from_hosts + control = submission + # debian: control = submission/sender_retain + # arch & debian: + control = dkim_disable_verify + + accept authenticated = * + control = submission + control = dkim_disable_verify + + accept message = relay not permitted 2 + recipients = lsearch;${lsearch.internet-aliases} + + require message = relay not permitted + domains = +local_domains : +relay_to_domains + + require + message = unknown user + verify = recipient/callout + + # deny message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text + # dnslists = black.list.example + # + # warn dnslists = black.list.example + # add_header = X-Warning: $sender_host_address is in a black list at $dnslist_domain + # log_message = found in $dnslist_domain + + # Client SMTP Authorization (csa) checks on the sending host. + # Such checks do DNS lookups for special SRV records. + # require verify = csa + + accept + + + acl_check_data: + # see av_scanner + #deny malware = * + # message = This message contains a virus ($malware_name). + + # Add headers to a message if it is judged to be spam. Before enabling this, + # you must install SpamAssassin. You may also need to set the spamd_address + # option above. + # + # warn spam = nobody + # add_header = X-Spam_score: $spam_score\n\ + # X-Spam_score_int: $spam_score_int\n\ + # X-Spam_bar: $spam_bar\n\ + # X-Spam_report: $spam_report + + # feature HELO_REWRITE + # XXX note that the public ip (162.219.5.183) resolves to viljetic.de + warn + sender_domains = viljetic.de : shackspace.de + set acl_m_special_dom = $sender_address_domain + + accept + + + begin routers + + # feature RETIOLUM_MAIL + retiolum: + debug_print = "R: retiolum for $local_part@$domain" + driver = manualroute + domains = ! ${retiolumHostname} : *.retiolum + transport = retiolum_smtp + route_list = ^.* $0 byname + no_more + + internet_aliases: + debug_print = "R: internet_aliases for $local_part@$domain" + driver = redirect + data = ''${lookup{$local_part@$domain}lsearch{${lsearch.internet-aliases}}} + + dnslookup: + debug_print = "R: dnslookup for $local_part@$domain" + driver = dnslookup + domains = ! +local_domains + transport = remote_smtp + ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 + # if ipv6-enabled then instead use: + # ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1 + + # (debian) same_domain_copy_routing = yes + # (debian) ignore private rfc1918 and APIPA addresses + # (debian) ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8 : 192.168.0.0/16 :\ + # 172.16.0.0/12 : 10.0.0.0/8 : 169.254.0.0/16 :\ + # 255.255.255.255 + + # Fail and bounce if the router does not find the domain in the DNS. + # I.e. no more routers are tried. + # There are a few cases where a dnslookup router will decline to accept an + # address; if such a router is expected to handle "all remaining non-local + # domains", then it is important to set no_more. + no_more + + # XXX this is only used because these "well known aliases" goto tv@cd.retiolum + # TODO bounce everything, there is no @cd.retiolum + system_aliases: + debug_print = "R: system_aliases for $local_part@$domain" + driver = redirect + data = ''${lookup{$local_part}lsearch{${lsearch.system-aliases}}} + + # TODO this is only b/c mv here... send mv's mails somewhere else... + local_user: + debug_print = "R: local_user for $local_part@$domain" + driver = accept + check_local_user + # local_part_suffix = +* : -* + # local_part_suffix_optional + transport = home_maildir + cannot_route_message = Unknown user + + begin transports + + retiolum_smtp: + driver = smtp + retry_include_ip_address = false + # serialize_hosts = TODO-all-slow-hosts + + remote_smtp: + driver = smtp + # debian has also stuff for tls, headers_rewrite and more here + + # feature HELO_REWRITE + # XXX note that the public ip (162.219.5.183) resolves to viljetic.de + helo_data = ''${if eq{$acl_m_special_dom}{} \ + {$primary_hostname} \ + {$acl_m_special_dom} } + + home_maildir: + driver = appendfile + maildir_format + maildir_use_size_file + directory = $home/Mail + directory_mode = 0700 + delivery_date_add + envelope_to_add + return_path_add + + begin retry + *.retiolum * F,42d,1m + * * F,2h,15m; G,16h,1h,1.5; F,4d,6h + + begin rewrite + begin authenticators + ''; + + + # group = mail + # mode = 0660 + + + #address_pipe: + # driver = pipe + # return_output + # + #address_file: + # driver = appendfile + # delivery_date_add + # envelope_to_add + # return_path_add + # + #address_reply: + # driver = autoreply + + + #maildrop_pipe: + # debug_print = "T: maildrop_pipe for $local_part@$domain" + # driver = pipe + # path = "/bin:/usr/bin:/usr/local/bin" + # command = "/usr/bin/maildrop" + # return_path_add + # delivery_date_add + # envelope_to_add + + + + + + ##begin retry + # Address or Domain Error Retries + + # Our host_redirect destinations might be offline a lot. + # TODO define fallback destinations(?) + #lsearch;${lsearch.internet-aliases} * F,42d,1m + + + ## begin rewrite + + # just in case (shackspace.de should already do this) + #tv@shackspace.de tv@SHACK_REDIR_HOSTNAME T + + + ## begin authenticators + #PLAIN: + # driver = plaintext + # server_set_id = $auth2 + # server_prompts = : + # server_condition = Authentication is not yet configured + # server_advertise_condition = ''${if def:tls_in_cipher } + + #LOGIN: + # driver = plaintext + # server_set_id = $auth1 + # server_prompts = <| Username: | Password: + # server_condition = Authentication is not yet configured + # server_advertise_condition = ''${if def:tls_in_cipher } + + + + }; + +} + +# config = '' +# primary_hostname = ${retiolumHostname} +# domainlist local_domains = @ : localhost +# domainlist relay_to_domains = *.retiolum +# hostlist relay_from_hosts = <; 127.0.0.1 ; ::1 +# +# acl_smtp_rcpt = acl_check_rcpt +# acl_smtp_data = acl_check_data +# +# host_lookup = * +# rfc1413_hosts = * +# rfc1413_query_timeout = 5s +# +# log_file_path = syslog +# syslog_timestamp = false +# syslog_duplication = false +# +# begin acl +# +# acl_check_rcpt: +# accept hosts = : +# control = dkim_disable_verify +# +# deny message = Restricted characters in address +# domains = +local_domains +# local_parts = ^[.] : ^.*[@%!/|] +# +# deny message = Restricted characters in address +# domains = !+local_domains +# local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./ +# +# accept local_parts = postmaster +# domains = +local_domains +# +# #accept +# # hosts = *.retiolum +# # domains = *.retiolum +# # control = dkim_disable_verify +# +# #require verify = sender +# +# accept hosts = +relay_from_hosts +# control = submission +# control = dkim_disable_verify +# +# accept authenticated = * +# control = submission +# control = dkim_disable_verify +# +# require message = relay not permitted +# domains = +local_domains : +relay_to_domains +# +# require verify = recipient +# +# accept +# +# +# acl_check_data: +# accept +# +# +# begin routers +# +# retiolum: +# driver = manualroute +# domains = ! ${retiolumHostname} : *.retiolum +# transport = remote_smtp +# route_list = ^.* $0 byname +# no_more +# +# nonlocal: +# debug_print = "R: nonlocal for $local_part@$domain" +# driver = redirect +# domains = ! +local_domains +# allow_fail +# data = :fail: Mailing to remote domains not supported +# no_more +# +# local_user: +# # debug_print = "R: local_user for $local_part@$domain" +# driver = accept +# check_local_user +# # local_part_suffix = +* : -* +# # local_part_suffix_optional +# transport = home_maildir +# cannot_route_message = Unknown user +# +# +# begin transports +# +# remote_smtp: +# driver = smtp +# +# home_maildir: +# driver = appendfile +# maildir_format +# directory = $home/Maildir +# directory_mode = 0700 +# delivery_date_add +# envelope_to_add +# return_path_add +# # group = mail +# # mode = 0660 +# +# begin retry +# *.retiolum * F,42d,1m +# * * F,2h,15m; G,16h,1h,1.5; F,4d,6h +# +# begin rewrite +# +# begin authenticators +# ''; +# }; +#} diff --git a/modules/tv/nginx.nix b/modules/tv/nginx.nix new file mode 100644 index 00000000..8b420613 --- /dev/null +++ b/modules/tv/nginx.nix @@ -0,0 +1,30 @@ +{ config, pkgs, ... }: + +{ + services.nginx = + let + name = config.networking.hostName; + qname = "${name}.retiolum"; + in + { + enable = true; + httpConfig = '' + sendfile on; + server { + listen 80; + server_name ${name} ${qname} localhost; + root /srv/http/${name}; + location ~ ^/~(.+?)(/.*)?$ { + alias /home/$1/public_html$2; + } + } + types { + text/css css; + text/html html; + image/svg+xml svg; + } + default_type text/html; + charset utf-8; + ''; + }; +} diff --git a/modules/tv/retiolum.nix b/modules/tv/retiolum.nix new file mode 100644 index 00000000..578547af --- /dev/null +++ b/modules/tv/retiolum.nix @@ -0,0 +1,228 @@ +{ config, lib, pkgs, ... }: + +with lib; + +{ + + ###### interface + + options = { + services.retiolum = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Enable tinc daemon for Retiolum."; + }; + + name = mkOption { + type = types.string; + default = config.networking.hostName; + # Description stolen from tinc.conf(5). + description = '' + This is the name which identifies this tinc daemon. It must + be unique for the virtual private network this daemon will + connect to. The Name may only consist of alphanumeric and + underscore characters. If Name starts with a $, then the + contents of the environment variable that follows will be + used. In that case, invalid characters will be converted to + underscores. If Name is $HOST, but no such environment + variable exist, the hostname will be read using the + gethostnname() system call This is the name which identifies + the this tinc daemon. + ''; + }; + + generateEtcHosts = mkOption { + type = types.string; + default = "both"; + description = '' + If set to short, long, or both, + then generate entries in /etc/hosts from subnets. + ''; + }; + + network = mkOption { + type = types.string; + default = "retiolum"; + description = '' + The tinc network name. + It is used to generate long host entries, + derive the name of the user account under which tincd runs, + and name the TUN device. + ''; + }; + + tincPackage = mkOption { + type = types.package; + default = pkgs.tinc; + description = "Tincd package to use."; + }; + + hosts = mkOption { + default = null; + description = '' + Hosts package or path to use. + If a path is given, then it will be used to generate an ad-hoc package. + ''; + }; + + iproutePackage = mkOption { + type = types.package; + default = pkgs.iproute; + description = "Iproute2 package to use."; + }; + + + privateKeyFile = mkOption { + # TODO if it's types.path then it gets copied to /nix/store with + # bad unsafe permissions... + type = types.string; + default = "/etc/tinc/retiolum/rsa_key.priv"; + description = "Generate file with tincd -K."; + }; + + connectTo = mkOption { + type = types.listOf types.string; + default = [ "fastpoke" "pigstarter" "kheurop" ]; + description = "TODO describe me"; + }; + + }; + }; + + + ###### implementation + + config = + let + cfg = config.services.retiolum; + tinc = cfg.tincPackage; + hostsType = builtins.typeOf cfg.hosts; + hosts = + if hostsType == "package" then + # use package as is + cfg.hosts + else if hostsType == "path" then + # use path to generate a package + pkgs.stdenv.mkDerivation { + name = "custom-retiolum-hosts"; + src = cfg.hosts; + installPhase = '' + mkdir $out + find . -name .git -prune -o -type f -print0 | xargs -0 cp --target-directory $out + ''; + } + else + abort "The option `services.retiolum.hosts' must be set to a package or a path" + ; + iproute = cfg.iproutePackage; + + retiolumExtraHosts = import (pkgs.runCommand "retiolum-etc-hosts" + { } + '' + generate() { + (cd ${hosts} + printf \'\' + for i in `ls`; do + names=$(hostnames $i) + for j in `sed -En 's|^ *Aliases *= *(.+)|\1|p' $i`; do + names="$names $(hostnames $j)" + done + sed -En ' + s|^ *Subnet *= *([^ /]*)(/[0-9]*)? *$|\1 '"$names"'|p + ' $i + done | sort + printf \'\' + ) + } + + case ${cfg.generateEtcHosts} in + short) + hostnames() { echo "$1"; } + generate + ;; + long) + hostnames() { echo "$1.${cfg.network}"; } + generate + ;; + both) + hostnames() { echo "$1.${cfg.network} $1"; } + generate + ;; + *) + echo '""' + ;; + esac > $out + ''); + + + confDir = pkgs.runCommand "retiolum" { + # TODO text + executable = true; + preferLocalBuild = true; + } '' + set -euf + + mkdir -p $out + + ln -s ${hosts} $out/hosts + + cat > $out/tinc.conf < $out/tinc-up <&2 + exit 23 + ''; + + rebuild = + '' + nixpkgs=''${nixpkgs-/home/tv/src/nixpkgs} + nixos-rebuild \ + --show-trace \ + -I nixpkgs="$nixpkgs" \ + switch \ + 2>&1 \ + | sed ${shell-escape '' + s|"\(/home/tv/src/config/[^":]*\)"|\1| + s|^trace:\s*\(.*\)|\1| + ''} + ''; + + }; + + wrap = script: + '' + #! /bin/sh + set -euf + ${script} + ''; + #lib=$lib + #export PATH=$bin:${makeSearchPath "bin" buildInputs} + + buildScript = name: script: + builtins.trace "building ${name}" + '' + echo ${shell-escape script} > $bin/${shell-escape name} + chmod +x $bin/${shell-escape name} + ''; + + + + tools = pkgs.stdenv.mkDerivation rec { + name = "tools"; + src = /var/empty; + + buildInputs = []; + + + buildPhase = + '' + mkdir $out + + bin=$out/bin + mkdir $bin + + ${concatStringsSep "\n" (attrValues (mapAttrs buildScript scripts))} + + ''; + #'' + #mkdir $out + + #lib=$out/lib + #cp -r lib $lib + + #bin=$out/bin + #mkdir $bin + #${concatStringsSep "\n" (attrValues (mapAttrs (name: script: + # '' + # { + # echo '#! /bin/sh' + # echo 'set -euf' + # echo "lib=$lib" + # echo "export PATH=$bin:${makeSearchPath "bin" buildInputs}" + # echo ${shell-escape script} + # } > $bin/${name} + # chmod +x $bin/${name} + # '') scripts))} + #''; + installPhase = ":"; + }; + +in + +{ + environment.systemPackages = [ tools ]; +} diff --git a/modules/tv/urxvt.nix b/modules/tv/urxvt.nix new file mode 100644 index 00000000..a9758124 --- /dev/null +++ b/modules/tv/urxvt.nix @@ -0,0 +1,24 @@ +{ pkgs, ... }: + + with builtins; + +let + users = [ "tv" ]; + urxvt = pkgs.rxvt_unicode; + mkService = user: { + description = "urxvt terminal daemon"; + wantedBy = [ "multi-user.target" ]; + restartIfChanged = false; + serviceConfig = { + Restart = "always"; + User = user; + ExecStart = "${urxvt}/bin/urxvtd"; + }; + }; + +in + +{ + environment.systemPackages = [ urxvt ]; + systemd.services = listToAttrs (map (u: { name = "${u}-urxvtd"; value = mkService u; }) users); +} diff --git a/modules/tv/users.nix b/modules/tv/users.nix new file mode 100644 index 00000000..f42ba33c --- /dev/null +++ b/modules/tv/users.nix @@ -0,0 +1,221 @@ +{ config, pkgs, ... }: + +let + inherit (builtins) attrValues; + inherit (pkgs.lib) concatMap filterAttrs mapAttrs concatStringsSep; + + + users = { + tv = { + uid = 1337; + group = "users"; + extraGroups = [ + "audio" + "video" + "wheel" + ]; + }; + + ff = { + uid = 13378001; + group = "tv-sub"; + extraGroups = [ + "audio" + "video" + ]; + }; + + cr = { + uid = 13378002; + group = "tv-sub"; + extraGroups = [ + "audio" + "video" + "bumblebee" + ]; + }; + + vimb = { + uid = 13378003; + group = "tv-sub"; + extraGroups = [ + "audio" + "video" + "bumblebee" + ]; + }; + + fa = { + uid = 2300001; + group = "tv-sub"; + }; + + rl = { + uid = 2300002; + group = "tv-sub"; + }; + + btc-bitcoind = { + uid = 2301001; + group = "tv-sub"; + }; + + btc-electrum = { + uid = 2301002; + group = "tv-sub"; + }; + + ltc-litecoind = { + uid = 2301101; + group = "tv-sub"; + }; + + eth = { + uid = 2302001; + group = "tv-sub"; + }; + + emse-hsdb = { + uid = 4200101; + group = "tv-sub"; + }; + + wine = { + uid = 13370400; + group = "tv-sub"; + extraGroups = [ + "audio" + "video" + "bumblebee" + ]; + }; + + # dwarffortress + df = { + uid = 13370401; + group = "tv-sub"; + extraGroups = [ + "audio" + "video" + "bumblebee" + ]; + }; + + # XXX visudo: Warning: Runas_Alias `FTL' referenced but not defined + FTL = { + uid = 13370402; + #group = "tv-sub"; + extraGroups = [ + "audio" + "video" + "bumblebee" + ]; + }; + + freeciv = { + uid = 13370403; + group = "tv-sub"; + }; + + xr = { + uid = 13370061; + group = "tv-sub"; + extraGroups = [ + "audio" + "video" + ]; + }; + + "23" = { + uid = 13370023; + group = "tv-sub"; + }; + + electrum = { + uid = 13370102; + group = "tv-sub"; + }; + + Reaktor = { + uid = 4230010; + group = "tv-sub"; + }; + + gitolite = { + uid = 7700; + }; + + skype = { + uid = 6660001; + group = "tv-sub"; + extraGroups = [ + "audio" + ]; + }; + + onion = { + uid = 6660010; + group = "tv-sub"; + }; + + zalora = { + uid = 1000301; + group = "tv-sub"; + extraGroups = [ + "audio" + # TODO remove vboxusers when har