Skip to content

Use bash as the default shell for SSH connect sessions - #5002

Open
anton-107 wants to merge 2 commits into
mainfrom
anekipelov/ssh-connect-bash-shell
Open

Use bash as the default shell for SSH connect sessions#5002
anton-107 wants to merge 2 commits into
mainfrom
anekipelov/ssh-connect-bash-shell

Conversation

@anton-107

Copy link
Copy Markdown
Contributor

Summary

  • Client-side fix: When no remote command is specified, spawnSSHClient now explicitly runs /bin/bash -l with -t for PTY allocation instead of relying on the default login shell from /etc/passwd (which is /bin/sh on Databricks compute images).
  • Server-side fix: Before starting sshd, the server attempts to update /etc/passwd to set bash as the login shell for the current user. This covers IDE connections (VS Code, Cursor) that manage their own SSH sessions and don't go through spawnSSHClient.
  • Both fixes degrade gracefully: the client-side fix only applies when no additional args are passed, and the server-side fix logs a warning and continues if /etc/passwd can't be modified.

Test plan

  • Verified databricks ssh connect --cluster=<id> opens a bash shell (tested manually)
  • Verify databricks ssh connect --cluster=<id> -- ls -la still works (additional args passthrough)
  • Verify IDE connections (VS Code/Cursor) get bash after a fresh server start
  • Verify sftp still works for IDE file operations

This pull request was AI-assisted by Isaac.

The default login shell on Databricks compute images is /bin/sh.
This change ensures users get bash in two ways:
1. Client-side: when no remote command is specified, explicitly
request /bin/bash -l with PTY allocation instead of relying on
the default login shell from /etc/passwd.
2. Server-side: attempt to update /etc/passwd to set bash as the
login shell before starting sshd, which also covers IDE
connections (VS Code, Cursor) that manage their own SSH sessions.
Co-authored-by: Isaac
@github-actions

Copy link
Copy Markdown
Contributor

Waiting for approval

Based on git history, these people are best suited to review:

  • @ilia-db -- recent work in experimental/ssh/internal/client/, experimental/ssh/internal/server/

Eligible reviewers: @andrewnester, @denik, @pietern, @renaudhartert-db, @shreyas-goenka, @simonfaltum

Suggestions based on git history. See OWNERS for ownership rules.

@anton-107
anton-107 requested a review from ilia-dbApril 16, 2026 17:03
return fmt.Errorf("user %s not found in %s", username, passwdPath)
}

return os.WriteFile(passwdPath, []byte(strings.Join(result, "\n")+"\n"), 0o644)

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.

I think this will always fail. The repl user is not root.

It might be possible with usermod if it is setuid but I'm not sure about that.

@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Commit: 7c1543e

Run: 27353327873

