ipaccess 0.0.2
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.
- 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
|
+
|