iptables-ruby 0.2.4

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.
@@ -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