iptablez 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
data/iptablez.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'iptablez/helpers/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "iptablez"
8
+ spec.version = Iptablez::VERSION
9
+ spec.authors = ["Kent 'picat' Gruber"]
10
+ spec.email = ["kgruber1@emich.edu"]
11
+
12
+ spec.summary = %q{A friendly Ruby API to iptables.}
13
+ #spec.description = %q{TODO: Write a longer description or delete this line.}
14
+ spec.homepage = "https://github.com/picatz/iptablez"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "bin"
21
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.14"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ end
data/lib/iptablez.rb ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "shellwords"
4
+ require "open3"
5
+
6
+ require "iptablez/helpers/version"
7
+ require "iptablez/helpers/helpers"
8
+ require "iptablez/commands/helpers/move_on"
9
+ require "iptablez/commands/helpers/errors"
10
+ require "iptablez/commands/helpers/argument_helpers"
11
+ require "iptablez/commands/flush_chain"
12
+ require "iptablez/commands/interface"
13
+ require "iptablez/commands/policy"
14
+ require "iptablez/commands/new_chain"
15
+ require "iptablez/commands/append_chain"
16
+ require "iptablez/commands/rename_chain"
17
+ require "iptablez/commands/delete_chain"
18
+ require "iptablez/commands/version"
19
+ require "iptablez/commands/list"
20
+ require "iptablez/chains/chains"
21
+
22
+ module Iptablez
23
+
24
+ def self.squid
25
+ # easter egg / mascot
26
+ puts "<コ:彡"
27
+ end
28
+
29
+ def self.giant_squid
30
+ puts File.read('giant_squid.txt')
31
+ end
32
+
33
+ end
@@ -0,0 +1,139 @@
1
+ # Chains Interface
2
+
3
+ The `Chains` Inteface is a collection of helpful methods to act as a clear, friendly interface to the possible collection of `Commands` which Iptablez implements. You're free to use the commands, just as the `Chains` interface would under the hood.
4
+
5
+ ## Method Overview
6
+ ```ruby
7
+ require 'iptablez'
8
+
9
+ Iptablez::Chains.all
10
+ Iptablez::Chains.defaults
11
+ Iptablez::Chains.create
12
+ Iptablez::Chains.rename
13
+ Iptablez::Chains.flush
14
+ Iptablez::Chains.delete
15
+ Iptablez::Chains.exists?
16
+ Iptablez::Chains.policies
17
+ Iptablez::Chains.policy?
18
+ Iptablez::Chains.user_defined
19
+ Iptablez::Chains.user_defined?
20
+ ```
21
+
22
+ ## All
23
+
24
+ Return a list of all the possible chains including defaults and user defined.
25
+
26
+ ```ruby
27
+ Iptablez::Chains.all
28
+ # => ["INPUT", "FORWARD", "OUTPUT", "cats", "dogs"]
29
+
30
+ Iptablez::Chains.all do |chain|
31
+ puts chain
32
+ end
33
+
34
+ Iptablez::Chains.all { |chain| puts chain }
35
+
36
+ Iptablez::Chains.all.each do |chain|
37
+ puts chain
38
+ end
39
+ ```
40
+
41
+ ## Defaults
42
+
43
+ Return a list of all the default chains.
44
+
45
+ ```ruby
46
+ Iptablez::Chains.defaults
47
+ # => ["INPUT", "FORWARD", "OUTPUT"]
48
+
49
+ Iptablez::Chains.defualts do |chain|
50
+ puts chain
51
+ end
52
+
53
+ Iptablez::Chains.defaults { |chain| puts chain }
54
+
55
+ Iptablez::Chains.defaults.each do |chain|
56
+ puts chain
57
+ end
58
+ ```
59
+
60
+ ## Delete
61
+
62
+ Delete a list of a given single `name` or an array of `names`. If the chain is not empty, the operation will fail. Consider flushing the chain(s) if you want to delete them.
63
+
64
+ ```ruby
65
+ Iptablez::Chains.delete(name: "frogs") # frogs is not a real chain
66
+ # => false
67
+
68
+ Iptablez::Chains.delete(name: "frogs") do |name, result|
69
+ if result # this will be false
70
+ puts "#{name} was deleted!"
71
+ else
72
+ puts "#{name} couldn't be deleted!"
73
+ end
74
+ end
75
+
76
+ Iptablez::Chains.delete(names: ["dogs", "cats"])
77
+ # => {"dogs"=>true, "cats"=>true}
78
+
79
+ Iptablez::Chains.delete(names: ["dogs", "cats"]) do |name, result|
80
+ puts name if result # won't happen, already deleted! :)
81
+ end
82
+ # => {"dogs"=>false, "cats"=>false}
83
+ ```
84
+
85
+ ## Exists?
86
+
87
+ Check if a list of a given single `name` or an array of `names` actually exists.
88
+
89
+ ```ruby
90
+ Iptablez::Chains.exists?(name: "frogs") # frogs is not a real chain
91
+ # => false
92
+
93
+ Iptablez::Chains.exists?(name: "frogs") do |name, result|
94
+ if result # this will be false
95
+ puts "#{name} exists!"
96
+ else
97
+ puts "#{name} doesn't exist!"
98
+ end
99
+ end
100
+
101
+ Iptablez::Chains.exists?(names: ["dogs", "cats"])
102
+ # => {"dogs"=>true, "cats"=>true}
103
+
104
+ Iptablez::Chains.exists?(names: ["dogs", "cats"]) do |name, result|
105
+ puts name if result # won't happen, already deleted! :)
106
+ end
107
+ # => {"dogs"=>false, "cats"=>false}
108
+ ```
109
+
110
+ ## Policies
111
+
112
+ Get a a list of a given single `name` or an array of `names` default policy response/target.
113
+
114
+ ```ruby
115
+ Iptablez::Chains.policies(name: "frogs") # frogs is not a real chain
116
+ # => false
117
+
118
+ Iptablez::Chains.exists?(name: "frogs") do |name, result|
119
+ if result # this will be false
120
+ puts "#{name} exists!"
121
+ else
122
+ puts "#{name} doesn't exist!"
123
+ end
124
+ end
125
+
126
+ Iptablez::Chains.exists?(names: ["dogs", "cats"])
127
+ # => {"dogs"=>true, "cats"=>true}
128
+
129
+ Iptablez::Chains.exists?(names: ["dogs", "cats"]) do |name, result|
130
+ puts name if result # won't happen, already deleted! :)
131
+ end
132
+ # => {"dogs"=>false, "cats"=>false}
133
+ ```
134
+
135
+
136
+
137
+
138
+
139
+
@@ -0,0 +1,227 @@
1
+ module Iptablez
2
+
3
+ module Chains
4
+
5
+ DEFAULT = ["INPUT", "FORWARD", "OUTPUT"]
6
+
7
+ # List all of the current chains found in +iptables+.
8
+ #
9
+ # @example Basic Usage
10
+ # Iptablez::Chains.all
11
+ # # => ["INPUT", "FORWARD", "OUTPUT"]
12
+ #
13
+ # @example Block Usage
14
+ # Iptablez::Chains.all do |chain|
15
+ # puts chain
16
+ # end
17
+ #
18
+ # @yield Each chain if a block is given.
19
+ # @return [Array<String>] An array of chain names.
20
+ def self.all
21
+ chains = Commands::List.full.find_all do |line|
22
+ line if line.split[0] == "Chain"
23
+ end.map(&:split).collect { |array| array[1] }
24
+ chains.each { |c| yield c } if block_given?
25
+ return chains
26
+ end
27
+
28
+ # List the default policies for default chains.
29
+ #
30
+ # @example Basic Usage
31
+ # Iptablez::Chains.policies
32
+ # # => {"INPUT"=>"ACCEPT", "FORWARD"=>"ACCEPT", "OUTPUT"=>"ACCEPT"}
33
+ #
34
+ # @example Block Usage
35
+ # Iptablez::Chains.policies do |name, policy|
36
+ # puts "#{name}: #{policy}"
37
+ # end
38
+ #
39
+ # @yield Each chain name and policy if a block is given.
40
+ # @return [Hash] Key value pairing of each chain and its default policy.
41
+ def self.policies(names: Iptablez::Chains.defaults, error: false, continue: !error)
42
+ Commands::List.defaults(names: names, continue: continue) do |result|
43
+ yield result if block_given?
44
+ end
45
+ end
46
+
47
+ # Check if there are any user defined chains, optionally
48
+ # giving a single name to check or an array of names.
49
+ #
50
+ # @example Basic Usage
51
+ # Iptablez::Chains.user_defined?
52
+ # # => false
53
+ # @example Basic Block Usage
54
+ # Iptablez::Chains.user_defined? do |result|
55
+ # if result
56
+ # puts "Found user defined chains."
57
+ # else
58
+ # puts "Did not find user defined chains."
59
+ # end
60
+ # end
61
+ # @example Check Single Name
62
+ # Iptablez::Chains.user_defined?(name: "dogs")
63
+ # # => false
64
+ # @example Check Single Name with a Block
65
+ # Iptablez::Chains.user_defined?(name: "dogs") do |result|
66
+ # result ? "User defined!" : "No user defined!"
67
+ # end
68
+ # @example Check Multiple Names
69
+ # Iptablez::Chains.user_defined?(names: ["dogs", "frogs"])
70
+ # # => {"dogs"=>false, "frogs"=>false}
71
+ # @example Check Multiple Names with a Block
72
+ # Iptablez::Chains.user_defined?(names: ["dogs", "frogs"]) do |name, result|
73
+ # phrase = if result
74
+ # "is"
75
+ # else
76
+ # "is not"
77
+ # end
78
+ # puts "#{name} #{phrase} user defined."
79
+ # end
80
+ # @param name [String] Single name.
81
+ # @param names [Array<String>] Multiple names.
82
+ #
83
+ # @yield results if a block is given.
84
+ # @return [Hash] key value pairing of each chain and the result of the check.
85
+ def self.user_defined?(name: false, names: [])
86
+ if name && names.empty?
87
+ r = user_defined.include?(name)
88
+ return r unless block_given?
89
+ yield r
90
+ elsif names[0] && ! name
91
+ r = {}
92
+ names.each do |n|
93
+ r.clear if block_given?
94
+ r[n] = user_defined.include?(n)
95
+ yield r.flatten if block_given?
96
+ end
97
+ return r unless block_given?
98
+ elsif names[0] && name
99
+ raise "Cannot use both a single name and multiple names together."
100
+ else
101
+ all.count > 3 ? true : false
102
+ end
103
+ end
104
+
105
+ # List all of the user_defined chains.
106
+ #
107
+ # @yield Each name of the user defined chains if a block if given.
108
+ # @return [Array<String>] Easy user defined chain as an array.
109
+ def self.user_defined
110
+ user_defined_chains = all.find_all { |c| c unless DEFAULT.include?(c) }
111
+ return user_defined_chains unless block_given?
112
+ user_defined_chains.each { |c| yield c }
113
+ end
114
+
115
+ # Check if a chain exists by a given name.
116
+ # @todo Fix documentation.
117
+ # @param name [String] Single name.
118
+ # @param names [Array<String>] Multiple names.
119
+ # @return [Boolean] Easy user defined chain as an array.
120
+ def self.exists?(name: false, names: [])
121
+ if name
122
+ all do |chain|
123
+ if chain == name
124
+ yield [name, true] if block_given?
125
+ return true
126
+ end
127
+ end
128
+ elsif !names.empty?
129
+ results = {}
130
+ names.each do |name|
131
+ results[name] = false
132
+ all { |chain| results[name] = true if chain == name }
133
+ yield [name, results[name]] if block_given?
134
+ end
135
+ results
136
+ end
137
+ end
138
+
139
+ def self.defaults
140
+ return DEFAULT unless block_given?
141
+ DEFAULT.each { |c| yield c }
142
+ end
143
+
144
+ def self.policy?(name:, policy:, names: [])
145
+ if name && names.empty?
146
+ r = if policies[name] == policy
147
+ true
148
+ else
149
+ false
150
+ end
151
+ return r unless block_given?
152
+ yield r
153
+ elsif names[0] && ! name && policy
154
+ r = {}
155
+ names.each do |n|
156
+ begin
157
+ r[n] = if policies[name] == policy
158
+ true
159
+ else
160
+ false
161
+ end
162
+ yield r[n] if block_given?
163
+ rescue => e
164
+ raise e
165
+ end
166
+ end
167
+ return r unless block_given?
168
+ elsif name && names[0]
169
+ raise "Cannot use both a single name and multiple names together."
170
+ end
171
+ end
172
+
173
+ def self.create(name: false, names: [], error: false, continue: !error)
174
+ if name
175
+ Commands::NewChain.chain(name: name, continue: continue) do |result|
176
+ yield result if block_given?
177
+ end
178
+ elsif !names.empty?
179
+ Commands::NewChain.chains(names: names, continue: continue) do |result|
180
+ yield result if block_given?
181
+ end
182
+ end
183
+ end
184
+
185
+ def self.delete(name: false, names: [], error: false, continue: !error)
186
+ if name
187
+ Commands::DeleteChain.chain(name: name, continue: continue) do |result|
188
+ yield result if block_given?
189
+ end
190
+ elsif !names.empty?
191
+ Commands::DeleteChain.chains(names: names, continue: continue) do |result|
192
+ yield result if block_given?
193
+ end
194
+ end
195
+ end
196
+
197
+ def self.flush(name: false, names: [], error: false, continue: !error)
198
+ if name
199
+ Commands::FlushChain.chain(name: name, continue: continue) do |result|
200
+ yield result if block_given?
201
+ end
202
+ elsif !names.empty?
203
+ Commands::FlushChain.chains(names: names, continue: continue) do |result|
204
+ yield result if block_given?
205
+ end
206
+ end
207
+ end
208
+
209
+ def self.rename(from: false, to: false, pairs: {}, error: false, continue: !error)
210
+ if from && to
211
+ Commands::RenameChain.chain(from: from, to: to, continue: continue) do |result|
212
+ yield result if block_given?
213
+ end
214
+ elsif (!from && to) or (from && !to)
215
+ return false if continue
216
+ raise ArgumentError, "Cannot use from: without to:"
217
+ elsif !pairs.empty?
218
+ Commands::RenameChain.chains(pairs: pairs, continue: continue) do |result|
219
+ yield result if block_given?
220
+ end
221
+ end
222
+ end
223
+
224
+
225
+ end
226
+
227
+ end
@@ -0,0 +1,62 @@
1
+ module Iptablez
2
+ module Commands
3
+ # The namespace to describe the `iptables` `-X` argument to delete a chain.
4
+ # @author Kent 'picat' Gruber
5
+ module AppendChain
6
+ # Move on Module
7
+ include MoveOn
8
+ include ArgumentHelpers
9
+
10
+ NO_CHAIN_MATCH_ERROR = 'iptables: No chain/target/match by that name.'.freeze
11
+ KNOWN_ERRORS = [NO_CHAIN_MATCH_ERROR].freeze
12
+
13
+ # @api private
14
+ # Determine a given error. Optionally a chain can be used to provide better context.
15
+ private_class_method def self.determine_error(error:, chain: false)
16
+ if error == NO_CHAIN_MATCH_ERROR
17
+ raise ChainExistenceError, "#{chain} doesn't exist!"
18
+ elsif
19
+ raise ChainNotEmpty, "#{chain} is not empty! Will probably need to flush (-F) it to delete it!"
20
+ else
21
+ raise error
22
+ end
23
+ end
24
+
25
+ # Delete a chain of a given +name+. This is the heart of this module.
26
+ # @param name [String] Single chain +name+.
27
+ # @param error [Boolean] Determine if operations should raise/halt other possible operations with errors.
28
+ # @param continue [Boolean] Determine if operations should continue despite errors.
29
+ #
30
+ # @example Basic Usage
31
+ # Iptablez::Commands::AppendChain.chain(name: "dogs")
32
+ # # => true
33
+ # Iptablez::Commands::List.chain(name: "kittens")
34
+ # # => ["all -- anywhere anywhere"]
35
+ #
36
+ # @yield [String, Boolean] The +name+ of the chain and +result+ of the operation if a block if given.
37
+ # @return [Boolean] The result of the operation.
38
+ #
39
+ # @raise An error will be raised if the +error+ or +continue+ keywords are +true+ and the operation fails.
40
+ def self.chain(name:, error: false, continue: !error, **args)
41
+ name = name.to_s unless name.is_a? String
42
+ name = name.shellescape
43
+ first_arguments = [Iptablez.bin_path, '-A', name]
44
+ args = ArgumentHelpers.normalize_arguments(args).values.map(&:split).flatten # fucking crazy shit here
45
+ cmd = first_arguments + args
46
+ _, e, s = Open3.capture3(cmd.join(" "))
47
+ #_, e, s = Open3.capture3(Iptablez.bin_path, '-A', name, args.values.map(&:split).flatten)
48
+ e.strip! # remove new line
49
+ if s.success?
50
+ yield [name, true] if block_given?
51
+ return true
52
+ elsif MoveOn.continue?(continue: continue, message: e, known_errors: KNOWN_ERRORS)
53
+ yield [name, false] if block_given?
54
+ return false
55
+ else
56
+ determine_error(chain: name, error: e)
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+ end