mirror of
https://github.com/tailscale/tailscale.git
synced 2026-07-20 21:23:07 +08:00
nixos: add userspace outbound proxy options
Add per-instance `proxy` (socks/http/both) and `proxyListenAddress`. "both" shares one address; tailscaled splits SOCKS from HTTP. Assert addresses are unique across instances and install `tailscale-proxies` to list them.
This commit is contained in:
parent
ae78ca5433
commit
ac126bc078
@ -71,6 +71,38 @@ in {
|
||||
'';
|
||||
};
|
||||
|
||||
proxy = mkOption {
|
||||
type = types.nullOr (types.enum ["off" "socks" "http" "both"]);
|
||||
default = null;
|
||||
example = "socks";
|
||||
description = ''
|
||||
Run an outbound proxy for accessing the tailnet without a TUN
|
||||
interface (the usual access method in userspace networking mode,
|
||||
also works in tun mode).
|
||||
|
||||
`null` or `"off"` disables it. `"socks"` runs a SOCKS5 server,
|
||||
`"http"` an outbound HTTP proxy, `"both"` runs both on the same
|
||||
`proxyListenAddress` (tailscaled routes SOCKS-looking connections
|
||||
to SOCKS and the rest to HTTP).
|
||||
|
||||
Run `tailscale-proxies` on the host to list the active proxy
|
||||
addresses across all instances.
|
||||
|
||||
See <https://tailscale.com/kb/1112/userspace-networking>
|
||||
'';
|
||||
};
|
||||
|
||||
proxyListenAddress = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost:1055";
|
||||
example = "0.0.0.0:1055";
|
||||
description = ''
|
||||
`[ip]:port` the proxy binds to. Both proxies share this address
|
||||
when `proxy = "both"`. Defaults to localhost to keep it off the
|
||||
wire. Must be unique across enabled instances.
|
||||
'';
|
||||
};
|
||||
|
||||
permitCertUid = mkOption {
|
||||
type = types.nullOr types.nonEmptyStr;
|
||||
default = null;
|
||||
|
||||
@ -91,6 +91,10 @@ self: {
|
||||
"--statedir=/var/lib/${meta.stateDir}"
|
||||
"--socket=${meta.socketPath}"
|
||||
]
|
||||
++ optional (cfg.proxy == "socks" || cfg.proxy == "both")
|
||||
"--socks5-server=${cfg.proxyListenAddress}"
|
||||
++ optional (cfg.proxy == "http" || cfg.proxy == "both")
|
||||
"--outbound-http-proxy-listen=${cfg.proxyListenAddress}"
|
||||
++ cfg.extraDaemonFlags;
|
||||
|
||||
# Build auth key query parameters string.
|
||||
@ -315,6 +319,42 @@ self: {
|
||||
'';
|
||||
};
|
||||
|
||||
# Whether an instance runs a proxy (null and "off" both mean disabled).
|
||||
proxyEnabled = cfg: cfg.proxy != null && cfg.proxy != "off";
|
||||
|
||||
# All enabled instances running a proxy, as {instance,proxy,address} records.
|
||||
proxyList = let
|
||||
entry = enabled: name: cfg:
|
||||
optional (enabled && proxyEnabled cfg) {
|
||||
instance = name;
|
||||
inherit (cfg) proxy;
|
||||
address = cfg.proxyListenAddress;
|
||||
};
|
||||
in
|
||||
entry singularCfg.enable "default" singularCfg
|
||||
++ concatMap (n: entry pluralCfg.${n}.enable n pluralCfg.${n})
|
||||
(attrNames pluralCfg);
|
||||
|
||||
proxyListJson = pkgs.writeText "tailscale-proxies.json" (builtins.toJSON proxyList);
|
||||
|
||||
# `tailscale-proxies [--json]`: list active proxy addresses across instances.
|
||||
proxyListPkg = pkgs.writeShellScriptBin "tailscale-proxies" ''
|
||||
if [ "$1" = "--json" ]; then
|
||||
cat ${proxyListJson}
|
||||
echo
|
||||
exit 0
|
||||
fi
|
||||
${pkgs.util-linux}/bin/column -t <<'EOF'
|
||||
INSTANCE PROXY ADDRESS
|
||||
${concatStringsSep "\n" (map (p: "${p.instance} ${p.proxy} ${p.address}") proxyList)}
|
||||
EOF
|
||||
cat <<'EOF'
|
||||
|
||||
# SOCKS5: socks5://ADDRESS HTTP: http://ADDRESS
|
||||
# e.g. curl --proxy socks5://localhost:1055 https://host.your-tailnet.ts.net
|
||||
EOF
|
||||
'';
|
||||
|
||||
# Generate the JSON config file for tailscale serve set-config --all.
|
||||
# Prepends "svc:" to each service name and omits `advertised` when null
|
||||
# to match the Go opt.Bool semantics (unset = advertised by default).
|
||||
@ -426,7 +466,30 @@ in {
|
||||
}
|
||||
]
|
||||
else []
|
||||
) (builtins.attrNames pluralCfg);
|
||||
) (builtins.attrNames pluralCfg)
|
||||
++ (let
|
||||
addrOf = enabled: c:
|
||||
optional
|
||||
(enabled
|
||||
&& proxyEnabled c
|
||||
&& !(lib.hasSuffix ":0" c.proxyListenAddress))
|
||||
c.proxyListenAddress;
|
||||
proxyAddrs =
|
||||
addrOf singularCfg.enable singularCfg
|
||||
++ concatMap (n: addrOf pluralCfg.${n}.enable pluralCfg.${n})
|
||||
(attrNames pluralCfg);
|
||||
dupes = lib.unique (lib.filter (
|
||||
a: lib.count (x: x == a) proxyAddrs > 1
|
||||
) proxyAddrs);
|
||||
in [
|
||||
{
|
||||
assertion = proxyAddrs == lib.unique proxyAddrs;
|
||||
message = ''
|
||||
Multiple Tailscale instances share a proxy listen address: ${concatStringsSep ", " dupes}.
|
||||
Each instance with a proxy enabled needs a unique `proxyListenAddress`.
|
||||
'';
|
||||
}
|
||||
]);
|
||||
|
||||
# ── Systemd services (singular + plural merged) ──
|
||||
systemd.services = mkMerge [
|
||||
@ -448,6 +511,7 @@ in {
|
||||
([cfg.package] ++ attrValues (mkCliWrapper name cfg))
|
||||
)
|
||||
pluralCfg))
|
||||
(mkIf (proxyList != []) [proxyListPkg])
|
||||
];
|
||||
|
||||
# ── DHCP exclusion (TUN mode only) ──
|
||||
|
||||
Loading…
Reference in New Issue
Block a user