2012/11/29

10分で理解するApache Web Serverのログに関する小ネタ

その1 ログにhttpでアクセスできるようにする

Hiroaki's blog: IISのアクセスログにhttpでアクセスするのApache版。
  • ログディレクトリを公開する
  • ディレクトリ、ファイルのアクセス権を調整する
の2つをやれば、OK。
多くの場合、ログは /var/log/httpd に書き出されているので、設定はこんな感じ
Alias /log/ "/var/log/httpd/"
<Directory "/var/log/httpd/">
    Order deny,allow
    Deny from all
    Allow from アクセスを許可するネットワーク・ホスト
</Directory>
たとえば、これをlog.confという名前でファイルに保存して、CentOSなら /etc/httpd/conf.d、FreeBSDなら /usr/local/etc/apache22/Includes に置いておけば、Apache起動時に読み込んでくれる。ディレクトリの一覧は不要でしょ?
/var/log/httpd のアクセス権って、所有者(root)しか許可されていないので、chmod og+r+x とかして緩めてあげないと、403エラーになっちゃう。俗に言うシェルアカウントを発行しているようなサーバでは、緩めて問題ないか、よーくよーく考えること。

アクセスは、http://サーバ名/log/ログファイル名 とすればOK。
パスワードのないsshのキーを作って専用アカウントに設定して…なんてしなくても、これなら手軽にアクセスログを取得できるでしょ?

その2 /server-status へのアクセスをログに残さない

MuninでApacheを監視すると、定期的に /server-status にアクセスする。のだが、チリも積もれば山となる。1,2ヶ月様子を見ても変なアクセスがないようであれば、ログに残さなくていいよね?ってことで、特定のURLへのアクセスをログに載せない設定。ちゃーんとマニュアルのログファイルのところに書いてあるんだが、ぜんぜん気がつかなかったよ。
SetEnvIf Request_URI "^/server-status" dontlog
CustomLog /var/log/httpd-access.log combined env=!dontlog
SetEnvIfを使っているから、「特定のサーバからのアクセスはログに残さない」なんてのも可。



2012/11/17

iPod Touchともろもろ購入

今更ながら、iPod Touch購入。最新ではなく、1個前の第4世代、しかも、工場修理品って奴。おもしろそう!ってアプリがiPhone/iPod Touch用だったりして試せないのは残念だし、初代iPadは居間に置いていて引き上げられないし。ぷららモバイルの契約があと1年残っているので、iPhoneも選択肢から除外。

で、中国から届くのを待っている間、「やっぱり液晶保護フィルムいるよね?」というわけで、ググって評判のよさそうな【送料無料】レイ・アウト 4th iPod touch用防指紋光沢保護フィルム RT-T4F/CR 【jan 457129798...を購入。
iPod Touchが届いて裏を見たら、ピッカピカに磨いてあった。「傷、目立つよね?」というわけで、あわてて今度はケースをググったり友達に訊いたりして情報収集。候補をアマゾンで見ていたら、もうちょっとCPのよい商品が紹介されていた。ので、それ、amix iPod touch4専用ハードケース Protection fix shell for iPod touch4 (クリア) に決定。充電ケーブルも一緒に購入すると割引ということだったので、まとめて購入。

ケースの梱包が不安って声があったけど、ケーブルと一緒に買ったためか、透明の汎用のプラスチックケースに並べられて梱包されていた。保護フィルムは2枚入っていたから、汚れてきたら張替えだな。



2012/11/10

WifiをチェックするAndroidアプリを作った

しばらく放置になっていたAndroidアプリを、形にした。
chkConnectは、定期的にWeb Pageにアクセスできるかどうかをチェックするアプリ。アクセスできなければWifiを切断する。再接続はAndroidまかせ。これで、アンテナから離れた席に座ってしまったから、いちいち手動でWifiをoffにするなんて手間ともおさらば。

間隔を設定するとはいえ、ちょくちょくWeb Pageにアクセスするから、電池はそれなりに喰ってしまう。一応、Wifiがoffになっているのを検知すればチェックはやめるし、画面が消えていたときの間隔も別に設定できるので、自分は便利に使えているのだが。
というわけで、Google Playで公開する前に人柱募集モードで公開。


Appleはリスクを背負って作っている

Appleはリスクを背負っているから、妥協せずに細部までこだわるし、それがユーザの満足に繋がっているのだ。
アップルのデザイン ジョブズは“究極”をどう生み出したのかは、対サムソンの部分はApple贔屓の、サムソンの印象が悪くなるような言葉の選択があるものの、どうしてAppleにはできて日本の電機メーカーにはできないかの理由(の一部)が明らかにされていると思う。

iPhoneやiPadを始めとする(物理的な)製品へのこだわり。Apple自らが製造機器を購入し、加工工場に貸し出すというのは驚いた。が、確かに、このスタイルでなければ製品に対する想いを型にはできない。加工工場がボトルネックになってしまうからだ。想いを具現化するために妥協はしない。だから、製品を購入したユーザーも満足し、次の製品により大きな期待をするのだろう。

自分が欲しい物を妥協せずに作る。だから、ボタンを押した感触や、操作感も妥協せず、製品として、サービスとして、統一されている。デザインは見てくれではなく、統一されたUX(ユーザー体験)だと考えているからこそ、デザイン重視といっても薄っぺらい製品にはならないのだ。
そして、「自分=開発者」ではなく、「自分=製品を使うユーザー」であるから、誰が使うんだ?というような製品にはならない。そんな、ターゲット不在の製品を作っている日本のメーカー。現場の問題ではなく、そんな製品企画にGoを出す経営陣が問題だろう。経営陣こそボーナスの一部を自社製品の現物支給とし、家族の容赦無い批判を受けて、ユーザーの目線を学ぶべきではないのか?

