feat(tailscale): add socket option for userspace tailscaled
This commit is contained in:
parent
c6cdbb630c
commit
036bb842fd
@ -75,6 +75,8 @@ export type GatewayTailscaleConfig = {
|
|||||||
mode?: GatewayTailscaleMode;
|
mode?: GatewayTailscaleMode;
|
||||||
/** Reset serve/funnel configuration on shutdown. */
|
/** Reset serve/funnel configuration on shutdown. */
|
||||||
resetOnExit?: boolean;
|
resetOnExit?: boolean;
|
||||||
|
/** Path to tailscaled socket (for userspace Tailscale). */
|
||||||
|
socket?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GatewayRemoteConfig = {
|
export type GatewayRemoteConfig = {
|
||||||
|
|||||||
@ -329,6 +329,7 @@ export const ClawdbotSchema = z
|
|||||||
.object({
|
.object({
|
||||||
mode: z.union([z.literal("off"), z.literal("serve"), z.literal("funnel")]).optional(),
|
mode: z.union([z.literal("off"), z.literal("serve"), z.literal("funnel")]).optional(),
|
||||||
resetOnExit: z.boolean().optional(),
|
resetOnExit: z.boolean().optional(),
|
||||||
|
socket: z.string().optional(),
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
export async function startGatewayTailscaleExposure(params: {
|
export async function startGatewayTailscaleExposure(params: {
|
||||||
tailscaleMode: "off" | "serve" | "funnel";
|
tailscaleMode: "off" | "serve" | "funnel";
|
||||||
resetOnExit?: boolean;
|
resetOnExit?: boolean;
|
||||||
|
socket?: string;
|
||||||
port: number;
|
port: number;
|
||||||
controlUiBasePath?: string;
|
controlUiBasePath?: string;
|
||||||
logTailscale: { info: (msg: string) => void; warn: (msg: string) => void };
|
logTailscale: { info: (msg: string) => void; warn: (msg: string) => void };
|
||||||
@ -17,13 +18,15 @@ export async function startGatewayTailscaleExposure(params: {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const socketOpts = params.socket ? { socket: params.socket } : undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (params.tailscaleMode === "serve") {
|
if (params.tailscaleMode === "serve") {
|
||||||
await enableTailscaleServe(params.port);
|
await enableTailscaleServe(params.port, undefined, socketOpts);
|
||||||
} else {
|
} else {
|
||||||
await enableTailscaleFunnel(params.port);
|
await enableTailscaleFunnel(params.port, undefined, socketOpts);
|
||||||
}
|
}
|
||||||
const host = await getTailnetHostname().catch(() => null);
|
const host = await getTailnetHostname(undefined, undefined, socketOpts).catch(() => null);
|
||||||
if (host) {
|
if (host) {
|
||||||
const uiPath = params.controlUiBasePath ? `${params.controlUiBasePath}/` : "/";
|
const uiPath = params.controlUiBasePath ? `${params.controlUiBasePath}/` : "/";
|
||||||
params.logTailscale.info(
|
params.logTailscale.info(
|
||||||
@ -45,9 +48,9 @@ export async function startGatewayTailscaleExposure(params: {
|
|||||||
return async () => {
|
return async () => {
|
||||||
try {
|
try {
|
||||||
if (params.tailscaleMode === "serve") {
|
if (params.tailscaleMode === "serve") {
|
||||||
await disableTailscaleServe();
|
await disableTailscaleServe(undefined, socketOpts);
|
||||||
} else {
|
} else {
|
||||||
await disableTailscaleFunnel();
|
await disableTailscaleFunnel(undefined, socketOpts);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
params.logTailscale.warn(
|
params.logTailscale.warn(
|
||||||
|
|||||||
@ -477,6 +477,7 @@ export async function startGatewayServer(
|
|||||||
const tailscaleCleanup = await startGatewayTailscaleExposure({
|
const tailscaleCleanup = await startGatewayTailscaleExposure({
|
||||||
tailscaleMode,
|
tailscaleMode,
|
||||||
resetOnExit: tailscaleConfig.resetOnExit,
|
resetOnExit: tailscaleConfig.resetOnExit,
|
||||||
|
socket: tailscaleConfig.socket,
|
||||||
port,
|
port,
|
||||||
controlUiBasePath,
|
controlUiBasePath,
|
||||||
logTailscale,
|
logTailscale,
|
||||||
|
|||||||
@ -101,7 +101,11 @@ export async function findTailscaleBinary(): Promise<string | null> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getTailnetHostname(exec: typeof runExec = runExec, detectedBinary?: string) {
|
export async function getTailnetHostname(
|
||||||
|
exec: typeof runExec = runExec,
|
||||||
|
detectedBinary?: string,
|
||||||
|
opts?: { socket?: string },
|
||||||
|
) {
|
||||||
// Derive tailnet hostname (or IP fallback) from tailscale status JSON.
|
// Derive tailnet hostname (or IP fallback) from tailscale status JSON.
|
||||||
const candidates = detectedBinary
|
const candidates = detectedBinary
|
||||||
? [detectedBinary]
|
? [detectedBinary]
|
||||||
@ -111,7 +115,10 @@ export async function getTailnetHostname(exec: typeof runExec = runExec, detecte
|
|||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
if (candidate.startsWith("/") && !existsSync(candidate)) continue;
|
if (candidate.startsWith("/") && !existsSync(candidate)) continue;
|
||||||
try {
|
try {
|
||||||
const { stdout } = await exec(candidate, ["status", "--json"], {
|
const args = opts?.socket
|
||||||
|
? ["--socket", opts.socket, "status", "--json"]
|
||||||
|
: ["status", "--json"];
|
||||||
|
const { stdout } = await exec(candidate, args, {
|
||||||
timeoutMs: 5000,
|
timeoutMs: 5000,
|
||||||
maxBuffer: 400_000,
|
maxBuffer: 400_000,
|
||||||
});
|
});
|
||||||
@ -350,33 +357,55 @@ export async function ensureFunnel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function enableTailscaleServe(port: number, exec: typeof runExec = runExec) {
|
export async function enableTailscaleServe(
|
||||||
|
port: number,
|
||||||
|
exec: typeof runExec = runExec,
|
||||||
|
opts?: { socket?: string },
|
||||||
|
) {
|
||||||
const tailscaleBin = await getTailscaleBinary();
|
const tailscaleBin = await getTailscaleBinary();
|
||||||
await execWithSudoFallback(exec, tailscaleBin, ["serve", "--bg", "--yes", `${port}`], {
|
const args = opts?.socket
|
||||||
|
? ["--socket", opts.socket, "serve", "--bg", "--yes", `${port}`]
|
||||||
|
: ["serve", "--bg", "--yes", `${port}`];
|
||||||
|
await execWithSudoFallback(exec, tailscaleBin, args, {
|
||||||
maxBuffer: 200_000,
|
maxBuffer: 200_000,
|
||||||
timeoutMs: 15_000,
|
timeoutMs: 15_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function disableTailscaleServe(exec: typeof runExec = runExec) {
|
export async function disableTailscaleServe(
|
||||||
|
exec: typeof runExec = runExec,
|
||||||
|
opts?: { socket?: string },
|
||||||
|
) {
|
||||||
const tailscaleBin = await getTailscaleBinary();
|
const tailscaleBin = await getTailscaleBinary();
|
||||||
await execWithSudoFallback(exec, tailscaleBin, ["serve", "reset"], {
|
const args = opts?.socket ? ["--socket", opts.socket, "serve", "reset"] : ["serve", "reset"];
|
||||||
|
await execWithSudoFallback(exec, tailscaleBin, args, {
|
||||||
maxBuffer: 200_000,
|
maxBuffer: 200_000,
|
||||||
timeoutMs: 15_000,
|
timeoutMs: 15_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function enableTailscaleFunnel(port: number, exec: typeof runExec = runExec) {
|
export async function enableTailscaleFunnel(
|
||||||
|
port: number,
|
||||||
|
exec: typeof runExec = runExec,
|
||||||
|
opts?: { socket?: string },
|
||||||
|
) {
|
||||||
const tailscaleBin = await getTailscaleBinary();
|
const tailscaleBin = await getTailscaleBinary();
|
||||||
await execWithSudoFallback(exec, tailscaleBin, ["funnel", "--bg", "--yes", `${port}`], {
|
const args = opts?.socket
|
||||||
|
? ["--socket", opts.socket, "funnel", "--bg", "--yes", `${port}`]
|
||||||
|
: ["funnel", "--bg", "--yes", `${port}`];
|
||||||
|
await execWithSudoFallback(exec, tailscaleBin, args, {
|
||||||
maxBuffer: 200_000,
|
maxBuffer: 200_000,
|
||||||
timeoutMs: 15_000,
|
timeoutMs: 15_000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function disableTailscaleFunnel(exec: typeof runExec = runExec) {
|
export async function disableTailscaleFunnel(
|
||||||
|
exec: typeof runExec = runExec,
|
||||||
|
opts?: { socket?: string },
|
||||||
|
) {
|
||||||
const tailscaleBin = await getTailscaleBinary();
|
const tailscaleBin = await getTailscaleBinary();
|
||||||
await execWithSudoFallback(exec, tailscaleBin, ["funnel", "reset"], {
|
const args = opts?.socket ? ["--socket", opts.socket, "funnel", "reset"] : ["funnel", "reset"];
|
||||||
|
await execWithSudoFallback(exec, tailscaleBin, args, {
|
||||||
maxBuffer: 200_000,
|
maxBuffer: 200_000,
|
||||||
timeoutMs: 15_000,
|
timeoutMs: 15_000,
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user