IoT機器にインターネット越しに接続したい

# 環境
IoT機器は外部のネットワークに接続する。
そのネットワークの設定変更は行うことができず、NAT越え・ポート転送などを行って外部から直接接続することはできない。
またファイアウォールによって外部からの通信を遮断するかもしれない。
![iot.png](https://qiita-image-store.s3.amazonaws.com/0/298237/2a3e4f3e-44a3-f974-dbeb-72054210db24.png)
IoT機器は悪意のある人によって解体・改ざんされる可能性もあるためファイルにパスワードを置いておくことはできない。
IoT機器を操作したい時に、毎回設置したところに行くことは現実的ではない。

少し調べてみたところ、技術的にはssh tunnelingとVPNを使う方法が一般的のようだ。
そこで今回はssh (remote) tunnelingを使って実現しようと思う。

ServerはグローバルIPを持っているか、DNSによって例えばwww.example.comという名前を持っているとする。
IoT機器にはpiyoというユーザがすでに存在しており、Serverには接続の踏み台に利用するためにhogeというユーザを作成する。
実際に使ったの機器は、Server: AWS EC2 Linuxインスタンス、IoT: Raspberry Pi 3B。

# 設定1 IoT機器側の設定 (ssh用にRSA暗号鍵を作成する)
PCから接続したいときはいつでもIoT機器に応答してもらいたいので、autosshというソフトウェアをインストール。
またパスワード無しでServerに接続するために、ssh-keygenでパスワード無しのキーを作成する。
すでにある公開鍵・秘密鍵を上書きしないために、id_rsa_nopassという適当な名前で保存する。

```
$ sudo apt-get install autossh
$ ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa_nopass
[パスワードを聞かれるがEnterを2回押して無しに設定する]
```

公開鍵の内容をServerに設定しないといけないので、何かしらの方法でコピーする。

```
$ cat ~/.ssh/id_rsa_nopass.pub
```

# 設定2 Server側の設定 (ログイン用ユーザの用意)
IoT機器ごときにServerの全リソースにアクセスさせるわけには行かないので、ほぼ何もできないようなアカウントを作成する。
ここでは適当なアカウント名hogeで作成した。

```
$ sudo useradd -m hoge
$ sudo passwd hoge
[一応推測されないようなhogeのログイン用パスワードを設定する]
$ sudo chsh -s /bin/bash hoge
```

ログインシェルのbashだけは実行できるように、コマンド本体と必要なライブラリをコピーする。
ご利用の環境ごとにlddコマンドを利用して必要なライブラリを確認し、コピーしてほしい。

```
$ which bash
/bin/bash
$ sudo mkdir /home/hoge/bin/
$ sudo cp /bin/bash /home/hoge/bin/bash

$ ldd /bin/bash
	linux-vdso.so.1 =>  (0x00007***********)
	libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007***********)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007***********)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007***********)
	/lib64/ld-linux-x86-64.so.2 (0x00007***********)
$ sudo mkdir -p /home/hoge/lib/x86_64-linux-gnu/
$ sudo cp /lib/x86_64-linux-gnu/{libtinfo.so.5,libdl.so.2,libc.so.6} /home/hoge/lib/x86_64-linux-gnu/
$ sudo mkdir -p /home/hoge/lib64/x86_64-linux-gnu/
$ sudo cp /lib64/ld-linux-x86-64.so.2 /home/hoge/lib64/
$ sudo mkdir /home/hoge/.ssh/
$ sudo touch /home/hoge/.ssh/authorized_keys
$ sudo chown -R hoge:hoge /home/hoge/*
$ sudo chown root:root /home/hoge/
$ sudo chmod 755 /home/hoge
```

# 設定3 Server側の設定 (sshdの設定)
IoT機器にsshでログインさせるために、設定1でコピーしたIoT機器の公開鍵を登録する。

```
$ sudo nano /home/hoge/.ssh/authorized_keys
[ペースト]
```

IoT機器からhogeにsshの接続リクエストがあった時の挙動を変更する。
末尾の方に、+から先の文字をコピペして追加する。
hogeというユーザに対しては、ログインしたら/home/hogeというディレクトリをrootディレクトリにし、TCP Forwardingは許可し、公開鍵認証を許可し、その許可する鍵のファイルは~/.ssh/authorized_keysに置く、ということを意味している。
最後に設定を反映させるためsshdを再起動している。

```
$ sudo nano /etc/ssh/sshd_config
+ Match User hoge
+    ChrootDirectory /home/hoge
+    X11Forwarding no
+    AllowTcpForwarding yes
+    PubkeyAuthentication yes
+    AuthorizedKeysFile .ssh/authorized_keys
$ sudo systemctl restart sshd
```

またIoT機器にServerからsshするためのRSA暗号鍵を作成する。
手順はIoT機器でやったこととほぼ一緒なので割愛。
すでに存在するものがあればそちらを利用しても構わない。
RSAの公開鍵(.pubとなっている方)の内容をコピーしておく。

# 設定4 IoT機器側の設定
Serverからのsshを受け付けるためにServerの公開鍵を設定する。

```
$ sudo nano /home/hoge/.ssh/authorized_keys
[ペースト]
```

# 設定5 通信の確認
まずはIoT機器からServerにsshする。
通常と異なるremoteオプションを利用しているので注意。
またログインシェル自体は動くが、lsを始めとするコマンドは一切動かない。
（必要があればコピーしてほしい）

```
$ ssh -i ~/.ssh/id_rsa_nopass -R 3001:localhost:22 hoge@www.example.com
$ ls [command not foundとなって動かない]
```

接続ができたらコネクションを張ったままで、Server側からIoT機器側にsshする。
もし/home/hoge/.ssh/id_rsa以外の秘密鍵を利用するなら、-iオプションで指定する。

```
$ sudo su - hoge
$ ssh -p 3001 piyo@localhost
```

接続ができたら設定自体はほぼ完成。
2つのsshの接続を切る。

どうなっているかというと、IoT機器はServerの22番ポートに対してsshで接続し、その受け口を3001番に割り当てている。
ServerがServer内の3001番ポートにsshすると、その接続はIoT機器に転送される。
ここで、通常はできない「Server側からIoT側へのssh」が可能になっている。

# 設定6 IoT側の設定 (Serverへのssh接続を自動化する)
ServerからIoT機器に接続するために、毎回IoT機器を操作するのは本末転倒なので、systemdを利用してIoT機器の起動時に自動起動するようにする。
.serviceファイルは実行するコマンドを絶対パスで記述するのが原則なので、予めautosshコマンドの絶対パスを調べておく。

```
$ which autossh
/usr/bin/autossh

$ sudo nano /etc/systemd/system/autossh.service
+ [Unit]
+ Description=Automatic SSH Remote Tunneling
+ After=network.target
+
+ [Install]
+ WantedBy=multi-user.target
+
+ [Service]
+ Type=simple
+ ExecStart=/usr/bin/autossh -M 0 -N -R 3001:localhost:22 -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -i /home/piyo/.ssh/id_rsa_nopass hoge@www.example.com
+ ExecStop=/bin/kill ${MAINPID}
+ Restart=on-failure
+ RestartSec=5
+ User=piyo
+ Group=piyo

$ sudo systemctl enable autossh
$ sudo systemctl start autossh
```

先ほどと同じように、ServerからIoT機器に接続できるか確認する。
接続できれば自動化も完了。
できなかったら、以下のコマンドを駆使して原因を探ってほしい。

```
piyo$ sudo systemctl status autossh -l [autossh serviceの状態確認]
hoge$ netstat -ntl [3001番のポートが作成されているか確認]
hoge$ ssh -p 3001 piyo@localhost -v [-vあるいは-vvオプションで詳細を表示]
```

# 参考
- [SSHポートフォワード（トンネリング）を使って、遠隔地からLAN内のコンピュータにログインする - ククログ(2014-09-12)](https://www.clear-code.com/blog/2014/9/12.html)
- [chrootを用いてSSHユーザのディレクトリを制限する](https://hacknote.jp/archives/32888/)
- [jfathman/ssh-tunnel: Create SSH Reverse Tunnel from IoT Device to Cloud Server for Remote Access](https://github.com/jfathman/ssh-tunnel)