From f03cafd49b48eabc4743b3a3791fd22f19cb0de1 Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Wed, 9 Feb 2022 12:09:49 +0100 Subject: Modularize and prepare to support multiple clusters --- nix/deuxfleurs.nix | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 nix/deuxfleurs.nix (limited to 'nix') diff --git a/nix/deuxfleurs.nix b/nix/deuxfleurs.nix new file mode 100644 index 0000000..9b8013c --- /dev/null +++ b/nix/deuxfleurs.nix @@ -0,0 +1,185 @@ +{ config, pkgs, ... }: + +let + cfg = config.deuxfleurs; +in + with builtins; + with pkgs.lib; +{ + options.deuxfleurs = + let wg_node = with types; submodule { + options = { + hostname = mkOption { + type = str; + description = "Host name"; + }; + IP = mkOption { + type = str; + description = "IP Address"; + }; + publicKey = mkOption { + type = str; + description = "Public key"; + }; + endpoint = mkOption { + type = nullOr str; + description = "Wireguard endpoint on the public Internet"; + }; + }; + }; + in + { + # Parameters that may vary between nodes + site_name = mkOption { + description = "Site (availability zone) on which this node is deployed"; + type = types.str; + }; + vpn_ip = mkOption { + description = "IP address of this node on the Wireguard VPN"; + type = types.str; + }; + vpn_listen_port = mkOption { + description = "Port for incoming Wireguard VPN connections"; + type = types.port; + }; + is_raft_server = mkOption { + description = "Make this node a RAFT server for the Nomad and Consul deployments"; + type = types.bool; + default = false; + }; + + # Parameters common to all nodes + cluster_name = mkOption { + description = "Name of this Deuxfleurs deployment"; + type = types.str; + }; + cluster_nodes = mkOption { + description = "Nodes that are part of the cluster"; + type = types.listOf wg_node; + }; + admin_nodes = mkOption { + description = "Machines that are part of the Wireguard VPN for administration purposes"; + type = types.listOf wg_node; + }; + admin_accounts = mkOption { + description = "List of users having an admin account on cluster nodes, maps user names to a list of authorized SSH keys"; + type = types.attrsOf (types.listOf types.str); + }; + }; + + config = { + # Configure admin accounts on all nodes + users.users = builtins.mapAttrs (name: publicKeys: { + isNormalUser = true; + extraGroups = [ "wheel" ]; + openssh.authorizedKeys.keys = publicKeys; + }) cfg.admin_accounts; + + # Configure Wireguard VPN between all nodes + networking.wireguard.interfaces.wg0 = { + ips = [ "${cfg.vpn_ip}/16" ]; + listenPort = cfg.vpn_listen_port; + privateKeyFile = "/root/wireguard-keys/private"; + peers = map ({ publicKey, endpoint, IP, ... }: { + publicKey = publicKey; + allowedIPs = [ "${IP}/32" ]; + endpoint = endpoint; + persistentKeepalive = 25; + }) (cfg.cluster_nodes ++ cfg.admin_nodes); + }; + + networking.firewall.allowedUDPPorts = [ cfg.vpn_listen_port ]; + + # Configure /etc/hosts to link all hostnames to their Wireguard IP + networking.extraHosts = builtins.concatStringsSep "\n" (map + ({ hostname, IP, ...}: "${IP} ${hostname}") + (cfg.cluster_nodes ++ cfg.admin_nodes)); + + # Enable Hashicorp Consul & Nomad + services.consul.enable = true; + services.consul.extraConfig = + (if cfg.is_raft_server + then { + server = true; + bootstrap_expect = 3; + } + else {}) // + { + datacenter = cfg.cluster_name; + node_meta = { + "site" = cfg.site_name; + }; + ui = true; + bind_addr = cfg.vpn_ip; + + ports.http = -1; + addresses.https = "0.0.0.0"; + ports.https = 8501; + + retry_join = map (node_info: node_info.IP) cfg.cluster_nodes; + + ca_file = "/var/lib/consul/pki/consul-ca.crt"; + cert_file = "/var/lib/consul/pki/consul2022.crt"; + key_file = "/var/lib/consul/pki/consul2022.key"; + verify_incoming = true; + verify_outgoing = true; + verify_server_hostname = true; + }; + + services.nomad.enable = true; + services.nomad.package = pkgs.nomad_1_1; + services.nomad.settings = + (if cfg.is_raft_server + then { server = { + enabled = true; + bootstrap_expect = 3; + }; } + else {}) // + { + region = cfg.cluster_name; + datacenter = cfg.site_name; + advertise = { + rpc = cfg.vpn_ip; + http = cfg.vpn_ip; + serf = cfg.vpn_ip; + }; + consul = { + address = "localhost:8501"; + ca_file = "/var/lib/nomad/pki/consul2022.crt"; + cert_file = "/var/lib/nomad/pki/consul2022-client.crt"; + key_file = "/var/lib/nomad/pki/consul2022-client.key"; + ssl = true; + }; + client = { + enabled = true; + network_interface = "wg0"; + meta = { + "site" = cfg.site_name; + }; + }; + tls = { + http = true; + rpc = true; + ca_file = "/var/lib/nomad/pki/nomad-ca.crt"; + cert_file = "/var/lib/nomad/pki/nomad2022.crt"; + key_file = "/var/lib/nomad/pki/nomad2022.key"; + verify_server_hostname = true; + verify_https_client = true; + }; + plugin = [ + { + docker = [ + { + config = [ + { + volumes.enabled = true; + allow_privileged = true; + } + ]; + } + ]; + } + ]; + }; + }; +} -- cgit v1.2.3