カテゴリー「FreeBSD」の38件の投稿

June 02, 2012

複数のディレクトリ下にあるファイルすべての MD5 ハッシュをとって,一致してるファイルを表示する perl script

極めて久々.
先日,別の拠点にあるファイルサーバーのデータを持ってきてこちらのファイルサーバーにコピーしようとしたのだけども,どうも中身が同じファイルが多いようなので,ファイルの MD5 ハッシュとって同じだったら同一内容のファイルだとして消そうと思った次第.
スクリプトの内容は下記ですが,このファイルを

$ ./filecomp.pl /hoge /booboo 

みたいに実行すると,/hoge も /booboo にもある(そのサブディレクトリも含む)ファイルも全部ごっちゃにして MD5 ハッシュ計算して一致したファイル名をフルパスで表示します.
例えば /hoge/ahhan.txt と /hoge/uhhun.txt, /booboo/ahhan.txt が一致してると,順番はどうなるかわかりませんが,
/hoge/ahhan.txt(タブ区切り)/hoge/uhhun.txt
(タブ二個)/booboo/ahhan.txt

みたいな感じで二個以上ダブってたら三個目以上は二個タブの後ろにどんどんリストアップされる格好.
コードは以下.

#!/usr/bin/perl

use strict;
use warnings;

use File::Util;
use Digest::MD5;

my $file_obj = File::Util->new(max_dives => 50000);
my @total_filelist;
my %key_filename;
my %key_hashed;
my $flag_overtwo;

foreach (@ARGV) {
my @temp_filelist = $file_obj->list_dir($_,'--files-only','--recurse');
push @total_filelist, @temp_filelist;
}

foreach (@total_filelist) {
open(TARGET, $_) || die "Can't open file, name: $_ :$!";
binmode(TARGET);
$key_filename{$_} = Digest::MD5->new->addfile(*TARGET)->hexdigest;
close(TARGET);
}

foreach (sort{$key_filename{$b} cmp $key_filename{$a}} keys %key_filename) {
if($key_hashed{$key_filename{$_}}) {
if(! defined($flag_overtwo)) {
print "$key_hashed{$key_filename{$_}} \t $_\n";
$flag_overtwo = "deja été";
}
else {
print "\t\t $_\n";
}
}else {
undef($flag_overtwo);
}
$key_hashed{$key_filename{$_}} = $_;
}


コード内で File::Util のオブジェクト作るときの max_dives ってのは最大で何個のファイル(ディレクトリも含む?)を“掘る”かっていう感じの数字でデフォルトがかなり小さくて小生の目的では全然ダメなので大きくしてます.cpan 見てデフォルトで行くなり増やすなり適宜よろしくお願いします.
最初のループでは,引数で指定されたディレクトリ以下のファイルをぶりぶり調べてフルパスで配列 total_filelist にぶち込む.次のループでは total_filelist にある全ファイルの MD5 ハッシュを計算して,ファイル名をキー,ハッシュ値を値とするハッシュ:key_filename にぶち込む,最後のループでは %key_file ハッシュを値(すなわち MD5 ハッシュ値)でソートして,MD5 ハッシュ値をキー,フルパスのファイル名を値とするハッシュ:key_hashed を作る(すなわち reverse(%hash) をいちいちやる)ループなのですが,MD5 ハッシュ値であるキーがダブルと上書きされていくので,すでにそのキーがあると標準出力に書きだすし,一回そのキーで書き出すとフラグ:flag_overtwo がたってそれ以降の同じ MD5 ハッシュ値のダブりの処理ではタブ二個と最新の値(ファイルフルパス名)が標準出力に出てくるという話.
壮大な大車輪の大再発明のような気もしますが,その場合こっそり指摘していただければ幸い.
現在はディレクトリ自体一緒だと判断するのに,このスクリプトで出てきたディレクトリを

$ cd /ahhan
$ find . -type f > ahhan
$ cd /uhhun
$ find . -type f > uhhun
$ wc -l ahhan uhhun

あるいは

$ diff ahhan uhhun

みたいな感じで確認してディレクトリごと片方を削除したりしてます.それさえもスクリプトに含めるのは一瞬考えましたが,ファイラーもどきを書いてしまうのは too much でもあり,僭越でもあり,汎用性を考えて上のままで使ってます.
経済的メモリーの使い方とかマシンパワーとかは考慮していませんので,適宜改善の上色々していただければ幸いです.
BSD マシン上では動いてますけど,active perl 上ではどうなのでしょう?
はるか前にもエントリーあげましたが,MD5 ハッシュを cpan のモジュールで云々するというのはゆーすけべーさんのエントリー“いかにして効率よく大量のおっぱい画像をダウンロードするか”のコードの最後のほうに触発されたということが大きいです(最近は改良版とか ruby 版とか出ているといううわさですが)

| | Comments (34) | TrackBack (0)

December 13, 2009

diablo-jdk が FreeBSD 8.0 で,そのままでは動かない件

Portsnap していたら, jakarta-tomcat がなくなって,origin not found とか言っている.ネットで調べるとどうも jakarta-tomcat の port は moved になっていて,何やら jakarta プロジェクトを卒業(?)したみたい.
しようがないので, www/tomcat55 をインストール.それでも動かない.環境変数を設定したりなんなりしたがやはりダメ.
色々みていくうちに, /usr/local/tomcat5.5/log ってディレクトリ発見.そこの catalina.out ってファイルに

Error occurred during initialization of VM
Unable to load ZIP library: /usr/local/diablo-jdk1.6.0/jre/lib/i386/libzip.so

って書いてあるので,これら全部 google のクエリーに入れて検索.
そしたらこんなページが出てきた.曰く, jdk がうまくインストールできない話のようである.
解決案として
/etc/libmap.conf に
[/usr/local/diablo-jdk1.6.0/]
libz.so.4 libz.so.5

と書けばうまくいくと書いてある.
早速そのファイルを使って,
> cd /usr/ports/java/diablo-jdk16
> sudo make deinstall
> sudo make install clean

