Archive for the ‘raspberry pi’ Category

最近はラズベリーパイをつかってIoTデバイスを作ろうという試みをやっています。

ラズパイのキットはこれが調子いいです。

Raspberry Pi Zero WH Starter Kit 8GB
ケイエスワイ
売り上げランキング: 48,720

ラズベリーパイ、Linuxが搭載されていますし、無線LANの挙動も実に軽快なので、LAN内にいる時はSSH接続がとても簡単です。しかし、IoTデバイスというのは常にLAN内にあるものとも限らず、SIMカードを使ってスマホのように各キャリアが提供するネットワークを使ってインターネットにアクセスする必要もあります。

私はラズパイをインターネットに接続させるSIMとして、Soracomを選びました。
SoracomのSIMにはグローバルIPが割り振られておりません。
そしてIPが割り当てられていない以上、一般的なTCP/IP - DNSを使ったSSH接続はできないということになります。

とはいえ開発中、Docomoネットワークを使ってインターネットに接続するSIMを使ったラズパイに対して、SSH接続したいことは往々にしてよく起こります。
それができなければわざわざシーケンス制御のプログラムを書いて実行しなければならない、といったような。

ということで、SoracomのSIMをはじめとする、グローバルIPが割り振られていないSIMを使ってインターネットにアクセスをしているラズパイに対して、SSH接続をする方法(NAT超えSSH接続)をここに記しておきたいと思います。

使っているラズパイの条件は下記の通りです。

raspberry pi : Raspbian GNU/Linux 9.8 (stretch)
autossh : 1.4e
soracom sim : SORACOM Air SIM plan-D (SMS/data)
3G USBドングル : Abit AK-020

中継サーバーが必須

まずは中継サーバーを用意しましょう。
この方法は図のように、中継サーバーを介してラズパイにアクセスをすることになります。

スクリーンショット 2019 03 18 17 57 29

上にも書きましたがSoracom SIMを使ったラズパイの場合、グローバルIPが割当てられていないことで、通常のTCP/IPだけでは座標を特定することができないからです。

なので、SSHポートフォワーディング(SSHトンネル)という技術を使って、中継サーバーを介してラズパイにアクセスできるようにします。

簡単に順番に仕組みを説明すると

  1. ラズパイ、起動直後に中継サーバーに対してSSHポートフォワーディングをする
  2. Macbook、中継サーバーにSSHアクセスする
  3. 中継サーバーに入ったMacbook、ポートフォワーディング情報をもとに、ラズパイにアクセスする

ということです。

ポートフォワーディングというのは、SSH接続をする際に、接続先のサーバーの任意のポートと、接続元のデバイスの任意のポートに転送(同一化)させるというような技術です。

このケースの場合、(ラズパイのSSHポートは22番であるという前提とします)接続先の中継サーバーの4649番のポートを、接続元のラズパイの22番ポートに転送するということによって、中継サーバーにアクセスしたMacbookは中継サーバーの4649番ポートにssh接続をすることで、ラズパイに対してssh接続をするという仕組みを取ります。

実践

前提として中継サーバーに接続する際に使うユーザーを中継サーバー側で用意しておく必要があります。

このケースの例としてuserを使いますので、中継サーバー側で作成しておいてください

# adduser user

鍵の作成

この方法ではラズパイの起動直後に中継サーバーに対してSSH接続をしなければなりませんので、パスワードを入力することができません。そのため公開鍵暗号方式を使ってパスワードなしで接続できるようにします。

まずは、中継サーバー側のsshd_configのPubkeyAuthenticationyesに設定しておきましょう。

次にラズパイ側でssh接続のための鍵を作ります。

ssh-keygen -t rsa

(鍵の暗号化方式はどれがよいかという議論はありますが、ここでは省略、今回はrsaを採用します。)

あとは対話形式で鍵の作成が進んでいきますが、パスフレーズを聞かれるところでは何も入力せずに進んでください。

+---[RSA 2048]----+ 
|       ..  . ... |
|        ... o .  |
|        o. + * o |
|       . .. o & o|
|        S    = O |
|            ..+ o|
|          ..oooEo|
|           o*==+*|
|           .*@BO=|
+----[SHA256]-----+

こういう暗号のイメージが表示されたら鍵の作成は完了です。

~/.ssh/に
id_rsa 
id_rsa.pub
の2つのファイルが生成されています。

id_rsaが秘密鍵、id_rsa.pubが公開鍵です。
秘密鍵は絶対に人の手に渡らないように、ネットワークに流れることのないようにしましょう。

中継サーバーへ公開鍵の転送

出来上がった公開鍵を中継サーバーに送り、鍵を認証してもらいます。

ssh-copy-id user@192.168.0.100

