%20%E3%81%AE%E6%84%8F%E5%91%B3.png)
背景:信頼できないマシンで暗号化ストレージを使用する方法を調べています。現在の設定では、sshfsを使用してLUKSで暗号化されたイメージにアクセスしています。リモートマシンは暗号化解除される地元でそして ext3 としてマウントします。(sshfs のみを使用すると、リモート マシンにアクセスした人が私のデータを見ることができます。) 私の設定例は次のとおりです。
# On the local machine:
sshfs remote:/home/crypt /home/crypt
cryptsetup luksOpen /home/crypt/container.img container
mount /dev/mapper/container /home/crypt-open
# Place cleartext files in /home/crypt-open,
# then reverse the above steps to unmount.
これをネットワーク障害に対して耐性のあるものにしたいです。そのためには、この設定でキャッシュ/バッファリングがどうなるかを理解したいと思います。次の 2 つのコマンドを検討してください。
dd if=/dev/random of=/home/crypt-open/test.dat bs=1000000 count=100
dd if=/dev/random of=/home/crypt-open/test.dat bs=1000000 count=100 conv=fsync
最初のコマンドは非常に速く戻り、ネットワーク トラフィックから、コマンドが戻った後もデータがまだ送信中であることがわかります。2 番目のコマンドは、データの転送が完了するまで待機しているようです。
具体的な質問:fsync()
この設定ではどのような保証がなされるのでしょうか?fsync()
が戻ったとき、これらのレイヤーに沿ってどの程度までデータが同期されることが保証されるのでしょうか? また、リモート マシンのハード ドライブに至るまで確実に同期されるようにするにはどうすればよいでしょうか?
--- /home/crypt-open on the local machine
|
| (ext3 fs)
|
--- /dev/mapper/container on the local machine
|
| (LUKS)
|
--- /home/crypt/container.img on the local machine
|
| (sshfs)
|
--- /home/crypt/container.img on the remote machine
|
| (ext3 fs)
|
--- hard drive on the remote machine
答え1
ここで最も弱いリンクはSSHFSコードだと思います。残りの部分はカーネルにあり、かなり頻繁に使用されているので、おそらく問題ありません。私はこれまでFUSEコードを実際に見たことがないので、私が見逃している何か他のことが起こっている可能性がありますが、SSHFS ソースコードSSHFS の の実装では、fsync()
多くの処理は実行されず、flush()
IO ストリームを呼び出すだけです。
static int sshfs_fsync(const char *path, int isdatasync,
struct fuse_file_info *fi)
{
(void) isdatasync;
return sshfs_flush(path, fi);
}
では、関数が fsync を強制するリモート マシンに同期コマンドを一切送信していないことsshfs.c:2551
がわかります。フラグは「書き込みから戻る前にコマンドがサーバーに送信されるのを待つ」という意味であり、「書き込みごとにサーバーで fsync する」という意味ではないと思います。後者の意味は非常に奇妙だからです。したがって、fsync の測定は、リモート ディスクの速度ではなくネットワークの速度によってボトルネックになるため、遅くなります。sshfs_flush()
sshfs.sync_write
static int sshfs_flush(const char *path, struct fuse_file_info *fi)
{
int err;
struct sshfs_file *sf = get_sshfs_file(fi);
struct list_head write_reqs;
struct list_head *curr_list;
if (!sshfs_file_is_conn(sf))
return -EIO;
if (sshfs.sync_write)
return 0;
(void) path;
pthread_mutex_lock(&sshfs.lock);
if (!list_empty(&sf->write_reqs)) {
curr_list = sf->write_reqs.prev;
list_del(&sf->write_reqs);
list_init(&sf->write_reqs);
list_add(&write_reqs, curr_list);
while (!list_empty(&write_reqs))
pthread_cond_wait(&sf->write_finished, &sshfs.lock);
}
err = sf->write_error;
sf->write_error = 0;
pthread_mutex_unlock(&sshfs.lock);
return err;
}
リモートSFTP実装では書き込み時にfsyncが実行される可能性もあるが、実際にはそうではないと思う。古いSFTP標準の草案(これが私が見つけた最善の方法です) この動作を指定する方法があります:
7.9. attrib-bits and attrib-bits-valid
...
SSH_FILEXFER_ATTR_FLAGS_SYNC
When the file is modified, the changes are written synchronously
to the disk.
これは、これがデフォルトではないことを意味します(fsyncしない方が速いため)。その標準ドキュメントによると、リモートファイルでfsyncを要求する方法はないようです。しかし、OpenSSHはこれをSFTPの拡張機能としてサポートしているようです。
/* SSH2_FXP_EXTENDED submessages */
struct sftp_handler extended_handlers[] = {
...
{ "fsync", "[email protected]", 0, process_extended_fsync, 1 },
...
};
static void
process_extended_fsync(u_int32_t id)
{
int handle, fd, ret, status = SSH2_FX_OP_UNSUPPORTED;
handle = get_handle();
debug3("request %u: fsync (handle %u)", id, handle);
verbose("fsync \"%s\"", handle_to_name(handle));
if ((fd = handle_to_fd(handle)) < 0)
status = SSH2_FX_NO_SUCH_FILE;
else if (handle_is_ok(handle, HANDLE_FILE)) {
ret = fsync(fd);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status);
}
その拡張機能をクエリして SSHFS で fsync を適切にサポートするのは難しいとは思いません。これはかなり合理的なことのように思えます。そうは言っても、Linux のネットワーク ブロック デバイス サポートを使用する方がおそらく簡単だと思います。このサポートはこれらすべてを適切にサポートしていると思います (ただし、私は自分で使用したことがないので、ひどいことになるかもしれません)。