104 lines
3.3 KiB
Nix
104 lines
3.3 KiB
Nix
# https://guekka.github.io/nixos-server-2/
|
|
{ config, lib, pkgs, ... }:
|
|
with lib; let
|
|
cfg = config.services.tailscaleAutoconnect;
|
|
in {
|
|
options.services.tailscaleAutoconnect = {
|
|
enable = mkEnableOption "tailscaleAutoconnect";
|
|
|
|
authkey = mkOption {
|
|
type = types.str;
|
|
description = "The authkey to use for authentication with Tailscale";
|
|
};
|
|
|
|
loginServer = mkOption {
|
|
type = types.str;
|
|
default = "";
|
|
description = "The login server to use for authentication with Tailscale";
|
|
};
|
|
|
|
advertiseExitNode = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether to advertise this node as an exit node";
|
|
};
|
|
|
|
exitNode = mkOption {
|
|
type = types.str;
|
|
default = "";
|
|
description = "The exit node to use for this node";
|
|
};
|
|
|
|
exitNodeAllowLanAccess = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether to allow LAN access to this node";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
assertions = [
|
|
{
|
|
assertion = cfg.authkey != "";
|
|
message = "authkey must be set";
|
|
}
|
|
{
|
|
assertion = cfg.exitNodeAllowLanAccess -> cfg.exitNode != "";
|
|
message = "exitNodeAllowLanAccess must be false if exitNode is not set";
|
|
}
|
|
{
|
|
assertion = cfg.advertiseExitNode -> cfg.exitNode == "";
|
|
message = "advertiseExitNode must be false if exitNode is set";
|
|
}
|
|
];
|
|
|
|
systemd.services.tailscale-autoconnect = {
|
|
description = "Automatic connection to Tailscale";
|
|
|
|
# make sure tailscale is running before trying to connect to tailscale
|
|
after = ["network-pre.target" "tailscale.service"];
|
|
wants = ["network-pre.target" "tailscale.service"];
|
|
wantedBy = ["multi-user.target"];
|
|
|
|
serviceConfig.Type = "oneshot";
|
|
|
|
script = with pkgs; ''
|
|
# wait for tailscaled to settle
|
|
sleep 2
|
|
|
|
# check if we are already authenticated to tailscale
|
|
status="$(${tailscale}/bin/tailscale status -json | ${jq}/bin/jq -r .BackendState)"
|
|
# if status is not null, then we are already authenticated
|
|
echo "tailscale status: $status"
|
|
if [ "$status" != "NeedsLogin" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
# otherwise authenticate with tailscale
|
|
# timeout after 10 seconds to avoid hanging the boot process
|
|
${coreutils}/bin/timeout 10 ${tailscale}/bin/tailscale up \
|
|
${lib.optionalString (cfg.loginServer != "") "--login-server=${cfg.loginServer}"} \
|
|
"--authkey=${cfg.authkey}"
|
|
|
|
# we have to proceed in two steps because some options are only available
|
|
# after authentication
|
|
${coreutils}/bin/timeout 10 ${tailscale}/bin/tailscale up \
|
|
${lib.optionalString (cfg.loginServer != "") "--login-server=${cfg.loginServer}"} \
|
|
${lib.optionalString (cfg.advertiseExitNode) "--advertise-exit-node"} \
|
|
${lib.optionalString (cfg.exitNode != "") "--exit-node=${cfg.exitNode}"} \
|
|
${lib.optionalString (cfg.exitNodeAllowLanAccess) "--exit-node-allow-lan-access"}
|
|
'';
|
|
};
|
|
|
|
networking.firewall = {
|
|
trustedInterfaces = [ "tailscale0" ];
|
|
allowedUDPPorts = [ config.services.tailscale.port ];
|
|
};
|
|
|
|
services.tailscale = {
|
|
enable = true;
|
|
useRoutingFeatures = if cfg.advertiseExitNode then "server" else "client";
|
|
};
|
|
};
|
|
}
|