WinSCPからSFTP、SCP接続する際にchrootで接続ディレクトリを制限する。

# はじめに
`WinSCP`は`SFTP`や`SCP`でサーバに接続してファイル転送等を行う際に便利なWindowsアプリケーションですが、`OpenSSH`の`chroot`の機能で接続できるディレクトリを制限したいと思いやってみたところ、中々苦戦したため記事に残しておこうと思います。

# chrootとは
ルートディレクトリを変更する機能となり、`chroot`の機能を使用することで、指定のディレクトリ配下をルートディレクトリとして動作させることができるので、今回のように特定のディレクトリ配下しか見せないということができます。

以下のように`user1`は特に制限せず、`user2`と`user3`は`chroot`で制限されていた場合、制限がない`user1`は全て閲覧可能ですが、`user2`と`user3`は`/chroot/userX`より上位には移動できないため、`user2`から`user3`、`user3`から`user2`のファイル・ディレクトリを閲覧することはできません。

```bash:chrootの例
/
├── 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設定](#sftp接続する際のchroot設定)
- [SCP接続する際のchroot設定](#scp接続する際のchroot設定)
- [SFTPユーザとSCPユーザを共存する際のchroot設定](#sftpユーザとscpユーザを共存する際のchroot設定)
- [同じユーザでSFTP、SCP接続する際のchroot設定](#同じユーザでsftpscp接続する際のchroot設定)

## 事前設定
事前に`chroot`でルートディレクトリ先となるディレクトリの作成と、接続確認用のユーザ、また、共通で必要となるディレクトリを先に作成しておきます。

今回は`SFTP`、`SCP`でそれぞれユーザを分けたいと思うので、ユーザと`chroot`で制限するディレクトリは以下のようにしたいと思います。

|ユーザ名|chrootディレクトリ|
|:--|:--|
|sftpuser|/chroot/sftpuser|
|scpuser|/chroot/scpuser|

```bash:chroot用ディレクトリ作成
sudo mkdir -p /chroot/sftpuser
sudo mkdir -p /chroot/scpuser
```

なお、今回作成するユーザは`SFTP`、`SCP`接続専用ユーザとして、接続時に`chroot`でルートディレクトリを変更するため、ユーザ作成時、不要なホームディレクトリは作成しないようにしますが、ホームディレクトリのパスは通常通りのパス指定で行ってください。

```bash:ユーザの作成（ホームディレクトリ指定のみ）
sudo useradd -d /home/sftpuser -M sftpuser
sudo useradd -d /home/scpuser -M scpuser
```

今回は説明簡略化のため、パスワード認証にしたいと思いますので、パスワードも設定しておきますが、実際の運用時はSSH鍵ファイルでの接続を検討しましょう。

```bash:パスワードの設定
sudo passwd sftpuser
sudo passwd scpuser
```

### chroot制限のSFTP、SCPを行った際のルートディレクトリについて
`chroot`で制限するルートディレクトリは`root`の権限である必要があるため、デフォルトでは`755`のパーミッションでディレクトリ作成されることから、ルートディレクトリ直下にはファイル等作成することができません。

そのため、ファイル転送用のディレクトリ（今回は`data`）を作成してそれぞれのユーザ権限にしておきます。

```bash:ファイル転送用ディレクトリ作成と権限変更
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`等で開き、以下のように設定します。

```bash:/etc/ssh/sshd_config
# /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`サービスを再起動しておきます。

```bash:sshdサービスの再起動
sudo systemctl restart sshd
```

### WinSCPによるSFTP接続確認
`chroot`制限を行っているとはいえ、接続方法は変わらないので、`WinSCP`で通常の接続と同様`SFTP`接続を行います。

![Monosnap_20240728_172957.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/2b113a74-aaba-eb50-ec46-e6f65af47175.png)

接続すると以下のようにルートディレクトリ直下に先ほど作成したファイル転送用の`data`ディレクトリしか存在しないことが確認できるかと思います。

![Monosnap_20240728_173450.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/9694c03c-59db-abfc-66b0-4ff54e6c355d.png)

なお、`SFTP`ではなく`SCP`で接続した場合は以下のようなエラーメッセージが表示されます。

```bash:SCP接続した場合のエラーメッセージ
開始時のメッセージ省略のエラー。選択したシェルは WinSCP 互換ではないかもしれません。 (bash をお勧めします)
```