#sshdのlistenポート番号を変更している場合は下記のように
ssh-copy-id user@192.168.2.30 -o port=10000

userと@以降のipについてはssh接続するユーザー名と接続先のホスト名またはIPアドレスを入力します。

コマンドが通ればパスワードを尋ねられますので、中継サーバー上でuserに設定しているパスワードを入力しましょう。公開鍵の転送が完了します。

この状態で

ssh user@192.168.0.100

と入力して、パスワードを聞かれずにサーバーにログインできれば成功です。

SSHポートフォワーディング

ラズパイから中継サーバーにログインする時に使うコマンドに、ポートフォワーディング用のオプションを追記します。

ssh -R 4649:localhost:22 user@192.168.0.100

-R 4649:localhost:22

というオプションを追加することで、中継サーバーの4649番ポートをラズパイの22番ポートに転送できます。

したがって上図に示しているMacbookは、中継サーバーにログインした後

ssh pi@localhost -o port=4649

とすることで、中継サーバーを経てラズパイにログインすることが可能になりました。

ラズパイから中継サーバーへssh接続の自動化

ラズパイの起動直後に中継サーバーへssh接続を自動化するには、ラズパイのcrontabに追記するのが良いのではないかと思います。

crontab -e

によってcrontabを開き、

@reboot ssh user@192.168.0.100

と書き込めば起動時に自動的に中継サーバーに接続しに行くようになります。

このとき、いくつかの設定を追加するのも良いかと思います。
具体的には ~/.ssh/config に下記のような設定を追加しておきます。

Host RelayServer
  HostName relay-server.com #中継サーバーのホスト名
  User user
  IdentityFile ~/.ssh/id_rsa
  
  StrictHostKeyChecking no
  UserKnownHostsFile /dev/null
  ServerAliveInterval 60
  ServerAliveCountMax 3
  ExitOnForwardFailure yes
  RemoteForward 4649 localhost:22

それぞれのオプションの説明をします。

HostNameは中継サーバーのアドレスです。
Userには中継サーバーにログインするユーザー名を入力します。
IdentityFileは秘密鍵の場所を書きます。

StrictHostKeyChecking no
UserKnownHostsFile /dev/null

によって、はじめて接続するホストの場合でも警告(fingerprint云々)を出さないようにしたり、known_hostsファイルに影響を与えることなく接続できます。

ServerAliveInterval 60
ServerAliveCountMax 3

このオプションによって、サーバーからのタイムアウトを回避します。
60秒毎にサーバーにメッセージを送り、ServerAliveCountMax の設定回数、3回まで繰りかえします。
もしサーバーが応答しなくなったら 180秒後に接続が切断されるようになるということですね。

ExitOnForwardFailure yes

このオプションによって、既にポートフォワード先のポートが空いてなかった場合はssh接続をせずに終了します。

RemoteForward 4649 localhost:22

このオプションで接続先の中継サーバーの4649番ポートに、接続元のラズパイの22番ポートに転送できます。

これらの設定が終われば、

ssh RelayServer

と入力するだけで上記で設定したオプションを踏まえたssh接続ができます。

自動で接続する際には -N -f オプションをつけてもいいです。それぞれ

-N は接続先でポートフォワーディングのみに利用する接続であることを明示するオプション

-f はバックグラウンドで実行することを明示するオプションです。

crontabの方にも
@reboot ssh -N -f RelayServer

と書いておくとこれらが自動で実行されます。

autosshのインストール

さらにautosshを使ってssh接続するという選択もあります。
普通のsshを使っても問題はないのですが、autosshの場合、セッションが何らかの原因で途切れてしまった場合、自動的に再接続をしてくれます。

autosshはsshと互換のあるコマンドですので、普段からautosshを使うのもありですね。

#apt-get install autossh

でインストールできます。

上記のことをautosshでやる場合、crontabには

@reboot autossh -M 0 -N -f RelayServer

と書きましょう。

-M 0 オプションは
autossh標準のsshコネクションモニタリングを無効にし、OpenSsh自体のモニタリングを有効にします。
OpenSsh自体のモニタリングとはServerAliveInterval 60や、ServerAliveCountMax 3などの設定のことです。
autosshのmanページで、これは推奨されています。

この方法でのSSH接続は信頼性があまり高くない

以上のことを実行すると、ラズパイ起動時に中継サーバーに自動で接続しにいき、そのセッションを使ってクライアント(macbook)からラズパイに接続しにいくことができるようになります。

しかし、ラズパイを再起動などすると、再接続までにとても時間がかかったりします(時間測ったら10分くらいかかりました)
その場合、中継サーバーをリブートしてから、ラズパイをリブートすると、すぐに接続できたりするのですが…

おそらく、ラズパイリブートする前にautosshの接続をきちんと切ってからリブートすればいいのかなあなんて今書きながら思いましたが、まだ試していません。

