Class: Tablomat::IPTablesBase
- Inherits:
-
Object
- Object
- Tablomat::IPTablesBase
- 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
Defined Under Namespace
Instance Attribute Summary collapse
-
#active ⇒ Object
readonly
Returns the value of attribute active.
-
#builtin_chains ⇒ Object
readonly
Returns the value of attribute builtin_chains.
-
#iptables_bin ⇒ Object
Returns the value of attribute iptables_bin.
-
#tmp_chain ⇒ Object
readonly
Returns the value of attribute tmp_chain.
Instance Method Summary collapse
- #activate ⇒ Object
- #append(table_name, chain_name, data) ⇒ Object
- #deactivate ⇒ Object
- #delete(table_name, chain_name, data) ⇒ Object
- #exec(cmd) ⇒ Object
-
#exists(table_name, chain_name, data = nil) ⇒ Object
rubocop:disable Metrics/AbcSize.
-
#get_active_rules(table = 'nat', chain = 'PREROUTING') ⇒ Object
rubocop:disable Metrics/AbcSize.
-
#initialize ⇒ IPTablesBase
constructor
A new instance of IPTablesBase.
-
#insert(table_name, chain_name, data, pos) ⇒ Object
rubocop:enable Metrics/AbcSize.
-
#normalize(data, table = 'filter') ⇒ Object
converts any given rule to the iptables internal description format by using a temporary table rubocop:disable Metrics/AbcSize.
- #parse_chain(chain, rules) ⇒ Object
-
#parse_data(data) ⇒ Object
rubocop:enable Metrics/AbcSize.
-
#parse_output(stdout) ⇒ Object
rubocop:disable Metrics/AbcSize.
- #parse_table(table, chains) ⇒ Object
-
#print ⇒ Object
rubocop:enable Metrics/AbcSize.
-
#switch_sources(old_src, new_src) ⇒ Object
used to easily switch rules from one src ip to another.
- #synchronize ⇒ Object
- #table(name, owned = true, &block) ⇒ Object
Methods included from Exec
Constructor Details
#initialize ⇒ IPTablesBase
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
#active ⇒ Object (readonly)
Returns the value of attribute active.
14 15 16 |
# File 'lib/tablomat/iptables.rb', line 14 def active @active end |
#builtin_chains ⇒ Object (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_bin ⇒ Object
Returns the value of attribute iptables_bin.
13 14 15 |
# File 'lib/tablomat/iptables.rb', line 13 def iptables_bin @iptables_bin end |
#tmp_chain ⇒ Object (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
#activate ⇒ Object
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 |
#deactivate ⇒ Object
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 |
#print ⇒ Object
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 |
#synchronize ⇒ Object
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 |