Crontab执行scp失败:MySQL备份跨服务器传输权限问题排查
I’ve run into this exact issue before—scripts that work perfectly when you run them manually but fail silently (or throw permission errors) via crontab. The root cause almost always boils down to environment differences or SSH authentication context that crontab doesn’t inherit from your interactive shell. Let’s break down the fixes step by step:
1. Fix SSH Agent Access for Crontab
When you run your script manually, your shell is connected to the ssh-agent session you set up earlier (with ssh-add). But crontab runs in a separate, stripped-down session that can’t access that agent by default. Here are two ways to fix this:
Option A: Use a Passwordless Backup Key (Simplest Approach)
For backup tasks, a dedicated passwordless key is often acceptable (just make sure it’s restricted to only scp/ssh access on server2):
- Generate the key pair:
ssh-keygen -t ed25519 -f ~/.ssh/backup_key(press Enter twice when prompted for a passphrase—leave it empty) - Copy the public key to server2 to enable passwordless login:
ssh-copy-id -i ~/.ssh/backup_key user@server2 - Update your script’s scp command to use this key explicitly:
scp -i ~/.ssh/backup_key /full/path/to/myfile user@server2:/full/destination/path
Option B: Pass Your SSH Agent Context to Crontab
If you want to keep using a passphrase-protected key, you need to tell crontab where to find your running agent:
- First, when you’re logged in manually, run
echo $SSH_AUTH_SOCKto get the agent’s socket path (it’ll look something like/tmp/ssh-abc123/agent.456). - Add this variable to your crontab entry. For example, if your backup runs daily at 2 AM:
Pro tip: The socket path might change on reboot, so a more reliable method is to save the socket info to a file when your agent starts. Add this to your0 2 * * * SSH_AUTH_SOCK=/tmp/ssh-abc123/agent.456 /usr/Backup/myshell.sh~/.bashrcor~/.profile:
Then, at the top of yourif [ -z "$SSH_AUTH_SOCK" ]; then eval $(ssh-agent -s) ssh-add ~/.ssh/your_private_key # Enter your passphrase once when you log in echo $SSH_AUTH_SOCK > ~/.ssh/agent_socket_path fimyshell.shscript, add:export SSH_AUTH_SOCK=$(cat ~/.ssh/agent_socket_path)
2. Use Absolute Paths for Everything
Crontab runs with a default working directory (usually /root or your user’s home folder), so relative paths in your script will break. Double-check:
- The path to your MySQL backup file is absolute (e.g.,
/var/lib/mysql/backups/myfile.sqlinstead of justmyfile) - The destination path on server2 is absolute (e.g.,
/backup/storage/instead offile destination—I noticed a typo in your original command, make sure that’s fixed too!) - You’re calling the script with its absolute path (which you already do:
/usr/Backup/myshell.sh—good job!)
3. Verify Permissions
Permissions are often the culprit here:
- Ensure your script is executable:
chmod +x /usr/Backup/myshell.sh - Make sure your private key has strict permissions (SSH rejects keys that are too open):
chmod 600 ~/.ssh/your_private_keyandchmod 700 ~/.ssh - If you’re running crontab as root, confirm root has access to the private key (or use the correct user’s crontab with
crontab -u yourusername -e)
4. Load Environment Variables in the Script
Crontab doesn’t load your shell’s profile files (.bashrc, .profile), so variables like PATH might be missing. Add this to the top of your script to load your interactive environment:
source ~/.bashrc # Or for login shells, source ~/.profile instead
Alternatively, explicitly set the PATH to include the directories you need:
export PATH="/usr/bin:/bin:/usr/sbin:/sbin"
5. Debug with Verbose Logging
If you’re still stuck, add verbose output to your scp command to see exactly where the authentication fails. Modify the scp line in your script:
scp -v /full/path/to/myfile user@server2:/full/destination/path >> /var/log/backup_scp.log 2>&1
After the crontab runs, check /var/log/backup_scp.log—it will show every step of the SSH handshake, so you can see exactly why permission is being denied.
内容的提问来源于stack exchange,提问作者user1884372

