Newer
Older
tperimeter / tperimeter.rst
@tundra tundra on 9 Jun 2012 19 KB Fixed typo.
.. footer:: $Id: tperimeter.rst,v 1.4 2012/06/09 21:23:29 tundra Exp $


=====================================================
``tperimeter`` - A Dynamic TCP Wrapper Control System
=====================================================


OVERVIEW
--------

``tperimeter`` is a system that provides a secure mechanism for remotely
opening access to internet services under TCP Wrapper control.  Consider the
following very typical scenario:

  An internet-facing server is configured with a variety of services that
  observe TCP Wrapper (``/etc/hosts.allow``) access control rules.  It is
  common to use this mechanism to very strictly limit which external IP
  addresses may even attempt connection to these services.  For instance, we
  may choose to limit ``ssh`` logins from IPs of hosts known to us. The
  problem with this is that legitimate users whose IP addresses may change
  will not be able to connect.  This is common when a user travels and uses
  internet connections in airports, hotels, or other places where ``dhcp``
  is used for dynamic IP assignment.  In this case, the user has no *a
  priori* knowledge of what his IP address will be and thus cannot have it
  added to ``/etc/hosts.allow`` to enable remote system access.

A number of solutions to this problem have been proposed such as VPNs
and so-called "port knocking", but they are complex and require some
level of custom client code.  ``tperimeter`` uses open-source tools to
solve the problem simply and elegantly.  A traveling user or a user
whose IP address changes regularly simply logs into a secure web page
and informs the system of their current IP address and which services
they'd like to have enabled for them.  ``tperimeter`` then fulfills
the request - typically within 1 minute or so - and grants access to
the system for that service from that IP address for a small window in
time - 10 minutes is typical.  This gives the user time to access the
desired service before TCP Wrappers once again close the system to its
default state.  These semantics thus provide flexible remote access
with an automatic reset to system default access control.


HOW ``tperimeter`` WORKS
------------------------

``tperimeter`` has two major components:

  1) A *requestor subsystem* that presents the user a web page to
     request a "hole" be opened in the TCP wrappers for a specific
     service from a particular IP address. This request is then
     enqueued on disk for subsequent processing.

  2) A *TCP wrapper rewrite* mechanism that periodically (under ``cron``
     control) rebuilds the ``/etc/hosts.allow`` file to reflect
     any pending user requests queued by the requestor subsystem.

The workflow is fairly simple:

  1) The user logs into the ``tperimeter`` web interface and specifies
     from what IP address and to which service they want access.

  2)  A ``cron`` job periodically runs to process any such pending requests by
      dynamically rewriting the ``hosts.allow`` file.

  3) The ``cron`` job then *deletes* the request from the
     ``tperimeter`` queue, but only if it has exceeded a certain age.
     This age is specified in the ``rebuild-hosts.allow.sh`` file with
     the ``${DURATION}`` variable.  This means that the next time
     ``hosts.allow`` is rebuilt by the ``cron`` job, old "holes"
     ``tperimeter`` opened in the TCP wrappers will be *removed*.
     This ensures that someone doesn't open a hole in the system that
     then stays there permanently.  This works fine in practice,
     because the TCP wrapper security model operates only at the time
     of the initial connection request.  The ``tperimeter`` semantics
     were designed to open a hole only long enough to allow the user
     to make the initial connection - that will stay in place even
     after the wrappers are closed back up to their default state.
     For example, you can open access with ``tperimeter`` to gain
     access for, say, ``ssh``.  Once you've made the connecton, it
     remains connected even after ``hosts.allow`` is rebuilt to its
     default closed state.

The requestor subsystem consists of two parts.  ``tperimeter-ui.html``
is the web interface for the user to specify the service and IP
address they want opened.  ``tperimeter.py`` is a CGI script that
parses the user's input and actually places it on the disk queue for
subsequent processing.

``rebuild-hosts.allow.sh`` is the TCP wrapper rewrite mechanism.
After a user has requested access, they must wait until this 
script runs again before the access is actually granted.  So, this
should be run in the ``root crontab`` every minute.

The queue where ``tperimeter`` requests are initially deposited and
subsequently processed is transparent to the user and requires no
administration by the system administrator.  The queue is created by
the web interface when a request is made.  It is then subsequently
processed by the TCP wrapper rewrite mechanism and then deleted as
described above.

Each request ``tperimeter`` receives is logged in the system log.
It is in the form::

  Sep 25 13:37:11 myhost tperimeter: User@192.168.0.2 Requested Service: sshd For Address: 10.0.1.23



HOW TO INSTALL ``tperimeter``
-----------------------------

This section provides an overview of how ``tperimeter`` is installed.
It assumes you are a capable systems administrator and does not go
into the minutae of permissions, file ownership and so forth.

In general, the requestor files should be owned by the web daemon
(usually ``www``) The wrapper rewrite script should be owned by
``root``.  These files should have permissions of ``700`` or possibly
``740``.  The file tree that describes the default wrapper
configuration should only be writable by ``root``.  If you're
particularly paranoid and don't want local users to know what your
default wrappers look like, you can also make this file tree only
readable by ``root`` as well.  And, of course, ``tperimeter.py``
and ``rebuild-hosts.allow.sh`` should be set as executable by
their respective owners.

First, you have to install the web interface.  The example here
assumes you're using ``apache``:

  1) Install ``tperimeter-ui.html`` in your desired location
     on the web site.  We'll use ``/www/RemoteAccess`` for
     our example.

  2) You can either rename it to ``index.html`` (ugly) or
     symlink to it (better)::

       ln -s tperimeter-ui.html index.html

  3) You probably don't want the whole world to have access to
     this service, so it's pretty much mandatory you use
     password access to the system.  With ``apache`` this means
     you'll need to install an appropriate ``.htaccess`` file
     in this directory or otherwise secure it in your ``apache``
     configuration file.

  4) It is *highly* recommended that you require ``https`` when accessing
     this URL.  There's no point in securing your system if the password
     is flowing in plain text over the internet.  So, when you're done,
     the user will gain access to ``tperimeter`` at::

       https://your.fine.website.com/RemoteAccess

Now, you need to install the CGI script:

  1) In your web site's ``cgi-bin`` directory, create a directory
     called ``tperimeter``.

  2) Copy ``tperimeter.py`` to this directory.  This script
     requires a fairly recent copy of ``python`` on your system.
     As written, it assumes that it is in ``/usr/local/bin/python``,
     so you may need to modify the script if your system has
     the ``python`` binary somewhere else.

  3) Now (very important) symlink the ``.htaccess`` file you created
     in the previous step for the web interface to this directory.
     This prevents Eeeeeeevil Hackers from running the requestor
     script directly from a URL and bypassing your website security::

       ln -s /www/RemoteAccess/.htaccess .htaccess

Next, you need to install the TCP wrapper rewrite subsystem:

  1) Create the directory::

       mkdir -p /usr/local/etc/tperimeter

  2) Copy the ``rebuild-hosts.allow.sh`` file to this directory.



Now, you have to create the entries that describe your "default" TCP
wrapper configuration.  ``tperimeter`` has to know how to build your
"standard" TCP wrapper file - i.e., The ``hosts.allow`` entries you
*always* want in place regardless whether or not there is pending
requests for temporary access via the web interface.  That's because
``rebuild-hosts.allow.sh`` runs periodically under ``cron`` control and
rebuilds the *entire* ``hosts.allow`` file.  

To make this simple, the list of things you always want in your
``hosts.allow`` file is represented by a directory tree with
zero-length files in it.  ``tperimeter`` uses the *names* of these
directories and files to build the default TCP wrapper file.  This is
a fairly standard Unix idiom - using the file namespace to represent
some larger behaviorial semantic.  This file tree is also found in
``/usr/local/etc/tperimeter``.  You'll find an example of what goes
where in the ``tperimeter-sample`` directory included in this
distribution that may be helpful as you study the following material.

  1) The ``prologue`` and ``epilogue`` files are simply copied
     respectively to the beginning and end of your ``hosts.allow``
     file.  You can put any custom wrapper statements in these
     files as needed.

  2) The ``allow`` and ``deny`` directories contain entries
     for specific services you wish to allow or deny.   You
     create a directory under these for each *service* you want
     to control.  Within that "service directory" you create
     (using ``touch``) one or more zero-length *files* that are *named*
     with the IP address or DNS name of the host you are allowing/denying.

     Say you want to allow ``ssh`` access from 64.2.3.1 and anyone in
     10.0.1.x, and deny ``ftp`` access from everyone.  Then the
     directory/file layout would look like this::

       /usr/local/etc/tperimeter/
                                allow/
                                     sshd/
                                         10.0.1.
                                         64.2.3.1
                                deny/
                                    ftpd/
                                        ALL


     Note that ``10.0.1.``, ``64.2.3.1``, and ``ALL`` are names of
     zero-length *files* easily created with the ``touch``
     utility.

 
     When ``hosts.allow`` get's rebuilt, this would result in two
     wrapper statements::

       ftpd: ALL :DENY
       sshd: 10.0.1. 64.2.3.1 :ALLOW
       

When ``rebuild-hosts.allow.sh`` runs under ``cron`` control it will
scan this directory structure.  First, it will emit any pending
``tperimeter`` access requests.  Then it will emit your ``prologue``.
Then it will process your ``deny`` entries in the format just
described.  Then it will do the same for your ``allow`` entries.
Finally, it will emit your ``epilogue``.  You should keep several
things in mind when laying out your individual allow/deny entries:

  1) All ``deny`` entries are processed *before* your ``allow``
     entries.  This means the ``deny`` statements will appear
     before any ``allow`` entries in your ``hosts.allow`` file.

  2) The entries in a given service directory are processed in
     alphanumeric order and will appear in your ``hosts.allow``
     in that same order.

  3) Take care to name your service directories properly.  They are
     named for the *service* not the client program that accesses
     it.  ``ssh`` access is in a directory named ``sshd``, ``ftp`` in
     ``ftpd`` and so on.

Once you get the hang of this, it is really simple to administer.  To
add or deny access for a particular host, just go to the service
directory in question and ``touch`` a file by that name.  To remove
access or denial, go to the directory and delete the file by that
name.  Thereafter, when ``rebuild-hosts.allow.sh`` runs again, it will
build a new TCP wrapper control file with your new settings.

You can see what your changes will look like by running
``rebuild-hosts.allow.sh`` manually.  By default, it emits output
to ``stdout``.  You can even rebuild the ``hosts.allow`` file yourself
if you don't want to wait for ``cron`` to do it::

   /usr/local/etc/tperimeter/rebuild-hosts.allow.sh >/etc/hosts.allow


Finally, you need to create a ``cron`` job that runs the TCP wrapper
rebuilding process regularly::

  # Update /etc/hosts.allow to accommodate any tperimeter requests
  0,10,20,30,40,50 * * * * /usr/local/etc/tperimeter/rebuild-hosts.allow.sh >/etc/hosts.allow


CUSTOMIZING ``tperimeter``
--------------------------

The ``tperimeter-ui.html`` file provided with this distribution is
very simple and contains the login banner and greeting message the
user sees when they authenticate and gain access to the ``tperimeter``
system.  You can modify this file to suit your taste and otherwise
match the look-and-feel of your web site.

You can modify the ``tperimeter.py`` file to indicate which services
may be accessed with ``tperimeter``.  You can also modify this file
to list any IP addresses that are *never* allowed access.  As a 
practical matter, you typically only need provide access to ``sshd``
for almost anything one would like to do remotely.


GOTCHAS
-------

There are a few things that can bite you when using ``tperimeter``:

  1) The requestor/rewrite systems work off a common disk queue without
     any access locking.  This means it is theoretically possible to get a
     race condition wherein the user queues a request just as the
     rewrite system finishes running and deletes the queue.  In this
     case, the user's request will never be fullfilled, and they'll
     have to request access again.

  2) Similarly, there is no locking done when the ``hosts.allow`` file
     is rewritten.  The script that produces the new version of this
     file simply overwrites it.  There is the possibility that, at the
     moment that file is being created, someone will attempt to access
     your system when there are no TCP wrapper rules in effect. In
     designing this system, it was felt that this exposure was quite
     low.  If this turns out not to be the case (remember, this is
     EXPERIMENTAL software), then the ``hosts.allow`` rewrite
     mechanism will have to be rewritten to make sure that there is
     always some level of protection in place as the file is being
     modified.  If you have comments or experience with such problems
     - or better still, can provide a better mechanism - please
     contact us at the email address found below.

  3) Some (many) dynamic DNS systems found in hotels, airports, and
     the like do not properly match forward and reverse DNS entries.
     If you use strict DNS rules in your ``hosts.allow`` file with
     and entry like ``ALL : PARANOID : RFC931 20 : DENY``, this
     would prevent access from such addresses even though they've
     been granted by ``tperimeter``.  For this reason, the wrapper
     rewrite script places any ``tperimeter`` access at the beginning
     of the ``hosts.allow`` file before your default configuration
     statements.  This should be benign, but it does have the effect
     of circumventing your strict TCP wrapper rules and you should
     be aware of this.  If you don't like this semantic, you can
     modify the ``rebuild-hosts.allow.sh`` file to suit your
     preferences.

  4) The ``tperimeter.py`` file is written to require full IP
     quads when requesting access.  For example, although TCP
     wrappers allow entries like ``64.23.`` to specify a range
     of addresses, you *cannot* enter such an IP specification via
     the ``tperimeter`` web interface.  You also cannot specify
     the host from which you desire access by using its name, only
     its full IP quad address.

  5) When initially logging into the ``tperimeter`` interface, the
     user probably does not know their "real" dynamic IP address -
     i.e., The address they need opened by ``tperimeter`` for the
     desired access.  The dynamic IP addressing systems found in
     hotels, airports, and self-service kiosks vary considerably in
     their use of ``NAT`` and other address translation schemes.

     There is a trivial solution to this problem, however.  Each time
     you log into the ``tperimeter`` system, simply submit the request
     without the service or address fields filled in.  ``tperimeter``
     will automatically generate a request for ssh access at the
     current address through which you're connecting.  (It determines
     the address from the http headers which is almost always the
     correct address to use.)  This makes it simple to just hit ``Enter``
     at the ``tperimeter`` entry screen and have the right thing happen.

6)   If you are in a given location for several days, be aware that many
     dynamic IP systems assign a new address each time you log in.
     So, the address you provided on your first day at a hotel may not
     be the same one the next day.  For this reason, the "trick"
     described above should be done every time you log into
     ``tperimeter``.
     


SECURITY RISKS
--------------

Any system that permits remote changes to the security environment of a
server has the inherent risk that it can be compromised.  The key to
minimizing such exposure is careful customization, integration, and testing. 
TundraWare Inc. makes **no** claims that this software will work without
security risks or compromise.  The software is **EXPERIMENTAL** and is
provided **AS-IS**.  It is up to you to take the necessary steps to ensure
this system is appropriate for your environment.

There are several areas you should take particular care to audit to ensure
your system security isn't going to get clobbered by ``tperimeter``:

  1) The web page the user accesses to make ``tperimeter`` requests
     should be both encrypted (SSL/hhtps) **and** be password protected.
     You have to make a policy decision whether to offer all authorized
     users the same ``tperimeter`` login name and password or give them
     each their own individual login credentials.  Either way, it's a good
     idea to watch the system log for ``tperimeter`` requests to see
     how often and from where requests are showing up.

  2) If you customize the programs, make sure you've not introduced coding
     errors that might cause it to cobble up your ``/etc/hosts.allow`` file.

  3) Similarly take great care to construct the file tree that describes
     your desired default environment carefully.

  4) Make sure that file and directory permissions are correct.  The
     requestor components (``tperimiter-ui.html`` and ``tperimeter.py``)
     should be owned by the web daemon user (usually ``www``) and have
     permissions of 740 or even 700.  ``rebuild-hosts.allow.sh`` should
     be owned by root, again with permissions of 740 or 700.

  5) It is common for dhcp-served users to exist behind a NATing firewall.
     That is, the dynamic IP address they are assigned is commonly
     non-routable and is NATed via public routable IP.  This means that when
     they request access via ``tperimeter`` they are providing the public IP
     address **that is serving many NATed users**.  In effect, the access
     control "hole" ``tperimeter`` is opening will be available to
     **everyone** behind that NATed IP.  This is why ``tperimeter`` only
     offers a brief window of connection for requested services.  It always
     "snaps back" to the system defaults for access.


OTHER NOTES
-----------

``tperimeter`` requires a fairly current version of the ``python``
programming language.

``tperimeter`` was developed and minimally tested on FreeBSD 4.x. and
apache 1.3.  It should work without modification (other than the
customizations noted above) on other FreeBSD, Linux, and Apple OS/X
systems, but has not been tested at all in these environments.


COPYRIGHT AND LICENSING
-----------------------


``tperimeter`` is Copyright(c) 2006-2012 TundraWare Inc.  For terms of
use, see the ``tperimeter-license.txt`` file in the program
distribution.


AUTHOR
------

Tim Daneliuk

tperimeter@tundraware.com