これだとどのようなエラーなのかよくわかりませんが、`PowerShell`等で、SSHコマンドで接続してみると以下のように表示され、`SFTP`以外では接続できないことがわかります。

```bash:internal-sftp設定したユーザにSSH接続した場合
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`サービスを再起動しておきます。

```bash:/etc/ssh/sshd_config
# デフォルトのまま
Subsystem	sftp	/usr/libexec/openssh/sftp-server

# ファイル末尾にscpuser用のchroot設定を追加
Match User scpuser
 ChrootDirectory /chroot/scpuser
```

```bash:sshdサービスの再起動
sudo systemctl restart sshd
```

### 必要ファイルのコピー
`SCP`接続した際に裏で行われている操作を行えるようにするため、必要なファイルを`chroot`で指定するルートディレクトリ配下に配置します。

以下の例を見るとイメージできるかと思いますが、`scpuser`は`/chroot/scpuser`配下がルートディレクトリとなるため、本物のディレクトリ・ファイル構造と同じように、配下に必要となるディレクトリやファイルを配置していきます。

```bash:/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`コマンドで作成します。

```bash:必要ディレクトリの作成
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`にシンボリックリンクでリンクしておきます。

```bash:シンボリックリンクの作成
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`オプションで同サーバ上の本物のディレクトリをマウントしてしまいます。

```bash:必要ディレクトリのマウント
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`にも記載しておきます。

```bash:/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`にコピーしておきます。

```bash:WinSCP接続する際に必要となる基本コマンドのコピー
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`のみ抜き出したものを出力します。

```bash:scpuser部分のみの/etc/passwd出力
sudo grep scpuser /etc/passwd > /chroot/scpuser/etc/passwd
```

### WinSCPによるSCP接続確認
設定できたのでWinSCPでSCP接続を行います。

`SCP`の場合、先ほど配置したファイル、ディレクトリが必要となることから、`SFTP`の場合とは異なり、以下のように`/chroot/scpuser`配下の各種ファイル・ディレクトリも表示されています。

![Monosnap_20240728_172735.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/cf2e4d49-1302-fe0c-7331-c774eaf0ba64.png)

また、今回は`WinSCP`で使用する基本的なコマンド（赤枠）は実行できるようにコマンド実行用のファイルを配置していますが、カスタムコマンド（青枠）まで実行できるようにはしていないため、もしカスタムコマンドも使いたいという場合は`usr/bin`等の配下に必要なファイルを配置してください。

![Monosnap_20240728_175307.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569912/580e46ed-e700-3c55-e758-7ced24f082d9.png)

なお、`SCP`ではなく`SFTP`で接続した場合は以下のようなエラーメッセージが表示されます。

```bash:SFTP接続した場合のエラーメッセージ
SFTP プロトコルを初期化できません。SFTP サーバが起動していますか？
```

`SFTP`接続をした場合、`ChrootDirectory`の設定は有効のため、`ChrootDirectory`に従ってルートディレクトリが設定されますが、`internal-sftp`ではなく、`sftp-server`での動作となるため、接続するために必要なファイルが足りず、接続できないことが原因となります。

こちらについては「[同じユーザでSFTP、SCP接続する際のchroot設定](#同じユーザでsftpscp接続する際の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`それぞれ使用することができるようになります。

```bash:/etc/ssh/sshd_config
# 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`サービスを再起動しておきます。

```bash: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`のときと同様以下のように設定します。

```bash:/etc/ssh/sshd_config
# デフォルトのまま
Subsystem	sftp	/usr/libexec/openssh/sftp-server

# ファイル末尾にscpuser用のchroot設定を追加
Match User scpuser
 ChrootDirectory /chroot/scpuser
```

```bash:sshdサービスの再起動
sudo systemctl restart sshd
```

必要なファイル・ディレクトリについては、`/usr/libexec`配下のファイル・ディレクトリが必要となるため、こちらは他の`lib`系ディレクトリと同様、`bind`オプションでマウントしようと思います。

```bash:libexec用ディレクトリの作成
sudo mkdir -p /chroot/scpuser/usr/libexec
```

```bash:libexec用ディレクトリのマウント
sudo mount --bind /usr/libexec /chroot/scpuser/usr/libexec
```

`lib`や`lib64`と同様、`/etc/fstab`にも記載しておきます。

```bash:/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`の違いなど、色々と知ることができ、昔ながらの機能ながら新たな発見ができました。