このブログを検索

2019/07/15

raspberry pi でWEBカメラ ~motion~

pi zero wにカメラをつけてWEBカメラにする。

パソコンで使うUSBカメラを使ったことはあったが、
Camera Module V2を使う。

pi zero wのケースはカメラをセットする穴がある。

ただし、カメラの穴があるふたをするとGPIOの金具があるので閉まらない。

金具部分があいたフタもあるのだが、それにはカメラの穴がない。

どっちかを加工すればいいのだろうが、とりあえずテープで止める。


piでWEBカメラをやる場合の定番としてmotionというソフトがある。

これは動画ではなくて、動きがあった場合のみ画像を更新するものであるが、

外の景色などを表示させるには十分である。


USBカメラの場合、カメラを接続してmotionをインストールすればすぐ使えるのだが、

Camera moduleを使う場合は、 /etc/modulesに以下の記述を追加してrebootが必要。

bcm2835-v4l2


あと、/boot/config.txtに以下の記述が必要なそうなのだが、
私の場合はすでに書いてあった。

start_x=1
gpu_mem=128

start_xって関係なさそうだけどな... まあいい。


vcgencmd get_camera を実施して、両方 1になっていること。

supported=1 detected=1


 ls /dev/video* を実行して、 video0があること。
(私の場合。10,11,12はカメラモジュールを認識しなくても出てくる)

/dev/video0  /dev/video10  /dev/video11  /dev/video12


以下でカメラの動作を確認できる。

(静止画)
raspistill -o test.jpg

-oは出力ファイル名


(動画)
raspivid -o test.h264 -t 10000

-oはファイル名、-tはミリ秒数

形式はh264とかいうものらしいのだが、
VLC media playerで再生できる。


コーデックは H264かMJPEGが指定できるようだ。

-cd, --codec    : Specify the codec to use - H264 (default) or MJPEG

raspivid -cd MJPEG -o test.mjpeg  -t 5000

が、mjpegも結局メディアプレイヤーでは再生できず。


motionは、apt install motion でインストール

apt-get install motion ではダメ、みたいなことがどこかに書いてあったが、
どっちでもできたような気がするが定かではない
(いろいろやったので)

motion.confの設定内容については情報がたくさんあるので特に書かない。


自動起動するために下記の設定をするというのがあるのだが、

/etc/default/motion に

start_motion_daemon=yes

zeroではうまくいかなかった。
psで見るとmotionは起動するようなのだが、ブラウザでアクセスすると起動していないときと同じように表示されない。

sudo motionを実行すると画面が表示されるがグレーで、
「カメラにアクセスできない」みたいなメッセージが表示される。

ログを見ると、すでに起動していてカメラが占有されている、
みたいなことらしい。

USBカメラを使った古いpiではうまく動いていたのだが.....


なので、手動起動にした。

sudo motion -b

-b はバックグラウンド起動のオプション。


psで起動確認

ps aux | grep motion
root       684 96.9  6.6 126476 25320 ?        Sl   22:31  31:29 motion -b
pi        1575  0.0  0.5   4360  1908 pts/0    S+   23:04   0:00 grep --color=auto motion

デーモン起動なので停止する場合はkillする。

もっとスマートな方法があるかな...



しばらく使って気づいたのだが、cameraモジュールはフォーカス(ピント)が手動で、
カメラのレンズ部分を回すことでおこなう。

白いプラスチックのダイヤルみたいなものが付属しているのだが、
それをレンズ部分にはめると回せる。

なくても楊枝かなんかで回せそう。

時計回りで遠くに、逆で近くにフォーカスする。



2019-04-08-raspbian-stretch-lite.img
を使い、諸アップデートをしている。

uname -aの結果
Linux raspberrypi 4.19.57+ #1244 Thu Jul 4 18:42:50 BST 2019 armv6l GNU/Linux

2019/07/14

raspberry pi zero W

raspberry pi zero wをセットアップする。

カメラ、ケースも買ってある。

pi zero wは、pi zeroに無線LANアダプタがついたものだ。

pi zeroも買ったのだが、やっぱりネットワーク接続は本体についていてほしい。

wなしのpi zeroだと、マイクロUSBハブをつけてそこに無線ドングルをつけねばならない。

せっかくの小さいボディなのに。


カメラは普通のpiで使うのと同じものだが、zeroのカメラインタフェースは小さい(名前は知らない)ので、ケーブルを付け替える必要がある。

そのケーブルも買っておいた。(ケースの付属品だったかな?)

先日セットアップしたpi3で使ったのと同じイメージをSDに書き込む。

使ったのはSD formatterとDD for Windows

カードを差して、電源を差す。小さいLEDが緑色にチカチカして、点灯状態になる。

これで起動しているはず....と、無線LANのSSIDとかパスワードの設定をしていない。

つながるはずがない。

調べると、以下2点を実施すればよい。

1. SDカードに ssh というファイルを作る
2. wpa_supplicant.conf ファイルを作り設定を書く

が、起動はしているようだが無線ルータのdhcpクライアントを表示してもクライアントが増えない。

おかしいな...

コマンドが実行できないのでケーブルを抜いて電源を落として、SDカードを見てみると、
さっき作ったsshとwpa_supplicant.confが消えている。

なぜだ?

仕方がないからディスプレイにつなぐか、と思ったらHDMIインタフェースがminiだ。

ケーブルがない。買ってくる。

HDMI miniケーブルでディスプレイにつなぎ、micro USBハブを介してキーボードをつなぐ。

起動ログが表示される。正常に起動している。

ifconfigを見るとwlanのインタフェースが見えない。

無効になってるのかなと調べかけて、待てよ、と本体裏の名前を見ると、「W」がない。



wpa_supplicant.confの文字コードのせいか、最後に空行があるとダメなのかとか、
悩みかけたがそんな問題ではなかった。

無線LANアダプタのない無印raspberry pi zeroを起動していたのだ。



「W」にSDカードと電源を差しなおして起動。

あっさりつながる。SSHでログイン。


最初なので諸更新をする。

sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade

sudo rpi-update
これは初めて

sudo reboot


カメラの設定

sudo raspi-config

5 Interfacing Options
P1 Camera
Would you like the camera interface to be enabled?
yes

reboot



raspistill -w 1920 -h 1080 -o pic001.jpg

で写真が撮れるということなので実行する。

pic001.jpg というファイルができる。

見たい。

パソコンに移すためにvsftpdをインストールする。
vsftpdのインストール方法については省略。

sambaを使った方がcoolか。後でやろう。

ftpでさっきの pic001.jpgをコピー。

ちゃんと撮れてる。けっこう画質がいい。


apacheを入れて、webで見るようにするか。

sudo apt-get install apache2

とやるだけで、httpアクセスするとテストページが表示される。


さて、カメラのセットアップもできた。

2019/06/22

さくらのVPSを1台解約するにあたって移行

2台もいらない。
低スペックの方を残す。

よく使うcgiと、メールと、twitter botと、それぐらいしか使わないから。

centos6からcentos7への移行となる。

途中で気づいたがpythonも2.7から3.6の移行となる。

python3系にするとyumでエラーになる。
yumは2系を前提としているからだ。

/bin/yum
を直すのは以前にもやった。

mod_sslをインストールしようとしたら、

Downloading packages:
  File "/usr/libexec/urlgrabber-ext-down", line 28
    except OSError, e:
                  ^
SyntaxError: invalid syntax

なんてエラーも出たので
/usr/libexec/urlgrabber-ext-down
も直す。

こんなことでいいのかな。

以前centos6でやったhttpsを使う設定をしていき、
最後にfirewallで443を開けるところまで来て、
そういえば7はiptableじゃない....

いまどうなってるかな...
firewall-cmd --list-all-zones

とやると、エラーになる。
これもpython2系にしないとダメだ。

どいつもこいつもpythonを使っていて、しかも2系なんだな...
3系にするのは先走りなのか...

サービスで開ける。

firewall-cmd --add-service=https --zone=public
firewall-cmd --add-service=https --zone=public --permanent
firewall-cmd --reload

終わり。


2019/05/18

raspberry pi3

Raspberry Pi3 を買ったまま放ってあったので
とりあえずインストールだけでもしておく。

ブログとかを参考にするのはいいが、ダウンロード先がバージョン固定の直接リンクになっていることがあるので注意

https://www.raspberrypi.org/downloads/raspbian/

最新を持ってくる

Raspbian Stretch Lite
Minimal image based on Debian Stretch
Version:April 2019
Release date:2019-04-08
Kernel version:4.14

Liteでいいでしょう...

インストール手順は、久しぶりなので公式のドキュメントを読む

https://www.raspberrypi.org/documentation/installation/installing-images/README.md

SDカードへのOSイメージ書き込みは etcher というツールを使うとなっている....
(失敗する。後述。)

https://www.balena.io/etcher/

SDカードは一緒に買っておいたSanDiskの16GBのもの。


コンソールケーブルをつなぐが反応がない。

speedが115200なのを忘れていたが、変更してもだめだ。

HDMIケーブルとキーボードをつないでみるがやはりダメ。

pwr(赤)とact(緑)が点灯している。

同じSDカードをpi2に入れるが同じ。

SDカードが悪いのか。



SDカードフォーマッターでフォーマットする。
念のためクイックじゃない方で。(「上書きフォーマット」とかいうやつ)

DD for Windowsをインストール。

対象ディスクが選べない。

「管理者として実行」が必要。

チッ。


etcherで使ったのと同じイメージを書き込む。

pi2 model Bにコンソールケーブルをつないで起動。

とりあえず pi2 modelB v1.1 が起動した。

コンソールは
黒 → GPIO 6
黄色 → 8
赤 → 10

※ケーブル色は私固有なので悪しからず


etcherではダメということか。

以前にも同じようなことをした覚えがある。

SDカードが問題ないことが確認できたので、pi3 に差して起動してみる。

ACT LEDの点灯状況からして起動しているように見えるが、
コンソールに何も表示されない。

電源投入後PWRが点灯、ACTが不規則に緑点滅して数秒でPWRのみ赤点灯となる。

pi2のLEDも同様なので、pi3も正常に起動していると思われる。

おそらくデフォルトではコンソールが有効でないのだろう。


HDMIケーブルでディスプレイをつないでみる。

やはり正常に起動していた。

家のルータにLANケーブルをつなぐとIPアドレスが取れていて、
名前解決もできる。

sshを有効にする

sudo raspi-config

5 Interfacing Options - P2 SSH

再起動

インターネットにつながりsshができれば後はどうにでもなる。







2019/04/28

Windowsの起動・終了時にバッチファイルを実行させて勤怠管理

以前イベントログを読んで勤怠管理をするというエントリを書いたが
https://monqy.blogspot.com/2019/04/powershell_16.html

もっと簡単な方法がある。

以下のような二つのバッチファイルを作る。


「hajimari.bat」
echo start:  %date% %time% >> c:\users\taro\working.txt

「owari.bat」
echo end:  %date% %time% >> c:\users\taro\working.txt

グループポリシーエディタを起動

「コマンドを指定して実行」で下記を実行

gpedit.msc


左側のツリーで「ローカルコンピューターポリシー」「コンピューターの構成」「Windowsの設定」「スクリプト(スタートアップ/シャットダウン)」

を選択

右側の「スタートアップ」、「シャットダウン」をそれぞれダブルクリックし、
「追加...」をクリックしてバッチファイルを選択する。


コマンドプロンプトで下記コマンドを実行してポリシーの変更を反映させる。

gpupdate

バッチファイルのリダイレクト先はパスをつけないと、
バッチファイルのある場所ではなく、変な場所に保存される。
しかも、スタートアップとシャットダウンで別々に。

同じファイルに書き込みたいので、パスをつける。

こっちのほうが簡単。

「勤怠管理」といってもあくまでも簡易的なものなので。

細かいことは言わないでください。






2019/04/27

HTTP通信が安全でない理由


前回のエントリ

で実行したcgiが、もしHTTP通信で実行されていたら、
その通信をキャプチャすると下記のように入力したパスワードがわかる。


これが「暗号化されていない通信(HTTP)は危険」である理由である。

「キャプチャすると通信内容がわかる」ということは、その通信が経由する装置、回線、サーバ、等にアクセスできる人々すべてに内容を把握されるおそれがあるということである。

これを防ぐためには、「暗号化」が必要である。

暗号化していない通信であれば、パスワードそのものでなくハッシュを保存していたとしても、サーバ管理者もユーザが入力したパスワードを知ることができてしまう。


「パスワードを暗号化して保存する」ではダメな理由

perlでcgiを作ってみた。

ハッシュアルゴリズムにmd5を使っているが、
md5もはや安全ではないらしい。

このスクリプトはあくまでもハッシュを作ることを実感するためのサンプルである。
実際にはより安全なアルゴリズムに変える必要がある。



ユーザ名とパスワードを登録するcgi

register_pwd.cgi
---
#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "register password";
print '<form action="save_pwd.cgi" method="post">';
print '<p>';
print 'name:<input type="text" name="username" size="20">';
print '</p>';
print '<p>';
print 'password:<input type="text" name="password" size="20">';
print '</p>';
print '<p>';
print '<input type="submit" value="送信"><input type="reset" value="リセット">';
print '</p>';
print '</form>';
---


ユーザ名とパスワードのソルト付きハッシュを保存するcgi

users.txt というファイルに保存する。
すでに同じユーザ名が登録されているかのチェックもする。

ソルトの作り方がアレかもしれないが、
ここもサンプルなので見逃してほしい。


save_pwd.cgi
---
#!/usr/bin/perl

use Digest::MD5 qw(md5 md5_hex);

if ($ENV{'REQUEST_METHOD'} eq 'POST') {
  read(STDIN, $alldata, $ENV{'CONTENT_LENGTH'});
} else {
  $alldata = $ENV{'QUERY_STRING'};
}
foreach $data (split(/&/, $alldata)) {
  ($key, $value) = split(/=/, $data);

  $value =~ s/\+/ /g;
  $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
  $value =~ s/\t//g;

  $in{"$key"} = $value;
}

print "Content-Type: text/html; charset=Shift_JIS\n\n";
print "<html>\n";
print "<head><title>save password</title></head>\n";
print "<body>\n";

$my_user = $in{'username'};
$my_pwd = $in{'password'};

print "<p>username: $my_user</p>\n";
print "<p>password: $my_pwd</p>\n";

$digest = md5_hex($my_pwd);

print "md5 digest:".$digest."<br>";

$num = int(rand(1000000));

$salt = '$1$'.$num.'$';

print '<br>'.$salt.'<br>';

$crypt = crypt($my_pwd, $salt);

print "md5 with salt: ".$crypt."<br>";

print '<br>';

$count = 0;

open (IN, "users.txt");
while(<IN>){
        @tmp_user = split(/,/,$_);
        if (@tmp_user[0] eq $my_user){
                $count++;
        }
}

if ($count > 0 ) {
        print("username ".$my_user." has been already registered.<br>");
}else {
        open (OUT,">> users.txt");
        print OUT $my_user.",".$crypt."\n";
        close (OUT);
        print 'registered<br>';
        print '<a href="/index.html">'.'index.html'.'</a>';
}
print "</body>\n";
print "</html>\n";

exit;
---

パスワード確認cgi
(ログインのイメージ)

kensho_pwd.cgi
---
#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "verify password";
print '<form action="hash_kensho.cgi" method="post">';
print '<p>';
print 'name:<input type="text" name="username" size="20">';
print '</p>';
print '<p>';
print 'password:<input type="text" name="password" size="20">';
print '</p>';
print '<p>';
print '<input type="submit" value="送信"><input type="reset" value="リセット">';
print '</p>';
print '</form>';

exit;
---

ユーザの入力したユーザ名を保存したユーザ名から検索し、
保存されているソルトを付加してハッシュを計算し、保存されているハッシュと比較する

users.txtを読んで、同じユーザ名を探す。

実際はDBを使うなどするのだろうがこれは実験なので。

---
#!/usr/bin/perl

use Digest::MD5 qw(md5 md5_hex);

if ($ENV{'REQUEST_METHOD'} eq 'POST') {
  read(STDIN, $alldata, $ENV{'CONTENT_LENGTH'});
} else {
  $alldata = $ENV{'QUERY_STRING'};
}
foreach $data (split(/&/, $alldata)) {
  ($key, $value) = split(/=/, $data);

  $value =~ s/\+/ /g;
  $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
  $value =~ s/\t//g;

  $in{"$key"} = $value;
}

print "Content-Type: text/html; charset=utf-8\n\n";
print "<html>\n";
print "<head><title>password kensho</title></head>\n";
print "<body>\n";

$my_user = $in{'username'};
$my_pwd = $in{'password'};

print "username: $my_user<br>";
print "password: $my_pwd<br>";

open (IN,"users.txt");
#$record = <IN>;

$count=0;
$tmp_user="";
$tmp_digest="";

while(<IN>){
        @tmp = split(/,/,$_);
        if(@tmp[0] eq $my_user){
                $count++;
                $tmp_user = @tmp[0];
                $tmp_digest = @tmp[1];
        }
}
close (IN);

if($count<1){
        print "unknown username<br>";
        exit;
}

chomp($tmp_digest);


$salt = substr($tmp_digest,0,10);

$crypt = crypt($my_pwd, $salt);

print "md5 with salt: ".$crypt."<br>";
print "saved md5    : ".$tmp_digest."<br>";

if ($tmp_digest eq $crypt ) {
        print "Welcome, ".$my_user." !!<br>";
}else{
        print "Incorrect password<br>";
}

print "</body>\n";
print "</html>\n";

exit;
---

実行例

ユーザ名とパスワードを入力

保存されたソルト付きハッシュ
上に表示されているのは、ソルトなしのハッシュ
$で挟まれているのがソルト。
ハッシュの先頭に付加されるのでそれを含めて保存する。


登録したユーザ名とパスワードを入力する


保存してあるハッシュと一致した。


違うユーザ名で同じパスワードを登録してみる。

jiro, password



ソルトなしのハッシュは、taro, passwordの場合と全く同じ

ソルト付きのハッシュは、同じパスワードから生成したものであっても、
taroのものと異なる。





「暗号化して保存する」と「ハッシュを保存する」は違う。

暗号化したデータは、しかるべき操作をすると元のデータが復号できるが、
ハッシュはできない。

もし悪意あるものがサーバにアクセスしてハッシュを入手したとしても、
できることは、元のデータを推測して、
そのデータのハッシュを計算して比較し、同じであるかどうかを確認することだけである。

ハッシュでできることは、元のデータを復元してそれを入力された値と比較することではない。
比較するのはあくまでもハッシュであり、
サーバ管理者でさえも、ユーザのパスワードが何かはわからないのである。
もしパスワードを暗号化して保存し、それを復号化して比較するのであれば、
サーバ管理者がユーザのパスワードを知ってしまうことになる。

また、もし暗号化されたパスワードを保存しているサーバにアクセスされてしまったら、
高確率で復号化する仕組みにもアクセスされるだろう。

そのことを知らなかったら、「暗号化して保存したってどうせ復号するんだから意味はない」と考えて、平文で保存してしまうのではないだろうか?

だから、「暗号化して保存する」と「ハッシュを保存する」の区別は重要なのである。


・・・と思ったのだが、記事を一回公開してから気づいた。
ハッシュを計算するときに、見ようと思えばユーザのパスワードは知ることができる。

先ほどのperlのスクリプトで、ユーザがフォームに入力したパスワードをprintで表示すればよい。
(実際、している。もちろん、本当に実装するときにそんなことはしないが。)

フォームにパスワードを入力してサーバに送信する前に、ハッシュを計算して、その結果を送信しなければいけないのか?

そんなことは不可能か?

通信が暗号化されていればサーバに到達するまではパスワードは他者に知られないが、

ハッシュ計算時には知ることができる。

それを保存さえしなければいいのか?


またこの時、もしソルトを付加せずに計算したハッシュを保存したらどうなるか。

1万人のユーザのハッシュの中で、同じものが50件あったとすると、
そのパスワードは誰でも思いつくパスワードであると想像でき、元のデータの推測が容易になってしまう。