tailscale and user groups
This commit is contained in:
parent
6447486643
commit
f733a5238c
7 changed files with 113 additions and 217 deletions
196
tailscale.nix
196
tailscale.nix
|
|
@ -10,137 +10,24 @@ with lib;
|
|||
let
|
||||
cfg = config.services.tailscale-netns;
|
||||
|
||||
instanceOptions =
|
||||
{ name, ... }:
|
||||
{
|
||||
options = {
|
||||
authKeyFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "Path to file containing a Tailscale auth key.";
|
||||
};
|
||||
|
||||
hostname = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
Tailscale hostname for this instance.
|
||||
Defaults to "<system-hostname>-<instance-name>".
|
||||
'';
|
||||
};
|
||||
|
||||
advertiseRoutes = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = "Subnet routes to advertise (e.g. [ \"10.0.0.0/24\" ]).";
|
||||
};
|
||||
|
||||
dns = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
Bootstrap DNS server for the namespace before Tailscale is connected.
|
||||
Defaults to `1.1.1.1`. Only used for the initial coordination server
|
||||
connection; once Tailscale is up it manages DNS inside the namespace.
|
||||
'';
|
||||
};
|
||||
|
||||
advertiseExitNode = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to advertise this node as an exit node.";
|
||||
};
|
||||
|
||||
acceptRoutes = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to accept routes advertised by other nodes.";
|
||||
};
|
||||
|
||||
exitNode = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = "Tailscale IP or hostname of exit node to use.";
|
||||
};
|
||||
|
||||
extraUpFlags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = "Extra flags passed to `tailscale up`.";
|
||||
};
|
||||
|
||||
extraDaemonFlags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = "Extra flags passed to `tailscaled`.";
|
||||
};
|
||||
};
|
||||
instanceOptions = {
|
||||
options.extraDaemonFlags = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = "Extra flags passed to `tailscaled`.";
|
||||
};
|
||||
|
||||
mkUpFlags =
|
||||
instanceCfg:
|
||||
let
|
||||
hostFlag = optional (
|
||||
instanceCfg.hostname != null
|
||||
) "--hostname=${escapeShellArg instanceCfg.hostname}";
|
||||
routeFlags = map (r: "--advertise-routes=${escapeShellArg r}") instanceCfg.advertiseRoutes;
|
||||
exitFlag = optional instanceCfg.advertiseExitNode "--advertise-exit-node";
|
||||
acceptFlag = optional instanceCfg.acceptRoutes "--accept-routes";
|
||||
exitNodeFlag = optional (
|
||||
instanceCfg.exitNode != null
|
||||
) "--exit-node=${escapeShellArg instanceCfg.exitNode}";
|
||||
in
|
||||
hostFlag ++ routeFlags ++ exitFlag ++ acceptFlag ++ exitNodeFlag ++ instanceCfg.extraUpFlags;
|
||||
|
||||
mkAutoconnectScript =
|
||||
name: instanceCfg:
|
||||
pkgs.writers.writeBash "tailscale-autoconnect-${name}" ''
|
||||
set -euo pipefail
|
||||
|
||||
TS="${pkgs.tailscale}/bin/tailscale"
|
||||
SOCKET="/run/tailscale-${name}/tailscaled.sock"
|
||||
TS_ARGS="--socket $SOCKET"
|
||||
|
||||
get_state() {
|
||||
$TS $TS_ARGS status --json --peers=false 2>/dev/null | ${pkgs.jq}/bin/jq -r '.BackendState // "NoState"'
|
||||
}
|
||||
|
||||
last_state=""
|
||||
while state="$(get_state)"; do
|
||||
if [[ "$state" != "$last_state" ]]; then
|
||||
case "$state" in
|
||||
NeedsLogin|NeedsMachineAuth|Stopped)
|
||||
echo "[tailscale-netns:${name}] server needs authentication, sending auth key"
|
||||
$TS $TS_ARGS up \
|
||||
--auth-key "$(cat ${instanceCfg.authKeyFile})" \
|
||||
${escapeShellArgs (mkUpFlags instanceCfg)}
|
||||
;;
|
||||
Running)
|
||||
echo "[tailscale-netns:${name}] tailscale is running"
|
||||
${pkgs.systemd}/bin/systemd-notify --ready
|
||||
exit 0
|
||||
;;
|
||||
NoState)
|
||||
echo "[tailscale-netns:${name}] tailscaled not ready yet"
|
||||
;;
|
||||
*)
|
||||
echo "[tailscale-netns:${name}] waiting for Running (state=$state)"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
last_state="$state"
|
||||
sleep 1
|
||||
done
|
||||
'';
|
||||
};
|
||||
|
||||
mkNetnsSetup =
|
||||
index: name: instanceCfg:
|
||||
let
|
||||
ns = "ts-${name}ns";
|
||||
hostIP = "10.201.${toString index}.1/24";
|
||||
hostAddr = "10.201.${toString index}.1";
|
||||
hostIP = "${hostAddr}/24";
|
||||
nsIP = "10.201.${toString index}.2/24";
|
||||
vethHost = "veth-ts-${name}";
|
||||
vethNS = "veth-ts-${name}-ns";
|
||||
fallbackLines = concatMapStrings (n: " echo nameserver ${n}\n") config.networking.nameservers;
|
||||
in
|
||||
{
|
||||
"netns@ts-${name}" = {
|
||||
|
|
@ -153,12 +40,42 @@ let
|
|||
set -euo pipefail
|
||||
${pkgs.coreutils}/bin/mkdir -p /etc/netns/${ns}
|
||||
|
||||
# Build resolv.conf for the netns. Skip loopback addresses
|
||||
# (systemd-resolved stub at 127.0.0.53 is not reachable from the netns).
|
||||
NS_DNS="${if instanceCfg.dns != null then instanceCfg.dns else "1.1.1.1"}"
|
||||
{
|
||||
${pkgs.gawk}/bin/awk '$1 == "nameserver" && $2 !~ /^127\./ && $2 != "::1" {print}' /etc/resolv.conf
|
||||
echo "nameserver $NS_DNS"
|
||||
dns_found=0
|
||||
if ${pkgs.systemd}/bin/resolvectl dns 2>/dev/null; then
|
||||
saved_ifs="$IFS"
|
||||
IFS=$'\n'
|
||||
for line in $(${pkgs.systemd}/bin/resolvectl dns 2>/dev/null); do
|
||||
case "$line" in
|
||||
*:*)
|
||||
server=$(echo "$line" | ${pkgs.gnused}/bin/sed 's/^[^:]*: //')
|
||||
case "$server" in
|
||||
127.*|::1|"") ;;
|
||||
*) echo "nameserver $server"; dns_found=1 ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
done
|
||||
IFS="$saved_ifs"
|
||||
fi
|
||||
|
||||
if [ "$dns_found" -eq 0 ]; then
|
||||
while IFS= read -r rline; do
|
||||
case "$rline" in
|
||||
nameserver*)
|
||||
ip=$(echo "$rline" | ${pkgs.gnused}/bin/sed 's/^nameserver //')
|
||||
case "$ip" in
|
||||
127.*|::1) ;;
|
||||
*) echo "$rline"; dns_found=1 ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
done < /etc/resolv.conf
|
||||
fi
|
||||
${optionalString (fallbackLines != "") ''
|
||||
if [ "$dns_found" -eq 0 ]; then
|
||||
${fallbackLines} fi
|
||||
''}
|
||||
} > /etc/netns/${ns}/resolv.conf
|
||||
|
||||
${pkgs.iproute2}/bin/ip netns add ${ns}
|
||||
|
|
@ -171,6 +88,7 @@ let
|
|||
${pkgs.iproute2}/bin/ip -n ${ns} addr add ${nsIP} dev ${vethNS}
|
||||
${pkgs.iproute2}/bin/ip -n ${ns} link set ${vethNS} up
|
||||
${pkgs.iproute2}/bin/ip -n ${ns} link set lo up
|
||||
${pkgs.iproute2}/bin/ip -n ${ns} route add default via ${hostAddr}
|
||||
|
||||
${pkgs.iptables}/bin/iptables -t nat -C POSTROUTING -s ${nsIP} -j MASQUERADE 2>/dev/null \
|
||||
|| ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s ${nsIP} -j MASQUERADE
|
||||
|
|
@ -231,10 +149,7 @@ in
|
|||
description = "Tailscale instances configured in separate network namespaces.";
|
||||
example = literalExpression ''
|
||||
{
|
||||
work = {
|
||||
authKeyFile = "/run/secrets/ts-work/authkey";
|
||||
acceptRoutes = true;
|
||||
};
|
||||
work = {};
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
|
@ -247,25 +162,12 @@ in
|
|||
index: elem:
|
||||
let
|
||||
name = elem.name;
|
||||
instanceCfg = elem.value;
|
||||
units = mkNetnsSetup index name instanceCfg;
|
||||
units = mkNetnsSetup index name elem.value;
|
||||
in
|
||||
[
|
||||
units."netns@ts-${name}"
|
||||
units."tailscaled-${name}"
|
||||
{ name = "netns@ts-${name}"; value = units."netns@ts-${name}"; }
|
||||
{ name = "tailscaled-${name}"; value = units."tailscaled-${name}"; }
|
||||
]
|
||||
++ optional (instanceCfg.authKeyFile != null) {
|
||||
"tailscaled-autoconnect-${name}" = {
|
||||
description = "Tailscale autoconnect for instance ${name}";
|
||||
after = [ "tailscaled-${name}.service" ];
|
||||
requires = [ "tailscaled-${name}.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
Type = "notify";
|
||||
};
|
||||
script = "${mkAutoconnectScript name instanceCfg}";
|
||||
};
|
||||
}
|
||||
) (attrsToList cfg.instances)
|
||||
)
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue