Skip to content

[SSH] Check port_fwd_serverless_enabled before starting serverless compute - #7063

Merged
anton-107 merged 2 commits into
mainfrom
ssh-preflight-port-forwarding-setting
Oct 16, 2026
Merged

anton-107 merged 2 commits into
mainfrom
ssh-preflight-port-forwarding-setting

Conversation

@anton-107

Copy link
Copy Markdown
Contributor

Changes

A serverless databricks ssh connect (CPU or --accelerator) now reads the port_fwd_serverless_enabled workspace setting first, in ProxyCommand mode too. When an administrator has turned it off, connect stops before it creates the secret scope, uploads binaries or submits the server run:

Connecting to my-session...
Error: port forwarding to serverless compute is disabled for this workspace (setting port_fwd_serverless_enabled), and the SSH tunnel needs it. Ask a workspace or account admin to enable it, or connect to a dedicated cluster with --cluster
  • checkPortForwardingToServerless reads the setting with WorkspaceSettingsV2.GetPublicWorkspaceSetting and refuses only when the read succeeds and effective_boolean_val is false.
  • A read that fails is logged at debug level and connect continues. The driver proxy's 403 from [SSH] Fail fast when the driver proxy refuses the tunnel #7057 then stops it.
  • libs/testserver answers the settings GET, defaulting port_fwd_serverless_enabled to true like the real setting.

Why

Stacked on #7057, PR 2 of DECO-28889. #7057 makes connect stop on the driver proxy's 403, but by then it has uploaded binaries and started serverless compute. The setting is what the proxy enforces, so checking it first avoids both.

The CLI can't see everything the proxy's gate (PortForwardingToServerlessGate) checks. The gate also needs its enforcement flag on and skips allow-listed ports, and the setting is only served through the public API in dev and staging (public_api_stage: STAGING; production answers Setting port_fwd_serverless_enabled is not enabled for public API). So a failed read is never an error, and the 403 handling stays as the backstop. The acceptance test for that backstop now stubs the settings read the way production answers it.

Tests

  • Unit: TestCheckPortForwardingToServerless covers disabled, enabled, no effective value, and a failed read. Plus new libs/testserver/settings_test.go tests.
  • Acceptance: new acceptance/ssh/connect-serverless-port-forwarding-disabled checks the error and that connect sent no requests besides reads. connect-serverless-feature-disabled keeps covering the 403 path, with the settings read failing. All acceptance/ssh tests pass.
  • Manual, dogfood, setting off: serverless CPU, --accelerator GPU_1xA10, and ProxyCommand mode (ssh -o ProxyCommand="databricks ssh connect --proxy --name …") all stop within 1 s with the error above.
  • Manual, dogfood, setting on: serverless connects and runs the remote command.

This pull request and its description were written by Isaac.

@eng-dev-ecosystem-bot

eng-dev-ecosystem-bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: a84b403

Run: 38904612933

Env ✅​pass 🙈​skip Time
✅​ aws linux-2core-8gb 276 17 5:37
✅​ aws-windows-latest-4core-16gb 278 15 3:42
✅​ azure linux-2core-8gb 275 17 5:43
✅​ azure-windows-latest-4core-16gb 277 15 5:30
✅​ gcp linux-2core-8gb 276 17 5:26
✅​ gcp-windows-latest-4core-16gb 278 15 3:38
Top 6 slowest tests (at least 2 minutes):
duration env testname
5:24 azure-windows-latest-4core-16gb TestAccept
3:50 aws linux-2core-8gb TestAccept
3:49 azure linux-2core-8gb TestAccept
3:45 gcp linux-2core-8gb TestAccept
3:37 aws-windows-latest-4core-16gb TestAccept
3:32 gcp-windows-latest-4core-16gb TestAccept

@rugpanov rugpanov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I left three non-blocking suggestions inline.

// environment. A read that fails therefore lets the connection go ahead, and the proxy's own 403
// (errDriverProxyForbidden) remains the backstop.
func checkPortForwardingToServerless(ctx context.Context, client *databricks.WorkspaceClient) error {
setting, err := client.WorkspaceSettingsV2.GetPublicWorkspaceSetting(ctx, settingsv2.GetPublicWorkspaceSettingRequest{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nice to have] This read uses the default SDK client, which retries 429 and 503 responses until its retry budget runs out. If the settings service is slow, an optional check can block a working connection for minutes, and the ProxyCommand child runs it again. Use newProbeAPIClient from agentshim.go, or give the call a short context timeout and continue when it expires.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO this should definitely be fixed, if not in this PR then in a followup

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1ea406f. The read now goes through newProbeAPIClient, so a 429 or 503 fails on the first attempt and the connection continues. I added a throttled case to TestCheckPortForwardingToServerless that asserts the setting is read exactly once.

cmdio.LogString(ctx, fmt.Sprintf("Connecting to %s...", sessionID))
}

if opts.IsServerlessMode() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nice to have] spawnSSHClient and ensureSSHConfigEntry write a ProxyCommand with --metadata, and that child process runs Run again. So one ssh connect sends the GET twice, and each IDE reconnect sends it again. With --metadata, nothing is uploaded or submitted, so the check prevents nothing. In production, the read always fails today. Run the check only when opts.IsServerlessMode() && opts.ServerMetadata == "", as the ValidateClusterAccess comment does for proxy mode.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1ea406f. The check now runs only when opts.IsServerlessMode() && opts.ServerMetadata == "", so the ProxyCommand child and IDE reconnects no longer send the GET.


if opts.IsServerlessMode() {
if err := checkPortForwardingToServerless(ctx, client); err != nil {
return err

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nice to have] This early return does not set outcome.errorCategory, so logSshTunnelEvent records UNKNOWN. The comment in Run says each failing step sets a category, and UNKNOWN is meant to signal CLI bugs. Add a category such as PORT_FORWARDING_DISABLED, which needs the matching proto change, and use it for the #7057 403 path too. If you add it in a follow-up PR, please say so here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this in the follow-up #7064. It adds PORT_FORWARDING_DISABLED for this early return and DRIVER_PROXY_FORBIDDEN for the #7057 403 path. Both still need the matching universe enum change.

Base automatically changed from ssh-fail-fast-driver-proxy-forbidden to main October 15, 2026 13:52
anton-107 and others added 2 commits October 16, 2026 14:56
…mpute

A serverless ssh connect now reads the port_fwd_serverless_enabled workspace
setting first, in proxy mode too. When an administrator has turned it off,
connect stops before it creates the secret scope, uploads binaries or submits
the server run, and says which setting is off and who can change it.

The driver proxy's gate also depends on an enforcement flag and a port
allow-list the CLI cannot see, and the setting is not served through the
public API in every environment (production today). A failed read therefore
lets the connection go ahead, and the 403 handling from the previous change
stays the backstop. The acceptance test for that path now stubs the settings
read the way production answers it.

DECO-28889

Co-authored-by: Isaac <no-reply@databricks.com>
The setting check used the default SDK client, which retries 429 and 503
until its retry budget runs out, so a slow settings service could hold up
a working connection for minutes. It now reads through the no-retry probe
client the AI Gateway check already uses.

The check is also skipped when reconnecting with --metadata. The
ProxyCommand child runs Run again with the server already running, so the
read prevented nothing and was repeated on every IDE reconnect.

Co-authored-by: Isaac <no-reply@databricks.com>
@anton-107
anton-107 force-pushed the ssh-preflight-port-forwarding-setting branch from 1ea406f to a84b403 Compare October 16, 2026 12:59
@anton-107
anton-107 enabled auto-merge October 16, 2026 13:03
@anton-107
anton-107 added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit 4244322 Oct 16, 2026
27 checks passed
@anton-107
anton-107 deleted the ssh-preflight-port-forwarding-setting branch October 16, 2026 14:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants