Class: Tablomat::IPTablesBase

Inherits:
Object
  • Object
show all
Extended by:
Exec
Defined in:
lib/tablomat/iptables.rb,
lib/tablomat/iptables/rule.rb,
lib/tablomat/iptables/chain.rb,
lib/tablomat/iptables/table.rb

Overview

The base class for the IPTables interface

Direct Known Subclasses

IP6Tables, IPTables

Defined Under Namespace

Classes: Chain, Rule, Table

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Exec

exec

Constructor Details

#initializeIPTablesBase

Returns a new instance of IPTablesBase.



16
17
18
19
20
21
22
23
24
# File 'lib/tablomat/iptables.rb', line 16

def initialize
  # get random value for rule creation hack, iptables limits name to 28 characters
  @tmp_chain_prefix = 'tablomat'

  @builtin_chains = { filter: %w[INPUT FORWARD OUTPUT], nat: %w[PREROUTING INPUT OUTPUT POSTROUTING], mangle: %w[PREROUTING INPUT FORWARD OUTPUT POSTROUTING] }
  @tables = {}

  @active = true
end

Instance Attribute Details

#activeObject (readonly)

Returns the value of attribute active.



14
15
16
# File 'lib/tablomat/iptables.rb', line 14

def active
  @active
end

#builtin_chainsObject (readonly)

Returns the value of attribute builtin_chains.



14
15
16
# File 'lib/tablomat/iptables.rb', line 14

def builtin_chains
  @builtin_chains
end

#iptables_binObject

Returns the value of attribute iptables_bin.



13
14
15
# File 'lib/tablomat/iptables.rb', line 13

def iptables_bin
  @iptables_bin
end

#tmp_chainObject (readonly)

Returns the value of attribute tmp_chain.



14
15
16
# File 'lib/tablomat/iptables.rb', line 14

def tmp_chain
  @tmp_chain
end

Instance Method Details

#activateObject



141
142
143
144
145
146
# File 'lib/tablomat/iptables.rb', line 141

def activate
  @active = true
  @tables.each do |_name, table|
    table.activate
  end
end

#append(table_name, chain_name, data) ⇒ Object



131
132
133
134
# File 'lib/tablomat/iptables.rb', line 131

def append(table_name, chain_name, data)
  data = normalize data, table_name
  table(table_name).append(chain_name, data)
end

#deactivateObject



148
149
150
151
152
153
# File 'lib/tablomat/iptables.rb', line 148

def deactivate
  @active = false
  tables.each do |_name, table|
    table.deactivate
  end
end

#delete(table_name, chain_name, data) ⇒ Object



136
137
138
139
# File 'lib/tablomat/iptables.rb', line 136

def delete(table_name, chain_name, data)
  data = normalize data, table_name
  table(table_name).delete(chain_name, data)
end

#exec(cmd) ⇒ Object



26
27
28
# File 'lib/tablomat/iptables.rb', line 26

def exec(cmd)
  Exec.exec(cmd)
end

#exists(table_name, chain_name, data = nil) ⇒ Object

rubocop:disable Metrics/AbcSize



116
117
118
119
120
121
122
123
# File 'lib/tablomat/iptables.rb', line 116

def exists(table_name, chain_name, data = nil)
  if data.nil?
    table(table_name).chain_exists(chain_name) && table(table_name).chain(chain_name).active
  else
    data = normalize data, table_name
    table(table_name).chain(chain_name).rules.count { |_k, v| normalize(v.description, table_name) == data && v.active } >= 1
  end
end

#get_active_rules(table = 'nat', chain = 'PREROUTING') ⇒ Object

rubocop:disable Metrics/AbcSize



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/tablomat/iptables.rb', line 156

def get_active_rules(table = 'nat', chain = 'PREROUTING')
  rrgx = /(?<key>--?[[:alpha:]-]+) (?<value>[[:alnum:]:\.]+)/.freeze
  switch_map = { '-s' => :source, '--source' => :source,
                 '-d' => :destination, '--destination' => :destination,
                 '--dport' => :dport, '--to-destination' => :to_dest,
                 '-p' => :protocol, '--protocol' => :protocol,
                 '-j' => :jump, '--jump' => :jump,
                 '--match-set' => :match }

  table(table).chain(chain).rules.filter { |_, r| r.active }.map do |_, rule|
    rule.description.to_enum(:scan, rrgx)
        .map { Regexp.last_match }
        .map { |m| [switch_map[m[:key]], m[:value]] }
        .filter { |m| m[0] }.to_h
  end
end

