You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat: Add UseKeychain SSH config option support (Phase 1 - Parsing)
Implement parsing support for the UseKeychain SSH configuration option,
an Apple-specific extension to OpenSSH for macOS Keychain integration.
Phase 1 Implementation (Parsing Only):
- Added use_keychain field to SshHostConfig (macOS only, conditional compilation)
- Implemented parsing logic for "usekeychain" keyword in authentication options
- Added configuration merge logic with proper precedence handling
- Platform-specific warnings on non-macOS systems
- Comprehensive test coverage (13 tests: 8 parser + 5 resolver)
- Full documentation in README.md and man page
Key Features:
- Uses #[cfg(target_os = "macos")] for platform-specific code
- Consistent with existing authentication options (parse_yes_no helper)
- Cross-platform compatibility with IgnoreUnknown directive
- Integration with AddKeysToAgent option
Phase 2 (Future):
- macOS Keychain API integration
- Actual key storage/retrieval functionality
Files Modified:
- src/ssh/ssh_config/types.rs - Added use_keychain field
- src/ssh/ssh_config/parser/options/authentication.rs - Parsing logic
- src/ssh/ssh_config/parser/options/mod.rs - Dispatcher routing
- src/ssh/ssh_config/resolver.rs - Merge logic
- src/ssh/ssh_config/parser/tests.rs - Parser tests (8 tests)
- src/ssh/ssh_config/resolver_tests.rs - Resolver tests (5 tests)
- README.md - User documentation
- docs/man/bssh.1 - Man page documentation
Related: #59
* feat: Complete Phase 2 macOS Keychain integration for UseKeychain option
Implement full macOS Keychain API integration for the UseKeychain SSH config
option, enabling automatic passphrase storage and retrieval.
Phase 2 Implementation (Keychain Integration):
- Implemented macOS Security Framework integration in src/ssh/keychain_macos.rs
- Added store_passphrase(), retrieve_passphrase(), and delete_passphrase() functions
- Integrated keychain operations into SSH authentication flow (src/ssh/auth.rs)
- Passphrases automatically stored after successful authentication
- Passphrases automatically retrieved on subsequent connections
- Secure memory handling using Zeroizing for all passphrase data
- Helper function determine_use_keychain() to extract config value
- Command executors updated to pass use_keychain from SSH config
- Cross-platform compatibility maintained with conditional compilation
Key Features:
- Seamless macOS Keychain integration via Security Framework
- Automatic passphrase storage after successful key-based authentication
- Automatic retrieval from Keychain before prompting user
- Secure zeroization of passphrases in memory
- Integration with SSH config UseKeychain option
- Works with both specified key files and default key locations
- Full test coverage (7 tests in keychain_macos module)
Security Considerations:
- Passphrases stored using macOS Security Framework's GenericPassword API
- Service name: "bssh-ssh-key-passphrase"
- Account name: canonical path of SSH key file
- Requires user authentication when Keychain is locked (managed by macOS)
- All sensitive data uses Zeroizing for secure memory cleanup
- Passphrase length validation (max 8KB) to prevent DoS
- Path canonicalization to prevent path traversal attacks
Files Modified:
- Cargo.toml - Added security-framework dependency for macOS
- src/ssh/keychain_macos.rs - New keychain module (498 lines, 7 tests)
- src/ssh/auth.rs - Integrated keychain in authentication flow
- src/app/initialization.rs - Added determine_use_keychain() helper
- src/app/dispatcher.rs - Extract and pass use_keychain from SSH config
- src/commands/exec.rs - Pass use_keychain to executor
- src/executor/* - Threading use_keychain through execution pipeline
- src/ssh/client/* - Updated connection methods with use_keychain parameter
- README.md - Updated documentation to reflect Phase 2 completion
- docs/man/bssh.1 - Updated man page documentation
Testing:
- All 357 tests pass including 7 new keychain tests
- Tests cover: store/retrieve, deletion, nonexistent passphrases, updates,
zeroization, length validation, and invalid paths
- Integration with existing auth tests
Related: #59 (Phase 2)
Depends on: d58efcd (Phase 1 - Parsing)
* fix: Add password authentication fallback to match OpenSSH behavior
Add automatic password authentication fallback when all key-based
authentication methods fail, matching standard OpenSSH behavior.
Problem:
- When connecting to new servers without authorized keys, connection
would fail immediately without prompting for password
- Users had to explicitly use --password flag even when no keys worked
- This differs from OpenSSH which tries password as last resort
Solution:
Implemented 6-step authentication priority (matches OpenSSH):
1. Password (if --password flag set)
2. SSH agent (if explicitly requested)
3. Specified key file (if provided via -i)
4. SSH agent auto-detection
5. Default key locations (~/.ssh/id_*)
6. Password fallback (NEW) - Interactive terminals only
Key Features:
- Fallback only occurs in interactive terminals (stdin is TTY)
- Non-interactive environments (CI/automation) get helpful error message
- Maintains all existing security measures:
- Passwords zeroized after use
- Timing attack mitigation (50ms normalization)
- No credential leakage in errors
- Backward compatible with existing workflows
Security:
- Uses atty crate to detect interactive terminals
- Non-interactive sessions cannot hang on password prompts
- Error messages provide clear guidance on authentication options
Testing:
- Added test_password_fallback_in_non_interactive()
- All 364 tests pass
- Verified interactive and non-interactive behavior
This fix significantly improves user experience by eliminating the need
for --password flag in most common scenarios while maintaining security
and automation compatibility.
Fixes authentication regression reported by users where password prompts
no longer appeared for new server connections.
* fix: Add use_keychain support to interactive mode
Interactive mode (direct hostname without -H flag) was missing
use_keychain field integration from Phase 2, causing authentication
to fail. Added conditional use_keychain field and updated all
interactive command instantiation points.
Fixes password prompt issue when using: bssh hostname
* fix: Add password authentication fallback for interactive mode
When publickey authentication fails in interactive mode, automatically
retry with password authentication (matching OpenSSH behavior).
Changes:
- Add password fallback retry in connect_to_node_pty (2 paths)
- Add password fallback retry in connect_to_node (2 paths)
- Only retry when stdin is a TTY (interactive terminal)
- Log retry attempt for debugging
Behavior now matches OpenSSH:
1. Try publickey authentication first
2. On failure, prompt for password if in interactive terminal
This fixes the issue where bssh would fail without password prompt
when publickey authentication failed.
* fix(security): validate SSH key file ownership before Keychain storage - Priority: CRITICAL
- Added ownership validation to prevent storing passphrases for keys owned by other users
- Check that current user owns the SSH key file before allowing Keychain storage
- Warn if SSH key has overly permissive permissions (world-readable)
- Also fixed unnecessary passphrase_bytes.clone() memory copy - Priority: LOW
- Added libc dependency for macOS to access getuid()
Security: Prevents unauthorized passphrase storage for other users' SSH keys
* fix(security/perf): add user consent for password fallback and eliminate code duplication - Priority: HIGH
Security improvements:
- Added explicit user consent prompt before password fallback authentication
- Password fallback now only occurs with user permission (yes/no prompt)
- Prevents unexpected password prompts that could lead to credential exposure
- Added 30-second timeout for consent prompt to prevent hanging
Performance improvements:
- Eliminated 249 lines of duplicated connection logic (4 identical retry blocks)
- Created establish_connection() helper to centralize connection handling
- Removed redundant password fallback attempts from connection.rs
- Authentication fallback logic now centralized in auth module
The password fallback with consent is now handled properly in the auth module's
determine_method() flow, eliminating the need for duplicate retry logic in the
connection layer. This reduces code complexity and improves maintainability.
* fix(security): add rate limiting between authentication attempts - Priority: MEDIUM
Security improvements:
- Added 100ms delay before each connection attempt in establish_connection()
- Added 1 second delay before password fallback consent prompt
- Prevents rapid-fire authentication attempts that could trigger fail2ban
- Helps mitigate brute-force attack attempts
- Gives servers time to process authentication failures
The rate limiting is applied at two levels:
1. Connection level: Small delay (100ms) before each connection attempt
2. Fallback level: Larger delay (1s) before password fallback prompt
This prevents both automated attacks and accidental DoS from rapid retries.
Performance: The double connection attempt issue is also resolved as a side effect
of the earlier refactoring - we now have a single connection path with proper
rate limiting instead of duplicate retry logic.
* fix: Remove unnecessary mut from executor in exec.rs
Variable shadowing is used for macOS-specific with_keychain call,
eliminating the need for mutable binding on non-macOS platforms.
This fixes the clippy unused_mut warning.
* fix: Make determine_use_keychain import and function conditional
- Add #[cfg(target_os = "macos")] to import in dispatcher.rs
- Add #[allow(dead_code)] to non-macOS stub function in initialization.rs
This fixes clippy unused_imports and dead_code warnings on non-macOS platforms
while maintaining API consistency across platforms.
Copy file name to clipboardExpand all lines: docs/man/bssh.1
+25Lines changed: 25 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -676,6 +676,31 @@ Possible values:
676
676
Example:
677
677
.IAddKeysToAgentyes
678
678
679
+
.TP
680
+
.BUseKeychain
681
+
.B(macOSonly)
682
+
Specifies whether to use the macOS Keychain for storing SSH key passphrases.
683
+
This is an Apple-specific patch to OpenSSH and is only available on macOS systems.
684
+
When enabled, passphrases are automatically retrieved from and stored in the macOS Keychain.
685
+
.RS
686
+
.IP\[bu]2
687
+
.BImplementation:
688
+
Fully integrated with macOS Keychain via Security Framework. Passphrases are securely stored after successful authentication and retrieved on subsequent connections.
689
+
.IP\[bu]2
690
+
.BCross-platformcompatibility:
691
+
Use
692
+
.IIgnoreUnknownUseKeychain
693
+
before
694
+
.IUseKeychain
695
+
in your SSH config to prevent errors on non-macOS systems.
696
+
.RE
697
+
.IP
698
+
Example:
699
+
.nf
700
+
.IIgnoreUnknownUseKeychain
701
+
.IUseKeychainyes
702
+
.fi
703
+
679
704
.TP
680
705
.BIdentityAgent
681
706
Specifies the path to the SSH agent socket to use for authentication.
0 commit comments