はじめに
WinSCPはSFTPやSCPでサーバに接続してファイル転送等を行う際に便利なWindowsアプリケーションですが、OpenSSHのchrootの機能で接続できるディレクトリを制限したいと思いやってみたところ、中々苦戦したため記事に残しておこうと思います。
chrootとは
ルートディレクトリを変更する機能となり、chrootの機能を使用することで、指定のディレクトリ配下をルートディレクトリとして動作させることができるので、今回のように特定のディレクトリ配下しか見せないということができます。
以下のようにuser1は特に制限せず、user2とuser3はchrootで制限されていた場合、制限がないuser1は全て閲覧可能ですが、user2とuser3は/chroot/userXより上位には移動できないため、user2からuser3、user3からuser2のファイル・ディレクトリを閲覧することはできません。
/
├── home
│ └── user1 ← user1は全ての場所を閲覧可能
│ └── dir1
│ └── dir2
└── chroot
├── user2 ← /chroot/user2をchrootでルートとした場合、user2はdir3とdir4のみ閲覧可能
│ └── dir3
│ └── dir4
└── user3 ← /chroot/user3をchrootでルートとした場合、user3はdir5とdir6のみ閲覧可能
└── dir5
└── dir6
但し、指定のディレクトリ配下をルートディレクトリとして動作させると、当然、その配下には/binや/etc等、サーバの動作に必要となるディレクトリは存在しないことから、サーバへのリモート接続時の制限として設定する場合、動作に必要となるコマンドやライブラリをchrootで制限しているディレクトリ配下に配置する必要が出てくる場合があります。
ちなみに、chrootはUnixでは昔からある機能となり、最近よく使われているDocker等のコンテナ技術はchrootがベースとなっている技術となります。
サーバ構成
今回は以下の構成のサーバで設定を行おうと思います。
OSにより若干手順が異なるかもしれませんが、OpenSSHを使用しているサーバならUbuntuや他のLinuxでも同様の手順でできるかと思います。
| 項目 | 内容 |
|---|---|
| OS | Red Hat Enterprise Linux 8.9 |
| OpenSSHサーババージョン | 8.0p1-19.el8_9.2 |
SFTP、SCP接続する際のパターン別chroot設定
SFTP、SCPどちらもSSHプロトコルを使用してファイル転送する機能となりますが、chrootで制限を行う場合、SFTP、SCP両方接続できるようにするのは大変なので、できればSFTPのみでファイル転送できるようにすることをおすすめします。
今回SFTP、SCPそれぞれchrootで制限する方法は紹介しますが、よほどの理由がなければSFTPのみで接続できるような運用としたほうがよいと思います。
- 事前設定
- SFTP接続する際のchroot設定
- SCP接続する際のchroot設定
- SFTPユーザとSCPユーザを共存する際のchroot設定
- 同じユーザでSFTP、SCP接続する際のchroot設定
事前設定
事前にchrootでルートディレクトリ先となるディレクトリの作成と、接続確認用のユーザ、また、共通で必要となるディレクトリを先に作成しておきます。
今回はSFTP、SCPでそれぞれユーザを分けたいと思うので、ユーザとchrootで制限するディレクトリは以下のようにしたいと思います。
| ユーザ名 | chrootディレクトリ |
|---|---|
| sftpuser | /chroot/sftpuser |
| scpuser | /chroot/scpuser |
sudo mkdir -p /chroot/sftpuser
sudo mkdir -p /chroot/scpuser
なお、今回作成するユーザはSFTP、SCP接続専用ユーザとして、接続時にchrootでルートディレクトリを変更するため、ユーザ作成時、不要なホームディレクトリは作成しないようにしますが、ホームディレクトリのパスは通常通りのパス指定で行ってください。
sudo useradd -d /home/sftpuser -M sftpuser
sudo useradd -d /home/scpuser -M scpuser
今回は説明簡略化のため、パスワード認証にしたいと思いますので、パスワードも設定しておきますが、実際の運用時はSSH鍵ファイルでの接続を検討しましょう。
sudo passwd sftpuser
sudo passwd scpuser
chroot制限のSFTP、SCPを行った際のルートディレクトリについて
chrootで制限するルートディレクトリはrootの権限である必要があるため、デフォルトでは755のパーミッションでディレクトリ作成されることから、ルートディレクトリ直下にはファイル等作成することができません。
そのため、ファイル転送用のディレクトリ(今回はdata)を作成してそれぞれのユーザ権限にしておきます。
sudo mkdir -p /chroot/sftpuser/data
sudo mkdir -p /chroot/scpuser/data
sudo chown sftpuser:sftpuser /chroot/sftpuser/data
sudo chown scpuser:scpuser /chroot/scpuser/data
もしルートディレクトリ直下にファイルを格納したい場合はchrootで制限するディレクトリのパーミッションを777等に変更してください。
SFTP接続する際のchroot設定
以下より実際にSFTP接続時のchroot設定を行っていきます。
SFTPでのchroot制限を行うため、/etc/ssh/sshd_configをvi等で開き、以下のように設定します。
# /usr/libexec/openssh/sftp-serverをコメントアウト
#Subsystem sftp /usr/libexec/openssh/sftp-server
# internal-sftpを追記
Subsystem sftp internal-sftp
# ファイル末尾にsftpuser用のchroot設定を追加
Match User sftpuser
ChrootDirectory /chroot/sftpuser
OpenSSHサーバ(sshdサービス)を使用している場合、chrootで制限したSFTP接続を行うためにはサブシステム設定でinternal-sftpを使用する必要があるため、デフォルトで設定されているsftp-serverをコメントアウトしたうえで、ChrootDirectoryでルートディレクトリとするディレクトリを指定します。
設定後、sshdサービスを再起動しておきます。
sudo systemctl restart sshd
WinSCPによるSFTP接続確認
chroot制限を行っているとはいえ、接続方法は変わらないので、WinSCPで通常の接続と同様SFTP接続を行います。
接続すると以下のようにルートディレクトリ直下に先ほど作成したファイル転送用のdataディレクトリしか存在しないことが確認できるかと思います。
なお、SFTPではなくSCPで接続した場合は以下のようなエラーメッセージが表示されます。
開始時のメッセージ省略のエラー。選択したシェルは WinSCP 互換ではないかもしれません。 (bash をお勧めします)
これだとどのようなエラーなのかよくわかりませんが、PowerShell等で、SSHコマンドで接続してみると以下のように表示され、SFTP以外では接続できないことがわかります。
This service allows sftp connections only.
Connection to hostname closed.
chroot設定を行わずに、デフォルトのsshdの設定(sftp-server)で接続する場合はSFTPでもSCPでも接続できますが、internal-sftpとchroot制限を行うとSFTP接続しかできなくなるので、ご注意ください。
SCP接続する際のchroot設定
SCPでchrootの制限を行う場合はSFTPのように簡単にはいかず、SCP接続時に必要となるコマンドファイルやライブラリ等をchrootで指定したルートディレクトリ配下に配置して、機能制限されたミニOSを作成するような作業を行う必要があります。
/etc/ssh/sshd_configの設定としてはファイル末尾にscpuser用のchroot設定を行うのみとなるため、以下のように設定してsshdサービスを再起動しておきます。
# デフォルトのまま
Subsystem sftp /usr/libexec/openssh/sftp-server
# ファイル末尾にscpuser用のchroot設定を追加
Match User scpuser
ChrootDirectory /chroot/scpuser
sudo systemctl restart sshd
必要ファイルのコピー
SCP接続した際に裏で行われている操作を行えるようにするため、必要なファイルをchrootで指定するルートディレクトリ配下に配置します。
以下の例を見るとイメージできるかと思いますが、scpuserは/chroot/scpuser配下がルートディレクトリとなるため、本物のディレクトリ・ファイル構造と同じように、配下に必要となるディレクトリやファイルを配置していきます。
/
├── bin
├── dev
├── etc
├── (略)
│
└── chroot
└── scpuser
├── bin ←シンボリックリンクで設定
├── dev ←/devをbindでマウント
├── data ←ファイル転送用ディレクトリ
├── etc ←scpuserのみ記載されたpasswdファイルを配置
├── lib ←シンボリックリンクで設定
├── lib64 ←シンボリックリンクで設定
└── usr
├── bin ←必要なコマンドファイルのみコピー
├── lib ←/usr/libをbindでマウント
└── lib64 ←/usr/lib64をbindでマウント
まずは、必要なディレクトリをmkdirコマンドで作成します。
sudo mkdir -p /chroot/scpuser/dev
sudo mkdir -p /chroot/scpuser/data
sudo mkdir -p /chroot/scpuser/etc
sudo mkdir -p /chroot/scpuser/usr
sudo mkdir -p /chroot/scpuser/usr/bin
sudo mkdir -p /chroot/scpuser/usr/lib
sudo mkdir -p /chroot/scpuser/usr/lib64
bin、lib、lib64は通常の構成でもシンボリックリンクで/usr/xxxにリンクされているため、同じように/chroot/scpuser/usr/xxxにシンボリックリンクでリンクしておきます。
sudo ln -s usr/bin /chroot/scpuser/bin
sudo ln -s usr/lib /chroot/scpuser/lib
sudo ln -s usr/lib64 /chroot/scpuser/lib64
また、dev、usr/lib、usr/lib64はmountコマンドのbindオプションで同サーバ上の本物のディレクトリをマウントしてしまいます。
sudo mount --bind /dev /chroot/scpuser/dev
sudo mount --bind /usr/lib /chroot/scpuser/usr/lib
sudo mount --bind /usr/lib64 /chroot/scpuser/usr/lib64
マウントした領域は再起動するとマウントが外れてしまうため、以下の情報を/etc/fstabにも記載しておきます。
/dev /chroot/scpuser/dev none bind 0 0
/usr/lib /chroot/scpuser/usr/lib none bind 0 0
/usr/lib64 /chroot/scpuser/usr/lib64 none bind 0 0
binに含まれるコマンドも、bindオプションでマウントしてしまっても良いですが、必要なコマンドのみに絞るほうがセキュリティ的には望ましいため、今回はWinSCPで接続した際に使うコマンドのみ/chroot/scpuser/usr/binにコピーしておきます。
sudo cp -a /usr/bin/bash /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/chgrp /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/chmod /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/chown /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/cp /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/ln /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/ls /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/mkdir /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/mv /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/rm /chroot/scpuser/usr/bin/
sudo cp -a /usr/bin/scp /chroot/scpuser/usr/bin/
/etc/passwdの設定のうち、scpuserの分のみ必要となるため、以下でscpuserのみ抜き出したものを出力します。
sudo grep scpuser /etc/passwd > /chroot/scpuser/etc/passwd
WinSCPによるSCP接続確認
設定できたのでWinSCPでSCP接続を行います。
SCPの場合、先ほど配置したファイル、ディレクトリが必要となることから、SFTPの場合とは異なり、以下のように/chroot/scpuser配下の各種ファイル・ディレクトリも表示されています。
また、今回はWinSCPで使用する基本的なコマンド(赤枠)は実行できるようにコマンド実行用のファイルを配置していますが、カスタムコマンド(青枠)まで実行できるようにはしていないため、もしカスタムコマンドも使いたいという場合はusr/bin等の配下に必要なファイルを配置してください。
なお、SCPではなくSFTPで接続した場合は以下のようなエラーメッセージが表示されます。
SFTP プロトコルを初期化できません。SFTP サーバが起動していますか?
SFTP接続をした場合、ChrootDirectoryの設定は有効のため、ChrootDirectoryに従ってルートディレクトリが設定されますが、internal-sftpではなく、sftp-serverでの動作となるため、接続するために必要なファイルが足りず、接続できないことが原因となります。
こちらについては「同じユーザでSFTP、SCP接続する際のchroot設定」で対処方法を説明していきます。
SFTPユーザとSCPユーザを共存する際のchroot設定
ここまでのSFTP、SCPの設定では、chroot制限のSFTPを有効にした場合はinternal-sftpサブシステムが全体にかかるため、chroot制限のSCPは使用できず、chroot制限のSCPを有効にした場合はchroot制限のSFTPを使用できませんでした。
internal-sftpはForceCommandの設定で特定のユーザ接続でのみ使用するように設定することができるため、デフォルトのサブシステムはsftp-serverのまま、sftpuserのみinternal-sftpを使用することで、chroot制限のSFTP、SCPそれぞれ使用することができるようになります。
# sftp-server設定はデフォルトのまま
Subsystem sftp /usr/libexec/openssh/sftp-server
# ForceCommand設定でsftpuserのみinternal-sftpを有効化する
Match User sftpuser
ChrootDirectory /chroot/sftpuser
ForceCommand internal-sftp
# scpuser用のchroot設定を追加
Match User scpuser
ChrootDirectory /chroot/scpuser
設定後、sshdサービスを再起動しておきます。
sudo systemctl restart sshd
この設定を行うことで、sftpuserでSFTP接続した場合はSFTPのchroot制限が行われ、scpuserでSCP接続した場合はSCPのchroot制限が行われるようになります。
同じユーザでSFTP、SCP接続する際のchroot設定
chroot制限のSCP設定を行った際には、同じユーザでSFTP接続することはできませんでしたが、そもそもchroot設定を行わない場合はどちらも同じ使用感で使用できるため、chroot設定を行ったから使用できないわけではなくchroot設定を行ったことで、SFTP接続するのに必要となるファイル・ディレクトリが参照できずに接続できなかったことが原因となります。
そのため、chrootのルートディレクトリに、SFTP接続に必要となるファイルも配置することで対処します。
ベースとしてはSCP接続する際の設定となるため、SCP接続する際の設定が行われている前提で進めます。
scpuserでSFTP、SCP接続するよう設定するため、/etc/ssh/sshd_configはSCPのときと同様以下のように設定します。
# デフォルトのまま
Subsystem sftp /usr/libexec/openssh/sftp-server
# ファイル末尾にscpuser用のchroot設定を追加
Match User scpuser
ChrootDirectory /chroot/scpuser
sudo systemctl restart sshd
必要なファイル・ディレクトリについては、/usr/libexec配下のファイル・ディレクトリが必要となるため、こちらは他のlib系ディレクトリと同様、bindオプションでマウントしようと思います。
sudo mkdir -p /chroot/scpuser/usr/libexec
sudo mount --bind /usr/libexec /chroot/scpuser/usr/libexec
libやlib64と同様、/etc/fstabにも記載しておきます。
/usr/libexec /chroot/scpuser/usr/libexec none bind 0 0
結果として、WinSCPでSFTP、SCPともに同じユーザで接続するためにはlibexecのマウントを追加するだけでできました。
これで、scpuserでSFTP、SCPどちらでもchrootで制限された接続、ファイル転送ができるようになります。
おわりに
SFTPをchrootで制限する方法は多くの方が紹介していたため、SCPも同じだろうと軽く考えていたら、まんまとハマりました。
SCPのchroot制限については、スバリこの設定をすればOKというような記事は見つからず、当然SFTP、SCP両方同じユーザでchroot制限を行って接続するような記事は見当たらなかったため、中々苦労しました。
今回の記事をまとめたことで、chrootの仕組みの復習、SCPとSFTPの違いなど、色々と知ることができ、昔ながらの機能ながら新たな発見ができました。



