autodrop 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +0 -0
- data/Rakefile +75 -0
- data/bin/autodrop +202 -0
- data/bin/autodrop.rb +202 -0
- data/conf/autodrop.conf.default +17 -0
- data/doc/README.jp.txt +164 -0
- data/doc/README.txt +165 -0
- data/misc/autodrop.sh +18 -0
- metadata +61 -0
data/ChangeLog
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
|
3
|
+
FILES = [
|
4
|
+
'ChangeLog',
|
5
|
+
'Rakefile',
|
6
|
+
'bin/autodrop',
|
7
|
+
'bin/autodrop.rb',
|
8
|
+
'conf/autodrop.conf.default',
|
9
|
+
'doc/README.jp.txt',
|
10
|
+
'doc/README.txt',
|
11
|
+
'misc/autodrop.sh',
|
12
|
+
]
|
13
|
+
|
14
|
+
desc "Same as 'rake bin/autodrop'"
|
15
|
+
task :default => [ "bin/autodrop" ]
|
16
|
+
|
17
|
+
desc "Make bin/autodrop"
|
18
|
+
file "bin/autodrop" do
|
19
|
+
sh "cp -f bin/autodrop.rb bin/autodrop"
|
20
|
+
sh "chmod 755 bin/autodrop"
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Do distclean"
|
24
|
+
task :distclean => [ :clobber_package ] do
|
25
|
+
sh "rm -f bin/autodrop"
|
26
|
+
end
|
27
|
+
|
28
|
+
task :gem => [ "bin/autodrop" ]
|
29
|
+
require 'rubygems'
|
30
|
+
require 'rake/gempackagetask'
|
31
|
+
spec = Gem::Specification.new do |s|
|
32
|
+
s.name = "autodrop"
|
33
|
+
s.version = "0.1.0"
|
34
|
+
s.author = "NOZAWA Hiromasa"
|
35
|
+
s.summary = "Automatic iptables DROP daemon"
|
36
|
+
s.homepage = 'http://rubyforge.org/projects/autodrop'
|
37
|
+
s.rubyforge_project = 'autodrop'
|
38
|
+
s.files = FILES
|
39
|
+
s.bindir = 'bin'
|
40
|
+
s.executables = 'autodrop'
|
41
|
+
s.require_path = []
|
42
|
+
end
|
43
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
44
|
+
pkg.need_zip = true
|
45
|
+
pkg.need_tar = true
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# for manual installation (without rubygem)
|
50
|
+
#
|
51
|
+
|
52
|
+
DEFAULT_BIN_INSTALL_DIR = '/usr/local/sbin'
|
53
|
+
DEFAULT_CONF_INSTALL_DIR = '/etc'
|
54
|
+
|
55
|
+
desc "Install (without rubygem)"
|
56
|
+
task :install => "bin/autodrop" do
|
57
|
+
bin_install_dir = ENV['BIN_INSTALL_DIR']
|
58
|
+
bin_install_dir ||= DEFAULT_BIN_INSTALL_DIR
|
59
|
+
conf_install_dir = ENV['CONF_INSTALL_DIR']
|
60
|
+
conf_install_dir ||= DEFAULT_CONF_INSTALL_DIR
|
61
|
+
directory bin_install_dir
|
62
|
+
directory conf_install_dir
|
63
|
+
sh "cp -f bin/autodrop #{bin_install_dir}/autodrop"
|
64
|
+
sh "cp -f conf/autodrop.conf.default #{conf_install_dir}/autodrop.conf.default"
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Uninstall (without rubygem)"
|
68
|
+
task :uninstall do
|
69
|
+
bin_install_dir = ENV['BIN_INSTALL_DIR']
|
70
|
+
bin_install_dir ||= DEFAULT_BIN_INSTALL_DIR
|
71
|
+
conf_install_dir = ENV['CONF_INSTALL_DIR']
|
72
|
+
conf_install_dir ||= DEFAULT_CONF_INSTALL_DIR
|
73
|
+
sh "rm -f #{bin_install_dir}/autodrop"
|
74
|
+
sh "rm -f #{conf_install_dir}/autodrop.conf.default"
|
75
|
+
end
|
data/bin/autodrop
ADDED
@@ -0,0 +1,202 @@
|
|
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.
|
data/bin/autodrop.rb
ADDED
@@ -0,0 +1,202 @@
|
|
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.
|
@@ -0,0 +1,17 @@
|
|
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'
|
data/doc/README.jp.txt
ADDED
@@ -0,0 +1,164 @@
|
|
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
|
data/doc/README.txt
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
autodrop README
|
2
|
+
|
3
|
+
NOZAWA Hiromasa, Tokyo Japan
|
4
|
+
|
5
|
+
|
6
|
+
= About
|
7
|
+
|
8
|
+
Autodrop is a daemon, observes syslog logs and forbid accesses from
|
9
|
+
remote hosts who continue wrong attempt to our host like knocking port
|
10
|
+
22 for 100,000,000 times a day. Autodrop uses iptables(8) and named
|
11
|
+
pipe from syslogd.
|
12
|
+
|
13
|
+
Autodrop adds DROP rule into iptables' INPUT table for the remote host
|
14
|
+
which generates log messages matched with same reguler expression more
|
15
|
+
than specified times in specified interval.
|
16
|
+
|
17
|
+
Multiple regular expression can be specified by config file. Also
|
18
|
+
matching count threshold and interval time to continue counting can be
|
19
|
+
customizable.
|
20
|
+
|
21
|
+
Autodrop watches a named pipe and not neither does polling nor stat(2)
|
22
|
+
on many log files. You need syslogd which can output logs to named
|
23
|
+
pipe.
|
24
|
+
|
25
|
+
Autodrop is written in Ruby scripting language then surely it will not
|
26
|
+
suit for very high traffic sites. However it works well to shut out
|
27
|
+
port 22 knockers for my small site.
|
28
|
+
|
29
|
+
Using autodrop can also shut out yourself from your host. Be careful.
|
30
|
+
|
31
|
+
|
32
|
+
= Requirement
|
33
|
+
|
34
|
+
* linux box (for iptables)
|
35
|
+
* syslogd (can output logs to named pipes)
|
36
|
+
* ruby 1.8.6 or later (I have not run autodrop on other versions)
|
37
|
+
|
38
|
+
|
39
|
+
= Licence
|
40
|
+
|
41
|
+
BSD
|
42
|
+
|
43
|
+
|
44
|
+
= Install
|
45
|
+
|
46
|
+
After doing 'gem install', you need to write config file.
|
47
|
+
Fix #! line in the script if necessary.
|
48
|
+
|
49
|
+
*1. gem install autodrop
|
50
|
+
|
51
|
+
*2. cd <GEMDIR>/gems/autodrop-x.x.x/conf
|
52
|
+
|
53
|
+
*3. sudo cp autodrop.conf.default /etc
|
54
|
+
|
55
|
+
*4. Edit /etc/autodrop.conf
|
56
|
+
|
57
|
+
|
58
|
+
= Usage
|
59
|
+
|
60
|
+
== Start
|
61
|
+
|
62
|
+
------------------------------
|
63
|
+
$ sudo ruby autodrop.rb
|
64
|
+
------------------------------
|
65
|
+
|
66
|
+
Default config file is /etc/autodrop.conf .
|
67
|
+
Another config file can be specified from command line.
|
68
|
+
|
69
|
+
------------------------------
|
70
|
+
$ ruby autodrop.rb -c /foo/bar/autodrop.conf
|
71
|
+
------------------------------
|
72
|
+
|
73
|
+
== Stop
|
74
|
+
|
75
|
+
------------------------------
|
76
|
+
$ sudo kill `cat /var/run/autodrop.pid`
|
77
|
+
------------------------------
|
78
|
+
|
79
|
+
== When Running
|
80
|
+
|
81
|
+
Autodrop itself also outputs syslog logs with prefix 'autodrop' when
|
82
|
+
started, stopped and each occurrences of DROP.
|
83
|
+
|
84
|
+
After running autodrop, iptables's INPUT table will filled with DROP
|
85
|
+
rules in few weeks or months but autodrop does not have ability to
|
86
|
+
clear them. Please do it by your hand when it required.
|
87
|
+
|
88
|
+
|
89
|
+
= autodrop.conf
|
90
|
+
|
91
|
+
All variables are not omit-able.
|
92
|
+
These are Ruby's constant variables.
|
93
|
+
|
94
|
+
* MESSAGES_TO_WATCH
|
95
|
+
|
96
|
+
Array of regular expressions.
|
97
|
+
Each expression must have $1 and it must match an IP address.
|
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
|
+
Matching count to do DROP.
|
111
|
+
|
112
|
+
------------------------------
|
113
|
+
COUNT_MAX = 3
|
114
|
+
------------------------------
|
115
|
+
|
116
|
+
* INTERVAL
|
117
|
+
|
118
|
+
Interval time to continue counting for a remote host matched
|
119
|
+
to a pattern. Specify in seconds.
|
120
|
+
Each interval timers are reset on each matches.
|
121
|
+
|
122
|
+
------------------------------
|
123
|
+
INTERVAL = 10
|
124
|
+
------------------------------
|
125
|
+
|
126
|
+
* IPTABLES_PROGRAM
|
127
|
+
|
128
|
+
iptables(8) command path.
|
129
|
+
|
130
|
+
------------------------------
|
131
|
+
IPTABLES_PROGRAM = '/sbin/iptables'
|
132
|
+
------------------------------
|
133
|
+
|
134
|
+
* PIDFILE
|
135
|
+
|
136
|
+
PID file path.
|
137
|
+
|
138
|
+
------------------------------
|
139
|
+
PIDFILE = '/var/run/autodrop.pid'
|
140
|
+
------------------------------
|
141
|
+
|
142
|
+
* WATCH_FIFO
|
143
|
+
|
144
|
+
Named pipe path to watch.
|
145
|
+
|
146
|
+
------------------------------
|
147
|
+
WATCH_FIFO = '/var/log/authfifo'
|
148
|
+
------------------------------
|
149
|
+
|
150
|
+
|
151
|
+
= Example: syslogd configuration
|
152
|
+
|
153
|
+
Create fifo,
|
154
|
+
------------------------------
|
155
|
+
mkfifo /var/log/authfifo
|
156
|
+
------------------------------
|
157
|
+
and add it to your syslog.conf
|
158
|
+
------------------------------
|
159
|
+
authpriv.* |/var/log/authfifo
|
160
|
+
------------------------------
|
161
|
+
|
162
|
+
`|' means `out put logs to this pipe'.
|
163
|
+
See your syslog.conf(5) for more details.
|
164
|
+
|
165
|
+
#eof
|
data/misc/autodrop.sh
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
AUTODROP=/usr/local/sbin/autodrop
|
4
|
+
CONF=/etc/autodrop.conf
|
5
|
+
PIDFILE=/var/run/autodrop.pid
|
6
|
+
|
7
|
+
case "$1" in
|
8
|
+
start)
|
9
|
+
$AUTODROP -c $CONF
|
10
|
+
;;
|
11
|
+
stop)
|
12
|
+
kill -TERM `cat ${PIDFILE}`
|
13
|
+
;;
|
14
|
+
*)
|
15
|
+
echo "usage: $0 { start | stop }" >&2
|
16
|
+
exit 1
|
17
|
+
;;
|
18
|
+
esac
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: autodrop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- NOZAWA Hiromasa
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-10 00:00:00 +09:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email:
|
18
|
+
executables:
|
19
|
+
- autodrop
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- ChangeLog
|
26
|
+
- Rakefile
|
27
|
+
- bin/autodrop
|
28
|
+
- bin/autodrop.rb
|
29
|
+
- conf/autodrop.conf.default
|
30
|
+
- doc/README.jp.txt
|
31
|
+
- doc/README.txt
|
32
|
+
- misc/autodrop.sh
|
33
|
+
has_rdoc: false
|
34
|
+
homepage: http://rubyforge.org/projects/autodrop
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
|
38
|
+
require_paths:
|
39
|
+
- []
|
40
|
+
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
rubyforge_project: autodrop
|
56
|
+
rubygems_version: 1.3.1
|
57
|
+
signing_key:
|
58
|
+
specification_version: 2
|
59
|
+
summary: Automatic iptables DROP daemon
|
60
|
+
test_files: []
|
61
|
+
|