ipaccess 0.0.4 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +0 -0
- data/.gemtest +0 -0
- data/.rspec +1 -0
- data/.yardopts +12 -0
- data/ChangeLog +1495 -0
- data/{docs/LGPL-LICENSE → LGPL-LICENSE} +0 -0
- data/Manifest.txt +76 -0
- data/README.md +96 -0
- data/Rakefile +65 -42
- data/docs/COPYING +41 -45
- data/docs/FAQ +12 -0
- data/docs/HISTORY +17 -0
- data/docs/LEGAL +1 -1
- data/docs/LGPL +166 -0
- data/docs/TODO +150 -7
- data/docs/images/ipaccess.png +0 -0
- data/docs/images/ipaccess_ac_for_args.png +0 -0
- data/docs/images/ipaccess_ac_for_socket.png +0 -0
- data/docs/images/ipaccess_logo.png +0 -0
- data/docs/images/ipaccess_relations.png +0 -0
- data/docs/images/ipaccess_setup_origin.png +0 -0
- data/docs/images/ipaccess_setup_origin_tab.png +0 -0
- data/docs/images/ipaccess_view.png +0 -0
- data/docs/rdoc.css +22 -0
- data/examples/ftp.rb +62 -0
- data/examples/http.rb +81 -0
- data/examples/imap.rb +37 -0
- data/examples/pop.rb +31 -0
- data/examples/smtp.rb +26 -0
- data/examples/tcp_server.rb +32 -0
- data/examples/tcp_socket.rb +7 -3
- data/examples/telnet.rb +32 -0
- data/examples/text_message.rb +45 -0
- data/lib/ipaccess.rb +557 -7
- data/lib/ipaccess/arm_sockets.rb +7 -4
- data/lib/ipaccess/ghost_doc/ghost_doc.rb +23 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_acl.rb +54 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_net_ftp.rb +213 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_net_http.rb +272 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_net_smtp.rb +186 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_net_telnet.rb +227 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_blacklist.rb +36 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_blacklist_e.rb +7 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_unblacklist.rb +36 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_unblacklist_e.rb +7 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_unwhitelist.rb +36 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_unwhitelist_e.rb +7 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_whitelist.rb +36 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_p_whitelist_e.rb +7 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_patched_usage.rb +64 -0
- data/lib/ipaccess/ghost_doc/ghost_doc_sockets.rb +571 -0
- data/lib/ipaccess/ip_access_check.rb +508 -0
- data/lib/ipaccess/ip_access_errors.rb +186 -40
- data/lib/ipaccess/ip_access_list.rb +955 -1122
- data/lib/ipaccess/ip_access_set.rb +212 -0
- data/lib/ipaccess/net/ftp.rb +39 -0
- data/lib/ipaccess/net/http.rb +39 -0
- data/lib/ipaccess/net/https.rb +30 -0
- data/lib/ipaccess/net/imap.rb +39 -0
- data/lib/ipaccess/net/pop.rb +46 -0
- data/lib/ipaccess/net/smtp.rb +39 -0
- data/lib/ipaccess/net/telnet.rb +38 -0
- data/lib/ipaccess/patches/generic.rb +807 -0
- data/lib/ipaccess/patches/net_ftp.rb +165 -0
- data/lib/ipaccess/patches/net_http.rb +175 -0
- data/lib/ipaccess/patches/net_https.rb +29 -0
- data/lib/ipaccess/patches/net_imap.rb +117 -0
- data/lib/ipaccess/patches/net_pop.rb +171 -0
- data/lib/ipaccess/patches/net_smtp.rb +130 -0
- data/lib/ipaccess/patches/net_telnet.rb +103 -0
- data/lib/ipaccess/{netaddr_patch.rb → patches/netaddr.rb} +20 -11
- data/lib/ipaccess/patches/sockets.rb +586 -0
- data/lib/ipaccess/socket.rb +52 -1
- data/lib/ipaccess/sockets.rb +4 -30
- data/spec/ip_access_list_spec.rb +33 -21
- data/spec/spec.opts +2 -2
- metadata +289 -63
- metadata.gz.sig +0 -0
- data/docs/DOWNLOAD +0 -17
- data/docs/README +0 -95
- data/docs/WELCOME +0 -8
- data/lib/ipaccess/ghost_doc.rb +0 -206
- data/lib/ipaccess/ghost_doc_acl.rb +0 -31
- data/lib/ipaccess/ip_access.rb +0 -456
- data/lib/ipaccess/ip_access_patches.rb +0 -431
- data/spec/core_spec.rb +0 -5
data/examples/imap.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require 'ipaccess/net/imap'
|
4
|
+
|
5
|
+
# Add host's IP by to black list of global output access set
|
6
|
+
IPAccess::Set::Global.output.blacklist 'randomseed.pl'
|
7
|
+
|
8
|
+
# Create custom access set with one blacklisted IP
|
9
|
+
acl = IPAccess::Set.new
|
10
|
+
acl.output.blacklist 'randomseed.pl'
|
11
|
+
acl.output.blacklist 'imap.heise.de'
|
12
|
+
|
13
|
+
#===== Example cases
|
14
|
+
|
15
|
+
# 1
|
16
|
+
|
17
|
+
imap = IPAccess::Net::IMAP.new('randomseed.pl')
|
18
|
+
imap.authenticate('LOGIN', 'joe_user', 'joes_password')
|
19
|
+
imap.examine('INBOX')
|
20
|
+
imap.search(["RECENT"]).each do |message_id|
|
21
|
+
envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
|
22
|
+
puts "#{envelope.from[0].name}: \t#{envelope.subject}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# 2
|
26
|
+
|
27
|
+
imap = Net::IMAP.new('imap.heise.de', 'imaps', true, nil, false)
|
28
|
+
|
29
|
+
IPAccess.arm imap, acl
|
30
|
+
|
31
|
+
imap.authenticate('LOGIN', 'joe_user', 'joes_password')
|
32
|
+
imap.examine('INBOX')
|
33
|
+
imap.search(["RECENT"]).each do |message_id|
|
34
|
+
envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"]
|
35
|
+
puts "#{envelope.from[0].name}: \t#{envelope.subject}"
|
36
|
+
end
|
37
|
+
|
data/examples/pop.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require 'ipaccess/net/pop'
|
4
|
+
|
5
|
+
# Add host's IP by to black list of global output access set
|
6
|
+
IPAccess::Set::Global.output.blacklist 'randomseed.pl'
|
7
|
+
|
8
|
+
# Create custom access set with one blacklisted IP
|
9
|
+
acl = IPAccess::Set.new
|
10
|
+
acl.output.blacklist 'randomseed.pl'
|
11
|
+
acl.output.blacklist 'gmail.com'
|
12
|
+
|
13
|
+
#===== Example cases
|
14
|
+
|
15
|
+
# 1
|
16
|
+
|
17
|
+
i = 0
|
18
|
+
IPAccess::Net::APOP.delete_all('randomseed.pl', 110,
|
19
|
+
'YourAccount', 'YourPassword') do |m|
|
20
|
+
File.open("inbox/#{i}", 'w') do |f|
|
21
|
+
f.write m.pop
|
22
|
+
end
|
23
|
+
i += 1
|
24
|
+
end
|
25
|
+
|
26
|
+
# 2
|
27
|
+
|
28
|
+
p = Net::POP3.new 'randomseed.pl'
|
29
|
+
IPAccess.arm p, acl
|
30
|
+
p.auth_only 'user', 'pass'
|
31
|
+
|
data/examples/smtp.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require 'ipaccess/net/smtp'
|
4
|
+
|
5
|
+
# Add host's IP by to black list of global output access set
|
6
|
+
IPAccess::Set::Global.output.blacklist 'randomseed.pl'
|
7
|
+
|
8
|
+
# Create custom access set with one blacklisted IP
|
9
|
+
acl = IPAccess::Set.new
|
10
|
+
acl.output.blacklist 'randomseed.pl'
|
11
|
+
acl.output.blacklist 'gmail.com'
|
12
|
+
|
13
|
+
#===== Example cases
|
14
|
+
|
15
|
+
# 1
|
16
|
+
|
17
|
+
IPAccess::Net::SMTP.start('randomseed.pl', 25) do |smtp|
|
18
|
+
;
|
19
|
+
end
|
20
|
+
|
21
|
+
# 2
|
22
|
+
|
23
|
+
p = Net::SMTP.new 'randomseed.pl'
|
24
|
+
IPAccess.arm p, acl
|
25
|
+
p.start 'user', 'pass'
|
26
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require 'ipaccess/socket'
|
4
|
+
|
5
|
+
acl = IPAccess::Set.new
|
6
|
+
acl.input.blacklist :strange
|
7
|
+
|
8
|
+
# 0
|
9
|
+
|
10
|
+
serv = IPAccess::TCPServer.new(31337) # create listening TCP socket
|
11
|
+
serv.acl = :private # create and use private access set
|
12
|
+
serv.blacklist :local, :private # block local and private IP addresses
|
13
|
+
serv.permit '127.0.0.5' # make an exception
|
14
|
+
|
15
|
+
puts serv.acl.show # show listed IP addresses
|
16
|
+
sock = serv.sysaccept # accept connection
|
17
|
+
|
18
|
+
|
19
|
+
# 1
|
20
|
+
|
21
|
+
# Create and use the TCP socket
|
22
|
+
|
23
|
+
s = IPAccess::TCPServer.new(31337, acl)
|
24
|
+
#s = TCPServer.new(31337)
|
25
|
+
|
26
|
+
# Arm object s
|
27
|
+
#IPAccess.arm s, acl
|
28
|
+
|
29
|
+
puts s.acl.show
|
30
|
+
puts "\nissue in terminal: telnet 127.0.0.1 31337\n\n"
|
31
|
+
g = s.accept
|
32
|
+
|
data/examples/tcp_socket.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
2
|
|
3
|
-
require 'ipaccess'
|
3
|
+
require 'ipaccess/socket'
|
4
4
|
|
5
|
-
|
6
|
-
IPAccess.
|
5
|
+
# Add host's IP by to black list of global output access set
|
6
|
+
IPAccess::Set::Global.output.blacklist '78.0.0.0/8'
|
7
|
+
|
8
|
+
# Create and use the TCP socket
|
7
9
|
s = TCPSocket.new('randomseed.pl', 80)
|
8
10
|
|
11
|
+
# Arm object s
|
12
|
+
IPAccess.arm s
|
9
13
|
|
data/examples/telnet.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require 'ipaccess/net/telnet' # load Net::Telnet version and IPAccess.arm method
|
4
|
+
|
5
|
+
opts = {}
|
6
|
+
opts["Host"] = 'randomseed.pl'
|
7
|
+
opts["Port"] = '80'
|
8
|
+
|
9
|
+
t = Net::Telnet.new(opts) # try to connect to remote host
|
10
|
+
|
11
|
+
begin
|
12
|
+
|
13
|
+
IPAccess.arm t # arm single Telnet object (will use global access set)
|
14
|
+
t.blacklist! 'randomseed.pl' # blacklist host while being connected
|
15
|
+
|
16
|
+
rescue IPAccessDenied => e
|
17
|
+
|
18
|
+
puts "Message:\t#{e.message}"
|
19
|
+
puts
|
20
|
+
puts "ACL:\t\t#{e.acl}"
|
21
|
+
puts "Exception:\t#{e.inspect}"
|
22
|
+
puts "Remote IP:\t#{e.peer_ip}"
|
23
|
+
puts "Rule:\t\t#{e.rule}"
|
24
|
+
puts "Originator:\t#{e.originator}"
|
25
|
+
puts "CIDR's Origin:\t#{e.peer_ip.tag[:Originator]}\n\n"
|
26
|
+
puts "Session closed:\t#{t.closed?}"
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
p t
|
31
|
+
p "end"
|
32
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require 'ipaccess/socket'
|
4
|
+
|
5
|
+
begin
|
6
|
+
|
7
|
+
# add some rules
|
8
|
+
acl = IPAccess::Set.new
|
9
|
+
acl.input.blacklist_reasonable "Local and private addresses aren't welcome here", :local, :private
|
10
|
+
acl.input.whitelist_reasonable "I like that system, it makes toasts", '172.16.0.10', '1234:1234:1234:1234:1234:1234:1234:1234'
|
11
|
+
|
12
|
+
# show the lists with reasons for entries
|
13
|
+
puts acl.show(true)
|
14
|
+
|
15
|
+
# create a server
|
16
|
+
s = IPAccess::TCPServer.new(31337, acl)
|
17
|
+
|
18
|
+
# let it stays opened on access denied event
|
19
|
+
s.opened_on_deny = true
|
20
|
+
|
21
|
+
# listen for a connection
|
22
|
+
puts "\nnow use terminal and issue: telnet 127.0.0.1 31337\n\n"
|
23
|
+
n = s.accept
|
24
|
+
|
25
|
+
rescue IPAccessDenied => e
|
26
|
+
|
27
|
+
# show some stuff carried along with an exception
|
28
|
+
puts "Message: #{e.message}"
|
29
|
+
puts
|
30
|
+
puts "Exception: #{e.inspect}"
|
31
|
+
puts "Remote IP: #{e.peer_ip} (#{e.peer_ip_short})"
|
32
|
+
puts "Rule: #{e.rule} (#{e.rule_short})"
|
33
|
+
puts "Reason: #{e.reason}"
|
34
|
+
puts "Originator: #{e.originator}"
|
35
|
+
puts "Internal Originator in CIDR: #{e.peer_ip.tag[:Originator]}"
|
36
|
+
puts "ACL: #{e.acl}"
|
37
|
+
|
38
|
+
# send a reason to our peer before closing connection
|
39
|
+
unless e.originator.closed?
|
40
|
+
e.originator.write(e.reason + "\n\r")
|
41
|
+
e.originator.close
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
data/lib/ipaccess.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
#
|
3
3
|
# Author:: Paweł Wilk (mailto:pw@gnu.org)
|
4
|
-
# Copyright:: Copyright (c) 2009 Paweł Wilk
|
5
|
-
# License:: This program is licensed under the terms of {GNU Lesser General Public License}[link:docs/LGPL
|
4
|
+
# Copyright:: Copyright (c) 2009-2014 by Paweł Wilk
|
5
|
+
# License:: This program is licensed under the terms of {GNU Lesser General Public License}[link:docs/LGPL.html] or {Ruby License}[link:docs/COPYING.html].
|
6
6
|
#
|
7
7
|
# Classes contained in this library allow you to create
|
8
8
|
# and manage IP access lists in an easy way. You may use
|
9
|
-
# IPAccess class to maintain inpu/output traffic control.
|
10
|
-
# You also may use
|
9
|
+
# IPAccess::Set class to maintain inpu/output traffic control.
|
10
|
+
# You also may use IPAccess::List class directly to build
|
11
11
|
# your own access sets based on black lists and white lists.
|
12
12
|
#
|
13
13
|
#--
|
@@ -28,8 +28,558 @@ require 'socket'
|
|
28
28
|
require 'resolv'
|
29
29
|
require 'netaddr'
|
30
30
|
|
31
|
-
require 'ipaccess/
|
31
|
+
require 'ipaccess/patches/netaddr'
|
32
32
|
require 'ipaccess/ip_access_list'
|
33
|
-
require 'ipaccess/
|
34
|
-
require 'ipaccess/sockets'
|
33
|
+
require 'ipaccess/ip_access_set'
|
35
34
|
|
35
|
+
# This module contains classes that are
|
36
|
+
# used to control IP access. There are
|
37
|
+
# three major components you may need:
|
38
|
+
#
|
39
|
+
# === IPAccess::List class
|
40
|
+
#
|
41
|
+
# This class lets you create IP
|
42
|
+
# access list with blacklisted
|
43
|
+
# and whitelisted elements. It
|
44
|
+
# also has methods for checking
|
45
|
+
# whether given IP matches the
|
46
|
+
# list.
|
47
|
+
#
|
48
|
+
# === IPAccess::Set class
|
49
|
+
#
|
50
|
+
# This class contains two
|
51
|
+
# objects that are instances
|
52
|
+
# of IPAccess::List class.
|
53
|
+
# It allows you to create so
|
54
|
+
# called access set. The access
|
55
|
+
# set contains members named
|
56
|
+
# +input+ and +output+. All methods
|
57
|
+
# that validate IP access do it
|
58
|
+
# against one of the lists. Input
|
59
|
+
# access list is for incomming
|
60
|
+
# and output for outgoing IP traffic.
|
61
|
+
# In case of connection-oriented
|
62
|
+
# sockets and other network objects
|
63
|
+
# the convention is to use output access
|
64
|
+
# list to validate connections that
|
65
|
+
# we initiate. The incomming traffic
|
66
|
+
# in that model means the connections
|
67
|
+
# initiated by a remote peer.
|
68
|
+
#
|
69
|
+
# === Patching engine
|
70
|
+
#
|
71
|
+
# IPAccess was initialy considered as a
|
72
|
+
# set of classes that you may use
|
73
|
+
# in your own programs to control
|
74
|
+
# IP access. That means your own classes
|
75
|
+
# used for communication should use
|
76
|
+
# access lists or sets before making any
|
77
|
+
# real connections or sending any datagrams.
|
78
|
+
#
|
79
|
+
# Fortunately there are many network classes,
|
80
|
+
# including sockets, that Ruby ships with.
|
81
|
+
# It would be waste of resources to not modify
|
82
|
+
# them to support IP access control and automagically
|
83
|
+
# throw exceptions when access should be denied.
|
84
|
+
#
|
85
|
+
# And here the special module method called +IPAccess.arm+
|
86
|
+
# comes in. It lets you patch most of Ruby's
|
87
|
+
# networking classes and objects. Besides
|
88
|
+
# equipping them in IPAccess::Set instance
|
89
|
+
# it also adds some methods for doing quick
|
90
|
+
# checks and changes in access lists.
|
91
|
+
#
|
92
|
+
# The patching engine can arm network classes and
|
93
|
+
# single network objects. It is not loaded by default
|
94
|
+
# since you may not want extra code attached to a
|
95
|
+
# program that uses access lists or sets with
|
96
|
+
# own access checking code.
|
97
|
+
#
|
98
|
+
# === Variants of popular classes
|
99
|
+
#
|
100
|
+
# Sometimes you want to write a code that
|
101
|
+
# uses standard Ruby's network objects
|
102
|
+
# but you find it dirty to alter classes or objects.
|
103
|
+
# In that case you may want to use static variants
|
104
|
+
# of Ruby's network classes that are not patches
|
105
|
+
# but derived classes.
|
106
|
+
#
|
107
|
+
# === Exceptions
|
108
|
+
#
|
109
|
+
# When you are dealing with patched (armed) versions
|
110
|
+
# of classes and objects or when you are using
|
111
|
+
# special variants of popular network classes, you have
|
112
|
+
# to rely on exceptions as the only way for
|
113
|
+
# access checking methods to tell your program
|
114
|
+
# that an event (like access denied) happened.
|
115
|
+
#
|
116
|
+
# Note that when exception is thrown
|
117
|
+
# the communication session is closed in case
|
118
|
+
# of connection-oriented network objects.
|
119
|
+
# You may change it by switching +opened_on_deny+
|
120
|
+
# attribute to +true+.
|
121
|
+
#
|
122
|
+
# See IPAccess::Set#check_in to know more
|
123
|
+
# about tracking original network object
|
124
|
+
# that caused exception to happend. Note
|
125
|
+
# that in case of armed versions of network
|
126
|
+
# classes (or access-contolled variants)
|
127
|
+
# an information about original network
|
128
|
+
# object stored within an exception will be set to
|
129
|
+
# +nil+ if access had been denied before
|
130
|
+
# object was initialized. This shouldn't
|
131
|
+
# happend often, since access checks are lazy
|
132
|
+
# (they are performed only when connection
|
133
|
+
# is going to be made).
|
134
|
+
#
|
135
|
+
# See IPAccessDenied for more information
|
136
|
+
# about what you can do with exceptions.
|
137
|
+
#
|
138
|
+
# === Sockets in armed network objects
|
139
|
+
#
|
140
|
+
# Specialized Ruby's network classes,
|
141
|
+
# such as Net::HTTP or Net::Telnet
|
142
|
+
# and their variants created by this library,
|
143
|
+
# make use of socket objects. For example
|
144
|
+
# Net::HTTP class uses TCPSocket instance to
|
145
|
+
# create TCP connection. When versions
|
146
|
+
# of these <tt>Net::</tt> objects with
|
147
|
+
# enabled access control are used then
|
148
|
+
# the internal routines of IPAccess
|
149
|
+
# will also try to patch underlying sockets and assign
|
150
|
+
# to them the same access set that is used by main
|
151
|
+
# object. It is done to avoid access leaks.
|
152
|
+
# However, such armed internal sockets will have
|
153
|
+
# +opened_on_deny+ flag switched on since
|
154
|
+
# closing session (and an eventual connection)
|
155
|
+
# should be settled by main object.
|
156
|
+
#
|
157
|
+
# === Ordination of elements
|
158
|
+
#
|
159
|
+
# To properly understand what are the most important
|
160
|
+
# structures mentioned above it's worth
|
161
|
+
# to look at the diagram:
|
162
|
+
#
|
163
|
+
# link:images/ipaccess_view.png
|
164
|
+
#
|
165
|
+
# == Usage
|
166
|
+
#
|
167
|
+
# === Handling access sets and access lists
|
168
|
+
#
|
169
|
+
# If you need just IP access lists that you will handle in your own way
|
170
|
+
# you may want to use two classes:
|
171
|
+
#
|
172
|
+
# * IPAccess::Set to maintain access sets (containing input and output access lists),
|
173
|
+
# * IPAccess::List to maintain single access list.
|
174
|
+
#
|
175
|
+
# === Using socket classes
|
176
|
+
#
|
177
|
+
# If you want standard sockets to have access control enabled
|
178
|
+
# you may want to use:
|
179
|
+
#
|
180
|
+
# * IPAccess::Socket (or issue <tt>IPAccess.arm Socket</tt>)
|
181
|
+
# * IPAccess::TCPSocket (or issue <tt>IPAccess.arm TCPSocket</tt>)
|
182
|
+
# * IPAccess::UDPSocket (or issue <tt>IPAccess.arm UDPSocket</tt>)
|
183
|
+
# * IPAccess::SOCKSocket (or issue <tt>IPAccess.arm SOCKSocket</tt>)
|
184
|
+
# * IPAccess::TCPServer (or issue <tt>IPAccess.arm TCPServer</tt>)
|
185
|
+
#
|
186
|
+
# Before using any of them you must issue:
|
187
|
+
#
|
188
|
+
# * <tt>require 'ipaccess/socket'</tt>
|
189
|
+
#
|
190
|
+
# Using the IPAccess.arm causes standard socket class to be altered,
|
191
|
+
# while \IPAccess:: classes are just new variants of socket
|
192
|
+
# handling classes.
|
193
|
+
#
|
194
|
+
# ==== Using other supported network classes
|
195
|
+
#
|
196
|
+
# If you want some working objects to have access control enabled
|
197
|
+
# you may want to use:
|
198
|
+
#
|
199
|
+
# * IPAccess::Net::Telnet (or issue <tt>IPAccess.arm Net::Telnet</tt>)
|
200
|
+
# * IPAccess::Net::HTTP (or issue <tt>IPAccess.arm Net::HTTP</tt>)
|
201
|
+
# * IPAccess::Net::FTP (or issue <tt>IPAccess.arm Net::FTP</tt>)
|
202
|
+
# * IPAccess::Net::POP3 (or issue <tt>IPAccess.arm Net::POP3</tt>)
|
203
|
+
# * IPAccess::Net::IMAP (or issue <tt>IPAccess.arm Net::IMAP</tt>)
|
204
|
+
# * IPAccess::Net::SMTP (or issue <tt>IPAccess.arm Net::SMTP</tt>)
|
205
|
+
#
|
206
|
+
# ==== Using single network objects
|
207
|
+
#
|
208
|
+
# If you want to enable access control for single network
|
209
|
+
# object from the list shown above you may issue:
|
210
|
+
#
|
211
|
+
# require 'ipaccess/net/http'
|
212
|
+
# obj = Net::HTTP.new(host, port)
|
213
|
+
# IPAccess.arm obj
|
214
|
+
#
|
215
|
+
# or
|
216
|
+
#
|
217
|
+
# require 'ipaccess/socket'
|
218
|
+
# socket = IPAccess::TCPServer.new(31337)
|
219
|
+
# IPAccess.arm socket
|
220
|
+
#
|
221
|
+
# ..and so on.
|
222
|
+
#
|
223
|
+
# === Structures
|
224
|
+
#
|
225
|
+
# IP addresses used by the classes are internaly and interfacialy
|
226
|
+
# represented by NetAddr::CIDR[http://netaddr.rubyforge.org/classes/NetAddr/CIDR.html]
|
227
|
+
# objects (NetAddr::CIDRv4[http://netaddr.rubyforge.org/classes/NetAddr/CIDRv4.html] and
|
228
|
+
# NetAddr::CIDRv6[http://netaddr.rubyforge.org/classes/NetAddr/CIDRv6.html]). Due to
|
229
|
+
# performance reasons any access list internally is represented as a tree
|
230
|
+
# (slightly modified NetAddr::Tree[http://netaddr.rubyforge.org/classes/NetAddr/Tree.html])
|
231
|
+
# with special tags assigning rules to virtual lists.
|
232
|
+
#
|
233
|
+
# === Relations
|
234
|
+
#
|
235
|
+
# Here is a diagram which shows relations
|
236
|
+
# between the IPAccess::TCPSocket class
|
237
|
+
# and other classes from this module:
|
238
|
+
#
|
239
|
+
# link:images/ipaccess_relations.png
|
240
|
+
|
241
|
+
module IPAccess
|
242
|
+
|
243
|
+
# This method converts names to NetAddr::CIDR objects. It returns an array of CIDR objects.
|
244
|
+
#
|
245
|
+
# Allowed input are strings (DNS names or IP addresses optionally with masks), numbers (IP addresses representation),
|
246
|
+
# IPSocket objects, URI objects, IPAddr objects, Net::HTTP objects, IPAddrList objects, NetAddr::CIDR objects,
|
247
|
+
# NetAddr::Tree objects, IPAccess::List objects, symbols, objects that contain file descriptors bound to sockets
|
248
|
+
# (including OpenSSL sockets) and arrays of these.
|
249
|
+
#
|
250
|
+
# In case of resolving the IPv6 link-local addresses
|
251
|
+
# zone index is removed. In case of DNS names there may
|
252
|
+
# occur Resolv::ResolvError exception. If there is an
|
253
|
+
# object that cannot be converted the ArgumentError
|
254
|
+
# exception is raised.
|
255
|
+
#
|
256
|
+
# When an argument called +:include_origins+ is present then the method will attach
|
257
|
+
# original converted objects to results as the +:Origin+ tag of CIDR objects (<tt>tag[:Origin]</tt>).
|
258
|
+
# This rule applies only to single objects or objects inside of arrays or sets.
|
259
|
+
# Objects that are kind of NetAddr::CIDR, IPAccess::Set, NetAddr::Tree and arrays will
|
260
|
+
# never be set as originators.
|
261
|
+
#
|
262
|
+
# ==== Examples
|
263
|
+
#
|
264
|
+
# to_cidrs("127.0.0.1") # uses the IP address
|
265
|
+
# to_cidrs(2130706433) # uses numeric representation of 127.0.0.1
|
266
|
+
# to_cidrs(:private, "localhost") # uses special symbol and DNS hostname
|
267
|
+
# to_cidrs(:private, :localhost) # uses special symbols
|
268
|
+
# to_cidrs [:private, :auto] # other way to write the above
|
269
|
+
# to_cidrs "10.0.0.0/8" # uses masked IP address
|
270
|
+
# to_cidrs "10.0.0.0/255.0.0.0" # uses masked IP address
|
271
|
+
# to_cidrs IPSocket.new("www.pl", 80) # uses the socket
|
272
|
+
# to_cidrs IPAddr("10.0.0.1") # uses IPAddr object
|
273
|
+
# to_cidrs NetAddr::CIDR.create("10.0.0.1") # uses NetAddr object
|
274
|
+
# to_cidrs URI('http://www.pl/') # uses URI
|
275
|
+
# to_cidrs 'http://www.pl/' # uses the extracted host string
|
276
|
+
# to_cidrs 'somehost.xx' # uses the host string (fetches ALL addresses from DNS)
|
277
|
+
# to_cidrs 'somehost.xx/16' # uses the host string and a netmask
|
278
|
+
#
|
279
|
+
# ==== Special symbols
|
280
|
+
#
|
281
|
+
# When symbol is passed to this method it tries to find out if it has special meaning.
|
282
|
+
# That allows you to create access rules in an easy way. For most of them you may
|
283
|
+
# also specify IP protocol version using +ipv4_+ or +ipv6_+ prefix.
|
284
|
+
#
|
285
|
+
# Known symbols are:
|
286
|
+
#
|
287
|
+
# <b>+:all+</b> (+:any+, +:anyone+, +:world+, +:internet+, +:net+, +:everything+, +:everyone+, +:everybody+, +:anybody+)
|
288
|
+
#
|
289
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
290
|
+
#
|
291
|
+
# Creates masked IP address that matches all networks:
|
292
|
+
# – 0.0.0.0/0
|
293
|
+
# – ::/0
|
294
|
+
#
|
295
|
+
# <b>+:broadcast+</b> (+:brd+)
|
296
|
+
#
|
297
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
298
|
+
#
|
299
|
+
# Creates masked IP address that matches generic broadcast address:
|
300
|
+
# – 255.255.255.255/32
|
301
|
+
# – ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128
|
302
|
+
#
|
303
|
+
# <b>+:local+</b> (+:localhost+, +:localdomain+, +:loopback+, +:lo+)
|
304
|
+
#
|
305
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
306
|
+
#
|
307
|
+
# Creates masked IP addresses that match localhost:
|
308
|
+
# – 127.0.0.1/8
|
309
|
+
# – ::1/128
|
310
|
+
#
|
311
|
+
# <b>+:auto+</b> (+:automatic+, +:linklocal+)
|
312
|
+
#
|
313
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
314
|
+
#
|
315
|
+
# Creates masked IP addresses that match automatically assigned address ranges:
|
316
|
+
# – 169.254.0.0/16
|
317
|
+
# – fe80::/10
|
318
|
+
#
|
319
|
+
# <b>+:private+</b> (+:intra+, +:intranet+, +:internal+)
|
320
|
+
#
|
321
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
322
|
+
#
|
323
|
+
# Creates masked IP addresses that match private ranges:
|
324
|
+
# – 10.0.0.0/8
|
325
|
+
# – 172.16.0.0/12
|
326
|
+
# – 192.168.0.0/16
|
327
|
+
# – 2001:10::/28
|
328
|
+
# – 2001:db8::/32
|
329
|
+
# – fc00::/7
|
330
|
+
# – fdde:9e1a:dc85:7374::/64
|
331
|
+
#
|
332
|
+
# <b>+:multicast+</b> (+:multi+, +:multiemission+)
|
333
|
+
#
|
334
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
335
|
+
#
|
336
|
+
# Creates masked IP addresses that match multicast addresses ranges:
|
337
|
+
# – 224.0.0.0/4
|
338
|
+
# – ff00::/8
|
339
|
+
# – ff02::1:ff00:0/104
|
340
|
+
#
|
341
|
+
# <b>+:reserved+</b> (+:example+)
|
342
|
+
#
|
343
|
+
# variants: +:ipv4_+
|
344
|
+
#
|
345
|
+
# Creates masked IP addresses that match reserved addresses ranges:
|
346
|
+
# – 192.0.2.0/24
|
347
|
+
# – 128.0.0.0/16
|
348
|
+
# – 191.255.0.0/16
|
349
|
+
# – 192.0.0.0/24
|
350
|
+
# – 198.18.0.0/15
|
351
|
+
# – 223.255.255.0/24
|
352
|
+
# – 240.0.0.0/4
|
353
|
+
#
|
354
|
+
# <b>+:strange+</b> (+:unusual+, +:nonpublic+, +:unpublic+)
|
355
|
+
#
|
356
|
+
# Creates masked IP addressess that match the following sets (both IPv4 and IPv6):
|
357
|
+
# – :local
|
358
|
+
# – :auto
|
359
|
+
# – :private
|
360
|
+
# – :reserved
|
361
|
+
# – :multicast
|
362
|
+
|
363
|
+
def self.to_cidrs(*addresses)
|
364
|
+
obj = addresses.flatten
|
365
|
+
include_origins = false
|
366
|
+
obj.delete_if { |x| include_origins = true if (x.is_a?(Symbol) && x == :include_origins) }
|
367
|
+
|
368
|
+
if obj.size == 1
|
369
|
+
obj = obj.first
|
370
|
+
else
|
371
|
+
ary = []
|
372
|
+
obj.each do |o|
|
373
|
+
ary += include_origins ? to_cidrs(o, :include_origins) : to_cidrs(o)
|
374
|
+
end
|
375
|
+
ary.flatten!
|
376
|
+
return ary
|
377
|
+
end
|
378
|
+
|
379
|
+
ori_obj = obj
|
380
|
+
|
381
|
+
# NetAddr::CIDR - immediate generation
|
382
|
+
if obj.is_a?(NetAddr::CIDR)
|
383
|
+
r = obj.dup
|
384
|
+
r.tag[:Originator] = ori_obj if include_origins
|
385
|
+
return [r]
|
386
|
+
end
|
387
|
+
|
388
|
+
# IPAccess::List - immediate generation
|
389
|
+
return obj.to_a if obj.is_a?(IPAccess::List)
|
390
|
+
|
391
|
+
# NetAddr::Tree - immediate generation
|
392
|
+
return obj.dump.map { |addr| addr[:CIDR] } if obj.is_a?(NetAddr::Tree)
|
393
|
+
|
394
|
+
# number or nil - immediate generation or exception
|
395
|
+
if (obj.is_a?(Numeric) || obj.nil?)
|
396
|
+
r = NetAddr::CIDR.create(obj)
|
397
|
+
r.tag[:Originator] = ori_obj if include_origins
|
398
|
+
return [r]
|
399
|
+
end
|
400
|
+
|
401
|
+
# object containing socket member (e.g. Net::HTTP) - fetch socket
|
402
|
+
if obj.respond_to?(:socket)
|
403
|
+
obj = obj.socket
|
404
|
+
elsif obj.respond_to?(:sock)
|
405
|
+
obj = obj.sock
|
406
|
+
elsif obj.respond_to?(:client_socket)
|
407
|
+
obj = obj.client_socket
|
408
|
+
elsif obj.instance_variable_defined?(:@socket)
|
409
|
+
obj = obj.instance_variable_get(:@socket)
|
410
|
+
elsif obj.instance_variable_defined?(:@client_socket)
|
411
|
+
obj = obj.instance_variable_get(:@client_socket)
|
412
|
+
elsif obj.instance_variable_defined?(:@sock)
|
413
|
+
obj = obj.instance_variable_get(:@sock)
|
414
|
+
end
|
415
|
+
obj = obj.io if (obj.respond_to?(:io) && obj.io.respond_to?(:getpeername))
|
416
|
+
|
417
|
+
# some file descriptor but not socket - fetch socket
|
418
|
+
obj = ::Socket.for_fd(obj.fileno) if (!obj.respond_to?(:getpeername) && obj.respond_to?(:fileno))
|
419
|
+
|
420
|
+
# Socket - immediate generation
|
421
|
+
if obj.respond_to?(:getpeername)
|
422
|
+
peeraddr = ::Socket.unpack_sockaddr_in(obj.getpeername).last.split('%').first
|
423
|
+
r = NetAddr::CIDR.create(peeraddr)
|
424
|
+
r.tag[:Originator] = ori_obj if include_origins
|
425
|
+
return [r]
|
426
|
+
end
|
427
|
+
|
428
|
+
# symbol - immediate generation
|
429
|
+
r_args = nil
|
430
|
+
if obj.is_a?(Symbol)
|
431
|
+
case obj
|
432
|
+
when :ipv4_all, :ipv4_any, :ipv4_anyone, :ipv4_world, :ipv4_internet, :ipv4_net, :ipv4_everything, :ipv4_everyone, :ipv4_everybody, :ipv4_anybody
|
433
|
+
obj = [ "0.0.0.0/0" ]
|
434
|
+
when :ipv6_all, :ipv6_any, :ipv6_anyone, :ipv6_world, :ipv6_internet, :ipv6_net, :ipv6_everything, :ipv6_everyone, :ipv6_everybody, :ipv6_anybody
|
435
|
+
obj = [ "0.0.0.0/0", "::/0" ]
|
436
|
+
when :ipv4_broadcast, :ipv4_brd
|
437
|
+
obj = [ "255.255.255.255/32" ]
|
438
|
+
when :ipv6_broadcast, :ipv6_brd
|
439
|
+
obj = [ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" ]
|
440
|
+
when :ipv4_local, :ipv4_localhost, :ipv4_loopback, :ipv4_lo
|
441
|
+
obj = [ "127.0.0.1/8" ]
|
442
|
+
when :ipv6_local, :ipv6_localhost, :ipv6_loopback, :ipv6_lo
|
443
|
+
obj = [ "::1/128" ]
|
444
|
+
when :ipv4_auto, :ipv4_automatic, :ipv4_linklocal
|
445
|
+
obj = [ "169.254.0.0/16" ]
|
446
|
+
when :ipv6_auto, :ipv6_automatic, :ipv6_linklocal
|
447
|
+
obj = [ "fe80::/10" ]
|
448
|
+
when :ipv4_private, :ipv4_intra, :ipv4_intranet, :ipv4_internal
|
449
|
+
obj = [ "10.0.0.0/8",
|
450
|
+
"172.16.0.0/12",
|
451
|
+
"192.168.0.0/16" ]
|
452
|
+
when :ipv6_private, :ipv6_intra, :ipv6_intranet, :ipv6_internal, :ipv6_ula, :ipv6_unique
|
453
|
+
obj = [ "2001:10::/28",
|
454
|
+
"2001:db8::/32",
|
455
|
+
"fc00::/7",
|
456
|
+
"fdde:9e1a:dc85:7374::/64" ]
|
457
|
+
when :ipv4_multicast, :ipv4_multi, :ipv4_multiemission
|
458
|
+
obj = [ "224.0.0.0/4" ]
|
459
|
+
when :ipv6_multicast, :ipv6_multi, :ipv6_multiemission
|
460
|
+
obj = [ "ff00::/8",
|
461
|
+
"ff02::1:ff00:0/104" ]
|
462
|
+
when :ipv4_example, :ipv4_reserved
|
463
|
+
obj = [ "192.0.2.0/24",
|
464
|
+
"128.0.0.0/16",
|
465
|
+
"191.255.0.0/16",
|
466
|
+
"192.0.0.0/24",
|
467
|
+
"198.18.0.0/15",
|
468
|
+
"223.255.255.0/24",
|
469
|
+
"240.0.0.0/4" ]
|
470
|
+
when :all, :any, :anyone, :world, :internet, :net, :everything, :everyone, :everybody, :anybody
|
471
|
+
r_args = [ :ipv4_all,
|
472
|
+
:ipv6_all ]
|
473
|
+
when :broadcast, :brd
|
474
|
+
r_args = [ :ipv4_broadcast,
|
475
|
+
:ipv6_broadcast ]
|
476
|
+
when :local, :localhost, :localdomain, :loopback, :lo
|
477
|
+
r_args = [ :ipv4_local,
|
478
|
+
:ipv6_local ]
|
479
|
+
when :auto, :automatic, :linklocal
|
480
|
+
r_args = [ :ipv4_auto,
|
481
|
+
:ipv6_auto ]
|
482
|
+
when :private, :intra, :intranet, :internal
|
483
|
+
r_args = [ :ipv4_private,
|
484
|
+
:ipv6_private ]
|
485
|
+
when :multicast, :multi, :multiemission
|
486
|
+
r_args = [ :ipv4_multicast,
|
487
|
+
:ipv6_multicast ]
|
488
|
+
when :reserved, :example
|
489
|
+
r_args = [ :ipv4_example ]
|
490
|
+
when :strange, :unusual, :nonpublic, :unpublic
|
491
|
+
r_args = [ :local,
|
492
|
+
:auto,
|
493
|
+
:private,
|
494
|
+
:reserved,
|
495
|
+
:multicast ]
|
496
|
+
else
|
497
|
+
raise ArgumentError, "provided symbol is unknown: #{obj.to_s}"
|
498
|
+
end
|
499
|
+
|
500
|
+
unless r_args.nil?
|
501
|
+
r_args.push :include_origins if include_origins
|
502
|
+
return to_cidrs(*r_args)
|
503
|
+
end
|
504
|
+
|
505
|
+
# strange types here
|
506
|
+
if obj.is_a?(Array)
|
507
|
+
return obj.map do |addr|
|
508
|
+
r = NetAddr::CIDR.create(addr)
|
509
|
+
r.tag[:Originator] = addr if include_origins
|
510
|
+
r
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
# URI or something that responds to host method - fetch string
|
516
|
+
obj = obj.host if obj.respond_to?(:host)
|
517
|
+
|
518
|
+
# objects of external classes
|
519
|
+
case obj.class.name.to_sym
|
520
|
+
when :IPAddr # IPAddr - fetch IP/mask string
|
521
|
+
obj = obj.native.inspect.split[1].chomp('>')[5..-1]
|
522
|
+
when :IPAddrList # IPAddrList - pass array to parse
|
523
|
+
return include_origins ? to_cidrs(obj.to_a, :include_origins) : to_cidrs(obj.to_a)
|
524
|
+
end
|
525
|
+
|
526
|
+
# string or similar - immediate generation
|
527
|
+
if obj.respond_to?(:to_s)
|
528
|
+
hostmask = ""
|
529
|
+
obj = obj.to_s
|
530
|
+
# URI
|
531
|
+
if obj =~ /^[^:]+:\/\/(.*)/
|
532
|
+
obj = $1.split('/').first
|
533
|
+
# IP in URI
|
534
|
+
if obj =~ /^\[([^\]]+)\]/
|
535
|
+
obj = $1
|
536
|
+
else
|
537
|
+
obj = obj.split(':').first
|
538
|
+
end
|
539
|
+
# host(s) and a mask
|
540
|
+
elsif obj =~ /^([^\/]+)(\/((\d{1,2}$)|(\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b$)))/
|
541
|
+
obj = $1
|
542
|
+
hostmask = $2
|
543
|
+
end
|
544
|
+
begin
|
545
|
+
ipa = obj.split('%').first.to_s
|
546
|
+
r = NetAddr::CIDR.create(ipa + hostmask)
|
547
|
+
rescue NetAddr::ValidationError
|
548
|
+
begin
|
549
|
+
addresses = Resolv::getaddresses(obj)
|
550
|
+
rescue NoMethodError # unhandled error
|
551
|
+
raise Resolv::ResolvError, "not connected or network error"
|
552
|
+
end
|
553
|
+
addresses.map! do |addr|
|
554
|
+
begin
|
555
|
+
r = NetAddr::CIDR.create(addr.split('%').first + hostmask)
|
556
|
+
r.tag[:Originator] = ori_obj
|
557
|
+
r
|
558
|
+
rescue ArgumentError
|
559
|
+
nil
|
560
|
+
end
|
561
|
+
end
|
562
|
+
addresses.flatten!
|
563
|
+
addresses.compact!
|
564
|
+
return addresses
|
565
|
+
end
|
566
|
+
r.tag[:Originator] = ori_obj
|
567
|
+
return [r]
|
568
|
+
end
|
569
|
+
|
570
|
+
# should never happend
|
571
|
+
r = obj.is_a?(NetAddr::CIDR) ? obj.dup : NetAddr::CIDR.create(obj.to_s)
|
572
|
+
r.tag[:Originator] = ori_obj
|
573
|
+
return [r]
|
574
|
+
end
|
575
|
+
|
576
|
+
# This method calls IPAccess.to_cidrs
|
577
|
+
# and returns first obtained entry containing
|
578
|
+
# single IP address with mask (NetAddr::CIDR).
|
579
|
+
|
580
|
+
def self.to_cidr(*addresses)
|
581
|
+
r = self.to_cidrs(*addresses)
|
582
|
+
return r.respond_to?(:first) ? first : r
|
583
|
+
end
|
584
|
+
|
585
|
+
end
|