catflap 0.0.1.pre

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.
Files changed (4) hide show
  1. data/bin/catflap +70 -0
  2. data/lib/catflap-http.rb +108 -0
  3. data/lib/catflap.rb +75 -0
  4. metadata +52 -0
data/bin/catflap ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'catflap'
5
+
6
+ options = {}
7
+ optparse = OptionParser.new do|opts|
8
+ opts.separator ""
9
+ opts.separator "Install/Configure/Service options:"
10
+ opts.on('-i', '--install', 'Install and initialize the catflap chain') do
11
+ options['install'] = true
12
+ end
13
+ opts.on('-u', '--uninstall', 'Uninstall catflap from iptables') do
14
+ options['uninstall'] = true
15
+ end
16
+ opts.on('-f', '--config-file <filepath>', String, 'Use config file to override default values') do |filepath|
17
+ options['config_file'] = true
18
+ end
19
+ opts.on('-s', '--start-server', 'Start the web api server daemon') do
20
+ options['start_server'] = true
21
+ end
22
+ opts.separator ""
23
+ opts.separator "Access rule control options:"
24
+ opts.on('-c', '--check <ipaddr>', String, 'Check if an IP address has access alreqdy') do |ip|
25
+ options['check'] = ip
26
+ end
27
+ opts.on('-a', '--add <ipaddr>', String, 'IP address to which access should be granted') do |ip|
28
+ options['add'] = ip
29
+ end
30
+ opts.on('-d', '--delete <ipaddr>', String, 'IP address or range to remove access previously granted') do |ip|
31
+ options['del'] = ip
32
+ end
33
+ opts.on('-F', '--file <filepath>', String, 'Input file of whitelisted IP addresses') do |filepath|
34
+ options['filepath'] = filepath
35
+ end
36
+ opts.on('-x', '--purge', 'Purge all catflap rules from iptables') do
37
+ options['purge'] = true
38
+ end
39
+ opts.on('-l', '--list', 'List catflap access rules') do
40
+ options['list'] = true
41
+ end
42
+ opts.separator ""
43
+ opts.separator "Output and process options:"
44
+ opts.on('-n', '--noop', 'Do not run the operation on iptables - no operation') do
45
+ cf.noop = true
46
+ end
47
+ opts.on('-p', '--print', 'Print the iptables generated to screen') do
48
+ cf.print = true
49
+ end
50
+ opts.on('-h', '--help', 'Print this help page.') do
51
+ puts opts
52
+ exit 0
53
+ end
54
+ end.parse! ARGV
55
+
56
+ unless options['start_server']
57
+ cf = Catflap.new(options['config_file'])
58
+ cf.purge_rules! if options['purge']
59
+ cf.install_rules! if options['install']
60
+ cf.uninstall_rules! if options['uninstall']
61
+ cf.add_address!(options['add']) if options['add']
62
+ cf.delete_address!(options['del']) if options['del']
63
+ cf.add_addresses_from_file!(options['filepath']) if options['filepath']
64
+ cf.check_address(options['check']) if options['check']
65
+ cf.list_rules if options['list']
66
+ else
67
+ require 'catflap-http'
68
+ CatflapWebserver::start_server(options['config_file'])
69
+ end
70
+
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'catflap'
4
+ require 'webrick'
5
+ include WEBrick
6
+
7
+ module CatflapWebserver
8
+
9
+ def self.generate_server(port)
10
+ config = {:Port => port}
11
+ server = HTTPServer.new(config)
12
+ yield server if block_given?
13
+ ['INT', 'TERM'].each {|signal|
14
+ trap(signal) {server.shutdown}
15
+ }
16
+ server.start
17
+ end
18
+
19
+ def self.start_server(port = 4777)
20
+ generate_server(port) do |server|
21
+ server.mount('/', Servlet)
22
+ end
23
+ end
24
+
25
+ class Servlet < HTTPServlet::AbstractServlet
26
+ def do_GET(req,resp)
27
+ # Split the path into piece
28
+ path = req.path[1..-1].split('/')
29
+ raise HTTPStatus::OK if path[0] == 'favicon.ico'
30
+ response_class = CatflapRestService.const_get("Service")
31
+
32
+ if response_class and response_class.is_a?(Class)
33
+ # There was a method given
34
+ if path[0]
35
+ response_method = path[0].to_sym
36
+ # Make sure the method exists in the class
37
+ raise HTTPStatus::NotFound if !response_class.respond_to?(response_method)
38
+
39
+ if path[0] == "enter"
40
+ url = response_class.send(response_method, req, resp)
41
+ resp.set_redirect(HTTPStatus::Redirect, url)
42
+ end
43
+
44
+ # Remaining path segments get passed in as arguments to the method
45
+ if path.length > 1
46
+ resp.body = response_class.send(response_method, req, resp, path[1..-1])
47
+ else
48
+ resp.body = response_class.send(response_method, req, resp)
49
+ end
50
+ raise HTTPStatus::OK
51
+
52
+ # No method was given, so check for an "index" method instead
53
+ else
54
+ raise HTTPStatus::NotFound if !response_class.respond_to?(:index)
55
+ resp.body = response_class.send(:index)
56
+ raise HTTPStatus::OK
57
+ end
58
+ else
59
+ raise HTTPStatus::NotFound
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ module CatflapRestService
66
+ class Service
67
+
68
+ @@cf = Catflap.new
69
+ @@cf.dports = '80,8080,443'
70
+
71
+ def self.index()
72
+ return "hello world"
73
+ end
74
+
75
+ def self.enter(req, resp)
76
+ ip = req.peeraddr.pop
77
+ host = req.addr[2]
78
+ @@cf.add_address!(ip) unless @@cf.check_address(ip)
79
+ return "http://" << host << ":80"
80
+ end
81
+
82
+ def self.add(req, resp, args)
83
+ ip = args[0]
84
+ unless @@cf.check_address(ip)
85
+ @@cf.add_address!(ip)
86
+ return "#{ip} has been granted access"
87
+ else
88
+ return "#{ip} already has access"
89
+ end
90
+ end
91
+
92
+ def self.remove(req, resp, args)
93
+ ip = args[0]
94
+ @@cf.delete_address!(ip)
95
+ return "Access granted to #{ip} has been removed"
96
+ end
97
+
98
+ def self.check(req, resp, args)
99
+ ip = args[0]
100
+
101
+ if @@cf.check_address(ip)
102
+ return "#{ip} has access to ports: #{@@cf.dports}"
103
+ else
104
+ return "#{ip} does not have access to ports: #{@@cf.dports}"
105
+ end
106
+ end
107
+ end
108
+ end
data/lib/catflap.rb ADDED
@@ -0,0 +1,75 @@
1
+ require 'yaml'
2
+
3
+ class Catflap
4
+
5
+ attr_accessor :chain, :dports, :print, :noop, :log_rejected
6
+
7
+ def initialize(config_file = nil)
8
+ @config = YAML.load_file(config_file) if config_file and File.exists?(config_file)
9
+ @chain = (@config['rules']['chain']) ? @config['rules']['chain'] : "catflap-accept"
10
+ @dports = (@config['rules']['dports']) ? @config['rules']['dports'] : "80,443"
11
+ @print = false
12
+ @noop = false
13
+ @log_rejected = true
14
+ end
15
+
16
+ def install_rules!
17
+ output = "iptables -N #{@chain}\n" # Create a new user-defined chain as a container for our catflap netfilter rules
18
+ output << "iptables -A #{@chain} -s 127.0.0.1 -p tcp -m multiport --dports #{@dports} -j ACCEPT\n" # Accept packets to localhost
19
+ output << "iptables -A INPUT -p tcp -m multiport --dports #{@dports} -j #{@chain}\n" # Jump from INPUT chain to the catflap chain
20
+ output << "iptables -A INPUT -p tcp -m multiport --dports #{@dports} -j LOG\n" if @log_rejected # Log any rejected packets to /var/log/messages
21
+ output << "iptables -A INPUT -p tcp -m multiport --dports #{@dports} -j DROP\n" # Drop any other packets to the ports on the INPUT chain
22
+ execute!(output)
23
+ end
24
+
25
+ def uninstall_rules!
26
+ output = "iptables -D INPUT -p tcp -m multiport --dports #{@dports} -j #{@chain}\n" # Remove user-defined chain from INPUT chain
27
+ output << "iptables -F #{@chain}\n" # Flush the catflap user-defined chain
28
+ output << "iptables -X #{@chain}\n" # Remove the catflap chain
29
+ output << "iptables -D INPUT -p tcp -m multiport --dports #{@dports} -j LOG\n" # Remove the logging rule
30
+ output << "iptables -D INPUT -p tcp -m multiport --dports #{@dports} -j DROP\n" # Remove the packet dropping rule
31
+ execute!(output)
32
+ end
33
+
34
+ def purge_rules!
35
+ output = "iptables -F #{@chain}"
36
+ execute!(output)
37
+ end
38
+
39
+ def list_rules
40
+ system "iptables -S #{@chain}"
41
+ end
42
+
43
+ def check_address(ip)
44
+ return system "iptables -C #{@chain} -s #{ip} -p tcp -m multiport --dports #{@dports} -j ACCEPT\n"
45
+ end
46
+
47
+ def add_address!(ip)
48
+ output = "iptables -I #{@chain} 1 -s #{ip} -p tcp -m multiport --dports #{@dports} -j ACCEPT\n"
49
+ execute!(output)
50
+ end
51
+
52
+ def delete_address!(ip)
53
+ output = "iptables -D #{@chain} -s #{ip} -p tcp -m multiport --dports #{@dports} -j ACCEPT\n"
54
+ execute!(output)
55
+ end
56
+
57
+ def add_addresses_from_file!(filepath)
58
+ if File.readable?(filepath)
59
+ output = ""
60
+ File.open(filepath, "r").each_line do |ip|
61
+ output << "iptables -I #{@chain} 1 -s #{ip.chomp} -p tcp -m multiport --dports #{@dports} -j ACCEPT\n"
62
+ end
63
+ execute!(output)
64
+ else
65
+ puts "The file #{filepath} is not readable!"
66
+ exit 1
67
+ end
68
+ end
69
+
70
+ def execute!(output)
71
+ if @print then puts output end
72
+ system output unless @noop
73
+ end
74
+
75
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: catflap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Nyk Cowham
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-01 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: A simple solution for service access (e.g. port 80 on webserver) where
15
+ a more robust and secure VPN solution is not available
16
+ email: nyk@demotix.com
17
+ executables:
18
+ - catflap
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - lib/catflap.rb
23
+ - lib/catflap-http.rb
24
+ - bin/catflap
25
+ homepage: http://rubygems.org/gems/catflap
26
+ licenses:
27
+ - MIT
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>'
42
+ - !ruby/object:Gem::Version
43
+ version: 1.3.1
44
+ requirements:
45
+ - NetFilters (iptables) installed and working
46
+ rubyforge_project:
47
+ rubygems_version: 1.8.11
48
+ signing_key:
49
+ specification_version: 3
50
+ summary: Manage NetFilter-based rules to grant port access on demand via commandline
51
+ or REST API requests
52
+ test_files: []