tablomat 1.2.1 → 1.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3acd84433060bbc3bf7e1502c05f0e9889694d3a32037e22e15f743ca01b5fc3
4
- data.tar.gz: 9dd4eaab8169d4a12bc8d84de57913f35d8ffc0d90d9f716e12a801f2936e4aa
3
+ metadata.gz: 173080a9ec9a2627d84f5d4cc6a9db6ae34cde2299411ff7ab8145250f303114
4
+ data.tar.gz: 7ea6702336b2d690e8c098a1b92af60c22522baaac228a8c4ec3b2c015715290
5
5
  SHA512:
6
- metadata.gz: 25075f75a86af90044a997409afec220443610589c68bba8d22144586724288ae860b5dcfb191ba64afaf14b420c8cfc838bc6e7f3b887f21a07a2ac99862c7e
7
- data.tar.gz: c8be92592b3fe8e3c67439db72aa7f1717da3c13ed32b412009a65ebeb4d9bdbbb291c3391a2dda28e684cefb758b9be79960f64d0bc7183e1279deebfbf62bc
6
+ metadata.gz: 389670b5b1fdec89b2939207cb6eafdad8a6896249ae240a07eb615d5cf1f85cd8f2fa54ee94dbce4471b528b49c8476aaedeb9d073ef6bafa321f177e66d3f7
7
+ data.tar.gz: '0591296122cfac89e226522a675129ad04b554b6074a6224e2b3de95fdb7deed16569bd8283bb34c164558343a31d0fb29c6013069c2da06453000dc5041fef4'
@@ -3,7 +3,7 @@
3
3
  require 'tablomat/iptables/rule'
4
4
 
5
5
  module Tablomat
6
- class IPTables
6
+ class IPTablesBase
7
7
  # The IPTables class is the interface to the iptables command
8
8
  class Chain
9
9
  attr_accessor :owned
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tablomat
4
- class IPTables
4
+ class IPTablesBase
5
5
  # IPTables are made of Rules
6
6
  class Rule
7
7
  attr_accessor :owned, :active, :method, :position
@@ -3,7 +3,7 @@
3
3
  require 'tablomat/iptables/chain'
4
4
 
5
5
  module Tablomat
6
- class IPTables
6
+ class IPTablesBase
7
7
  # Puts the Table in IPTables
8
8
  class Table
9
9
  attr_accessor :owned
@@ -7,17 +7,13 @@ require 'tempfile'
7
7
  require 'tablomat/exec'
8
8
 
9
9
  module Tablomat
10
- # The IPTables interface
11
- class IPTables
10
+ # The base class for the IPTables interface
11
+ class IPTablesBase
12
12
  extend Exec
13
13
  attr_accessor :iptables_bin
14
14
  attr_reader :active, :builtin_chains, :tmp_chain
15
15
 
16
16
  def initialize
17
- # iptables must be in PATH
18
- @iptables_bin = 'iptables'
19
- @iptables_bin = "sudo #{@iptables_bin}" if Etc.getlogin != 'root'
20
-
21
17
  # get random value for rule creation hack, iptables limits name to 28 characters
22
18
  @tmp_chain_prefix = 'tablomat'
23
19
 
@@ -25,7 +21,6 @@ module Tablomat
25
21
  @tables = {}
26
22
 
27
23
  @active = true
28
- synchronize
29
24
  end
30
25
 
31
26
  def exec(cmd)
@@ -199,4 +194,26 @@ module Tablomat
199
194
  pp self
200
195
  end
201
196
  end
197
+
198
+ # The IPTables interface
199
+ class IPTables < IPTablesBase
200
+ def initialize
201
+ super()
202
+ # iptables must be in PATH
203
+ @iptables_bin = 'iptables'
204
+ @iptables_bin = "sudo #{@iptables_bin}" if Etc.getlogin != 'root'
205
+ synchronize
206
+ end
207
+ end
208
+
209
+ # The IP6Tables interface
210
+ class IP6Tables < IPTablesBase
211
+ def initialize
212
+ super()
213
+ # ip6tables must be in PATH
214
+ @iptables_bin = 'ip6tables'
215
+ @iptables_bin = "sudo #{@iptables_bin}" if Etc.getlogin != 'root'
216
+ synchronize
217
+ end
218
+ end
202
219
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tablomat
4
- VERSION = '1.2.1'
4
+ VERSION = '1.3.0'
5
5
  end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'etc'
