When attempting to establish an SSH tunnel between virtual machines for MySQL access, many developers encounter the frustrating "Permission denied (publickey)" error. This typically occurs during the ssh-copy-id
process, where the system seems to reject valid credentials.
The reported setup involves:
Host A: Physical Linux machine
VM B: MySQL server (target)
VM C: Client machine (source)
The goal is passwordless authentication from C → A → B for persistent MySQL tunneling.
First, verify your key pair integrity:
# On client machine (C)
ssh-keygen -l -f ~/.ssh/id_rsa
ssh-keygen -l -f ~/.ssh/id_rsa.pub
# Should show matching fingerprints
On Host A, ensure these settings in /etc/ssh/sshd_config
:
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no # Temporarily set to yes for debugging
PermitEmptyPasswords no
After changes, restart SSH:
sudo systemctl restart sshd # For systemd systems
# OR
sudo service ssh restart # For older systems
Use verbose SSH output to identify failures:
ssh -vvv -i ~/.ssh/id_rsa user@hostA -p port
# Look for these key messages:
# - "Offering public key"
# - "Server accepts key"
# - "Authentication succeeded"
When ssh-copy-id
fails, manually append the public key:
# On client (C)
cat ~/.ssh/id_rsa.pub | ssh user@hostA "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Critical permission requirements:
Client (C):
~/.ssh: 700
~/.ssh/id_rsa: 600
~/.ssh/id_rsa.pub: 644
Server (A):
~/.ssh: 700
~/.ssh/authorized_keys: 600
For automatic reconnection, create a systemd service:
[Unit]
Description=MySQL SSH Tunnel
After=network.target
[Service]
User=youruser
ExecStart=/usr/bin/ssh -N -L 3306:localhost:3306 user@hostB -i /home/user/.ssh/id_rsa
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
- Conflicting keys in multiple
authorized_keys
files - Mismatched key algorithms (RSA vs ED25519)
- Firewall blocking SSH port
- SELinux/AppArmor restrictions
Check key usage in auth logs:
# On server (A)
sudo tail -f /var/log/auth.log | grep -i "publickey"
# Look for "Accepted publickey" or "Failed publickey" messages
When attempting to set up passwordless authentication between machine C and machine A (hosting VM B with MySQL), the ssh-copy-id
operation fails with:
ssh-copy-id -i id_rsa.pub user@host -p port
Enter passphrase for key '/home/user/.ssh/id_rsa':
Permission denied (publickey).
First, verify your key pair integrity:
# On machine C (source)
ssh-keygen -lf ~/.ssh/id_rsa.pub
ssh-keygen -lf ~/.ssh/id_rsa
# On machine A (destination)
cat ~/.ssh/authorized_keys | while read line; do
echo $line | ssh-keygen -lf -;
done
This helps identify if the public key exists in the destination's authorized_keys with matching fingerprint.
Run in verbose mode to pinpoint the failure:
ssh -vvv -i ~/.ssh/id_rsa user@host -p port
Critical things to check in the output:
- Which identity file is being offered
- Server's accepted authentication methods
- Exact point where authentication fails
On machine A (destination), verify these settings in /etc/ssh/sshd_config
:
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no # Temporarily set to yes for debugging
ChallengeResponseAuthentication no
Reload SSH daemon after changes:
sudo systemctl restart sshd
# Or for older systems:
sudo /etc/init.d/ssh restart
If ssh-copy-id
fails, manually append the key:
# On machine C
cat ~/.ssh/id_rsa.pub | ssh user@host -p port "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Then fix permissions:
ssh user@host -p port "chmod 700 ~/.ssh; chmod 600 ~/.ssh/authorized_keys"
Ensure your private key is loaded in the agent:
eval ssh-agent
ssh-add ~/.ssh/id_rsa # Enter passphrase when prompted
ssh-add -l # Verify loaded keys
Symptom | Possible Cause | Solution |
---|---|---|
Permission denied immediately | Server not accepting publickey auth | Check sshd_config PubkeyAuthentication |
Passphrase prompt then denial | Key mismatch or wrong passphrase | Verify key fingerprints match |
Works with password auth only | authorized_keys permissions wrong | chmod 600 authorized_keys |
Once authentication works, create persistent tunnel:
autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" \
-N -L 3306:localhost:3306 user@host -p port -i ~/.ssh/id_rsa
For systemd service (create /etc/systemd/system/mysql-tunnel.service
):
[Unit]
Description=MySQL SSH Tunnel
After=network.target
[Service]
User=youruser
ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -N -L 3306:localhost:3306 user@host -p port -i /home/youruser/.ssh/id_rsa
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target