マルチテナントSFTPサーバを AWS で冗長化する

uzukiAWS, efs, ftp, s3, sftp, ssh

開発グループの uzuki です。

早速ですが、可用性と一貫性を担保する SFTP サービスにご興味はございませんか?

昨今様々なデータのやり取りが各サービス間で行われている中、ビッグデータの連携の主軸に FTP を利用しているものもあります。

そういったサービスとの連携を行うにあたり、可用性と一貫性を両立させた SFTP サービスを提供できるようにと思い、下記3要件を満たす SFTP サービスを構築してみました。

  1. SFTP インスタンスを冗長化することにより、可用性を担保
  2. 複数の SFTP インスタンスを運用しながら、どのインスタンスからも常に同じデータにアクセスできる一貫性を担保
  3. 利用者ごとに認証情報を個別にし、利用者ごとにアクセスするディレクトリを隔離することによりマルチテナント機能とファイルの機密性を担保

構成

プラットフォームには AWS を選択します。構成は下記の通り。

特に難しいことはせずに、Network Load Balancer 配下に EC2 インスタンスを複数設置します。

AWS S3 にデータ保管領域としてのバケットを用意し、そのバケットを SFTP インスタンスから s3fs を用いてマウントします。複数の SFTP インスタンスから同じバケットを同じファイルパスにマウントすることで、どのインスタンスからも常に同じデータにアクセスできるようになります。

※本サービス構築時点では EFS が東京リージョンにきていなかったので S3 をディスクとして利用しました。安定性や速度面から、新たに構築するのであれば EFS を利用することをおすすめします。

利用者からは SFTP インスタンスに対して、Network Load Balancer 経由で SFTP 接続をしてデータにアクセスしてもらいます。認証には公開鍵認証を用います

冗長化する方法

Network Load Balancer を利用すれば特に問題なく構築できます。また、複雑な設定は不要で、ターゲットインスタンスの指定とヘルスチェックポートの設定のみで設定は完了します。

SFTP の場合、そのプロトコル仕様により TCP コネクションが張りっぱなしとなるため、複数インスタンスでの確立済のセッション同期などの工夫は不要です。しかしながらインスタンスの異常停止や、メンテナンスで意図的にインスタンスを停止する場合には同時に TCP コネクションも切れてしまうため、クライアント側にリトライしてもらうか許容してもらう必要があります。

Network Load Balancer が受け付けるポート番号は、データ連携元の制約があればそちらに合わせるべきですが、そうでない場合は 22 や 115 など、いわゆる Well-known Port を避けておくのが無難でしょう。

SFTP インスタンスの構築

CentOS7 系で構築を行っています。他ディストリビューションを利用している場合は、適宜コマンドを置き換えてください。

まず、必要なグループ・ディレクトリの作成から行います。

# groupadd sftp-user -g 500
# install -d -o root -g root -m 750 /var/sftp

SFTP 通信を許可させるユーザを限定するため、sftp-user グループを作成しました。また s3fs のマウント先として /var/sftp ディレクトリを作成しました。

続いて SFTP アプリケーションの設定です。SFTP 専用のアプリケーションもありますが、シンプルに sshd を利用してサービス化します。通信ポートについてですが、22 番ポートを通常の SSH 接続用に残すことにすると、何かしら別のポートを利用する必要があります。以下では Network Load Balancer から 10022 番ポートで受け付けるようにしています。

# vi /etc/ssh/sshd_sftp_config

Port               10022
Protocol           2
SyslogFacility     AUTHPRIV

PermitRootLogin                 no
PasswordAuthentication          no
ChallengeResponseAuthentication no

PubkeyAuthentication yes
AuthorizedKeysFile   .ssh/authorized_keys

AllowTcpForwarding no
UsePAM             yes

X11Forwarding no
PidFile       /var/run/sftp.pid

AllowGroups sftp-users

UseDNS no

Subsystem sftp internal-sftp
Match group sftp-users
ClientAliveInterval 30
ForceCommand internal-sftp -l INFO
ChrootDirectory /var/sftp/%u/

末尾の ChrootDirectory /var/sftp/%u/ が前述の「利用者ごとにアクセスするディレクトリを隔離する」部分となります。各利用者は /var/sftp/[user name]/ 以下のディレクトリのみアクセスが許可され、他の利用者のファイルの参照が行なえません。

この設定ファイルを用いて sshd を起動しても良いのですが、サービスファイルを作成することにより、簡単にサービス化が可能となります。

# vi /etc/systemd/system/sshd_sftp.service

[Unit]
Description=OpenSSH server daemon only sftp
After=syslog.target network.target auditd.service

[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/local/sbin/sshd_sftp -D -f /etc/ssh/sshd_sftp_config
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=30s

[Install]
WantedBy=multi-user.target

# systemctl enable sshd_sftp
# systemctl start sshd_sftp

最後に、クライアントが保持する finger print による SSH 接続エラーの回避のため、サーバ側の鍵ペア(/etc/ssh/ssh_host_*)を各インスタンス間で同一になるよう、スナップショット(AWS では AMI)を取得してインスタンスのコピーを行うか、構成管理ツールで各インスタンスに同一の鍵ペアを設置するなどの工夫が必要です。

これをしないと、クライアントが最初に接続したインスタンスとは異なるインスタンスに接続した場合、最初に接続したインスタンスから取得した finger print とは異なる finger print であるためクライアント側で SSH 接続エラーが発生してしまう可能性があります。

利用者を追加する方法

以下は利用者が増えるごとに必要な作業となります。

利用者ごとにユーザアカウントを追加し、利用者ごとの認証情報を追加します。この方法は各インスタンスごとに同じ操作が必要となるため、スクリプトを用意したり、構成管理ツールを活用するなど、自動化するのがおすすめです。

・全インスタンスで必要な作業
# useradd -g sftp-user [user name]
# install -d -o [user name] -g [user name] -m 700 /home/[user name]/.ssh
# echo [公開鍵] > /home/[user name]/.ssh/authorized_keys
# chmod 600 /home/[user name]/.ssh/authorized_keys
# chown [user name]:[user name] /home/[user name]/.ssh/authorized_keys

最後に、S3 上にユーザごとのディレクトリを作成します。SFTP アプリケーションに sshd を利用するため、SFTP 用ルートディレクトリは root のみに作成権限を与えます。sshd では、SFTP 用ルートディレクトリに root 以外が書き込めるパーミションとなっているとサービス開始できないためです。ただし、それだけだと利用者がディレクトリ内にファイルを設置できないため、利用者のユーザアカウントで操作可能な何かしらのディレクトリを作成する必要があります。以下では data ディレクトリを作成しています

・1度の実行だけでOK (実質的に S3 の操作であるため)
# install -d -o root -g sftp-user -m 750 /var/sftp/[user name]
# install -d -o [user name] -g sftp-user -m 700 /var/sftp/[user name]/data

まとめ

上記の通り、特に難しいことはせずに可用性と一貫性を担保した SFTP サービスが構築できます。AWS を利用することにより、ロードバランサーの可用性担保・データの完全性担保が容易に行なえます。

この構成をミニマムで構築した場合、データ通信量を除けば費用は月1万円もかかりません。

SFTP サービス構築で悩んでいる方はぜひお試しください。

uzukiAWS, efs, ftp, s3, sftp, ssh

Posted by uzuki