ip-wrangler 0.1.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.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ name = 'ip-wrangler'
4
+ gem = Gem::Specification.find_by_name(name)
5
+ command = File.join(gem.full_gem_path, 'bin/ip-wrangler-stop.sh')
6
+ system(command, *ARGV)
@@ -0,0 +1,36 @@
1
+ #!/bin/bash
2
+
3
+ export __dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4
+ export __dir=$( dirname ${__dir} )
5
+
6
+ function usage() {
7
+ echo "Usage: $(basename $0) -P <pid_file|maybe:ip-wrangler.pid> -h (help and exit)"
8
+ }
9
+
10
+ while getopts 'P:h' __flag; do
11
+ case "${__flag}" in
12
+ P)
13
+ export __pid_file="$(realpath ${OPTARG})"
14
+ ;;
15
+ h)
16
+ usage
17
+ exit 0
18
+ ;;
19
+ *)
20
+ error "Unexpected option: ${__flag}"
21
+ usage
22
+ exit 1
23
+ ;;
24
+ esac
25
+ done
26
+
27
+ if [ -z "${__pid_file}" ]
28
+ then
29
+ echo "No PID file defined."
30
+ usage
31
+ exit 1
32
+ fi
33
+
34
+ pushd ${__dir}/lib/ 2>&1 >> /dev/null
35
+ thin -P ${__pid_file} stop
36
+ popd 2>&1 >> /dev/null
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ name = 'ip-wrangler'
4
+ gem = Gem::Specification.find_by_name(name)
5
+ command = File.join(gem.full_gem_path, 'bin/ip-wrangler-test.sh')
6
+ system(command, *ARGV)
@@ -0,0 +1,166 @@
1
+ #!/bin/bash
2
+
3
+ #set -x
4
+
5
+ export __IP="127.0.0.1"
6
+ export __PORT=8400
7
+
8
+ export __USER=username
9
+ export __PASS=password
10
+
11
+ if (( "$#" >= "1" )); then
12
+ export __IP=$1
13
+ fi
14
+
15
+ if (( "$#" >= "2" )); then
16
+ export __PORT=$2
17
+ fi
18
+
19
+ if (( "$#" >= "3" )); then
20
+ export __USER=$3
21
+ fi
22
+
23
+ if (( "$#" >= "4" )); then
24
+ export __PASS=$4
25
+ fi
26
+
27
+ echo "${__IP} ${__PORT} ${__USER} ${__PASS}"
28
+
29
+ read
30
+
31
+ for __ip in `seq 1 3`; do
32
+ for __port in `seq 1000 1010`; do
33
+ curl -u ${__USER}:${__PASS} -X POST --data "" http://${__IP}:${__PORT}/nat/port/127.0.0.${__ip}/${__ip}${__port}/tcp
34
+ echo ""
35
+ curl -u ${__USER}:${__PASS} -X POST --data "" http://${__IP}:${__PORT}/nat/port/127.0.0.${__ip}/${__ip}${__port}/udp
36
+ echo ""
37
+ done
38
+ done
39
+
40
+ read
41
+
42
+ for __ip in `seq 1 3`; do
43
+ for __port in `seq 1005 1015`; do
44
+ curl -u ${__USER}:${__PASS} -X POST --data "" http://${__IP}:${__PORT}/nat/port/127.0.0.${__ip}/${__ip}${__port}
45
+ echo ""
46
+ done
47
+ done
48
+
49
+ read
50
+
51
+ for __ip in `seq 1 3`; do
52
+ curl -u ${__USER}:${__PASS} -X POST --data "" http://${__IP}:${__PORT}/nat/ip/127.0.1.${__ip}
53
+ echo ""
54
+ done
55
+
56
+ read
57
+
58
+ curl -u ${__USER}:${__PASS} http://${__IP}:${__PORT}/nat/port
59
+ echo ""
60
+
61
+ for __ip in `seq 1 3`; do
62
+ curl -u ${__USER}:${__PASS} http://${__IP}:${__PORT}/nat/port/127.0.0.${__ip}
63
+ echo ""
64
+ done
65
+
66
+ curl -u ${__USER}:${__PASS} http://${__IP}:${__PORT}/nat/ip
67
+ echo ""
68
+
69
+ read
70
+
71
+ for __ip in `seq 1 3`; do
72
+ for __port in `seq 1000 1010`; do
73
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/nat/port/127.0.0.${__ip}/${__ip}${__port}/tcp
74
+ echo ""
75
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/nat/port/127.0.0.${__ip}/${__ip}${__port}/udp
76
+ echo ""
77
+ done
78
+ done
79
+
80
+ read
81
+
82
+ for __ip in `seq 1 3`; do
83
+ for __port in `seq 1005 1015`; do
84
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/nat/port/127.0.0.${__ip}/${__ip}${__port}
85
+ echo ""
86
+ done
87
+ done
88
+
89
+ read
90
+
91
+ for __ip in `seq 1 3`; do
92
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/nat/port/127.0.0.${__ip}
93
+ echo ""
94
+ done
95
+
96
+ for __id in `seq 1 2`; do
97
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/nat/ip/127.0.1.${__id}
98
+ echo ""
99
+ done
100
+
101
+ read
102
+
103
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/nat/ip/127.0.1.3
104
+ echo ""
105
+
106
+ read
107
+
108
+ curl -u ${__USER}:${__PASS} http://${__IP}:${__PORT}/
109
+ echo ""
110
+
111
+ read
112
+
113
+ for __ip in `seq 1 3`; do
114
+ for __port in `seq 1000 1010`; do
115
+ curl -u ${__USER}:${__PASS} -X POST --data "[{\"port\": ${__ip}${__port}, \"proto\": \"tcp\"}]" http://${__IP}:${__PORT}/dnat/127.0.0.${__ip}
116
+ echo ""
117
+ curl -u ${__USER}:${__PASS} -X POST --data "[{\"port\": ${__ip}${__port}, \"proto\": \"udp\"}]" http://${__IP}:${__PORT}/dnat/127.0.0.${__ip}
118
+ echo ""
119
+ done
120
+ done
121
+
122
+ read
123
+
124
+ for __ip in `seq 1 3`; do
125
+ for __port in `seq 1005 1015`; do
126
+ curl -u ${__USER}:${__PASS} -X POST --data "[{\"port\": ${__ip}${__port}, \"proto\": \"tcp\"}, {\"port\": ${__ip}${__port}, \"proto\": \"udp\"}]" http://${__IP}:${__PORT}/dnat/127.0.0.${__ip}
127
+ echo ""
128
+ done
129
+ done
130
+
131
+ read
132
+
133
+ curl -u ${__USER}:${__PASS} http://${__IP}:${__PORT}/dnat
134
+ echo ""
135
+
136
+ for __ip in `seq 1 3`; do
137
+ curl -u ${__USER}:${__PASS} http://${__IP}:${__PORT}/dnat/127.0.0.${__ip}
138
+ echo ""
139
+ done
140
+
141
+ read
142
+
143
+ for __ip in `seq 1 3`; do
144
+ for __port in `seq 1000 1010`; do
145
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/dnat/127.0.0.${__ip}/${__ip}${__port}/tcp
146
+ echo ""
147
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/dnat/127.0.0.${__ip}/${__ip}${__port}/udp
148
+ echo ""
149
+ done
150
+ done
151
+
152
+ read
153
+
154
+ for __ip in `seq 1 3`; do
155
+ for __port in `seq 1005 1015`; do
156
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/dnat/127.0.0.${__ip}/${__ip}${__port}
157
+ echo ""
158
+ done
159
+ done
160
+
161
+ read
162
+
163
+ for __ip in `seq 1 3`; do
164
+ curl -u ${__USER}:${__PASS} -X DELETE --data "" http://${__IP}:${__PORT}/nat/port/127.0.0.${__ip}
165
+ echo ""
166
+ done
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ip_wrangler/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'ip-wrangler'
8
+ spec.version = IpWrangler::VERSION
9
+ spec.authors = ['Paweł Suder', 'Jan Meizner', 'Bartosz Wilk']
10
+ spec.email = ['pawel@suder.info', 'j.meizner@cyfronet.pl', 'b.wilk@cyfronet.pl']
11
+ spec.description = %q{Iptables DNAT manager}
12
+ spec.summary = %q{Service is responsible for managing DNAT rules in iptables nat table}
13
+ spec.homepage = 'https://github.com/dice-cyfronet/ip-wrangler'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'sinatra', '~> 1.4'
22
+ spec.add_dependency 'thin', '~> 1.6'
23
+
24
+ spec.add_dependency 'sequel', '~> 4.19'
25
+ spec.add_dependency 'sqlite3', '~> 1.3'
26
+
27
+ spec.add_dependency 'json', '~> 1.8'
28
+
29
+ spec.add_development_dependency 'bundler', '~> 1.6'
30
+ spec.add_development_dependency 'rake', '~> 10.4'
31
+ end
data/lib/config.ru ADDED
@@ -0,0 +1,127 @@
1
+ require 'bundler'
2
+ require 'eventmachine'
3
+ require 'fileutils'
4
+ require 'json'
5
+ require 'logger'
6
+ require 'rubygems'
7
+ require 'sequel'
8
+ require 'sinatra'
9
+ require 'thin'
10
+ require 'yaml'
11
+
12
+ require './ip_wrangler/db'
13
+ require './ip_wrangler/ip'
14
+ require './ip_wrangler/iptables'
15
+ require './ip_wrangler/nat'
16
+ require './ip_wrangler/exec'
17
+
18
+ unless ENV.has_key?('__config_file')
19
+ puts 'No config file. Exiting.'
20
+ exit(1)
21
+ end
22
+
23
+ config_file = ENV['__config_file']
24
+
25
+ unless File.file?(config_file)
26
+ puts "#{config_file} not found. Exiting."
27
+ exit(2)
28
+ end
29
+
30
+ config = YAML.load_file(config_file)
31
+
32
+ unless ENV.has_key?('__no_log')
33
+ console_logger = File.new("#{config['log_dir']}/thin_output.log", 'a')
34
+
35
+ STDOUT.reopen(console_logger)
36
+ STDERR.reopen(console_logger)
37
+ end
38
+
39
+ puts "Checking if #{config['db_path']} is existing..."
40
+
41
+ unless File.file?(config['db_path'])
42
+ puts "Create #{config['db_path']}"
43
+ FileUtils.touch(config['db_path'])
44
+ end
45
+
46
+ puts 'Checking database...'
47
+
48
+ db = Sequel.connect("sqlite://#{config['db_path']}")
49
+
50
+ unless db.table_exists?(:nat_ports)
51
+ puts 'No nat_ports table. Creating it...'
52
+
53
+ db.create_table?(:nat_ports) do
54
+ primary_key :id
55
+ String :public_ip
56
+ Int :public_port
57
+ String :private_ip
58
+ Int :private_port
59
+ String :protocol
60
+ end
61
+
62
+ db.add_index(:nat_ports, [:public_ip, :public_port])
63
+
64
+ %w(tcp udp).each do |protocol|
65
+ ports = (config['port_start']..config['port_stop']).map { |port| [config['port_ip'], port, nil, nil, protocol] }.to_a
66
+ db[:nat_ports].import([:public_ip, :public_port, :private_ip, :private_port, :protocol], ports)
67
+ end
68
+
69
+ end
70
+
71
+ unless db.table_exists?(:nat_ips)
72
+ puts 'No nat_ips table. Creating it...'
73
+
74
+ db.create_table?(:nat_ips) do
75
+ primary_key :id
76
+ String :public_ip
77
+ String :private_ip
78
+ end
79
+
80
+ db.add_index(:nat_ips, :public_ip)
81
+
82
+ if config['ip'] != nil
83
+ config['ip'].each do |public_ip|
84
+ db[:nat_ips].insert(public_ip: public_ip, private_ip: nil)
85
+ puts "Add ip:#{public_ip}"
86
+ end
87
+ end
88
+ end
89
+
90
+ db.disconnect
91
+
92
+ puts "Creating chain #{config['iptables_chain_name']}_PRE in nat table..."
93
+ command_new_pre_nat_chain = IpWrangler::Command.new_chain("#{config['iptables_chain_name']}_PRE", 'nat')
94
+ IpWrangler::Exec.execute_iptables_command(command_new_pre_nat_chain)
95
+
96
+ puts "Creating chain #{config['iptables_chain_name']}_POST in nat table..."
97
+ command_new_post_nat_chain = IpWrangler::Command.new_chain("#{config['iptables_chain_name']}_POST", 'nat')
98
+ IpWrangler::Exec.execute_iptables_command(command_new_post_nat_chain)
99
+
100
+ puts 'Appending rule, if not exists, to PREROUTING chain...'
101
+ command_check_pre_nat_jump_rule = IpWrangler::Command.check_rule('PREROUTING', 'nat', [IpWrangler::Parameter.jump("#{config['iptables_chain_name']}_PRE")])
102
+ IpWrangler::Exec.execute_iptables_command(command_check_pre_nat_jump_rule)
103
+ if $?.exitstatus == 1
104
+ command_append_pre_nat_jump_rule = IpWrangler::Command.append_rule('PREROUTING', 'nat', [IpWrangler::Parameter.jump("#{config['iptables_chain_name']}_PRE")])
105
+ IpWrangler::Exec.execute_iptables_command(command_append_pre_nat_jump_rule)
106
+ end
107
+
108
+ puts 'Appending rule, if not exists, to POSTROUTING chain...'
109
+ command_check_post_nat_jump_rule = IpWrangler::Command.check_rule('POSTROUTING', 'nat', [IpWrangler::Parameter.jump("#{config['iptables_chain_name']}_POST")])
110
+ IpWrangler::Exec.execute_iptables_command(command_check_post_nat_jump_rule)
111
+ if $?.exitstatus == 1
112
+ command_append_post_nat_jump_rule = IpWrangler::Command.append_rule('POSTROUTING', 'nat', [IpWrangler::Parameter.jump("#{config['iptables_chain_name']}_POST")])
113
+ IpWrangler::Exec.execute_iptables_command(command_append_post_nat_jump_rule)
114
+ end
115
+
116
+ Bundler.require
117
+
118
+ require File.dirname(__FILE__) + '/ip_wrangler/main.rb'
119
+
120
+ set :environment, ENV['RACK_ENV'].to_sym
121
+ set :app_file, 'ip_wrangler/main.rb'
122
+ set :raise_errors, true
123
+
124
+ use Rack::MethodOverride
125
+ disable :run, :reload
126
+
127
+ run Sinatra::Application
@@ -0,0 +1,20 @@
1
+ log_dir: /tmp
2
+ db_path: /tmp/ipt.db
3
+
4
+ username: username
5
+ password: password
6
+
7
+ iptables_chain_name: IPT_WR
8
+
9
+ ext_ip: 10.0.0.1
10
+ port_ip: 10.0.0.1
11
+ port_start: 1024
12
+ port_stop: 2048
13
+
14
+ # Example of IP list
15
+ #ip:
16
+ # - 10.0.0.2
17
+ # - 10.0.0.3
18
+ # - 10.0.0.4
19
+
20
+ ip:
@@ -0,0 +1,99 @@
1
+ module IpWrangler
2
+ class DB
3
+ def initialize(db_name, logger)
4
+ @db = Sequel.connect('sqlite://' + db_name)
5
+ @logger = logger
6
+ end
7
+
8
+ def select_nat_port(private_ip = nil, private_port = nil, protocol = nil)
9
+ params = { private_ip: private_ip, private_port: private_port,
10
+ protocol: protocol }.select do |_, value|
11
+ !value.nil?
12
+ end
13
+ nat_ports = []
14
+ @db[:nat_ports].where(params).each do |nat_port|
15
+ if nat_port[:private_port] && nat_port[:private_port]
16
+ nat_ports.push(nat_port)
17
+ end
18
+ end
19
+ nat_ports
20
+ end
21
+
22
+ def select_nat_ip(private_ip = nil, public_ip = nil)
23
+ params = { private_ip: private_ip, public_ip: public_ip }.select do |_, value|
24
+ !value.nil?
25
+ end
26
+ nat_ips = []
27
+ @db[:nat_ips].where(params).each do |nat_ip|
28
+ if nat_ip[:private_ip]
29
+ nat_ips.push(nat_ip)
30
+ end
31
+ end
32
+ nat_ips
33
+ end
34
+
35
+ def insert_nat_port(public_ip, public_port, private_ip, private_port, protocol)
36
+ params = { public_ip: public_ip, public_port: public_port,
37
+ protocol: protocol }
38
+ data = { private_ip: private_ip, private_port: private_port }
39
+ @db[:nat_ports].where(params).update(data)
40
+ @logger.info("Insert nat ip port entry: #{public_ip}/#{public_port} -> "\
41
+ "#{private_ip}/#{private_port} (#{protocol})")
42
+ end
43
+
44
+ def insert_nat_ip(public_ip, private_ip)
45
+ params = { public_ip: public_ip }
46
+ data = { private_ip: private_ip }
47
+ @db[:nat_ips].where(params).update(data)
48
+ @logger.info("Insert nat ip entry: #{public_ip} -> #{private_ip}")
49
+ end
50
+
51
+ def delete_nat_port(private_ip, private_port = nil, protocol = nil)
52
+ params = { private_ip: private_ip, private_port: private_port,
53
+ protocol: protocol }.select do |_, value|
54
+ !value.nil?
55
+ end
56
+ data = { private_ip: nil, private_port: nil }
57
+ @db[:nat_ports].where(params).update(data)
58
+ @logger.info("Delete nat ip port entry: #{private_ip}/#{private_port} (#{protocol})")
59
+ end
60
+
61
+ def delete_nat_ip(private_ip, public_ip = nil)
62
+ params = { private_ip: private_ip,
63
+ public_ip: public_ip }.select do |_, value|
64
+ !value.nil?
65
+ end
66
+ data = { private_ip: nil }
67
+ @db[:nat_ips].where(params).update(data)
68
+ @logger.info("Delete nat ip entry: #{public_ip}")
69
+ end
70
+
71
+ def get_first_empty_nat_port(protocol)
72
+ params = { private_ip: nil, private_port: nil,
73
+ protocol: protocol }
74
+ empty_nat_ports = @db[:nat_ports].where(params)
75
+ unless empty_nat_ports.empty?
76
+ return empty_nat_ports.to_a[0]
77
+ end
78
+ nil
79
+ end
80
+
81
+ def get_first_empty_nat_ip
82
+ params = { private_ip: nil }
83
+ empty_nat_ips = @db[:nat_ips].where(params)
84
+ unless empty_nat_ips.empty?
85
+ return empty_nat_ips.to_a[0]
86
+ end
87
+ nil
88
+ end
89
+
90
+ def not_exists_nat_port?(public_ip, public_port, protocol, private_ip, private_port)
91
+ @db[:nat_ports].where(public_ip: public_ip, public_port: public_port,
92
+ private_ip: private_ip, private_port: private_port, protocol: protocol).empty?
93
+ end
94
+
95
+ def not_exists_nat_ip?(public_ip, private_ip)
96
+ @db[:nat_ips].where(public_ip: public_ip, private_ip: private_ip).empty?
97
+ end
98
+ end
99
+ end