web-dev-qa-db-ja.com

名前付きパイプの転送にscp(1)、sftp(1)、またはrsync(1)を強制します

Btrfsサブボリュームの完全バックアップと増分バックアップをテープアーカイブサービスに転送するつもりです。このサービスは、FTPおよびSSHエンドポイントを公開します。 SSHエンドポイントで任意のコマンドを実行することが許可されている場合は、次の手順を実行して増分バックアップを実行します。

_btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME | compress | encrypt |
    ssh -p $PORT $USER@$ENDPOINT "cat > $SUBVOLUME.$YYYYMMDD.btrfs.bz2.gpg"
_

しかし、私はそれをすることを許可されていません:

_$ ssh -p $PORT $USER@$ENDPOINT
Last login: Mon Jan  3 01:23:45 2067 from 123.456.789.123

This account is restricted by rssh.
Allowed commands: scp sftp rsync 

If you believe this is in error, please contact your system administrator.

Connection to some.remote.endpoint closed.
_

そのため、代わりに私が考えたのは、転送にSCPプロトコルを使用することでした。しかし、私のscpバイナリは名前付きパイプの転送を拒否します。

_$ scp -P $PORT <(btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME | compress | encrypt) \
    $USER@$ENDPOINT:$SUBVOLUME.$YYYYMMDD.btrfs.bz2.gpg
/dev/fd/63: not a regular file
_

皮肉なことに、どうやら scpは一度正しいことをしていた 。名前付きパイプを転送することが正気で便利だと誰もが思っていたわけではないと思います。編集(2017-06-03):これは誤った観察でした。 Kensterが指摘しているように、SCPプロトコルは、サイズが不明なファイルの送信を許可していません。

UPDATE(2017-06-04):SFTPプロトコルを使用してデータを転送しようとしました。明らかに、FTPプロトコルは、ftplink )およびncftpputlink 、セクションの説明、最後の段落)バイナリ。私が試したSFTPクライアント(sftplftp)にはそのようなサポートは見つかりませんでした。これは、SFTPが(FTPとは異なり)不明なサイズのファイルの送信をサポートしていないことを示している可能性があります(私が思ったのは、SFTPはSSHを介して調整されたFTPだけではなく、別のプロトコルです)。

UPDATE(2017-06-05):SFTPプロトコルバージョン3インターネットドラフト( リンク )によると、各アプリケーション-レベルパケットは、ペイロードの前にペイロードの長さを指定する必要があります( リンク 、セクション3)。ただし、SFTPは、書き込まれたファイル( link 、セクション6.4)でのシークをサポートし、現在のファイルの終わりを超える書き込みを明示的にサポートします。したがって、クライアント側で小さなバッファーを使用して、サイズが不明なファイルを既知のサイズの小さなチャンクで送信できる場合は、次のようにします。

_#!/bin/bash
# <Exchange SSH_FXP_INIT requests.>
# <Send an SSH_FXP_OPEN request.>
CHUNK_SIZE=32768
OFFSET=0
IFS=''; while read -r -N $CHUNK_SIZE CHUNK; do
  ACTUAL_SIZE=`cat <<<"$CHUNK" | head -c -1 | wc -c`
  # <Send an SSH_FXP_WRITE request with payload $CHUNK of size
  # $ACTUAL_SIZE at offset $OFFSET.>
  OFFSET=$(($OFFSET+$ACTUAL_SIZE))
done < <(command)
# <Send an SSH_FXP_CLOSE request.>
_

ただし、シェルを介して手動で通信を行うのは非常に面倒です。この種の機能を公開するSFTPクライアントを探しています。

参考文献

4
Witiko

lftp 4.6.1以降はこれを実行できるはずです: https://github.com/lavv17/lftp/issues/104

残念ながら、リンクされた問題で提案されたコマンドは機能しませんが、わずかに変更されたコマンドは機能します。

lftp -p $port sftp://$user:$pass@$Host -e "put /dev/stdin -o $filename"

bug のため、SSHエージェントを使用する場合は、パスワードフィールドに何かを入力する必要があります。しかし、どんなランダムな文字列でもかまいません。

このコマンドは/dev/stdinから読み取ります。別の名前付きパイプを使用する必要がある場合は、それも機能するはずです。そうでない場合は、いつでもcat named-pipe | lftp ...できます。

lftpを100GBを超えるアップロードで問題なくテストしました。


rcloneはSFTPもサポートしており、1.38(次のバージョン)でリリースされる予定で、rcatコマンドを使用してパイプ入力からアップロードできるはずです。

3
Bob

SCPはあなたの目的にはあまり適していません。 SCPプロトコルは、ファイルとして保存するために、未知のサイズのバイトストリームをリモートシステムに送信することをサポートしていません。ファイルを送信するためのSCPプロトコルメッセージでは、最初にファイルのサイズを送信し、次にファイルを構成するバイトを送信する必要があります。パイプから読み取られたバイトのストリームでは、通常、パイプが生成するバイト数がわからないため、正しいサイズを含むSCPプロトコルメッセージを送信する方法はありません。

(私が見つけたSCPプロトコルの唯一のオンライン説明は ここここ です。「C」メッセージの説明に焦点を合わせてください。)

SFTPプロトコルはこの種のものに使用できます。私の知る限り、通常のsftpコマンドラインユーティリティは、パイプの読み取りとリモートファイルとしての保存をサポートしていません。ただし、最新のプログラミング言語(Perl、python、Ruby、C#、Java、Cなど)にはSSH/SFTPライブラリがあります。これらの言語のいずれかを使用する方法を知っている場合は、必要なことを実行するユーティリティを作成するのは簡単です。

シェルスクリプトで立ち往生している場合は、ファイルを転送するのに十分なSCPプロトコルをスプーフィングする可能性があります。次に例を示します。

#!/bin/bash
cmd='cat /etc/group'

size=$($cmd | wc -c)    
{
        echo C0644 $size some-file
        $cmd
        echo -n -e '\000'
} | ssh user@Host scp -v -p -t /some/directory

これにより、アクセス許可644のリモートシステムのsome-file/some/directoryが作成されます。ファイルの内容は、$cmdが標準出力に書き込むものになります。コマンドを2回実行していることに注意してください。リソースの消費と副作用は、それが意味するものです。また、コマンドmustは、毎回同じバイト数を出力する必要があります。

6
Kenster

Perlを記述できる場合は、 Net :: SFTP :: Foreign を使用してみてください。これにより、開いているファイル記述子からSFTP経由でファイルを転送できます。

#!/usr/bin/Perl
# untested!
use Net::SFTP::Foreign;
my $user = ...;
my $Host = ...;
my $remote_path = ...;
my $btrfs_cmd = 'btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME';
my $sftp = Net::SFTP::Foreign->new("$user\@$Host", ...);
open my($pipe), "$btrfs_cmd | compress | encrypt |"
    or die $!;
$sftp->put($fh, $remote_path);
2
salva