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
@@ -0,0 +1,106 @@
|
|
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 DeleteChain
|
6
|
+
# Move on Module
|
7
|
+
include MoveOn
|
8
|
+
|
9
|
+
NO_CHAIN_MATCH_ERROR = 'iptables: No chain/target/match by that name.'.freeze
|
10
|
+
CHAIN_NOT_EMPTY = 'iptables: Directory not empty.'.freeze
|
11
|
+
|
12
|
+
KNOWN_ERRORS = [NO_CHAIN_MATCH_ERROR, CHAIN_NOT_EMPTY].freeze
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
# Determine a given error. Optionally a chain can be used to provide better context.
|
16
|
+
private_class_method def self.determine_error(error:, chain: false)
|
17
|
+
if error == NO_CHAIN_MATCH_ERROR
|
18
|
+
raise ChainExistenceError, "#{chain} doesn't exist!"
|
19
|
+
elsif
|
20
|
+
raise ChainNotEmpty, "#{chain} is not empty! Will probably need to flush (-F) it to delete it!"
|
21
|
+
else
|
22
|
+
raise error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Delete all of the user defined chains.
|
27
|
+
#
|
28
|
+
# @example Basic Usage
|
29
|
+
# Iptablez::Commands::DeleteChain.all
|
30
|
+
# # => {"dogs"=>true}
|
31
|
+
#
|
32
|
+
# @example Basic Usage with a Block
|
33
|
+
# Iptablez::Commands::DeleteChain.all do |name, result|
|
34
|
+
# puts "#{name} has been deleted." if result # true
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# @yield Each chain name and boolean if it has been successfully deleted.
|
38
|
+
# @return [Hash] Key value pairing of each user defined chain and boolean if it has been successfully deleted.
|
39
|
+
def self.all(error: false, continue: !error)
|
40
|
+
results = {}
|
41
|
+
chains(names: Iptablez::Chains.user_defined, continue: continue, fly_by: fly_by) do |name, result|
|
42
|
+
yield [name, result] if block_given?
|
43
|
+
results[name] = result
|
44
|
+
end
|
45
|
+
return false if results.empty?
|
46
|
+
results
|
47
|
+
end
|
48
|
+
|
49
|
+
# Delete a chain of a given +name+. This is the heart of this module.
|
50
|
+
# @param name [String] Single chain +name+.
|
51
|
+
# @param error [Boolean] Determine if operations should raise/halt other possible operations with errors.
|
52
|
+
# @param continue [Boolean] Determine if operations should continue despite errors.
|
53
|
+
#
|
54
|
+
# @example Basic Usage
|
55
|
+
# Iptablez::Commands::DeleteChain.chain(name: "dogs")
|
56
|
+
# # => false
|
57
|
+
# Iptablez::Commands::DeleteChain.chain(name: "cats")
|
58
|
+
# # => true
|
59
|
+
# @example Basic Usage with a Block
|
60
|
+
# Iptablez::Commands::DeleteChain.chain(name: "dogs") do |name, result|
|
61
|
+
# puts "#{name} deleted!" if result
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# @yield [String, Boolean] The +name+ of the chain and +result+ of the operation if a block if given.
|
65
|
+
# @return [Boolean] The result of the operation.
|
66
|
+
#
|
67
|
+
# @raise An error will be raised if the +error+ or +continue+ keywords are +true+ and the operation fails.
|
68
|
+
def self.chain(name:, error: false, continue: !error)
|
69
|
+
name = name.to_s unless name.is_a? String
|
70
|
+
_, e, s = Open3.capture3(Iptablez.bin_path, '-X', name.shellescape)
|
71
|
+
e.strip! # remove new line
|
72
|
+
if s.success?
|
73
|
+
yield [name, true] if block_given?
|
74
|
+
return true
|
75
|
+
elsif MoveOn.continue?(continue: continue, message: e, known_errors: KNOWN_ERRORS)
|
76
|
+
yield [name, false] if block_given?
|
77
|
+
return false
|
78
|
+
else
|
79
|
+
determine_error(chain: name, error: e)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Delete each name is a given array of names.
|
84
|
+
# @param names [Array<String>] An array of chains to delete.
|
85
|
+
# @param error [Boolean] Determine if operations should raise/halt other possible operations with errors.
|
86
|
+
# @param continue [Boolean] Determine if operations should continue despite errors.
|
87
|
+
#
|
88
|
+
# @example Basic Usage
|
89
|
+
# Iptablez::Commands::DeleteChain.chain(names: ["dogs", "whales"])
|
90
|
+
# # => {"dogs"=>false, "whales"=>true}
|
91
|
+
#
|
92
|
+
# @yield [String, Boolean] The name of the chain and result of the operation if a block if given.
|
93
|
+
# @return [Hash] Key value pairing of each given chain and the result of the operation.
|
94
|
+
def self.chains(names:, error: false, continue: !error)
|
95
|
+
results = {}
|
96
|
+
names.each do |name|
|
97
|
+
results[name] = chain(name: name, continue: continue) do |name, result|
|
98
|
+
yield [name, result] if block_given?
|
99
|
+
end
|
100
|
+
end
|
101
|
+
results
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Iptablez
|
2
|
+
module Commands
|
3
|
+
module FlushChain
|
4
|
+
# Move on Module
|
5
|
+
include MoveOn
|
6
|
+
|
7
|
+
NO_CHAIN_MATCH_ERROR = 'iptables: No chain/target/match by that name.'.freeze
|
8
|
+
KNOWN_ERRORS = [NO_CHAIN_MATCH_ERROR].freeze
|
9
|
+
|
10
|
+
# Flush all of the possible `iptables` chains.
|
11
|
+
# @example Basic Usage
|
12
|
+
# Iptablez::Commands::Flush.all
|
13
|
+
# # => {"INPUT"=>true, "FORWARD"=>true, "OUTPUT"=>true}
|
14
|
+
# @example Basic Usage with a Block
|
15
|
+
# Iptablez::Commands::Flush.all do |name, result|
|
16
|
+
# puts "#{name} flushed!" if result
|
17
|
+
# end
|
18
|
+
def self.all(names: Iptablez::Chains.all, error: false, continue: !error)
|
19
|
+
chains(names: names, continue: continue) do |name, result|
|
20
|
+
yield [name, result] if block_given?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Flush the rules for a chain of a given `name`. This is the heart of this modules.
|
25
|
+
# @todo Document params.
|
26
|
+
# @example Basic Usage
|
27
|
+
# Iptablez::Commands::Flush.chain(name: "INPUT")
|
28
|
+
# # => true
|
29
|
+
# Iptablez::Commands::Flush.chain(name: "cats") # not a real chain
|
30
|
+
# # => false
|
31
|
+
# @example Basic Usage with a Block
|
32
|
+
# Iptablez::Commands::Flush.chain(name: "INPUT") do |name, result|
|
33
|
+
# puts "#{name} flushed" if result
|
34
|
+
# end
|
35
|
+
def self.chain(name:, error: false, continue: !error)
|
36
|
+
name = name.to_s unless name.is_a? String
|
37
|
+
_, e, s = Open3.capture3(Iptablez.bin_path, '-F', name.shellescape)
|
38
|
+
e.strip!
|
39
|
+
if s.success?
|
40
|
+
yield [name, true] if block_given?
|
41
|
+
return true
|
42
|
+
elsif MoveOn.continue?(continue: continue, message: e, known_errors: KNOWN_ERRORS)
|
43
|
+
yield [name, false] if block_given?
|
44
|
+
return false
|
45
|
+
else
|
46
|
+
determine_error(chain: name, error: e)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Flush the rules for multiple chains of their given `names`.
|
51
|
+
# @todo Document params.
|
52
|
+
# @example Basic Usage
|
53
|
+
# Iptablez::Commands::Flush.chains(names: ["dogs", "cats"])
|
54
|
+
# # => {"dogs"=>true, "cats"=>false}
|
55
|
+
# @example Basic Usage with a Block
|
56
|
+
# Iptablez::Commands::Flush.chains(names: ["dogs", "cats"]) do |name, result|
|
57
|
+
# puts "#{name} flushed" if result
|
58
|
+
# end
|
59
|
+
def self.chains(names:, error: false, continue: !error)
|
60
|
+
results = {}
|
61
|
+
names.each do |name|
|
62
|
+
results[name] = chain(name: name, continue: continue) do |name, result|
|
63
|
+
yield [name, result] if block_given?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
results
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
module Iptablez
|
2
|
+
module Commands
|
3
|
+
# Kent, wtf is this shit?
|
4
|
+
# Don't worry about iiiiit bruuuhhhhhvvv.
|
5
|
+
# @author Kent 'picat' Gruber
|
6
|
+
module ArgumentHelpers
|
7
|
+
|
8
|
+
def self.wait(seconds: false)
|
9
|
+
if seconds # don't wait forever? :P
|
10
|
+
{ wait: "-w #{seconds}" }
|
11
|
+
else
|
12
|
+
{ wait: "-w" }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.counters(packets:, bytes:)
|
17
|
+
{ counters: "-c #{packets} #{bytes}" }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.fragment
|
21
|
+
{ fragment: "-f" }
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.destination(dst:, ip: true, port: !ip)
|
25
|
+
unless port
|
26
|
+
{ destination: "-d #{dst}" }
|
27
|
+
else
|
28
|
+
destination_port(dst: dst)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.destination_port(dst:)
|
33
|
+
{ destination_port: "--dport #{dst}" }
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.source(src:, ip: true, port: !ip)
|
37
|
+
unless port
|
38
|
+
{ source: "-s #{src}" }
|
39
|
+
else
|
40
|
+
source_port(src: src)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.source_port(src:)
|
45
|
+
{ source_port: "-sport #{src}" }
|
46
|
+
end
|
47
|
+
|
48
|
+
# @todo
|
49
|
+
#def self.ip_range(from:, to:)
|
50
|
+
# { ip_range: "-m iprange #{fromt}-#{to}" }
|
51
|
+
#end
|
52
|
+
|
53
|
+
def self.table(name: "filter")
|
54
|
+
{ table: "-t #{name}" }
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.jump(target:)
|
58
|
+
{ jump: "-j #{target}" }
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.goto(target:)
|
62
|
+
{ goto: "-g #{target}" }
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.interface(name:, out: false)
|
66
|
+
argument = if out
|
67
|
+
"-o"
|
68
|
+
else
|
69
|
+
"-i"
|
70
|
+
end
|
71
|
+
{ interface: "#{argument} #{name}" }
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.protocol(protocol:)
|
75
|
+
{ protocol: "-p #{protocol}" }
|
76
|
+
end
|
77
|
+
|
78
|
+
# @example Basic Usage
|
79
|
+
# # note how jump: is compared to goto:
|
80
|
+
# args = {:goto=>"-g INPUT", :jump=>"INPUT"}
|
81
|
+
# # lets normalize that shit
|
82
|
+
# Iptablez::Commands::ArgumentHelpers.normalize_arguments(args)
|
83
|
+
# # => {:jump=>"-j INPUT", :goto=>"-g INPUT"}
|
84
|
+
def self.normalize_arguments(args)
|
85
|
+
results = {}
|
86
|
+
results[:jump] = normalize_jump(args[:jump])[:jump] if args[:jump]
|
87
|
+
results[:goto] = normalize_goto(args[:goto])[:goto] if args[:goto]
|
88
|
+
results[:protocol] = normalize_protocol(args[:protocol])[:protocol] if args[:protocol]
|
89
|
+
results[:interface]= normalize_interface(args[:interface])[:interface] if args[:interface]
|
90
|
+
results[:src] = normalize_source(args[:src])[:source] if args[:src]
|
91
|
+
results[:sport] = normalize_source(args[:sport], port: true)[:source_port] if args[:sport]
|
92
|
+
results[:dst] = normalize_destination(args[:dst])[:destination] if args[:dst]
|
93
|
+
results[:dport] = normalize_destination(args[:dport], port: true)[:destination_port] if args[:dport]
|
94
|
+
results
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.normalize_jump(arg)
|
98
|
+
if arg["-j"] || arg["--jump"]
|
99
|
+
{ jump: arg }
|
100
|
+
else
|
101
|
+
jump(target: arg)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.normalize_goto(arg)
|
106
|
+
if arg["-g"] || arg["--goto"]
|
107
|
+
{ goto: arg }
|
108
|
+
else
|
109
|
+
goto(target: arg)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.normalize_protocol(arg)
|
114
|
+
if arg["-p"] # @todo Check || arg["--protocol"]
|
115
|
+
{ protocol: arg }
|
116
|
+
else
|
117
|
+
goto(target: arg)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.normalize_interface(arg, out: false)
|
122
|
+
if arg["-i"] || arg["-o"] # @todo
|
123
|
+
{ interface: arg }
|
124
|
+
else
|
125
|
+
interface(name: arg, out: out)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.normalize_table(arg)
|
130
|
+
if arg["-t"] || arg["--table"] # @todo
|
131
|
+
{ table: arg }
|
132
|
+
else
|
133
|
+
table(name: arg)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.normalize_destination(arg, port: false)
|
138
|
+
if arg["-d"] || arg["--dport"] # @todo
|
139
|
+
if port
|
140
|
+
{ destination_port: arg }
|
141
|
+
else
|
142
|
+
{ destination: arg }
|
143
|
+
end
|
144
|
+
else
|
145
|
+
destination(dst: arg, port: port)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.normalize_source(arg, port: false)
|
150
|
+
if arg["-s"] || arg["--sport"] # @todo
|
151
|
+
if port
|
152
|
+
{ source_port: arg }
|
153
|
+
else
|
154
|
+
{ source: arg }
|
155
|
+
end
|
156
|
+
else
|
157
|
+
source(src: arg, port: port)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Iptablez
|
2
|
+
module Commands
|
3
|
+
# @todo Document this module. Kind'a important.
|
4
|
+
module MoveOn
|
5
|
+
# @api private
|
6
|
+
# Determine if the method should should move on with its life when things go wrong.
|
7
|
+
def self.continue?(continue:, message:, known_errors:, force: false)
|
8
|
+
return true if force
|
9
|
+
return true if continue && known_errors.include?(message)
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Iptablez
|
2
|
+
module Commands
|
3
|
+
# @todo Add inline document to this.
|
4
|
+
module Interface
|
5
|
+
|
6
|
+
def self.delete_chain(name: false, all: false)
|
7
|
+
if name
|
8
|
+
DeleteChain.chain(name: name)
|
9
|
+
elsif all
|
10
|
+
DeleteChain.all
|
11
|
+
else
|
12
|
+
raise "No chain name/all specified."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.flush(chain: false, all: true)
|
17
|
+
if chain
|
18
|
+
Flush.chain(name: chain)
|
19
|
+
elsif all
|
20
|
+
Flush.all
|
21
|
+
else
|
22
|
+
raise "No chain/all specified."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.list(chain: false, all: true, number: false)
|
27
|
+
if chain && ! number
|
28
|
+
List.chain(name: chain)
|
29
|
+
elsif number && chain
|
30
|
+
List.number(chain: chain, number: number)
|
31
|
+
elsif all
|
32
|
+
List.all
|
33
|
+
else
|
34
|
+
raise "No chain/number/all specified."
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.new_chain(name:)
|
39
|
+
NewChain.chain(name: name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.policy(target:, chain: false, all: true)
|
43
|
+
if chain && target
|
44
|
+
Policy.chain(name: chain, target: target)
|
45
|
+
elsif all && target
|
46
|
+
Policy.all(target: target)
|
47
|
+
else
|
48
|
+
raise "No chain/target/all specified."
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.rename_chain(from:, to:)
|
53
|
+
if from && to
|
54
|
+
RenameChain.chain(from: from, to: to)
|
55
|
+
else
|
56
|
+
raise "No from/to specified."
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.version(full: false)
|
61
|
+
if full
|
62
|
+
Version.full
|
63
|
+
else
|
64
|
+
Version.number
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.zero(all: true, chain: false)
|
69
|
+
if chain
|
70
|
+
Zero.chain(name: chain)
|
71
|
+
elsif all
|
72
|
+
Zero.all
|
73
|
+
else
|
74
|
+
raise "No all/chain specified."
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|