5
+
6
+ describe Tablomat::IP6Tables do
7
+ describe 'Initialize module' do
8
+ @iptables = Tablomat::IP6Tables.new
9
+ end
10
+
11
+ before(:all) do
12
+ @command = 'ip6tables'
13
+ @command = "sudo #{@command}" if Etc.getlogin != 'root'
14
+
15
+ @iptables = Tablomat::IP6Tables.new
16
+ @iptables.activate
17
+ end
18
+
19
+ after(:all) do
20
+ # cleanup tables
21
+ @iptables.table('mangle').chain('test', &:apply_delete)
22
+ @iptables.table('mangle').chain('custom', &:apply_delete)
23
+ @iptables.table('filter').chain('rspec', &:apply_delete)
24
+ @iptables.table('nat').chain('rspec', &:apply_delete)
25
+ end
26
+
27
+ it 'does raise an error when getting an invalid ruleset' do
28
+ output = <<~IPTABLE
29
+ *nat
30
+ *mangle
31
+ :PREROUTING ACCEPT [57:17363]
32
+ :INPUT ACCEPT [57:17363]
33
+ :FORWARD ACCEPT [0:0]
34
+ :OUTPUT ACCEPT [60:16575]
35
+ :POSTROUTING ACCEPT [60:16575]
36
+ COMMIT
37
+ *filter
38
+ IPTABLE
39
+ expect { @iptables.parse_output(output) }.to raise_error(StandardError)
40
+ end
41
+
42
+ it 'converts rules to iptables-save format via tmp chain' do
43
+ expect(@iptables.normalize(protocol: :tcp, sport: 123, source: 'aaaa::ffff')).to eq('-s aaaa::ffff/128 -p tcp -m tcp --sport 123')
44
+ expect(@iptables.normalize('-s aaaa::ffff/128 -p tcp -m tcp --sport 123')).to eq('-s aaaa::ffff/128 -p tcp -m tcp --sport 123')
45
+ expect(@iptables.normalize('-s aaaa::ffff/128 -m tcp --sport 123 -p tcp')).to eq('-s aaaa::ffff/128 -p tcp -m tcp --sport 123')
46
+ end
47
+
48
+ it 'gets current ruleset' do
49
+ # remove all existing rules in mangle FORWARD (hurts running host rules during test)
50
+ `#{@command}-save > /tmp/iptables.save`
51
+ `#{@command} -t mangle -F FORWARD`
52
+ `#{@command} -t mangle -A FORWARD -p tcp -j ACCEPT`
53
+ @iptables.synchronize
54
+
55
+ `#{@command}-restore < /tmp/iptables.save`
56
+ # @iptables.print
57
+
58
+ # check that filter:INPUT is not owned!
59
+ expect(@iptables.table('filter').chain('INPUT').owned).to be_falsey
60
+
61
+ expect(@iptables.table('mangle').chain('FORWARD').rule(@iptables.normalize(protocol: :tcp, jump: :ACCEPT)).owned).to be_falsey
62
+ expect(@iptables.table('mangle').chain('FORWARD').rule(@iptables.normalize(protocol: :tcp, jump: :ACCEPT)).active).to be_truthy
63
+ end
64
+
65
+ it 'creates new chain in table mangle' do
66
+ @iptables.table('mangle').chain('custom').activate
67
+ expect(@iptables.table('mangle').chain('custom').active).to be_truthy
68
+ end
69
+
70
+ it 'allows to retrieve active rules' do
71
+ @iptables.append('nat', 'PREROUTING', source: 'cccc::ffff',
72
+ destination: 'cccc::fffe',
73
+ protocol: :tcp,
74
+ dport: 8080,
75
+ jump: 'DNAT', to: 'cccc::bbbb:9090')
76
+ target_hash = { source: 'cccc::ffff', destination: 'cccc::fffe', dport: '8080', jump: 'DNAT', protocol: 'tcp', to_dest: 'cccc::bbbb:9090' }
77
+ rules = @iptables.get_active_rules
78
+ expect(rules.length).to eq(1)
79
+ expect(rules.pop).to eq(target_hash)
80
+ @iptables.delete('nat', 'PREROUTING', source: 'cccc::ffff',
81
+ destination: 'cccc::fffe',
82
+ protocol: :tcp,
83
+ dport: 8080,
84
+ jump: 'DNAT', to: 'cccc::bbbb:9090')
85
+ end
86
+
87
+ it 'is not possible to set policy for non builtin chains' do
88
+ expect do
89
+ @iptables.table('mangle').chain('custom') do |chain|
90
+ chain.policy 'RETURN'
91
+ chain.apply
92
+ end
93
+ end.to raise_error('Unable to assign policy to non builtin chains, TODO: implement handling')
94
+ end
95
+
96
+ # it "can set policy for builtin chains" do
97
+ # @iptables.table("mangle").chain("INPUT") do | chain |
98
+ # chain.set_policy "ACCEPT"
99
+ # end
100
+ # @iptables.activate
101
+ # end
102
+
103
+ it 'can remove chains' do
104
+ # create chain
105
+ @iptables.table('mangle').chain('test').activate
106
+ expect(@iptables.exists('mangle', 'test')).to be_truthy
107
+ @iptables.table('mangle').chain('test').deactivate
108
+ expect(@iptables.exists('mangle', 'test')).to be_falsey
109
+ end
110
+
111
+ it 'checks if rule already exists' do
112
+ # check precondition
113
+ expect(@iptables.exists('mangle', 'custom', protocol: :tcp, sport: 123)).to be_falsy
114
+
115
+ # check existing
116
+ @iptables.append('mangle', 'custom', protocol: :tcp, sport: 123)
117
+ expect(@iptables.exists('mangle', 'custom', protocol: :tcp, sport: 123)).to be_truthy
118
+ expect(@iptables.table('mangle').chain('FORWARD').rule(@iptables.normalize(protocol: :tcp, jump: :ACCEPT)).active).to be_truthy
119
+
120
+ # check missing
121
+ @iptables.delete('mangle', 'custom', protocol: :tcp, sport: 123)
122
+ expect(@iptables.exists('mangle', 'custom', protocol: :tcp, sport: 123)).to be_falsy
123
+ end
124
+
125
+ it 'appends new rule via system' do
126
+ expect(@iptables.exists('mangle', 'custom', protocol: :tcp, sport: 123)).to be_falsy
127
+ @iptables.append('mangle', 'custom', protocol: :tcp, sport: 123)
128
+ expect(@iptables.table('mangle').chain('FORWARD').rule(@iptables.normalize(protocol: :tcp, jump: :ACCEPT)).owned).to be_falsey
129
+ expect(@iptables.table('mangle').chain('FORWARD').rule(@iptables.normalize(protocol: :tcp, jump: :ACCEPT)).active).to be_truthy
130
+ end
131
+
132
+ it 'can remove rules via system' do
133
+ expect(@iptables.exists('mangle', 'custom', protocol: :tcp, sport: 123)).to be_truthy
134
+ @iptables.delete('mangle', 'custom', protocol: :tcp, sport: 123)
135
+ expect(@iptables.exists('mangle', 'custom', protocol: :tcp, sport: 123)).to be_falsey
136
+ end
137
+
138
+ it 'changes all rules for a specific source address to another source address' do
139
+ @iptables.append('filter', 'rspec', protocol: :tcp, sport: 123, source: 'aaaa::ffff')
140
+ @iptables.switch_sources('aaaa::ffff', 'aaaa::bbbb')
141
+ expect(@iptables.exists('filter', 'rspec', source: 'aaaa::bbbb', protocol: :tcp, sport: 123)).to be_truthy
142
+ expect(@iptables.exists('filter', 'rspec', source: 'aaaa::ffff', protocol: :tcp, sport: 123)).to be_falsey
143
+ @iptables.append('nat', 'rspec', protocol: :tcp, sport: 123, source: 'aaaa::ffff', jump: :DNAT, 'to-destination': 'cccc::ffff:123')
144
+ @iptables.switch_sources('aaaa::ffff', 'aaaa::bbbb')
145
+ expect(@iptables.exists('nat', 'rspec', source: 'aaaa::bbbb', protocol: :tcp, sport: 123, jump: :DNAT, 'to-destination': 'cccc::ffff:123')).to be_truthy
146
+ expect(@iptables.exists('nat', 'rspec', source: 'aaaa::ffff', protocol: :tcp, sport: 123, jump: :DNAT, 'to-destination': 'cccc::ffff:123')).to be_falsey
147
+ end
148
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'etc'
5
+
6
+ describe Tablomat::IP6Tables, Tablomat::IPSet do
7
+ describe 'Initialize module' do
8
+ @ipset = Tablomat::IPSet.new
9
+ @iptables = Tablomat::IP6Tables.new
10
+ end
11
+
12
+ before(:all) do
13
+ @command = 'ipset'
14
+ @command = "sudo #{@command}" if Etc.getlogin != 'root'
15
+ @ipset = Tablomat::IPSet.new
16
+ @iptables = Tablomat::IP6Tables.new
17
+ @iptables.activate
18
+ end
19
+
20
+ after(:all) do
21
+ # cleanup table and ipsets
22
+ @iptables.table('mangle').chain('custom', &:apply_delete)
23
+ @ipset.destroy_all
24
+ end
25
+
26
+ it 'can create and delete rules using ipsets if set exists' do
27
+ # initialize ipset and create set
28
+ @ipset.create(set_name: 'testset1', type: 'hash:ip', create_options: 'family inet6')
29
+ # create rule
30
+ @iptables.append('mangle', 'custom', set: @ipset.matchset(set_name: 'testset1', flags: 'src'), protocol: :tcp, sport: 123)
31
+ expect(@iptables.exists('mangle', 'custom', set: @ipset.matchset(set_name: 'testset1', flags: 'src'), protocol: :tcp, sport: 123)).to be_truthy
32
+ @iptables.delete('mangle', 'custom', set: @ipset.matchset(set_name: 'testset1', flags: 'src'), protocol: :tcp, sport: 123)
33
+ expect(@iptables.exists('mangle', 'custom', set: @ipset.matchset(set_name: 'testset1', flags: 'src'), protocol: :tcp, sport: 123)).to be_falsey
34
+ end
35
+
36
+ it 'can create and delete rules using ipsets with Options' do
37
+ @iptables.append('mangle', 'custom', match: @ipset.matchset(set_name: 'testset1', flags: 'src', option: '--update-subcounters', negate_option: true, negate: true), protocol: :tcp, sport: 123)
38
+ expect(@iptables.exists('mangle', 'custom', match: @ipset.matchset(set_name: 'testset1', flags: 'src', option: '--update-subcounters', negate_option: true, negate: true), protocol: :tcp, sport: 123)).to be_truthy
39
+ @iptables.delete('mangle', 'custom', match: @ipset.matchset(set_name: 'testset1', flags: 'src', option: '--update-subcounters', negate_option: true, negate: true), protocol: :tcp, sport: 123)
40
+ expect(@iptables.exists('mangle', 'custom', match: @ipset.matchset(set_name: 'testset1', flags: 'src', option: '--update-subcounters', negate_option: true, negate: true), protocol: :tcp, sport: 123)).to be_falsey
41
+ end
42
+
43
+ it 'can not create rules using a set if the set does not exist' do
44
+ expect(@ipset.set('testset2').exists?).to be_falsey
45
+ expect { @iptables.append('mangle', 'custom', match: @ipset.matchset(set_name: 'testset', flags: 'src'), protocol: :tcp, sport: 123) }.to raise_error(RuntimeError)
46
+ end
47
+
48
+ it 'can not destroy a set if a rule is using it' do
49
+ @ipset.create(set_name: 'testset3', type: 'hash:ip', create_options: 'family inet6')
50
+ @iptables.append('mangle', 'custom', match: @ipset.matchset(set_name: 'testset3', flags: 'src'), protocol: :tcp, sport: 123)
51
+ expect(@iptables.exists('mangle', 'custom', match: @ipset.matchset(set_name: 'testset3', flags: 'src'), protocol: :tcp, sport: 123)).to be_truthy
52
+ expect { @ipset.destroy(set_name: 'testset3') }.to raise_error(Tablomat::IPSetError)
53
+ @iptables.delete('mangle', 'custom', match: @ipset.matchset(set_name: 'testset3', flags: 'src'), protocol: :tcp, sport: 123)
54
+ expect(@iptables.exists('mangle', 'custom', match: @ipset.matchset(set_name: 'testset3', flags: 'src'), protocol: :tcp, sport: 123)).to be_falsey
55
+ @ipset.destroy(set_name: 'testset3')
56
+ end
57
+
58
+ it 'lists the set in active rules, if a set is given' do
59
+ @ipset.create(set_name: 'testset4', type: 'hash:ip', create_options: 'family inet6')
60
+ @iptables.append('mangle', 'custom', match: @ipset.matchset(set_name: 'testset4', flags: 'src'), protocol: :tcp, sport: 123)
61
+ active_rules = @iptables.get_active_rules('mangle', 'custom')
62
+ expect(active_rules[-1][:match]).to eq('testset4')
63
+ @iptables.delete('mangle', 'custom', match: @ipset.matchset(set_name: 'testset4', flags: 'src'), protocol: :tcp, sport: 123)
64
+ @ipset.destroy(set_name: 'testset4')
65
+ end
66
+
67
+ it 'does not list a set in active rules, if no set is given' do
68
+ @ipset.create(set_name: 'testset5', type: 'hash:ip', create_options: 'family inet6')
69
+ @iptables.append('mangle', 'custom', source: 'aaaa::ffff', protocol: :tcp, sport: 123)
70
+ active_rules = @iptables.get_active_rules('mangle', 'custom')
71
+ expect(active_rules[-1].key?(:match)).to be_falsey
72
+ @iptables.delete('mangle', 'custom', source: 'aaaa::ffff', protocol: :tcp, sport: 123)
73
+ @ipset.destroy(set_name: 'testset5')
74
+ end
75
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'simplecov'
4
+ require 'simplecov-cobertura'
5
+
6
+ SimpleCov.formatters = [
7
+ SimpleCov::Formatter::CoberturaFormatter,
8
+ SimpleCov::Formatter::HTMLFormatter
9
+ ]
10
+
4
11
  SimpleCov.start do
5
12
  enable_coverage :branch
6
13
  add_filter '/spec/'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tablomat
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthias Wübbeling
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-03-12 00:00:00.000000000 Z
12
+ date: 2022-11-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -81,6 +81,26 @@ dependencies:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
83
  version: 0.18.5
84
+ - !ruby/object:Gem::Dependency
85
+ name: simplecov-cobertura
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '1.4'
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: 1.4.1
94
+ type: :development
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: '1.4'
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 1.4.1
84
104
  description: A simple wrapper for iptables
85
105
  email:
86
106
  - matthias.wuebbeling@cs.uni-bonn.de
@@ -99,7 +119,9 @@ files:
99
119
  - lib/tablomat/iptables/rule.rb
100
120
  - lib/tablomat/iptables/table.rb
101
121
  - lib/tablomat/version.rb
122
+ - spec/ip6tables_spec.rb
102
123
  - spec/ipset_spec.rb
124
+ - spec/ipsetip6tables_spec.rb
103
125
  - spec/ipsetiptables_spec.rb
104
126
  - spec/iptables_spec.rb
105
127
  - spec/spec_helper.rb
@@ -122,12 +144,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
144
  - !ruby/object:Gem::Version
123
145
  version: '0'
124
146
  requirements: []
125
- rubygems_version: 3.0.3
147
+ rubygems_version: 3.3.16
126
148
  signing_key:
127
149
  specification_version: 4
128
150
  summary: This wrapper provides an interface to iptables.
129
151
  test_files:
152
+ - spec/ip6tables_spec.rb
130
153
  - spec/ipset_spec.rb
154
+ - spec/ipsetip6tables_spec.rb
131
155
  - spec/ipsetiptables_spec.rb
132
156
  - spec/iptables_spec.rb
133
157
  - spec/spec_helper.rb