ssh
SSH Skill
Comprehensive SSH for secure remote access, file transfers, tunneling, and server hardening.
Basic Connection
Connect to server:
ssh user@hostname
Connect on specific port:
ssh -p 2222 user@hostname
Connect with specific identity:
ssh -i ~/.ssh/my_key user@hostname
SSH Config
Config file location: ~/.ssh/config
Create or edit for easier connections:
mkdir -p ~/.ssh
chmod 700 ~/.ssh
cat > ~/.ssh/config << 'EOF'
Host myserver
HostName 192.168.1.100
User deploy
Port 22
IdentityFile ~/.ssh/myserver_key
ForwardAgent yes
ServerAliveInterval 60
EOF
chmod 600 ~/.ssh/config
Then connect with just:
ssh myserver
Key Management
Generate new key (Ed25519, recommended):
ssh-keygen -t ed25519 -C "your_email@example.com"
Generate RSA key (legacy compatibility):
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
Copy public key to server:
ssh-copy-id user@host
Copy specific key:
ssh-copy-id -i ~/.ssh/mykey.pub user@host
Manual key copy:
# Display public key
cat ~/.ssh/id_ed25519.pub
# On server, as the user
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys # Paste public key
chmod 600 ~/.ssh/authorized_keys
SSH Agent
Start agent:
eval "$(ssh-agent -s)"
Add key to agent:
ssh-add ~/.ssh/id_ed25519
Add with macOS keychain:
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
List loaded keys:
ssh-add -l
Running Remote Commands
Execute single command:
ssh user@host "ls -la /var/log"
Execute multiple commands:
ssh user@host "cd /app && git pull && pm2 restart all"
Run with pseudo-terminal (for interactive):
ssh -t user@host "htop"
File Transfer
SCP
Copy file to remote:
scp local.txt user@host:/remote/path/
Copy file from remote:
scp user@host:/remote/file.txt ./local/
Copy directory recursively:
scp -r ./local_dir user@host:/remote/path/
rsync (preferred)
Sync directory to remote:
rsync -avz ./local/ user@host:/remote/path/
Sync from remote:
rsync -avz user@host:/remote/path/ ./local/
With progress and compression:
rsync -avzP ./local/ user@host:/remote/path/
Dry run first:
rsync -avzn ./local/ user@host:/remote/path/
Port Forwarding (Tunnels)
Local Forward
Access remote service locally:
ssh -L 8080:localhost:80 user@host
# Now localhost:8080 connects to host's port 80
Forward to another host:
ssh -L 5432:db-server:5432 user@jumphost
# Access db-server:5432 via localhost:5432
Remote Forward
Expose local service to remote:
ssh -R 9000:localhost:3000 user@host
# Remote's port 9000 connects to your local 3000
Dynamic SOCKS Proxy
ssh -D 1080 user@host
# Use localhost:1080 as SOCKS5 proxy for network pivoting
Jump Hosts / Bastion
Connect through jump host:
ssh -J jumphost user@internal-server
Multiple jumps:
ssh -J jump1,jump2 user@internal-server
In config file:
Host internal
HostName 10.0.0.50
User deploy
ProxyJump bastion
Multiplexing (Connection Sharing)
In ~/.ssh/config:
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
Create socket directory:
mkdir -p ~/.ssh/sockets
Known Hosts
Remove old host key:
ssh-keygen -R hostname
Scan and add host key:
ssh-keyscan hostname >> ~/.ssh/known_hosts
Common SSH Options
| Option | Description |
|---|---|
-p PORT |
Connect to specific port |
-X |
Enable X11 forwarding |
-L local:remote:port |
Local port forwarding |
-R remote:local:port |
Remote port forwarding |
-D port |
Dynamic SOCKS proxy |
-N |
Don't execute remote command (for tunnels) |
-f |
Run in background |
-v |
Verbose mode (use -vv, -vvv for more) |
Debugging
Verbose output:
ssh -v user@host
Very verbose:
ssh -vv user@host
Maximum verbosity:
ssh -vvv user@host
Test SSH config syntax:
sudo sshd -t
Check SSH service status:
sudo systemctl status sshd
SSH Hardening (Server Side)
Create Non-Root User
# Create user
sudo adduser deployer
# Add to sudo group
sudo usermod -aG sudo deployer
# Test sudo access
su - deployer
sudo whoami # Should output: root
Hardened sshd_config
Edit /etc/ssh/sshd_config:
# Disable root login
PermitRootLogin no
# Disable password authentication
PasswordAuthentication no
# Disable empty passwords
PermitEmptyPasswords no
# Limit authentication attempts
MaxAuthTries 3
# Allow specific users
AllowUsers deployer
# Use only SSH protocol 2
Protocol 2
# Disable X11 forwarding (unless needed)
X11Forwarding no
# Set login grace time
LoginGraceTime 60
# Disable host-based authentication
HostbasedAuthentication no
Optional Advanced Settings
# Change default port (reduces automated scanner noise)
Port 2222
# Disable agent forwarding
AllowAgentForwarding no
# Disable TCP forwarding (if not needed)
AllowTcpForwarding no
# Set idle timeout
ClientAliveInterval 300
ClientAliveCountMax 2
Test and Restart
# Test configuration
sudo sshd -t
# Restart SSH service
sudo systemctl restart sshd
Security Best Practices
- Use Ed25519 keys (faster, more secure than RSA)
- Set
PasswordAuthentication noon production servers - Use
fail2banto block brute force attempts - Keep keys encrypted with passphrases
- Use
ssh-agentto avoid typing passphrase repeatedly - Restrict key usage with
command=in authorized_keys - Always test new SSH config before restarting (keep session open)
- Use
AllowUsersorAllowGroupsto limit access - Monitor authentication logs:
/var/log/auth.log - Rotate SSH keys periodically
File Permissions
# Private key (MUST be 600)
chmod 600 ~/.ssh/id_ed25519
# Public key (can be 644)
chmod 644 ~/.ssh/id_ed25519.pub
# SSH directory
chmod 700 ~/.ssh
# Authorized keys
chmod 600 ~/.ssh/authorized_keys
# SSH config
chmod 600 ~/.ssh/config
Troubleshooting
| Issue | Solution |
|---|---|
| Connection refused | Verify SSH running; check firewall; confirm port |
| Auth failed | Check key permissions (600); verify authorized_keys format |
| Host key changed | Remove old entry: ssh-keygen -R hostname |
| Tunnel not working | Check GatewayPorts in sshd_config; verify firewall |
| Permission denied | Check file permissions; verify user has access |