iptablez 1.0.0.pre
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.
- 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
|