Comment rsyncier des fichiers entre deux télécommandes?

54

J'aimerais transférer des fichiers entre deux hôtes distants à l'aide d'un shell local, mais il semble que rsync ne supporte pas la synchronisation si deux télécommandes sont spécifiées comme suit:

$ rsync -vuar host1:/var/www host2:/var/www
The source and destination cannot both be remote.

Quelles autres solutions de contournement / commandes que je pourrais utiliser pour obtenir des résultats similaires?

Kenorb
la source
1
En fait, vous pouvez effectuer une synchronisation entre deux hôtes distants en utilisant sshfs sur un troisième hôte. Il suffit d'utiliser sshfs pour monter host1 et host2 sur l'hôte 3. Puis rsync entre 1 et 2.
William Legg
@WilliamLegg l'inconvénient d'utiliser sshfsest que rsyncles systèmes de fichiers source et de destination sont alors tous deux locaux, ce qui désactive son algorithme delta. À ce stade, vous pouvez presque aussi bien utiliser cp -p. Voir la réponse qui propose ceci et ses commentaires ultérieurs.
Roaima

Réponses:

50

Comme vous l'avez découvert, vous ne pouvez pas utiliser rsync avec une source et une destination distantes. En supposant que les deux serveurs ne peuvent pas se parler directement, il est possible d'utiliser ssh pour créer un tunnel via votre ordinateur local.

Au lieu de

rsync -vuar host1:/var/www host2:/var/www

tu peux utiliser ça

ssh -R localhost:50000:host2:22 host1 'rsync -e "ssh -p 50000" -vuar /var/www localhost:/var/www'

Si vous le souhaitez, l' -Roption configure un canal inversé du port 50000 sur l'hôte1 qui mappe (via votre ordinateur local) sur le port 22 de l'hôte2. Il n'y a pas de connexion directe entre hôte1 et hôte2.

Roaima
la source
1
J'aime la solution de @ roaima, mais je ne pouvais pas la faire fonctionner pour moi pour diverses raisons. Finalement, j'ai sshfsmonté les deux répertoires distants localement, puis rsyncsur les deux répertoires montés localement.
aidan
Possible de voir un exemple d'utilisation d'une clé? Avoir du mal à comprendre comment utiliser -ipour spécifier les clés nécessaires aux commandes ssh.
Onassar
@onassar ajoute le -i key...paramètre à l'intérieur des guillemets après la sshcommande. Si cela ne vous aide pas, n'hésitez pas à poser une nouvelle question, en vous référant au contexte
roaima
1
la connexion inverse ne lit pas le fichier ~ / .ssh / config du côté local - vous devez utiliser quelque chose qui peut être résolu comme s'il n'y avait pas de fichier de configuration SSH
Florenz Kley
1
"En supposant que les deux serveurs ne puissent pas se parler directement". Cette solution fonctionne autour d'un problème de pare-feu ou de NAT empêchant une connexion SSH directe . Toutefois, il ne traite pas le cas où, pour des raisons de sécurité, l'utilisateur source (sur l'hôte 1) ne dispose d'aucune clé ou information d'identification ou d'autorisations d'écriture insuffisantes sur la destination. Pour cela, consultez la solution de Kevin Cox ou utilisez une connexion indirecte à l'aide d'un script ou scp -3.
Cedric Knight
22

Vous n'avez pas expliqué pourquoi vous ne vouliez pas vous connecter à un hôte, puis en copier un sur l'autre. Je vais donc partager l'une de mes raisons et solutions.

Je ne pouvais pas me connecter à une machine puis à rsync à l'autre parce qu'aucun des deux hôtes ne disposait d'une clé SSH pouvant se connecter à l'autre. J'ai résolu ce problème en utilisant le transfert d'agent SSH pour permettre au premier hôte d'utiliser ma clé SSH lorsque j'étais connecté.

AVERTISSEMENT: le transfert SSH permet à l'hôte d'utiliser votre clé SSH pour la durée de votre connexion. Bien qu'ils ne puissent pas copier votre clé, ils peuvent se connecter à d'autres ordinateurs avec cette clé. Assurez-vous de bien comprendre les risques et n'utilisez pas le transfert d'agent pour des machines auxquelles vous ne faites pas confiance.

La commande suivante utilisera le transfert d’agent SSH pour ouvrir une connexion directe de host1à host2. Cela présente l'avantage que la machine exécutant la commande ne gêne pas le transfert.

ssh -A host1 rsync -vuar /var/www host2:/var/www
Kevin Cox
la source
3
+1 pour expliquer un cas d'utilisation valide (où l'utilisateur distant sur hôte1 n'a pas de permission sur le serveur de destination); pour la mise en garde importante en matière de sécurité (utilisez le transfert de port -Dplutôt que -Ade contourner le réseau plutôt que les restrictions de clé); pour expliquer l'avantage; pour la commande étant courte; et cela fonctionne réellement. Notez que vous devrez peut-être spécifier username@host1s'il est différent du nom d'utilisateur local. De plus, rsync vérifie la clé de l'hôte lors de la connexion à l'hôte2. La clé de l'hôte1 doit donc déjà se trouver dans ~ / .ssh / known_hosts sur l'hôte2, sinon la commande échouera.
Cedric Knight
Réponse phénoménale, cela m'a aidé à orchestrer dans TeamCity certaines choses que je ne pouvais pas faire auparavant (NB, pour les autres utilisateurs de TeamCity, vous devez ajouter la "Fonction de construction" appelée "Agent SSH" à votre configuration de construction avant de l'utiliser ssh -A, voir confluence. jetbrains.com/display/TCD10/SSH+Agent ).
John Zwinck
12

J'aime la réponse de Roaima, mais les chemins sont les mêmes dans les deux exemples, obscurcissant lequel est lequel. Nous avons établi que ce qui suit ne fonctionne pas:

rsync -vuar host1:/host1/path host2:/host2/path

Mais ceci fait (j'ai omis la bind_address explicite de localhost de l' -Roption puisque c'est la valeur par défaut):

ssh -R 50000:host2:22 host1 'rsync -e "ssh -p 50000" -vuar /host1/path localhost:/host2/path'

Notez que vous devez configurer correctement les clés ssh entre les deux hôtes distants, avec la clé privée sur hôte1 et la clé publique sur hôte2.

Pour déboguer la connexion, divisez cela en deux parties et ajoutez le statut détaillé:

localhost$ ssh -v -R 50000:host2:22 host1

Si cela fonctionne, vous aurez un shell sur host1. Maintenant, essayez la commande rsync de host1. Je vous recommande de le faire dans une fenêtre différente pour éviter que les informations ssh détaillées ne soient mélangées avec les informations d'état rsync:

host1$ rsync -e "ssh -p 50000" -vuar /host1/path localhost:/host2/path
Jaybrau
la source
Dans mon exemple, les chemins sont source, destination. Le rsyncest lancé sur hôte1 avec la cible sur hôte2. (Vous auriez pu demander des éclaircissements dans un commentaire.)
roaima
1
J'aurais commenté, mais vous ne pouvez pas commenter le post de quelqu'un d'autre sans une réputation de 50+.
Jaybrau
7

Reformatage de la réponse de roaima dans la syntaxe de script bash (et ajout de caractères de continuation de ligne '\' pour plus de clarté) J'ai choisi le port 22000 au hasard ...

SOURCE_USER=user1
SOURCE_HOST=hostname1
SOURCE_PATH=path1

TARGET_USER=user2
TARGET_HOST=host2
TARGET_PATH=path2

ssh -l $TARGET_USER -A -R localhost:22000:$TARGET_HOST:22 \
$SOURCE_USER@$SOURCE_HOST "rsync -e 'ssh -p 22000' -vuar $SOURCE_PATH \
$TARGET_USER@localhost:$TARGET_PATH"
David I.
la source
1
il me semble que tout ce que vous avez fait est de remplacer ses noms d'hôtes arbitraires par des variables?
Jeff Schaller
3
Oui je l'ai fait. Il ajoute une clarté pour moi sur ce qui est la machine source, qui est la cible, et où vont les chemins source et cible. Il m'a fallu un certain temps pour régler tout ça et ce n'était pas évident avec les noms d'hôtes simples.
David I.
La prochaine fois, n'hésitez pas à améliorer la réponse de quelqu'un d'autre directement en la modifiant.
roaima
Cette réponse a été la solution pour moi car elle combine le transfert ssh-agent (-A) avec le tunnel inverse (-R).
camelthemammel
3

La manière idéale serait d'exécuter le rsyncsur l'un de ces serveurs. Mais si vous ne voulez pas exécuter un script sur le serveur distant. Vous pouvez exécuter un script sur votre système local, effectuer une opération ssh et y exécuter la commande rsync.

ssh user@$host1 <<ENDSSH >> /tmp/rsync.out 2>&1 rsync -vuar /var/www host2:/var/www ENDSSH

En outre, comme vous le savez peut-être, rysnc effectue une synchronisation à sens unique. Si vous souhaitez une synchronisation bidirectionnelle, vous pouvez consulter osync ( https://github.com/deajan/osync ). Je l'utilise et l'ai trouvé utile.

Rahul
la source
0

Juste comme une information supplémentaire:

Si vous utilisez un hôte de saut pour connecter les deux autres machines mais qu'elles ne peuvent pas se joindre directement, vous pouvez utiliser sshfs comme support entre ces deux machines comme ceci (sur l'hôte de saut):

$ mkdir ~/sourcepath ~/destpath
$ sshfs sourcehost:/target/dir ~/sourcepath
$ sshfs desthost:/target/dir ~/destpath
$ rsync -vua ~/sourcepath ~/desthpath

SSHFS fournit les deux chemins sur l'hôte de saut et rsync gère la synchronisation des fichiers comme toujours (à la différence que cela se fait virtuellement localement).

Roger Lehmann
la source
1
Notez que la performance sera terrible. En effet, pour détecter les modifications, rsync lira le fichier à partir du serveur source, en transférant l’ensemble sur le réseau. Cela dit, si vous ne pouvez pas effectuer de transfert direct, vous devrez le manger si vous utilisez rsync. De même, ne montez pas la destination, cela est inutile et causera à rsync de modifier certaines valeurs par défaut car il pense parler à un disque local.
Kevin Cox
0

Vous pouvez exécuter un serveur rsyncd sur l’un des ordinateurs.

C’est cette approche que je suis en train de prendre, car je ne souhaite pas utiliser ssh pour permettre à la 'source' (en termes rsync) d’accéder à la 'destination' en tant que root sans mot de passe (comme requis pour utiliser le tunneling SSH avec rsync dans un script)

Dans mon cas, j’ai simplement configuré un serveur rsyncd sur l’ordinateur de destination avec un seul utilisateur autorisé à partir du PC source et utilisé par rsync du côté source.

Fonctionne très bien.

RustyCar
la source
0

Essayez d'utiliser ceci. Ça marche pour moi.

ssh src_user@src_host 'rsync -av /src/dir/location/ dest_user@dest_host:/dest/dir/loc/'
Nikhil Pandey
la source
0

Un script facile à utiliser

Au fil des ans, je l'ai fait plusieurs fois avec plus ou moins les mêmes astuces que dans toutes les autres réponses. Cependant, comme il est très facile de se tromper dans les détails et de passer beaucoup de temps à résoudre le problème, le script ci-dessous a été développé:

  1. Facilite la spécification de tous les détails (source, destination, options)
  2. Teste progressivement chaque étape et donne un retour d'informations en cas de problème, afin que vous sachiez quoi réparer.
  3. Fonctionne dans les cas où ssh -Ales données d'authentification ne parviennent pas à se propager (je ne sais pas pourquoi cela se produit parfois car la solution de contournement était plus facile que de rechercher la cause du problème)
  4. Enfin fait le travail.

Comment utiliser le script

  1. Assurez-vous que vous pouvez ssh sur les deux hôtes à partir de localhost sans saisir de mot de passe.
  2. Définir les variables dans les premières lignes du script
  3. L'exécuter.

Comment ça fonctionne

Comme je l'ai dit, il utilise les mêmes astuces que dans toutes les autres réponses:

  • l' -Roption de ssh de ssh de localhost à host1 tout en configurant un transfert de port qui permet ensuite à host1 de se connecter via localhost à host2 ( -R localhost:$FREE_PORT:$TARGET_ADDR_PORT)
  • -AOption de ssh permettant d'authentifier facilement le second ssh chanel

Mon c'est compliqué! Y a-t-il un moyen plus facile?

Lorsque vous copiez la totalité ou la plupart des octets de la source à la destination, son utilisation est beaucoup plus simple tar:

ssh $SOURCE_HOST "tar czf - $SOURCE_PATH" \
    | ssh $TARGET_HOST "tar xzf - -C $TARGET_PATH/"

Le scénario

#!/bin/bash
#-------------------SET EVERYTHING BELOW-------------------
# whatever you type after ssh to connect to SOURCE/TARGE host 
# (e.g. 1.2.3.4:22, user@host:22000, ssh_config_alias, etc)
# So if you use "ssh foo" to connect to SOURCE then 
# you must set SOURCE_HOST=foo
SOURCE_HOST=host1 
TARGET_HOST=host2 
# The IP address or hostname and ssh port of TARGET AS SEEN FROM LOCALHOST
# So if ssh -p 5678 [email protected] will connect you to TARGET then
# you must set TARGET_ADDR_PORT=1.2.3.4:5678 and
# you must set TARGET_USER=someuser
TARGET_ADDR_PORT=1.2.3.4:5678
TARGET_USER=someuser

SOURCE_PATH=/mnt/foo  # Path to rsync FROM
TARGET_PATH=/mnt/bar  # Path to rsync TO

RSYNC_OPTS="-av --bwlimit=14M --progress" # rsync options
FREE_PORT=54321 # just a free TCP port on localhost
#---------------------------------------------------------

echo -n "Test: ssh to $TARGET_HOST: "
ssh $TARGET_HOST echo PASSED| grep PASSED || exit 2

echo -n "Test: ssh to $SOURCE_HOST: "
ssh $SOURCE_HOST echo PASSED| grep PASSED || exit 3

echo -n "Verifying path in $SOURCE_HOST "
ssh $SOURCE_HOST stat $SOURCE_PATH | grep "File:" || exit 5

echo -n "Verifying path in $TARGET_HOST "
ssh $TARGET_HOST stat $TARGET_PATH | grep "File:" || exit 5

echo "configuring ssh from $SOURCE_HOST to $TARGET_HOST via locahost"
ssh $SOURCE_HOST "echo \"Host tmpsshrs; ControlMaster auto; ControlPath /tmp/%u_%r@%h:%p; hostname localhost; port $FREE_PORT; user $TARGET_USER\" | tr ';' '\n'  > /tmp/tmpsshrs"

# The ssh options that will setup the tunnel
TUNNEL="-R localhost:$FREE_PORT:$TARGET_ADDR_PORT"

echo 
echo -n "Test: ssh to $SOURCE_HOST then to $TARGET_HOST: "
if ! ssh -A $TUNNEL $SOURCE_HOST "ssh -A -F /tmp/tmpsshrs tmpsshrs echo PASSED" | grep PASSED ; then
        echo
        echo "Direct authentication failed, will use plan #B:"
        echo "Please open another terminal, execute the following command"
        echo "and leave the session running until rsync finishes"
        echo "(if you're asked for password use the one for $TARGET_USER@$TARGET_HOST)"
        echo "   ssh -t -A $TUNNEL $SOURCE_HOST ssh -F /tmp/tmpsshrs tmpsshrs"
        read -p "Press [Enter] when done..."
fi

echo "Starting rsync"
ssh -A $TUNNEL $SOURCE_HOST "rsync -e 'ssh -F /tmp/tmpsshrs' $RSYNC_OPTS $SOURCE_PATH tmpsshrs:$TARGET_PATH"

echo
echo "Cleaning up"
ssh $SOURCE_HOST "rm /tmp/tmpsshrs"
Ndemou
la source
tarC'est génial quand vous avez un seul transfert (non incrémental) et que votre transfert se termine en un seul passage. D'autre part, rsyncavec les poignées de transfert, les redémarrages et les transferts incrémentiels.
Roaima
1
Bien sûr, @roaima - je ne considère pas l'équivalent en goudron. J'ai laissé cette référence pour le jour où je lirai ceci afin de résoudre un problème où rsync ne sera pas nécessaire à 100%.
Ndemou
-1

Il est possible d'utiliser tarvia sshpour transférer les fichiers:

ssh -n user1@host1 'tar jcf - -C /var/www .' | ssh user2@host2 'tar jxvf - -C /var/www'

Modifiez le jparamètre (for tar) à zdeux endroits si vous souhaitez compresser l'archive avec gzip, au lieu de bzip2. Généralement, bzip2la compression est plus élevée que gzip, mais elle est plus lente, changez-la en fonction de vos besoins (voir: bzip2 vs gzip ).

Connexe: Comment copier entre deux hôtes distants à l'aide de tar transféré dans SSH à partir d'un serveur distant derrière un pare-feu?


Alternativement (pour sécuriser la bande passante, en raison de la compression transparente), il est possible sshfsde monter un système de fichiers distant en local et de l'utiliser rsyncnormalement, par exemple

$ sshfs user1@host1:/var/www /mnt
$ rsync -vuar /mnt user2@host2:/var/www
Kenorb
la source
1
Monter le système de fichiers localement ne sauvegardera aucune bande passante entre la machine source et la machine locale. Il va cependant économiser la bande passante entre le local et la destination (comme il est pas monté localement). Utiliser rsync de cette manière n'a de sens que si vous essayez d'économiser de la bande passante sur la destination.
Kevin Cox