feat: osh-encrypt-rsync: handle sqlite and user logs along with ttyrec files

This commit is contained in:
Stéphane Lesimple 2022-01-20 15:51:11 +00:00 committed by Stéphane Lesimple
parent 86c7bf39e6
commit 2c2064a484
6 changed files with 394 additions and 164 deletions

View file

@ -5,10 +5,9 @@ use warnings;
use 5.010;
use File::Temp;
use File::Basename;
use File::Find;
use File::Path;
use File::Copy;
use File::Copy 'move';
use Getopt::Long;
use Fcntl qw{ :flock };
use IPC::Open3;
@ -20,7 +19,8 @@ use OVH::Bastion;
use OVH::SimpleLog;
my %config;
my ($dryRun, $configTest, $forceRsync, $forceEncrypt, $noDelete, $encryptOnly, $rsyncOnly, $verbose, $help);
my ($dryRun, $configTest, $forceDelete, $forceEncrypt, $noDelete, $encryptOnly, $rsyncOnly, $verbose, $help);
$verbose = 0;
local $| = 1;
sub is_new_gpg {
@ -41,7 +41,7 @@ sub gpg_sign {
my %params = @_;
my @cmd = qw{ gpg --batch --trust-model always --sign --passphrase-fd 0 };
push @cmd, qw{ --pinentry-mode loopback } if is_new_gpg();
push @cmd, "-v" if $config{'trace'};
push @cmd, "-v" if $verbose >= 2;
push @cmd, '--local-user', $params{'signkey'}, '--output', '-', $params{'infile'};
my $outfile;
@ -78,7 +78,7 @@ sub gpg_encrypt {
push @cmd, qw{ --pinentry-mode loopback } if is_new_gpg();
push @cmd, '--local-user', $params{'signkey'};
}
push @cmd, "-v" if $config{'trace'};
push @cmd, "-v" if $verbose >= 2;
foreach my $recipient (@{$params{'recipients'}}) {
push @cmd, "-r", $recipient;
}
@ -103,12 +103,66 @@ sub gpg_encrypt {
return 0; # success
}
sub test_config {
my $error;
sub config_load_and_lint {
my $fnret;
# Useful when erroring before we had a chance to actually read the config,
# and the configured syslog_facility value. This will be overriden below once we
# know what the user configured.
OVH::SimpleLog::setSyslog('local6');
# we can have CONFIGDIR/osh-encrypt-rsync.conf
# but also CONFIGDIR/osh-encrypt-rsync.conf.d/*
# later files override the previous ones, item by item
my @configfilelist;
if (-f -r OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf") {
push @configfilelist, OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf";
}
if (-d -x OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf.d") {
if (opendir(my $dh, OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf.d")) {
my @subfiles = map { OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf.d/" . $_ } grep { /\.conf$/ } readdir($dh);
closedir($dh);
push @configfilelist, sort @subfiles;
}
}
# no config file, fail early
if (not @configfilelist) {
_err "Error, no config file found!";
return 1;
}
# load config files in order
foreach my $configfile (@configfilelist) {
_log "Configuration: loading configfile $configfile...";
$fnret = OVH::Bastion::load_configuration_file(
file => $configfile,
rootonly => 1,
);
if (not $fnret) {
_err "Error while loading configuration from $configfile, aborting (" . $fnret->msg . ")";
return 1;
}
foreach my $key (keys %{$fnret->value}) {
$config{$key} = $fnret->value->{$key};
}
# we'll be using our own config file as a handy flock() backend
$config{'lockfile'} = $configfile if not defined $config{'lockfile'};
}
# set logging info as soon as we can, before vetting the rest of the config
$config{'syslog_facility'} //= 'local6';
if ($config{'syslog_facility'}) {
OVH::SimpleLog::setSyslog($config{'syslog_facility'});
}
else {
OVH::SimpleLog::closeSyslog();
}
OVH::SimpleLog::setLogFile($config{'logfile'}) if $config{'logfile'};
# normalize / define defaults / quick checks
$config{'trace'} = $config{'trace'} ? 1 : 0;
if (not exists $config{'recipients'}) {
_err "config error: recipients must be defined";
return 1;
@ -123,17 +177,86 @@ sub test_config {
return 1;
}
if ($config{'encrypt_and_move_delay_days'} !~ /^\d+$/) {
_err "config error: encrypt_and_move_delay_days is not a positive integer!";
$config{'encrypt_and_move_to_directory'} //= '/home/.encrypt';
# new config option found
if (defined $config{'encrypt_and_move_ttyrec_delay_days'}) {
# check proper syntax
if ($config{'encrypt_and_move_ttyrec_delay_days'} !~ /^(?:\d+|-1)$/) {
_err "config error: encrypt_and_move_ttyrec_delay_days is not a positive integer nor -1!";
return 1;
}
# syntax is good but we also have the deprecated name, warn and proceed
if (defined $config{'encrypt_and_move_delay_days'}) {
_warn "config: deprecated option 'encrypt_and_move_delay_days' exists, but has been ignored as "
. "we also have the new option 'encrypt_and_move_ttyrec_delay_days' in the configuration";
}
}
# new config option not found
else {
# do we have the legacy option name ?
if (defined $config{'encrypt_and_move_delay_days'}) {
# yes, check proper syntax
if ($config{'encrypt_and_move_delay_days'} !~ /^(?:\d+|-1)$/) {
_err "config error: encrypt_and_move_delay_days is not an integer >= -1!";
return 1;
}
else {
# syntax ok, save it to the new name
$config{'encrypt_and_move_ttyrec_delay_days'} = delete $config{'encrypt_and_move_delay_days'};
}
}
}
foreach my $key (qw{ encrypt_and_move_user_logs_delay_days encrypt_and_move_user_sqlites_delay_days }) {
$config{$key} //= 31;
if ($config{$key} !~ /^(?:\d+|-1)$/) {
_err "config error: $key is not an integer >= -1!";
return 1;
}
}
$config{'rsync_delay_before_remove_days'} //= 0;
if ($config{'rsync_delay_before_remove_days'} !~ /^(?:\d+|-1)$/) {
_err "config error: rsync_delay_before_remove_days is not an integer >= -1!";
return 1;
}
if ($config{'rsync_delay_before_remove_days'} !~ /^\d+$/) {
_err "config error: rsync_delay_before_remove_days is not a positive integer!";
$config{'rsync_destination'} //= '';
$config{'rsync_rsh'} //= '';
# ensure the various config files defined all the keywords we need
foreach my $keyword (qw{ signing_key signing_key_passphrase }) {
next if defined $config{$keyword};
_err "Missing mandatory configuration item '$keyword', aborting";
return 1;
}
# ok, check if my gpg conf is good
_log "Config successfully loaded.";
if ($verbose) {
require Data::Dumper;
local $Data::Dumper::Sortkeys = 1;
local $Data::Dumper::Terse = 1;
# hide passphrase
my $passphrase = $config{'signing_key_passphrase'};
$config{'signing_key_passphrase'} = '***REDACTED***';
print Data::Dumper::Dumper({config => \%config});
$config{'signing_key_passphrase'} = $passphrase;
}
return 0;
}
sub config_test {
my $error;
# check if my gpg conf is good
my $infile = File::Temp->new(UNLINK => 1, TMPDIR => 1);
print {$infile} time();
close($infile);
@ -293,21 +416,53 @@ sub encrypt_once {
return 0; # no error
}
my $openedFiles = undef;
# this sub is called for each file found
sub potentially_work_on_this_file {
# file must be a ttyrec file or an osh_http_proxy_ttyrec-ish file
my $filetype;
$filetype = 'ttyrec' if m{^/home/[^/]+/ttyrec/[^/]+/[^/]+(\.ttyrec(\.zst)?)?$};
$filetype = 'proxylog' if m{^/home/[^/]+/ttyrec/[^/]+/\d+-\d+-\d+\.txt$};
$filetype or return;
# file must be either:
# - a ttyrec file or an osh_http_proxy_ttyrec-ish file
# - a user sqlite file (possibly compressed)
# - a user log file (possibly compressed)
my ($filetype, $file_delay);
if (m{^/home/[^/]+/ttyrec/[^/]+/[^/]+(?:\.ttyrec(?:\.zst)?)?$}) {
$filetype = 'ttyrec';
$file_delay = $config{'encrypt_and_move_ttyrec_delay_days'};
}
elsif (m{^/home/[^/]+/ttyrec/[^/]+/\d+-\d+-\d+\.txt$}) {
$filetype = 'proxylog';
$file_delay = $config{'encrypt_and_move_ttyrec_delay_days'};
# must exist and be a file
# never touch a file that's too recent because we might still write to it:
$file_delay = 1 if $file_delay < 1;
}
elsif (m{^/home/[^/]+/[^/]+\.log(?:\.gz|\.xz)?$}) {
$filetype = 'userlog';
$file_delay = $config{'encrypt_and_move_user_logs_delay_days'};
# never touch a file that's too recent because we might still write to it:
$file_delay = 31 if $file_delay < 31;
}
elsif (m{^/home/[^/]+/[^/]+\.sqlite(?:\.gz|\.xz)?$}) {
$filetype = 'usersqlite';
$file_delay = $config{'encrypt_and_move_user_sqlites_delay_days'};
# never touch a file that's too recent because we might still write to it:
$file_delay = 31 if $file_delay < 31;
}
else {
# ignore this file
return;
}
# we might not have the right to touch some filetypes, as per config
return if ($file_delay < 0);
# $_ must exist and be a file
-f or return;
my $file = $_;
# first, check if we populated $openedFiles as a hashref
# first, populate (once) the list of ttyrec files that are still opened by ttyrec
state $openedFiles;
if (ref $openedFiles ne 'HASH') {
$openedFiles = {};
if (open(my $fh_lsof, '-|', "lsof -a -n -c ttyrec -- /home/")) {
@ -323,25 +478,20 @@ sub potentially_work_on_this_file {
}
}
# still open ? don't touch
# still open? don't touch
if (exists $openedFiles->{$file}) {
_log "File $file is still opened by ttyrec, skipping";
return;
}
# and must be older than encrypt_and_move_delay_days days
# ignore files that are too recent (as per config)
my $mtime = (stat($file))[9];
if ($mtime > time() - 86400 * $config{'encrypt_and_move_delay_days'}) {
_log "File $file is too recent, skipping" if $verbose;
return;
}
# for proxylog, never touch a file that's < 86400 sec old (because we might still write to it)
if ($filetype eq 'proxylog' and $mtime > time() - 86400) {
_log "File $file is too recent (proxylog), skipping" if $verbose;
if ($mtime > time() - 86400 * $file_delay) {
_log "File $file is too recent ($filetype: $file_delay days), skipping" if $verbose;
return;
}
# ok, this file is eligible, go
my $error = encrypt_multi(source_file => $file, destination_directory => $config{'encrypt_and_move_to_directory'}, remove_source_on_success => not $noDelete);
if ($error) {
_err "Got an error for $file, skipping!";
@ -356,6 +506,7 @@ sub directory_filter { ## no critic (RequireArgUnpacking)
if ($File::Find::dir eq '/home') {
my @out = ();
foreach (@_) {
push @out, $_ if -e "/home/$_/lastlog";
if (-d "/home/$_/ttyrec") {
push @out, $_ if -d "/home/$_/ttyrec";
}
@ -376,17 +527,20 @@ sub print_usage {
$0 [options]
--dry-run Don't actually compress/encrypt/rsync, just show what would be done
--config-test Test the validity of the config file
--verbose More logs
rsync phase:
--no-delete During the encrypt phase, don't delete the source files after successful encryption
--rsync-only Skip the encrypt phase, just rsync the already encrypted & moved files
--force-rsync Don't wait for the configured number of days before rsyncing files, do it immediately
--config-test Test the validity of the config file and GPG setup
--verbose More logs, use twice to also get gpg raw output
encryption phase:
--encrypt-only Skip the rsync phase, just encrypt & move the files
--force-encrypt Don't wait for the configured number of days before encrypting & moving files, do it immediately
--encrypt-only Encrypt and move the files, but skip the rsync phase
--force-encrypt Don't wait for the configured number of days before encrypting & moving files, do it immediately.
Note that filetypes that have their amount of days set to -1 from the config file will still be ignored,
and the minimum configurable amount of time still applies per filetype (i.e. to avoid moving a file still in use).
rsync phase:
--rsync-only Skip the encryption phase, just rsync the already encrypted & moved files
--force-delete Don't wait for the configured number of days before deleting rsynced files,
do it as soon as they're transferred
--no-delete Don't delete local files after rsyncing, even if the configured amount of days has passed
EOF
return;
@ -402,9 +556,9 @@ sub main {
"no-delete" => \$noDelete,
"encrypt-only" => \$encryptOnly,
"rsync-only" => \$rsyncOnly,
"force-rsync" => \$forceRsync,
"force-delete" => \$forceDelete,
"force-encrypt" => \$forceEncrypt,
"verbose" => \$verbose,
"verbose+" => \$verbose,
"help" => \$help,
)
)
@ -419,55 +573,16 @@ sub main {
return 0;
}
# we can have CONFIGDIR/osh-encrypt-rsync.conf
# but also CONFIGDIR/osh-encrypt-rsync.conf.d/*
# later files override the previous ones, item by item
my $fnret;
my $lockfile;
my @configfilelist;
if (-f -r OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf") {
push @configfilelist, OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf";
}
if (-d -x OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf.d") {
if (opendir(my $dh, OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf.d")) {
my @subfiles = map { OVH::Bastion::main_configuration_directory() . "/osh-encrypt-rsync.conf.d/" . $_ } grep { /\.conf$/ } readdir($dh);
closedir($dh);
push @configfilelist, sort @subfiles;
}
}
if (not @configfilelist) {
_err "Error, no config file found!";
if (config_load_and_lint() != 0) {
_err "Configuration is invalid, aborting";
return 1;
}
foreach my $configfile (@configfilelist) {
_log "Configuration: loading configfile $configfile...";
$fnret = OVH::Bastion::load_configuration_file(
file => $configfile,
rootonly => 1,
);
if (not $fnret) {
_err "Error while loading configuration from $configfile, aborting (" . $fnret->msg . ")";
return 1;
}
foreach my $key (keys %{$fnret->value}) {
$config{$key} = $fnret->value->{$key};
}
# we'll be using our own config file as a handy flock() backend
$lockfile = $configfile if not defined $lockfile;
}
$verbose ||= $config{'verbose'};
# ensure no other copy of myself is already running
# except if we are in rsync-only mode (concurrency is then not a problem)
my $lockfh;
if (not $rsyncOnly) {
if (!open($lockfh, '<', $lockfile)) {
if (!open($lockfh, '<', $config{'lockfile'})) {
# flock() needs a file handler
_log "Couldn't open config file, aborting";
@ -479,33 +594,20 @@ sub main {
}
}
# default values
$config{'syslog_facility'} ||= 'local6';
# ensure the various config files defined all the keywords we need
foreach my $keyword (
qw{
signing_key signing_key_passphrase recipients encrypt_and_move_to_directory
encrypt_and_move_delay_days rsync_delay_before_remove_days
}
)
{
next if defined $config{$keyword};
_err "Missing mandatory configuration item '$keyword', aborting";
return 1;
}
OVH::SimpleLog::setLogFile($config{'logfile'}) if $config{'logfile'};
OVH::SimpleLog::setSyslog($config{'syslog_facility'}) if $config{'syslog_facility'};
if ($forceRsync) {
$config{'rsync_delay_days'} = 0;
if ($forceDelete) {
$config{'rsync_delay_before_remove_days'} = 0;
}
if ($forceEncrypt) {
$config{'encrypt_and_move_delay_days'} = 0;
foreach my $type (qw{ ttyrec user_logs useer_sqlites }) {
# keep config at -1 if it's set at -1 (i.e. filetype disabled)
$config{"encrypt_and_move_${type}_delay_days"} = 0 if $config{"encrypt_and_move_${type}_delay_days"} > 0;
}
}
if (test_config() != 0) {
$verbose ||= $config{'verbose'};
if (config_test() != 0) {
_err "Config test failed, aborting";
return 1;
}
@ -600,7 +702,7 @@ sub main {
}
}
_log "Done";
_log "Done, got " . (OVH::SimpleLog::nb_errors()) . " error(s) and " . (OVH::SimpleLog::nb_warnings()) . " warning(s).";
return 0;
}

View file

@ -5,13 +5,16 @@ osh-encrypt-rsync.conf reference
.. note::
The osh-encrypt-rsync script is called by cron and is responsible for encrypting
and optionally pushing the recorded ``ttyrec`` files to a distant server.
and optionally pushing the recorded ``ttyrec`` files to a distant server, along
with the user logs (``/home/*/*.log``) and user sqlite files (``/home/*/*.sqlite``).
The global log and sqlite files are also handled (located in ``/home/logkeeper/``).
Note that logs send through syslog are NOT managed by this script.
.. warning::
If left unconfigured, this script won't do anything, and recorded ``ttyrec`` files
won't be encrypted or moved out from the server. This might not be a problem for
low-traffic bastions or if you have plenty of storage available, though.
If left unconfigured, this script won't do anything, and the recorded ``ttyrec`` files,
along with the log and sqlite files won't be encrypted or moved out from the server.
This might not be a problem for low-traffic bastions or if you have plenty of storage available, though.
Option List
===========

View file

@ -33,7 +33,9 @@ These options configure how the script uses GPG to encrypt and sign the ttyrec f
- `signing_key_passphrase`_
- `recipients`_
- `encrypt_and_move_to_directory`_
- `encrypt_and_move_delay_days`_
- `encrypt_and_move_ttyrec_delay_days`_
- `encrypt_and_move_user_logs_delay_days`_
- `encrypt_and_move_user_sqlites_delay_days`_
Push files to a remote destination options
------------------------------------------
@ -57,16 +59,21 @@ logfile
:Default: ``""``
File where the logs will be written to (don't forget to configure ``logrotate``!). Note that using this configuration option, the script will directly write to the file, without using syslog. If empty, won't log directly to any file.
File where the logs will be written to (don't forget to configure ``logrotate``!).
Note that using this configuration option, the script will directly write to the file, without using syslog.
If empty, won't log directly to any file.
syslog_facility
***************
:Type: ``string``
:Default: ``local6``
:Default: ``"local6"``
The syslog facility to use for logging the script output. If set to the empty string, we'll not log through syslog at all. If this configuration option is missing from your config file altogether, the default value will be used (local6), which means that we'll log to syslog.
The syslog facility to use for logging the script output.
If set to the empty string, we'll not log through syslog at all.
If this configuration option is missing from your config file altogether,
the default value will be used (local6), which means that we'll log to syslog.
Encryption and signing
----------------------
@ -78,7 +85,8 @@ signing_key
:Default: ``(none), setting a value is mandatory``
ID of the GPG key used to sign the ttyrec files. The key must be in the local root keyring, check it with ``gpg --list-secret-keys``
ID of the GPG key used to sign the ttyrec files.
The key must be in the local root keyring, check it with ``gpg --list-secret-keys``
signing_key_passphrase
**********************
@ -87,7 +95,10 @@ signing_key_passphrase
:Default: ``(none), setting a value is mandatory``
This passphrase should be able to unlock the ``signing_key`` defined above. As a side note, please ensure this configuration file only readable by root (0640), to protect this passphrase. As a security measure, the script will refuse to read the configuration otherwise.
This passphrase should be able to unlock the ``signing_key`` defined above.
As a side note, please ensure this configuration file only readable by root (0640),
to protect this passphrase. As a security measure,
the script will refuse to read the configuration otherwise.
recipients
**********
@ -104,26 +115,65 @@ To encrypt to a single layer but with 3 keys being able to decrypt the ttyrec, u
A common use of multi-layer encryption is to have the first layer composed of the auditors' GPG keys, and
the second layer composed of the sysadmins' GPG keys. During an audit, the sysadmins would get the ttyrec encrypted file,
decrypt the second encryption layer (the first for decryption), and handle the now only auditor-protected file to the auditors.
All public keys must be in the local keyring (gpg --list-keys).
Don't forget to trust those keys "ultimately" in your keyring, too (gpg --edit-key ID)
All public keys must be in the local root keyring (gpg --list-keys).
Don't forget to trust those keys "ultimately" in root's keyring, too (gpg --edit-key ID)
encrypt_and_move_to_directory
*****************************
:Type: ``string, a valid directory name``
:Default: ``/home/.encrypt``
:Default: ``"/home/.encrypt"``
After encryption (and compression), move ttyrec files to subdirs of this directory. It'll be created if it doesn't exist yet. You may want this directory to be the mount point of a remote filer, if you wish. If you change this, it's probably a good idea to ensure that the path is excluded from the master/slave synchronization, in ``/etc/bastion/osh-sync-watcher.rsyncfilter``. This is already the case for the default value.
After encryption (and compression), move ttyrec, user sqlite and user log files to subdirs of this directory.
It'll be created if it doesn't exist yet.
You may want this directory to be the mount point of a remote filer, if you wish.
If you change this, it's probably a good idea to ensure that the path is excluded from the
master/slave synchronization, in ``/etc/bastion/osh-sync-watcher.rsyncfilter``.
This is already the case for the default value.
encrypt_and_move_delay_days
***************************
encrypt_and_move_ttyrec_delay_days
**********************************
:Type: ``int > 0``
:Type: ``int > 0, or -1``
:Default: ``14``
Don't touch ttyrec files that have a modification time more recent than this amount of days. They won't be encrypted nor moved yet, and will still be readable by the ``selfPlaySession`` command.
Don't touch ttyrec files that have a modification time more recent than this amount of days.
The files won't be encrypted nor moved yet, and will still be readable by the ``selfPlaySession`` command.
You can set this to a (possibly) much higher value, the only limit is the amount of disk space you have.
If set to -1, the ttyrec files will never get encrypted or moved by this script.
The eligible files will be encrypted and moved to ``encrypt_and_move_to_directory``.
NOTE: The old name of this option is `encrypt_and_move_delay_days`.
If it is found in your configuration file and `encrypt_and_move_ttyrec_delay_days` is not,
then the value of `encrypt_and_move_delay_days` will be used instead of the default.
encrypt_and_move_user_logs_delay_days
*************************************
:Type: ``int >= 31, or -1``
:Default: ``31``
Don't touch user log files (``/home/*/*.log``) that have been modified more recently than this amount of days.
The bare minimum is 31 days, to ensure we're not moving a current-month file.
You can set this to a (possibly) much higher value, the only limit is the amount of disk space you have.
If set to -1, the user log files will never get encrypted or moved by this script.
The eligible files will be encrypted and moved to ``encrypt_and_move_to_directory``.
encrypt_and_move_user_sqlites_delay_days
****************************************
:Type: ``int >= 31, or -1``
:Default: ``31``
Don't touch user sqlite files (``/home/*/*.sqlite``) that have been modified more recently than this amount of days.
The files won't be encrypted nor moved yet, and will still be usable by the ``selfListSessions`` command.
The bare minimum is 31 days, to ensure we're not moving a current-month file.
You can set this to a (possibly) much higher value, the only limit is the amount of disk space you have.
If set to -1, the user sqlite files will never get encrypted or moved by this script.
The eligible files will be encrypted and moved to ``encrypt_and_move_to_directory``.
Push files to a remote destination
----------------------------------
@ -135,9 +185,14 @@ rsync_destination
:Default: ``""``
:Example: ``user@remotebackup.example.org:/remote/dir``
:Example: ``"user@remotebackup.example.org:/remote/dir"``
The value of this option will be passed to ``rsync`` as the destination. If empty, this will **disable** ``rsync``, meaning that the ttyrec files will be encrypted, but not moved out of the server.
The value of this option will be passed to ``rsync`` as the destination.
Note that the source of the rsync is already configured above, as the ``encrypt_and_move_to_directory``.
We only rsync the files that have already been encrypted and moved there.
If this option is empty, this will **disable** ``rsync``, meaning that the ttyrec files will be encrypted,
but not moved out of the server. In other words, the files will pile up in ``encrypt_and_move_to_directory``,
which can be pretty okay in you have enough disk space.
rsync_rsh
*********
@ -146,16 +201,27 @@ rsync_rsh
:Default: ``""``
:Example: ``ssh -p 222 -i /root/.ssh/id_ed25519_backup``
:Example: ``"ssh -p 222 -i /root/.ssh/id_ed25519_backup"``
The value of this option will be passed to ``rsync``'s ``--rsh`` option. This is useful to specify an SSH key or an alternate SSH port for example. This option is ignored when ``rsync`` is disabled (i.e. when ``rsync_destination`` is empty).
The value of this option will be passed to ``rsync``'s ``--rsh`` option.
This is useful to specify an SSH key or an alternate SSH port for example.
This option is ignored when ``rsync`` is disabled (i.e. when ``rsync_destination`` is empty).
rsync_delay_before_remove_days
******************************
:Type: ``int >= 0``
:Type: ``int >= 0, or -1``
:Default: ``0``
After encryption/compression, and successful rsync to remote, wait for this amount of days before removing the encrypted/compressed files locally. Specify 0 to remove the files as soon as they're transferred. This option is ignored when ``rsync`` is disabled (i.e. when ``rsync_destination`` is empty).
After encryption/compression, and successful rsync of ``encrypt_and_move_to_directory`` to remote,
wait for this amount of days before removing the encrypted/compressed files locally.
Specify 0 to remove the files as soon as they're transferred.
This option is ignored when ``rsync`` is disabled (i.e. when ``rsync_destination`` is empty).
Note that if rsync is enabled (see ``rsync_destination`` above), we'll always sync the files present in
``encrypt_and_move_to_directory`` as soon as we can, to ensure limitation of logs data loss in case of
catastrophic failure of the server. The ``rsync_delay_before_remove_days`` option configures the number
of days after we remove the files locally, but note that these have already been transferred remotely
as soon as they were present in ``encrypt_and_move_to_directory``.
To rsync the files remotely but never delete them locally, set this to -1.

View file

@ -1,7 +1,10 @@
###################################################################
## Config for /opt/bastion/bin/cron/osh-encrypt-rsync.pl, the script
## responsible for signing and encrypting with GPG, then rotating and
## pushing to an external system the produced ttyrec files.
## pushing to an external system the following files:
## - User-produced ttyrec files (in their home's ttyrec/ folder)
## - User-scoped sqlite files (/home/*/*.sqlite)
## - User-scoped log files (/home/*/*.log)
##
## Any file in /etc/bastion/osh-encrypt-rsync.conf.d will also be
## parsed, in alphabetical order, and take precedence over any
@ -17,25 +20,34 @@
# >> These options configure the way the script logs its actions
#
# logfile (string, path to a file)
# DESC: File where the logs will be written to (don't forget to configure ``logrotate``!). Note that using this configuration option, the script will directly write to the file, without using syslog. If empty, won't log directly to any file.
# DESC: File where the logs will be written to (don't forget to configure ``logrotate``!).
# Note that using this configuration option, the script will directly write to the file, without using syslog.
# If empty, won't log directly to any file.
# DEFAULT: ""
"logfile": "",
#
# syslog_facility (string)
# DESC: The syslog facility to use for logging the script output. If set to the empty string, we'll not log through syslog at all. If this configuration option is missing from your config file altogether, the default value will be used (local6), which means that we'll log to syslog.
# DEFAULT: local6
# DESC: The syslog facility to use for logging the script output.
# If set to the empty string, we'll not log through syslog at all.
# If this configuration option is missing from your config file altogether,
# the default value will be used (local6), which means that we'll log to syslog.
# DEFAULT: "local6"
"syslog_facility": "local6",
#
# > Encryption and signing
# >> These options configure how the script uses GPG to encrypt and sign the ttyrec files
#
# signing_key (string, GPG key ID in short or long format)
# DESC: ID of the GPG key used to sign the ttyrec files. The key must be in the local root keyring, check it with ``gpg --list-secret-keys``
# DESC: ID of the GPG key used to sign the ttyrec files.
# The key must be in the local root keyring, check it with ``gpg --list-secret-keys``
# DEFAULT: (none), setting a value is mandatory
"signing_key": "FFFFFFFF",
#
# signing_key_passphrase (string)
# DESC: This passphrase should be able to unlock the ``signing_key`` defined above. As a side note, please ensure this configuration file only readable by root (0640), to protect this passphrase. As a security measure, the script will refuse to read the configuration otherwise.
# DESC: This passphrase should be able to unlock the ``signing_key`` defined above.
# As a side note, please ensure this configuration file only readable by root (0640),
# to protect this passphrase. As a security measure,
# the script will refuse to read the configuration otherwise.
# DEFAULT: (none), setting a value is mandatory
"signing_key_passphrase": "configure_this_passphrase",
#
@ -48,8 +60,8 @@
# A common use of multi-layer encryption is to have the first layer composed of the auditors' GPG keys, and
# the second layer composed of the sysadmins' GPG keys. During an audit, the sysadmins would get the ttyrec encrypted file,
# decrypt the second encryption layer (the first for decryption), and handle the now only auditor-protected file to the auditors.
# All public keys must be in the local keyring (gpg --list-keys).
# Don't forget to trust those keys "ultimately" in your keyring, too (gpg --edit-key ID)
# All public keys must be in the local root keyring (gpg --list-keys).
# Don't forget to trust those keys "ultimately" in root's keyring, too (gpg --edit-key ID)
# DEFAULT: (none), setting a value is mandatory
"recipients": [
[ "AAAAAAAA", "BBBBBBBB" ],
@ -57,32 +69,79 @@
],
#
# encrypt_and_move_to_directory (string, a valid directory name)
# DESC: After encryption (and compression), move ttyrec files to subdirs of this directory. It'll be created if it doesn't exist yet. You may want this directory to be the mount point of a remote filer, if you wish. If you change this, it's probably a good idea to ensure that the path is excluded from the master/slave synchronization, in ``/etc/bastion/osh-sync-watcher.rsyncfilter``. This is already the case for the default value.
# DEFAULT: /home/.encrypt
# DESC: After encryption (and compression), move ttyrec, user sqlite and user log files to subdirs of this directory.
# It'll be created if it doesn't exist yet.
# You may want this directory to be the mount point of a remote filer, if you wish.
# If you change this, it's probably a good idea to ensure that the path is excluded from the
# master/slave synchronization, in ``/etc/bastion/osh-sync-watcher.rsyncfilter``.
# This is already the case for the default value.
# DEFAULT: "/home/.encrypt"
"encrypt_and_move_to_directory": "/home/.encrypt",
#
# encrypt_and_move_delay_days (int > 0)
# DESC: Don't touch ttyrec files that have a modification time more recent than this amount of days. They won't be encrypted nor moved yet, and will still be readable by the ``selfPlaySession`` command.
# encrypt_and_move_ttyrec_delay_days (int > 0, or -1)
# DESC: Don't touch ttyrec files that have a modification time more recent than this amount of days.
# The files won't be encrypted nor moved yet, and will still be readable by the ``selfPlaySession`` command.
# You can set this to a (possibly) much higher value, the only limit is the amount of disk space you have.
# If set to -1, the ttyrec files will never get encrypted or moved by this script.
# The eligible files will be encrypted and moved to ``encrypt_and_move_to_directory``.
# NOTE: The old name of this option is `encrypt_and_move_delay_days`.
# If it is found in your configuration file and `encrypt_and_move_ttyrec_delay_days` is not,
# then the value of `encrypt_and_move_delay_days` will be used instead of the default.
# DEFAULT: 14
"encrypt_and_move_delay_days": 14,
"encrypt_and_move_ttyrec_delay_days": 14,
#
# encrypt_and_move_user_logs_delay_days (int >= 31, or -1)
# DESC: Don't touch user log files (``/home/*/*.log``) that have been modified more recently than this amount of days.
# The bare minimum is 31 days, to ensure we're not moving a current-month file.
# You can set this to a (possibly) much higher value, the only limit is the amount of disk space you have.
# If set to -1, the user log files will never get encrypted or moved by this script.
# The eligible files will be encrypted and moved to ``encrypt_and_move_to_directory``.
# DEFAULT: 31
"encrypt_and_move_user_logs_delay_days": 31,
#
# encrypt_and_move_user_sqlites_delay_days (int >= 31, or -1)
# DESC: Don't touch user sqlite files (``/home/*/*.sqlite``) that have been modified more recently than this amount of days.
# The files won't be encrypted nor moved yet, and will still be usable by the ``selfListSessions`` command.
# The bare minimum is 31 days, to ensure we're not moving a current-month file.
# You can set this to a (possibly) much higher value, the only limit is the amount of disk space you have.
# If set to -1, the user sqlite files will never get encrypted or moved by this script.
# The eligible files will be encrypted and moved to ``encrypt_and_move_to_directory``.
# DEFAULT: 31
"encrypt_and_move_user_sqlites_delay_days": 31,
#
# > Push files to a remote destination
# >> These options configure the way the script uses rsync to optionally push the encrypted files out of the server
#
# rsync_destination (string)
# DESC: The value of this option will be passed to ``rsync`` as the destination. If empty, this will **disable** ``rsync``, meaning that the ttyrec files will be encrypted, but not moved out of the server.
# DESC: The value of this option will be passed to ``rsync`` as the destination.
# Note that the source of the rsync is already configured above, as the ``encrypt_and_move_to_directory``.
# We only rsync the files that have already been encrypted and moved there.
# If this option is empty, this will **disable** ``rsync``, meaning that the ttyrec files will be encrypted,
# but not moved out of the server. In other words, the files will pile up in ``encrypt_and_move_to_directory``,
# which can be pretty okay in you have enough disk space.
# DEFAULT: ""
# EXAMPLE: user@remotebackup.example.org:/remote/dir
# EXAMPLE: "user@remotebackup.example.org:/remote/dir"
"rsync_destination": "",
#
# rsync_rsh (string)
# DESC: The value of this option will be passed to ``rsync``'s ``--rsh`` option. This is useful to specify an SSH key or an alternate SSH port for example. This option is ignored when ``rsync`` is disabled (i.e. when ``rsync_destination`` is empty).
# EXAMPLE: ssh -p 222 -i /root/.ssh/id_ed25519_backup
# DESC: The value of this option will be passed to ``rsync``'s ``--rsh`` option.
# This is useful to specify an SSH key or an alternate SSH port for example.
# This option is ignored when ``rsync`` is disabled (i.e. when ``rsync_destination`` is empty).
# EXAMPLE: "ssh -p 222 -i /root/.ssh/id_ed25519_backup"
# DEFAULT: ""
"rsync_rsh": "",
#
# rsync_delay_before_remove_days (int >= 0)
# DESC: After encryption/compression, and successful rsync to remote, wait for this amount of days before removing the encrypted/compressed files locally. Specify 0 to remove the files as soon as they're transferred. This option is ignored when ``rsync`` is disabled (i.e. when ``rsync_destination`` is empty).
# rsync_delay_before_remove_days (int >= 0, or -1)
# DESC: After encryption/compression, and successful rsync of ``encrypt_and_move_to_directory`` to remote,
# wait for this amount of days before removing the encrypted/compressed files locally.
# Specify 0 to remove the files as soon as they're transferred.
# This option is ignored when ``rsync`` is disabled (i.e. when ``rsync_destination`` is empty).
# Note that if rsync is enabled (see ``rsync_destination`` above), we'll always sync the files present in
# ``encrypt_and_move_to_directory`` as soon as we can, to ensure limitation of logs data loss in case of
# catastrophic failure of the server. The ``rsync_delay_before_remove_days`` option configures the number
# of days after we remove the files locally, but note that these have already been transferred remotely
# as soon as they were present in ``encrypt_and_move_to_directory``.
# To rsync the files remotely but never delete them locally, set this to -1.
# DEFAULT: 0
"rsync_delay_before_remove_days": 0
}

View file

@ -21,6 +21,11 @@ my $FACILITY;
# Program name
my $PROGNAME;
# Incremented at each call of _err and _warn, count can be
# fetched with nb_errors() and nb_warnings()
my $nb_errors = 0;
my $nb_warnings = 0;
BEGIN {
# Extract program base name
$PROGNAME = $0;
@ -59,9 +64,9 @@ sub closeSyslog {
return 1;
}
sub _log { _display('LOG', @_); return 1; } ## no critic (RequireArgUnpacking,ProhibitUnusedPrivateSubroutines)
sub _warn { _display('WARN', @_); return 1; } ## no critic (RequireArgUnpacking,ProhibitUnusedPrivateSubroutines)
sub _err { _display('ERR', @_); return 1; } ## no critic (RequireArgUnpacking,ProhibitUnusedPrivateSubroutines)
sub _log { _display('LOG', @_); return 1; } ## no critic (RequireArgUnpacking,ProhibitUnusedPrivateSubroutines)
sub _warn { _display('WARN', @_); $nb_warnings++; return 1; } ## no critic (RequireArgUnpacking,ProhibitUnusedPrivateSubroutines)
sub _err { _display('ERR', @_); $nb_errors++; return 1; } ## no critic (RequireArgUnpacking,ProhibitUnusedPrivateSubroutines)
# Display a message
sub _display {
@ -115,6 +120,9 @@ sub _display {
return 1;
}
sub nb_errors { return $nb_errors; }
sub nb_warnings { return $nb_warnings; }
END {
close($LOG_FH) if (defined $LOG_FH);
Sys::Syslog::closelog() if (defined $FACILITY);

View file

@ -47,14 +47,6 @@ testsuite_scripts()
nocontain "ERROR:"
nocontain "Unexpected termination"
# compress old logs
success compress_old_logs $r0 /opt/bastion/bin/cron/osh-compress-old-logs.sh
contain "Done"
nocontain "WARN:"
nocontain "ERROR:"
nocontain "Unexpected termination"
# lingering sessions reaper
success lingering_sessions_reaper $r0 /opt/bastion/bin/cron/osh-lingering-sessions-reaper.sh