Ereshkigal
view release on metacpan or search on metacpan
docs/kurs/checkpoint.md view on Meta::CPAN
init POSTs `/web_api/login` and carries the returned session id as
`X-chkp-sid` thereafter. Per-IP host objects are named
`<prefix>_<name>_<ip>` with dots/colons flattened to dashes:
| operation | API traffic |
|------------|-----------------------------------------------------------------------------|
| `init` | `POST /web_api/login` |
| `ban` | `POST /web_api/add-host` with `{"name":"<obj>","ip-address":"<ip>","groups":["<group>"]}`, then `POST /web_api/publish` |
| `unban` | `POST /web_api/delete-host` with `{"name":"<obj>"}`, then publish |
| `list` | no API call â the kur's own ban book |
| `check` | `POST /web_api/keepalive` â session still valid (and refreshed) |
| `flush` | delete-host + publish per banned IP |
| `re_init` | teardown (best effort), init (fresh login), re-add + publish per IP |
| `teardown` | delete-host + publish per banned IP (ban book kept) |
## self_heal
`check` is a session keepalive â it validates (and refreshes) the
session, nothing about objects, group, policy, or installation. A
timed-out session fails checks and bans; the `re_init` that
self_heal then triggers performs a fresh login, so session expiry
heals itself at the next ban/unban. With `self_heal` on, every ban's
keepalive also keeps the session alive.
## Gotchas
- The install-policy caveat dominates â publish â enforce.
- Two API calls per ban and unban (change + publish); publishes are
not cheap on busy management servers. High-churn ban sources will
make SmartConsole's audit log very talkative.
- The kur never logs out; sessions die by server-side timeout.
Combined with per-user session caps, give the kur its own API user
and don't restart it in a tight loop.
lib/Ereshkigal.pm view on Meta::CPAN
log_drek( 'err', 'kur "' . $name . '" ' . $how . ', restarting in ' . $delay . ' seconds' );
$kernel->delay_set( 'restart_kur', $delay, $name );
return;
} ## end sub _poe_kur_reaped
# The manager session's remove_kur handler, which stops one kur and drops it
# from the registry for good. The actual removal has to happen in the manager
# session as destroying a POE::Wheel::Run from within another session leaves
# its watchers behind, keeping the manager session alive forever... which is
# why _cmd_remove_kur only marks the entry disabled and posts here rather
# than doing the work itself.
#
# A running kur is asked to stop over its own socket, so it checkpoints its
# tablets and tears its firewall setup down properly. Only if that fails is
# it sent a TERM, which gets the process gone but leaves whatever it was
# holding in the firewall. Either way the entry is then unwired... its wheel
# dropped from wheel_to_kur, its PID from pid_to_kur, and the registry entry
# deleted.
#
lib/Ereshkigal.pm view on Meta::CPAN
# same dangling state at runtime... every registered kur is scanned for gates
# naming this one, and if any do the removal is refused naming them. Remove
# the gate first, or the member stays.
#
# Setting enabled to 0 before posting matters as well... it is what stops the
# reap handler restarting the kur when the stop it is about to be sent
# actually kills it.
#
# The real work is left to the manager session, as a POE::Wheel::Run must be
# destroyed in the session watching it or its watchers outlive it and keep
# the session alive forever.
#
# The config file is not rewritten, so a kur defined there returns at the
# next manager start.
#
# Note this takes no context and authorizes nothing of its own... the
# dispatch entry has already run _authorize with no kur names.
#
# Args...
#
# $request :: Required. The decoded request hash ref. Must carry args
lib/Ereshkigal/Kur.pm view on Meta::CPAN
},
'sig_shutdown' => sub {
my $signal = $_[ARG0];
$_[KERNEL]->sig_handled;
if ( $self->{stopping} ) {
return;
}
log_drek( 'info', 'SIG' . $signal . ' received, tearing the backend down', undef, $ident );
$self->_stop_guts;
# _stop_guts set stopping, so the pending sweep alarm is the
# only thing keeping this session alive... clear it and fire
# the server session's shutdown so the kernel can exit
$_[KERNEL]->delay('sweep');
$_[KERNEL]->post( $ident, 'shutdown' );
},
},
);
log_drek( 'info', 'started... socket=' . $self->socket_path . ' backend=' . $self->{backend}, undef, $ident );
$poe_kernel->run;
t/kur-checkpoint.t view on Meta::CPAN
$disabled->_tick;
is( slurp( $disabled->state_path ), $old_raw, 'checkpoint 0 disables the periodic rewrite' );
is( $disabled->{last_checkpoint}, 0, 'and last_checkpoint stays put' );
#
# loading... the row time is compared against now to decide restoration
#
my $now = time;
open( my $csv_fh, '>', $dir . '/cache/kur.loader.csv' ) || die($!);
print $csv_fh "ip,time,ban_time_left\n" . '10.0.0.1,' . $now . ',500' . "\n" # timed, alive
. '10.0.0.2,' . ( $now - 60 ) . ',10' . "\n" # expired while down
. '10.0.0.3,' . $now . ',0' . "\n" # permanent
. "not,enough\n" # malformed... field count
. '10.0.0.4,junk,50' . "\n" # malformed... time
. '10.0.0.5,' . $now . ',junk' . "\n" # malformed... left
. '010.0.0.7,' . $now . ',500' . "\n" # will not normalize... leading zero octet
. 'notanip,' . $now . ',500' . "\n" # will not normalize... not an IP at all
. "\n" # blank
. '10.0.0.6,' . $now . ',500' . "\n"; # good row after the junk
close($csv_fh);
t/kur-server.t view on Meta::CPAN
my $raw = IO::Socket::UNIX->new(
'Type' => IO::Socket::UNIX::SOCK_STREAM(),
'Peer' => $socket,
) || die($!);
print $raw "this is not json\n";
my $raw_line = <$raw>;
close($raw);
like( $raw_line, qr/invalid JSON/, 'malformed JSON gets an error response' );
is( $client->call_ok('ping')->{pong}, 1, 'server still alive after the bad input' );
#
# a second kur with the same name refuses to clobber the live socket
#
my $second_pid = spawn_kur( %spawn_opts, 'quiet' => 1 );
my $second_exit = wait_for_exit($second_pid);
ok( defined($second_exit) && $second_exit != 0, 'second kur with the same name exits nonzero' );
is( $client->call_ok('ping')->{pong}, 1, 'first kur survived the second one' );
t/kur-server.t view on Meta::CPAN
$result = $client->call_ok( 'ban', { 'ips' => ['9.9.9.1'], 'ban_time' => 3600 } );
is( $result->{ips}{'9.9.9.1'}{status}, 'ok', 'long timed ban ok' );
kill( 'KILL', $pid );
wait_for_exit($pid);
$pid = spawn_kur(%spawn_opts);
# the SIGKILLed kur left a stale socket file behind, so poll till the
# respawned one actually answers
my $alive = 0;
$waited = 0;
while ( $waited < 10 ) {
my $pong = eval { $client->call_ok('ping'); };
if ( defined($pong) && $pong->{pong} ) {
$alive = 1;
last;
}
select( undef, undef, undef, 0.5 );
$waited += 0.5;
}
ok( $alive, 'kur back up after being killed' );
$result = $client->call_ok('banned');
ok( ( grep { $_ eq '9.9.9.1' } @{ $result->{banned} } ), 'timed ban still banned after the restart' );
ok( $result->{expires}{'9.9.9.1'} > time, 'and still tracked with it\'s expiry' );
ok( ( grep { $_ eq '8.8.8.8' } @{ $result->{banned} } ), 'permanent ban still banned after the restart' );
#
# stop
#
t/manager.t view on Meta::CPAN
last;
}
select( undef, undef, undef, 0.5 );
$waited += 0.5;
} ## end while ( $waited < 20 )
ok( $respawned, 'sshd respawned after being killed' );
is( $status->{kurs}{sshd}{restarts}, 1, 'restart counted' );
# can't just wait on the socket path as the SIGKILLed kur left a stale
# socket file behind, so poll till the respawned one actually answers
my $alive = 0;
$waited = 0;
while ( $waited < 20 ) {
my $pong = eval { $sshd_client->call_ok('ping'); };
if ( defined($pong) && $pong->{pong} ) {
$alive = 1;
last;
}
select( undef, undef, undef, 0.5 );
$waited += 0.5;
}
ok( $alive, 'respawned sshd answers on it\'s socket' );
$result = $client->call_ok( 'ban', { 'ips' => ['6.6.6.6'], 'kur' => 'sshd' } );
is( $result->{kurs}{sshd}{ips}{'6.6.6.6'}{status}, 'ok', 'bans work on the respawned kur' );
#
# stop... this is also the regression test for wheel destruction from the
# wrong session, as the add/remove above has to not keep the manager alive
#
$result = $client->call_ok('stop');
is( $result->{stopping}, 1, 'stop response' );
ok( wait_for_gone($manager_socket), 'manager socket gone' );
ok( wait_for_gone( $dir . '/run/kur/sshd.sock' ), 'sshd socket gone' );
ok( wait_for_gone( $dir . '/run/kur/smtp.sock' ), 'smtp socket gone' );
ok( wait_for_gone( $dir . '/run/pid' ), 'manager pid file gone' );
is( wait_for_exit( $manager_pid, 20 ), 0, 'manager exited 0' );
( run in 1.849 second using v1.01-cache-2.11-cpan-14f38c9f855 )