diff --git a/common/guzzlord-exports.nix b/common/guzzlord-exports.nix new file mode 100644 index 0000000..4468b5e --- /dev/null +++ b/common/guzzlord-exports.nix @@ -0,0 +1,32 @@ +with builtins; +with import ; + +{pkgs, lib, config, hostName, ...}: +with lib; + +let cfg = config.services.guzzlords; in + +let + islandToExportList = guzzlordName: island: conf: if (island == config.networking.hostName) then + ['' + ${conf.path} ${guzzlordName}(rw,nohide,insecure,no_subtree_check) + ''] else []; + guzzlordToIslandConfigs = (guzzlordName: conf: + (attrValues (mapAttrs (islandToExportList guzzlordName) conf.islands))); +in +{ + config = let exports = join-string "\n" (flatten (attrValues (mapAttrs guzzlordToIslandConfigs cfg))); in + mkIf (exports != "") { + services.nfs.server = { + inherit exports; + enable = true; + statdPort = 4000; + lockdPort = 4001; + mountdPort = 4002; + }; + networking.firewall = { + allowedTCPPorts = [111 2049 4000 4001 4002 20048]; + allowedUDPPorts = [111 2049 4000 4001 4002 20048]; + }; + }; +} diff --git a/common/guzzlord-imports.nix b/common/guzzlord-imports.nix new file mode 100644 index 0000000..2284ab5 --- /dev/null +++ b/common/guzzlord-imports.nix @@ -0,0 +1,18 @@ +{pkgs, lib, config, hostName, ...}: + +with lib; +with builtins; + +{ + options = {}; + + config = { + fileSystems = mapAttrs' (islandName: islandConf: + nameValuePair "/mnt/guzzlord/${islandName}" { + device = "${islandName}:${islandConf.path}"; + fsType = "nfs"; + }) + (filterAttrs (n: v: v.type == "nfs") + (attrByPath [config.networking.hostName "islands"] {} config.services.guzzlords)); + }; +} diff --git a/common/guzzlord-options.nix b/common/guzzlord-options.nix new file mode 100644 index 0000000..8e995a8 --- /dev/null +++ b/common/guzzlord-options.nix @@ -0,0 +1,42 @@ +{pkgs, lib, config, ...}: +with lib; +with builtins; + +{ + options = with types; { + services.guzzlords = mkOption { + default = {}; + type = attrsOf (submodule ({config, name, ...}@args: { + options = { + guzzlord = mkOption { + type = str; + default = name; + description = '' + the server running the guzzlord software + + defaults to the name of the guzzlord service + ''; + }; + islands = let guzzlord = name; in mkOption { + type = attrsOf (submodule ({config, name, ...}@args: { + options = { + path = mkOption { + type = str; + default = "/tank"; + description = "the path on the associated host to serve via guzzlord"; + }; + type = mkOption { + type = str; + default = "nfs"; + description = '' + the type of island. may either be nfs or usb (unimplemented) + ''; + }; + }; + })); + }; + }; + })); + }; + }; +} diff --git a/common/k3s-cluster.nix b/common/k3s-cluster.nix new file mode 100644 index 0000000..f39c4e5 --- /dev/null +++ b/common/k3s-cluster.nix @@ -0,0 +1,119 @@ +{pkgs, config, ...}: + +with pkgs.lib; + +let + cfg = config.services.k3s-cluster; + agentTokenFilename = cfg.agentTokenFile; + agentTokenFileArg = ''--agent-token-file ${escapeShellArg agentTokenFilename}''; + serverTokenFilename = "/etc/k3s-server-token.txt"; + serverTokenFileArg = "--token-file ${escapeShellArg serverTokenFilename}"; + serverArg = if (cfg.leader != null) then "--server https://${escapeShellArg cfg.leader}:6443" else ""; +in +{ + +options = with types; { + services.k3s-cluster.secretNamespace = mkOption { + type = nullOr str; + description = '' + namespace used with deterministic-passwords to isolate the + secrets for this cluster. this should be the same for all + members of the cluster, agent or server, and different for all + other clusters. + ''; + default = null; + }; + services.k3s-cluster.enabled = mkEnableOption "k3s cluster"; + + services.k3s-cluster.leader = mkOption { + default = null; + type = nullOr str; + description = '' + hostname or IP of cluster leader + + This should be set to null (the default) for a cluster leader. + + For a member server, this should be set to an address which may + be used to reach the cluster leader from this host. + + After completion of cluster formation, this may be set to any + member server. This is a viable path forward when the original + leader dies. + + This string will be wrapped in https://...:6443 + ''; + }; + + services.k3s-cluster.agentTokenFile = mkOption { + default = "/etc/k3s-agent-token.txt"; + type = str; + description = "agent token file path for agents and servers"; + }; + services.k3s-cluster.serverTokenFile = mkOption { + default = "/etc/k3s-server-token.txt"; + type = str; + description = "server token file path for servers"; + }; + services.k3s-cluster.role = mkOption { + default = "server"; + type = str; + description = "server or agent, passed on to k3s"; + }; +}; + + +config = { services.k3s = mkIf cfg.enabled ( + if (cfg.role == "server") then { + extraFlags = mkForce "--cluster-init ${serverArg} ${agentTokenFileArg} ${serverTokenFileArg}"; + enable = mkForce true; + role = mkForce "server"; + } else { + extraFlags = mkForce "${agentTokenFileArg}"; + role = "agent"; + serverAddr = "https://${cfg.leader}:6443"; + enable = mkForce true; + } +); + +systemd = mkIf (cfg.enabled && cfg.leader == null && cfg.role == "server") { + sockets = { + tokenCAHash = { + listenStreams = [ "0.0.0.0:65479" ]; + wantedBy = [ "multi-user.target" ]; + socketConfig.Accept = "yes"; + }; + }; + services = { + "tokenCAHash@" = { + script = '' + cat /var/lib/rancher/k3s/server/agent-token|cut -d: -f 1 + ''; + startLimitIntervalSec = 0; + serviceConfig.Type = "oneshot"; + serviceConfig.StandardInput = "socket"; + }; + }; +}; + +networking.firewall.allowedTCPPorts = mkIf cfg.enabled [ 6443 53 65479 ]; +networking.firewall.allowedUDPPorts = mkIf cfg.enabled [ 53 ]; + +environment.deterministic-passwords.secrets = mkIf (cfg.enabled) { + "k3s-agent-token" = { + namespace = cfg.secretNamespace; + destination = agentTokenFilename; + before = ["k3s.service"]; + writer = '' + echo "$(nc ${if cfg.leader == null then "localhost" else cfg.leader} 65479 < /dev/null)::server:$secret" > "$destination" + ''; + }; + "k3s-server-token" = mkIf (cfg.role == "server") { + namespace = cfg.secretNamespace; + destination = serverTokenFilename; + before = ["k3s.service"]; + writer = '' + echo "$(nc ${if cfg.leader == null then "localhost" else cfg.leader} 65479 < /dev/null)::server:$secret" > "$destination" + ''; + }; +};}; +}