「いいものが売れる」なんてのは、作り手の思い上がりなのだ。現実はもっと簡単で、「欲しがるものが売れる」のだ。だから、小耳に挟んで、お店で見てみて触ってみて、買って帰って箱から出して、使ってみる、といった一連の行動に対して、「欲しかったのはこれでしょ?」とアピールすることが重要なのだ。「そうそう、これが欲しかったんだよ」と満足できたものが、その人にとっての「いいもの」であり、「いいもの」とはそういう意味なのだ。そして、「いいもの」を伝えるために、Appleはユーザーが製品を買うお店の「デザイン」や、箱から出すときの演出までこだわった。だから、製品がこれだけ熱狂的に受け入れられているのだろう。

最後に、購入したのは第一版第一刷だけど、ちょっと校正がという部分があった。全く同じ文章が二箇所にあったのだけど、もう直っているのかな?

【第1章】ジョブズにとってデザインとは何か?
 アップルのデザイン活性型経営
 コラム1 ジョブズだからできた究極のシンプル

【第2章】分解して分かるアップルデザインの真髄
 分解・解剖!iPhone4S の内部構造
 アルミニウムの使いこなし方に驚く!
 樹脂にも独自の工夫を加えるアップル

【第3章】触れてうっとり、インターフェースの秘密
 ジョブズ自身が未来のユーザー役に
 識者に聞くアップルのインターフェース
  (1)長谷川踏太「広告以上にブランドメッセージを伝えている」
  (2)増井俊之「開発現場にも届く、強いリーダーシップを」

【第4章】アップルストアに挑んだ日本人デザイナー
 感動を共有するスペースデザイン
 コラム2:デザインのためなら流通でも戦う
 コラム3:量販店的な売り方に満足しないなら

【第5章】アップルの広告・グラフィックデザイン
 ジョブズのセンスが光るもう1つの歴史
 コラム4:時代を経ても変わらぬアップルの広告
 ショートインタビュー 猪子寿之「ネットワーク中心の社会を見据えた思想」
 コラム5:750 枚のタートルネック・シャツ

【第6章】革命の始まりはiMacだった
 インタビュー ジョナサン・アイブin 1999
 ショートインタビュー 藤崎圭一郎「体験を変えたアップルのデザイン」

【第7章】アップルが争っても守りたいデザイン
 GALAXY は果たして模倣なのか?
 サムスンの逆襲!
 第1ラウンドは痛み分けの結果に
 こんなにある!ジョブズ名義のデザイン特許
 ショートインタビュー 山中俊治「モノが実現するユートピア」

【第8章】ジョブズが夢見た未来のデザイン
 アップルTVとリモコンさえあれば…
 ドッキングステーションにこだわる理由
 ユニバーサルドックも変形する
 ジェスチャー入力でボタン不要の世界へ
 スタイラスペン入力もあり!
 ショートインタビュー 坂井直樹「iCar は生まれるか?」
 コラム6:iCloudとSiriに見るアップルの夢


2012/10/28

DVDイメージを一括mount

DVDのコピーが違法行為なので、意味がないけど。
DVDイメージコピーをDLNAサーバに突っ込んでおくと、いちいちDVDを入れ替えなくてもDLNA機器のリモコン操作だけで次々見れるので、快適。というわけで、DVDイメージを一括でmount/unmountするscript。FreeBSD用。むかーしむかーしにイメージを作っていたという人にしか役に立たないけど。
ネットで視聴なら、こんなことしなくてもリモコン操作だけでラクラクだけどね。

一括マウント。マウント先は/media。DVD毎にフォルダを分けてイメージを作成したと想定。

#!/usr/local/bin/bash
FILES=$(/usr/bin/locate ISO | /usr/bin/grep '^/int.*ISO$')

for isofile in $FILES
do
  devnum=$(/sbin/mdconfig -a -t vnode -f "${isofile}")
  dir=$(/usr/bin/dirname ${isofile}|/usr/bin/sed 's/\.ISO$//')
  name=$(/usr/bin/basename $dir)
  /bin/mkdir /media/$name > /dev/null 2>&1
  /sbin/mount -r -t cd9660 /dev/$devnum /media/$name
done

一括アンマウントは

#!/usr/local/bin/bash
DEVS=$(/bin/ls /dev/md* |/usr/bin/grep -v ctl)

for target in $DEVS
do
  num=$(/bin/echo $target|/usr/bin/sed 's/^.*md//')
  /sbin/umount $target
  /sbin/mdconfig -d -u $num
done
/bin/rmdir /media/*

スクリプトは可読性を高めるため、bash用の拡張構文を使用。たいしたことはやっていないので、/bin/sh の範囲でも書けるけど、仕事でメンテナンス性の高いコードを書けるよう、普段から練習。


クックブックは電子書籍の方が便利だと思う。O'Reilly Japan - bashクックブック



2012/10/27

メジャーアップデートしたMunin

Muninがアップデートして、2.0になった。FreeBSDのportsにも反映されたので、アップデート。
Node(監視される側)は一部1.4.6のままだけど、情報は問題なく取得できている。
何より嬉しいのは、ちゃんとIPv6に対応していること。munin.conf に address を書くとき、[]で囲むようにすればOK。というわけで、v6パッチのお役目終了。


2012/10/21

Software Design 11月号

第2特集はMunin。仕事でもプライベートでも、サーバのリソース監視で絶賛活躍中。監視対象となるリソースを接続or動作させている状態でMuninをインストールすると、リソース監視の設定をある程度やってくれる。というか、多くの場合は、それでOK。監視のための設定コストを低く始められるので、取り敢えず始めて、見たいものが出てきたら順次追加、というパターンでOK。死活監視としての利用はちょっと微妙なので、Monitという別のソフトを利用している。

そういえば、日本Muninユーザ会というのができたんだそうだ。
Munin.jp - Munin User Group Japan



2012/10/13

最近買った本

ガベージコレクションのアルゴリズムと実装 まぁ、マニアックといえばマニアックかもしれないけど。どんな仕組みで動いているのか知っていて損はないし、そういった奥行きが足腰の強さに現れてくるのではないか?


リバースエンジニアリング ―Pythonによるバイナリ解析技法 (Art Of Reversing)こっちもまぁマニアックだけど、上とはまたちょっと違ったレイヤー。どんな動きをしているのか、出来上がっているものを調べる側。




2012/09/30

FreeBSDのVPN ServerでIPv6をバラまく

Hiroaki's blog: LinuxのVPN ServerでIPv6をバラまくと同じことをFreeBSDでやってみる。

L2TP/IPsecを使うので、Kernelのパラメータを修正してbuild。これはIPsecを使おうとしたときには毎度おなじみの話なので、ここでは省略。詳しくは、[FreeBSD] L2TP/IPsecサーバを立てる | 雑記帳などを参照。
次。mpd5の話。インストールについては、L2TP/IPsecをやるときと同じなので、略。IPv6をばら撒くための、設定について。IPv6 Part 6: Configuring An IPv6 Network Assigned Over PPPoE Using FreeBSD » mmacleod.caがとても参考になる。
/usr/local/etc/mpd5/mpd.conf の内容。
startup:
        set user kanri irnak admin
        set console self 127.0.0.1
        set console open
        log +ipcp +ipv6cp +lcp +link +auth +ecp +ccp
        set console enable logging

default:
        load l2tp_server

l2tp_server:
        create bundle template B4
        set bundle enable ipv6cp
        set iface idle 1800
        set iface mtu 1454
        set iface enable tcpmssfix
        set iface up-script /usr/local/etc/mpd5/mpd.linkup
        set iface down-script /usr/local/etc/mpd5/mpd.linkdown
        create link template L4 l2tp
        set link action bundle B4
        set link enable multilink
        set auth enable system-acct
        set link keep-alive 10 60
        set link disable chap eap
        set link enable chap-msv2
        set link mtu 1454
        set link mru 1454
        set link enable incoming
ポイントは、ipv6cpを有効にしていること。IPv4の割り当ては行わないので、ipcpは記述しない。
つながったらroutingやfirewallの設定の変更が必要になるので、それらは up-sciptで指定した /usr/local/etc/mpd5/mpd.linkup に書く。
1つ目の引数がInterface(ng0とかng1とか)なので、
/sbin/ifconfig $1 inet6 割り当てるアドレス prefixlen 割り当てるprefix alias
とかしてあげればOK。ルーティングは適当に。例えば、P2Pだからってprefixを長くしてあげると、アドレスが一番マッチしたルートを使おうとするので、ルーティングを意識しなくてもよきに計らってくれたりする。
同様に、切断されたときの処理を down-scriptで指定した /usr/local/etc/mpd5/mpd.linkdown に書く。

クライアントのMac OSX側。こちらは、Hiroaki's blog: LinuxのVPN ServerでIPv6をバラまくと同じ。


Wifiスポットなど、まだIPv6を割り当ててくれないサービスも多く、6to4が通らないことも多いので、こういう無理やりな方法がまだまだ必要。


2012/09/17

OSXでDNS Serverを変更する

Mt. Lion(に限らず、OSX)では、/etc/resolv.conf は飾りなので、書き換えたところで実質的には意味が無い。Configure DNS lookups from the terminal - Mac OS X HintsにDNS Serverの変更方法が説明されている。それを参考に作ったスクリプトが、これ。
#!/bin/bash
if [ $# -lt 1 ]; then
    echo "$(/usr/bin/basename $0) DNSServer [DNSServer ...]"
    exit 1
fi
key=$(echo 'show State:/Network/Global/IPv4' | /usr/sbin/scutil | /usr/bin/awk '/PrimaryService/{print $3;}')
domain=$(echo "show State:/Network/Service/${key}/DNS" | /usr/sbin/scutil | /usr/bin/awk '/DomainName/{print $3;}')

/usr/sbin/scutil <<EOF
open
d.init
d.add ServerAddresses * $*
d.add DomainName ${domain:-localdomain}
set State:/Network/Service/${key}/DNS
quit
EOF

って、上記記事のコメントにも同じようなスクリプトが載っていたorz。まぁ、こっちは、割り当てられたDNS ServerがAAAA filterしちゃっているときに変更するのが目的なので、ドメイン名は既に割り当てられているものを使うようにしている。


2012/08/27

IPsecでNASへのバックアップ通信を暗号化する(NAS側)

Hiroaki's blog: IPsecでNASへのバックアップ通信を暗号化するの続き。AFP(Netatalk)の動いている、バックアップデータを受け取るサーバ側の設定。FreeBSD 7.3 + IPsec Toolsで。

kernelの再構築や、IPsec Toolsのインストールは略。
まずは/usr/local/etc/racoon/racoon.conf の内容。
# $Id: racoon.conf,v 1.3 2012/08/25 04:47:32 root Exp root $
#
# $KAME: racoon.conf.in,v 1.18 2001/08/16 06:33:40 itojun Exp $

# "path" must be placed before it should be used.
# You can overwrite which you defined, but it should not use due to confusing.
path include "/usr/local/etc/racoon" ;
#include "remote.conf" ;

# search this file for pre_shared_key with various ID key.
path pre_shared_key "/usr/local/etc/racoon/psk.txt" ;

# racoon will look for certificate file in the directory,
# if the certificate/certificate request payload is received.
path certificate "/usr/local/etc/cert" ;

# "log" specifies logging level.  It is followed by either "notify", "debug"
# or "debug2".
log info;

# "padding" defines some parameter of padding.  You should not touch these.
padding
{
        maximum_length 20;      # maximum padding length.
        randomize off;          # enable randomize length.
        strict_check off;       # enable strict check.
        exclusive_tail off;     # extract last one octet.
}

# if no listen directive is specified, racoon will listen to all
# available interface addresses.
listen
{
        #isakmp ::1 [7000];
        #isakmp 202.249.11.124 [500];
        #admin [7002];          # administrative's port by kmpstat.
        #strict_address;        # required all addresses must be bound.
#       isakmp IPv4アドレス [500];
        isakmp IPv6アドレス [500];
}

# Specification of default various timer.
timer
{
        # These value can be changed per remote node.
        counter 10;             # maximum trying count to send.
        interval 3 sec; # maximum interval to resend.
        persend 1;              # the number of packets per a send.

        # timer for waiting to complete each phase.
        phase1 30 sec;
        phase2 30 sec;
}

remote anonymous
{
        exchange_mode aggressive, main;
        doi             ipsec_doi;
        situation       identity_only;
        generate_policy on;
        proposal_check  obey;
        dpd_delay       20;
        passive         on;
        support_proxy   on;
        ike_frag        on;
        verify_identifier off;
    proposal {
            authentication_method rsasig;
            encryption_algorithm 3des;
            hash_algorithm sha1;
            dh_group 2;
    }
    proposal {
        encryption_algorithm    aes;
        hash_algorithm          sha256;
        authentication_method   pre_shared_key;
        dh_group                modp1024;
    }
    proposal {
        encryption_algorithm    3des;
        hash_algorithm          sha256;
        authentication_method   pre_shared_key;
        dh_group                modp1024;
    }
   proposal {
      authentication_method pre_shared_key;
      hash_algorithm sha1;
      encryption_algorithm aes 256;
      lifetime time 3600 sec;
      dh_group 2;
   }
   proposal {
      authentication_method pre_shared_key;
      hash_algorithm sha1;
      encryption_algorithm aes;
      lifetime time 3600 sec;
      dh_group 2;
   }
   

   proposal_check strict;
}

# Phase 2 proposal (for IPsec SA)
sainfo anonymous
{
        pfs_group 2;
        lifetime time 10 minutes;
        encryption_algorithm aes, rijndael, 3des;
        authentication_algorithm hmac_sha1;
        compression_algorithm deflate;
}
client側のIPアドレスは固定ではないため、anonymousで定義する必要がある。remote {} も sainfo{} も、client側とパラメータをそろえること。proposalは一致するものがあればよい。

次。共有キー。psk.txtは
メールアドレス 共有キー

とする。メールアドレスは、client側のconfファイルのremote{}で指定したメールアドレスとする。aggressive modeで接続してくるため、IDとしてIPアドレス以外のものも使用できるのだ。クライアント側でmy_identifier  user_fqdnとしているので、同じメールアドレスを使う。

最後に、どの通信を暗号化対象にするかを /usr/local/etc/racoon/setkey.conf に指定する。
# $Id: setkey.conf,v 1.2 2012/08/25 11:53:25 root Exp $
flush;
spdflush;
spdadd ::1 ::1 any -P in none;
spdadd ::1 ::1 any -P out none;
spdadd NASのあるネットワーク/マスク[0] NASのアドレス/0[548] any -P in none;
spdadd NASのアドレス/0[548] NASのあるネットワーク/マスク/マスク[0] any -P out none;
spdadd ::/0[0] NASのアドレス/0[548] any -P in ipsec esp/transport//require;
spdadd NASのアドレス/0[548] ::/0[0] any -P out ipsec esp/transport//require;
noneで終わっている行は、暗号化を行わない指定。自分自身と通信する場合、自分と同じネットワーク内と通信する場合は、暗号化を行わないようにする。
暗号化を行う条件は、ちょうどクライアント側とin/outを逆にした関係になる。

これでOK。動作確認。Wiresharkなどでパケットをキャプチャしながら、クライアント側で
telnet NASのアドレス 548
とする。IKASMPのパケットの後ESPのパケットになり、かつ、telnetが接続できていればOK。

racoon.conf でlogをdebugにすると、内容が膨大で接続の確認には向かない。tcpdumpやwiresharkでパケットをキャプチャして、どこまで進んでいるかを確認しながら設定を見直すのが現実的。
Phase1が確立していなければ、remote {} の部分とpsk.txtを確認。Phase2であればsainfo。また、暗号化されずにtcp/548に接続しに行っているようであれば、setkeyに喰わせるファイルを確認。firewallでrejectされていることもあるので、設定を見直すことも必要。UDP 500/4500とプロトコル番号50/51。
ログやtcpdumpの出力は意外と見かけないのが、また切り分けを難しくしていると思う。
が参考になる。
また、他に参考にしたページがいくつか。



2012/08/26

IPsecでNASへのバックアップ通信を暗号化する

外出先であっても時間がくればTimeMachineが動いてバックアップを行うので、ネットワークに繋がっているのであれば、普段使っているNASに取って欲しいところ。
この時、バックアップで使うAFPというプロトコルは暗号化を行わないので、別の手段で暗号化を行う必要がある。そこで、IPsecで暗号化を行なってみる。

  • IPv6での通信とする
  • NASのあるネットワークにいる場合、暗号化は行わない
  • 事前共有キーによる暗号化

の3点を条件とする。

  • クライアント側はMountain Lion。racoonを使う。
  • サーバ側はFreeBSD。racoonを使う。

直接設定ファイルを書くことによって、「AFPだけ暗号化する」ようにする。
お約束だが、内容の真偽も含めて、自己責任で。最悪、どこでもMy Macが動かなくなることもあるので。

Mountain Lion側。
/var/run/racoon/ に動的生成した設定ファイルを置くようになっている様子だが、消されそうなので、/etc/racoon/remote/ を作って、そこに置くことにする。
そこに置いた設定ファイルが取り込まれるよう、/etc/racoon/racoon.conf に手を加える。racoonはどこでもMy Macで使っているので、ミスがあると動かなくなるので注意。
racoon.confの最後に
include "/var/run/racoon/*.conf" ;
という行があるので、その手前(上の行)に
include "/etc/racoon/remote/*.conf" ;
を加える。
次に、/etc/racoon/remote/ に、NASに接続するための設定ファイルを作成する。ファイル名は.confで終わっていればなんでも良いので、例えば、NASのhost名にする。
中身は
remote NASのIPアドレス {
   exchange_mode aggressive;
   doi ipsec_doi;
   situation identity_only;
   verify_identifier off;
   my_identifier  user_fqdn       "メールアドレス";
   initial_contact off;
   support_proxy on;
   proposal_check obey;
   
    proposal {
        encryption_algorithm    aes;
        hash_algorithm          sha256;
        authentication_method   pre_shared_key;
        dh_group                modp1024;
    }
    proposal {
        encryption_algorithm    aes;
        hash_algorithm          sha1;
        authentication_method   pre_shared_key;
        dh_group                modp1024;
    }
    proposal {
        encryption_algorithm    3des;
        hash_algorithm          sha256;
        authentication_method   pre_shared_key;
        dh_group                modp1024;
    }
    proposal {
        encryption_algorithm    3des;
        hash_algorithm          sha1;
        authentication_method   pre_shared_key;
        dh_group                modp1024;
    }
 
   proposal {
      authentication_method pre_shared_key;
      hash_algorithm sha1;
      encryption_algorithm aes 256;
      lifetime time 3600 sec;
      dh_group 2;
   }
   
   proposal {
      authentication_method pre_shared_key;
      hash_algorithm md5;
      encryption_algorithm aes 256;
      lifetime time 3600 sec;
      dh_group 2;
   }
    proposal {
      authentication_method pre_shared_key;
      hash_algorithm sha1;
      encryption_algorithm aes;
      lifetime time 3600 sec;
      dh_group 2;
   }
   
   proposal {
      authentication_method pre_shared_key;
      hash_algorithm md5;
      encryption_algorithm aes;
      lifetime time 3600 sec;
      dh_group 2;
   }
   
   proposal {
      authentication_method pre_shared_key;
      hash_algorithm sha1;
      encryption_algorithm 3des;
      lifetime time 3600 sec;
      dh_group 2;
   }
   
   proposal {
      authentication_method pre_shared_key;
      hash_algorithm md5;
      encryption_algorithm 3des;
      lifetime time 3600 sec;
      dh_group 2;
   }
}
とする。
ポイントはexchange_mode。これをaggressiveだけにしているので、Phase 1はaggressive modeで折衝が開始される。そのため、事前共有キーファイルでの相手のIDとしてIP Address以外のものも使えるようになる。 受け側のracoonでは相手のIDとして*(任意の文字列)が使えないため、割り当てられるAddressが固定ではないモバイル環境からアクセスする場合、メールアドレスなど、IP Address以外のものを指定する必要があるため、aggressive modeは必須となる。
通常であれば、この後にsainfoを定義するのだが、既に定義されているのだ。/var/run/racoon/anonymous.conf がそれ。
sainfo の部分はこんな感じ
sainfo anonymous { 
  pfs_group 2;
  lifetime time 10 min;
  encryption_algorithm aes;
  authentication_algorithm hmac_sha1;
  compression_algorithm deflate;
}
ここまでで、暗号化するときの各種パラメータを定義した。後は、どの通信を暗号化するのかを定義する。これは、「NASにバックアップする通信」なので、NASに送る奴とNASから返ってくる奴とを定義する。
例えば、/etc/racoon/NASのhost名.key というファイルに
spdadd NASのいるネットワーク/マスク[0] NASのアドレス[548] any -P out none;
spdadd NASのアドレス[548] NASのいるネットワーク/マスク[0] any -P in none;
spdadd ::/0[0] NASのアドレス[548] any -P out ipsec esp/transport//require;
spdadd NASのアドレス[548] ::/0[0] any -P in ipsec esp/transport//require;
と書く。これをsetkey -f で喰わせてやればよいのだが、マシンを起動するたびに忘れずに実行する必要があるので、例えば、rootでcrontab -eして
@reboot /usr/sbin/setkey -f /etc/racoon/NASのhost名.key
と書いておく。

最後に、事前共有キー。/etc/racoon/psk.txt が既にあるので、同じように、
NASのアドレス 事前共有キー
の1行を追加しておく。

サーバに繋ぐ前に、一度Macを再起動させて問題なさそうなことを確認しておく。
再起動してログインしたら、アプリケーション→ユーティリティ→コンソール.app を開く。左側のログリストで「すべてのメッセージ」を選択し、右側に表示されるracoonのメッセージを確認する。問題なければ、「IPSec Phase2 established」の行が表示されているはず。何らかのエラーが出ているようであれば、/etc/racoon/racoon.conf および /etc/racoon/remote/*.conf の中身を確認して修正、racoonの再起動(わからなければ、Macの再起動)。放置しておくと、どこでもMy Macで接続できないことがある!

サーバ側の設定についてはまた今度。


2012/08/19

LinuxのVPN ServerでIPv6をバラまく

IPv4アドレスが枯渇したと言われているが、3Gや公衆のWifiスポットで未だにIPv6アドレスが割り当てられることはない。しかも、割り当てられるIPv4アドレスがPrivateアドレスで、6to4などのtunnelが使えなかったりする。
割り当てられないのであれば、自分で割り当てよう!ということで、L2TPで繋いだVPNでIPv6を流してみる。

今回は、IPv6アドレスを割り当てられているLinux(CloudCoreのCentOS 5.8)に対してOSX(Mountain Lion)からVPN接続し、踊る亀を見られるようにする。

まずはLinux側。すでに何らかの形で固定のIPv6アドレスを、/64よりも大きなブロックで割り当てていることが前提。6to4で割り当てられるのは/48なので、それでもOK。
最初は普通にL2TPのサーバにする。さくらのVPSでiPhone用の野良WiFi通信傍受対策のL2TP/IPsec(VPN)を設定したメモ(CentOS5) - nori_no のメモなどを参考に。openswanもxl2tpdもrpmがあったので、それでインストール。とりあえずはIPv4のアドレスを割り当てるようにする。

次に、Mountain Lion側。システム環境設定→ネットワーク で、新しいサービスを追加する。
  • インターフェイス:VPN
  • VPN タイプ:L2TP over IPSec
  • サービス名:適当に
さらに
  • サーバアドレス:Linuxのホスト名またはIPv4アドレス
  • アカウント名:Linuxの/etc/ppp/chap-secrets に書いたclientの名前
とし、認証設定ボタンを押下。
  • コンピュータ認証:/etc/ipsec.secret に書いたパスワード
  • パスワード:/etc/ppp/chap-secrets に書いたパスワード
をセットして、OK。
適用ボタンを押して設定を保存したら、接続ボタンを押して、繋がるかどうか確認。詳細...ボタンを押して、TCP/IPの欄でIPv4 アドレスやサブネットマスクなどが割り当てられていることを確認する。
  • Linux側では、UDPの1701, 500, 4500が通信できるようにiptablesが設定されていること。
  • 設定に問題ないようであれば、ipsecを再起動してみる(sudo /sbin/service ipsec restart)
  • Mountain Lion側では、パスワードを正しく設定していること。
が確認ポイント。IPv4で使う訳ではないので、NATやルーティングは設定不要。

ここまでできたら、IPv6用の設定に移る。
Linux側の /etc/ppp/options.xl2tpd の最後に、

+ipv6
ipv6cp-accept-local
を追加。 /etc/sysconfig/network-scripts にifcfg-ppp0 というファイル(ppp1やppp2かもしれないが、通常は0でOK)を新たに作る。中身は

IPV6INIT=yes
IPV6ADDR=受け側のIPv6アドレス/64
とする。

次。/etc/ppp/ipv6-up がバグっているような気がしてならない。ので、ちょっと手を入れる。
47行目前後にある

CONFIG=$REALDEVICE


CONFIG=$LOGDEVICE
にする。
そして、/etc/ppp にipv6-up.local というファイルを作る。中身は

#!/bin/bash

PREFIX="割り振るIPv6のネットワーク"
/sbin/ip6tables -P FORWARD ACCEPT
/sbin/ip6tables -A FORWARD -i $1 -j ACCEPT 
/sbin/ip6tables -A INPUT -i $1 -j ACCEPT 
とする。ifcfg-ppp0で/64としているので、PREFIXは::/64 で終わるアドレスとなる。このファイルに実行権限をつけることを忘れないこと。これは、IPv6アドレスが割り当てられた時に実行される。

Mountain Lion側は、システム環境設定→ネットワーク でさっき作ったサービスを選択し、詳細... のTCP/IP タブを選択。IPv4 の構成を「切」にする。
また、/etc/ppp/ipv6-up というファイルを作り、実行権限を付けておく。中身は

#!/bin/sh
/sbin/ifconfig $1 inet6 add 割り当てるIPv6アドレス/64
/sbin/route add -inet6 default $4%$1
/sbin/route change -inet6 default $4%$1
とする。Linux側で最後を::1として、Mountain Lion側では::2とするとわかりやすいかも。
これで設定はOK。作った「サービス」に繋いだ後、踊る亀にアクセスして、亀の絵がアニメーションしていればIPv6で接続できている。


本来であれば、Linux側でradvdを動かしていればMountain Lion側のIPv6アドレスは自動で割り当てられるのだが、 IPv6 Address on L2TP VPN: Apple Support Communitiesで指摘されているように、アドレスの割当が行われない。そのため、/etc/ppp/ipv6-up を用意して手動で割当を行っている。


2012/08/11

もうちょっと数当てゲーム

Hiroaki's blog: もう一回 数当てゲームで残した乱数の部分を見てみる。
isUsedNum :: Char->String->Int
isUsedNum _ [] = 0
isUsedNum a (x:xs)
             | a == x    = 1 + isUsedNum a xs
             | otherwise = isUsedNum a xs

isSuitableNum :: String->Bool
isSuitableNum [] = True
isSuitableNum (x:xs) = isUsedNum x xs == 0 && isSuitableNum xs

normNum :: String->String
normNum xs
        | (l < 4)       = normNum $ "0" ++ xs
        | otherwise     = xs
        where l = length xs

targetNum :: [Int]->String
targetNum (x:xs)
          | (isSuitableNum strX) = strX
          | otherwise            = targetNum xs
          where strX = normNum $ show x

main = do
     r <- getStdRandom $ randomR (0, 99)
     let t = targetNum $ randomRs (0,9999) (mkStdGen r)
  • 乱数で4ケタの数字を求める。この時、数字はいくつも生成してリストにしておく
  • リストの頭から数字を1つ取り出し、数当てゲームで使える数字だったら採用。違ったら、リストの頭の数字を捨てて、リストの頭から数字を取りだすところからやり直し。→ targetNum
  • 採用した数字が3ケタだったら、頭に0をつける。→ normNum
  • 数当てゲームで使えるかどうかは isSuitableNum。一桁ずつ取り出して、既に使っているかどうかを順番にチェック→isUsedNum
isUsedNumって、elem使えばもっと簡単になるよね。あと、「数字がダブらないこと」という条件があるから、求める4ケタの数字は0123~9876になる。もっとも、これはコメント入れておかないと混乱することになるけど。

では、数当て問題 - Hit & Blow -: ツムジのひとりごとのコードを拝見。

-- xs からランダムに重複しない n 個の要素を取り出す
randomPicUp :: Eq a => Int -> [a] -> IO [a]
randomPicUp n xs = loop n (length xs - 1) xs []
  where
    loop 0 _ _ prd = return prd
    loop c m xs prd = do
      g <- newStdGen
      let (i, _) = randomR (0, m) g
      let x = xs !! i
      loop (c - 1) (m - 1) (delete x xs) (x : prd)

main :: IO ()
main = do
  target <- randomPicUp 4 ['0'..'9']
大きな違いは、
  • 0~9から数字を1個選ぶ。これを4回行って、4けたの数字とする
という考え方だろう。randomPicUpの中にある、loop c m xs prdがそれだ。
  • cはループカウンタかな?xsは選び出す数字の候補で、最初は0~9だけど、選ばれた数字を除外していく。mは選び出す数字の候補の数のようだから、xsの長さから求めればいいよね。
  • prdは選んだ数字のリスト。これを数えるようにすれば、cはいらなくなるかな?
とはいえ、ここまでシンプルに書けるのね~。


2012/08/09

もう一回 数当てゲーム

数当て問題 - Hit & Blow -: ツムジのひとりごとで取り上げていただいたので、自分のコードと比べてみる。

このゲームの肝であるHitとBlowを求める処理のうち、まずはBlowから。
Blowは、「数字として当たっているし、場所(桁)も当たり」ということ。

countBlow :: String->String->Int
countBlow [] _ = 0
countBlow _ [] = 0
countBlow (x:xs) (y:ys)
          | x == y    = 1 + countBlow xs ys
          | otherwise = countBlow xs ys
  • 文字列を2つ受け取って、それぞれの先頭を比較する。一致していたら、戻り値に1を加える。
  • 文字列が終わりでなかったら、比較しなかった残りの文字列それぞれを引数にして、自分を呼び出す。
  • 受け取った文字列のうち、どちらかが空だったら、0を返す
で、それを
  • 文字列を2つ受け取って、それぞれの桁について、同じ数字だったらTrue、違ったらFalseにする→受け取った文字列と同じ長さの、Boolのリストができる
  • そのBoolのリストのうち、Trueの数がBlow
と考える。
そうすると、文字(Char)のリストである文字列を2つ受け取って、それぞれの要素に対して関数を適用する→zipWithの出番
また、BoolのリストでTrueを数えるのは
  • filterでリストのうちTrueのものだけにする
  • それからリストの要素数を数える
とすればよいので、

countTrue :: [Bool] -> Int
countTrue = length . filter (== True)

となる。
結局、Blowは

countBlow :: String -> String -> Int
countBlow target = countTrue . zipWith (==) target

で求められると。


次。Hit。
Hitは、「数字としては当たっているけど、場所(桁)は違う」ということ。
単純に「数字として当たっている」ものを数えてしまうと、Blowもカウントしてしまう。でも、Blowも求めるのだから、「数字として当たりの数」- Blow として求めればよい。
ということで、どうやって「数字として当たりの数」を求めるか?

isUsedNum :: Char->String->Int
isUsedNum _ [] = 0
isUsedNum a (x:xs)
             | a == x    = 1 + isUsedNum a xs
             | otherwise = isUsedNum a xs

countUsedNum :: String->String->Int
countUsedNum [] _ = 0
countUsedNum (x:xs) ys = isUsedNum x ys + countUsedNum xs ys

うーん、回りくどい。2つの文字列を引数として受け取って、1つ目の文字列を1文字ずつ、2つ目の文字列の構成要素か見ている(isUsedNum)。これも、文字列を1文字ずつ比較しているから、遅くなるはず。
こっちも、Blowと同様、「構成要素であればTrue」のリストを作って、最後にTrueの数を数えればいいわけだ。

countHit :: String -> String -> Int
countHit target = countTrue . map (`elem` target)

mapが肝かな?関数とリストを受け取って、リストの1つ1つの要素に対して関数を適用する(関数で処理する)。だから、mapの結果のリストって、与えたリストと同じ長さになる
で、mapにどんな関数を与えるかというと、target の構成要素かどうかを返す関数を与える。あとは、Trueの数を数えればOK。


リストの使い方がうまくない。まぁ、もっといろいろプログラム書いて慣れないとね。


2012/08/05

数あてゲームのバグをつぶす

Hiroaki's blog: Haskellで数あてゲームを書いてみる
にはバグがある。
当てる数を0以上9999以下の乱数で求めているのだが、この数字を文字列にする際、頭の0が失われるのだ。
だから、求める数字が4つにならないことがある。また、数が100未満の場合、0が複数回使われていることをチェックできないのだ。
というわけで、ゲームのルールに従っている数字かどうかをチェックする前に、4桁になるような処理を追加してみた。

import System.Random
import Control.Monad(when)

isUsedNum :: Char->String->Int
isUsedNum _ [] = 0
isUsedNum a (x:xs)
             | a == x    = 1 + isUsedNum a xs
             | otherwise = isUsedNum a xs

countUsedNum :: String->String->Int
countUsedNum [] _ = 0
countUsedNum (x:xs) ys = isUsedNum x ys + countUsedNum xs ys

countBlow :: String->String->Int
countBlow [] _ = 0
countBlow _ [] = 0
countBlow (x:xs) (y:ys)
          | x == y    = 1 + countBlow xs ys
          | otherwise = countBlow xs ys

resultString :: String->String->(Bool, String)
resultString xs ys =
             (endFlag, show hit ++ " hit(s) " ++ show blow ++ " blow(es).")
             where blow = countBlow xs ys
                   hit = countUsedNum xs ys - blow
                   endFlag = blow == 4

isSuitableNum :: String->Bool
isSuitableNum [] = True
isSuitableNum (x:xs) = isUsedNum x xs == 0 && isSuitableNum xs

checkNum :: String->IO ()
checkNum compNum = do
         putStrLn "Number ? "
         guessNum <- getLine
         when (not $ null guessNum) $ do
              let (flag, msg) = resultString compNum guessNum
              putStrLn msg
              when (not flag) $ checkNum compNum
         return ()

normNum :: String->String
normNum xs
        | (l < 4)       = normNum $ "0" ++ xs
        | otherwise     = xs
        where l = length xs

targetNum :: [Int]->String
targetNum (x:xs)
          | (isSuitableNum strX) = strX
          | otherwise            = targetNum xs
          where strX = normNum $ show x

main = do
     r <- getStdRandom $ randomR (0, 99)
     let t = targetNum $ randomRs (0,9999) (mkStdGen r)
     checkNum t
     putStrLn t


2012/07/29

Haskellで数あてゲームを書いてみる

言語は実際にプログラムを書いてみないと身に付かない。ということで、昔、ポケコンでよく見かけた数あてゲームをHaskellで書いてみた。
コンピュータが生成した数字をあてる。
コンピュータは、予想した数に対して
 Hit: 数字はあっているが、場所が異なる数字の数
 Blow:数字も場所もあっている数字の数
の2種類のヒントを返す

import System.Random
import Control.Monad(when)

isUsedNum :: Char->String->Int
isUsedNum _ [] = 0
isUsedNum a (x:xs)
             | a == x    = 1 + isUsedNum a xs
             | otherwise = isUsedNum a xs

countUsedNum :: String->String->Int
countUsedNum [] _ = 0
countUsedNum (x:xs) ys = isUsedNum x ys + countUsedNum xs ys

countBlow :: String->String->Int
countBlow [] _ = 0
countBlow _ [] = 0
countBlow (x:xs) (y:ys)
          | x == y    = 1 + countBlow xs ys
          | otherwise = countBlow xs ys

resultString :: String->String->(Bool, String)
resultString xs ys =
             (endFlag, show hit ++ " hit(s) "
                                ++ show blow ++ " blow(s).")
             where blow = countBlow xs ys
                   hit = countUsedNum xs ys - blow
                   endFlag = blow == 4

isSuitableNum :: String->Bool
isSuitableNum [] = True
isSuitableNum (x:xs) = isUsedNum x xs == 0 && isSuitableNum xs

checkNum :: String->IO ()
checkNum compNum = do
         putStr "Number ? "
         guessNum <- getLine
         when (not $ null guessNum) $ do
              let (flag, msg) = resultString compNum guessNum
              putStrLn msg
              when (not flag) $ checkNum compNum
         return ()

targetNum :: [Int]->String
targetNum (x:xs)
          | (isSuitableNum strX) = strX
          | otherwise            = targetNum xs
          where strX = show x

main = do
     r <- getStdRandom $ randomR (0, 99)
     let t = targetNum $ randomRs (0,9999) (mkStdGen r)
     checkNum t
     putStrLn t

乱数はすごいHaskellたのしく学ぼう! の通りにはいかなかった。パッケージングが変更になったんだそうだ。
cabal install random
としてパッケージを追加。また、使い方はRandom モジュールの使い方 : tnomuraのブログが参考になった。



2012/07/27

Mountain LionでPostfixの設定が変わった

OS Xのmail daemonはPostfixなのだが、Mountain Lionになってディレクトリがちょっと変わったようだ。Lionの時の設定のままだと、起動に失敗する。
確認すべき設定は、main.cfの2カ所。queue_directoryとdata_directoryに指定しているディレクトリがなくなっているので、Mountain Lion用の設定に変える。
queue_directory = /var/spool/postfix
data_directory = /var/lib/postfix


2012/07/15

これならすぐに実践投入できる!リーダブルコード

ちょっと話題のリーダブルコード を早速読んだ。いいよ、これ。
人によっては、この程度のことはもう実践しているというだろうけど、実践できていないコードも山のようにある訳で。ちゃんとテストコードを書いてリファクタリングするときに、この本のテクニックを使うようにすれば、次に手を入れるとき、もっとやりやすくなっているはずだ。
Eclipseでは、関数なり変数なりにマウスカーソルをあてればJava Doc形式のコメントを表示するので、コメントを付けることも多いと思う。でも、メソッドの中はどう?
コメントなしで、コードで表現しきるのがbestなんだろうけど、名前が長くなりすぎて処理の流れが追いにくくなるのは本末転倒。どういう方針で付ければよいのか、ヒントというか答えの1つがこの本には書いてある。
そうそう、メソッドというか、関数の出口(return)を1カ所にしろというのは昔むかしの授業で言われた。 流れが見にくくなるし、無駄だなぁと思いながらも、レポートや試験ではフラグを追加して逃げた。 最終的にどういうコードに落ちるかを考えたら、用が済んだらとっととreturnすべきだし、本書でもそれを勧めている。
付録には、おすすめの本がリストされている。その中の1冊、JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス は眺めたことがあるけど、やっぱり、JavaScriptは言語仕様的に大規模なソフトウェアを書くのには向いていないんじゃないかな?


2012/07/08

広告を消す!

広告を表示するために時間がかかったり、セキュリティ的に問題のある中身が表示されたりということがあったりするので、表示しないようにしてみる。
すべてを非表示にするのは難しいけど、MicroAd を無効に!! の巻を参考にして設定変更したDNSを見るように、うちのネットワーク設定を変更。
広告がFlashだとそれだけでパワー喰うから、効果あるはず。