Die Programmiersprache Ruby

Blog|

Forum|

Wiki  


Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]

Ein neues Thema erstellen Auf das Thema antworten  [ 2 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: MyFail2Ban
BeitragVerfasst: 30 Jun 2015, 02:22 
Offline
Schüler

Registriert: 11 Apr 2013, 12:48
Beiträge: 49
Hallo Ruby Gemeinde,

letztens sah ich auf unseren Servern sehr viele Zugriffsversuche von Außerhalb und landesweite Subnetze in aus den betreffenden Regionen.
Da die Server keine Public Services unterhielten war das in Ordnung. Trotzdem löst man sowas normalerweise eleganter. Es gibt in Python das Programm/Script
Fail2Ban.

Aus Interesse am Thema habe ich mir Gedanken gemacht, und selbst mein eigenes kleines Fail2Ban in Ruby geschrieben. Bitte lest über meinen Code drüber und sagt mir was ich wie besser machen kann. Als PN oder hier als Beitrag, ich würde mich sehr über konstruktive Kritik freuen.

Zum Programm selbst: Es studiert aktuell die Log Datei auth.log, dies ist per Konfigurationsdatei änderbar. Die Anzahl der wiederholten Fehlversuche bis zum Ban lässt sich auch in der Cfg Datei bestimmen, sowie der String für den Netfilter. Ob ich nun ipset add menge ip nutzen möchte, oder iptables, oder sonst einen Netfilter - ist im Grunde egal, solange ich darauf achte das ich __val__ für die IP Adresse als Variable in der Konfigurationsdatei setze. Die gebannten Ip's werden in der Datei bannedip's vermerkt, diese dient auch dazu doppelte ausführen vom Netfilter für eine IP zu vermeiden. Zusätzlich schreibt das Programm ein Log über seine aktivitäten, so dass man immer weiß was das Tool wann wie gemacht hat.

Einige Sachen ärgern mich, und zwar kann ich nicht wirklich überprüfen ob iptables oder ipset ordnungsgemäß ausgeführt worden sind. System("iptables .....") hilft mir nicht in dem Fall.

Gruß
compiler



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

#Konfigurationsdatei
#Dateiname config
#wird zum Start ben��tigt

#Dateiname wo geblockte IP's zwischengespeichert werden.
banfilename = blockedip

#Dateiname der Logdatei
authfilename = auth.log

#Anzahl der falschen Einlogversuche
failvalue = 3

#Name der Logdatei des Fail2netfilter

prglog = fail2net.log

#Regel zum Bannen
#
#ckrule = iptables -I INPUT -s __val__ -j REJECT
blockrule = ipset add attackerip __val__
#Alertstring
#
alertstring = Failed password





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346



#MyFail2Ban.rb
#
#By M.K. aka r3d0x aka Stylefighter aka Mahatma Vegandhi

class ListAgent
@liste
def initialize(liste)
@liste = liste
LogAgent::add "ListAgent: Die Liste wurde erstellt."
end

def getListe()
return @liste
end

end

class CountAgent
@liste
@merkliste
@ipliste
def initialize
@merkliste = Array.new
@ipliste = Array.new
end

def setListe(liste)
@liste = liste
LogAgent::add "CountAgent: Die Liste habe ich erhalten."
end

def drin(zahl)
@merkliste.each do |z|
if zahl == z
return true
end
end
return false
end

def debug
LogAgent::add @merkliste.inspect
LogAgent::add @ipliste.inspect
end

def ipdrin(zahl)
@ipliste.each_with_index do |z,i|
if zahl == z.ip
z.count+=1
end
end

end

def checkListe()
@liste.each do |zahl|
if !drin(zahl)
@merkliste.push(zahl)
ip = Ip.new
ip.ip = zahl
ip.count = 1
@ipliste.push(ip)
else
self.ipdrin(zahl)
end
end
end

def getCountList()
return @ipliste
end

end

class Ip
attr_accessor :ip
attr_accessor :count
end

class Admin
@failvalue
@liste
@banfilehdl

def initialize
@liste = Array.new
@failvalue = 3
@banfilename = 'banfile'

end

def setFailvalue(num)
@failvalue = num
end

def setBanfilename(name)
@banfilename = name
end

def setListe(objectfeld)
@liste = objectfeld
LogAgent::add "Admin: ich habe die Liste erhalten."
end

def setFailvalue(max)
@failvalue = max
end

def checkListe()
@liste.each do |ip_mit_anzahl|
if ip_mit_anzahl.count > @failvalue
LogAgent::add "Admin: Die IP #{ip_mit_anzahl.ip} wird gesperrt."
self.ban(ip_mit_anzahl.ip)
end
end
end

def ban(adresse)
self.log2file(adresse)
end

def already_blocked(adresse)
begin

rescue
LogAgent::add "Admin: Probleme beim Lesen von Datei #{@banfilename}. "
end
end

def isbanned(adresse)
begin
tmp = File.open(@banfilename, "r")
tmp.each do |line|
if adresse[0] == line.strip
return true
break
end
end
tmp.close
rescue
LogAgent::add "Admin: Probleme beim Lesen von Datei #{@banfilename}. "
end
end

def log2file(adresse)
if !isbanned(adresse)
Netfilter::reject(adresse[0])
if Netfilter::execute
tmp = File.open(@banfilename, "a+")
tmp.puts(adresse)
tmp.close
end
else
LogAgent::add "Admin: Ignoriere Adresse #{adresse}, Adresse wird bereits geblockt. "
end

end

end


class AuthReader
@authfilename
@alertstring
@attacker
def initialize
LogAgent::add "AuthReader: gestartet."
@attacker = Array.new
@authfilename = "auth.log"

end

def setAuthfilename(name)
@authfilename = name
end

def setAlertstring(name)
@alertstring = name
end

def prepare
begin
tmp = File.open(@authfilename, "r")
tmp.each do |line|
if line.match(@alertstring)
@attacker.push(line.scan(/[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*/))
end
end
end
LogAgent::add "AuthReader: Logdatei #{@authfilename} erfolgreich eingelesen."
rescue
LogAgent::add "AuthReader: Fehler beim Einlesen von Datei #{@authfilename}."
end

def getAttackerIp
return @attacker
end
end

class LogAgent
@filehandler
def self.init(dateiname)
@filehandler = File.open(dateiname, "a+")
LogAgent::add "LogAgent: gestartet."
end

def self.add(msg)
zs=Time.new
@filehandler.puts("#{zs}: #{msg}")
end
end

class Netfilter
@rule
@tmprule
def self.setBlockrule(rule)
@rule = rule
end

def self.reject(ip)
begin
@tmprule = @rule.gsub(/__val__/,ip)
rescue
LogAgent::add "Netfilter: Parsen der Regel schlug fehl."
exit
end
end

def self.execute
status = system(@tmprule)
if status
LogAgent::add "Netfilter: #{@tmprule} erfolgreich ausgef����hrt."
return true
else
LogAgent::add "Netfilter: #{@tmprule} konnte nicht gestartet werden."
return false
end
end
end

class ConfigAgent
@cfgdateiname
@hash

def self.init
@hash = Hash.new
@cfgdateiname = 'config'
end

def self.getConfigHash
begin
self.readconfig
return @hash
rescue
LogAgent::add "ConfigAgent: Konfigurationsdatei konnte nicht gestartet werden."
return false
end

end

def self.readconfig
begin
self.init
tmp = File.open(@cfgdateiname, "r")
tmp.each do |line|
if line.match(/banfilename = /)

@hash['banfilename'] = line.match(/(?<=\=).*/)
end

if line.match(/authfilename = /)

@hash['authfilename'] = line.match(/(?<=\=).*/)
end


if line.match(/failvalue = /)

@hash['failvalue'] = line.match(/(?<=\=).*/)
end


if line.match(/prglog = /)
@hash['prglog'] = line.match(/(?<=\=).*/)
end

if line.match(/blockrule = /)
@hash['blockrule'] = line.match(/(?<=\=).*/)
end


if line.match(/alertstring = /)
@hash['alertstring'] = line.match(/(?<=\=).*/)
end



end
end

rescue
LogAgent::add "ConfigAgent: Konfigurationsdatei #{@cfgdateiname} konnte nicht gelesen werden."
end
end

class AppCtrl
def self.init
#lade Konfigurationsdatei
if cfgprm = ConfigAgent::getConfigHash
#definiere Dateiname f����r Logdatei des Tools
LogAgent::init(cfgprm['prglog'].to_s.strip)
Netfilter::setBlockrule(cfgprm['blockrule'].to_s)

auth = AuthReader.new
#konfigurationsdatei der Logdatei / auth.log
auth.setAuthfilename(cfgprm['authfilename'].to_s.strip)
auth.setAlertstring(cfgprm['alertstring'].to_s.gsub(/^\s/,""))
auth.prepare
angreifer_dokument = auth.getAttackerIp

lAgent = ListAgent.new(angreifer_dokument)

dokument = lAgent.getListe()
cAgent = CountAgent.new
cAgent.setListe(dokument)
cAgent.checkListe
#cAgent.debug

abzaehlung = cAgent.getCountList()

admin = Admin.new
#definiere die Anzahl der mi����gl����ckten Connect Versuche bis Ban.
admin.setFailvalue(cfgprm['failvalue'].to_s.strip.to_i)
#definiere Logdatei der gebannten IPv4 Adressen
admin.setBanfilename(cfgprm['banfilename'].to_s.strip)
admin.setListe(abzaehlung)
admin.checkListe
end
end
end

AppCtrl.init



Dateianhänge:
MyFail2Ban.tar [10 KiB]
287-mal heruntergeladen
Nach oben
 Profil  
 
 Betreff des Beitrags: Re: MyFail2Ban
BeitragVerfasst: 03 Aug 2016, 11:00 
Offline
Interpreter

Registriert: 10 Dez 2007, 17:37
Beiträge: 1906
Ist zwar schon einige Zeit her, aber ich habe den Post gerade erst gefunden.

Ein paar Anmerkungen hätte ich zu deinem Skript. Einiges davon weisst du vielleicht schon.

fail2ban hat eine grosse Auswahl an actions die ausgeführt werden können, wenn eine IP auffällt. Das sperren einer IP über iptables ist nur eine davon.

Wenn das Sperren über IP-Tables verwendet wird, dann verwendet fail2ban custom chains, um seine bans zu verwalten. Das macht das ganz etwas einfacher.

z.B. verwendet fail2ban den Befehl



1
2

iptables -n -L <chain> | grep -q 'fail2ban-<name>[ \t]'


Um zu prüfen, ob eine IP schon gesperrt ist. Mit einer Custom chain geht das sehr leicht.
Ansonsten hat iptables noch die Option --check, damit kann man prüfen, ob eine Rule schon existiert.

Dein Regexp könnte noch etwas getuned werden (https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html).

Das gem ipaddres benutzt aktuell folgenden regexp



1
2

Regexp.new(/((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/)


Ein wenig Bauchschmerzen habe ich bei deinem system Aufruf. Zur Zeit kann ich kein Problem mit deinem
RegExp entdecken, aber wenn das Programm sich ändert könnten sich dort Fehler einschleichen, vor allem wenn
man den RegExp für IPV6 anspasst.


Das was dort matched wird direkt an system übergeben und dein Programm wird wohl als root laufen.
Das sehe ich als potentielles Risiko. Dort solltest du mal prüfen, wie in Ruby Parameter sicher und escaped
an einen Shell Befehl übergeben werden können. Vielleicht rufst du auch nicht direkt iptables auf, sondern
ein wrapper script, das dann intern mit sudo und ein paar Validierungen arbeitet.

fail2ban hat noch einen grossen Vorteil bei der Logfile-Überwachung. Es benutzt gamin zur Fileüberwachung. Damit muss die Datei nicht auf Veränderungen gepollt werden, sondern bekommt eine Notification, wenn es Änderungen gibt. Das schon die Resourcen.

_________________
Grüße
Jack


Nach oben
 Profil  
 
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 2 Beiträge ] 

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
cron