From 8e3ac385f74b64ce16671302979e4d920c8edacb Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Sun, 25 Oct 2026 23:58:32 +0100 Subject: [PATCH] fix(terminal): pass the cloudflared Access service token to SSH Closes #12312 --- docker/coolify-terminal/terminal-utils.js | 14 ++++++++++- .../coolify-terminal/terminal-utils.test.js | 23 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docker/coolify-terminal/terminal-utils.js b/docker/coolify-terminal/terminal-utils.js index 6acb9645a6..8675e10516 100644 --- a/docker/coolify-terminal/terminal-utils.js +++ b/docker/coolify-terminal/terminal-utils.js @@ -123,10 +123,22 @@ export function createTerminalUpgradeHandler({ wss, authenticate, onRejected = ( const DEFAULT_TERMINAL_PATH = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'; +// Variables passed to the SSH process besides PATH. `cloudflared access ssh` reads the +// Access service token from them in the ProxyCommand of Cloudflare Tunnel servers. +const ALLOWED_TERMINAL_ENV = ['TUNNEL_SERVICE_TOKEN_ID', 'TUNNEL_SERVICE_TOKEN_SECRET']; + export function getTerminalProcessEnv(environment = process.env) { - return { + const env = { PATH: environment.PATH || DEFAULT_TERMINAL_PATH, }; + + for (const name of ALLOWED_TERMINAL_ENV) { + if (environment[name]) { + env[name] = environment[name]; + } + } + + return env; } export function getTerminalSessionTimeout() { diff --git a/docker/coolify-terminal/terminal-utils.test.js b/docker/coolify-terminal/terminal-utils.test.js index b00dfa7d9f..d0452e1f72 100644 --- a/docker/coolify-terminal/terminal-utils.test.js +++ b/docker/coolify-terminal/terminal-utils.test.js @@ -54,6 +54,29 @@ test('getTerminalProcessEnv uses the default PATH when PATH is empty', () => { }); }); +test('getTerminalProcessEnv passes the cloudflared Access service token to SSH proxy commands', () => { + assert.deepEqual(getTerminalProcessEnv({ + PATH: '/usr/bin:/bin', + TUNNEL_SERVICE_TOKEN_ID: 'token-id.access', + TUNNEL_SERVICE_TOKEN_SECRET: 'token-secret', + TUNNEL_SERVICE_URL: 'must-not-be-inherited', + APP_KEY: 'must-not-be-inherited', + }), { + PATH: '/usr/bin:/bin', + TUNNEL_SERVICE_TOKEN_ID: 'token-id.access', + TUNNEL_SERVICE_TOKEN_SECRET: 'token-secret', + }); +}); + +test('getTerminalProcessEnv omits empty cloudflared service token variables', () => { + assert.deepEqual(getTerminalProcessEnv({ + PATH: '/usr/bin:/bin', + TUNNEL_SERVICE_TOKEN_ID: '', + }), { + PATH: '/usr/bin:/bin', + }); +}); + test('extractTargetHost normalizes quoted IPv4 hosts from generated ssh commands', () => { const sshArgs = extractSshArgs( "timeout 3600 ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ServerAliveInterval=20 -o ConnectTimeout=10 'root'@'10.0.0.5' 'bash -se' << \\\\$abc\necho hi\nabc"