iptables-ruby 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ require 'logger'
2
+ unless($log)
3
+ $log = Logger.new(STDOUT)
4
+ $log.level = Logger::WARN
5
+ end
@@ -0,0 +1,51 @@
1
+ require 'iptables/logger'
2
+
3
+ module IPTables
4
+ class Primitives
5
+ attr_reader :children
6
+ def initialize(primitives_hash)
7
+ @children = {}
8
+ raise "expected Hash" unless primitives_hash.is_a? Hash
9
+ primitives_hash.each{ |name, info|
10
+ child = nil
11
+ case info
12
+ when Array, String
13
+ child = info
14
+ when Hash
15
+ child = Primitives.new(info)
16
+ else
17
+ raise "unknown primitive type: #{name}"
18
+ end
19
+
20
+ self.instance_variable_set "@#{name}", child
21
+ self.class.class_eval do
22
+ define_method(name) { child }
23
+ end
24
+ @children[name] = child
25
+ }
26
+ end
27
+
28
+ def substitute(identifier)
29
+ components = identifier.split(/\./)
30
+ the_first = components.first
31
+ the_rest = components[1 .. -1].join('.')
32
+ raise "failed to substitute unknown primitive: #{the_first}" unless @children.has_key? the_first
33
+ case @children[the_first]
34
+ when Primitives
35
+ raise "failed to substitute partial primitive: #{the_first}" if the_rest.empty?
36
+ return @children[the_first].substitute(the_rest)
37
+ else
38
+ return @children[the_first]
39
+ end
40
+ end
41
+
42
+ def has_primitive?(identifier)
43
+ begin
44
+ self.substitute(identifier)
45
+ return true
46
+ rescue
47
+ return false
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,851 @@
1
+ require 'iptables/logger'
2
+
3
+ module IPTables
4
+ class Tables
5
+ # The main iptables object, encompassing all tables, their chains, their rules, etc
6
+ attr_reader :config, :tables
7
+
8
+ # Example: *filter
9
+ @@parse_table_regex = /^\*(\S+)$/
10
+ # Example: # Generated by iptables-save v1.4.4 on Wed Sep 26 18:38:44 2012
11
+ @@parse_comment_regex = /^#/
12
+
13
+ def initialize(input, config=nil)
14
+ @config = config
15
+ $log.debug('init IPTables')
16
+ @tables = Hash.new
17
+
18
+ case input
19
+ when Hash
20
+ input.keys.sort.each{ |table_name|
21
+ table_info = input[table_name]
22
+ case table_info
23
+ when nil, false
24
+ @tables[table_name] = table_info
25
+ next
26
+ end
27
+ table = Table.new(table_name, self, table_info)
28
+ @tables[table_name] = table
29
+ }
30
+
31
+ when String
32
+ self.parse(input.split(/\n/))
33
+
34
+ else
35
+ raise "don't know how to handle input: #{input.inspect}"
36
+ end
37
+ end
38
+
39
+ def as_array(comments = true)
40
+ array = []
41
+ $log.debug('IPTables array')
42
+ @tables.keys.sort.each{ |name|
43
+ table = @tables[name]
44
+ $log.debug("#{name}: #{table}")
45
+ next if table.nil?
46
+ array << '*'+name
47
+ array += table.as_array(comments)
48
+ array << 'COMMIT'
49
+ }
50
+ return array
51
+ end
52
+
53
+ def merge(merged)
54
+ raise "must merge another IPTables::Tables" unless merged.class == IPTables::Tables
55
+ merged.tables.each{ |table_name, table_object|
56
+ $log.debug("merging table #{table_name}")
57
+
58
+ case table_object
59
+ when false
60
+ $log.debug("deleting table #{table_name}")
61
+ @tables.delete(table_name)
62
+ next
63
+
64
+ when nil
65
+ next
66
+ end
67
+
68
+ # only a Table is expected from here onwards
69
+
70
+ # merged table
71
+ if (@tables.has_key? table_name) and not (@tables[table_name].nil?)
72
+ @tables[table_name].merge(table_object)
73
+ next
74
+ end
75
+
76
+ # new table
77
+ @tables[table_name] = table_object
78
+ }
79
+
80
+ # find and apply any node rule addition points
81
+ @tables.each{ |name, table|
82
+ next unless table.class == IPTables::Table
83
+ $log.debug("applying additions to table #{name}")
84
+ table.apply_additions(merged)
85
+ }
86
+ end
87
+
88
+ def get_node_additions(table_name, chain_name)
89
+ $log.debug("finding additions for table #{table_name}, chain #{chain_name}")
90
+ return unless @tables.has_key? table_name
91
+ return unless @tables[table_name].class == IPTables::Table
92
+ return @tables[table_name].get_node_additions(chain_name)
93
+ end
94
+
95
+ def parse(lines)
96
+ position = 0
97
+ while position < lines.length
98
+ line = lines[position]
99
+ #$log.debug(line)
100
+ position += 1
101
+
102
+ case line
103
+ when @@parse_comment_regex, 'COMMIT'
104
+ # ignored
105
+ when @@parse_table_regex
106
+ @tables[$1] = IPTables::Table.new($1, self)
107
+ position += @tables[$1].parse(lines[position .. -1])
108
+ else
109
+ raise "unhandled line: #{line}"
110
+ end
111
+ end
112
+ raise 'no tables found' unless @tables.any?
113
+ end
114
+ end
115
+
116
+ class TablesComparison
117
+ def initialize(tables1, tables2)
118
+ raise "must provide two tables" unless (tables1.class == IPTables::Tables) and (tables2.class == IPTables::Tables)
119
+ @tables1 = tables1
120
+ @tables2 = tables2
121
+ @table_diffs = []
122
+
123
+ @including_comments = true
124
+ @compared = false
125
+ end
126
+
127
+ def compare
128
+ return if @compared
129
+ @equal = true
130
+
131
+ tables1_tables = @tables1.tables.keys.sort
132
+ tables2_tables = @tables2.tables.keys.sort
133
+ @only_in_current = (tables1_tables - tables2_tables).reject{ |t| @tables1.tables[t].nil? }
134
+ @only_in_new = (tables2_tables - tables1_tables).reject{ |t| @tables2.tables[t].nil? }
135
+ @equal = false if @only_in_current.any? or @only_in_new.any?
136
+
137
+ @table_diffs = []
138
+ (tables1_tables - @only_in_current - @only_in_new).each{ |table|
139
+ table1 = @tables1.tables[table]
140
+ table2 = @tables2.tables[table]
141
+
142
+ # nil tables are only created by policy, never parsed
143
+ # they mean "use the parsed policy here"
144
+ # which means "for comparison purposes, they are always equal"
145
+ next if table1.nil? or table2.nil?
146
+
147
+ table_comparison = IPTables::TableComparison.new(table1, table2)
148
+ if @including_comments
149
+ table_comparison.include_comments
150
+ else
151
+ table_comparison.ignore_comments
152
+ end
153
+ next if table_comparison.equal?
154
+
155
+ @equal = false
156
+ @table_diffs << table_comparison
157
+ }
158
+
159
+ return nil
160
+ end
161
+
162
+ def ignore_comments
163
+ @including_comments = false
164
+ @compared = false
165
+ end
166
+
167
+ def include_comments
168
+ @including_comments = true
169
+ @compared = false
170
+ end
171
+
172
+ def equal?
173
+ self.compare
174
+ return @equal
175
+ end
176
+
177
+ def as_array
178
+ self.compare
179
+ array = []
180
+ return array if self.equal?
181
+ if @only_in_current.any?
182
+ @only_in_current.each{ |table_name|
183
+ array << "Missing table: #{table_name}"
184
+ array.concat @tables1.tables[table_name].as_array
185
+ }
186
+ end
187
+ if @only_in_new.any?
188
+ @only_in_new.each{ |table_name|
189
+ array << "New table: #{table_name}"
190
+ next if @tables2.tables[table_name].nil?
191
+ array.concat @tables2.tables[table_name].as_array
192
+ }
193
+ end
194
+ if @table_diffs.any?
195
+ @table_diffs.each{ |table_comparison|
196
+ array.concat table_comparison.as_array
197
+ }
198
+ end
199
+ return array
200
+ end
201
+ end
202
+
203
+ class Table
204
+ # standard tables: nat, mangle, raw, filter
205
+ attr_reader :chains, :name, :node_addition_points, :my_iptables
206
+
207
+ # Example: :INPUT DROP [0:0]
208
+ @@chain_policy_regex = /^:(\S+)\s+(\S+)\s+/
209
+ # Example: -A INPUT -m comment --comment "BEGIN: in-bound traffic"
210
+ @@chain_rule_regex = /^-A\s+(\S+)\s+(.+)/
211
+
212
+ def initialize(name, my_iptables, table_info_hash={})
213
+ @name = name
214
+ @my_iptables = my_iptables
215
+ $log.debug("init Table #{@name}")
216
+
217
+ @node_addition_points = {}
218
+ @chains = {}
219
+
220
+ table_info_hash.keys.sort.each{ |chain_name|
221
+ chain_info = table_info_hash[chain_name]
222
+ case chain_info
223
+ when Hash
224
+ @chains[chain_name] = IPTables::Chain.new(chain_name, chain_info, self)
225
+
226
+ when false, nil
227
+ @chains[chain_name] = chain_info
228
+
229
+ else
230
+ raise "don't know how to handle #{chain_name}: #{chain_info.inspect}"
231
+ end
232
+ }
233
+ $log.debug("table #{@name} is #{self}")
234
+ end
235
+
236
+ def as_array(comments = true)
237
+ policies = []
238
+ chains = []
239
+
240
+ # special sorting rule INPUT FORWARD OUTPUT are always first, in this order
241
+ chain_order = @chains.keys.sort()
242
+ %w/INPUT FORWARD OUTPUT/.reverse.each{ |chain|
243
+ next unless chain_order.include? chain
244
+ chain_order -= [chain]
245
+ chain_order.unshift(chain)
246
+ }
247
+ $log.debug("chain order: #{chain_order.inspect}")
248
+
249
+ chain_order.each{ |name|
250
+ $log.debug("chain #{name}")
251
+ chain = @chains[name]
252
+ policies.push ":#{name} #{chain.output_policy}"
253
+ chains += chain.as_array(comments)
254
+ }
255
+ return policies + chains
256
+ end
257
+
258
+ def path()
259
+ @name
260
+ end
261
+
262
+ def merge(table_object)
263
+ table_object.chains.each{ |chain_name, chain_object|
264
+ $log.debug("merging chain #{chain_name}")
265
+
266
+ case chain_object
267
+ when false
268
+ @chains.delete(chain_name)
269
+ next
270
+
271
+ when nil
272
+ next
273
+ end
274
+ # only a Chain is expected from here onwards
275
+
276
+ # merge Chain
277
+ if @chains.has_key? chain_name
278
+ @chains[chain_name].merge(chain_object)
279
+ next
280
+ end
281
+
282
+ # copy Chain
283
+ @chains[chain_name] = chain_object if chain_object.complete?
284
+ }
285
+ end
286
+
287
+ def apply_additions(other_firewall)
288
+ $log.debug("node addition points: #{@node_addition_points.inspect}")
289
+ @chains.each{ |name, chain_object|
290
+ $log.debug("looking for additions to chain #{name}")
291
+ next unless @node_addition_points.has_key? name
292
+ chain_object.apply_additions(other_firewall)
293
+ }
294
+ end
295
+
296
+ def register_node_addition_point(addition_name)
297
+ $log.debug("registering node addition point for #{addition_name}")
298
+ @node_addition_points[addition_name] = true
299
+ end
300
+
301
+ def get_node_additions(chain_name)
302
+ return unless @chains.has_key? chain_name
303
+ return @chains[chain_name].get_node_additions
304
+ end
305
+
306
+ def parse(lines)
307
+ position = 0
308
+ while position < lines.length
309
+ line = lines[position]
310
+ position += 1
311
+
312
+ case line
313
+ when @@chain_policy_regex
314
+ @chains[$1] = IPTables::Chain.new($1, {'policy' => $2}, self)
315
+ when @@chain_rule_regex
316
+ raise "unrecognized chain: #{$1}" unless @chains.has_key? $1
317
+ @chains[$1].parse_rule($2)
318
+ else
319
+ $log.debug("returning on unrecognized line: #{line}")
320
+ # back up a line
321
+ return position - 1
322
+ end
323
+ end
324
+ end
325
+ end
326
+
327
+ class TableComparison
328
+ def initialize(table1, table2)
329
+ raise "must provide two tables" unless (table1.class == IPTables::Table) and (table2.class == IPTables::Table)
330
+ raise "table names should match" unless table1.name == table2.name
331
+ @table1 = table1
332
+ @table2 = table2
333
+
334
+ @including_comments = true
335
+ @compared = false
336
+ end
337
+
338
+ def compare
339
+ return if @compared
340
+ @equal = true
341
+
342
+ table1_chains = @table1.chains.keys.sort
343
+ table2_chains = @table2.chains.keys.sort
344
+ @only_in_current = table1_chains - table2_chains
345
+ @only_in_new = table2_chains - table1_chains
346
+ @equal = false if @only_in_current.any? or @only_in_new.any?
347
+
348
+ @chain_diffs = []
349
+ (table1_chains - @only_in_current - @only_in_new).each{ |chain|
350
+ chain_comparison = IPTables::ChainComparison.new(@table1.chains[chain], @table2.chains[chain])
351
+ if @including_comments
352
+ chain_comparison.include_comments
353
+ else
354
+ chain_comparison.ignore_comments
355
+ end
356
+ next if chain_comparison.equal?
357
+
358
+ @equal = false
359
+ @chain_diffs << chain_comparison
360
+ }
361
+
362
+ return nil
363
+ end
364
+
365
+ def ignore_comments
366
+ @including_comments = false
367
+ @compared = false
368
+ end
369
+
370
+ def include_comments
371
+ @including_comments = true
372
+ @compared = false
373
+ end
374
+
375
+ def missing
376
+ self.compare
377
+ return @only_in_current
378
+ end
379
+
380
+ def new
381
+ self.compare
382
+ return @only_in_new
383
+ end
384
+
385
+ def changed
386
+ self.compare
387
+ return @chain_diffs
388
+ end
389
+
390
+ def as_array
391
+ self.compare
392
+ array = []
393
+ return array if self.equal?
394
+ array << "Changed table: #{@table1.name}"
395
+ if self.missing.any?
396
+ self.missing.each{ |chain_name|
397
+ array << 'Missing chain:'
398
+ array.concat @table1.chains[chain_name].all_as_array
399
+ }
400
+ end
401
+ if self.new.any?
402
+ self.new.each{ |chain_name|
403
+ array << 'New chain:'
404
+ array.concat @table2.chains[chain_name].all_as_array
405
+ }
406
+ end
407
+ if self.changed.any?
408
+ self.changed.each{ |chain_comparison|
409
+ array.concat chain_comparison.as_array
410
+ }
411
+ end
412
+ return array
413
+ end
414
+
415
+ def equal?
416
+ self.compare
417
+ return @equal
418
+ end
419
+ end
420
+
421
+ class Chain
422
+ # example chain names in filter table: INPUT, FORWARD, OUTPUT
423
+ attr_reader :additions, :name, :node_addition_points, :my_table, :policy, :rules
424
+
425
+ def initialize(name, chain_info_hash, my_table)
426
+ @name = name
427
+ @chain_info_hash = chain_info_hash
428
+ @my_table = my_table
429
+
430
+ $log.debug("init Chain #{@name}")
431
+ @node_addition_points = []
432
+
433
+ @policy = @chain_info_hash['policy']
434
+ @rules = self.find_and_add_type('rules')
435
+ @additions = self.find_and_add_type('additions')
436
+ end
437
+
438
+ def find_and_add_type(data_type)
439
+ rules = []
440
+ return unless @chain_info_hash.has_key? data_type
441
+ @chain_info_hash[data_type].each_with_index{ |rule, index|
442
+ rule_object = IPTables::Rule.new(rule, self)
443
+ rule_object.set_position(index)
444
+ rules.push(rule_object)
445
+ }
446
+ return rules
447
+ end
448
+
449
+ def output_policy()
450
+ return (@policy == nil) ? '-' : @policy
451
+ end
452
+
453
+ def as_array(comments = true)
454
+ $log.debug("Chain #{@name} array")
455
+ return [] if @rules == nil
456
+ rules = @rules.collect{ |rule| rule.as_array(comments)}.flatten
457
+ $log.debug(rules)
458
+ return rules
459
+ end
460
+
461
+ def all_as_array(comments = true)
462
+ return [
463
+ ":#{@name} #{self.output_policy}",
464
+ self.as_array
465
+ ].flatten
466
+ end
467
+
468
+ def merge(chain_object)
469
+ # if found, replace policy
470
+ @policy = chain_object.policy unless chain_object.policy.nil?
471
+
472
+ # if found, replace rules
473
+ @rules = chain_object.rules unless chain_object.rules.nil?
474
+ end
475
+
476
+ def path()
477
+ @my_table.path + ".#{@name}"
478
+ end
479
+
480
+ def register_node_addition_point(rule_object, addition_name)
481
+ @node_addition_points.push(rule_object) unless @node_addition_points.include? rule_object
482
+ @my_table.register_node_addition_point(addition_name)
483
+ end
484
+
485
+ def get_node_additions()
486
+ return if @additions.empty?
487
+ return @additions
488
+ end
489
+
490
+ def apply_additions(other_firewall)
491
+ @node_addition_points.each{ |rule_object|
492
+ $log.debug("applying additions for #{rule_object.path}")
493
+ rule_object.apply_additions(other_firewall)
494
+ }
495
+ end
496
+
497
+ def parse_rule(args)
498
+ @rules = [] if @rules.nil?
499
+ # parsed rules come with trailing whitespace; remove
500
+ rule_object = IPTables::Rule.new(args.strip, self)
501
+ rule_object.set_position(@rules.length)
502
+ @rules.push(rule_object)
503
+ end
504
+
505
+ def complete?
506
+ if @rules.nil?
507
+ return true if @additions.nil?
508
+ return false
509
+ end
510
+ return true if @rules.any?
511
+ end
512
+ end
513
+
514
+ class ChainComparison
515
+ require 'diff/lcs'
516
+
517
+ def initialize(chain1, chain2)
518
+ raise "must provide two chains" unless (chain1.class == IPTables::Chain) and (chain2.class == IPTables::Chain)
519
+ raise "first and second chain should have same name" unless chain1.name == chain2.name
520
+ @chain1 = chain1
521
+ @chain2 = chain2
522
+
523
+ @including_comments = true
524
+ @compared = false
525
+ end
526
+
527
+ def ignore_comments
528
+ @including_comments = false
529
+ @compared = false
530
+ end
531
+
532
+ def include_comments
533
+ @including_comments = true
534
+ @compared = false
535
+ end
536
+
537
+ def compare
538
+ return if @compared
539
+
540
+ @equal = true
541
+
542
+ @missing_rules = {}
543
+ @new_rules = {}
544
+ Diff::LCS.diff(
545
+ @chain1.as_array(@including_comments),
546
+ @chain2.as_array(@including_comments)
547
+ ).each{ |diffgroup|
548
+ diffgroup.each{ |diff|
549
+ if diff.action == '-'
550
+ @missing_rules[diff.position] = diff.element
551
+ else
552
+ @new_rules[diff.position] = diff.element
553
+ end
554
+ @equal = false
555
+ }
556
+ }
557
+
558
+ @new_policy = false
559
+ unless @chain1.policy == @chain2.policy
560
+ @new_policy = true
561
+ @equal = false
562
+ end
563
+
564
+ @compared = true
565
+ return nil
566
+ end
567
+
568
+ def equal?
569
+ self.compare
570
+ return @equal
571
+ end
572
+
573
+ def missing
574
+ self.compare
575
+ return @missing_rules
576
+ end
577
+
578
+ def new
579
+ self.compare
580
+ return @new_rules
581
+ end
582
+
583
+ def as_array
584
+ self.compare
585
+ array = []
586
+ return array if self.equal?
587
+ array << "Changed chain: #{@chain1.name}"
588
+ array << "New policy: #{@chain2.policy}" if self.new_policy?
589
+ if self.missing.any?
590
+ self.missing.keys.sort.each{ |rule_num|
591
+ array << "-#{rule_num}: #{self.missing[rule_num]}"
592
+ }
593
+ end
594
+ if self.new.any?
595
+ self.new.keys.sort.each{ |rule_num|
596
+ array << "+#{rule_num}: #{self.new[rule_num]}"
597
+ }
598
+ end
599
+ return array
600
+ end
601
+
602
+ def new_policy?
603
+ self.compare
604
+ return @new_policy
605
+ end
606
+ end
607
+
608
+ class Rule
609
+ # possible key names for custom named tcp and/or udp services
610
+ @@valid_custom_service_keys = %w/service_name service_udp service_tcp/
611
+ attr_reader :position, :rule_hash, :type
612
+
613
+ @@parse_comment_regex = /^\-m\s+comment\s+\-\-comment\s+"([^"]+)"/
614
+
615
+ def initialize(rule_info, my_chain)
616
+ $log.debug("received Rule info #{rule_info.inspect}")
617
+
618
+ @rule_info = rule_info
619
+ case rule_info
620
+ when String
621
+ self.handle_string(rule_info)
622
+ when Hash
623
+ @rule_hash = rule_info
624
+ else
625
+ raise "don't know how to handle rule_info: #{rule_info.inspect}"
626
+ end
627
+
628
+ @my_chain = my_chain
629
+
630
+ @position = nil
631
+
632
+ # expanded rules will use this instead of @args
633
+ @children = []
634
+
635
+ @args = ''
636
+
637
+ self.handle_requires_primitive
638
+
639
+ case @rule_hash.length
640
+ when 1
641
+ @type = @rule_hash.keys.first
642
+ when 2, 3
643
+ @type = 'custom_service'
644
+ else
645
+ raise 'do not know how to handle this rule'
646
+ end
647
+
648
+ $log.debug("create Rule #{@type}")
649
+
650
+ case @type
651
+ when 'comment'
652
+
653
+ when 'custom_service'
654
+ self.handle_custom_service()
655
+
656
+ when 'empty'
657
+
658
+ when 'interpolated'
659
+ self.handle_interpolated()
660
+
661
+ when 'macro'
662
+ self.handle_macro()
663
+
664
+ when 'node_addition_points'
665
+ self.handle_node_addition_points()
666
+
667
+ when 'raw'
668
+
669
+ when 'service'
670
+ self.handle_service()
671
+
672
+ when 'service_tcp'
673
+
674
+ when 'service_udp'
675
+
676
+ when 'ulog'
677
+
678
+ else
679
+ raise "unrecognized rule type #{@type}"
680
+ end
681
+ end
682
+
683
+ def add_child(rule_hash)
684
+ @children.push(IPTables::Rule.new(rule_hash, @my_chain))
685
+ end
686
+
687
+ def handle_requires_primitive
688
+ @requires_primitive = nil
689
+ return unless @rule_hash.has_key? 'requires_primitive'
690
+ @requires_primitive = @rule_hash['requires_primitive']
691
+ @rule_hash.delete('requires_primitive')
692
+ config = @my_chain.my_table.my_iptables.config
693
+ raise 'missing config' if config.nil?
694
+ primitives = config.primitives
695
+ raise 'missing primitives' if primitives.nil?
696
+ @rule_hash = {'empty' => nil} unless primitives.has_primitive?(@requires_primitive)
697
+ end
698
+
699
+ def handle_custom_service()
700
+ raise "missing service name: #{@rule_hash.inspect}" unless @rule_hash.has_key? 'service_name'
701
+
702
+ custom_service_port = nil
703
+ custom_services = []
704
+ @rule_hash.keys.sort.each{ |key|
705
+ next if key == 'service_name'
706
+ raise "unknown service key: #{key}" unless @@valid_custom_service_keys.include? key
707
+ custom_services << {key => @rule_hash[key]}
708
+ # set the custom service port if exactly one custom service has a port
709
+ # or both services have the same port
710
+ if custom_service_port.nil?
711
+ custom_service_port = @rule_hash[key]
712
+ else
713
+ custom_service_port = nil unless @rule_hash[key].to_i == custom_service_port.to_i
714
+ end
715
+ }
716
+
717
+ if custom_service_port.nil?
718
+ self.add_child({'comment' => "_ #{@rule_hash['service_name']}"})
719
+ else
720
+ self.add_child({'comment' => "_ Port #{custom_service_port} - #{@rule_hash['service_name']}"})
721
+ end
722
+ custom_services.each{ |service_hash|
723
+ self.add_child(service_hash)
724
+ }
725
+ end
726
+
727
+ def handle_interpolated()
728
+ config = @my_chain.my_table.my_iptables.config
729
+ raise 'missing config' if config.nil?
730
+ interpolations = config.interpolations
731
+ $log.debug("interpolating: #{@rule_hash['interpolated']}")
732
+ interpolations.children(@rule_hash['interpolated']).each{ |rule_hash|
733
+ self.add_child(rule_hash)
734
+ }
735
+ end
736
+
737
+ def handle_macro()
738
+ config = @my_chain.my_table.my_iptables.config
739
+ raise 'missing config' if config.nil?
740
+ macro = config.macros.named[@rule_hash['macro']]
741
+ $log.debug("macro: #{macro.name}")
742
+ macro.children.each{ |rule_hash|
743
+ self.add_child(rule_hash)
744
+ }
745
+ end
746
+
747
+ def handle_node_addition_points()
748
+ self.add_child({'empty' => nil})
749
+ @rule_hash['node_addition_points'].each{ |addition_name|
750
+ @my_chain.register_node_addition_point(self, addition_name)
751
+ }
752
+ end
753
+
754
+ def handle_service()
755
+ config = @my_chain.my_table.my_iptables.config
756
+ raise 'missing config' if config.nil?
757
+ service = config.services.named[@rule_hash['service']]
758
+ $log.debug("service: #{service.name}")
759
+ service.children.each{ |rule_hash|
760
+ self.add_child(rule_hash)
761
+ }
762
+ end
763
+
764
+ def handle_string(rule_info)
765
+ # try to parse strings
766
+
767
+ if rule_info =~ @@parse_comment_regex
768
+ # if we're a comment, set as comment
769
+ @rule_hash = {'comment' => $1}
770
+ else
771
+ # otherwise set as raw
772
+ @rule_hash = {'raw' => rule_info}
773
+ end
774
+ end
775
+
776
+ def as_array(comments = true)
777
+ case @type
778
+ when 'comment'
779
+ return [] unless comments
780
+ self.generate_comment()
781
+
782
+ when 'empty'
783
+ return []
784
+
785
+ when 'raw'
786
+ self.generate_raw()
787
+
788
+ when 'service_tcp'
789
+ self.generate_tcp()
790
+
791
+ when 'service_udp'
792
+ self.generate_udp()
793
+
794
+ when 'ulog'
795
+ self.generate_ulog()
796
+ end
797
+
798
+ if @children.empty?
799
+ raise "@args is empty" unless @args.length > 0
800
+ return ["-A #{@my_chain.name} #{@args}"]
801
+ else
802
+ rules = @children.collect{ |child| child.as_array(comments)}.flatten
803
+ $log.debug(rules)
804
+ return rules
805
+ end
806
+ end
807
+
808
+ def generate_comment()
809
+ @args = %Q|-m comment --comment "#{@rule_hash['comment']}"|
810
+ end
811
+
812
+ def generate_raw()
813
+ @args = @rule_hash['raw']
814
+ end
815
+
816
+ def generate_tcp()
817
+ @args = "-p tcp -m tcp --sport 1024:65535 --dport #{@rule_hash['service_tcp']} -m state --state NEW,ESTABLISHED -j ACCEPT"
818
+ end
819
+
820
+ def generate_udp()
821
+ @args = "-p udp -m udp --sport 1024:65535 --dport #{@rule_hash['service_udp']} -m state --state NEW,ESTABLISHED -j ACCEPT"
822
+ end
823
+
824
+ def generate_ulog()
825
+ @args = %Q|-m limit --limit 1/sec --limit-burst 2 -j ULOG --ulog-prefix "#{@my_chain.name}:"|
826
+
827
+ if @rule_hash['ulog'] == '-p tcp'
828
+ @args = "-p tcp #{@args}"
829
+ end
830
+ end
831
+
832
+ def path()
833
+ @my_chain.path + ".#{@position}"
834
+ end
835
+
836
+ def set_position(number)
837
+ @position = number
838
+ end
839
+
840
+ def apply_additions(other_firewall)
841
+ @rule_hash['node_addition_points'].each{ |addition_name|
842
+ other_rules = other_firewall.get_node_additions(@my_chain.my_table.name, addition_name)
843
+ next if other_rules.nil?
844
+ $log.debug("applying additions at #{addition_name}")
845
+ other_rules.each{ |other_rule_object|
846
+ self.add_child(other_rule_object.rule_hash)
847
+ }
848
+ }
849
+ end
850
+ end
851
+ end