としてみた.で,再起動.
あら不思議,変な目のネコのページが出てきた.これで一安心.

| | Comments (194) | TrackBack (0)

October 11, 2009

FFmpeg ,おお FFmpeg

 筆者の FreeBSD マシンには数千曲の AAC エンコードしたファイルが入っているが,どうも AAC エンコードというのは環境依存というか,要は普通の人のパソコンで iTunes が入っていないと聞くことができなかったりと面倒くさい.ならば,少々不本意であるが, mp3 にしてみようと思った.
 AAC から mp3 の変換には FFmpeg というプログラムがよさげ.インストールしてみた.

> cd /usr/ports/multimedia/ffmpeg
> sudo make install clean

 iTunes で CD をリッピングすると [Artist name]/[Album name]/ というディレクトリ名にバラバラに入るので,以下のスクリプトで変換を試みた.
#!/bin/sh

find /usr/music -type f -name '*.m4a' -print |
while read music_path
do
store_file=`echo $music_path | sed -e s#^/usr/music#/usr/store_file# -e s#\\\.m4a$#\\\.mp3#`
ffmpeg -i "$music_path" "$store_file"
done

なんか,変換先のディレクトリが作られていないとエラーになるみたいなので,変換元のディレクトリをまるごとコピーして,
find /usr/store_file -type f -name '*.*' -exec rm {} \;
でファイルだけ消す.
実行すると,当たり前だが argument too long .
xargs の使い方がいまいちよくわからないので変換元のファイル情報を find でファイルに全部書き出す.
> find /usr/music -type f -name '*.m4a' -print > aac_list

スクリプトを
#!/bin/sh

while read music_path
do
store_file=`echo $music_path | sed -e s#^/usr/music#/usr/store_file# -e s#\\\.m4a$#\\\.mp3#`
ffmpeg -i "$music_path" "$store_file"
done <$1

のようにして,
sh script_name aac_list
で実行.
...数十曲ぐらいしか変換しない...
ググってみたら FFmpeg のプロセスが動いているうちは次の変換プロセスを動かさないようにする,みたいな tip がのっていたので,素直にそれにしたがった.
#!/bin/sh

while read music_path
do
cmd=`ps -ax | grep ffmpeg`
while [ $cmd != "" ]
sleep 10 ;
echo waiting
cmd=`ps -ax | grep ffmpeg`
done
store_file=`echo $music_path | sed -e s#^/usr/music#/usr/store_file# -e s#\\\.m4a$#\\\.mp3#`
ffmpeg -i "$music_path" "$store_file"
done <$1

実行.
やっぱりうまくいかない.
またググる.
Perl module で p5-FFmpeg-Command ってのを見つける.早速,
> cd /usr/ports/multimedia/p5-FFmpeg-Command
> sudo make install clean

で,インストール.
 > man FFmpeg::Command
でサンプルファイルを見つける.最初の2行をいれてみる.
#!/usr/bin/perl
use strict;
use FFmpeg::Command;

my $ffmpeg = FFmpeg::Command->new('/usr/local/bin/ffmpeg');

実行.
Syntax error: Bad fd number
Can't find ffmpeg command. at test_perl_ffmpeg line 5

ffmpeg はちゃんとある.
 > which ffmpeg
/usr/local/bin/ffmpeg

new の引数は省略できるというので () にしてみるが,結果は同じ.

仕方ないので FreeBSD はあきらめる.
ノートに Ubuntu が入っているのだが, Ubuntu に soundconverter というのがある.これと, gstreamer-plugin-ugly とか bad とか嫌な名前のものをかたっぱしから入れる.あとはほかのサイトに書いてあるとおり.大体一昼夜かかった.

しかし, ffmpeg についてはどこか解説ないかな.

| | Comments (112) | TrackBack (0)

October 09, 2009

VIA の ARTiGO で FreeBSD 手のひらサーバー

さて,今更ながらだが, VIA の ARTiGO を購入した. ARTiGO とはいっても, VIA のサイトに載っている ARTiGO A1000 とは微妙に違う.日本PCサービスという会社がマイナーアップデートした ARTiGO-PC-15 というモデルで CPU が 1.0 GHz から 1.5 GHz に,また, VIA のものだと HDD とメモリーは自分で積まなければならないが,本品は 80 GB の HDD と 1 GB のメモリーを積んでいるという商品だ.できれば CPU だけグレードアップしてあとは自分で選択できる方がよかったが,まあ,これはこれでよし.クレバリーで \44,940- (今見たらもう売ってない).これを選んだのは,手のひらサーバーを構築するとして,小型ベアボーンだと FitPC などという競合商品があるのだが, ARTiGO にはシリアルポートがついている.これが決め手となった. HDD は HGST の 500 GB の HDD をテクノハウス東映で購入\7,760.


Artigo_01家についてバラしてみた.まずはメインボードまわり.写真はメインボードと 2.5" HDD ホルダーとファン・ヒートシンクがくっついたものだが,正直ファンレスだと思っていた( A1000 はファンレス仕様)のでかなりがっかり.


Artigo_02次が 500 GB の HDD .我が家でもっとも記憶容量の大きい HDD . 2.5" で 500 GB とは時代も変わったものだ.


Artigo_03次が電源基板. ARTiGO は AC アダプターだが,ここで 12 V までは落とさないらしい. AC アダプターは電源を入れると火花が出たりとちょっとなにそれこわい.


Artigo_04組み立ては,まずメインボードについている HDD ブラケットに 2.5" HDD をネジ止めする.


Artigo_05筐体はこんな感じ.


Artigo_06ネジ止めしたメインボード + HDD の HDD に SATA の電源 + 信号ケーブルを接続しておく.


Artigo_07このセットを筐体にネジ止めする.筆者は元エンジニアで組み立てにはそれなりの自信があったが,これは苦戦した.筐体が小さいので基板が思うところに寄らない.


