How to Securely Pass Rsync Credentials in Bash Scripts for Automated Sync Jobs


4 views

When automating rsync transfers via cron jobs, we often encounter authentication hurdles. The key issue lies in properly passing credentials to the rsync daemon without interactive prompts. Let's examine why your current approach might fail.

#!/bin/bash
USER=name
RSYNC_PASSWORD=pass
DEST="myhost::mymodule"

/usr/bin/rsync -rltvvv . $DEST

The main problem in your script is that environment variables need to be exported to be visible to child processes (like rsync). A simple assignment like USER=name only sets the variable for the current shell session.

Here's the corrected version:

#!/bin/bash
export RSYNC_PASSWORD="your_password"
export USER="your_username"  # or LOGNAME
DEST="myhost::mymodule"

/usr/bin/rsync -rltvvv --no-blocking-io . "$DEST"

While exporting passwords works, it's not the most secure method. Here are three better approaches:

1. Using SSH Keys Instead

For SSH-based transfers (not daemon mode):

#!/bin/bash
/usr/bin/rsync -rltvv -e "ssh -i /path/to/private_key" . user@host:/path/

2. Rsync Secret File Method

Create a secrets file (e.g., /etc/rsync.secrets):

username:password

Then use it in your script:

#!/bin/bash
/usr/bin/rsync -rltvv --password-file=/etc/rsync.secrets . user@host::module

3. Temporary Environment with env

For one-time commands without permanent export:

#!/bin/bash
env RSYNC_PASSWORD="pass" /usr/bin/rsync -rltvv . user@host::module

When encountering "@ERROR: auth failed on module", check:

  • File permissions on /etc/rsyncd.secrets (should be 600)
  • Correct username/password in rsyncd.conf
  • Hosts allow/deny rules in rsyncd.conf
  • Time synchronization between servers

Here's a robust implementation with error handling:

#!/bin/bash

# Configuration
SYNC_USER="syncuser"
SYNC_PASS="correct-horse-battery-staple"
REMOTE_HOST="backup.example.com"
MODULE="data_backup"
LOG_FILE="/var/log/rsync_backup.log"

# Temporary credential handling
export RSYNC_PASSWORD="$SYNC_PASS"
trap 'unset RSYNC_PASSWORD' EXIT

# Execute sync with error checking
if ! /usr/bin/rsync -az --no-blocking-io --delete \
    --log-file="$LOG_FILE" \
    --timeout=300 \
    --contimeout=30 \
    . "${SYNC_USER}@${REMOTE_HOST}::${MODULE}"; then
    echo "[$(date)] Rsync failed" >> "$LOG_FILE"
    exit 1
fi

When working with rsync daemon authentication, there are several key points to understand about the authentication mechanism:

  • Rsync daemon uses a separate authentication system from SSH
  • Module access is controlled through the secrets file defined in rsyncd.conf
  • Environment variables must be set before the rsync command executes

The issue in your script stems from how environment variables are being set. Here's the proper implementation:

#!/bin/bash
export RSYNC_PASSWORD="your_secure_password"
DEST="myuser@myhost::mymodule"

/usr/bin/rsync -avz --progress . "$DEST"

Key improvements:

  • Using export makes the variable available to child processes
  • The username is included in the destination string
  • Simplified flags while maintaining verbosity with --progress

While the above works, storing passwords in scripts is risky. Here are better approaches:

Option 1: Using SSH Keys Instead

#!/bin/bash
DEST="myuser@myhost:/path/to/destination"

/usr/bin/rsync -avz -e "ssh -i /path/to/private_key" . "$DEST"

Option 2: Using a Password File with Restricted Permissions

#!/bin/bash
RSYNC_PASSWORD_FILE="/etc/rsync/password"
DEST="myuser@myhost::mymodule"

/usr/bin/rsync -avz --password-file="$RSYNC_PASSWORD_FILE" . "$DEST"

Then create the password file:

sudo mkdir -p /etc/rsync
echo "your_secure_password" | sudo tee /etc/rsync/password
sudo chmod 600 /etc/rsync/password

If you're still encountering problems, check these aspects:

  • Verify rsyncd.conf has secrets file = /path/to/secrets properly configured
  • Ensure the secrets file has correct permissions (chmod 600)
  • Check that the username in your script matches an entry in the secrets file
  • Test connectivity first with telnet myhost 873

Here's a robust implementation suitable for production environments:

#!/bin/bash
# Set variables
RSYNC_USER="myuser"
RSYNC_HOST="myhost"
RSYNC_MODULE="mymodule"
LOG_FILE="/var/log/rsync_$(date +%Y%m%d).log"

# Execute rsync with error handling
if /usr/bin/rsync -avz --delete --progress \
    --password-file=/etc/rsync/password \
    . "${RSYNC_USER}@${RSYNC_HOST}::${RSYNC_MODULE}" >> "$LOG_FILE" 2>&1
then
    echo "Rsync completed successfully at $(date)" >> "$LOG_FILE"
else
    echo "Rsync failed with exit code $? at $(date)" >> "$LOG_FILE"
    exit 1
fi

This script includes logging, error handling, and follows security best practices while maintaining the original functionality.