いずれにしろ、この方法を使ったSSH接続はあくまで開発途中での実験やデバッグに用いるのがよさそうです。

中継サーバー上のポートListenの履歴を、ラズパイが沈黙した後にすぐに削除するなどの施策を取ることができれば完全に安定させることができるのかも知れませんが。

なお、この記事を書くにあたって下記のサイトを参考にさせていただきました。
ありがとうございます。

SSH公開鍵認証でパスワード無しでログインする!ユーザも指定できる! | ぴぐろぐ

Soracom Airで繋がったデバイスにリモートからSSHする | DevelopersIO

OpenSSHの警告メッセージを出さないようにする方法 - Qiita

autossh(1): monitor/restart ssh sessions - Linux man page

raspberryPi_logo

まだ原始的な段階で、Modemに対して直接コマンドを送ることでSMSを送受信できた。

概要:
3gドングルに、ソラコムのSMS機能付きSIMを入れて、ラズパイと接続。
そのラズパイとiPhone(スマホ)の間でSMSを送受信する。

やってることはほぼこれと同じ
3gドングルを使いATコマンドで日本語のSMSを送信する - Qiita

3Gドングルの構成も同じ
AK-020 : https://soracom.jp/products/ak-020/
Soracom Air Sim : https://soracom.jp/products/

screen(懐かしいな)をインストールして/dev/ttyUSB1とシリアル通信
上記参考サイト様のまんまで成功できました。

スクリーンショット 2019 03 08 14 55 02

それで、ラズパイ、つまりLINUX側でSMSの受信を確認する方法が書いてなかったのでここに合わせて書かせてもらいます。

ラズパイ側からスマホに向けて送信されたテキストに対して、スマホから適当に英文で返信を返します。
(今の時点では日本語をエンコードできないので)

次に、screen上で下記コマンドを打ちます。

AT+CMGL=“REC UNREAD"

これで、スマホから送ったメッセージテキストがターミナル上に表示されました。

これについて詳しくは下記動画を御覧ください。

Text Messaging (SMS) on a Raspberry Pi - YouTube

raspberryPi_logo

この間自宅にRaspberry Piが届きまして、早速色々といじくっている最中です。

とりあえず、Windows FAT32フォーマットの1TB外付けHDDを繋げてみました。
USBで接続してdmesgコマンドを打ってみる。
dmesgはLinuxカーネルのログを表示したり制御したりできます。kazmax様のサイトへリンクを張らせて頂きました。

・・・すると、dmesgで以下の画像の様な出力を得ました。

ふむふむ、どうやらこの時点で既にHDDは認識されているようです。
/dev/sdcに該当HDDが追加されているのがわかります。
ちなみに/dev/sdcはHDDそれ自体を、/dev/sdc1はHDDに切られたパーティションを表します。

私のHDDですと、パーティションは一つしか持ってないので、sdc1を指定してあげると良い事になります。

と言う事で実際にマウントしてみました。
はじめに/mnt/mediaというディレクトリを作成してから

# mount /dev/sdc1 /mnt/media

とする事でマウントが出来ました。
しかし中身をみてみると、どうも日本語の文字コードの変換が旨く行ってない様子。
WindowsではDOSの表示での文字コードにcp932というものを用いるのが原因のようです。
それに、フォーマットの指定も忘れていました。

こりゃいかんという事で、やり直す為にまずアンマウントを試みたのですが

# umount /dev/sdc1

umount: /home/******/sdc1: device is busy.
(In some cases useful info about processes that use
the device is found by lsof(8) or fuser(1))

と出力されてどうやらHDDがbusyでアンマウントが上手く行っていません。
こういう時にどうすれば良いのか。

umountに -l オプションを付けてあげると良いとのこと。
-fよりも良い感じにアンマウントしてくれるらしい。

以下manより引用
-f Force unmount (in case of an unreachable NFS system). (Requires kernel 2.1.116 or later.)
-l Lazy unmount. Detach the filesystem from the filesystem hierarchy now, and cleanup all references to the filesystem as soon as it is not busy anymore.
(Requires kernel 2.4.11 or later.)

と言う事で

# umount -l /dev/sdc1

で無事にアンマウントが出来ました。

続いて文字化けが起こらないように、新たに必要なオプションを付けて再度マウントを行います。

# mount -t vfat /dev/sdc1 /mnt/media -o codepage=932,iocharset=utf8

-tはファイルシステムのフォーマットを指定します。FAT32はvfatというコマンドになります。
-o はファイルシステム固有のオプションを引数にとるという事で、引数が複数個ある場合は、カンマを区切りにします。
ここではcp932をutf8に変換するという事を指定しています。

これで問題なくマウントする事に成功しました。

以上です。