Artigo_08フロントパネルからの端子をメインボードに接続する.添付のマニュアルでは分かりにくい. ARTiGO の公式サイトに行って自分の見やすい図を見つけよう.


Artigo_09次に電源ボードを筐体に固定する.この時 SATA まわりのケーブルを電源ボードに巻きつける加減で固定するのだが,電源ボードを固定するにはボードをやや右寄りに押し付けて固定しなければならない.この時,押し付けすぎると巻きつけた SATA ケーブルが抜けてしまって,あとで HDD が認識されなくなる.ここも狭い筐体ならではの試練だ.


Artigo_10電源ボードを固定したら, SATA まわりのケーブルの反対端の接続.メインボードの電源の接続などを行う.もう配線がぐしゃぐしゃだ.


Artigo_11一応,内側の配線が完了したところ.まだケーブルが整頓していないが無理やり蓋を閉める予定.


Artigo_12さて,懸案のシリアルポートだが,予想をはるかに上回るアクセスのしにくさ.シリアルポートの端子は, CPU ファンが回り込んだところと, VGA 端子に囲まれたくぼみの底にある.このため,筐体を組み立ててからシリアルポートの配線を引き出すのは至難の技だ.ここはなんともがっかり.


Artigo_13組み立てたところ.やっぱり小さい.ヘッドレスにするにはもってこいと言えよう.


Artigo_14我が住処のデジモノ置き場に据えた.上はまだ出たての 5 ポートスイッチ(一万円ぐらいした).後ろにはアトミックのスラロームの板が見えるがもうでぶんちょでスキー自体できない. ARTiGOはパワー LED が高輝度の青色 LED でやたら明るい.ビニールテープを三重に張ったがまだ明るい.また, ARTiGO のファンは 4500 rpm とかなり高回転,なかなか音が大きい.と言うわけで,家人の反応は悪く.連続運転は危機的状態にある.


さて,この PC に FreeBSD を入れて PC サーバーにしようとした.結果的にはうまく行ったが,以下注意点.


BIOS は最初から "Halt on: All but keyboard" になっている.このため,筐体にシリアルポートを接続してインストール作業に入れる.


と,思いきや, BIOS のブートメニューにひとくせ見られる.普通だったらブートの順番があって, CD-ROM, USB device, primary HDD, network boot という順番になっていると思うのだが, ARTiGO は違う.外部媒体( USB接続媒体, pxe ブート)のみか,内部 HDD ブートを排他的にしか選べない.だから, HDD に OS をインストールしたあとでも, BIOS 設定を戻さないと pxe ブートまで行って頑として HDD からはブートしない.したがって, OS インストール時はまず外部媒体のブートを選択してインストールを行い,終了したら BIOS を HDD ブートに戻してやる必要がある.


以上, VIA ARTiGO のセッテイングレポート.ずいぶん前から欲しかったものなので,手に入れてかなり満足.あとは耐久性.しばらく走らせて火が出ないかチェックする.

| | Comments (25) | TrackBack (0)

August 29, 2009

Subversion の portupgrade で mod_dav が二回 load されるようになってしまう

表題の通りなのだが, Apache22 で mod_dav を使えるように設定しておくと, Subversion のアップデートの時, httpd.conf の LoadModule のところに, mod_dav が二回定義され,そのため Apache のスタート時に 2回目の mod_dav の宣言以降の LoadModule されるモジュールが読み込まれないことが 2回起こった(何か日本語が変)


httpd.conf では宣言するモジュールの順番によってはモジュールがひいては Apache が動かないという事態が発生するらしい.ここで,筆者の httpd.conf のLoadModule 部分を晒しておく.

LoadModule authn_file_module libexec/apache22/mod_authn_file.so
LoadModule authn_dbm_module libexec/apache22/mod_authn_dbm.so
LoadModule authn_anon_module libexec/apache22/mod_authn_anon.so
LoadModule authn_default_module libexec/apache22/mod_authn_default.so
LoadModule authn_alias_module libexec/apache22/mod_authn_alias.so
LoadModule authz_host_module libexec/apache22/mod_authz_host.so
LoadModule authz_groupfile_module libexec/apache22/mod_authz_groupfile.so
LoadModule authz_user_module libexec/apache22/mod_authz_user.so
LoadModule authz_dbm_module libexec/apache22/mod_authz_dbm.so
LoadModule authz_owner_module libexec/apache22/mod_authz_owner.so
LoadModule authz_default_module libexec/apache22/mod_authz_default.so
LoadModule auth_basic_module libexec/apache22/mod_auth_basic.so
LoadModule auth_digest_module libexec/apache22/mod_auth_digest.so
LoadModule file_cache_module libexec/apache22/mod_file_cache.so
LoadModule cache_module libexec/apache22/mod_cache.so
LoadModule disk_cache_module libexec/apache22/mod_disk_cache.so
LoadModule dumpio_module libexec/apache22/mod_dumpio.so
LoadModule include_module libexec/apache22/mod_include.so
LoadModule filter_module libexec/apache22/mod_filter.so
LoadModule charset_lite_module libexec/apache22/mod_charset_lite.so
LoadModule deflate_module libexec/apache22/mod_deflate.so
LoadModule ldap_module libexec/apache22/mod_ldap.so
LoadModule log_config_module libexec/apache22/mod_log_config.so
LoadModule logio_module libexec/apache22/mod_logio.so
LoadModule env_module libexec/apache22/mod_env.so
LoadModule mime_magic_module libexec/apache22/mod_mime_magic.so
LoadModule cern_meta_module libexec/apache22/mod_cern_meta.so
LoadModule expires_module libexec/apache22/mod_expires.so
LoadModule headers_module libexec/apache22/mod_headers.so
LoadModule usertrack_module libexec/apache22/mod_usertrack.so
LoadModule unique_id_module libexec/apache22/mod_unique_id.so
LoadModule setenvif_module libexec/apache22/mod_setenvif.so
LoadModule version_module libexec/apache22/mod_version.so
LoadModule ssl_module libexec/apache22/mod_ssl.so
LoadModule mime_module libexec/apache22/mod_mime.so
LoadModule dav_module libexec/apache22/mod_dav.so
LoadModule status_module libexec/apache22/mod_status.so
LoadModule autoindex_module libexec/apache22/mod_autoindex.so
LoadModule asis_module libexec/apache22/mod_asis.so
LoadModule info_module libexec/apache22/mod_info.so
LoadModule cgi_module libexec/apache22/mod_cgi.so
LoadModule dav_fs_module libexec/apache22/mod_dav_fs.so
LoadModule vhost_alias_module libexec/apache22/mod_vhost_alias.so
LoadModule negotiation_module libexec/apache22/mod_negotiation.so
LoadModule dir_module libexec/apache22/mod_dir.so
LoadModule imagemap_module libexec/apache22/mod_imagemap.so
LoadModule actions_module libexec/apache22/mod_actions.so
LoadModule speling_module libexec/apache22/mod_speling.so
LoadModule userdir_module libexec/apache22/mod_userdir.so
LoadModule alias_module libexec/apache22/mod_alias.so
LoadModule rewrite_module libexec/apache22/mod_rewrite.so
LoadModule php5_module libexec/apache22/libphp5.so
LoadModule proxy_module libexec/apache22/mod_proxy.so
LoadModule proxy_ajp_module libexec/apache22/mod_proxy_ajp.so
LoadModule dav_svn_module libexec/apache22/mod_dav_svn.so
LoadModule authz_svn_module libexec/apache22/mod_authz_svn.so

