diff options
Diffstat (limited to 'tv/modules')
| -rw-r--r-- | tv/modules/consul.nix | 118 | ||||
| -rw-r--r-- | tv/modules/default.nix | 9 | ||||
| -rw-r--r-- | tv/modules/ejabberd.nix | 166 | ||||
| -rw-r--r-- | tv/modules/iptables.nix | 126 | 
4 files changed, 419 insertions, 0 deletions
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'); +#}  | 
