From afb6afff1d0f81d8a0dcfd94fa8e46a849bb094f Mon Sep 17 00:00:00 2001 From: tv Date: Mon, 27 Jul 2015 02:02:34 +0200 Subject: * tv -> tv * --- tv/modules/consul.nix | 118 ++++++++++++++++++++++++++++++++++ tv/modules/default.nix | 9 +++ tv/modules/ejabberd.nix | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ tv/modules/iptables.nix | 126 ++++++++++++++++++++++++++++++++++++ 4 files changed, 419 insertions(+) create mode 100644 tv/modules/consul.nix create mode 100644 tv/modules/default.nix create mode 100644 tv/modules/ejabberd.nix create mode 100644 tv/modules/iptables.nix (limited to 'tv/modules') diff --git a/tv/modules/consul.nix b/tv/modules/consul.nix new file mode 100644 index 000000000..83a430c2f --- /dev/null +++ b/tv/modules/consul.nix @@ -0,0 +1,118 @@ +{ config, lib, pkgs, ... }: + +# if quorum gets lost, then start any node with a config that doesn't contain bootstrap_expect +# but -bootstrap +# TODO consul-bootstrap HOST that actually does is +# TODO tools to inspect state of a cluster in outage state + +with import ../lib { inherit lib pkgs; }; +let + cfg = config.tv.consul; + + out = { + options.tv.consul = api; + config = mkIf cfg.enable (mkMerge [ + imp + { tv.iptables.input-retiolum-accept-new-tcp = [ "8300" "8301" ]; } + # TODO udp for 8301 + ]); + }; + + api = { + enable = mkEnableOption "tv.consul"; + + dc = mkOption { + type = types.label; + }; + hosts = mkOption { + type = with types; listOf host; + }; + encrypt-file = mkOption { + type = types.str; # TODO path (but not just into store) + default = "/root/src/secrets/consul-encrypt.json"; + }; + data-dir = mkOption { + type = types.str; # TODO path (but not just into store) + default = "/var/lib/consul"; + }; + self = mkOption { + type = types.host; + }; + server = mkOption { + type = types.bool; + default = false; + }; + GOMAXPROCS = mkOption { + type = types.int; + default = cfg.self.cores; + }; + }; + + consul-config = { + datacenter = cfg.dc; + data_dir = cfg.data-dir; + log_level = "INFO"; + #node_name = + server = cfg.server; + enable_syslog = true; + retry_join = + # TODO allow consul in other nets than retiolum [maybe] + concatMap (host: host.nets.retiolum.addrs) + (filter (host: host.name != cfg.self.name) cfg.hosts); + leave_on_terminate = true; + } // optionalAttrs cfg.server { + bootstrap_expect = length cfg.hosts; + leave_on_terminate = false; + }; + + imp = { + environment.systemPackages = with pkgs; [ + consul + ]; + + systemd.services.consul = { + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = with pkgs; [ + consul + ]; + environment = { + GOMAXPROCS = toString cfg.GOMAXPROCS; + }; + serviceConfig = { + PermissionsStartOnly = "true"; + SyslogIdentifier = "consul"; + User = user.name; + PrivateTmp = "true"; + Restart = "always"; + ExecStartPre = pkgs.writeScript "consul-init" '' + #! /bin/sh + mkdir -p ${cfg.data-dir} + chown ${user.name}: ${cfg.data-dir} + install -o ${user.name} -m 0400 ${cfg.encrypt-file} /tmp/encrypt.json + ''; + ExecStart = pkgs.writeScript "consul-service" '' + #! /bin/sh + set -euf + exec >/dev/null + exec consul agent \ + -config-file=${toFile "consul.json" (toJSON consul-config)} \ + -config-file=/tmp/encrypt.json + ''; + #-node=${cfg.self.fqdn} \ + #ExecStart = "${tinc}/sbin/tincd -c ${confDir} -d 0 -U ${user} -D"; + }; + }; + + users.extraUsers = singleton { + inherit (user) name uid; + }; + }; + + user = { + name = "consul"; + uid = 2999951406; # genid consul + }; + +in +out diff --git a/tv/modules/default.nix b/tv/modules/default.nix new file mode 100644 index 000000000..bb10d8261 --- /dev/null +++ b/tv/modules/default.nix @@ -0,0 +1,9 @@ +_: + +{ + imports = [ + ./consul.nix + ./ejabberd.nix + ./iptables.nix + ]; +} diff --git a/tv/modules/ejabberd.nix b/tv/modules/ejabberd.nix new file mode 100644 index 000000000..2910a9a69 --- /dev/null +++ b/tv/modules/ejabberd.nix @@ -0,0 +1,166 @@ +{ config, lib, pkgs, ... }: + +with builtins; +with lib; +let + cfg = config.tv.ejabberd; + + out = { + options.tv.ejabberd = api; + config = mkIf cfg.enable imp; + }; + + api = { + enable = mkEnableOption "tv.ejabberd"; + + certFile = mkOption { + type = types.str; + default = "/root/src/secrets/ejabberd.pem"; + }; + + hosts = mkOption { + type = with types; listOf str; + }; + }; + + imp = { + environment.systemPackages = [ my-ejabberdctl ]; + + systemd.services.ejabberd = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = "yes"; + PermissionsStartOnly = "true"; + SyslogIdentifier = "ejabberd"; + User = user.name; + ExecStartPre = pkgs.writeScript "ejabberd-start" '' + #! /bin/sh + install -o ${user.name} -m 0400 ${cfg.certFile} /etc/ejabberd/ejabberd.pem + ''; + ExecStart = pkgs.writeScript "ejabberd-service" '' + #! /bin/sh + ${my-ejabberdctl}/bin/ejabberdctl start + ''; + }; + }; + + users.extraUsers = singleton { + inherit (user) name uid; + home = "/var/ejabberd"; + createHome = true; + }; + }; + + user = { + name = "ejabberd"; + uid = 3499746127; # genid ejabberd + }; + + my-ejabberdctl = pkgs.writeScriptBin "ejabberdctl" '' + #! /bin/sh + set -euf + exec env \ + SPOOLDIR=/var/ejabberd \ + EJABBERD_CONFIG_PATH=${config-file} \ + ${pkgs.ejabberd}/bin/ejabberdctl \ + --logs /var/ejabberd \ + "$@" + ''; + + config-file = pkgs.writeText "ejabberd.cfg" '' + {loglevel, 3}. + {hosts, ${toErlang cfg.hosts}}. + {listen, + [ + {5222, ejabberd_c2s, [ + starttls, + {certfile, "/etc/ejabberd/ejabberd.pem"}, + {access, c2s}, + {shaper, c2s_shaper}, + {max_stanza_size, 65536} + ]}, + {5269, ejabberd_s2s_in, [ + {shaper, s2s_shaper}, + {max_stanza_size, 131072} + ]}, + {5280, ejabberd_http, [ + captcha, + http_bind, + http_poll, + web_admin + ]} + ]}. + {s2s_use_starttls, required}. + {s2s_certfile, "/etc/ejabberd/ejabberd.pem"}. + {auth_method, internal}. + {shaper, normal, {maxrate, 1000}}. + {shaper, fast, {maxrate, 50000}}. + {max_fsm_queue, 1000}. + {acl, local, {user_regexp, ""}}. + {access, max_user_sessions, [{10, all}]}. + {access, max_user_offline_messages, [{5000, admin}, {100, all}]}. + {access, local, [{allow, local}]}. + {access, c2s, [{deny, blocked}, + {allow, all}]}. + {access, c2s_shaper, [{none, admin}, + {normal, all}]}. + {access, s2s_shaper, [{fast, all}]}. + {access, announce, [{allow, admin}]}. + {access, configure, [{allow, admin}]}. + {access, muc_admin, [{allow, admin}]}. + {access, muc_create, [{allow, local}]}. + {access, muc, [{allow, all}]}. + {access, pubsub_createnode, [{allow, local}]}. + {access, register, [{allow, all}]}. + {language, "en"}. + {modules, + [ + {mod_adhoc, []}, + {mod_announce, [{access, announce}]}, + {mod_blocking,[]}, + {mod_caps, []}, + {mod_configure,[]}, + {mod_disco, []}, + {mod_irc, []}, + {mod_http_bind, []}, + {mod_last, []}, + {mod_muc, [ + {access, muc}, + {access_create, muc_create}, + {access_persistent, muc_create}, + {access_admin, muc_admin} + ]}, + {mod_offline, [{access_max_user_messages, max_user_offline_messages}]}, + {mod_ping, []}, + {mod_privacy, []}, + {mod_private, []}, + {mod_pubsub, [ + {access_createnode, pubsub_createnode}, + {ignore_pep_from_offline, true}, + {last_item_cache, false}, + {plugins, ["flat", "hometree", "pep"]} + ]}, + {mod_register, [ + {welcome_message, {"Welcome!", + "Hi.\nWelcome to this XMPP server."}}, + {ip_access, [{allow, "127.0.0.0/8"}, + {deny, "0.0.0.0/0"}]}, + {access, register} + ]}, + {mod_roster, []}, + {mod_shared_roster,[]}, + {mod_stats, []}, + {mod_time, []}, + {mod_vcard, []}, + {mod_version, []} + ]}. + ''; + + + # XXX this is a placeholder that happens to work the default strings. + toErlang = builtins.toJSON; + +in +out diff --git a/tv/modules/iptables.nix b/tv/modules/iptables.nix new file mode 100644 index 000000000..cbf49f577 --- /dev/null +++ b/tv/modules/iptables.nix @@ -0,0 +1,126 @@ +{ config, lib, pkgs, ... }: + +with builtins; +with lib; +let + cfg = config.tv.iptables; + + out = { + options.tv.iptables = api; + config = mkIf cfg.enable imp; + }; + + api = { + enable = mkEnableOption "tv.iptables"; + + input-internet-accept-new-tcp = mkOption { + type = with types; listOf (either int str); + default = []; + }; + + input-retiolum-accept-new-tcp = mkOption { + type = with types; listOf (either int str); + default = []; + }; + }; + + imp = { + networking.firewall.enable = false; + + systemd.services.tv-iptables = { + description = "tv-iptables"; + wantedBy = [ "network-pre.target" ]; + before = [ "network-pre.target" ]; + after = [ "systemd-modules-load.service" ]; + + path = with pkgs; [ + iptables + ]; + + restartIfChanged = true; + + serviceConfig = { + Type = "simple"; + RemainAfterExit = true; + Restart = "always"; + ExecStart = "@${startScript} tv-iptables_start"; + }; + }; + }; + + + accept-new-tcp = port: + "-p tcp -m tcp --dport ${port} -m conntrack --ctstate NEW -j ACCEPT"; + + rules = iptables-version: + pkgs.writeText "tv-iptables-rules${toString iptables-version}" '' + *nat + :PREROUTING ACCEPT [0:0] + :INPUT ACCEPT [0:0] + :OUTPUT ACCEPT [0:0] + :POSTROUTING ACCEPT [0:0] + ${concatMapStringsSep "\n" (rule: "-A PREROUTING ${rule}") ([] + ++ [ + "! -i retiolum -p tcp -m tcp --dport 22 -j REDIRECT --to-ports 0" + "-p tcp -m tcp --dport 11423 -j REDIRECT --to-ports 22" + ] + )} + COMMIT + *filter + :INPUT DROP [0:0] + :FORWARD DROP [0:0] + :OUTPUT ACCEPT [0:0] + :Retiolum - [0:0] + ${concatMapStringsSep "\n" (rule: "-A INPUT ${rule}") ([] + ++ [ + "-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT" + "-i lo -j ACCEPT" + ] + ++ map accept-new-tcp (unique (map toString cfg.input-internet-accept-new-tcp)) + ++ ["-i retiolum -j Retiolum"] + )} + ${concatMapStringsSep "\n" (rule: "-A Retiolum ${rule}") ([] + ++ { + ip4tables = [ + "-p icmp -m icmp --icmp-type echo-request -j ACCEPT" + ]; + ip6tables = [ + "-p ipv6-icmp -m icmp6 --icmpv6-type echo-request -j ACCEPT" + ]; + }."ip${toString iptables-version}tables" + ++ map accept-new-tcp (unique (map toString cfg.input-retiolum-accept-new-tcp)) + ++ { + ip4tables = [ + "-p tcp -j REJECT --reject-with tcp-reset" + "-p udp -j REJECT --reject-with icmp-port-unreachable" + "-j REJECT --reject-with icmp-proto-unreachable" + ]; + ip6tables = [ + "-p tcp -j REJECT --reject-with tcp-reset" + "-p udp -j REJECT --reject-with icmp6-port-unreachable" + "-j REJECT" + ]; + }."ip${toString iptables-version}tables" + )} + COMMIT + ''; + + startScript = pkgs.writeScript "tv-iptables_start" '' + #! /bin/sh + set -euf + iptables-restore < ${rules 4} + ip6tables-restore < ${rules 6} + ''; + +in +out + +#let +# cfg = config.tv.iptables; +# arg' = arg // { inherit cfg; }; +#in +# +#{ +# options.tv.iptables = import ./options.nix arg'; +# config = lib.mkIf cfg.enable (import ./config.nix arg'); +#} -- cgit v1.2.3