いますぐ実践! Linuxシステム管理

いますぐ実践! Linux システム管理 / Vol.255 / 読者数:2539名

こんばんは、うすだです。

先日、Ubuntu をようやく 13.10 に上げたのですが、案の定、 日本語入力のところでハマりました。

デフォルトでは、入力ソースの切り替えが IME の有効・無効になるよう設定されます。 ですが、入力ソースを切り替えると、 xmodmap で設定した内容がきれいさっぱり消えてしまうため、 いままで慣れ親しんできたキー入力が行えなくなります。

結局、入力ソースを Mozc だけにして、 Mozc の設定で IME の有効・無効に [Shift]+[Space] を割り当てて、 現在に至っています。

この方法だと、インジケータのアイコンが「あ」のままになってしまうのですが、 今のところはほぼ困っていないため、よしとしています。

ずっと同じ環境にいて、その環境に慣れてしまっていると、 環境が大きく変わらざるをえない現象に遭遇したとき、つい悪態をつきたくなります。

ですが、それは、今までの環境が恵まれていた、 ということを気づかせるために起こったことかもしれません。
(お腹をこわしてトイレから出られなくなったとき、 健康体だった自分が恵まれていたことに気づくのと同じように…。)

ですので、今まで不自由なく日本語入力が行えていたこと、 バージョンを上げても結果的に問題なく使えるようになったこと、 その過程でいろいろ学べたことに、感謝したいと思います。

…となかなか素直にはなれませんが、今回もはりきってまいりますかね。

今回のお題 - arpwatch でMACアドレスをかき集める (レベル:やや中級)

最近、BYODとか言われていますが、もともと厳しくない会社だと、太古の昔から、 私物の PC などを社内LANにつなげることが黙認されていたように思います。

どんなマシンが社内に生息しているのか、把握しきれていない管理者さんにとって、 どこの馬の骨ともわからないマシンがこれ以上増えることは、 頭痛の種以外のナニモノでもありません。

とはいえ、現状の把握から逃げることはできません。ざっくりとでもよいので、 情報をかき集めたいですよね。

というわけで今回は、「arpwatch」を使って、 LAN上にいるマシンをある程度洗い出してみたいと思います。

arpwatch とは? ARP…とは? MACアドレス…って?

arpwatch は、LAN上を飛び交う「ARP(Address Resolution Protocol)」をもとに、 IP アドレスと MAC アドレスの組を洗い出し、報告するデーモンです。

ARP とは、IPアドレスから MAC アドレスを得るためのプロトコルです。

同じサブネット内のマシンとは、ルータを介さず直接通信するのですが、 そのとき、相手のIPアドレスだけでは通信できません。 LANカードなどのインタフェースに割り当てられている「MACアドレス」が必要です。

そこで、このIPアドレスを使っているマシンのMACアドレスを教えてくれ、 という ARP リクエストパケットを、ブロードキャストします。
すると、相手が、MACアドレスはこれですという ARP リプライパケットを返します。

また、それだけでなく、私のMACアドレスはこれになりましたよと、 自らカミングアウトすることもあります。(それが偽りのこともまれに…。)

arpwatch は、それらの情報を集めて、メールで通知したり、 ファイルに書き出したりしてくれる、便利なひとです。

たいていのディストリビューションには、同名のパッケージがあります。
いつものように、インストールからはじめてください。

  # yum install arpwatch            (RedHat系の場合)
  $ sudo apt-get install arpwatch   (Debian系の場合)

 

ちなみに、MACアドレスの長さは48ビットですが、 そのうち前半24ビットがベンダーIDで、下記に登録されています。 MACアドレスを見れば、どこのベンダーのものかが、一発でわかります。

http://standards.ieee.org/develop/regauth/oui/oui.txt

arpwatch を動かします

arpwatch は、1つのネットワークインタフェースを見て動作します。
デフォルトの eth0 から情報を取得するなら、何も設定せず、 サービスを起動するだけです。

  # service arpwatch start
  (くどいようですが、root権限がなければsudoコマンド経由で。)

eth0 以外のこのインタフェースから、という場合、

  /etc/sysconfig/arpwatch  (RedHat系の場合)
  /etc/defaults/arpwatch   (Debian系の場合)

