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
@@ -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
|