#insert(table_name, chain_name, data, pos) ⇒ Object

rubocop:enable Metrics/AbcSize



126
127
128
129
# File 'lib/tablomat/iptables.rb', line 126

def insert(table_name, chain_name, data, pos)
  data = normalize data, table_name
  table(table_name).insert(chain_name, data, pos)
end

#normalize(data, table = 'filter') ⇒ Object

converts any given rule to the iptables internal description format by using a temporary table rubocop:disable Metrics/AbcSize



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/tablomat/iptables.rb', line 98

def normalize(data, table = 'filter')
  synchronize
  tmp_chain = "#{@tmp_chain_prefix}#{Digest::SHA256.hexdigest Random.rand(1024).to_s}"[0, 28]
  self.table(table).chain(tmp_chain).activate
  self.table(table).append(tmp_chain, data)
  synchronize
  normalized = data
  self.table(table).chain(tmp_chain).rules.select { |_k, r| r.owned == false }.each do |_k, r|
    normalized = r.description
  end
  self.table(table).delete(tmp_chain, data)
  self.table(table).chain(tmp_chain).deactivate
  self.table(table).chain(tmp_chain).apply_delete
  normalized
end

#parse_chain(chain, rules) ⇒ Object



81
82
83
84
85
86
# File 'lib/tablomat/iptables.rb', line 81

def parse_chain(chain, rules)
  rules.each do |rule|
    r = chain.rule(rule, false)
    r.activate true
  end
end

#parse_data(data) ⇒ Object

rubocop:enable Metrics/AbcSize



65
66
67
68
69
70
71
# File 'lib/tablomat/iptables.rb', line 65

def parse_data(data)
  data.each do |table, chains|
    t = self.table(table, false)
    t.activate true
    parse_table(t, chains)
  end
end

#parse_output(stdout) ⇒ Object

rubocop:disable Metrics/AbcSize



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/tablomat/iptables.rb', line 44

def parse_output(stdout)
  stdout = stdout.split("\n").reject { |s| s[0] == '#' }.join("\n")
  data = {}
  stdout.split(/^\*/).reject { |s| s == '' }.each do |ruleset|
    table, rule = ruleset.match(/(.+?)\n(.*)/m).to_a[1..-1]
    data[table] = {}

    raise "Empty ruleset in table #{table}" if table.nil?

    rule.scan(/^:(.+?)\s+/).each do |match|
      data[table][match[0]] = []
    end

    rule.scan(/^-A (.+?) (.+?)\n/).each do |match|
      data[table][match[0]] << match[1]
    end
  end
  parse_data(data)
end

#parse_table(table, chains) ⇒ Object



73
74
75
76
77
78
79
# File 'lib/tablomat/iptables.rb', line 73

def parse_table(table, chains)
  chains.each do |chain, rules|
    c = table.chain(chain, false)
    c.activate true
    parse_chain(c, rules)
  end
end

rubocop:enable Metrics/AbcSize



192
193
194
195
# File 'lib/tablomat/iptables.rb', line 192

def print
  require 'pp'
  pp self
end

#switch_sources(old_src, new_src) ⇒ Object

used to easily switch rules from one src ip to another



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/tablomat/iptables.rb', line 174

def switch_sources(old_src, new_src)
  @tables.to_a.each do |_kt, tbl|
    tbl.chains.to_a.each do |_kc, chn|
      next if chn.name.include? 'tablomat'

      chn.rules.to_a.each do |_key, rule|
        next unless rule.description.include? "-s #{old_src}"

        new_data = normalize(rule.description, tbl.name).sub "-s #{old_src}", "-s #{new_src}"
        pos = rule.position + 1
        delete(tbl.name, chn.name, rule.description)
        insert(tbl.name, chn.name, new_data, pos)
      end
    end
  end
end

#synchronizeObject



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/tablomat/iptables.rb', line 30

def synchronize
  # called regularly by normalize
  command = "#{@iptables_bin}-save"
  stdout = `#{command} 2>&1`.strip << "\n"
  if $CHILD_STATUS != 0
    # throw error
    puts "Invalid return value when calling #{command}"
  end
  parse_output stdout
rescue StandardError => e
  puts "[error] #{e}"
end

#table(name, owned = true, &block) ⇒ Object



88
89
90
91
92
93
94
# File 'lib/tablomat/iptables.rb', line 88

def table(name, owned = true, &block)
  name = name.to_s.downcase
  (@tables[name] || Table.new(self, name, owned)).tap do |table|
    @tables[name] = table
    block&.call(table)
  end
end