の中の、RedHat系なら「OPTIONS」、 Debian系なら「ARGS」という変数に「-i インタフェース名」を追加します。 たとえば、eth1 を指定するなら下記のようにします。

  ARGS="-N -p -i eth1"  # もとは "-N -p" でした

また、Debian系の場合は、/etc/arpwatch.confの各行に、 インタフェース名とオプションを書いておくと、 それらのインタフェースを全部見てくれます。 (実のところは、その数だけ arpwatch を起動します。)

通知のメールは、デフォルトでは root 宛に届きます。これを変更したい場合、 RedHat系では「-e」、Debian系では「-m」オプションを使用します。

 

それから、arpwatch をシステム起動時に自動で起動させるには、 いつものように以下を実行します。(Debian系ではすでにそうなっています。)

  # chkconfig arpwatch on             (RedHat系の場合)
  $ sudo update-rc.d arpwatch enable  (Debian系の場合)

逆に、起動しないようにするには、以下を実行します。

  # chkconfig arpwatch off             (RedHat系の場合)
  $ sudo update-rc.d arpwatch disable  (Debian系の場合)

ちゃんと収集しているか確認をします

とりあえず、動いているかどうかは、syslog を見ればわかります。

  # grep arpwatch /var/log/messages     (RedHat系の場合)
  $ sudo grep arpwatch /var/log/syslog  (Debian系の場合)

と実行すると、問題なければ下記のような出力が得られます。

  日時 ホスト名 arpwatch: Running as uid=数字 gid=数字
  日時 ホスト名 arpwatch: listening on インタフェース名(I/F名)

マシンを検知したときは、syslog に以下のメッセージが出力されます。

  日時 ホスト名 arpwatch: new station IPアドレス MACアドレス I/F名

そして、下記の内容のメールが届きます。

  From: arpwatch
  To: root
  Subject: new station (ホスト名) I/F名

            hostname: ホスト名
          ip address: IPアドレス
           interface: I/F名
    ethernet address: MACアドレス
     ethernet vendor: ↑のベンダー名
           timestamp: 日時

さらに、/var/lib/arpwatch/arp.dat(あるいはI/F名.dat)にも情報を出力します。 15分おきに、1行に1つ、下記のフォーマットで出力します。

  MACアドレス  IPアドレス  日時  ホスト名  I/F名

個々の間は、上記ではスペースになっていますが、実際はタブです。
具体的には、こんな感じです。(下記もスペースに変換しています。)

  $ sudo cat /var/lib/arpwatch/arp.dat
  00:01:02:03:04:05    192.168.1.1     1392463365     foo     eth0
  06:07:08:09:0a:0b    192.168.1.2     1392463369         eth0

日時は、1970年1月1日0時0分0秒から現在までの秒です。dateコマンドで変換できます。

  $ date --date='@1392463365'
  2014年  2月 15日 土曜日 20:22:45 JST

…と思ったら、RedHat系では、日付の情報まででした。

  # cat /var/lib/arpwatch/arp.dat
  00:11:22:33:44:55    10.0.1.1     1392463363
  66:77:88:99:aa:bb    10.0.1.2     1392463372

DBに書いてみます

さて、これでめでたしめでたし、にすると、 arpwatch を動かしただけになってしまいます。とはいえ、 いつものメールで飛ばす、は arpwatch がすでにしてくださっています。

じゃあ、前回からの流れで、fluentd 経由で MongoDB に登録してみようと思います。

なんだこの無理やり感は…と思わないよう、よろしくお願いいたします。
また、すみませんが、以降は Ubuntu 13.10 でのみ確認しています。

