Class: IPAccess::List::Check
- Inherits:
-
IPAccess::List
- Object
- NetAddr::Tree
- IPAccess::List
- IPAccess::List::Check
- Defined in:
- lib/ipaccess/ip_access_check.rb
Overview
This class maintains a simple access list containing rules and methods for checking access.
Rules management
Use instance methods to add or remove rules from the list. Both IPv4 and IPv6 addresses are supported. See IPAccess.cidrs for available formats of addresses.
Checking access
To check an access you may call methods that belong to this list.
There are different variants of this methods for different IP representations. That's because speed is important here. If you have a socket object you want to test then you should use a method that checks sockets. If your IP is in a text format you may want to use a method that checks IP addresses written as strings.
Exceptions
Access checking methods throw exceptions that are
kind of IPAccessDenied or derivative. You may also set
the exception class using exception attribute
of instances. Each exception contain
IP address, rule that matched, diagnostic message
and an optional object that points to so
called originator described below. See IPAccessDenied
to see all attributes present in an exception.
Accessing original object
You can pass an optional object to almost all
of the access checking methods. It usually will be
passed as the last argument called originator.
The originator is intended to be used as a
helpful reference to original object for which an
access is checked.
You may want to ask why there is a need for originator when some object is tested already. The problem is that not just sockets can raise exceptions. If you want your program to block access before socket is even created (for outgoing traffic) then you may validate IP earlier. There are also some situations when you want to test something that represents network object's IP but it's not related to network object itself. For example, imagine that your program creates objects for HTTP sessions. In your HTTP class you may add some access checks but the tested object will be a socket. In that case you may want to pass HTTP object to an access checking method as the originator while performing access check on a socket.
Originator is transported within an exception
so you can use it in rescue section to send some data
or do other stuff before closing network
object. In case of patched network objects and
special variants of network classes that
this library also provides, you may also find
:opened_on_deny option helpful to achieve that.
In case of general purpose method like check you cannot pass the originator because it uses variant list of arguments of different kinds – in that case however the originators will be established using original, checked objects. The only disadvantage is that you cannot set the originators manually.
If the additional argument originator is nil
or is not passed to the access checking method, the method
will try to obtain it automagically. How?
It will try to fetch it from the :Originator tag
of NetAddr::CIDR object that had been used. This tag
will be preset because the method will add :inclulde_origins
option while calling IPAccess.to_cidrs on a list of
arguments that are going to be checked. That magic
works also for specialized access checking methods.
In those cases the originator is not fetched from
NetAddr::CIDR object's tag but comes from a real object
(Socket, String, etc.) that had been passed to method
for testing.
Access checking methods will try to fill up missing
:Originator tag in each processed object containing
IP address while figuring out the original object.
By object containing IP address we mean NetAddr::CIDR
kind of object that is used by underlying routines to
check access and reported during exception raising
as peer_ip attribute. Checking methods will use private
method setup_originator to decide which object should
be choosen as the originator.
Instance Attribute Summary collapse
-
#exception ⇒ Object
This attribute contains default exception that this class should throw when an access is denied.
Instance Method Summary collapse
-
#check(*addresses) ⇒ Object
This method checks IP access for CIDR objects.
-
#check_cidr(cidr, originator = nil) ⇒ Object
This method checks access for a CIDR object.
-
#check_fd(fd, originator = nil, &block) ⇒ Object
This method checks IP access but bases on file descriptor.
-
#check_ipstring(ipstring, originator = nil) ⇒ Object
This method checks access for a string containing IP address.
-
#check_sockaddr(sockaddr, originator = nil) ⇒ Object
This method checks access for a sockaddr.
-
#check_socket(socket, originator = nil) ⇒ Object
This method checks access for a socket.
-
#delete!(*addresses) ⇒ Object
This method removes CIDR rules specified by the given objects containing IP information.
-
#initialize(*addresses) ⇒ Check
constructor
Creates new IPAccess::List::Ch object.
-
#scream!(addr, rule, use_exception = IPAccessDenied, originator = nil, socket = nil) ⇒ Object
Raises default exception including important informations like remote IP address, rule that IP matched to, used access list and optional object passed as an argument.
-
#setup_originator(cidr, orig = nil, net_obj = nil) ⇒ Object
protected
This method helps with setting up a proper originator for an object that is checked against access set.
Constructor Details
#initialize(*addresses) ⇒ Check
Creates new IPAccess::List::Ch object. You may pass objects (containing IP information) to it. These objects will create black list rules. See IPAccess.to_cidrs description for more info on how to pass arguments.
IPAccess::List::Check object and/or NetAddr::CIDR object(s) may carry black or white list assignments inside. If such object(s) will be used to create initial ruleset then assignment found there would be used instead of default.
You should avoid passing hostnames as arguments since DNS is not reliable and responses may change with time,. That may cause security flaws.
Examples
IPAccess::List::Check.new '192.168.0.0/16', '127.0.0.1/255.0.0.0'
IPAccess::List::Check.new :private, :local
IPAccess::List::Check.new "randomseed.pl", :nonpublic
163 164 165 166 |
# File 'lib/ipaccess/ip_access_check.rb', line 163 def initialize(*addresses) @exception = IPAccessDenied super(*addresses) end |
Instance Attribute Details
#exception ⇒ Object
This attribute contains default exception that this class should throw when an access is denied. It must be a kind of IPAccessDenied or derivative.
141 142 143 |
# File 'lib/ipaccess/ip_access_check.rb', line 141 def exception @exception end |
Instance Method Details
#check(*addresses) ⇒ Object
This method checks IP access for
CIDR objects. If the access is denied it raises an exception
reporting first rejected IP. The exception class is taken from
a exception attribute of this object – it should be IPAccessDenied
or derivative kind of object.
If the access is granted it returns an array containing the given argument(s).
The argument called addresses
should be a list of objects containing IP
addresses. See the description of IPAccess.to_cidrs
for more info about arguments you may pass.
This method will try to set up originators
for tested addresses. That's why it will pass
:include_origins option to underlying methods
which use IPAccess.to_cidrs to fetch
IP addresses from many kinds of objects.
Tracking original network objects
Exception raises when some IP is denied.
That IP comes from one, particular object
passed as one of the arguments. This object
is passed as originator attribute of the
exception.
That allows you to find the original network object
that had been checked, not just its internal
representation (+peer_ip+ attribute) that
is a kind of NetAddr::CIDR.
Be aware that NetAddr::CIDR objects passed
as arguments may also have
originators set inside (check tag).
In that case the originator is simply copied.
Also remember that NetAddr::CIDR objects
are never set nor reported as originators.
In order to track originators the :include_origin
option is used when calling IPAccess.to_cidrs.
Passing a block
Optional block may be passed to this method. It will
be called once, when the access for a remote IP
address turns out to be denied. If it will
evaluate to true then no exception is raised,
even if the IP is not allowed to connect.
Remember to return false or nil in the block
to avoid random admissions. The block may take
the following arguments:
* +address+ of a denied IP (a kind of NetAddr::CIDR) * +rule+ that matched (a kind of NetAddr::CIDR) * +access_list+ pointing to a used access list (this object) * +addresses+ containing an array of arguments (IP addresses) * +originator+ indended to be placed as the +originator+ attribute in an exception
Faster alternatives
This method is relatively easy to use but you may also try more efficient access checking methods if your object contains information about single IP and is a known kind.
See the protected check method description for more info about internals. See IPAccess.to_cidrs description for more info about arguments you may pass to this method.
Workflow
In order to understand this method's logic properly you may look at the diagram:
387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/ipaccess/ip_access_check.rb', line 387 def check(*addresses) # :yields: address, rule, list, addresses, originator return addresses if self.empty? addresses.push :include_origins pairs = self.denied(*addresses) unless pairs.empty? addr = pairs.first[:IP] rule = pairs.first[:Rule] originator = setup_originator(addr, originator) dont_scream = false dont_scream = yield(addr, rule, self, addresses, originator) if block_given? scream!(addr, rule, @exception, originator, nil) unless dont_scream end return addresses end |
#check_cidr(cidr, originator = nil) ⇒ Object
This method checks access for a CIDR object. It works the same way as check_socket but tests NetAddr::CIDR objects.
464 465 466 467 468 469 470 471 472 473 474 475 |
# File 'lib/ipaccess/ip_access_check.rb', line 464 def check_cidr(cidr, originator=nil) # :yields: address, rule, list, cidr, originator pair = self.denied_cidr(cidr, true) unless pair.empty? addr = pair[:IP] rule = pair[:Rule] originator = setup_originator(addr, originator) dont_scream = false dont_scream = yield(addr, rule, self, cidr, originator) if block_given? scream!(addr, rule, @exception, originator, nil) unless dont_scream end return cidr end |
#check_fd(fd, originator = nil, &block) ⇒ Object
This method checks IP access but bases on file descriptor. It works the same way as check_socket but tests file descriptor.
499 500 501 502 503 |
# File 'lib/ipaccess/ip_access_check.rb', line 499 def check_fd(fd, originator=nil, &block) # :yields: address, rule, access_list, socket, originator originator = fd if originator.nil? check_socket(::Socket.for_fd(fd), originator, &block) return fd end |
#check_ipstring(ipstring, originator = nil) ⇒ Object
This method checks access for a string containing IP address. It works the same way as check_socket but tests Strings containing IP addresses.
481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
# File 'lib/ipaccess/ip_access_check.rb', line 481 def check_ipstring(ipstring, originator=nil) # :yields: address, rule, list, ipstring, originator return ipstring if self.empty? addr = NetAddr::CIDR.create(ipstring.split('%').first) pair = self.denied_cidr(addr, true) unless pair.empty? addr = pair[:IP] rule = pair[:Rule] originator = setup_originator(addr, originator, ipstring) dont_scream = false dont_scream = yield(addr, rule, self, ipstring, originator) if block_given? scream!(addr, rule, @exception, originator, nil) unless dont_scream end return ipstring end |
#check_sockaddr(sockaddr, originator = nil) ⇒ Object
This method checks access for a sockaddr. It works the same way as check_socket but tests sockaddr structures.
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 |
# File 'lib/ipaccess/ip_access_check.rb', line 441 def check_sockaddr(sockaddr, originator=nil) # :yields: address, rule, list, sockaddr, orig return sockaddr if self.empty? begin peeraddr = Socket.unpack_sockaddr_in(sockaddr).last rescue ArgumentError # sockaddr is not INET return sockaddr end peer_ip = NetAddr::CIDR.create(peeraddr.split('%').first) pair = self.denied_cidr(peer_ip, true) unless pair.empty? addr = pair[:IP] rule = pair[:Rule] originator = setup_originator(addr, originator, sockaddr) dont_scream = false dont_scream = yield(addr, rule, self, sockaddr, originator) if block_given? scream!(addr, rule, @exception, originator, nil) unless dont_scream end return sockaddr end |
#check_socket(socket, originator = nil) ⇒ Object
This method checks access for a socket.
It works the same way as the check method but expects you
to give a single Socket object instead of a list of
arguments and an optional originator object to be used
as an originator when raising an exception.
If the last argument is not given then the originator
will be set to tested object. To reset originator
pass :none as the last argument.
In order to understand this method's logic
properly you may look at the diagram:
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/ipaccess/ip_access_check.rb', line 416 def check_socket(socket, originator=nil) # :yields: address, rule, list, socket, originator if (self.empty? || !socket.respond_to?(:getpeername)) return socket end begin peeraddr = Socket.unpack_sockaddr_in(socket.getpeername).last rescue IOError, Errno::ENOTCONN, Errno::ENOTSOCK, ArgumentError # socket is not INET, not a socket nor connected return socket end peer_ip = NetAddr::CIDR.create(peeraddr.split('%').first) pair = self.denied_cidr(peer_ip, true) unless pair.empty? addr = pair[:IP] rule = pair[:Rule] originator = setup_originator(addr, originator, socket) dont_scream = false dont_scream = yield(addr, rule, self, socket, originator) if block_given? scream!(addr, rule, @exception, originator, socket) unless dont_scream end return socket end |
#delete!(*addresses) ⇒ Object
This method removes CIDR rules specified by the given objects containing IP information. It returns an array of removed CIDR rules.
Make sure you will specify correct and exact netmasks
in order to delete proper rules. This method will NOT
remove rules that imprecisely match given address or rules
that logically depends on specified rules, e.g.
removing 192.168.0.0/16 will leave 192.168.0.0/24
untouched. To create access exceptions for some
ranges and/or addresses whitelist them using permit
method. This method removes rules matching exact addresses/masks.
If the first or last argument is a symbol and it's :white
or :black then the specified rule will be removed
only from the given (white or black) list. If the list is
not specified the rule will be removed from both lists.
Special case: some CIDR objects may carry information about access list they should belong to. If the last argument of this method does not specify access list and added rule is the kind of special CIDR containing information about assignment to some list then this extra sugar will be used while removing. These special CIDR objects are usualy result of passing IPAccess::List as an argument. To be sure, whichaccess list will be altered always give its name when passing IPAccess::List.
You should avoid passing hostnames as arguments since DNS is not reliable and responses may change with time, which may cause security flaws.
See IPAccess.to_cidrs description for more info about arguments you may pass to it.
211 212 213 214 |
# File 'lib/ipaccess/ip_access_check.rb', line 211 def delete!(*addresses) removed = super(*addresses) return removed end |
#scream!(addr, rule, use_exception = IPAccessDenied, originator = nil, socket = nil) ⇒ Object
Raises default exception including important informations like remote IP address, rule that IP matched to, used access list and optional object passed as an argument.
First argument (+addr+) should be a testet IP address in CIDR object and second
argument (+rule+) of the same kind should contain a matching rule.
Third argument should be an exception class that will be used
to raise an exception. The last +originator+argument should be
an object that will be stored within the exception's object
as originator. It's recommended for it to be an object that
was used for communication and therefore tested but not a socket.
An underlying socket that caused an exception should be given as socket
if the object is not direct cause of the exception but the socket is.
In case of raw socket objects socket should be the same as originator.
305 306 307 |
# File 'lib/ipaccess/ip_access_check.rb', line 305 def scream!(addr, rule, use_exception=IPAccessDenied, originator=nil, socket=nil) raise use_exception.new(addr, rule, self, originator, socket) end |
#setup_originator(cidr, orig = nil, net_obj = nil) ⇒ Object (protected)
This method helps with setting up
a proper originator for an object
that is checked against access set.
Originator is the original object
that uses a checked IP address.
This definition is very general and that's
why there is a need for a method
like this, that will try to set it
up properly.
The cidr argument (containing the IP
address) should be a kind of
NetAddr::CIDR. It may also contain a special
tag inside called :Originator. Any
existing information about the originator contained
in this object's tag will never be overwritten.
However, it may not be used if second argument,
orig is given.
This orig argument should be an object
to which the IP address from cidr relates to.
If it's not given then the value found in
the +cidr+'s originator tag is assigned to it.
The last argument, net_obj is intended
to be "a rescue object" that will be treated
as originator if everything else fails. If the orig
is set to :none then the original object will
always be returned as nil without affecting
tag inside cidr.
Workflow
For better understanding how it works you may look at the workflow diagram:
To predict the logic in an easy way you may also find the input/output states table useful:
After calling this method you may find a reference to two original objects. First in a +cidr+'s tag and second returned by the method. By converntion you should rely more on returned value since it may carry a real object (e.g. Net::Something) whereas tag may point to an underlying object that had been used to fetch IP from (e.g. TCPSocket).
This method returns the originator or nil.
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/ipaccess/ip_access_check.rb', line 270 def setup_originator(cidr, orig=nil, net_obj=nil) if orig.nil? if (cidr.respond_to?(:tag) && !cidr.tag[:Originator].nil?) orig = cidr.tag[:Originator] else unless net_obj.nil? orig = net_obj cidr.tag[:Originator] = orig end end elsif orig == :none orig = nil elsif (cidr.respond_to?(:tag) && cidr.tag[:Originator].nil?) cidr.tag[:Originator] = orig end return orig end |