iptablez 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +141 -0
- data/Rakefile +6 -0
- data/Vagrantfile +100 -0
- data/bin/console +14 -0
- data/bin/iptblz.rb +21 -0
- data/bin/setup +8 -0
- data/giant_squid.txt +12 -0
- data/iptablez.gemspec +27 -0
- data/lib/iptablez.rb +33 -0
- data/lib/iptablez/chains/README.md +139 -0
- data/lib/iptablez/chains/chains.rb +227 -0
- data/lib/iptablez/commands/append_chain.rb +62 -0
- data/lib/iptablez/commands/delete_chain.rb +106 -0
- data/lib/iptablez/commands/flush_chain.rb +72 -0
- data/lib/iptablez/commands/helpers/argument_helpers.rb +163 -0
- data/lib/iptablez/commands/helpers/errors.rb +11 -0
- data/lib/iptablez/commands/helpers/move_on.rb +14 -0
- data/lib/iptablez/commands/interface.rb +80 -0
- data/lib/iptablez/commands/list.rb +210 -0
- data/lib/iptablez/commands/new_chain.rb +71 -0
- data/lib/iptablez/commands/policy.rb +65 -0
- data/lib/iptablez/commands/rename_chain.rb +65 -0
- data/lib/iptablez/commands/version.rb +18 -0
- data/lib/iptablez/commands/zero.rb +18 -0
- data/lib/iptablez/helpers/helpers.rb +46 -0
- data/lib/iptablez/helpers/version.rb +3 -0
- metadata +121 -0
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
|