NAME

Ereshkigal::Kur - FW handler for Ereshkigal.

VERSION

Version 0.0.1

SYNOPSIS

use Ereshkigal::Kur;

my $kur = Ereshkigal::Kur->new(
              'name'      => 'sshd',
              'backend'   => 'ipfw',
              'ports'     => ['22'],
              'protocols' => ['tcp'],
          );

$kur->start_server;

Each Kur instance wraps a single Net::Firewall::BlockerHelper instance and serves it up via a POE::Component::Server::JSONUnix server listening on a unix socket under $run_base_dir/kur/.

METHODS

new

Initiates the object. All errors are considered fatal, meaning if new fails it will die.

- name :: Name of this specific instance. Must match /^[a-zA-Z0-9\-]+$/.
    Default :: undef

- backend :: The backend to use for Net::Firewall::BlockerHelper.
    Default :: undef

- ports :: An array of ports to block, passed to Net::Firewall::BlockerHelper.
    Default :: []

- protocols :: An array of protocols to block, passed to Net::Firewall::BlockerHelper.
    Default :: []

- prefix :: Prefix to use, passed to Net::Firewall::BlockerHelper.
    Default :: undef, left for the backend to default to kur

- options :: Backend specific options hash, passed to Net::Firewall::BlockerHelper.
    Default :: undef, left for the backend to default to {}

- self_heal :: Self heal setting, passed to Net::Firewall::BlockerHelper.
    Default :: undef, left for the backend to default to 1

- ban_time :: How long bans should last in seconds. 0 means bans never
      time out. May be overridden per ban request.
    Default :: 600

- checkpoint :: Seconds between periodic rewrites of the ban state CSV.
      0 disables the periodic rewrite... ban/unban, stop, and on demand
      checkpoints still happen.
    Default :: 60

- enable_cidr :: Boolean for whether CIDR banning is enabled for this
      instance. Even when set, CIDR commands only work if the backend
      supports CIDR bans. Config files carry strings rather than
      booleans, so the value is folded... undef, the empty string, 0,
      false, no, and off are all off, and anything else at all is on.
    Default :: 0

- cidr_silent_drop :: Boolean for how a CIDR command is handled when CIDR
      banning is not available for this instance, either because
      enable_cidr is off or the backend does not support it. When set the
      command is silently dropped, returning dropped => 1, rather than
      erroring, which is the point when fanning out to a mix of CIDR
      capable and incapable instances. Folded the same way enable_cidr
      is.
    Default :: 0

- run_base_dir :: Base dir for run files. The socket and PID for this
      instance live under C<$run_base_dir/kur/> named for this instance.
    Default :: /var/run/ereshkigal

- cache_base_dir :: Base dir for cache files. The ban state and the
      unban retry state for this instance are persisted as CSVs under
      here, named for the instance, so timed bans and unbans still owed
      to the firewall both survive a restart. See L</state_path>,
      L</cidr_state_path>, L</retry_state_path>, and
      L</cidr_retry_state_path> for the four.
    Default :: /var/cache/ereshkigal

socket_path

Returns the path of the unix socket for this instance.

my $socket_path = $kur->socket_path;

pid_path

Returns the path of the PID file for this instance.

my $pid_path = $kur->pid_path;

state_path

Returns the path of the ban state CSV for this instance.

my $state_path = $kur->state_path;

cidr_state_path

Returns the path of the CIDR ban state CSV for this instance. This is kept separate from "state_path" so the single IP state format stays untouched.

my $cidr_state_path = $kur->cidr_state_path;

retry_state_path

Returns the path of the unban retry state CSV for this instance, the tablet carrying entries whose unban at expiry failed and is still owed to the firewall.

my $retry_state_path = $kur->retry_state_path;

cidr_retry_state_path

Returns the path of the CIDR unban retry state CSV for this instance, the CIDR counterpart of "retry_state_path".

my $cidr_retry_state_path = $kur->cidr_retry_state_path;

start_server

Starts up the POE::Component::Server::JSONUnix server for this instance, calling $poe_kernel->run.

This should not be expected to return till the server is told to stop.

The socket is chmoded to 0600 given only the manager, running as the same user, talks to it.

A ban sweeper is also started, which checks once a second for timed bans that have expired and unbans them, and handles the periodic checkpointing of the ban state CSVs. SIGTERM and SIGINT are handled, checkpointing and tearing the backend down the same as the stop command before exiting.

IPs passed to ban and unban are validated and normalized to their canonical string form, so variant spellings of the same IP, most notably IPv6 long form vs short form as well as case, are all treated as the same IP. For ban anything failing to validate errors per IP without disturbing the rest of the request, while for unban it is fatal to the request.

The JSON commands handled are as below.

- ban :: Ban the IPs specified via the array args.ips. args.ban_time,
      if defined, overrides the instance default for how long the bans
      should last in seconds, with 0 meaning never time out. Banning an
      already banned IP just refreshes its timer.

- unban :: Check if the IP, args.ip, is banned and if so unban it.

- cidr_ban :: Ban the CIDR ranges specified via the array args.cidrs,
      otherwise behaving like ban. Only handled when enable_cidr is set
      and the backend supports CIDR bans, otherwise it is either dropped
      or refused per cidr_silent_drop.

- cidr_unban :: Check if the CIDR, args.cidr, is banned and if so unban
      it. Gated the same as cidr_ban.

- banned :: Return a list of banned IPs along with an expires map of
      when each times out, 0 meaning never. banned_cidr and cidr_expires
      carry the same for CIDR bans. unban_retries and cidr_unban_retries
      carry the per entry book keeping for unbans still owed to the
      firewall.

- status :: Return instance status info and stats, including ban_time,
      counts of timed and permanent bans, the next expiry, and how many
      unbans are still owed to the firewall along with how long the
      longest owed has been outstanding.

- flush :: Unban everything currently banned, ranges as well as single
      IPs, emptying both ban books and both unban retry books with them.

- re_init :: Have the backend tear its firewall setup down and build it
      again, re-banning everything the ban books carry. Bans are not
      enforced while that is happening. Tearing down takes any rule a
      failed unban left behind, so the retry books are emptied too.

- checkpoint :: Write the ban state CSVs out now.

- clear_retries :: Forget unbans still owed to the firewall, either the
      single one named by args.ip or args.cidr, or all of them when
      neither is given. Only the book keeping is forgotten, nothing is
      asked of the firewall, so anything genuinely still banished there
      stays that way.

- stop :: Checkpoint, teardown the backend, and exit.

ERROR CODES / ERROR FLAGS

Error handling is provided by Error::Helper. All errors are considered fatal.

1, NErunBaseDir

The run base dir or the kur dir under it does not exist or is not a directory.

2, invalidName

Name not defined or does not match /^[a-zA-Z0-9\-]+$/.

3, backendInitFailed

Failed to initialize the backend.

4, nonRWrunBaseDir

The run base dir or the kur dir under it is not readable or writable by the current user.

5, NEcacheBaseDir

The cache base dir does not exist or is not a directory.

6, nonRWcacheBaseDir

The cache base dir is not readable or writable by the current user.

7, invalidBanTime

ban_time is not a non-negative int of seconds.

8, invalidCheckpoint

checkpoint is not a non-negative int of seconds.