ipaccess 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +64 -0
- data/docs/COPYING +61 -0
- data/docs/DOWNLOAD +17 -0
- data/docs/LEGAL +11 -0
- data/docs/LGPL-LICENSE +169 -0
- data/docs/README +95 -0
- data/docs/TODO +18 -0
- data/docs/WELCOME +8 -0
- data/examples/tcp_socket.rb +9 -0
- data/lib/ipaccess.rb +35 -0
- data/lib/ipaccess/arm_sockets.rb +33 -0
- data/lib/ipaccess/ghost_doc.rb +206 -0
- data/lib/ipaccess/ghost_doc_acl.rb +31 -0
- data/lib/ipaccess/ip_access.rb +455 -0
- data/lib/ipaccess/ip_access_errors.rb +131 -0
- data/lib/ipaccess/ip_access_list.rb +1209 -0
- data/lib/ipaccess/ip_access_patches.rb +435 -0
- data/lib/ipaccess/netaddr_patch.rb +127 -0
- data/lib/ipaccess/sockets.rb +53 -0
- data/spec/core_spec.rb +5 -0
- data/spec/ip_access_list_spec.rb +302 -0
- data/spec/rcov.opts +7 -0
- data/spec/spec.opts +2 -0
- metadata +84 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
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-LICENSE.html] or {Ruby License}[link:docs/COPYING.html].
|
6
|
+
#
|
7
|
+
# This file contains IPAccessDenied class
|
8
|
+
# used to report access denials by
|
9
|
+
# IPAccess objects.
|
10
|
+
#
|
11
|
+
#--
|
12
|
+
#
|
13
|
+
# Copyright (C) 2009 by Paweł Wilk. All Rights Reserved.
|
14
|
+
#
|
15
|
+
# This program is free software; you can redistribute it and/or modify
|
16
|
+
# it under the terms of either: 1) the GNU Lesser General Public License
|
17
|
+
# as published by the Free Software Foundation; either version 3 of the
|
18
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
19
|
+
#
|
20
|
+
# See the file COPYING for complete licensing information.
|
21
|
+
#
|
22
|
+
#++
|
23
|
+
|
24
|
+
|
25
|
+
# This class handles IP access denied exceptions.
|
26
|
+
|
27
|
+
class IPAccessDenied < Errno::EACCES
|
28
|
+
|
29
|
+
# Creates new object. First argument should be
|
30
|
+
# a NetAddr::CIDR object containing address
|
31
|
+
# of denied connection. Second argument should
|
32
|
+
# be a CIDR rule that matched. Last argument
|
33
|
+
# should be an IPAccess object.
|
34
|
+
|
35
|
+
def initialize(addr, rule=nil, access_list=nil)
|
36
|
+
@peer_ip = addr
|
37
|
+
@rule = rule
|
38
|
+
@access_list = access_list
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns string representation of access list name rule.
|
42
|
+
|
43
|
+
def list_desc
|
44
|
+
if (@access_list.is_a?(IPAccess) && !@access_list.name.to_s.empty?)
|
45
|
+
"#{@access_list.name} "
|
46
|
+
elsif @access_list.is_a?(String)
|
47
|
+
"#{@access_list} "
|
48
|
+
else
|
49
|
+
""
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns string representation of rule.
|
54
|
+
|
55
|
+
def rule_desc
|
56
|
+
if @rule.is_a?(NetAddr::CIDR)
|
57
|
+
if @rule.version == 6
|
58
|
+
rule = @rule.to_s(:Short => true)
|
59
|
+
rule = ":#{rule}" if rule =~ /^\//
|
60
|
+
else
|
61
|
+
rule = @rule.to_s
|
62
|
+
end
|
63
|
+
return " rule: #{rule}"
|
64
|
+
elsif @rule.is_a?(String)
|
65
|
+
return " rule: #{@rule}"
|
66
|
+
else
|
67
|
+
return ""
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns string representation of address.
|
72
|
+
|
73
|
+
def addr_desc
|
74
|
+
if @peer_ip.is_a?(NetAddr::CIDR)
|
75
|
+
if @peer_ip.version == 6
|
76
|
+
if @peer_ip.to_i(:netmask) == ((2**128)-1)
|
77
|
+
return @peer_ip.ip(:Short => true)
|
78
|
+
else
|
79
|
+
pip = @peer_ip.to_s(:Short => true)
|
80
|
+
pip = ":#{pip}" if pip =~ /^\//
|
81
|
+
return pip
|
82
|
+
end
|
83
|
+
else
|
84
|
+
if @peer_ip.to_i(:netmask) == 4294967295
|
85
|
+
return @peer_ip.ip
|
86
|
+
else
|
87
|
+
return @peer_ip.to_s
|
88
|
+
end
|
89
|
+
end
|
90
|
+
elsif @peer_ip.is_a?(String)
|
91
|
+
return @peer_ip
|
92
|
+
else
|
93
|
+
return @peer_ip.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Shows an error message.
|
98
|
+
|
99
|
+
def message
|
100
|
+
return "connection with #{addr_desc} " +
|
101
|
+
"denied by #{list_desc}#{rule_desc}"
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
# This class handles IP access denied exceptions
|
107
|
+
# for incoming connections/datagrams.
|
108
|
+
|
109
|
+
class IPAccessDenied::Input < IPAccessDenied
|
110
|
+
|
111
|
+
def message
|
112
|
+
return "incoming connection from " +
|
113
|
+
"#{addr_desc} denied by " +
|
114
|
+
"#{list_desc}#{rule_desc}"
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
# This class handles IP access denied exceptions
|
120
|
+
# for outgoing connections/datagrams.
|
121
|
+
|
122
|
+
class IPAccessDenied::Output < IPAccessDenied
|
123
|
+
|
124
|
+
def message
|
125
|
+
return "outgoing connection to " +
|
126
|
+
"#{addr_desc} denied by " +
|
127
|
+
"#{list_desc}#{rule_desc}"
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
@@ -0,0 +1,1209 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
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-LICENSE.html] or {Ruby License}[link:docs/COPYING.html].
|
6
|
+
#
|
7
|
+
# This file contains IPAccessList class, which uses
|
8
|
+
# NetAddr::Tree to implement IP access list.
|
9
|
+
#
|
10
|
+
#--
|
11
|
+
#
|
12
|
+
# Copyright (C) 2009 by Paweł Wilk. All Rights Reserved.
|
13
|
+
#
|
14
|
+
# This program is free software; you can redistribute it and/or modify
|
15
|
+
# it under the terms of either: 1) the GNU Lesser General Public License
|
16
|
+
# as published by the Free Software Foundation; either version 3 of the
|
17
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
18
|
+
#
|
19
|
+
# See the file COPYING for complete licensing information.
|
20
|
+
#
|
21
|
+
#++
|
22
|
+
|
23
|
+
require 'ipaddr'
|
24
|
+
require 'socket'
|
25
|
+
require 'resolv'
|
26
|
+
require 'netaddr'
|
27
|
+
require 'ipaccess/netaddr_patch'
|
28
|
+
|
29
|
+
# This class implements easy to
|
30
|
+
# manage IP access list based on NetAddr::Tree
|
31
|
+
# which uses binary search to speed up
|
32
|
+
# matching process. It stores data in a tree
|
33
|
+
# of NetAddr::CIDR objects and allows to add,
|
34
|
+
# remove and search them.
|
35
|
+
#
|
36
|
+
# ==== Access lists
|
37
|
+
#
|
38
|
+
# To control access IPAccessList maintains
|
39
|
+
# two abstract lists: white list and black
|
40
|
+
# list. Each list contains rules (CIDR objects
|
41
|
+
# with information about IP address and
|
42
|
+
# network mask). Access is evaluated as
|
43
|
+
# blocked when tested IP address matches
|
44
|
+
# rule from black list and not matches any rule
|
45
|
+
# from white list. Basically, white list rules
|
46
|
+
# override black list rules.
|
47
|
+
#
|
48
|
+
# To be precise: in order to increase
|
49
|
+
# lookups performance internally there
|
50
|
+
# are no real lists but one tree containing
|
51
|
+
# specially marked objects.
|
52
|
+
#
|
53
|
+
# ==== Basic Operations
|
54
|
+
#
|
55
|
+
# This class has no methods that actualy
|
56
|
+
# do network operations, it just allows
|
57
|
+
# you to check IP against black and
|
58
|
+
# white list. There are 2 major types
|
59
|
+
# of operations you can perform: rules
|
60
|
+
# management and access checks.
|
61
|
+
#
|
62
|
+
# Rules management methods allow you to
|
63
|
+
# add, remove and find IP access rules.
|
64
|
+
# Access checks let you test if given
|
65
|
+
# address or addresses are allowed or
|
66
|
+
# denied to perform network operations
|
67
|
+
# according to rules.
|
68
|
+
#
|
69
|
+
# ==== IPv4 and IPv6
|
70
|
+
#
|
71
|
+
# IPv6 addresses that are IPv4 compatible
|
72
|
+
# or IPv4 masked are automatically
|
73
|
+
# translated into IPv4 addresses while
|
74
|
+
# adding or searching.
|
75
|
+
#
|
76
|
+
# ==== Examples
|
77
|
+
#
|
78
|
+
# Examples of usage:
|
79
|
+
#
|
80
|
+
# access = IPAccessList.new # create new access list
|
81
|
+
# access.blacklist :ipv4_private # blacklist private IPv4 addresses
|
82
|
+
# access.whitelist '172.16.0.7' # whitelist 172.16.0.7
|
83
|
+
# access.granted? '172.16.0.7' # check access
|
84
|
+
# access.granted? '172.16.0.1' # check access
|
85
|
+
#
|
86
|
+
# Examples of deny-all & allow-selected strategy:
|
87
|
+
#
|
88
|
+
# access = IPAccessList.new # create new access list
|
89
|
+
# access.deny :all # blacklist all
|
90
|
+
# access.allow '192.168.1.0/24' # allow my private network
|
91
|
+
# access.allow :local # allow localhost
|
92
|
+
#
|
93
|
+
# puts access.show # display internal structure
|
94
|
+
# puts access.blacklist # display blacklisted IP addresses
|
95
|
+
# puts access.whitelist # display whitelisted IP addresses
|
96
|
+
|
97
|
+
class IPAccessList < NetAddr::Tree
|
98
|
+
|
99
|
+
# Creates new IPAccessList object. You may pass objects
|
100
|
+
# containing IP information to it. These objects will
|
101
|
+
# create black list rules. See obj_to_cidr description
|
102
|
+
# for more info on how to pass arguments.
|
103
|
+
#
|
104
|
+
# IPAccessList object and/or NetAddr::CIDR object(s) may
|
105
|
+
# carry black or white list assignments inside. If such
|
106
|
+
# object(s) will be used to create initial ruleset then
|
107
|
+
# assignment found there would be used instead of default.
|
108
|
+
#
|
109
|
+
# You should avoid passing hostnames as arguments since
|
110
|
+
# DNS is not reliable and responses may change with time.
|
111
|
+
# That may cause security flaws.
|
112
|
+
#
|
113
|
+
# ==== Examples
|
114
|
+
#
|
115
|
+
# IPAccessList.new '192.168.0.0/16', '127.0.0.1/255.0.0.0'
|
116
|
+
# IPAccessList.new :private, :local
|
117
|
+
# IPAccessList.new "randomseed.pl", :nonpublic
|
118
|
+
|
119
|
+
def initialize(*args)
|
120
|
+
args = [] if args == [nil]
|
121
|
+
super()
|
122
|
+
add!(*args) unless args.empty?
|
123
|
+
return self
|
124
|
+
end
|
125
|
+
|
126
|
+
# This method converts names to NetAddr::CIDR objects. It returns an array of CIDR objects.
|
127
|
+
#
|
128
|
+
# Allowed input: strings (DNS names or IP addresses optionally with masks), numbers (IP addresses representation),
|
129
|
+
# IPSocket objects, URI objects, IPAddr objects, Net::HTTP objects, IPAddrList objects, NetAddr::CIDR objects,
|
130
|
+
# NetAddr::Tree objects, IPAccessList objects, symbols, objects that contain file descriptors bound to sockets,
|
131
|
+
# and arrays of these.
|
132
|
+
#
|
133
|
+
# In case of resolving the IPv6 link-local addresses zone index is removed.
|
134
|
+
#
|
135
|
+
# ==== Examples
|
136
|
+
#
|
137
|
+
# obj_to_cidr("127.0.0.1") # uses IP address
|
138
|
+
# obj_to_cidr(2130706433) # uses numeric representation of 127.0.0.1
|
139
|
+
# obj_to_cidr(:private, "localhost") # uses special symbol and DNS hostname
|
140
|
+
# obj_to_cidr(:private, :localhost) # uses special symbols
|
141
|
+
# obj_to_cidr [:private, :auto] # other way to write the above
|
142
|
+
# obj_to_cidr "10.0.0.0/8" # uses masked IP address
|
143
|
+
# obj_to_cidr "10.0.0.0/255.0.0.0" # uses masked IP address
|
144
|
+
# obj_to_cidr IPSocket.new("www.pl", 80) # uses socket
|
145
|
+
# obj_to_cidr IPAddr("10.0.0.1") # uses IPAddr object
|
146
|
+
# obj_to_cidr NetAddr::CIDR.create("10.0.0.1") # uses NetAddr object
|
147
|
+
# obj_to_cidr URI('http://www.pl/') # uses URI
|
148
|
+
# obj_to_cidr 'http://www.pl/' # uses extracted host string
|
149
|
+
#
|
150
|
+
# ==== Special symbols
|
151
|
+
#
|
152
|
+
# When symbol is passed to this method it tries to find out if it has special meaning.
|
153
|
+
# That allows you to create access rules in an easy way. For most of them you may
|
154
|
+
# also specify IP protocol version using +ipv4_+ or +ipv6_+ prefix.
|
155
|
+
#
|
156
|
+
# Known symbols are:
|
157
|
+
#
|
158
|
+
# <b>+:all+</b> (+:any+, +:anyone+, +:world+, +:internet+, +:net+, +:everything+, +:everyone+, +:everybody+, +:anybody+)
|
159
|
+
#
|
160
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
161
|
+
#
|
162
|
+
# Creates masked IP address that matches all networks:
|
163
|
+
# – 0.0.0.0/0
|
164
|
+
# – ::/0
|
165
|
+
#
|
166
|
+
# <b>+:broadcast+</b> (+:brd+)
|
167
|
+
#
|
168
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
169
|
+
#
|
170
|
+
# Creates masked IP address that matches generic broadcast address:
|
171
|
+
# – 255.255.255.255/32
|
172
|
+
# – ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128
|
173
|
+
#
|
174
|
+
# <b>+:local+</b> (+:localhost+, +:localdomain+, +:loopback+, +:lo+)
|
175
|
+
#
|
176
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
177
|
+
#
|
178
|
+
# Creates masked IP addresses that match localhost:
|
179
|
+
# – 127.0.0.1/8
|
180
|
+
# – ::1/128
|
181
|
+
#
|
182
|
+
# <b>+:auto+</b> (+:automatic+, +:linklocal+)
|
183
|
+
#
|
184
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
185
|
+
#
|
186
|
+
# Creates masked IP addresses that match automatically assigned address ranges:
|
187
|
+
# – 169.254.0.0/16
|
188
|
+
# – fe80::/10
|
189
|
+
#
|
190
|
+
# <b>+:private+</b> (+:intra+, +:intranet+, +:internal+)
|
191
|
+
#
|
192
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
193
|
+
#
|
194
|
+
# Creates masked IP addresses that match private ranges:
|
195
|
+
# – 10.0.0.0/8
|
196
|
+
# – 172.16.0.0/12
|
197
|
+
# – 192.168.0.0/16
|
198
|
+
# – 2001:10::/28
|
199
|
+
# – 2001:db8::/32
|
200
|
+
# – fc00::/7
|
201
|
+
# – fdde:9e1a:dc85:7374::/64
|
202
|
+
#
|
203
|
+
# <b>+:multicast+</b> (+:multi+, +:multiemission+)
|
204
|
+
#
|
205
|
+
# variants: +:ipv4_+ and +:ipv6_+
|
206
|
+
#
|
207
|
+
# Creates masked IP addresses that match multicast addresses ranges:
|
208
|
+
# – 224.0.0.0/4
|
209
|
+
# – ff00::/8
|
210
|
+
# – ff02::1:ff00:0/104
|
211
|
+
#
|
212
|
+
# <b>+:reserved+</b> (+:example+)
|
213
|
+
#
|
214
|
+
# variants: +:ipv4_+
|
215
|
+
#
|
216
|
+
# Creates masked IP addresses that match reserved addresses ranges:
|
217
|
+
# – 192.0.2.0/24
|
218
|
+
# – 128.0.0.0/16
|
219
|
+
# – 191.255.0.0/16
|
220
|
+
# – 192.0.0.0/24
|
221
|
+
# – 198.18.0.0/15
|
222
|
+
# – 223.255.255.0/24
|
223
|
+
# – 240.0.0.0/4
|
224
|
+
#
|
225
|
+
# <b>+:strange+</b> (+:unusual+, +:nonpublic+, +:unpublic+)
|
226
|
+
#
|
227
|
+
# Creates masked IP addressess that match the following sets (both IPv4 and IPv6):
|
228
|
+
# – :local
|
229
|
+
# – :auto
|
230
|
+
# – :private
|
231
|
+
# – :reserved
|
232
|
+
# – :multicast
|
233
|
+
|
234
|
+
def self.obj_to_cidr(*obj)
|
235
|
+
obj = obj.flatten
|
236
|
+
|
237
|
+
if obj.size == 1
|
238
|
+
obj = obj.first
|
239
|
+
else
|
240
|
+
ary = []
|
241
|
+
obj.each { |o| ary += obj_to_cidr(o) }
|
242
|
+
ary.flatten!
|
243
|
+
return ary
|
244
|
+
end
|
245
|
+
|
246
|
+
# NetAddr::CIDR - immediate generation
|
247
|
+
return [obj.dup] if obj.is_a?(NetAddr::CIDR)
|
248
|
+
|
249
|
+
# IPAccessList - immediate generation
|
250
|
+
return obj.to_a if obj.is_a?(self.class)
|
251
|
+
|
252
|
+
# NetAddr::Tree - immediate generation
|
253
|
+
return obj.dump.map { |addr| addr[:CIDR] } if obj.is_a?(NetAddr::Tree)
|
254
|
+
|
255
|
+
# number - immediate generation
|
256
|
+
return [NetAddr::CIDR.create(obj)] if obj.is_a?(Numeric)
|
257
|
+
|
258
|
+
# IPAddr - fetch IP/mask string
|
259
|
+
obj = obj.native.inspect.split[1].chomp('>')[5..-1] if obj.is_a?(IPAddr)
|
260
|
+
|
261
|
+
# object containing socket member (e.g. Net::HTTP) - fetch socket
|
262
|
+
if obj.respond_to?(:socket)
|
263
|
+
obj = obj.socket
|
264
|
+
elsif obj.respond_to?(:client_socket)
|
265
|
+
obj = obj.client_socket
|
266
|
+
elsif obj.instance_variable_defined?(:@socket)
|
267
|
+
obj = obj.instance_variable_get(:@socket)
|
268
|
+
end
|
269
|
+
obj = obj.io if (obj.respond_to?(:io) && obj.io.respond_to?(:getpeername))
|
270
|
+
|
271
|
+
# some file descriptor but not socket - fetch socket
|
272
|
+
obj = Socket.for_fd(obj.fileno) if (!obj.respond_to?(:getpeername) && obj.respond_to?(:fileno))
|
273
|
+
|
274
|
+
# Socket - immediate generation
|
275
|
+
if obj.respond_to?(:getpeername)
|
276
|
+
peeraddr = Socket.unpack_sockaddr_in(obj.getpeername).last.split('%').first
|
277
|
+
return [NetAddr::CIDR.create(peeraddr)]
|
278
|
+
end
|
279
|
+
|
280
|
+
# symbol - immediate generation
|
281
|
+
if obj.is_a?(Symbol)
|
282
|
+
case obj
|
283
|
+
when :ipv4_all, :ipv4_any, :ipv4_anyone, :ipv4_world, :ipv4_internet, :ipv4_net, :ipv4_everything, :ipv4_everyone, :ipv4_everybody, :ipv4_anybody
|
284
|
+
obj = [ "0.0.0.0/0" ]
|
285
|
+
when :ipv6_all, :ipv6_any, :ipv6_anyone, :ipv6_world, :ipv6_internet, :ipv6_net, :ipv6_everything, :ipv6_everyone, :ipv6_everybody, :ipv6_anybody
|
286
|
+
obj = [ "0.0.0.0/0", "::/0" ]
|
287
|
+
when :ipv4_broadcast, :ipv4_brd
|
288
|
+
obj = [ "255.255.255.255/32" ]
|
289
|
+
when :ipv6_broadcast, :ipv6_brd
|
290
|
+
obj = [ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" ]
|
291
|
+
when :ipv4_local, :ipv4_localhost, :ipv4_loopback, :ipv4_lo
|
292
|
+
obj = [ "127.0.0.1/8" ]
|
293
|
+
when :ipv6_local, :ipv6_localhost, :ipv6_loopback, :ipv6_lo
|
294
|
+
obj = [ "::1/128" ]
|
295
|
+
when :ipv4_auto, :ipv4_automatic, :ipv4_linklocal
|
296
|
+
obj = [ "169.254.0.0/16" ]
|
297
|
+
when :ipv6_auto, :ipv6_automatic, :ipv6_linklocal
|
298
|
+
obj = [ "fe80::/10" ]
|
299
|
+
when :ipv4_private, :ipv4_intra, :ipv4_intranet, :ipv4_internal
|
300
|
+
obj = [ "10.0.0.0/8",
|
301
|
+
"172.16.0.0/12",
|
302
|
+
"192.168.0.0/16" ]
|
303
|
+
when :ipv6_private, :ipv6_intra, :ipv6_intranet, :ipv6_internal, :ipv6_ula, :ipv6_unique
|
304
|
+
obj = [ "2001:10::/28",
|
305
|
+
"2001:db8::/32",
|
306
|
+
"fc00::/7",
|
307
|
+
"fdde:9e1a:dc85:7374::/64" ]
|
308
|
+
when :ipv4_multicast, :ipv4_multi, :ipv4_multiemission
|
309
|
+
obj = [ "224.0.0.0/4" ]
|
310
|
+
when :ipv6_multicast, :ipv6_multi, :ipv6_multiemission
|
311
|
+
obj = [ "ff00::/8",
|
312
|
+
"ff02::1:ff00:0/104" ]
|
313
|
+
when :ipv4_example, :ipv4_reserved
|
314
|
+
obj = [ "192.0.2.0/24",
|
315
|
+
"128.0.0.0/16",
|
316
|
+
"191.255.0.0/16",
|
317
|
+
"192.0.0.0/24",
|
318
|
+
"198.18.0.0/15",
|
319
|
+
"223.255.255.0/24",
|
320
|
+
"240.0.0.0/4" ]
|
321
|
+
when :all, :any, :anyone, :world, :internet, :net, :everything, :everyone, :everybody, :anybody
|
322
|
+
return obj_to_cidr(:ipv4_all,
|
323
|
+
:ipv6_all)
|
324
|
+
when :broadcast, :brd
|
325
|
+
return obj_to_cidr(:ipv4_broadcast,
|
326
|
+
:ipv6_broadcast)
|
327
|
+
when :local, :localhost, :localdomain, :loopback, :lo
|
328
|
+
return obj_to_cidr(:ipv4_local,
|
329
|
+
:ipv6_local)
|
330
|
+
when :auto, :automatic, :linklocal
|
331
|
+
return obj_to_cidr(:ipv4_auto,
|
332
|
+
:ipv6_auto)
|
333
|
+
when :private, :intra, :intranet, :internal
|
334
|
+
return obj_to_cidr(:ipv4_private,
|
335
|
+
:ipv6_private)
|
336
|
+
when :multicast, :multi, :multiemission
|
337
|
+
return obj_to_cidr(:ipv4_multicast,
|
338
|
+
:ipv6_multicast)
|
339
|
+
when :reserved, :example
|
340
|
+
return obj_to_cidr(:ipv4_example)
|
341
|
+
when :strange, :unusual, :nonpublic, :unpublic
|
342
|
+
return obj_to_cidr(:local,
|
343
|
+
:auto,
|
344
|
+
:private,
|
345
|
+
:reserved,
|
346
|
+
:multicast)
|
347
|
+
else
|
348
|
+
raise ArgumentError, "Provided symbol is unknown: #{obj.to_s}"
|
349
|
+
end
|
350
|
+
return obj.map { |addr| NetAddr::CIDR.create(addr) } if obj.is_a?(Array)
|
351
|
+
end
|
352
|
+
|
353
|
+
# URI or something that responds to host method - fetch string
|
354
|
+
obj = obj.host if obj.respond_to?(:host)
|
355
|
+
|
356
|
+
# IPAddrList - immediate generation
|
357
|
+
return obj.to_a if obj.class.name.to_sym == :IPAddrList
|
358
|
+
|
359
|
+
# string or similar - immediate generation
|
360
|
+
if obj.respond_to?(:to_s)
|
361
|
+
obj = obj.to_s
|
362
|
+
# URI
|
363
|
+
if obj =~ /^[^:]+:\/\/(.*)/
|
364
|
+
obj = $1.split('/').first
|
365
|
+
# IP in URI
|
366
|
+
if obj =~ /^\[([^\]]+)\]/
|
367
|
+
obj = $1
|
368
|
+
else
|
369
|
+
obj = obj.split(':').first
|
370
|
+
end
|
371
|
+
end
|
372
|
+
begin
|
373
|
+
obj = NetAddr::CIDR.create(obj.split('%').first)
|
374
|
+
rescue NetAddr::ValidationError
|
375
|
+
addresses = Resolv::getaddresses(obj)
|
376
|
+
addresses.map! do |addr|
|
377
|
+
begin
|
378
|
+
NetAddr::CIDR.create(addr.split('%').first)
|
379
|
+
rescue ArgumentError
|
380
|
+
nil
|
381
|
+
end
|
382
|
+
end
|
383
|
+
addresses.flatten!
|
384
|
+
addresses.compact!
|
385
|
+
return addresses
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
# should never happend
|
390
|
+
return obj.is_a?(NetAddr::CIDR) ? [obj.dup] : [NetAddr::CIDR.create(obj.to_s)]
|
391
|
+
end
|
392
|
+
|
393
|
+
# This method calls IPAccessList.obj_to_cidr
|
394
|
+
|
395
|
+
def obj_to_cidr(*args)
|
396
|
+
self.class.obj_to_cidr(*args)
|
397
|
+
end
|
398
|
+
|
399
|
+
# This method finds all matching addresses in the list
|
400
|
+
# and returns an array containing these addresses.
|
401
|
+
# If the optional block is supplied, each matching element
|
402
|
+
# is passed to it, and the block‘s result is stored
|
403
|
+
# in the output array.
|
404
|
+
#
|
405
|
+
# Ba aware that it may call the block for same object twice
|
406
|
+
# if you'll pass two matching addresses.
|
407
|
+
#
|
408
|
+
# See obj_to_cidr description for more info about arguments
|
409
|
+
# you may pass to it.
|
410
|
+
|
411
|
+
def grep(*args)
|
412
|
+
return [] if empty?
|
413
|
+
out_ary = []
|
414
|
+
addrs = obj_to_cidr(*args)
|
415
|
+
addrs.each do |addr|
|
416
|
+
m = included_cidr(addr)
|
417
|
+
out_ary.push( block_given? ? yield(m) : m) unless m.nil?
|
418
|
+
end
|
419
|
+
return out_ary
|
420
|
+
end
|
421
|
+
|
422
|
+
alias_method :search, :grep
|
423
|
+
|
424
|
+
# This method finds all addresses in the list that are
|
425
|
+
# equal to given addresses/netmasks and returns an array containing
|
426
|
+
# these addresses. It is intended to be used to operate on
|
427
|
+
# lists rather than to match IPs to them.
|
428
|
+
#
|
429
|
+
# If the optional block is supplied,
|
430
|
+
# each matching element is passed to it, and the block‘s
|
431
|
+
# result is stored in the output array.
|
432
|
+
#
|
433
|
+
# See obj_to_cidr description for more info about arguments
|
434
|
+
# you may pass to it.
|
435
|
+
|
436
|
+
def grep_exact(*args)
|
437
|
+
return [] if empty?
|
438
|
+
out_ary = []
|
439
|
+
addrs = obj_to_cidr(*args)
|
440
|
+
addrs.each do |addr|
|
441
|
+
m = included_cidr(addr)
|
442
|
+
if (m == addr)
|
443
|
+
out_ary.push( block_given? ? yield(m) : m)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
return out_ary
|
447
|
+
end
|
448
|
+
|
449
|
+
# This method adds new rule(s) to access list. By default
|
450
|
+
# elements are added to black list. If first or last argument
|
451
|
+
# is +:white+ or +:black+ then element is added to the specified
|
452
|
+
# list.
|
453
|
+
#
|
454
|
+
# If the given rule is exact (IP and mask) as pre-existent
|
455
|
+
# rule in the same access list then it is not added.
|
456
|
+
#
|
457
|
+
# You should avoid passing hostnames as arguments since
|
458
|
+
# DNS is not reliable and responses may change with time
|
459
|
+
# which may cause security flaws.
|
460
|
+
#
|
461
|
+
# Special case: some CIDR objects may carry information about
|
462
|
+
# access list they should belong to. If the last argument
|
463
|
+
# of this method does not specify access list and added rule
|
464
|
+
# is the kind of special CIDR containing information about
|
465
|
+
# assignment to some list then this extra sugar will be used
|
466
|
+
# in assignment instead of default +:black+. These special
|
467
|
+
# CIDR object are usualy result of passing IPAccessList
|
468
|
+
# as an argument. To be sure which access
|
469
|
+
# list will be altered always give its name when passing
|
470
|
+
# IPAccessList.
|
471
|
+
#
|
472
|
+
# See obj_to_cidr description for more info about arguments
|
473
|
+
# you may pass to it.
|
474
|
+
|
475
|
+
def add!(*args)
|
476
|
+
acl_list = nil
|
477
|
+
acl_list = args.shift if (args.first.is_a?(Symbol) && (args.first == :white || args.first == :black))
|
478
|
+
acl_list = args.pop if (args.last.is_a?(Symbol) && (args.last == :white || args.last == :black))
|
479
|
+
return nil if args.empty?
|
480
|
+
addrs = obj_to_cidr(*args)
|
481
|
+
addrs.each do |addr|
|
482
|
+
addr = addr.ipv4 if addr.ipv4_compliant?
|
483
|
+
add_list = acl_list.nil? ? addr.tag[:ACL] : acl_list # object with extra sugar
|
484
|
+
add_list = :black if add_list.nil?
|
485
|
+
exists = find_me(addr)
|
486
|
+
if exists.nil?
|
487
|
+
addr.tag[:Subnets] = []
|
488
|
+
addr.tag[:ACL] = add_list
|
489
|
+
add_to_tree(addr)
|
490
|
+
elsif exists.tag[:ACL] != add_list
|
491
|
+
exists.tag[:ACL] = :ashen
|
492
|
+
end
|
493
|
+
end
|
494
|
+
return nil
|
495
|
+
end
|
496
|
+
|
497
|
+
alias_method :add, :add!
|
498
|
+
|
499
|
+
# This method removes CIDR rules specified by the given
|
500
|
+
# objects containing IP information. It returns an array
|
501
|
+
# of removed CIDR rules.
|
502
|
+
#
|
503
|
+
# Make sure you will specify correct and exact netmasks
|
504
|
+
# in order to delete proper rules. This method will NOT
|
505
|
+
# remove rules that imprecisely match given address or rules
|
506
|
+
# that logically depends on specified rules, e.g.
|
507
|
+
# removing +192.168.0.0/16+ will leave +192.168.0.0/24+
|
508
|
+
# untouched. To create access exceptions for some
|
509
|
+
# ranges and/or addresses whitelist them using permit
|
510
|
+
# method. This method removes rules matching exact addresses/masks.
|
511
|
+
#
|
512
|
+
# If the first or last argument is a symbol and it's +:white+
|
513
|
+
# or +:black+ then the specified rule will be removed
|
514
|
+
# only from the given (white or black) list. If the list is
|
515
|
+
# not specified the rule will be removed from both lists.
|
516
|
+
#
|
517
|
+
# Special case: some CIDR objects may carry information about
|
518
|
+
# access list they should belong to. If the last argument
|
519
|
+
# of this method does not specify access list and added rule
|
520
|
+
# is the kind of special CIDR containing information about
|
521
|
+
# assignment to some list then this extra sugar will be used
|
522
|
+
# while removing. These special CIDR objects are usualy result
|
523
|
+
# of passing IPAccessList as an argument. To be sure which access
|
524
|
+
# list will be altered always give its name when passing
|
525
|
+
# IPAccessList.
|
526
|
+
#
|
527
|
+
# You should avoid passing hostnames as arguments since
|
528
|
+
# DNS is not reliable and responses may change with time
|
529
|
+
# which may cause security flaws.
|
530
|
+
#
|
531
|
+
# See obj_to_cidr description for more info about arguments
|
532
|
+
# you may pass to it.
|
533
|
+
|
534
|
+
def delete!(*args)
|
535
|
+
acl_list = nil
|
536
|
+
acl_list = args.shift if (args.first.is_a?(Symbol) && (args.first == :white || args.first == :black))
|
537
|
+
acl_list = args.pop if (args.last.is_a?(Symbol) && (args.last == :white || args.last == :black))
|
538
|
+
removed = []
|
539
|
+
return removed if (args.empty? || empty?)
|
540
|
+
addrs = obj_to_cidr(*args)
|
541
|
+
addrs.each do |addr|
|
542
|
+
addr = addr.ipv4 if addr.ipv4_compliant?
|
543
|
+
exists = find_me(addr)
|
544
|
+
unless exists.nil?
|
545
|
+
src_list = acl_list.nil? ? addr.tag[:ACL] : acl_list
|
546
|
+
src_list = nil if src_list == :ashen
|
547
|
+
ex_list = exists.tag[:ACL]
|
548
|
+
parent = exists.tag[:Parent]
|
549
|
+
children = exists.tag[:Subnets]
|
550
|
+
if (!src_list.nil? && ex_list == :ashen)
|
551
|
+
removed.push exists.safe_dup(:Subnets, :Parent)
|
552
|
+
exists.tag[:ACL] = (src_list == :black) ? :white : :black
|
553
|
+
elsif (src_list.nil? || ex_list == src_list)
|
554
|
+
removed.push exists.safe_dup(:Subnets, :Parent)
|
555
|
+
parent.tag[:Subnets].delete(exists)
|
556
|
+
children.each { |childaddr| add_to_parent(childaddr, parent) }
|
557
|
+
end
|
558
|
+
end # if found
|
559
|
+
end # args.each
|
560
|
+
|
561
|
+
return removed
|
562
|
+
end
|
563
|
+
|
564
|
+
alias_method :del!, :delete!
|
565
|
+
alias_method :delete, :delete!
|
566
|
+
|
567
|
+
# Adds IP addresses in given object(s) to white list if called
|
568
|
+
# with at least one argument. Returns white list if called
|
569
|
+
# without arguments (array of CIDR objects).
|
570
|
+
#
|
571
|
+
# You should avoid passing hostnames as arguments since
|
572
|
+
# DNS is not reliable and responses may change with time
|
573
|
+
# which may cause security flaws.
|
574
|
+
|
575
|
+
def whitelist(*args)
|
576
|
+
args.empty? ? to_a(:white) : add!(:white, *args)
|
577
|
+
end
|
578
|
+
|
579
|
+
alias_method :add_white, :whitelist
|
580
|
+
alias_method :allow, :whitelist
|
581
|
+
alias_method :permit, :whitelist
|
582
|
+
|
583
|
+
# Adds IP addresses in given object(s) to black list if called
|
584
|
+
# with at least one argument. Returns black list if called
|
585
|
+
# without arguments (array of CIDR objects).
|
586
|
+
#
|
587
|
+
# You should avoid passing hostnames as arguments since
|
588
|
+
# DNS is not reliable and responses may change with time
|
589
|
+
# which may cause security flaws.
|
590
|
+
|
591
|
+
def blacklist(*args)
|
592
|
+
args.empty? ? to_a(:black) : add!(:black, *args)
|
593
|
+
end
|
594
|
+
|
595
|
+
alias_method :add_black, :blacklist
|
596
|
+
alias_method :deny, :blacklist
|
597
|
+
alias_method :block, :blacklist
|
598
|
+
|
599
|
+
# This method returns an array of matching CIDR objects
|
600
|
+
# for the given objects containing IP information.
|
601
|
+
#
|
602
|
+
# It is designed to browse rules, NOT to check access. To do access
|
603
|
+
# check use IPAccessList#granted and IPAccessList#denied methods.
|
604
|
+
#
|
605
|
+
# See obj_to_cidr description for more info about arguments
|
606
|
+
# you may pass to it.
|
607
|
+
#
|
608
|
+
# Examples:
|
609
|
+
# access = IPAccessList.new '127.0.0.1/8' # blacklisted local IP
|
610
|
+
# access.included '127.0.0.1' # returns [127.0.0.0/8]
|
611
|
+
# access.included '127.0.0.1/24' # returns [127.0.0.0/8]
|
612
|
+
# access.included '127.0.0.1'/8 # returns [127.0.0.0/8]
|
613
|
+
# access.included '127.0.1.2'/8 # returns [127.0.0.0/8]
|
614
|
+
|
615
|
+
def included(*args)
|
616
|
+
found = []
|
617
|
+
return found if empty?
|
618
|
+
addrs = obj_to_cidr(*args)
|
619
|
+
return found if addrs.empty?
|
620
|
+
addrs.each do |addr|
|
621
|
+
rule = included_cidr(addr)
|
622
|
+
found.push(rule) unless rule.nil?
|
623
|
+
end
|
624
|
+
return found
|
625
|
+
end
|
626
|
+
|
627
|
+
# This method returns +true+ if ALL
|
628
|
+
# of the given objects containing IP information
|
629
|
+
# match some rules. Otherwise it returns +false+.
|
630
|
+
#
|
631
|
+
# It is designed to browse rules, NOT to check access. To do access
|
632
|
+
# check use IPAccessList#granted and IPAccessList#denied methods.
|
633
|
+
#
|
634
|
+
# See obj_to_cidr description for more info about arguments
|
635
|
+
# you may pass to it.
|
636
|
+
|
637
|
+
def include?(*args)
|
638
|
+
return false if empty?
|
639
|
+
addrs = obj_to_cidr(*args)
|
640
|
+
return false if addrs.empty?
|
641
|
+
addrs.each do |addr|
|
642
|
+
rule = included_cidr(addr)
|
643
|
+
return false if rule.nil?
|
644
|
+
end
|
645
|
+
return true
|
646
|
+
end
|
647
|
+
|
648
|
+
alias_method :include_all?, :include?
|
649
|
+
|
650
|
+
# This method returns first matching CIDR rule from
|
651
|
+
# the given objects containing IP information.
|
652
|
+
# Otherwise it returns +nil+.
|
653
|
+
#
|
654
|
+
# It is designed to browse rules, NOT to check access. To do access
|
655
|
+
# check use IPAccessList#granted and IPAccessList#denied methods.
|
656
|
+
#
|
657
|
+
# See obj_to_cidr description for more info about arguments
|
658
|
+
# you may pass to it.
|
659
|
+
|
660
|
+
def included_first(*args)
|
661
|
+
return nil if empty?
|
662
|
+
addrs = obj_to_cidr(*args)
|
663
|
+
return nil if addrs.empty?
|
664
|
+
addrs.each do |addr|
|
665
|
+
rule = included_cidr(addr)
|
666
|
+
return rule unless rule.nil?
|
667
|
+
end
|
668
|
+
return nil
|
669
|
+
end
|
670
|
+
|
671
|
+
# This method returns +true+ if at least one of
|
672
|
+
# the given objects containing IP information
|
673
|
+
# matches rule from the list. Otherwise it returns +false+.
|
674
|
+
#
|
675
|
+
# It is designed to browse rules, NOT to check access. To do access
|
676
|
+
# check use IPAccessList#granted and IPAccessList#denied methods.
|
677
|
+
#
|
678
|
+
# See obj_to_cidr description for more info about arguments
|
679
|
+
# you may pass to it.
|
680
|
+
|
681
|
+
def include_one?(*args)
|
682
|
+
not included_first.nil?
|
683
|
+
end
|
684
|
+
|
685
|
+
# This method returns matching CIDR rule if the given IP address
|
686
|
+
# (expressed as CIDR object) is on the list. Otherwise it returns +nil+.
|
687
|
+
|
688
|
+
def included_cidr(addr)
|
689
|
+
addr = addr.ipv4 if addr.ipv4_compliant?
|
690
|
+
root = addr.version == 4 ? @v4_root : @v6_root
|
691
|
+
return nil if root.tag[:Subnets].empty?
|
692
|
+
found = nil
|
693
|
+
found = find_me(addr)
|
694
|
+
found = find_parent(addr) if found.nil?
|
695
|
+
return nil if (found.nil? || found.hash == root.hash || !found.matches?(addr))
|
696
|
+
return found.safe_dup(:Subnets, :Parent)
|
697
|
+
end
|
698
|
+
|
699
|
+
# This method returns +true+ if the given IP address
|
700
|
+
# (expressed as CIDR object) matches some rule.
|
701
|
+
# Otherwise it returns +false+.
|
702
|
+
#
|
703
|
+
# It is designed to browse rules, NOT to check access. To do access
|
704
|
+
# check use granted_cidr and denied_cidr methods.
|
705
|
+
|
706
|
+
def include_cidr?(addr)
|
707
|
+
not included_cidr(addr).nil?
|
708
|
+
end
|
709
|
+
|
710
|
+
# This method returns an array containing CDIR objects that
|
711
|
+
# are result of finding IP rules given in the array.
|
712
|
+
#
|
713
|
+
# It is designed to browse rules, NOT to check access. To do access
|
714
|
+
# check use granted_cidr and denied_cidr methods.
|
715
|
+
#
|
716
|
+
# You should avoid passing hostnames as arguments since
|
717
|
+
# DNS is not reliable and responses may change with time
|
718
|
+
# which may cause security flaws.
|
719
|
+
|
720
|
+
def rule_exists(list, *args)
|
721
|
+
found = []
|
722
|
+
return found if empty?
|
723
|
+
addrs = obj_to_cidr(*args)
|
724
|
+
return found if addrs.empty?
|
725
|
+
addrs.each do |addr|
|
726
|
+
rule = rule_exists_cidr(list, addr)
|
727
|
+
found.push(rule) unless rule.nil?
|
728
|
+
end
|
729
|
+
return found
|
730
|
+
end
|
731
|
+
private :rule_exists
|
732
|
+
|
733
|
+
# This method returns CDIR object that
|
734
|
+
# equals given IP rule in the given list.
|
735
|
+
# It returns +nil+ if such rule doesn't
|
736
|
+
# exists.
|
737
|
+
#
|
738
|
+
# It is designed to check rules, NOT access. To do access
|
739
|
+
# check use granted_cidr and denied_cidr methods.
|
740
|
+
|
741
|
+
def rule_exists_cidr(list, addr)
|
742
|
+
addr = addr.ipv4 if addr.ipv4_compliant?
|
743
|
+
root = addr.version == 4 ? @v4_root : @v6_root
|
744
|
+
return nil if root.tag[:Subnets].empty?
|
745
|
+
found = find_me(addr)
|
746
|
+
if (found.nil? || found.hash == root.hash ||
|
747
|
+
(found.tag[:ACL] != list && found.tag[:ACL] != :ashen))
|
748
|
+
return nil
|
749
|
+
else
|
750
|
+
return found.safe_dup(:Subnets, :Parent)
|
751
|
+
end
|
752
|
+
end
|
753
|
+
private :rule_exists_cidr
|
754
|
+
|
755
|
+
# This method returns an array containing CDIR objects that
|
756
|
+
# are result of finding given IP rules in the black list.
|
757
|
+
#
|
758
|
+
# It is designed to browse rules, NOT to check access. To do access
|
759
|
+
# check use IPAccessList#granted and IPAccessList#denied methods.
|
760
|
+
#
|
761
|
+
# See obj_to_cidr description for more info about arguments
|
762
|
+
# you may pass to it.
|
763
|
+
|
764
|
+
def find_blacklist_rules(*args)
|
765
|
+
rule_exists(:black, *args)
|
766
|
+
end
|
767
|
+
|
768
|
+
alias_method :find_blacklist_rule, :find_blacklist_rules
|
769
|
+
|
770
|
+
# This method returns CDIR object that
|
771
|
+
# equals given IP rule in the black list.
|
772
|
+
# Otherwise it returns +nil+.
|
773
|
+
#
|
774
|
+
# It is designed to browse rules, NOT to check access. To do access
|
775
|
+
# check use granted_cidr and denied_cidr methods.
|
776
|
+
|
777
|
+
def find_blacklist_rule_cidr(addr)
|
778
|
+
rule_exists_cidr(:black, addr)
|
779
|
+
end
|
780
|
+
|
781
|
+
# This method returns +true+ if ALL of the given
|
782
|
+
# IP addresses/masks are present in the black list.
|
783
|
+
#
|
784
|
+
# It is designed to browse rules, NOT to check access. To do access
|
785
|
+
# check use IPAccessList#granted and IPAccessList#denied methods.
|
786
|
+
#
|
787
|
+
# See obj_to_cidr description for more info about arguments
|
788
|
+
# you may pass to it.
|
789
|
+
|
790
|
+
def blacklist_rules_exist?(*args)
|
791
|
+
addrs = obj_to_cidr(*args)
|
792
|
+
return found if addrs.empty?
|
793
|
+
addrs.each do |addr|
|
794
|
+
rule = rule_exists_cidr(:black, addr)
|
795
|
+
return false if rule.nil?
|
796
|
+
end
|
797
|
+
return true
|
798
|
+
end
|
799
|
+
|
800
|
+
alias_method :blacklist_rule_exists?, :blacklist_rules_exist?
|
801
|
+
|
802
|
+
# This method returns +true+ if the given
|
803
|
+
# IP address is on the IP rules black list.
|
804
|
+
#
|
805
|
+
# It is designed to browse rules, NOT to check access. To do access
|
806
|
+
# check use IPAccessList#granted and IPAccessList#denied methods.
|
807
|
+
|
808
|
+
def blacklist_rule_exists_cidr?(addr)
|
809
|
+
not rule_exists_cidr(:black, addr).nil?
|
810
|
+
end
|
811
|
+
|
812
|
+
# This method returns an array containing CDIR objects that
|
813
|
+
# is result of finding given IP rules in the white list.
|
814
|
+
#
|
815
|
+
# It is designed to browse rules, NOT to check access. To do access
|
816
|
+
# check use IPAccessList#granted and IPAccessList#denied methods.
|
817
|
+
#
|
818
|
+
# See obj_to_cidr description for more info about arguments
|
819
|
+
# you may pass to it.
|
820
|
+
|
821
|
+
def find_whitelist_rules(*args)
|
822
|
+
rule_exists(:white, *args)
|
823
|
+
end
|
824
|
+
|
825
|
+
alias_method :find_blacklist_rule, :find_blacklist_rules
|
826
|
+
|
827
|
+
# This method returns CDIR object that
|
828
|
+
# equals given IP rule in the white list.
|
829
|
+
#
|
830
|
+
# It is designed to check rules, NOT access. To do access
|
831
|
+
# check use allowed_cidr and denied_cidr methods.
|
832
|
+
|
833
|
+
def find_whitelist_rule_cidr(addr)
|
834
|
+
rule_exists_cidr(:white, addr)
|
835
|
+
end
|
836
|
+
|
837
|
+
# This method returns +true+ if ALL of the given
|
838
|
+
# IP addresses are on the white list.
|
839
|
+
#
|
840
|
+
# It is designed to check rules, NOT access. To do access
|
841
|
+
# check use allowed and denied methods.
|
842
|
+
#
|
843
|
+
# See obj_to_cidr description for more info about arguments
|
844
|
+
# you may pass to it.
|
845
|
+
|
846
|
+
def whitelist_rules_exist?(*args)
|
847
|
+
addrs = obj_to_cidr(*args)
|
848
|
+
return found if addrs.empty?
|
849
|
+
addrs.each do |addr|
|
850
|
+
rule = rule_exists_cidr(:white, addr)
|
851
|
+
return false if rule.nil?
|
852
|
+
end
|
853
|
+
return true
|
854
|
+
end
|
855
|
+
|
856
|
+
# This method returns +true+ if the given
|
857
|
+
# IP address is on the IP rules white list.
|
858
|
+
#
|
859
|
+
# It is designed to check rules, NOT access. To do access
|
860
|
+
# check use allowed and denied methods.
|
861
|
+
|
862
|
+
def whitelist_rule_exists_cidr?(addr)
|
863
|
+
not rule_exists_cidr(:white, addr).nil?
|
864
|
+
end
|
865
|
+
|
866
|
+
# This method returns CIDR rule that is the same
|
867
|
+
# as given IP and mask. It returns copy
|
868
|
+
# of a rule that query matches (CIDR object) or +nil+
|
869
|
+
# if no match was found.
|
870
|
+
#
|
871
|
+
# Examples:
|
872
|
+
# access = IPAccessList.new '127.0.0.1/8' # blacklisted local IP
|
873
|
+
# access.find '127.0.0.1' # returns nil
|
874
|
+
# access.find '127.0.0.1/24' # returns nil
|
875
|
+
# access.find '127.0.0.1'/8 # returns CIDR: 127.0.0.0/8
|
876
|
+
# access.find '127.0.1.2'/8 # returns CIDR: 127.0.0.0/8
|
877
|
+
#
|
878
|
+
# If you want simpler or more fancy search in rules
|
879
|
+
# (e.g. without need to specify mask or with ability to
|
880
|
+
# check many rules at one time) use methods like
|
881
|
+
# find_blacklist_rules or find_whitelist_rules.
|
882
|
+
#
|
883
|
+
# It is designed to browse rules, NOT to check access. To do access
|
884
|
+
# check use IPAccessList#granted and IPAccessList#denied methods.
|
885
|
+
#
|
886
|
+
# See obj_to_cidr description for more info about argument
|
887
|
+
# you may pass to it. Be aware that in case of name or special
|
888
|
+
# symbol given as an address only first result will be used and
|
889
|
+
# it will probably do not match because lack of proper netmask.
|
890
|
+
|
891
|
+
def find(addr)
|
892
|
+
return nil if empty?
|
893
|
+
addr = obj_to_cidr(addr)
|
894
|
+
return nil if addr.empty?
|
895
|
+
addr = addr.first
|
896
|
+
addr = addr.ipv4 if addr.ipv4_compliant?
|
897
|
+
root = addr.version == 4 ? @v4_root : @v6_root
|
898
|
+
return nil if root.tag[:Subnets].empty?
|
899
|
+
return super(addr)
|
900
|
+
end
|
901
|
+
|
902
|
+
# This method returns a hash containing CIDR object of
|
903
|
+
# given address named +:IP+ and CIDR object of the matching rule
|
904
|
+
# named +:Rule+ if the given CIDR contains blacklisted
|
905
|
+
# and not whitelisted address. Otherwise it returns an empty
|
906
|
+
# hash.
|
907
|
+
#
|
908
|
+
# It should be used to check access for one IP. It is
|
909
|
+
# recommended to use it in low-level routines.
|
910
|
+
#
|
911
|
+
# To not create copy of object when reporting rule
|
912
|
+
# but to use reference to original entry you may set
|
913
|
+
# second argument +true+. Use this with caution since
|
914
|
+
# modifying returned object may affect internal
|
915
|
+
# structure of access list.
|
916
|
+
|
917
|
+
def denied_cidr(addr, nodup=false)
|
918
|
+
addr = addr.ipv4 if addr.ipv4_compliant?
|
919
|
+
root = addr.version == 4 ? @v4_root : @v6_root
|
920
|
+
list = root
|
921
|
+
return nil if list.tag[:Subnets].length.zero?
|
922
|
+
|
923
|
+
until (li = NetAddr.cidr_find_in_list(addr, list.tag[:Subnets])).nil?
|
924
|
+
if li.is_a?(Integer)
|
925
|
+
li = list.tag[:Subnets][li]
|
926
|
+
break
|
927
|
+
else
|
928
|
+
if li.tag[:ACL] == :black
|
929
|
+
list = li
|
930
|
+
else
|
931
|
+
break
|
932
|
+
end
|
933
|
+
end
|
934
|
+
end
|
935
|
+
|
936
|
+
ret = {}
|
937
|
+
li = list if li.nil?
|
938
|
+
if (!li.nil? && li.tag[:ACL] == :black && li.matches?(addr))
|
939
|
+
if nodup
|
940
|
+
rule = li
|
941
|
+
addr = addr
|
942
|
+
else
|
943
|
+
rule = li.safe_dup(:Subnets, :Parent)
|
944
|
+
addr = addr.safe_dup
|
945
|
+
end
|
946
|
+
ret[:IP] = addr
|
947
|
+
ret[:Rule] = rule
|
948
|
+
end
|
949
|
+
return ret
|
950
|
+
end
|
951
|
+
|
952
|
+
# This method returns +true+ if the given CIDR contains
|
953
|
+
# blacklisted and not whitelisted address. Otherwise
|
954
|
+
# it returns +false+.
|
955
|
+
#
|
956
|
+
# It should be used to check access for one IP. It is recommended
|
957
|
+
# to use it in low-level routines.
|
958
|
+
|
959
|
+
def denied_cidr?(addr)
|
960
|
+
not denied_cidr(addr, true).empty?
|
961
|
+
end
|
962
|
+
|
963
|
+
# This method checks if access for IP or IPs is denied.
|
964
|
+
# It returns an array of hashes containing tested CIDR
|
965
|
+
# objects (named +:IP+) and rules objects (named +:Rule+).
|
966
|
+
# Pair is present in output if given IP address matches
|
967
|
+
# black list rules and noesn't match white list rules.
|
968
|
+
#
|
969
|
+
# See obj_to_cidr description for more info about arguments
|
970
|
+
# you may pass to it.
|
971
|
+
#
|
972
|
+
# It should be used to check access for many IP addresses
|
973
|
+
# and/or address(-es) that are not necessarily represented
|
974
|
+
# by CIDR objects.
|
975
|
+
#
|
976
|
+
# You should avoid passing hostnames as arguments since
|
977
|
+
# DNS is not reliable and responses may change with time
|
978
|
+
# which may cause security flaws.
|
979
|
+
#
|
980
|
+
# To not create copy of objects when reporting rules
|
981
|
+
# but to use reference to original entries you may set
|
982
|
+
# last argument +true+. Use this with caution since
|
983
|
+
# modifying returned object may affect internal
|
984
|
+
# structure of access list.
|
985
|
+
|
986
|
+
def denied(*args)
|
987
|
+
found = []
|
988
|
+
return found if empty?
|
989
|
+
nodup = args.last.is_a?(TrueClass) ? args.pop : false
|
990
|
+
addrs = obj_to_cidr(*args)
|
991
|
+
addrs.each do |addr|
|
992
|
+
pair = denied_cidr(addr, nodup)
|
993
|
+
found.push(pair) unless pair.empty?
|
994
|
+
end
|
995
|
+
return found
|
996
|
+
end
|
997
|
+
|
998
|
+
# This method returns +true+ if at least one of given CIDR
|
999
|
+
# objects matches black list rules and doesn't match white
|
1000
|
+
# list rules. Otherwise it returns +false+.
|
1001
|
+
#
|
1002
|
+
# See obj_to_cidr description for more info about arguments
|
1003
|
+
# you may pass to it.
|
1004
|
+
|
1005
|
+
def denied?(*args)
|
1006
|
+
args.push true
|
1007
|
+
not denied(*args).empty?
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
alias_method :denied_one?, :denied?
|
1011
|
+
alias_method :denied_one_of?, :denied?
|
1012
|
+
|
1013
|
+
# This method returns given CIDR object
|
1014
|
+
# if the given CIDR is not blacklisted or whitelisted.
|
1015
|
+
# Otherwise it returns +nil+.
|
1016
|
+
#
|
1017
|
+
# It should be used to check access for one IP. It is recommended
|
1018
|
+
# to use it in low-level routines.
|
1019
|
+
|
1020
|
+
def granted_cidr(addr)
|
1021
|
+
denied_cidr(addr, true).empty? ? addr : nil
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
# This method returns +true+ if the given CIDR is not
|
1025
|
+
# blacklisted or whitelisted. Otherwise it returns +false+.
|
1026
|
+
#
|
1027
|
+
# It should be used to check access for one IP. It is
|
1028
|
+
# recommended to use it in low-level routines.
|
1029
|
+
|
1030
|
+
def granted_cidr?(addr)
|
1031
|
+
denied_cidr(addr, true).empty?
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
# This method returns an array of the given CIDR objects that
|
1035
|
+
# don't match black list rules or match white list rules.
|
1036
|
+
#
|
1037
|
+
# See obj_to_cidr description for more info about arguments
|
1038
|
+
# you may pass to it.
|
1039
|
+
#
|
1040
|
+
# It should be used to check access for many IP addresses
|
1041
|
+
# and/or address(-es) that are not necessarily represented
|
1042
|
+
# by CIDR objects.
|
1043
|
+
#
|
1044
|
+
# You should avoid passing hostnames as arguments since
|
1045
|
+
# DNS is not reliable and responses may change with time
|
1046
|
+
# which may cause security flaws.
|
1047
|
+
|
1048
|
+
def granted(*args)
|
1049
|
+
found = []
|
1050
|
+
return found if empty?
|
1051
|
+
args = obj_to_cidr(*args)
|
1052
|
+
args.each do |addr|
|
1053
|
+
rule = denied_cidr(addr, true)
|
1054
|
+
found.push(addr) if rule.empty?
|
1055
|
+
end
|
1056
|
+
return found
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
# This method returns +true+ if all of given CIDR
|
1060
|
+
# objects are not blacklisted or are whitelisted.
|
1061
|
+
# Otherwise it returns +false+.
|
1062
|
+
#
|
1063
|
+
# See obj_to_cidr description for more info about arguments
|
1064
|
+
# you may pass to it.
|
1065
|
+
#
|
1066
|
+
# You should avoid passing hostnames as arguments since
|
1067
|
+
# DNS is not reliable and responses may change with time
|
1068
|
+
# which may cause security flaws.
|
1069
|
+
|
1070
|
+
def granted?(*args)
|
1071
|
+
args.push true
|
1072
|
+
denied(*args).empty?
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
alias_method :granted_one?, :granted?
|
1076
|
+
alias_method :granted_one_of?, :granted?
|
1077
|
+
|
1078
|
+
# Returns new instance containing elements from this object
|
1079
|
+
# and objects passed as an argument. If objects contain IP
|
1080
|
+
# information but it's impossible to obtain whether they
|
1081
|
+
# relate to black or white list, then blacklisting is assumed.
|
1082
|
+
#
|
1083
|
+
# See obj_to_cidr description for more info about arguments
|
1084
|
+
# you may pass to it.
|
1085
|
+
|
1086
|
+
def +(*args)
|
1087
|
+
obj = self.class.new(self)
|
1088
|
+
obj.add!(*args)
|
1089
|
+
return obj
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
# Returns new list with removed CIDR objects which are exactly the same as objects passed as an argument.
|
1093
|
+
#
|
1094
|
+
# See obj_to_cidr description for more info about arguments
|
1095
|
+
# you may pass to it.
|
1096
|
+
|
1097
|
+
def -(*args)
|
1098
|
+
self_copy = self.class.new(self)
|
1099
|
+
self_copy.delete(*args)
|
1100
|
+
return self_copy
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
# Returns list of addresses and masks as a string with elements joined using space or given string.
|
1104
|
+
|
1105
|
+
def join(sep=' ')
|
1106
|
+
dump.map do |obj|
|
1107
|
+
obj[:CIDR].to_s
|
1108
|
+
end.join(sep)
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
# Remove all elements.
|
1112
|
+
|
1113
|
+
def clear!
|
1114
|
+
remove!('0.0.0.0/0')
|
1115
|
+
remove!('::/0')
|
1116
|
+
end
|
1117
|
+
|
1118
|
+
# This method returns +true+ if the list is empty.
|
1119
|
+
|
1120
|
+
def empty?
|
1121
|
+
@v4_root.tag[:Subnets].empty? &&
|
1122
|
+
@v6_root.tag[:Subnets].empty?
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
# This operator calls add! method.
|
1126
|
+
|
1127
|
+
def <<(*args)
|
1128
|
+
add!(*args)
|
1129
|
+
return self
|
1130
|
+
end
|
1131
|
+
|
1132
|
+
# This method returns an array of CIDR objects belonging
|
1133
|
+
# to given access list. If no list is specified it returns
|
1134
|
+
# an array containing all lists. It preserves access list
|
1135
|
+
# information in copied objects.
|
1136
|
+
|
1137
|
+
def dump_flat_list(parent, type=nil)
|
1138
|
+
list = []
|
1139
|
+
parent.tag[:Subnets].each do |entry|
|
1140
|
+
if (type.nil? || entry.tag[:ACL] == type || entry.tag[:ACL] == :ashen)
|
1141
|
+
list.push(entry)
|
1142
|
+
end
|
1143
|
+
if (entry.tag[:Subnets].length > 0)
|
1144
|
+
list.concat dump_flat_list(entry, type)
|
1145
|
+
end
|
1146
|
+
end
|
1147
|
+
list.map { |cidr| cidr.safe_dup(:Subnets, :Parent) }
|
1148
|
+
return list
|
1149
|
+
end
|
1150
|
+
private :dump_flat_list
|
1151
|
+
|
1152
|
+
# This method produces array of CIDR objects that
|
1153
|
+
# belong to an access list specified by type (:white or :black).
|
1154
|
+
# If no type is given it returns all entries. It preserves
|
1155
|
+
# access list assignment information in CIDR copies.
|
1156
|
+
|
1157
|
+
def to_a(type=nil)
|
1158
|
+
dump_flat_list(@v4_root, type) +
|
1159
|
+
dump_flat_list(@v6_root, type)
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
# This method shows internal tree of CIDR objects marked
|
1163
|
+
# with access list they belong to. While interpreting it
|
1164
|
+
# you should be aware that access for tested IP will not
|
1165
|
+
# be denied if black list rule has at least one whitelisted,
|
1166
|
+
# preceding rule in the path that leads to it. You may
|
1167
|
+
# also notice doubled entries sometimes. That happens
|
1168
|
+
# in case when the same rule is belongs to both:
|
1169
|
+
# black list and white list.
|
1170
|
+
|
1171
|
+
def show()
|
1172
|
+
printed = "IPv4 Tree\n---------\n"
|
1173
|
+
list4 = dump_children(@v4_root)
|
1174
|
+
list6 = dump_children(@v6_root)
|
1175
|
+
|
1176
|
+
list4.each do |entry|
|
1177
|
+
cidr = entry[:CIDR]
|
1178
|
+
depth = entry[:Depth]
|
1179
|
+
alist = cidr.tag[:ACL]
|
1180
|
+
indent = depth.zero? ? "" : " " * (depth*3)
|
1181
|
+
if alist == :ashen
|
1182
|
+
printed << "[black] #{indent}#{cidr.desc}\n"
|
1183
|
+
printed << "[white] #{indent}#{cidr.desc}\n"
|
1184
|
+
else
|
1185
|
+
alist = cidr.tag[:ACL].nil? ? "[undef]" : "[#{cidr.tag[:ACL]}]"
|
1186
|
+
printed << "#{alist} #{indent}#{cidr.desc}\n"
|
1187
|
+
end
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
printed << "\nIPv6 Tree\n---------\n" if list6.length.nonzero?
|
1191
|
+
list6.each do |entry|
|
1192
|
+
cidr = entry[:CIDR]
|
1193
|
+
depth = entry[:Depth]
|
1194
|
+
alist = cidr.tag[:ACL]
|
1195
|
+
indent = depth.zero? ? "" : " " * (depth*3)
|
1196
|
+
if alist == :ashen
|
1197
|
+
printed << "[black] #{indent}#{cidr.desc(:Short => true)}\n"
|
1198
|
+
printed << "[white] #{indent}#{cidr.desc(:Short => true)}\n"
|
1199
|
+
else
|
1200
|
+
alist = cidr.tag[:ACL].nil? ? "[undef]" : "[#{cidr.tag[:ACL]}]"
|
1201
|
+
printed << "#{alist} #{indent}#{cidr.desc(:Short => true)}\n"
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
return printed
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
end # class IPAccessList
|
1208
|
+
|
1209
|
+
|