筆者の Apache はとてもメタボなので参考程度に...

| | Comments (53) | TrackBack (0)

August 13, 2009

ssh でのバックアップに四苦八苦(後編):改行コードと echo

前編の続き)
 ssh を使って,手元のマシンのバックアップをリモートに取ろうという作戦.ネットで調べた結果,以下のシェルスクリプトで実現可能とわかった.

#!/bin/sh

while read backup_path
do
    backup_file=`echo $backup_path | sed s,^/,, | sed s,/,_,g`'.tgz'
    tar zcvf $backup_file $backup_path
    file_list=${file_list}'|'$backup_file
done <$1
file_list=`echo $file_list | tr '|' '\n'`
printf "%s\n" $file_list > file_list
file_list=${file_list}'|file_list'
file_list=`echo $file_list | tr '|' '\n'`
echo $file_list | xargs -J % scp % username@hostname:/backup_path
echo $file_list | xargs rm
ファイル file_list は,リモートのマシンから手元のマシンにリストアするときに
> cat file_list | xargs ssh username@hostname " % cat % " | tar zxvf -
みたいにして実現可能かと思ったがそうはいかなかった. このスクリプトに,引数としてバックアップしたいディレクトリを一行ずつ入れたファイルをもってくれば,それらのディレクトリをバックアップしてくれる算段だ. ところで,このスパゲッティ・スクリプト,改行コードを入れるところがこれまたハイパー・スパゲッティなのだが,筆者はこれしか打開策がなかった.  たとえば,改行などを扱うときは printf を使えばいいとあるが,以下のシェルスクリプトで以下の出力
aaa
bbb
ccc
を期待してみる.
#!/bin/sh
a="aaa"
b="bbb"
c="ccc"
d=`printf "%s\n%s" $a $b`
d=`printf "%s\n%s" $d $c`
printf "%s\n" $d
しかしながら,このスクリプトの実行結果は
aaa
bbbccc
となり,二回目の改行の挿入がうまくいっていないことがわかる.  echo についても疑問がある. echo には二種類あり,builtin のコマンドと,/bin/echo と二種類あるらしい.筆者は普段 /bin/tcsh 使いだが(あんまり関係ないか),コマンドラインでどちらの echo を使っているか調べると
> which echo
echo: shell built-in command.
と builtin のコマンドを使っていることがわかる.だが,
#!/bin/sh
which echo
とやると,
/bin/echo
となる.これって当たり前なのだろうか?私には不思議.

| | Comments (182) | TrackBack (0)

August 11, 2009

mod_jk が使えなくなった. mod_proxy_ajp を使うらしい

 弊ブログでは Tomcat と Apache の連携が mod_jk (もっと正確に言えば mod_jk2 )で可能と書いている.筆者の経験では Apache 2.2 系でもある時点までは mod_jk2 で動いていた(確かに,多分).
 動かなくなって調べてみたのだが,どうやら mod_jk2 の代わりに mod_proxy_ajp を使うらしい.
 仕方ないので, Apache を作り直して入れ直した.
アンインストール.

> cd /usr/ports/www/apache22
> sudo make deinstall

mod_proxy_ajp を入れるように指示.
> sudo make config

で, "PROXY: Enable mod_proxy" と "PROXY_AJP: Enable mod_proxy_ajp" とある項目にチェックを入れる.
インストール.
 > sudo make install clean 

/usr/local/etc/apache22/httpd.conf に以下の二行が入っているか確認.というか入れる.
LoadModule proxy_module libexec/apache22/mod_proxy.so
LoadModule proxy_ajp_module libexec/apache22/mod_proxy_ajp.so

できあがったら, Tomcat のサンプルが動くか試してみる.
/usr/local/etc/apache22/Include/ajp.conf に以下を記述.
<Location /jsp-examples/>
ProxyPass ajp://localhost:8009/jsp-examples/
</Location>

<Location /servlets-examples/>
    ProxyPass ajp://localhost:8009/servlets-examples/
</Location>
これで,ブラウザーから http://hostname/jsp-examples/ とやって,サンプルが見えれば成功. あとはよしなに.

| | Comments (19) | TrackBack (0)

August 10, 2009

ssh でのバックアップに四苦八苦

 筆者は FreeBSD のマシンを LAN 内に置いていたので,バックアップは mount_smbfs で Samba マシンにとっていたのだが,先日の記事の通り,マシンを DMZ に移動した.使用しているルーター(PR-200NE)の仮想 DMZ だと,仮想 DMZ から LAN を見たときに NAT が入るらしいので, NetBIOS が通らない,したがって Samba サーバーに接続することができなくなってしまった.いちいち USB メモリーをさしてコピーするのも面倒くさいので困っていたところ, Sakura レンタルサーバーを契約していることを思い出したので,そのサーバーにバックアップすることにした.
 前にも紹介した, "FreeBSD Hacks" には,

> tar zcvf - /source_dir | ssh username@hostname "cat > /dest_dir/filename"

でバックアップすることができ,リストアは
> ssh username@hostname "cat /dest_dir/filename" | tar zxvf -

で可能と web に書いてあった.
 これではナニなので,生まれて初めて(笑)シェルスクリプトを書いてみた.まず,バックアップするディレクトリを列挙したファイルを用意する.
> cat path_to_backup
/etc
/usr/local/etc
/usr/src/sys/i386/conf

シェルスクリプトは以下
> cat my_backup.sh
#!/bin/sh

while read backup_path
do
    tar zcvf - $backup_path |\
        ssh username@hostname "cat >\
        dest_dir/`echo $backup_path | sed s,^/,, | sed s,/,_,g`.tgz"
done <$1
これを
> ./my_backup.sh path_to_backup
と実行すると,リモートホストの dest_dir に, etc.tgz , usr_local_etc.tgz と usr_src_sys_i386_conf.tgz ができる(本当は拡張子の前に `date +%d-%m-%y` としてタイムスタンプを入れたかったがうまくいかなかった).  が,しかしこれではファイルを一つ作るごとに ssh 接続するので,そのたびにパスワードをいれなければならない(鍵承認にすればいいという話もあるが).そこで,次のようにしてみた.
> cat my_backup2.sh
#!/bin/sh
echo "" > file_list
while read backup_path
do
    backup_file=`echo $backup_path | sed s,^/,, | sed s,/,_,g`'.tgz'
    tar zcvf $backup_file $backup_path
    echo $backup_file >> file_list
done <$1
cat file_list | xargs -J % scp % username@hostname:dest_dir
scp file_list username@hostname:dest_dir
cat file_list | xargs rm
rm file_list
このスクリプトを先の例のように実行してみると, scp でファイルを送り出すところまではいくのだが, xargs の EOF マーカーがおかしいらしく,入力待ちのまま止まってしまう. file_list というファイルを作るのではなく.
file_list=${file_list}${backup_file}"\n"
として,最後に変数 $file_list をファイル file_list に流し込もうとしたのだが,改行コードをどうしても出力することができずこのようになった.何かとてもスパゲッティコードだが,何とかなるものかならないものか.詳しい方いたら,請う情報. 後編に続く)

| | Comments (1) | TrackBack (0)

August 02, 2009

OpenLDAP を使った ProFTPD サーバーの構築

 FTP サーバーは,標準の ftpd もあるが,複雑な構成を目指す場合やや物足りない感じがある.と,いうわけで ProFTPD を使ってみた.また,ユーザー管理にシステムユーザーを追加したり,そうでなくて MySQL を使ったりする方法もあるが,ここは OpenLDAP を使ってみた.ある意味,ユーザー管理はすっきりしているといえばすっきりしている.まあ,要はかっこうつけである.
 LDAP については,スキーマだの,オブジェクトだの,エントリ・ DN と難解な用語が多く,概念をすべて理解するのも一苦労だが筆者は“入門LDAP/OpenLDAP ディレクトリサービス導入・運用ガイド”(デージーネット著,秀和システム)で一応の理解を得た.こういうものは習うより慣れろ的なところもあり,そういう意味ではこの本は実用を中心にツボを得た一冊だと思う.

* OpenLDAP のインストールと設定.
 インストール自体は簡単.

> cd /usr/ports/net/openldap24-server
> sudo make install

 設定の前に, LDAP のデータ構成を考えておかなければならない.今回は FTP ユーザーの管理だけなので以下のようになる.
以下は gihyo.jp の LDAP の解説(ここここ)を参考にした(というか,そのまま)
-+--- dc=subdomain,dc=yourdomain,dc=suffix
|
+-+- ou=FTPGroup,dc=subdomain,dc=yourdomain,dc=suffix
| +- cn=group1,ou=FTPGroup,dc=subdomain,dc=yourdomain,dc=suffix
| +- cn=group2,ou=FTPGroup,dc=subdomain,dc=yourdomain,dc=suffix
|
+-+- ou=FTPUser,dc=subdomain,dc=yourdomain,dc=suffix
+- cn=user1,ou=FTPUser,dc=subdomain,dc=yourdomain,dc=suffix
+- cn=user2,ou=FTPUser,dc=subdomain,dc=yourdomain,dc=suffix

ドメインの構成要素がいくつもある場合(aaa.bbb.cc.ddのような場合), dc はドメインコンポーネントの略なので dc=aaa,dc=bbb,dc=cc,dc=dd とずらずら並べていけばいいようである. localhost だけで運用する場合はあまり神経質に考える必要があるとも思えない.
 構成が決まったら /usr/local/etc/slap.conf を編集する. /usr/local/etc/slap.conf.default を雛形にして作るといい.変更点は以下.
/usr/local/etc/openldap

include         /usr/local/etc/openldap/schema/cosine.schema
include         /usr/local/etc/openldap/schema/inetorgperson.schema
include         /usr/local/etc/openldap/schema/nis.schema
suffix          "dc=subdomain,dc=yourdomain,dc=suffix"
rootdn          "cn=Manager,dc=subdomain,dc=yourdomain,dc=suffix"
rootpw          {SSHA}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
include で始まる行は,スキーマファイルの読み込みの定義.余計なファイルを読み込んでいるかもしれないが,今回の構成では,これらのものを読み込むこととした. suffix は先の計画表の各エントリの末尾を指定するもの, rootdn 及び rootpw はいわば LDAP 管理者の名前とパスワード.パスワードは平文で記入するのではなく.必ず
> slappasswd
コマンドで得られた出力を記入する.  一応,設定ファイルが完成したら
> sudo slaptest
で,チェックを行う.エラーがなければ, rc.conf に以下を追加してサーバーとして起動を行う.
/etc/rc.conf
slapd_enable="YES"
slapd_flags='-h "ldapi://%2fvar%2frun%2fopenldap%2fldapi/ ldap://0.0.0.0/"'
slapd_sockets="/var/run/openldap/ldapi"
 実際のデータ投入の前に,便利なツール phpldapadmin をインストールしておこう.
> cd /usr/ports/net/phpldapadmin
> sudo make install clean
これで投入データが一覧で一目であっているかわかるようになる. phpldapadmin の設定は, phpmyadmin (バックナンバー参照)に似ていて,また,設定方法はインストールの最後に標準出力に出てくるので必要あらば, script コマンドで出力をとっておこう.  では,データの投入だ.まず, group1 と user1 を投入する.次のファイルを作成しよう.group1の gid は 10000.user1の uid は 10000 で, user1 は group1 に属している設定.
user01.ldif
dn: dc=subdomain,dc=yourdomain,dc=suffix
objectClass: dcObject
objectClass: organization
dc: scosco
o: scosco
dn: ou=FTPGroup,dc=subdomain,dc=yourdomain,dc=suffix
objectClass: organizationalUnit
ou: FTPGroup
dn: ou=FTPUser,dc=subdomain,dc=yourdomain,dc=suffix
objectClass: organizationalUnit
ou: FTPUser
dn: cn=group1,ou=FTPGroup,dc=subdomain,dc=yourdomain,dc=suffix
objectClass: posixGroup
cn: group1
gidNumber: 10000
dn: cn=user1, ou=FTPUser,dc=subdomain,dc=yourdomain,dc=suffix
objectClass: posixAccount
objectClass: account
cn: user1
uid: user1
userPassword: {SSHA}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
homeDirectory: /usr/home/readonly
uidNumber: 10000
gidNumber: 10000
完成したら,
> ldapadd -x -D "cn=Manager,dc=subdomain,dc=yourdomain,dc=suffix" -W -f user01.ldif
で,rootdn のパスワードを入力して投入.成功したようなら, phpldapadmin で様子を見てみよう. organization は設定しないとはまるらしい.このユーザーは読み込み専用ユーザーの予定なので, /usr/home/readonly はただ作ればいい.オーナーは root:wheel にして,アクセス権は0644にでもしておこう.  うまくいったら,調子に乗って書き込みユーザーを作ろう.
user02.ldif
dn: cn=group2,ou=FTPGroup,dc=subdomain,dc=yourdomain,dc=suffix
objectClass: posixGroup
cn: group2
gidNumber: 10001
dn: cn=user2, ou=FTPUser,dc=subdomain,dc=yourdomain,dc=suffix
objectClass: posixAccount
objectClass: account
cn: user2
uid: user2
userPassword: {SSHA}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
homeDirectory: /usr/home/writable
uidNumber: 10001
gidNumber: 10001
投入
> ldapadd -x -D "cn=Manager,dc=subdomain,dc=yourdomain,dc=suffix" -W -f user02.ldif
成功したら, /usr/home/writable を作成.ユーザー名とグループ名はシステムに認識されていないので, uid と gid で指定する.すなわち:
> sudo mkdir /usr/home/writable
> sudo chown 10001:10001 /usr/home/writable

* ProFTPD のインストールと設定
 インストールはそのまま

> cd /usr/ports/ftp/proftpd
> sudo make install clean

 設定は /usr/local/etc/proftpd.conf.sample を参考に /usr/local/etc/proftpd.conf を作る.下記は, proftpd.conf.sample からの変更点と追加点.
/usr/local/etc/proftpd.conf

LDAPServer                      localhost
LDAPDoAuth         on "dc=subdomain,dc=yourdomain,dc=suffix" "(&(cn=%v)(objectClass=posixAccount))"
LDAPDoUIDLookups   on "ou=FTPUser,dc=subdomain,dc=yourdomain,dc=suffix"
LDAPDoGIDLookups   on "ou=FTPGroup,dc=subdomain,dc=yourdomain,dc=suffix"
DefaultRoot ~ !group2
AllowOverwrite                on
<Limit WRITE>
    Order allow,deny
    AllowUser user2
    DenyAll
</Limit>
TimesGMT off
SetEnv  TZ :/etc/localtime
DefaultRoot は, "~" と指定すると,ユーザーのホーム・ディレクトリより上位のディレクトリには移動できないことを示すが, "![group name]" で, [group name] グループに所属するユーザーはその制限を受けないことを指定している(ここで管理する単位がユーザーではなくグループであることに注意したい).したがって上記の例では, group2 に属する user2 はホームディレクトリだけでなく,どのディレクトリも移動できることになる. AllowOverwrite は上書きを許すかどうかであるが, user2 は書き込み可能ユーザーにしたいのでここは "on" にする.そして, <Limit WRITE> の指定は書き込みに関する制限の指定で, user2 は書き込み可能であるが,それ以外は拒否する記述である.そして,最後の2行はアップロードしたファイルのタイムスタンプやログのタイムスタンプが GMT ではなく, localtime を反映するための設定である.これで, /etc/rc.conf に
proftpd_enable="YES"
とすれば,基本的には設定完了なのであるが,ここで,パッシブモードの問題があるので以下にメモしておく.  Windows などでは ftp はデフォルトで“アクティブ”モードで動くらしいが, Gnome の Nautilus や gFTP などはデフォルトでパッシブモードで接続しようとする.そうなると,ファイアーウォールなどでポートを閉じていると外から来た FTP 接続要求に答えることができない.そこで,ファイアーウォールに穴を開けるわけだが, ProFTPD のデフォルトではポート範囲の設定がないので( 1024 から 65535 かな?)やたらめったらポートを開放しなくてはならない. ProFTPD には PassivePorts ディレクティブというのがあって,これで FTP を受けるポートの範囲を指定することができる.これを用いて, /usr/local/etc/proftpd.conf に,
PassivePorts 60000 65535        # These ports should be safe
とすれば,ポート範囲が 60000 番から 65535 番に制限できる.  それにしたがって,ファイアーウォールに穴を開けなければならないので(筆者が使っている) Packet Filter だと, /etc/pf.conf に
# accept passive FTP
pass in on $ext_if proto tcp from any to any port > 59999
とすれば,パッシブ接続を要求するクライアントからも通信することができる.

<参考> ProFTPD.org のディレクティブリスト(英語だが,日本語版より例などが豊富).

| | Comments (62) | TrackBack (0)

July 26, 2009

FreeBSD を DMZ においてみた. OpenBSD/Packet filter など

 特に意味はないのだけども,FreeBSDのマシンをDMZにおいて,外からもアクセスできるようにしてみた.
 構成は図の通り(Microsoft Visioで作成).LANには家庭のパソコンだのプリンターだのがおいてありそれらは 192.168.a.0/24の中にある.DMZは192.168.b.0/24.それぞれルーターのアドレスがあり,DNSサーバーの役割もしている.という感じ.Dmz_01
 まずはルーターの管理画面に入って設定をする.ここでは,フレッツで広く使われている,NTTのPR-200NEでその設定を見ていこう.
 2番目の図で,“高度な設定”でDMZの設定を行う.開放するポートが限定されているのならば,静的IPマスカレードでもいいのだがそれではサーバー公開の醍醐味に欠ける.ここは黙ってDMZの設定.Dmz_02
 3番目の図がその設定.例えば,LANが192.168.10.0/24,DMZを192.168.11.0/24,DMZサーバーアドレスを192.168.11.100にするのであれば,図の最初の赤丸,2番目の赤丸にチェックを入れ,3番目の赤丸に "192.168.11.100" と入力する.そののちに,仮想DMZ側 ネットワーク,“自動設定”のボタンを押すと,DMZ側のルーターのアドレスが決定される.すなわち,デフォルト・ゲートウェイである.メモしておこう.これらの変更を /etc/rc.conf や /etc/resolv.conf に施さなければならない.Dmz_03

* dyndnsとddclient
** dyndns
次は,外からアクセスするのにいちいちIPアドレスを確認してアクセスするのはダサい.ここはやはりdynamic DNSだ.DDNSのサービスをしているところはもはやさまざまだが,一応老舗っぽいDynDNS.comを選んだ.ここは,ログインして [Support] - [Tools] - Update Client Configurator といくと,ddclient の configuration file を作ってくれるのでとてもいい.まずは,アカウントとサブドメインを一つゲット.

** ddclient
インストールした覚えがないが,多分,portsからdynamic DNSのclientをインストール.

> cd /usr/ports/dns/ddclient
> sudo make install clean

ddclient は DynDNS.com でもサポートしているので心強い. config file は上に述べた sample を /usr/local/etc/ddclient.conf に流し込んでもいいのだけど,一応, /usr/local/etc/ddclient.conf.sample と見比べながら作成した.
/usr/local/etc/ddclient.conf

daemon=300                              # check every 300 seconds
syslog=yes                              # log update msgs to syslog
mail=root                               # mail all msgs to root
mail-failure=あなたのメールアドレス        # mail failed update msgs to root
pid=/var/run/ddclient.pid               # record PID in file.
ssl=yes                                 # use ssl-support.  Works with
                                        # ssl-library
## To obtain an IP address from Web status page (using the proxy if defined)
use=web, web=checkip.dyndns.org/, web-skip='IP Address' # found after IP Address
login=ログイン名                                      # default login
password=パスワード                               # default password
##
## dyndns.org dynamic addresses
##
## (supports variables: wildcard,mx,backupmx)
##
server=members.dyndns.org,              \
protocol=dyndns2                        \
(あなたの取得したdyndnsのサブドメイン名を書く)
これでいいと思う. あとは,/etc/rc.conf に
ddclient_enable="YES"
と書けば終了(たぶん).これでかっこいいサーバーが立てられる.orega-osama.dyndns.orgとかやってください.

* 証明書がないとsshできないようにする
これで,サーバーは公開されているわけだが,ssh の port が開いている.筆者のサーバーはパスワードがやや脆弱だ.少しでもリスクを減らすために,証明書を持っていない client からは ssh login できないようにしてみた.

** ssh ホスト側設定変更
まず, /etc/ssh/sshd_config

PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys

# Change to yes to enable built-in password authentication.
PasswordAuthentication no
PermitEmptyPasswords no
# Change to no to disable PAM authentication
ChallengeResponseAuthentication no

** 鍵を作って置く
鍵の作成

> ssh-keygen -t dsa

dsa と rsa はどっちがいいのか正直よく分からない.
dsa だと,多分 ~/.ssh に id_dsa と id_dsa.pub というファイルが配置される.(されなければ,できた鍵ファイルを ~/.ssh にコピー)
さらに, id_dsa.pub の内容を authorized_keys に流し込む,すなわち,
> cat id_dsa.pub >> authorized_keys

これで,ホスト側の設定は(たぶん)おわり.

** Client側の設定
*** Ubuntuの場合
 結論から言えば,ホスト側の先に3つ作ったファイルを Ubuntu client の作業ホームディレクトリの ~/.ssh にコピーすればOK.公開鍵は必要ないような気がするが.何か動かなかったので全部コピーした.Ubuntu は一回 ssh ログインに成功すると次からはパスフレーズもきいてこなくなるのでちょっと不安.ノートパソコンを落とすわけにはいかなくなった.
*** Windowsの場合(PuTTY)
 秘密鍵をPuTTYの作業ディレクトリにコピーして,指定すればいいのかと思ったが,もう一段作業が必要.
id_dsa を Windows にコピーしてそれを鍵としても動かない.動かすためには puttygen.exe (PuTTY Key Generator) にロードして変換が必要なようだ.終わったら, PuTTY を起動して,[接続] - [SSH] - [認証] でプライベートキーファイルを指定する.

これで,再起動すれば,ssh のセキュリティは幾分向上する.

* Firewall (OpenBSD/Packet Filter)
とは言っても,ネットにホストを置いた以上,いわれのない攻撃や将来のことを考えておくと何らかの防御が必要.幾分考えた後,ファイアーウォールは OpenBSD/Packet filter でいくことに決定.勝因は設定ファイルのわかりやすさ.
帯域制御とかからんでくると,カーネルの再構築が必要のようだが,今回はなし.Ports collection のインストールもない.
まずは,設定ファイル.フィルターの定義はいろいろと悩んだのだが,後藤大地さんの FreeBSD ビギナーズ・バイブルの設定をほんのアレンジして使ってみた.以下は設定ファイルの内容.

/etc/pf.conf

# Macros
ext_if="fxp0"
tcp_services = "{ 21, ssh, https, http, 8180 }"
# Tables
table <priv_nets> { 127.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8, !192.168.0.0/23 }
table <block_ur_global_nets> { 169.253.0.0/16, 192.0.2.0/24, 244.0.0.0/4, 240.0.0.0/4 }
set block-policy drop
set loginterface $ext_if
set skip on lo
scrub in
scrub out all random-id
# Filter rules
# default block all
block log all
# full allow to local loop back
pass quick on lo0 all
# From WAN to Server settings
# block local ip
block drop in quick on $ext_if from <priv_nets> to any
# block unreachable global nets
block drop in quick on $ext_if from <block_ur_global_nets> to any
# accept all packets after connected
pass in on $ext_if inet proto tcp from any to $ext_if flags A/A
# accept dns
pass in on $ext_if inet proto udp from any to $ext_if port domain
pass in on $ext_if inet proto tcp from any to $ext_if port domain flags S/SA
# accept dns request result
pass in on $ext_if inet proto udp from any port domain to $ext_if
# accept ntp request result
pass in on $ext_if inet proto udp from any port ntp to $ext_if
# accept tcp services
pass in on $ext_if inet proto tcp from any to $ext_if port $tcp_services flags S/SA
# pass icmp
pass in on $ext_if inet proto icmp all icmp-type { 0, 3 }
# From Server to WAN settings
# block local ip
block drop out quick on $ext_if from any to <priv_nets>
# block unreachable global nets
block drop out quick on $ext_if from any to <block_ur_global_nets>
# accept all packets after connected
pass out on $ext_if inet proto tcp from $ext_if to any flags A/A
# accept all packets to connect
pass out on $ext_if inet proto tcp from $ext_if to any flags S/SA
# accept dns
pass out on $ext_if inet proto udp from $ext_if to any port domain
# accept dns request result
pass out on $ext_if inet proto udp from $ext_if port domain to any
# accept ntp
pass out on $ext_if inet proto udp from $ext_if port ntp to any
# accept ping
pass out on $ext_if inet proto icmp all icmp-type 8
ネットワークアドレス範囲はちょっと凝った記述をしようとするなら,テーブルを使う方が賢明なようだ.例えば,192.168.10.0/24の192.168.10.10だけをパスするという場合,
pass .... from { 192.168.10.0/24, !192.168.10.10 } to ....
というのはいけないらしい(OpenBSD/Packet Filterのページより). 今回少し苦労したのはこのファイルのテーブル <priv_nets> である.すなわち,ルーター用途のようなマシンではプライベートアドレスからのアクセスは IP を騙っているので捨てなさい,ということなのだが,本例の場合,マシンは 192.168.0.0/24 のアドレスにあって dns の問い合わせはルーターに飛んで結果もルーターから戻ってくる.したがって, 192.168.0.0/24 から/へのパケットは破棄できない.また,家庭内の LAN のアドレスも 192.168.1.0/24 なのでこれらのパケットは通してやる必要がある.これらの結果, !192.168.0.0/23 という苦肉の記述が入っている.(冒頭でせっかく伏せ字にしたのに晒してしまった...) 設定ファイルを作ったところで再起動する前に設定ファイルを確認しよう.Packet Filter は起動時に設定ファイルがおかしくても:特にフィルター・ルールが間違っているだけの場合,特に何も教えてくれない.確認するためには pfctl コマンドを用いて.
# pfctl -f /etc/pf.conf     Load the pf.conf file
# pfctl -nf /etc/pf.conf    Parse the file, but don't load it
# pfctl -Nf /etc/pf.conf    Load only the NAT rules from the file
# pfctl -Rf /etc/pf.conf    Load only the filter rules from the file
# pfctl -sn                 Show the current NAT rules 
# pfctl -sr                 Show the current filter rules
# pfctl -ss                 Show the current state table
# pfctl -si                 Show filter stats and counters
# pfctl -sa                 Show EVERYTHING it can show
とあるので,
# pfctl -f /etc/pf.conf
として
# pfctl -sr
で,フィルタールールがあっているか確認する.と,いうようにできる.特に, pfctl -f ... でおかしければ教えてくれる. あとは,起動時に自動で立ち上がるように, /etc/rc.conf に以下を追記することだけだ.
pf_enable="YES"                  # Set to YES to enable packet filter (pf)
pf_rules="/etc/pf.conf"         # rules definition file for pf
pf_program="/sbin/pfctl"        # where the pfctl program lives
pf_flags=""                     # additional flags for pfctl
pflog_enable="YES"               # Set to YES to enable packet filter logging
pflog_logfile="/var/log/pflog"  # where pflogd should store the logfile
pflog_program="/sbin/pflogd"    # where the pflogd program lives
pflog_flags=""                  # additional flags for pflogd
ログは以下のコマンドで(設定してあれば)見ることができる.上記の例だとドロップされたパケットの記録が残っているはずだ.
# tcpdump -n -e -ttt -r /var/log/pflog
なんか,pf のフィルタールールはもっとシンプルになりそうなものだけどどうなのだろう.どなたかエロい人御教授頼む.

| | Comments (67) | TrackBack (1)