RubyでIPアドレスからホスト名を得て、IRCに通知させる

#ruby

ただResolv#getname1を使っただけなんですが、どういう目的で使いたかったのかを説明します。

背景

ペパボではコミュニケーションツールとして主にIRCが用いられています。 一方、外部とのやりとりにはまだまだメールは廃れておりません。 また、ホスティングのようなサービスを運営していると、不正利用との戦いは避けられず、時折外部のセキュリティ会社やabuse対策チーム等から警告メールが届くことがあります2

課題

エンジニアは作業に集中すると、メールチェックを忘れてしまうため、重要なメールはIRCで通知させています3。 先ほどの警告メールのような場合、その件名にIPアドレスが表記されていることがあります。

※特定のメールが届くと、以下のようにikachan経由で通知が届く。

12:34:56  ikachan | Mail from notify@from.abuse => Title: ABUSE NUM#123456ABCDEF From (10.20.30.40)

ところが、サーバの管理台数が増えるに連れ、IPアドレスに対応するホスト名など覚えることは出来ず、またメールが届く度に逆引きすることは非効率です。

解決

最近ではHubotも人気ですが、今回はcinchを利用します45、 IRCへの通知内容からIPアドレスを検出し、ホスト名を出力されるために以下のようなコードを用意し、botとして常駐させておきます。

on :message, /(\d+\.\d+\.\d+\.\d+)/ do |m, ipaddr|
  require 'resolv'

  if ipaddr =~ Resolv::IPv4::Regex
    hostname = Resolv.getname(ipaddr)
  end

  m.target.notice "#{ipaddr} is pointed to #{hostname}"
end

先のような通知がIRCに届くと、マッチしたipaddrがIPv4がどうかを調べた上でホスト名を返すようになります。

00:43:54  -- | Notice(cinch): 10.20.30.40 is pointed to alpha.beta.gamma

まだまだ改良の余地はあると思いますが、まあこれぐらいでいいかな…というところから始めています。

ちなみに(\d+\.\d+\.\d+\.\d+)だけだとメール通知以外にも引っかかってしまうため、通知内容とマッチさせる条件を工夫するとよいでしょう。

余談

はじめ、on :messageの正規表現にResolv::IPv4::Regex6を使おうとしたんですが、以下のように\A ... \zだったので「IPアドレスが含まれる文字列」とmatch出来ませんでした。

$ ruby -r Resolv -e 'p Resolv::IPv4::Regex'
/\A((?x-mi:0
               |1(?:[0-9][0-9]?)?
               |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
               |[3-9][0-9]?))\.((?x-mi:0
               |1(?:[0-9][0-9]?)?
               |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
               |[3-9][0-9]?))\.((?x-mi:0
               |1(?:[0-9][0-9]?)?
               |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
               |[3-9][0-9]?))\.((?x-mi:0
               |1(?:[0-9][0-9]?)?
               |2(?:[0-4][0-9]?|5[0-5]?|[6-9])?
               |[3-9][0-9]?))\z/

終わりに

IRCに流れてきた内容にIPアドレスが含まれていた場合、そのホスト名を調べるのが面倒だったので、cinchを使って逆引きした結果をIRC通知するようにしました。

Rubyのドキュメントを読む度に便利そうなメソッドが見つかるので楽しいです。 Happy IRC, Happy Ruby Life!