nfsinkhole

nfsinkhole is a Python package for setting up a Unix server as a sinkhole (all protocols/ports to a secondary interface).

nfsinkhole

Warning

This version is considered experimental. Do not attempt to use this library in production until tests via travis and docker are setup, stable, and sufficiently covered.

Attention

You are responsible for rotating log files (/var/log/nfsinkhole*), and syslog forwarding must be configured manually (automation pending).

nfsinkhole is a Python library and scripts for setting up a Unix server as a sinkhole (monitor, log/capture, and drop all traffic to a secondary interface).

The default setup arguments monitor/capture all traffic. Setup arguments are provided to configure protocols, ports, rate limiting, logging, source IP/CIDR exclusions from logging, and optional packet capture.

All sinkhole events are written to /var/log/nfsinkhole-events.log. Optionally, you can enable tcpdump to output packet capture text to /var/log/nfsinkhole-pcap.log if your version of tcpdump supports packet printing; otherwise reverts to /var/log/nfsinkhole.pcap.

Features

  • Simple install script
  • Installs as a init.d/systemctl service
  • Service modifies iptables on start/stop, no need to persist iptables
  • rsyslog and syslog-ng (pending) supported
  • RedHat/CentOS 6/7 tested
  • Python 2.6+ and 3.0+ supported
  • Built-in support for dealing with SELinux/AppArmor
  • Packet capture of sinkhole traffic (printed output to log for tcpdump v4.5+)
  • Useful set of utilities
  • Detailed logging to /var/log/nfsinkhole-*
  • Syslog forwarding configuration (pending)
  • BSD license

Planned Improvements

  • API/class documentation
  • syslog-ng support (currently partially built; unused)
  • Tests via travis-ci/docker
  • Coverage via coverage.io
  • Exception handling overhaul
  • Set logging level (currently debug)
  • BIND/Microsoft/etc DNS server configuration documentation/examples
  • Monitoring use case examples
  • Automatic configuration for syslog forwarding
  • SIEM parsers/apps/plugins
  • Official support/testing for more OS environments
  • Support handling exceptions for HIPS and other endpoint security products
  • Intelligent handling/handshakes (inspired by iptrap - https://github.com/jedisct1/iptrap)

Dependencies

OS:

iptables (likely already included in base OS)
tcpdump (optional - likely already included in base OS)

Python 2.6:

argparse

Python 2.7, 3.0+:

None!

Installing

Attention

The nfsinkhole service, iptables rules, and tcpdump must run as root. You can still use user/virtualenv Python environments, for the library, but ultimately, the core sinkhole will be run as root.

Note

Replace any below occurence of <INTERFACE> with the name of your sinkhole network interface name.

Base OS (no pip)

RHEL/CentOS 6

GitHub - Stable:

wget -O argparse.tar.gz https://github.com/ThomasWaldmann/argparse/tarball/master
tar -C argparse -zxvf argparse.tar.gz
cd argparse
python setup.py install --user prefix=
cd ..
rm -Rf argparse
wget -O nfsinkhole.tar.gz https://github.com/secynic/nfsinkhole/tarball/master
tar -C nfsinkhole -zxvf nfsinkhole.tar.gz
cd nfsinkhole
python setup.py install --user prefix=
cd ..
rm -Rf nfsinkhole
python ~/.local/bin/nfsinkhole-setup.py --interface <INTERFACE> --install --pcap
RHEL/CentOS 7

GitHub - Stable:

wget -O nfsinkhole.tar.gz https://github.com/secynic/nfsinkhole/tarball/master
tar -C nfsinkhole -zxvf nfsinkhole.tar.gz
cd nfsinkhole
python setup.py install --user prefix=
cd ..
rm -Rf nfsinkhole
python ~/.local/bin/nfsinkhole-setup.py --interface <INTERFACE> --install --pcap

Service

Once installed you need to start the nfsinkhole service.

RHEL/CentOS 6

sudo service nfsinkhole start

RHEL/CentOS 7

sudo systemctl start nfsinkhole.service

Special Thanks

Thank you JetBrains for the PyCharm open source support!

Contributing

Issue submission

Issues are tracked on GitHub:

Follow the guidelines detailed in the appropriate section below. As a general rule of thumb, provide as much information as possible when submitting issues.

Bug reports

  • Title should be a short, descriptive summary of the bug

  • Include the OS, Python, and nfsinkhole versions affected

  • Provide a context (with code example) in the description of your issue. What are you attempting to do?

  • Include the full obfuscated output. Make sure to set DEBUG logging:

    import logging
    LOG_FORMAT = ('[%(asctime)s] [%(levelname)s] [%(filename)s:%(lineno)s] '
       '[%(funcName)s()] %(message)s')
    logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT)
    
  • Include sources of information with links or screenshots

  • Do you have a suggestion on how to fix the bug?

Feature Requests

  • Title should be a short, descriptive summary of the feature requested
  • Provide use case examples
  • Include sources of information with links or screenshots
  • Do you have a suggestion on how to implement the feature?

Testing

Testing code and infrastructure is in progress.

Questions

I am happy to answer any questions and provide assistance where possible. Please be clear and concise. Provide examples when possible. Check the nfsinkhole documentation and the issue tracker before asking a question.

Pull Requests

What to include

Aside from the core code changes, it is helpful to provide the following (where applicable):

  • Unit tests
  • Examples
  • Sphinx configuration changes in /docs
  • Requirements (python2.6.txt, etc)

Guidelines

  • Title should be a short, descriptive summary of the changes
  • Follow PEP 8 where possible.
  • Follow the Google docstring style guide for comments
  • Must be compatible with Python 2.6, 2.7, and 3+
  • Must not break OS compatibility for RHEL 6/7, CentOS 6/7
  • Break out reusable code to functions
  • Make your code easy to read and comment where necessary
  • Reference the GitHub issue number in the description (e.g., Issue #01)
  • When running nosetests, make sure to follow Testing

License

Copyright (c) 2016 Philip Hane All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Changelog

0.1.0 (2016-08-29)

  • Initial release

apparmor

TODO

iptables

TODO

rsyslog

TODO

selinux

TODO

service

TODO

syslog-ng

TODO

tcpdump

TODO

utils

TODO

Library Structure

class nfsinkhole.apparmor.AppArmor[source]

The class for managing apparmor policy enforcement, if it is installed.

disable_enforcement(module='usr.sbin.tcpdump')[source]

The function for disabling AppArmor enforcement for a module.

Parameters:module – Module in /etc/apparmor.d to disable.
Returns:True if disabling enforcement was successful, or False.
Return type:Boolean
enable_enforcement(module='usr.sbin.tcpdump')[source]

The function for enabling AppArmor enforcement for a module.

Parameters:module – Module in /etc/apparmor.d to enable.
Returns:True if enabling enforcement was successful, or False.
Return type:Boolean
exception nfsinkhole.exceptions.BinaryNotFound[source]

An Exception for when a binary is not detected.

exception nfsinkhole.exceptions.IPTablesError[source]

An Exception for when a iptables process generates stderr output.

exception nfsinkhole.exceptions.IPTablesExists[source]

An Exception for when iptables rules, related to nfsinkhole, exist.

exception nfsinkhole.exceptions.IPTablesNotExists[source]

An Exception for when iptables rules, related to nfsinkhole, don’t exist.

exception nfsinkhole.exceptions.SubprocessError[source]

An Exception for when a generic subprocess generates stderr output.

class nfsinkhole.iptables.IPTablesSinkhole(interface=None, interface_addr=None, log_prefix='"[nfsinkhole] "', protocol='all', dport='0:65535', hashlimit='1/h', hashlimitmode='srcip, dstip, dstport', hashlimitburst='1', hashlimitexpire='3600000', srcexclude='127.0.0.1')[source]

The class for managing sinkhole configuration within iptables.

Parameters:
  • interface – The secondary network interface dedicated to sinkhole traffic. Warning: Do not accidentally set this to your primary interface. It will drop all traffic, and kill your remote access.
  • interface_addr – The IP address assigned to interface.
  • log_prefix – Prefix for syslog messages.
  • protocol – The protocol(s) to log (all traffic will still be dropped). Accepts a comma separated string of protocols (tcp,udp,udplite,icmp,esp,ah,sctp) or all.
  • dport – The destination port(s) to log (for applicable protocols). Range should be in the format startport:endport or 0,1,2,3,n..
  • hashlimit – Set the hashlimit rate. Hashlimit is used to tune the amount of events logged. See the iptables-extensions docs: http://ipset.netfilter.org/iptables-extensions.man.html
  • hashlimitmode – Set the hashlimit mode, a comma separated string of options (srcip,srcport,dstip,dstport). More options here results in more logs generated.
  • hashlimitburst – Maximum initial number of packets to match.
  • hashlimitexpire – Number of milliseconds to keep entries in the hash table.
  • srcexclude – Exclude a comma separated string of source IPs/CIDRs from logging.
create_drop_rule()[source]

The function for writing the iptables DROP rule for the interface.

create_rules()[source]

The function for writing iptables rules related to nfsinkhole.

delete_drop_rule()[source]

The function for deleting the iptables DROP rule for the interface.

delete_rules()[source]

The function for deleting iptables rules related to nfsinkhole.

list_existing_rules(filter_io_drop=False)[source]

The function for retrieving current iptables rules related to nfsinkhole.

Parameters:filter_io_drop – Boolean for only showing the DROP rules for INPUT and OUTPUT. These are not shown by default. This exists to avoid allowing packets on the interface if the service is down. If installed, the interface always drops all traffic regardless of the service state.
Returns:Matching sinkhole lines returned by iptables -S.
Return type:List
Raises:IPTablesError – A Unix process had an error (stderr).
class nfsinkhole.rsyslog.RSyslog(is_systemd=False)[source]

The class for managing rsyslog checks and configuration.

Parameters:is_systemd – True if systemd is in use, False if not (use init.d).
create_config(prefix='[nfsinkhole] ')[source]

The function for creating the rsyslog config.

Parameters:prefix – The log prefix set in iptables.
delete_config()[source]

The function for deleting the rsyslog config.

get_version()[source]

The function for checking the rsyslog version.

Returns:rsyslog version string if found, or None.
Return type:String
restart()[source]

The function for restarting the rsyslog service.

selinux_associate()[source]

The function for associating the rsyslog config with selinux.

class nfsinkhole.selinux.SELinux[source]

The class for managing selinux.

associate(path)[source]

The function for associating a file path with selinux

class nfsinkhole.service.SystemService(interface=None, interface_addr=None, log_prefix='"[nfsinkhole] "', protocol='all', dport='0:65535', hashlimit='1/h', hashlimitmode='srcip, dstip, dstport', hashlimitburst='1', hashlimitexpire='3600000', srcexclude='127.0.0.1', pcap=True)[source]

The class for managing the nfsinkhole init.d/systemd service.

Parameters:
  • interface – The secondary network interface dedicated to sinkhole traffic. Warning: Do not accidentally set this to your primary interface. It will drop all traffic, and kill your remote access.
  • interface_addr – The IP address assigned to interface.
  • log_prefix – Prefix for syslog messages.
  • protocol – The protocol(s) to log (all traffic will still be dropped). Accepts a comma separated string of protocols (tcp,udp,udplite,icmp,esp,ah,sctp) or all.
  • dport – The destination port(s) to log (for applicable protocols). Range should be in the format startport:endport or 0,1,2,3,n..
  • hashlimit – Set the hashlimit rate. Hashlimit is used to tune the amount of events logged. See the iptables-extensions docs: http://ipset.netfilter.org/iptables-extensions.man.html
  • hashlimitmode – Set the hashlimit mode, a comma separated string of options (srcip,srcport,dstip,dstport). More options here results in more logs generated.
  • hashlimitburst – Maximum initial number of packets to match.
  • hashlimitexpire – Number of milliseconds to keep entries in the hash table.
  • srcexclude – Exclude a comma separated string of source IPs/CIDRs from logging.
  • pcap – Enable packet capture text or raw depending on tcpdump version.’
check_systemd()[source]

The function for checking if systemd is implemented.

Returns:A tuple: is_systemd, svc_path.
Return type:Tuple (Boolean, String)
create_service()[source]

The function for creating the init.d/systemd service.

delete_service()[source]

The function for deleting the init.d/systemd service.

class nfsinkhole.syslog_ng.SyslogNG(is_systemd=False)[source]

The class for managing syslog-ng checks and configuration.

Parameters:is_systemd – True if systemd is in use, False if not (init.d).
create_config(prefix='[nfsinkhole] ')[source]

The function for creating the syslog-ng config. (incomplete/unused)

Parameters:prefix – The log prefix set in iptables.
delete_config()[source]

The function for deleting the syslog-ng config.

get_version()[source]

The function for checking the syslog-ng version.

Returns:syslog-ng version string if found, or None.
Return type:String
restart()[source]

The function for restarting the syslog-ng service.

selinux_associate()[source]

The function for associating the syslog-ng config with selinux.

class nfsinkhole.tcpdump.TCPDump(sbin='/usr/sbin/tcpdump')[source]

The class for managing tcpdump checks.

Parameters:sbin – Path to tcpdump binary
check_packet_print()[source]

The function for checking if tcpdump/nflog support packet printing.

Returns:True if packet printing is supported, or False.
Return type:Boolean
get_version()[source]

The function for checking the tcpdump version.

Returns:tcpdump version string if found, or None.
Return type:String
nfsinkhole.utils.get_default_interface()[source]

The function for getting the default Unix network interface address.

Returns:The network interface name, or None.
Return type:String
nfsinkhole.utils.get_interface_addr(interface=None)[source]

The function for automatically determining a Unix network interface address.

Parameters:interface – The network interface name.
Returns:The IP address for the interface, or None.
Return type:String
nfsinkhole.utils.popen_wrapper(cmd_arr=None, raise_err=False, log_stdout_line=True)[source]

The function for subprocess with custom logging output.

Parameters:
  • cmd_arr – Array of command strings to pass to subprocess.Popen().
  • raise_err – If stderr is encountered, raise SubprocessError.
  • log_stdout_line – If True, logs each stdout line as a separate log entry. If False, logs all of stdout in a single log entry.
Returns:

stdout, stderr of the completed subprocess.

Return type:

Tuple

Raises:
  • ValueError – cmd_arr argument is not provided or is None.
  • TypeError – cmd_arr argument is not a list.
  • SubprocessError – The subprocess encountered an error (stderr). raise_err must be True for this.
nfsinkhole.utils.set_system_timezone(timezone='UTC')[source]

The function for setting the system timezone.

Parameters:timezone – The timezone to set, see /usr/share/zoneinfo/* for options.
Raises:SubprocessError – One of the processes associated with manual timezone configuration encountered an error.