Rubyに挑戦中です。Perl歴11年(でも知識はPerl4止まり)の私はどうしてもPerlっぽいRubyを書いてしまうのですが、このスクリプトをRubyっぽく書くには、どうしたらいいのでしょうか。お題は、syslogを監視して、sshdへの不正っぽいアクセスが連続したら/etc/hosts.denyにアクセス拒否リストを追加するスクリプトです。

http://d.hatena.ne.jp/pekeq/20060109/p1

回答の条件
  • URL必須
  • 1人2回まで
  • 登録:2006/01/09 22:27:14
  • 終了:--

回答(2件)

id:kusigahama No.1

櫛ヶ浜やぎ回答回数4ベストアンサー獲得回数02006/01/10 12:25:28

ポイント25pt

http://jp.rubyist.net/magazine/?0011-CodeReview#l41

Rubyist Magazine - あなたの Ruby コードを添削します 【第 2 回】 HexStruct.rb

こちらに応募されるといいのではないでしょうか。

というだけではナンなので、たいしたことは言えませんが少しだけ書いてみます。


print cmd + ”¥n”

は、

puts cmd

と書けます。


@@badHosts = Hash.new

@@badHosts = Hash.new {|hash, key| hash[key] = 0 }

とする(デフォルト値を指定する)ことで、25行目からの

if @@badHosts[host] then

 @@badHosts[host] += 1

else

 @@badHosts[host] = 1

end

を、

@@badHosts[host] += 1

だけにできます。


45行目からの

x_month = m[1]

x_date = m[2]

x_time = m[3]

……は、

x_all, x_month, x_date, x_time, …… = m

と多重代入できます。……が、これは元のままの方が見やすいですかね。


56行目からの判定部分は同様の処理が連続しているので

[

 # Invalid user bgray from ::ffff:82.85.1.1

 [x_arg, /^Invalid user .* from (::ffff:)?([-.0-9A-Za-z]+)/, 2],

 

 # User root from 10.10.10.10 not allowed because not listed in AllowUsers

 [x_arg, /^User .* from (::ffff:)?([-.0-9A-Za-z]+) not allowed/, 2],

 

 # Did not receive identification string from ::ffff:164.125.1.1

 [x_arg, /^Did not receive identification string from (::ffff:)?([-.0-9A-Za-z]+)/, 2],

 

].each{|arg, re, n|

 next if arg !~ re

 addBad Regexp.last_match[n]

}

などと、まとめてしまうと良いかも。

もう少し汎用的には

[

 # Invalid user bgray from ::ffff:82.85.1.1

 proc{

  x_arg =~ /^Invalid user .* from (::ffff:)?([-.0-9A-Za-z]+)/ ? $2 : nil

 },

 # User root from 10.10.10.10 not allowed because not listed in AllowUsers

 proc{

  x_arg =~ /^User .* from (::ffff:)?([-.0-9A-Za-z]+) not allowed/ ? $2 : nil

 },

 # Did not receive identification string from ::ffff:164.125.1.1

 proc{

  x_arg =~ /^Did not receive identification string from (::ffff:)?([-.0-9A-Za-z]+)/ ? $2 : nil

 },

].each{|pr|

 x_badhost = pr.call

 addBad(x_badhost) if x_badhost

}

という感じでしょうか。

id:pekeq

Hashのデフォルト値というのは、こうやって使うんですね!「いったい何に使うんだろう」と思っていました。ありがとうございます。

2006/01/10 13:23:32
id:rudeboyjet No.2

rudeboyjet回答回数11ベストアンサー獲得回数12006/01/11 10:13:58

ポイント25pt

http://www.ruby-lang.org/ja/man/

Rubyリファレンスマニュアル - Rubyリファレンスマニュアル

class Observation

  def Observation.start(arg)

    Observation.new.start(arg)

  end


  def initialize

    @blacklist = Hash.new{|hash,key| hash[key] = 0}

    @judgement = 10

  end

  

  def start(target)

    pipe = IO.popen(”tail -f #{target}”,’r’)

    loop do

      if (input = pipe.gets)

        result = analyze(input)

        if result[’protocol’] =~ /ssh/ && result[’success?’] == false

          add_black(result[’ip’])

        end


        add_deny(result[’ip’]) if @blacklist[result[’ip’]] > @judgement

      end

    end

  end


  def analyze(str)

    flag = true

    ary = str.split

    date = ary.slice!(0..2).join(’ ’)

    host  = ary.shift

    protocol = ary.shift

    if pos = ary.index(’from’)

      ip = ary.slice!(pos + 1)

    end

      flag = false if ary.include?(’not’||’invalid’)


    return {’date’ => ”#{date}”, ’host’ => ”#{host}”, ’protocol’ => ”#{protocol}”,

    ’ip’ => ”#{ip}”, ’success?’ => ”#{flag}”}

  end


  def add_black(ip)

    @blacklist[ip] += 1

  end


  def add_deny(ip)

    file = File.open(”/etc/hosts.deny”,’a+’)

    file.flock(File::LOCK_EX)

    file.puts(ip)

    file.flock(File::LOCK_UN)

    file.close

  end

end


if $0 == __FILE__

  Observation.start(’/var/log/messages’)

end


Rubyっぽくってことでこんな感じでどうでしょう?

急ごしらえなんで実用レベルのものではないですけど。

id:pekeq

なるほど。1)まずはClassを作り、2)コマンドラインから呼び出された場合はif $0==__FILE__で処理する というのがRubyっぽい構造に見えますね。Class内部もRubyっぽくてステキです

2006/01/11 13:30:06

コメントはまだありません

この質問への反応(ブックマークコメント)

トラックバック

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません