{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" ''; }; };}; }