ssh

SKILL.md

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 no on production servers
  • Use fail2ban to block brute force attempts
  • Keep keys encrypted with passphrases
  • Use ssh-agent to avoid typing passphrase repeatedly
  • Restrict key usage with command= in authorized_keys
  • Always test new SSH config before restarting (keep session open)
  • Use AllowUsers or AllowGroups to 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
Weekly Installs
1
First Seen
5 days ago
Installed on
mcpjam1
claude-code1
junie1
windsurf1
zencoder1
crush1