autodrop 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,202 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'optparse'
4
- require 'syslog'
5
-
6
- DEFAULT_CONFFILE = '/etc/autodrop.conf'
7
-
8
- $debug = false
9
-
10
- def log msg
11
- if $debug
12
- puts "#{Time.now}: syslog: #{msg}"
13
- else
14
- if Syslog.opened?
15
- Syslog.info msg
16
- end
17
- end
18
- end
19
-
20
- def debug msg
21
- return unless $debug
22
- puts "#{Time.now}: debug: #{msg}"
23
- end
24
-
25
- def terminate whymsg, status = 0
26
- log "terminate (#{whymsg})"
27
- exit! status
28
- end
29
-
30
- module Autodrop
31
- # { ip => Dog obj }
32
- @dogs = {}
33
-
34
- def Autodrop.house ip
35
- @dogs.delete ip
36
- end
37
-
38
- def Autodrop.watch
39
- log "start watching '#{WATCH_FIFO_}'"
40
- loop {
41
- begin
42
- File.open(WATCH_FIFO_, File::RDONLY | File::NONBLOCK) { |f|
43
- ready = File.select([f])
44
- if ready
45
- line = ready[0][0].gets
46
- ip = nil
47
- MESSAGES_TO_WATCH.each { |msg|
48
- ip = $1 if line =~ msg
49
- }
50
- if ip
51
- if @dogs.has_key? ip
52
- @dogs[ip].bark
53
- else
54
- @dogs[ip] = Dog.new(ip)
55
- end
56
- end
57
- end
58
- }
59
- rescue Errno::ENOENT => ex
60
- log "lost FIFO '#{WATCH_FIFO_}'. exit"
61
- break
62
- end
63
- }
64
- end
65
- end # Autodrop
66
-
67
- class Dog
68
- def initialize ip
69
- @ip = ip
70
- @count = 1
71
- @time = Time.now + INTERVAL
72
- @thread = Thread.new {
73
- debug "[#{@ip}] bark! (#{@count})"
74
- loop {
75
- if Time.now >= @time
76
- debug "[#{@ip}] leave"
77
- break
78
- end
79
- if @count >= COUNT_MAX
80
- bite
81
- break
82
- end
83
- debug "[#{@ip}] grrr..."
84
- sleep 1
85
- }
86
- Autodrop.house @ip
87
- debug "[#{@ip}] end"
88
- }
89
- end
90
-
91
- def bark
92
- @count += 1
93
- @time = Time.now + INTERVAL
94
- debug "[#{@ip}] bark! (#{@count})"
95
- end
96
-
97
- def bite
98
- if $debug
99
- log "DROP (#{@ip})"
100
- return
101
- end
102
- if system(IPTABLES_PROGRAM, '-I', 'INPUT', '-s', @ip, '-j', 'DROP')
103
- log "DROP (#{@ip})"
104
- else
105
- log "error (iptables fail)"
106
- end
107
- end
108
- end # Dog
109
-
110
- ### main
111
-
112
- @conffile = nil
113
- opts = OptionParser.new
114
- opts.on("-c CONFFILE", "--config CONFFILE", String, /.*/,
115
- "Configuration file",
116
- "(default: '#{DEFAULT_CONFFILE}')") { |conffile|
117
- @conffile = conffile
118
- }
119
- opts.on("-d", "--debug", nil, nil,
120
- "Debug mode",
121
- "+ no daemon",
122
- "+ write logs to stdout",
123
- "+ watch fifo named './fifo'") { |flag|
124
- $debug = true
125
- }
126
- opts.parse! ARGV
127
- opts = nil
128
-
129
- @conffile ||= DEFAULT_CONFFILE
130
- unless File.file? @conffile
131
- puts "#{@conffile} does not exist."
132
- exit 1
133
- end
134
-
135
- eval File.readlines(@conffile).join("\n")
136
-
137
- unless File.executable? IPTABLES_PROGRAM
138
- puts "#{IPTABLES_PROGRAM} is not executable."
139
- exit 1
140
- end
141
-
142
- if $debug
143
- WATCH_FIFO_ = 'fifo'
144
- else
145
- WATCH_FIFO_ = WATCH_FIFO
146
- if Process.euid != 0
147
- puts 'Run as root'
148
- exit 1
149
- end
150
- end
151
-
152
- begin
153
- Syslog.open('autodrop', Syslog::LOG_PID|Syslog::LOG_CONS, Syslog::LOG_AUTH)
154
- if $debug
155
- Autodrop.watch
156
- else
157
- # daemonify self
158
- Process.fork {
159
- Process.setsid
160
- Dir.chdir "/"
161
- trap("SIGINT") { terminate 'SIGINT' }
162
- trap("SIGTERM") { terminate 'SIGTERM' }
163
- trap("SIGHUP") { terminate 'SIGHUP' }
164
- STDIN.reopen "/dev/null"
165
- STDOUT.reopen "/dev/null"
166
- STDERR.reopen "/dev/null"
167
- File.open(PIDFILE, 'w') {|f|
168
- f.puts Process.pid
169
- }
170
- Autodrop.watch
171
- }
172
- end
173
- rescue => ex
174
- log "error (#{ex}). exit"
175
- exit! 1
176
- end
177
-
178
- # Copyright (c) 2009, NOZAWA Hiromasa. All rights reserved.
179
- #
180
- # Redistribution and use in source and binary forms, with or without
181
- # modification, are permitted provided that the following conditions
182
- # are met:
183
- #
184
- # 1. Redistributions of source code must retain the above copyright
185
- # notice, this list of conditions and the following disclaimer.
186
- # 2. Redistributions in binary form must reproduce the above
187
- # copyright notice, this list of conditions and the following
188
- # disclaimer in the documentation and/or other materials provided
189
- # with the distribution.
190
- #
191
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
192
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
193
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
194
- # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
195
- # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
196
- # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
197
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
198
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
199
- # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
200
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
201
- # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
202
- # POSSIBILITY OF SUCH DAMAGE.
@@ -1,202 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'optparse'
4
- require 'syslog'
5
-
6
- DEFAULT_CONFFILE = '/etc/autodrop.conf'
7
-
8
- $debug = false
9
-
10
- def log msg
11
- if $debug
12
- puts "#{Time.now}: syslog: #{msg}"
13
- else
14
- if Syslog.opened?
15
- Syslog.info msg
16
- end
17
- end
18
- end
19
-
20
- def debug msg
21
- return unless $debug
22
- puts "#{Time.now}: debug: #{msg}"
23
- end
24
-
25
- def terminate whymsg, status = 0
26
- log "terminate (#{whymsg})"
27
- exit! status
28
- end
29
-
30
- module Autodrop
31
- # { ip => Dog obj }
32
- @dogs = {}
33
-
34
- def Autodrop.house ip
35
- @dogs.delete ip
36
- end
37
-
38
- def Autodrop.watch
39
- log "start watching '#{WATCH_FIFO_}'"
40
- loop {
41
- begin
42
- File.open(WATCH_FIFO_, File::RDONLY | File::NONBLOCK) { |f|
43
- ready = File.select([f])
44
- if ready
45
- line = ready[0][0].gets
46
- ip = nil
47
- MESSAGES_TO_WATCH.each { |msg|
48
- ip = $1 if line =~ msg
49
- }
50
- if ip
51
- if @dogs.has_key? ip
52
- @dogs[ip].bark
53
- else
54
- @dogs[ip] = Dog.new(ip)
55
- end
56
- end
57
- end
58
- }
59
- rescue Errno::ENOENT => ex
60
- log "lost FIFO '#{WATCH_FIFO_}'. exit"
61
- break
62
- end
63
- }
64
- end
65
- end # Autodrop
66
-
67
- class Dog
68
- def initialize ip
69
- @ip = ip
70
- @count = 1
71
- @time = Time.now + INTERVAL
72
- @thread = Thread.new {
73
- debug "[#{@ip}] bark! (#{@count})"
74
- loop {
75
- if Time.now >= @time
76
- debug "[#{@ip}] leave"
77
- break
78
- end
79
- if @count >= COUNT_MAX
80
- bite
81
- break
82
- end
83
- debug "[#{@ip}] grrr..."
84
- sleep 1
85
- }
86
- Autodrop.house @ip
87
- debug "[#{@ip}] end"
88
- }
89
- end
90
-
91
- def bark
92
- @count += 1
93
- @time = Time.now + INTERVAL
94
- debug "[#{@ip}] bark! (#{@count})"
95
- end
96
-
97
- def bite
98
- if $debug
99
- log "DROP (#{@ip})"
100
- return
101
- end
102
- if system(IPTABLES_PROGRAM, '-I', 'INPUT', '-s', @ip, '-j', 'DROP')
103
- log "DROP (#{@ip})"
104
- else
105
- log "error (iptables fail)"
106
- end
107
- end
108
- end # Dog
109
-
110
- ### main
111
-
112
- @conffile = nil
113
- opts = OptionParser.new
114
- opts.on("-c CONFFILE", "--config CONFFILE", String, /.*/,
115
- "Configuration file",
116
- "(default: '#{DEFAULT_CONFFILE}')") { |conffile|
117
- @conffile = conffile
118
- }
119
- opts.on("-d", "--debug", nil, nil,
120
- "Debug mode",
121
- "+ no daemon",
122
- "+ write logs to stdout",
123
- "+ watch fifo named './fifo'") { |flag|
124
- $debug = true
125
- }
126
- opts.parse! ARGV
127
- opts = nil
128
-
129
- @conffile ||= DEFAULT_CONFFILE
130
- unless File.file? @conffile
131
- puts "#{@conffile} does not exist."
132
- exit 1
133
- end
134
-
135
- eval File.readlines(@conffile).join("\n")
136
-
137
- unless File.executable? IPTABLES_PROGRAM
138
- puts "#{IPTABLES_PROGRAM} is not executable."
139
- exit 1
140
- end
141
-
142
- if $debug
143
- WATCH_FIFO_ = 'fifo'
144
- else
145
- WATCH_FIFO_ = WATCH_FIFO
146
- if Process.euid != 0
147
- puts 'Run as root'
148
- exit 1
149
- end
150
- end
151
-
152
- begin
153
- Syslog.open('autodrop', Syslog::LOG_PID|Syslog::LOG_CONS, Syslog::LOG_AUTH)
154
- if $debug
155
- Autodrop.watch
156
- else
157
- # daemonify self
158
- Process.fork {
159
- Process.setsid
160
- Dir.chdir "/"
161
- trap("SIGINT") { terminate 'SIGINT' }
162
- trap("SIGTERM") { terminate 'SIGTERM' }
163
- trap("SIGHUP") { terminate 'SIGHUP' }
164
- STDIN.reopen "/dev/null"
165
- STDOUT.reopen "/dev/null"
166
- STDERR.reopen "/dev/null"
167
- File.open(PIDFILE, 'w') {|f|
168
- f.puts Process.pid
169
- }
170
- Autodrop.watch
171
- }
172
- end
173
- rescue => ex
174
- log "error (#{ex}). exit"
175
- exit! 1
176
- end
177
-
178
- # Copyright (c) 2009, NOZAWA Hiromasa. All rights reserved.
179
- #
180
- # Redistribution and use in source and binary forms, with or without
181
- # modification, are permitted provided that the following conditions
182
- # are met:
183
- #
184
- # 1. Redistributions of source code must retain the above copyright
185
- # notice, this list of conditions and the following disclaimer.
186
- # 2. Redistributions in binary form must reproduce the above
187
- # copyright notice, this list of conditions and the following
188
- # disclaimer in the documentation and/or other materials provided
189
- # with the distribution.
190
- #
191
- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
192
- # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
193
- # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
194
- # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
195
- # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
196
- # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
197
- # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
198
- # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
199
- # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
200
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
201
- # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
202
- # POSSIBILITY OF SUCH DAMAGE.
@@ -1,17 +0,0 @@
1
- # configration file for autodrop
2
- # -*-ruby-*-
3
-
4
- # $1 must match with an IP address
5
- MESSAGES_TO_WATCH =
6
- [
7
- # OpenSSH
8
- /Invalid user [^\s]+ from (.+)/,
9
- /Address (.+) maps to [^\s]+, but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!/,
10
- ]
11
-
12
- COUNT_MAX = 3
13
- INTERVAL = 10 # sec
14
-
15
- IPTABLES_PROGRAM = '/sbin/iptables'
16
- PIDFILE = '/var/run/autodrop.pid'
17
- WATCH_FIFO = '/var/log/authfifo'
@@ -1,164 +0,0 @@
1
- autodrop README
2
-
3
- NOZAWA Hiromasa, Tokyo, Japan
4
-
5
-
6
- = 概要
7
-
8
- ホストへのしつこいアクセスを iptables で弾くデーモン。
9
-
10
- syslog のログを監視して、パターンにマッチするログを一定期間内に繰り返し
11
- 生じさせるホストを iptables の DROP ルールでアクセス禁止にする。
12
-
13
- 監視するログのパターン、ログの発生回数、発生回数のカウントを続ける期間
14
- を設定できる。パターンは複数設定できる。
15
-
16
- 複数のログファイルをポーリングしたり stat したりせずに、単一の名前つき
17
- パイプを見るようになっている。このため syslogd が目的のログを名前つきパ
18
- イプへ吐くように設定する必要がある (syslog.conf で '|' を使う)。
19
-
20
- Ruby 製です。Ruby なので、というかスクリプト言語製なので本格的に高トラ
21
- フィックな環境には向かないでしょう。でもウチでは毎日々々延々と 22 番を
22
- 叩きにくる人たちをしっかり弾いてくれるので重宝してます。なかなか重宝す
23
- るので公開してみます。
24
-
25
- この手のツールの常として自分を自分のホストから締め出すことも可能なので
26
- 注意してください。
27
-
28
-
29
- = 必要なもの
30
-
31
- * iptables なので linux
32
- * syslogd
33
- * ruby 1.8.6 あたり
34
-
35
-
36
- = ライセンス
37
-
38
- BSD
39
-
40
-
41
- = インストール
42
-
43
- gem install したあと設定ファイルを作る必要があります。
44
- #! 行も必要なら書き換えてください。
45
-
46
- *1. gem install autodrop
47
-
48
- *2. cd <GEMDIR>/gems/autodrop-x.x.x/conf
49
-
50
- *3. sudo cp autodrop.conf.default /etc
51
-
52
- *6. /etc/autodrop.conf を編集
53
-
54
-
55
- = 使い方
56
-
57
- == 起動
58
-
59
- root で実行するようになってます。
60
-
61
- ------------------------------
62
- $ sudo ruby autodrop.rb
63
- ------------------------------
64
-
65
- 設定ファイルのデフォルトは /etc/autodrop.conf です。
66
- コマンドラインから指定すれば別の場所にある設定ファイルも使えます。
67
-
68
- ------------------------------
69
- $ ruby autodrop.rb -c /foo/bar/autodrop.conf
70
- ------------------------------
71
-
72
- 起動すると /var/run/autodrop.pid が作られます。
73
-
74
- == 停止
75
-
76
- ------------------------------
77
- $ sudo kill `cat /var/run/autodrop.pid`
78
- ------------------------------
79
-
80
- == 稼働中
81
-
82
- autodrop 自身もプレフィクス `autodrop' で起動、終了、DROP の発生を
83
- syslog に吐きます。
84
-
85
- 稼働中は iptables の INPUT テーブルに DROP ルールが溜まる一方になるので、
86
- たまに手でクリアすることになるかと思います。定期的にクリアするような機
87
- 能はありません。
88
-
89
-
90
- = autodrop.conf
91
-
92
- どの設定も省略不可。どれも単なる ruby の定数です。
93
-
94
- * MESSAGES_TO_WATCH
95
-
96
- 監視するメッセージのパターンを正規表現で書いた配列。
97
- $1 がリモートホストの IP アドレスにマッチするようにする。
98
-
99
- ------------------------------
100
- MESSAGES_TO_WATCH =
101
- [
102
- # OpenSSH's
103
- /Invalid user [^\s]+ from (.+)/,
104
- /Address (.+) maps to.*POSSIBLE BREAK-IN ATTEMPT!/,
105
- ]
106
- ------------------------------
107
-
108
- * COUNT_MAX
109
-
110
- DROP するまでのマッチ回数。
111
-
112
- ------------------------------
113
- COUNT_MAX = 3
114
- ------------------------------
115
-
116
- * INTERVAL
117
-
118
- マッチしたパターン (パターンと IP アドレスの組) についてカウン
119
- トをとり続ける時間。秒で指定する。
120
- マッチするたびに 0 に戻る。
121
-
122
- ------------------------------
123
- INTERVAL = 10
124
- ------------------------------
125
-
126
- * IPTABLES_PROGRAM
127
-
128
- 用いる iptables を指定する。
129
-
130
- ------------------------------
131
- IPTABLES_PROGRAM = '/bin/iptables'
132
- ------------------------------
133
-
134
- * PIDFILE
135
-
136
- pid ファイルのパス。
137
-
138
- ------------------------------
139
- PIDFILE = '/var/run/autodrop.pid'
140
- ------------------------------
141
-
142
- * WATCH_FIFO
143
-
144
- 監視する名前つきパイプのパス。
145
-
146
- ------------------------------
147
- WATCH_FIFO = '/var/log/authfifo'
148
- ------------------------------
149
-
150
-
151
- = 参考: syslogd の設定例
152
-
153
- パイプを作って、
154
- ------------------------------
155
- sudo mkfifo /var/log/authfifo
156
- ------------------------------
157
- syslog.conf に登録する。
158
- ------------------------------
159
- authpriv.* |/var/log/authfifo
160
- ------------------------------
161
-
162
- 「|」で名前つきパイプへの出力になる。詳しくは syslog.conf(5) で。
163
-
164
- #eof