tailプラグインの場合、ファイルが変わったら(iノードが変わったら)、 先頭から読んでくれると書いてあります。確認してみると、

  $ sudo ls -1i /var/lib/arpwatch/*
  8675988 /var/lib/arpwatch/arp.dat
  8676013 /var/lib/arpwatch/arp.dat-

  …だいたい15分後…

  $ sudo ls -1i /var/lib/arpwatch/*
  8675934 /var/lib/arpwatch/arp.dat
  8675988 /var/lib/arpwatch/arp.dat-

ファイルが入れ替わるので、再読込してくれそうです。
というわけで、こんな設定を td-agent.conf に追加しました。

# 入力は tail プラグイン
<source>
  type tail
  path /var/lib/arpwatch/arp.dat
  tag arpwatch
  format tsv
  keys macaddr,ipaddr,time,host,iface
  time_key time
  time_format %s
  pos_file /var/log/td-agent/arpwatch.pos
</source>

# 出力は mongo プラグイン
<match arpwatch>
  type mongo
  host localhost
  port 27017
  database td-agent
  collection arpwatch
  capped
  capped_size 32m
  flush_interval 5m
</match>

arp.dat はタブ区切りなので、format は tsv でいけます。
Ubuntu で試しているため、keys では5つのキーを指定しています。
そして、MongoDB の td-agent データベースの arpwatch コレクションに出力します。 サイズは 32MB 限定にしましたが、特に根拠はありません。
arp.dat の更新は15分おきですが、念のため5分おきにしています。

さて、td-agent を restart して…の前に、 arp.dat は arpwatch ユーザかグループの人でないと読めません。熟考の末、 arpwatch グループの人にしてしまいます。

  $ sudo usermod -G 既存のグループ...,arpwatch td-agent

さて、td-agent を restart して、15分くらい経ったら、確認します。

  $ mongo
  > use td-agent
  > show collections
  arpwatch
  loadavg
  system.indexes
  ...中略...

arpwatch コレクションができています。
中身も、ちゃんと入っていました。

  > db.arpwatch.find()
  { "_id" : ObjectId("あいでぃー"), "macaddr" : "00:01:02:03:04:05", \
    "ipaddr" : "192.168.1.1", "host" : "foo", "iface" : "eth0", \
    "time" : ISODate("2014-02-15T13:09:52Z") }
  { "_id" : ObjectId("あいでぃー"), "macaddr" : "06:07:08:09:0a:0b", \
    "ipaddr" : "192.168.1.2", "host" : "", "iface" : "eth0", \
    "time" : ISODate("2014-02-15T13:09:52Z") }
  ...後略...

ただ、このやり方だと、MongoDB にどんどん追加されていきます。

  $ sudo wc -l /var/lib/arpwatch/arp.dat
  16
  $ mongo
  > use td-agent
  > db.arpwatch.count()
  51

上記の場合、arpwatch の検出総数が16個なのに対し、 MongoDB には51個も書き出されています。 古いのを残したまま追記していくからですね。

全部記録したいという方もいらっしゃるとは思いますが、今欲しいのは、 今のIPアドレスとMACアドレスの組です。ただ、 fluentd の範疇だけではどうしようもなさそうです。
…仕方ないので、スクリプトで対処してみました。

#!/usr/bin/ruby
require 'mongo'
coll = Mongo::Connection.new.db("td-agent").collection("arpwatch")

macaddrs = Hash::new
times = Hash::new
counts = Hash::new(0)
puts "count : #{coll.count}"
coll.find({}, :sort => ["time", Mongo::ASCENDING]).each { |row|
    macaddrs[row["ipaddr"]] = row["macaddr"]
    times[row["ipaddr"]] = row["time"]
    counts[row["ipaddr"]] += 1
}

macaddrs.each_pair { |key, value|
    printf("%-15s : %-17s : %s (%d)\n", key, value, times[key], counts[key])
}

MongoDB から古い順に読んで、IPアドレスをキーとした連想配列に入れるだけ、 の Ruby スクリプトです。結果的に最新の情報だけを出力します。

あ、もし Ruby が入っていなければ、入れてください。

  $ sudo apt-get install ruby ruby-mongo

上記を、たとえば showarpwatch.rb というファイル名で保存したなら、 実行権をつけて(初回のみ)、実行してみてください。

  $ chmod +x showarpwatch.rb
  $ ./showarpwatch.rb
  192.168.1.1     : 00:01:02:03:04:05 : 2014-02-15 13:59:04 UTC (8)
  192.168.1.2     : 06:07:08:09:0a:0b : 2014-02-15 12:12:23 UTC (8)
  192.168.1.3     : 0c:0d:0e:0f:00:01 : 2014-02-15 13:33:10 UTC (8)
  ...後略...

全部ではなく、最新の情報だけが出力されます。されるはずです。

<> 手でコマンド実行するのか…などといった、釈然としない気持ちを抱いた貴兄は、 execプラグインで外部コマンドに託してみてくださいませ。

ちなみに、fluentd に関しては、前回と前々回に紹介しております。

Vol.253 - fluentd を使ってみる
http://www.usupi.org/sysad/253.html
Vol.254 - fluentd を使ってみる / ジャスティス・フォーエバー
http://www.usupi.org/sysad/254.html

おわりに

以上、arpwatch を使ってMACアドレスをかき集めてみました。

当初は、怪しいマシンのMACアドレスを調べて、 犯人探しっぽいことをやるつもりでいたのですが、 前職で arpwatch を使っていたことを思い出し、 まるっと情報を得る方が有用なんじゃないかと思い、方針を変えました。

そして最後は、fluentd に無理やりこじつけてみました。
…いえ、これは必然だったのではないかと思います。思うことにします。

arpwatch は、最近更新されていない感がものすごくしますが、 作り自体はシンプルなので、他と組合せていろいろできたりすると思います。
ぜひ、いろいろやってみてくださいませ。

宿題の答え

前回の宿題は、

  Fluentd で、特定のログに絞ったりなど、フィルター的なことができる
  かどうか、試してみましょう。

でした。

プラグインには、inputとoutputの他に、「buffer」があります。

Bufferプラグインの概要 | Fluentd
http://docs.fluentd.org/ja/articles/buffer-plugin-overview

でも、出力時のバッファリングに関するプラグインで、 入出力の間で何か小細工するためのものではなさそうです。

となると、入力時に絞るしかなさそうです。
以降では、syslog から、ntpd のログだけをファイルに記録する、 ということをやってみたいと思います。

まず、tailプラグインで syslog を全部ファイルに記録しようとすると、 設定はこんな感じになります。

<source>
  type tail
  path /var/log/syslog
  tag syslog.all
  format syslog
  pos_file /var/log/td-agent/syslog.pos
</source>
<match syslog.all>
  type file
  path /var/log/td-agent/syslog
  flush_interval 10s
</match>

format に syslog を指定していますが、format の1行を正規表現で表すと、 こうなります。

format /^(?<time>[^ ]* [^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/
time_format %b %d %H:%M:%S

ちなみに、上記設定は、下記からそのまま持ってきています。

tailインプットプラグイン | Fluentd
http://docs.fluentd.org/ja/articles/in_tail

ident が ntpd のときだけに絞ればよいので、まるっと置き換えます。

format /^(?<time>[^ ]* [^ ]* [^ ]*) (?<host>[^ ]*) ntpd(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/
time_format %b %d %H:%M:%S

…ほんとにこれでいいのでしょうか。
実は、テストするサイトがあります。

Fluentular: a Fluentd regular expression editor
http://fluentular.herokuapp.com/

上記にアクセスし、Regular Expression に、 format で指定した正規表現(前後の「/」は除く)を入力します。

^(?<time>[^ ]* [^ ]* [^ ]*) (?<host>[^ ]*) ntpd(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$

Test String には、syslog の出力から適当なものを選んで入力します。

Feb 15 14:23:00 tamao ntpd[21655]: peers refreshed

Parseボタンをクリックして、Match Groupsに host, pid および message が出力されていれば、おそらく成功です。

あとは、この設定を td-agent.conf などに追記して、 td-agentサービスを再起動するだけです。
いちおう、設定例を以下に示します。

<source>
  type tail
  path /var/log/syslog
  tag syslog.ntpd
  format /^(?<time>[^ ]* [^ ]* [^ ]*) (?<host>[^ ]*) ntpd(?:\[(?<pid>[0-9]+)\])?[^\:]*\: *(?<message>.*)$/
  time_format %b %d %H:%M:%S
  pos_file /var/log/td-agent/ntpd.pos
</source>
<match syslog.ntpd>
  type file
  path /var/log/td-agent/ntpd.log
  flush_interval 10s
</match>

ただ、マッチしないログは、warningとしてtd-agent.logに怒涛のごとく出力されます。 ご注意ください。

今回の宿題

今回の宿題は、

  arp.dat の内容をDBに登録(更新)するようにしてみましょう。

です。

fluentd を使わず、スクリプトかなにかでDBに直接書いてしまえばいいのでは、 と思った貴兄は、トライしてみてください。

できれば、スクリプトを手で動かさなくてよいようにしてください。

あとがき

最近、複数台の Windows PC を、 Linux とのデュアルブートにするという経験(お仕事)を、やや間接的にしています。

どうやると楽に出来るのかなと思い、Google様に伺ってみると、 もう大半が仮想環境で、 イメージをコピーするだけ(なのでほぼ無料!)という情報ばかり集まりました。

物理的なマシンでも、Vagrant など使って自動で構築できたらなあなどと思いつつ、 結局、全台を同じ手順でインストールしていく、 というベタな人海戦術的方法に落ち着きました。

PCのファームウェアが BIOS から UEFI になり、 パーティションテーブルが MBR から GPT に遷移していますが、 HDD の物理的な構成に左右されるところは、あまり解消されていないように思います。

それこそ、ディスクイメージをコピーしたら動いてしまうくらい 抽象化されていると楽なんだけどなあと思いながら、 そのためにどうあるべきかなど考える能力も余裕もなく、現在に至っています。

じゃあ、Windows の仮想環境で Linux のイメージを動かせばいいのでは? ということになるわけですが、 カメラなどをLinuxで直接扱いたいというニーズもありまして…。

ま、そうこうしているうちに、PC がどんどん使われなくなっていって、 そんな悩みもなくなる(あるいは別の悩みになる)かもしれませんね。

 

今回も、ここまで読んでいただき、誠にありがとうございました。
次回は、3月2日(日) の未明にお会いしましょう!

 

「いますぐ実践! Linux システム管理」の解除は、以下からできます。
http://www.usupi.org/sysad/ (まぐまぐ ID:149633)

バックナンバーは、こちらにほぼ全部そろっています。
http://www.usupi.org/sysad/backno.html

「栗日記」- ツッコみやすい絵がよい絵な気がしてきました。
http://www.usupi.org/kuri/ (まぐまぐ ID:126454)
http://usupi.seesaa.net/ (栗日記ブログ)
http://usupi.org/k/ (モバイル栗日記)
http://twitter.com/kuriking/ (栗つぶやき)
http://facebook.com/kuriking3 (栗顔本)


[バックナンバーのトップへ] [Linux システム管理のトップへ]

トップ

バックナンバー
    [日付順] [目的別]

プロフィール

▼ リンク

独学Linux
Linuxデスクトップ環境に関する情報が満載です。 メルマガもありますよ。
Server World
CentOS 6をサーバとしたときの設定例が、これでもかというくらいたくさん載っています。 CentOS以外のディストリビューション(Fedora, Ubuntu)も充実しています。
LINUXで自宅サーバーを構築・導入(Fedora9)
Fedora9のインストールの仕方から管理方法まで、詳しく載っています。 SearchManには情報がもりだくさんです。
マロンくん.NET
〜サーバ管理者への道〜
Linuxをサーバとして使用するための、いろいろな設定方法が載っています。 マロンくんもかわいいです。 なんといっても、マロンくんという名前がいいですね!!
日経Linux
今や数少なくなってしまったLinuxの雑誌。ニュースやガイドもあります。
Linux Square − @IT
@ITが提供する、Linux の情報が満載。 載っていない設定方法はないんじゃないでしょうか。
gihyo.jp…技術評論社
Linuxに限らず様々な技術情報が満載のサイト。 SoftwareDesign誌も、 ソフトウェア技術者は必見です。
SourceForge.JP Magazine
Linux に限らず、オープンソース関連の記事が網羅されています。
ITmediaエンタープライズ:Linux Tips 一覧
Tips というより FAQ 集でしょうか。わからないことがあれば覗きましょう。
IBM developerWorks : Linux
開発者向けですが、勉強になりますよ。
Yahoo!ニュース - Linux
Yahoo!のLinuxに関するニュース一覧です。
栗日記
システム管理とかと全然関係ありませんが、毎日栗の絵を描いています。
システム管理につかれちゃったとき、癒されたいときに、ご覧ください。:-)
WEB RANKING - PC関連
ランキングに参加してみました。押してやってください。

▼ 作ってみました

Add to Google

▼ せんでん




▼ 最近読んだ本

▼ 気に入ってる本