Env🟨​KNOWN🔄​flaky💚​RECOVERED🙈​SKIP✅​pass🙈​skipTime
🟨​aws linux72152629699:54
🟨​aws windows71526696715:49
💚​aws-ucws linux71536088313:02
💚​aws-ucws windows71536288111:29
💚​azure linux1172679677:30
💚​azure windows11726996511:56
💚​azure-ucws linux1173658799:46
💚​azure-ucws windows11736787713:08
💚​gcp linux1172639708:47
💚​gcp windows11726596811:53
24 interesting tests: 15 SKIP, 7 KNOWN, 2 flaky
Test Nameaws linuxaws windowsaws-ucws linuxaws-ucws windowsazure linuxazure windowsazure-ucws linuxazure-ucws windowsgcp linuxgcp windows
🟨​TestAccept🟨​K🟨​K💚​R💚​R💚​R💚​R💚​R💚​R💚​R💚​R
🙈​TestAccept/bundle/invariant/no_drift🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/permissions🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions🟨​K🟨​K💚​R💚​R🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=direct🟨​K🟨​K💚​R💚​R
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/with_permissions/DATABRICKS_BUNDLE_ENGINE=terraform🟨​K🟨​K💚​R💚​R
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions🟨​K🟨​K💚​R💚​R🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=direct🟨​K🟨​K💚​R💚​R
🟨​TestAccept/bundle/resources/permissions/jobs/destroy_without_mgmtperms/without_permissions/DATABRICKS_BUNDLE_ENGINE=terraform🟨​K🟨​K💚​R💚​R
🙈​TestAccept/bundle/resources/postgres_branches/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/recreate🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/replace_existing🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/update_protected🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_branches/without_branch_id🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_endpoints/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_endpoints/recreate🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/postgres_projects/update_display_name🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/synced_database_tables/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_endpoints/drift/recreated_same_name🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_indexes/basic🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/bundle/resources/vector_search_indexes/grants/select🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🙈​TestAccept/ssh/connection🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S🙈​S
🔄​TestSecretsPutSecretBytesValue🔄​f✅​p🙈​s🙈​s✅​p✅​p✅​p✅​p✅​p✅​p
🔄​TestSecretsPutSecretStringValue🔄​f✅​p🙈​s🙈​s✅​p✅​p✅​p✅​p✅​p✅​p
Top 32 slowest tests (at least 2 minutes):
durationenvtestname
6:15azure windowsTestAccept
6:12azure-ucws windowsTestAccept
6:09gcp windowsTestAccept
5:10aws-ucws windowsTestAccept
5:00gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:47gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:39gcp windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
4:18aws-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
4:11gcp linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:36aws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:17azure windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:17azure linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:15aws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:15aws-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:08aws-ucws linuxTestAccept/bundle/deploy/files/no-snapshot-sync/DATABRICKS_BUNDLE_ENGINE=terraform
3:08azure linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:04azure-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
3:02aws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
3:00aws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:56gcp linuxTestAccept
2:55azure linuxTestAccept
2:54aws-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:52azure windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:51aws-ucws linuxTestAccept
2:51azure-ucws linuxTestAccept
2:50azure-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:46azure-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=terraform
2:40azure-ucws linuxTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:36aws-ucws linuxTestAccept/bundle/resources/volumes/recreate/DATABRICKS_BUNDLE_ENGINE=terraform
2:33aws-ucws windowsTestAccept/bundle/resources/apps/inline_config/DATABRICKS_BUNDLE_ENGINE=direct
2:03aws-ucws linuxTestAccept/bundle/resources/model_serving_endpoints/basic/DATABRICKS_BUNDLE_ENGINE=terraform
2:01aws-ucws linuxTestAccept/bundle/resources/jobs/shared-root-path/DATABRICKS_BUNDLE_ENGINE=terraform

Chambras pushed a commit to Chambras/newDatabrickscli that referenced this pull request Jul 6, 2026
…atabricks#5687)
## Summary
When `databricks ssh connect` is run with no trailing command, the CLI
now explicitly launches a login `bash` shell over a PTY instead of
relying on the compute image's default login shell (which is `/bin/sh`
on Databricks compute images). This gives a consistent, predictable
interactive shell.
- Interactive (no args): appends `-t` and runs `command -v bash
>/dev/null 2>&1 && exec bash -l || exec "${SHELL:-/bin/sh}" -l`.
Graceful fallback to `$SHELL`/`/bin/sh` if bash is absent.
- Non-interactive (`databricks ssh connect ... -- ls -la`): unchanged —
the user's command is passed verbatim with no PTY/bash wrapper.
This supersedes databricks#5002. That PR also tried to set bash as the login shell
server-side by editing `/etc/passwd`, which @pietern correctly pointed
out can never work — the `repl` user is not root. That approach is
dropped; this change is purely client-side. Forcing bash inside the IDE
(VS Code/Cursor) terminal is intentionally out of scope and tracked
separately.
Logic is isolated in a new `buildRemoteShellArgs` helper to keep the
change small and unit-testable.
## Test plan
- [x] `go test ./experimental/ssh/internal/client/` — new
`TestBuildRemoteShellArgs` covers interactive + passthrough cases.
- [x] Existing `acceptance/ssh/connection/` test exercises the
non-interactive path (regression guard) and is unchanged.
- [ ] Manual: `databricks ssh connect --cluster=<id>` opens a bash
prompt (`echo $0`); `databricks ssh connect --cluster=<id> -- ls -la`
still works.
Jira: DECO-27476
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@anton-107@eng-dev-ecosystem-bot@pietern