SSH & Jump Hosts¶
Merlya manages SSH connections with pooling, authentication, and jump host support.
Connection Pool¶
Connections are reused to improve performance:
- Pool size: 50 connections max (LRU eviction)
- Pool timeout: 600 seconds (configurable)
- Connect timeout: 30 seconds
- Command timeout: 60 seconds
Authentication¶
Merlya tries authentication methods in order:
- SSH Agent - Keys loaded in ssh-agent
- Key file - Private key from inventory or default
- Passphrase prompt - For encrypted keys
- Password - If configured
- Keyboard-interactive - For MFA
SSH Agent¶
If ssh-agent is running, Merlya uses it automatically:
Encrypted Keys¶
For encrypted private keys, Merlya prompts for the passphrase:
Passphrases can be cached in keyring for the session.
MFA/2FA¶
Keyboard-interactive authentication is supported for MFA:
Jump Hosts / Bastions¶
Access servers through a bastion host using the via parameter.
Natural Language¶
Check disk on db-server via bastion
Execute 'uptime' on web-01 through jump-host
Analyse 192.168.1.100 via ansible
Patterns Detected¶
Merlya recognizes these patterns:
English:
via hostnamethrough hostnameusing bastion hostname
French:
via hostnamevia la machine hostnameen passant par hostnameà travers hostname
How It Works¶
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Merlya │ ──── │ Bastion │ ──── │ Target │
│ │ SSH │ (jump) │ SSH │ (db-01) │
└──────────┘ └──────────┘ └──────────┘
- Merlya connects to bastion via SSH
- Creates tunnel through bastion to target
- Executes commands on target through tunnel
- Returns results
Inventory Configuration¶
Set a default jump host for a host in inventory:
The via parameter in commands overrides inventory settings.
Multiple Hops¶
For multiple jump hosts, configure the chain in inventory:
/hosts add jump1 1.2.3.4
/hosts add jump2 10.0.0.1 --jump jump1
/hosts add target 192.168.1.100 --jump jump2
Host Resolution¶
When you reference a host, Merlya resolves it in order:
- Inventory - Hosts added via
/hosts add - SSH Config -
~/.ssh/configentries - Known hosts -
~/.ssh/known_hosts - /etc/hosts - System hosts file
- DNS - Standard DNS resolution
Host Names¶
Reference inventory hosts with the @ prefix:
This resolves @web01 from inventory and uses its hostname, port, and username.
For direct IP addresses, you can also use:
192.168.1.10— direct IP (no inventory lookup)[email protected]— direct IP with explicit username
Note: Secret references also use
@(e.g.,@db-password). The agent distinguishes them by context:@namein atargetfield is a host,@namein a task description is a secret.
SSH Configuration¶
In ~/.merlya/config.yaml:¶
ssh:
pool_timeout: 600 # Connection reuse time
connect_timeout: 30 # Connection timeout
command_timeout: 60 # Command timeout
default_user: admin # Default SSH user
default_key: ~/.ssh/id_ed25519 # Default key
Per-Host Configuration¶
When adding hosts:
Troubleshooting¶
Connection Timeout¶
Solutions: - Check network connectivity - Verify host is reachable - Increase connect_timeout in config
Authentication Failed¶
Solutions: - Verify SSH key is correct - Check ssh-agent is running - Ensure public key is in authorized_keys
Permission Denied¶
Solutions: - Check username is correct - Verify key permissions (600 for private key) - Try with password authentication
Jump Host Issues¶
Solutions: - Verify jump host is accessible - Check jump host authentication - Ensure target is reachable from jump host
Host Key Verification¶
Solutions: - Add host to ~/.ssh/known_hosts - Or enable auto-add (less secure)
Privilege Elevation¶
Merlya handles privilege elevation (sudo, doas, su) on remote hosts using explicit per-host configuration. No auto-detection - you configure the elevation method when adding a host.
Configuration¶
Elevation is configured per-host in the inventory:
# Configure elevation when editing a host
/hosts edit web01
# → Prompts for elevation_method: none, sudo, sudo_password, doas, doas_password, su
# Or import with elevation configured (TOML/YAML/CSV)
/hosts import inventory.toml
Elevation Methods¶
| Method | Description | Password Required |
|---|---|---|
none | No elevation (default) | No |
sudo | sudo with NOPASSWD | No |
sudo_password | sudo requiring password | Yes |
doas | doas with NOPASSWD (BSD) | No |
doas_password | doas requiring password | Yes |
su | su (root password) | Yes |
TOML Example¶
[hosts.web01]
hostname = "192.168.1.10"
user = "deploy"
elevation_method = "sudo_password"
elevation_user = "root"
[hosts.db01]
hostname = "192.168.1.20"
user = "admin"
elevation_method = "sudo" # NOPASSWD configured
Password Handling¶
Elevation passwords are handled securely:
- Prompt on demand - Asks for password when needed
- Session caching - Optionally cache for current session
- Keyring storage - Store in system keyring (macOS Keychain, Linux Secret Service)
- Reference tokens - Commands use
@elevation:hostname:password - Safe logging - Logs show
@elevation:..., never actual passwords
Usage in Commands¶
When a command requires elevation, Merlya:
- Checks host's
elevation_methodconfiguration - Prompts for confirmation (DIAGNOSTIC) or HITL approval (CHANGE)
- Requests password if needed
- Executes with appropriate elevation
> Read /var/log/secure on web01
# Host web01 has elevation_method=sudo_password
# Prompts: "Execute as root on web01? [Y/n]"
# If yes, prompts for password
# Executes: sudo -S cat /var/log/secure
Clear Elevation Passwords¶
/secret clear-elevation # List stored elevation passwords
/secret clear-elevation web01 # Clear for specific host
/secret clear-elevation --all # Clear all elevation passwords
Secret References¶
For commands requiring passwords, use secret references:
Secret references (@name) are: - Stored in system keyring - Resolved at execution time - Never appear in logs
Elevation Configuration¶
Per-host elevation settings are cached in host metadata:
Troubleshooting¶
"Permission denied" persists¶
Solutions: - Check user has sudo/doas access on target - Run /ssh elevation detect <host> to check capabilities - Verify /etc/sudoers configuration - Try explicit elevation: run as root
Wrong password loops¶
Merlya prevents infinite loops by tracking failed methods:
# Check which methods have failed
/ssh elevation status <host>
# Reset to try again with correct password
/ssh elevation reset <host>
User not in sudoers¶
If /ssh elevation detect shows "user NOT in sudoers": - Merlya will automatically fall back to su - Or add the user to sudoers on the remote host
Password prompt loops¶
Solutions: - Clear cached password: /secret delete elevation:hostname:password - Reset elevation: /ssh elevation reset <host> - Check password is correct - Verify sudo doesn't require TTY (requiretty in sudoers)
Security Best Practices¶
- Use SSH keys instead of passwords
- Use SSH agent to avoid passphrase prompts
- Use jump hosts for internal servers
- Limit pool timeout for sensitive environments
- Audit SSH keys regularly with
/scan --security - Use NOPASSWD sudo for automation accounts
- Never embed passwords in commands - use
@secretreferences