Merge pull request 'tailscale: add support for multiple tailnets' (#26) from develop into release-25.11
All checks were successful
Deploy docs / build-and-deploy (push) Successful in 11s
All checks were successful
Deploy docs / build-and-deploy (push) Successful in 11s
Reviewed-on: #26
This commit is contained in:
commit
e801c318a7
1 changed files with 125 additions and 32 deletions
|
|
@ -1,9 +1,17 @@
|
||||||
{ config, lib, ... }:
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.services.tailscale;
|
cfg = config.services.tailscale;
|
||||||
|
|
||||||
inherit (lib)
|
inherit (lib)
|
||||||
|
concatStrings
|
||||||
|
filterAttrs
|
||||||
|
mapAttrsToList
|
||||||
mkIf
|
mkIf
|
||||||
mkOption
|
mkOption
|
||||||
optional
|
optional
|
||||||
|
|
@ -12,39 +20,124 @@ let
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.services.tailscale = {
|
options.services.tailscale = {
|
||||||
loginServer = mkOption {
|
tailnets = mkOption {
|
||||||
type = types.str;
|
default = { };
|
||||||
description = "The Tailscale login server to use.";
|
type = types.attrsOf (
|
||||||
};
|
types.submodule {
|
||||||
enableSSH = mkOption {
|
options = {
|
||||||
type = types.bool;
|
loginServer = mkOption {
|
||||||
default = false;
|
type = types.str;
|
||||||
description = "Enable Tailscale SSH functionality.";
|
description = "Login server for this tailnet.";
|
||||||
};
|
};
|
||||||
acceptDNS = mkOption {
|
authKeyFile = mkOption {
|
||||||
type = types.bool;
|
type = types.nullOr types.str;
|
||||||
default = true;
|
default = null;
|
||||||
description = "Enable Tailscale's MagicDNS and custom DNS configuration.";
|
description = "Path to auth key secret.";
|
||||||
|
};
|
||||||
|
enableSSH = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
acceptDNS = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
default = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Connect to this tailnet on boot.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config =
|
||||||
services.tailscale = {
|
let
|
||||||
authKeyFile = config.sops.secrets."tailscale/auth-key".path;
|
defaultTailnets = filterAttrs (_: t: t.default) cfg.tailnets;
|
||||||
extraSetFlags = optional cfg.enableSSH "--ssh" ++ optional cfg.acceptDNS "--accept-dns";
|
defaultTailnet =
|
||||||
extraUpFlags = [
|
if defaultTailnets == { } then null else builtins.head (builtins.attrValues defaultTailnets);
|
||||||
"--login-server=${cfg.loginServer}"
|
|
||||||
]
|
entries = mapAttrsToList (name: tcfg: ''
|
||||||
++ optional cfg.enableSSH "--ssh"
|
TAILNETS["${name}"]="${tcfg.loginServer}|${if tcfg.enableSSH then "true" else "false"}|${
|
||||||
++ optional cfg.acceptDNS "--accept-dns";
|
if tcfg.acceptDNS then "true" else "false"
|
||||||
|
}|${if tcfg.authKeyFile != null then tcfg.authKeyFile else ""}"
|
||||||
|
'') cfg.tailnets;
|
||||||
|
|
||||||
|
tailnetSwitchCli = pkgs.writeShellScriptBin "tailnet-switch" ''
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TAILSCALE="${cfg.package}/bin/tailscale"
|
||||||
|
|
||||||
|
declare -A TAILNETS
|
||||||
|
${concatStrings entries}
|
||||||
|
|
||||||
|
CHOICE="''${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "$CHOICE" ]]; then
|
||||||
|
if [[ -t 0 ]]; then
|
||||||
|
CHOICE=$(printf '%s\n' "''${!TAILNETS[@]}" | sort | ${pkgs.fzf}/bin/fzf --prompt="Switch tailnet: ")
|
||||||
|
else
|
||||||
|
echo "Usage: tailnet-switch <tailnet-name>" >&2
|
||||||
|
printf 'Available tailnets: %s\n' "''${!TAILNETS[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "''${TAILNETS[$CHOICE]+x}" ]]; then
|
||||||
|
echo "Unknown tailnet: $CHOICE" >&2
|
||||||
|
printf 'Available tailnets: %s\n' "''${!TAILNETS[@]}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS='|' read -r LOGIN_SERVER ENABLE_SSH ACCEPT_DNS AUTH_KEY_FILE <<< "''${TAILNETS[$CHOICE]}"
|
||||||
|
|
||||||
|
echo "Switching to: $CHOICE ($LOGIN_SERVER)"
|
||||||
|
|
||||||
|
"$TAILSCALE" down --accept-risk=lose-ssh 2>/dev/null || true
|
||||||
|
"$TAILSCALE" logout 2>/dev/null || true
|
||||||
|
|
||||||
|
UP_FLAGS=("--login-server=$LOGIN_SERVER")
|
||||||
|
[[ "$ENABLE_SSH" == "true" ]] && UP_FLAGS+=("--ssh")
|
||||||
|
[[ "$ACCEPT_DNS" == "true" ]] && UP_FLAGS+=("--accept-dns")
|
||||||
|
[[ -n "$AUTH_KEY_FILE" ]] && UP_FLAGS+=("--auth-key=$(cat "$AUTH_KEY_FILE")")
|
||||||
|
|
||||||
|
"$TAILSCALE" up "''${UP_FLAGS[@]}"
|
||||||
|
|
||||||
|
echo "Switched to tailnet: $CHOICE"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
(builtins.length (builtins.attrValues (filterAttrs (_: t: t.default) cfg.tailnets))) <= 1;
|
||||||
|
message = "services.tailscale.tailnets: Only one tailnet can be set as default.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = cfg.tailnets != { };
|
||||||
|
message = "services.tailscale.tailnets: At least one tailnet must be defined.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
services.tailscale = mkIf (defaultTailnet != null) (
|
||||||
|
with defaultTailnet;
|
||||||
|
{
|
||||||
|
inherit authKeyFile;
|
||||||
|
extraSetFlags = optional enableSSH "--ssh" ++ optional acceptDNS "--accept-dns";
|
||||||
|
extraUpFlags = [
|
||||||
|
"--login-server=${loginServer}"
|
||||||
|
]
|
||||||
|
++ optional enableSSH "--ssh"
|
||||||
|
++ optional acceptDNS "--accept-dns";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
environment.systemPackages = [ tailnetSwitchCli ];
|
||||||
|
|
||||||
|
environment.shellAliases.ts = "${cfg.package}/bin/tailscale";
|
||||||
|
|
||||||
|
networking.firewall.trustedInterfaces = [ cfg.interfaceName ];
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.shellAliases = {
|
|
||||||
ts = "${cfg.package}/bin/tailscale";
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.firewall.trustedInterfaces = [ cfg.interfaceName ];
|
|
||||||
|
|
||||||
sops.secrets."tailscale/auth-key" = { };
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue