ript 0.8.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.
Files changed (79) hide show
  1. data/.gitignore +6 -0
  2. data/.rbenv-version +1 -0
  3. data/AUTHORS.md +16 -0
  4. data/CHANGELOG.md +93 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +62 -0
  7. data/LICENCE +19 -0
  8. data/README.md +564 -0
  9. data/Rakefile +136 -0
  10. data/bin/rbenv-sudo +18 -0
  11. data/bin/ript +207 -0
  12. data/dist/init.d +48 -0
  13. data/examples/accept-multiple-from-and-to.rb +16 -0
  14. data/examples/accept-with-a-list-of-ports.rb +13 -0
  15. data/examples/accept-with-specific-port-and-interface.rb +14 -0
  16. data/examples/accept-without-specific-from.rb +11 -0
  17. data/examples/accept.rb +12 -0
  18. data/examples/basic.rb +4 -0
  19. data/examples/dash-in-partition-name.rb +2 -0
  20. data/examples/drop.rb +11 -0
  21. data/examples/duplicate-partition-names/foobar1.rb +2 -0
  22. data/examples/duplicate-partition-names/foobar2.rb +2 -0
  23. data/examples/errors-undefined-method-with-no-match.rb +12 -0
  24. data/examples/errors-undefined-method.rb +12 -0
  25. data/examples/forward-dnat-with-different-destination-port.rb +16 -0
  26. data/examples/forward-dnat-with-explicit-from-and-port-mappings.rb +11 -0
  27. data/examples/forward-dnat-with-explicit-from-and-ports.rb +11 -0
  28. data/examples/forward-dnat-with-explicit-from.rb +11 -0
  29. data/examples/forward-dnat-with-explicit-protocols.rb +15 -0
  30. data/examples/forward-dnat-with-multiple-froms.rb +13 -0
  31. data/examples/forward-dnat-with-multiple-ports.rb +10 -0
  32. data/examples/forward-dnat-with-multiple-sources.rb +15 -0
  33. data/examples/forward-dnat.rb +16 -0
  34. data/examples/forward-snat-with-explicit-from.rb +16 -0
  35. data/examples/forward-snat-with-multiple-sources.rb +13 -0
  36. data/examples/forward-snat.rb +9 -0
  37. data/examples/log-and-accept.rb +12 -0
  38. data/examples/log-and-drop.rb +11 -0
  39. data/examples/log-dnat.rb +10 -0
  40. data/examples/log-snat.rb +13 -0
  41. data/examples/log.rb +11 -0
  42. data/examples/missing-address-definition-in-destination.rb +15 -0
  43. data/examples/missing-address-definition-in-from.rb +15 -0
  44. data/examples/multiple-partitions-in-this-file.rb +14 -0
  45. data/examples/multiple-partitions/bar.rb +11 -0
  46. data/examples/multiple-partitions/foo.rb +17 -0
  47. data/examples/partition-name-exactly-20-characters.rb +2 -0
  48. data/examples/partition-name-longer-than-20-characters.rb +2 -0
  49. data/examples/postclean.rb +10 -0
  50. data/examples/preclean.rb +10 -0
  51. data/examples/raw-with-chain-deletion.rb +9 -0
  52. data/examples/raw-with-flush.rb +9 -0
  53. data/examples/raw.rb +50 -0
  54. data/examples/reject.rb +11 -0
  55. data/examples/space-in-partition-name.rb +2 -0
  56. data/features/cli.feature +115 -0
  57. data/features/dsl/errors.feature +107 -0
  58. data/features/dsl/filter.feature +187 -0
  59. data/features/dsl/logging.feature +114 -0
  60. data/features/dsl/nat.feature +271 -0
  61. data/features/dsl/raw.feature +28 -0
  62. data/features/setup.feature +58 -0
  63. data/features/step_definitions/cli_steps.rb +15 -0
  64. data/features/step_definitions/example_steps.rb +44 -0
  65. data/features/support/env.rb +25 -0
  66. data/lib/ript/bootstrap.rb +20 -0
  67. data/lib/ript/dsl.rb +14 -0
  68. data/lib/ript/dsl/primitives.rb +7 -0
  69. data/lib/ript/dsl/primitives/common.rb +78 -0
  70. data/lib/ript/dsl/primitives/filter.rb +145 -0
  71. data/lib/ript/dsl/primitives/nat.rb +206 -0
  72. data/lib/ript/dsl/primitives/raw.rb +45 -0
  73. data/lib/ript/exceptions.rb +2 -0
  74. data/lib/ript/partition.rb +162 -0
  75. data/lib/ript/patches.rb +10 -0
  76. data/lib/ript/rule.rb +70 -0
  77. data/lib/ript/version.rb +3 -0
  78. data/ript.gemspec +33 -0
  79. metadata +232 -0
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ module Ript
4
+ module DSL
5
+ module Primitives
6
+ module Raw
7
+
8
+ def raw?
9
+ @raw
10
+ end
11
+
12
+ def raw(rules)
13
+ @raw = true
14
+ commands = rules.split("\n").reject {|l| l !~ /^\s*[^#]+$/}
15
+
16
+ commands.each do |command|
17
+ validate_destructiveness(command)
18
+
19
+ attributes = {:raw => command}
20
+
21
+ @table << Rule.new(attributes)
22
+ end
23
+ end
24
+
25
+ private
26
+ def validate_destructiveness(command)
27
+ if command =~ /(\-F|\-\-flush)/
28
+ puts "Error: partition #{@name} - you can't use raw rules that flush tables or chains!"
29
+ puts "Offending rule:\n\n #{command}\n\n"
30
+ puts "Exiting."
31
+ exit 140
32
+ end
33
+
34
+ if command =~ /\s+(\-X|\-\-delete-chain)/
35
+ puts "Error: partition #{@name} - you can't use raw rules that delete chains!"
36
+ puts "Offending rule:\n\n #{command}\n\n"
37
+ puts "Exiting."
38
+ exit 140
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,2 @@
1
+ class LabelError < NameError
2
+ end
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << Pathname.new(__FILE__).dirname.parent.expand_path.to_s
4
+
5
+ module Ript
6
+ class Partition
7
+ attr_reader :name, :filename, :line
8
+
9
+ include DSL::Primitives::Common
10
+ include DSL::Primitives::NAT
11
+ include DSL::Primitives::Filter
12
+ include DSL::Primitives::Raw
13
+
14
+ def initialize(name, block, options={})
15
+ @filename, @line = caller[2].split(':')[0..1]
16
+ @labels = {}
17
+ @prerouting = []
18
+ @postrouting = []
19
+ @input = []
20
+ @forward = []
21
+ @table = []
22
+ @name = name
23
+ # TODO should we rename this to no_is or something since that is what it really means
24
+ if options[:rules]
25
+ @raw = true
26
+ @table = options[:rules]
27
+ end
28
+
29
+ # Even when suplying our own rules we need the placeholders below to know if anything changed
30
+ @setup = []
31
+ @setup << Rule.new("table" => "nat", "new-chain" => "#{@name}-d")
32
+ @setup << Rule.new("table" => "nat", "new-chain" => "#{@name}-s")
33
+ @setup << Rule.new("table" => "filter", "new-chain" => "#{@name}-a")
34
+
35
+ # Provide a label for the zero-address
36
+ label "all", :address => "0.0.0.0/0"
37
+
38
+ begin
39
+ instance_eval &block unless block.nil?
40
+ rescue NoMethodError => e
41
+ method = e.message[/`(.+)'/, 1]
42
+ filename, line = e.backtrace.first[/(.*):(\d)/].split(':')
43
+ if filename =~ /\/lib\/ript\//
44
+ puts "Looks like you found a bug in Ript around line #{line} in #{filename}"
45
+ puts "Specifically, this is the exception raised:"
46
+ puts
47
+ puts " #{e.message}"
48
+ puts
49
+ puts "And here is the backtrace:"
50
+ puts
51
+ puts e.backtrace.map {|l| " #{l}\n" }.join
52
+ puts
53
+ puts "Please report this bug at http://github.com/bulletproofnetworks/ript"
54
+ puts
55
+ else
56
+ puts "You tried using the '#{method}' method on line #{line} in #{filename}"
57
+ similar = self.class.instance_methods.grep(/#{method}/)
58
+ if similar.size > 0
59
+ puts "This method doesn't exist in the DSL. Did you mean:"
60
+ puts
61
+ self.class.instance_methods.grep(/#{method}/).each do |m|
62
+ puts " - #{m}"
63
+ end
64
+ puts
65
+ else
66
+ puts "This method doesn't exist in the DSL. There aren't any other methods with similar names. :-("
67
+ end
68
+ end
69
+ puts "Aborting."
70
+ exit 131
71
+ rescue LabelError => e
72
+ puts e.message
73
+ puts "Aborting."
74
+ exit 131
75
+ end
76
+ end
77
+
78
+ # FIXME: Maybe implement the concept of dirtiness?
79
+ def id
80
+ return @id if @id
81
+ joined = (@setup.map {|rule| rule.to_iptables } +
82
+ @prerouting.map {|rule| rule.to_iptables }.uniq +
83
+ @postrouting.map {|rule| rule.to_iptables }.uniq +
84
+ @input.map {|rule| rule.to_iptables }.uniq +
85
+ @forward.map {|rule| rule.to_iptables }.uniq +
86
+ @table.map {|rule| rule.to_iptables }.uniq).join(' ')
87
+ @id = "#{Digest::MD5.hexdigest(joined)[0..5]}"
88
+ end
89
+
90
+ def update_id(object, key, id)
91
+ object.map { |rule|
92
+ rule[key] += "#{id}" unless rule[key] == "LOG"
93
+ rule.to_iptables
94
+ }
95
+ end
96
+
97
+ def to_iptables
98
+ if raw?
99
+ # TODO How do we clean up raw rules?
100
+ puts update_id(@setup, "new-chain", id).uniq
101
+ puts @table.map {|rule| rule.to_iptables }
102
+ puts
103
+ else
104
+ puts update_id(@setup, "new-chain", id).uniq
105
+ puts update_id(@table, "append", id).uniq
106
+ puts update_id(@prerouting, "jump", id).uniq
107
+ puts update_id(@postrouting, "jump", id).uniq
108
+ puts update_id(@input, "jump", id).uniq
109
+ puts update_id(@forward, "jump", id).uniq
110
+ puts
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ @partitions = []
117
+ @filenames = []
118
+
119
+ def partition(name, &block)
120
+ filename, line = caller.first.split(':')[0..1]
121
+
122
+ if c = @partitions.find {|c| c.name == name } then
123
+ puts "Error: Partition name '#{name}' is already defined!"
124
+ puts " - existing definition: #{c.filename}:#{c.line}"
125
+ puts " - new definition: #{filename}:#{line}"
126
+ puts "Aborting."
127
+ exit 140
128
+ end
129
+
130
+ if name =~ /\s+/
131
+ puts "Error: #{filename}:#{line}"
132
+ puts "Error: Partition name '#{name}' can't contain whitespace."
133
+ puts "Aborting."
134
+ exit 140
135
+ end
136
+
137
+ if name.count('-') > 0
138
+ puts "Error: #{filename}:#{line}"
139
+ puts "Error: Partition name '#{name}' can't contain dashes ('-')."
140
+ puts "Aborting."
141
+ exit 140
142
+ end
143
+
144
+ if name.length > 20
145
+ puts "Error: #{filename}:#{line}"
146
+ puts "Error: Partition name '#{name}' cannot be longer than 20 characters."
147
+ puts "Aborting."
148
+ exit 140
149
+ end
150
+
151
+ if @filenames.include?(filename)
152
+ puts "Error: #{filename}:#{line}"
153
+ puts "Error: Multiple partition definitions are not permitted in the same file."
154
+ puts "Aborting."
155
+ exit 140
156
+ else
157
+ @filenames << filename
158
+ end
159
+
160
+ partition = Ript::Partition.new(name, block)
161
+ @partitions << partition
162
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ class Hash
4
+ def insert_before(key, opts={})
5
+ before = self.dup.take_while {|k, v| k != key }
6
+ after = self.dup.drop_while {|k, v| k != key }
7
+ before << opts.to_a.flatten
8
+ self.clear.merge!(Hash[before]).merge!(Hash[after])
9
+ end
10
+ end
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << Pathname.new(__FILE__).dirname.parent.expand_path.to_s
4
+
5
+ require 'digest/md5'
6
+
7
+ module Ript
8
+ class Rule
9
+ def initialize(opts={})
10
+ @comment = opts.delete(:comment)
11
+ @raw = opts.delete(:raw)
12
+ @args = []
13
+ @opts = opts
14
+ end
15
+
16
+ def [](key)
17
+ @opts[key]
18
+ end
19
+
20
+ def []=(key, value)
21
+ @opts[key] = value
22
+ end
23
+
24
+ def add_option(argument, parameter)
25
+ @args << "--#{argument} #{parameter}"
26
+ end
27
+
28
+ def to_iptables
29
+ @args.clear
30
+ @opts.each_pair do |argument, parameter|
31
+ add_option(argument, parameter)
32
+ end
33
+
34
+ if comment?
35
+ "#{self.to_command} #{self.comment}"
36
+ else
37
+ self.to_command
38
+ end
39
+ end
40
+
41
+ def raw?
42
+ @raw
43
+ end
44
+
45
+ def to_command
46
+ if raw?
47
+ @raw
48
+ else
49
+ "iptables #{@args.join(' ')}"
50
+ end
51
+ end
52
+
53
+ def comment
54
+ "--match comment --comment '#{id}'"
55
+ end
56
+
57
+ def id
58
+ Digest::MD5.hexdigest(self.to_command)
59
+ end
60
+
61
+ def comment?
62
+ @comment
63
+ end
64
+
65
+ # Display the rule in iptables form, perferably before an update_id has been run
66
+ def to_s
67
+ %("#{to_command}")
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,3 @@
1
+ module Ript
2
+ VERSION = '0.8.4'
3
+ end
@@ -0,0 +1,33 @@
1
+ #
2
+ # -*- encoding: utf-8 -*-
3
+ $:.push File.expand_path("../lib", __FILE__)
4
+ require "ript/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "ript"
8
+ s.version = Ript::VERSION
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = [ "Lindsay Holmwood" ]
11
+ s.email = [ "lindsay@bulletproof.net" ]
12
+ s.homepage = "http://bulletproof.net/"
13
+ s.summary = %q{DSL for iptables, and tool for incrementally applying firewall rules}
14
+ s.description = %q{Ript provides a clean Ruby DSL for describing firewall rules, and implements database migrations-like functionality for applying the rules}
15
+
16
+ s.rubyforge_project = "ript"
17
+
18
+ s.required_ruby_version = ">= 1.9.2"
19
+ s.required_rubygems_version = ">= 1.3.6"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = ["lib"]
25
+
26
+ #s.add_runtime_dependency "colorize", ">= 0"
27
+ s.add_development_dependency "rake", ">= 0"
28
+ s.add_development_dependency "rspec", ">= 0"
29
+ s.add_development_dependency "cucumber", ">= 1.1.9"
30
+ s.add_development_dependency "aruba", ">= 0"
31
+ s.add_development_dependency "colorize", ">= 0"
32
+ s.add_development_dependency "fpm", ">= 0.4.5"
33
+ end
metadata ADDED
@@ -0,0 +1,232 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ript
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Lindsay Holmwood
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: cucumber
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.1.9
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.9
62
+ - !ruby/object:Gem::Dependency
63
+ name: aruba
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: colorize
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: fpm
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: 0.4.5
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 0.4.5
110
+ description: Ript provides a clean Ruby DSL for describing firewall rules, and implements
111
+ database migrations-like functionality for applying the rules
112
+ email:
113
+ - lindsay@bulletproof.net
114
+ executables:
115
+ - rbenv-sudo
116
+ - ript
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - .gitignore
121
+ - .rbenv-version
122
+ - AUTHORS.md
123
+ - CHANGELOG.md
124
+ - Gemfile
125
+ - Gemfile.lock
126
+ - LICENCE
127
+ - README.md
128
+ - Rakefile
129
+ - bin/rbenv-sudo
130
+ - bin/ript
131
+ - dist/init.d
132
+ - examples/accept-multiple-from-and-to.rb
133
+ - examples/accept-with-a-list-of-ports.rb
134
+ - examples/accept-with-specific-port-and-interface.rb
135
+ - examples/accept-without-specific-from.rb
136
+ - examples/accept.rb
137
+ - examples/basic.rb
138
+ - examples/dash-in-partition-name.rb
139
+ - examples/drop.rb
140
+ - examples/duplicate-partition-names/foobar1.rb
141
+ - examples/duplicate-partition-names/foobar2.rb
142
+ - examples/errors-undefined-method-with-no-match.rb
143
+ - examples/errors-undefined-method.rb
144
+ - examples/forward-dnat-with-different-destination-port.rb
145
+ - examples/forward-dnat-with-explicit-from-and-port-mappings.rb
146
+ - examples/forward-dnat-with-explicit-from-and-ports.rb
147
+ - examples/forward-dnat-with-explicit-from.rb
148
+ - examples/forward-dnat-with-explicit-protocols.rb
149
+ - examples/forward-dnat-with-multiple-froms.rb
150
+ - examples/forward-dnat-with-multiple-ports.rb
151
+ - examples/forward-dnat-with-multiple-sources.rb
152
+ - examples/forward-dnat.rb
153
+ - examples/forward-snat-with-explicit-from.rb
154
+ - examples/forward-snat-with-multiple-sources.rb
155
+ - examples/forward-snat.rb
156
+ - examples/log-and-accept.rb
157
+ - examples/log-and-drop.rb
158
+ - examples/log-dnat.rb
159
+ - examples/log-snat.rb
160
+ - examples/log.rb
161
+ - examples/missing-address-definition-in-destination.rb
162
+ - examples/missing-address-definition-in-from.rb
163
+ - examples/multiple-partitions-in-this-file.rb
164
+ - examples/multiple-partitions/bar.rb
165
+ - examples/multiple-partitions/foo.rb
166
+ - examples/partition-name-exactly-20-characters.rb
167
+ - examples/partition-name-longer-than-20-characters.rb
168
+ - examples/postclean.rb
169
+ - examples/preclean.rb
170
+ - examples/raw-with-chain-deletion.rb
171
+ - examples/raw-with-flush.rb
172
+ - examples/raw.rb
173
+ - examples/reject.rb
174
+ - examples/space-in-partition-name.rb
175
+ - features/cli.feature
176
+ - features/dsl/errors.feature
177
+ - features/dsl/filter.feature
178
+ - features/dsl/logging.feature
179
+ - features/dsl/nat.feature
180
+ - features/dsl/raw.feature
181
+ - features/setup.feature
182
+ - features/step_definitions/cli_steps.rb
183
+ - features/step_definitions/example_steps.rb
184
+ - features/support/env.rb
185
+ - lib/ript/bootstrap.rb
186
+ - lib/ript/dsl.rb
187
+ - lib/ript/dsl/primitives.rb
188
+ - lib/ript/dsl/primitives/common.rb
189
+ - lib/ript/dsl/primitives/filter.rb
190
+ - lib/ript/dsl/primitives/nat.rb
191
+ - lib/ript/dsl/primitives/raw.rb
192
+ - lib/ript/exceptions.rb
193
+ - lib/ript/partition.rb
194
+ - lib/ript/patches.rb
195
+ - lib/ript/rule.rb
196
+ - lib/ript/version.rb
197
+ - ript.gemspec
198
+ homepage: http://bulletproof.net/
199
+ licenses: []
200
+ post_install_message:
201
+ rdoc_options: []
202
+ require_paths:
203
+ - lib
204
+ required_ruby_version: !ruby/object:Gem::Requirement
205
+ none: false
206
+ requirements:
207
+ - - ! '>='
208
+ - !ruby/object:Gem::Version
209
+ version: 1.9.2
210
+ required_rubygems_version: !ruby/object:Gem::Requirement
211
+ none: false
212
+ requirements:
213
+ - - ! '>='
214
+ - !ruby/object:Gem::Version
215
+ version: 1.3.6
216
+ requirements: []
217
+ rubyforge_project: ript
218
+ rubygems_version: 1.8.23
219
+ signing_key:
220
+ specification_version: 3
221
+ summary: DSL for iptables, and tool for incrementally applying firewall rules
222
+ test_files:
223
+ - features/cli.feature
224
+ - features/dsl/errors.feature
225
+ - features/dsl/filter.feature
226
+ - features/dsl/logging.feature
227
+ - features/dsl/nat.feature
228
+ - features/dsl/raw.feature
229
+ - features/setup.feature
230
+ - features/step_definitions/cli_steps.rb
231
+ - features/step_definitions/example_steps.rb
232
+ - features/support/env.rb