ip-wrangler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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