2020-10-16 00:32:37 +08:00
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
package OVH :: Bastion ;
use common :: sense ;
use DBD :: SQLite ;
use Time :: HiRes ;
use Sys :: Syslog qw ();
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
use JSON ;
2020-10-16 00:32:37 +08:00
my $_syslog_inited = 0 ; # lazy init
sub syslog {
my ( $criticity , $message ) = @ _ ;
if ( not OVH :: Bastion :: config ( 'enableSyslog' ) -> value ()) {
return 1 ; # don't do anything
}
if ( not $_syslog_inited ) {
$_syslog_inited = 1 ;
Sys :: Syslog :: openlog ( OVH :: Bastion :: config ( 'syslogDescription' ) -> value (), 'nofatal' , OVH :: Bastion :: config ( 'syslogFacility' ) -> value ());
}
# if message is tainted, forcefully untaint it
# or we'll crash and we won't even log that
# we crashed because it's tainted so we would
# crash trying to log the crash
( $message ) = $message =~ /^ ( .* ) $ / ;
eval { Sys :: Syslog :: syslog ( $criticity , $message ); };
if ( $ @ ) {
osh_warn ( " Couldn't syslog, report to administrator ( $ @) " );
}
return 1 ;
}
sub syslog_close {
if ( $_syslog_inited ) {
Sys :: Syslog :: closelog ();
$_syslog_inited = 0 ;
}
2020-12-15 18:20:08 +08:00
return ;
2020-10-16 00:32:37 +08:00
}
END {
syslog_close ();
}
sub syslogFormatted {
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# do NOT use warn_syslog in this func, or we would loop. warn() is fine.
2020-10-16 00:32:37 +08:00
my % params = @ _ ;
my $criticity = $params { 'criticity' } || 'info' ;
my $type = $params { 'type' } || 'unknown' ;
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
my $fieldsref = $params { 'fields' };
2020-10-16 00:32:37 +08:00
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
if ( ref $fieldsref ne 'ARRAY' ) {
2020-10-16 00:32:37 +08:00
my $error = " bad call to syslogFormatted, invalid fields parameter " ;
warn ( $error ); # this will in turn log into syslog with the trace() thanks to the SIG{'WARN'} handler
return R ( 'INTERNAL_ERROR' , msg => $error );
}
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
my @ fields = @ $fieldsref ;
unshift @ fields , [ 'gid' , (( split ( / / , $ )))[ 0 ])];
unshift @ fields , [ 'uid' , $ > ];
unshift @ fields , [ 'sudo_user' , $ENV { 'SUDO_USER' }];
unshift @ fields , [ 'sysuser' , OVH :: Bastion :: get_user_from_env () -> value ];
unshift @ fields , [ 'ppid' , getppid ()];
unshift @ fields , [ 'pid' , $ $ ];
unshift @ fields , [ 'version' , $OVH :: Bastion :: VERSION ];
unshift @ fields , [ 'uniqid' , ( $ENV { 'UNIQID' } || '-' )];
2020-10-16 00:32:37 +08:00
my @ msg = ( $type );
if (( $type eq 'die' || $type eq 'warn' ) && $criticity eq 'info' ) {
# in that case, "downgrade" the criticity of the message
@ msg = ( " $type - $criticity " );
}
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
my % dedupe ;
foreach my $item ( @ fields ) {
2020-10-16 00:32:37 +08:00
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# each $item item is a 2-dimensional array for key => value
2020-10-16 00:32:37 +08:00
if ( ref $item ne 'ARRAY' ) {
my $error = " bad call to syslogFormatted, invalid item in fields (ref " . ( ref $item ) . " ) " ;
warn ( $error );
return R ( 'INTERNAL_ERROR' , msg => $error );
}
my ( $key , $value ) = @ $item ;
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# warn if we have the same key more than once
if ( exists $dedupe { $key }) {
warn ( " syslogFormatted(): got multiple ' $key ' keys " );
}
$dedupe { $key } = $value ;
2020-10-16 00:32:37 +08:00
# remove any \n in the value
$value =~ s / \n / / g ;
# replace any \ by an escaped \ aka \\
$value =~ s { \\ }{ \\\\ } g ;
# replace any " by \"
$value =~ s { " } { \\ " } g ;
push @ msg , qq { $key = " $value " };
}
my $flatmsg = join ( " " , @ msg );
OVH :: Bastion :: syslog ( $criticity , $flatmsg );
return R ( 'OK' , value => $flatmsg );
}
sub warn_syslog {
2020-12-14 20:55:42 +08:00
my $msg = shift ;
my $noisy = shift ;
osh_warn ( " WARN: " . $msg ) if $noisy ;
2020-10-16 00:32:37 +08:00
return syslogFormatted (
type => 'code-warning' ,
fields => [[ 'msg' => $msg ]]
);
}
sub _sql_update_db {
my % params = @ _ ;
my $sqltype = $params { 'sqltype' };
my $dbh = $params { 'dbh' };
my $sth ;
my $result ;
my $fnret ;
$dbh -> do ( " PRAGMA synchronous=0 " );
# get current user_version of db
$sth = $dbh -> prepare ( " PRAGMA user_version " );
return R ( 'KO' , msg => " getting user_version (prepare) " ) if not $sth ;
$result = $sth -> execute ();
return R ( 'KO' , msg => " getting user_version (execute) " ) if not $result ;
my $user_version = $sth -> fetchrow_array ();
if ( $user_version <= 0 ) {
# set journal_mode, this is a no-op if already in WAL
if ( ! $dbh -> do ( " PRAGMA journal_mode=WAL " )) {
return R ( 'KO' , msg => " setting journal mode " );
}
# create table
$result = $dbh -> do (
$sqltype eq 'local'
2020-12-15 18:20:08 +08:00
? << 'EOS'
CREATE TABLE IF NOT EXISTS connections (
2020-10-16 00:32:37 +08:00
id INTEGER PRIMARY KEY ,
timestamp INTEGER ,
timestampusec INTEGER ,
account TEXT ,
cmdtype TEXT ,
allowed INTEGER ,
hostfrom TEXT ,
ipfrom TEXT ,
portfrom INTEGER ,
bastionip TEXT ,
bastionport INTEGER ,
hostto TEXT ,
ipto TEXT ,
portto INTEGER ,
user TEXT ,
plugin TEXT ,
ttyrecfile TEXT ,
ttyrecsize INTEGER ,
params TEXT ,
timestampend INTEGER ,
timestampendusec INTEGER ,
returnvalue INTEGER ,
comment TEXT ,
2020-12-15 18:20:08 +08:00
uniqid TEXT )
EOS
: << 'EOS'
CREATE TABLE IF NOT EXISTS connections_summary (
2020-10-16 00:32:37 +08:00
id INTEGER PRIMARY KEY ,
timestamp INTEGER ,
account TEXT ,
cmdtype TEXT ,
allowed INTEGER ,
ipfrom TEXT ,
ipto TEXT ,
portto INTEGER ,
user TEXT ,
plugin TEXT ,
2020-12-15 18:20:08 +08:00
uniqid TEXT )
EOS
2020-10-16 00:32:37 +08:00
);
return R ( 'KO' , msg => " creating table ( $sqltype ) " ) if not $result ;
# create indexes if needed
my @ columns = (
$sqltype eq 'local'
? qw { timestamp ipto uniqid }
: qw { timestamp ipto uniqid }
);
my $table = ( $sqltype eq 'local' ? " connections " : " connections_summary " );
foreach my $column ( @ columns ) {
$dbh -> do ( " CREATE INDEX IF NOT EXISTS idx_ $column ON $table ( $column ) " )
or return R ( 'KO' , msg => " creating index idx_ $column on $table " );
}
$dbh -> do ( " PRAGMA user_version=1 " )
or return R ( 'KO' , msg => " setting user_version to 1 " );
$user_version = 1 ;
}
# endof version==0
if ( $user_version == 1 ) {
; # insert here future schema modifications
}
return R ( 'OK' , msg => " sql update done " );
}
sub _sql_log_insert_file {
# don't call me directly, use sql_log_insert() !
my % params = @ _ ;
my $file = $params { 'file' };
my $account = $params { 'account' };
my $cmdtype = $params { 'cmdtype' };
my $allowed = $params { 'allowed' };
my $hostfrom = $params { 'hostfrom' };
my $ipfrom = $params { 'ipfrom' };
my $portfrom = $params { 'portfrom' };
my $bastionip = $params { 'bastionip' };
my $bastionport = $params { 'bastionport' };
my $hostto = $params { 'hostto' };
my $ipto = $params { 'ipto' };
my $portto = $params { 'portto' };
my $user = $params { 'user' };
my $plugin = $params { 'plugin' };
my $params = $params { 'params' };
my $comment = $params { 'comment' };
2021-01-13 00:12:43 +08:00
my $ttyrecfile = $params { 'ttyrecfile' };
2020-10-16 00:32:37 +08:00
my $timestamp = $params { 'timestamp' };
my $timestampusec = $params { 'timestampusec' };
my $uniqid = $params { 'uniqid' };
my $sqltype = $params { 'sqltype' };
if ( $sqltype ne 'local' and $sqltype ne 'global' ) {
return R ( 'ERR_INVALID_PARAMETER' , msg => " Invalid parameter sqltype " );
}
if ( $sqltype eq 'global' ) {
2021-01-12 17:47:38 +08:00
# if it doesn't exist yet, create the file ourselves, chgrp it to bastion-users and set the
# proper rights (0660). if the file already exists, we won't do anything.
2020-10-16 00:32:37 +08:00
# -journal -shm and -wal files are created with same perms by sqlite
2021-01-12 17:47:38 +08:00
OVH :: Bastion :: create_file_if_not_exists ( file => $file , perms => oct ( 660 ), group => " bastion-users " );
2020-10-16 00:32:37 +08:00
}
# big db-related retry block:
# open db, set journal_mode, create table if not exists, insert data
# opendb usually always works
# journal_mode can get you a weird transient error on high concurrency (such as "failed to open database")
# same for create and insert (such as "attempted to write on readonly database")
# ... so we'll retry up to 20 times if any error arises, starting from the beginning,
# to ensure we're not locked with a readonly-$dbh for some obscure race-condition-related reason
my ( $dbh , $sth , $result , $doing );
foreach my $retry ( 0 .. 19 ) {
# if we're retrying, sleep a bit before, to ease concurrency
2020-12-15 18:20:08 +08:00
sleep ( $retry / 50 + rand () / 10 ) if $retry ;
2020-10-16 00:32:37 +08:00
# on each retry, clean those vars (undef $dbh disconnects if connected)
undef $dbh ;
undef $sth ;
undef $result ;
undef $doing ;
# connect to db
$dbh = DBI -> connect ( " dbi:SQLite:dbname= $file " , " " , " " , { PrintError => 0 , RaiseError => 0 });
if ( ! $dbh ) {
$doing = " opening database " ;
next ; # retry
}
my $fnret = _sql_update_db ( dbh => $dbh , sqltype => $sqltype );
if ( ! $fnret ) {
$doing = $fnret -> msg ;
next ; # retry
}
# preparing data insertion query
my $prepare ;
my @ execute ;
if ( $sqltype eq 'local' ) {
@ execute = (
2021-01-13 00:12:43 +08:00
$uniqid , $timestamp , $timestampusec , $account , $cmdtype , $allowed , $hostfrom , $ipfrom , $portfrom , $bastionip ,
$bastionport , $hostto , $ipto , $portto , $user , $plugin , $params , $comment , $ttyrecfile
2020-10-16 00:32:37 +08:00
);
2021-01-27 00:18:42 +08:00
$prepare =
" INSERT INTO connections "
. " (uniqid,timestamp,timestampusec,account,cmdtype,allowed,hostfrom,ipfrom,portfrom,bastionip,bastionport,hostto,ipto,portto,user,plugin,params,comment,ttyrecfile) "
. 'VALUES ('
. ( '?,' x ( @ execute - 1 )) . '?)' ;
2020-10-16 00:32:37 +08:00
}
elsif ( $sqltype eq 'global' ) {
@ execute = ( $uniqid , $timestamp , $account , $cmdtype , $allowed , $ipfrom , $ipto , $portto , $user , $plugin );
2021-01-27 00:18:42 +08:00
$prepare = " INSERT INTO connections_summary (uniqid,timestamp,account,cmdtype,allowed,ipfrom,ipto,portto,user,plugin) " . 'VALUES (' . ( '?,' x ( @ execute - 1 )) . '?)' ;
2020-10-16 00:32:37 +08:00
}
# prepare insertion on db
$sth = $dbh -> prepare ( $prepare );
if ( ! $sth ) {
$doing = " inserting data (prepare) " ;
next ; # retry
}
# execute insertion
$result = $sth -> execute ( @ execute );
if ( ! $result ) {
$doing = " inserting data (execute) " ;
next ; # retry
}
# if we're here, it worked, stop retrying
last ;
}
# if this is set, we probably reached max retry in previous loop without succeeding
if ( $DBI :: err ) {
2021-01-08 01:11:43 +08:00
warn_syslog ( " Failed after multiple retries [ $sqltype ] err $DBI ::err while doing [ $doing ]: $DBI ::errstr ( $file ) " );
2020-10-16 00:32:37 +08:00
return R ( 'ERR_SQL_EXECUTE' , msg => " SQL error [ $sqltype ] err $DBI ::err while doing [ $doing ]: $DBI ::errstr " );
}
return R ( 'OK' , value => { id => $dbh -> last_insert_id ( " " , " " , " " , " " )});
}
sub log_access_insert {
my % params = @ _ ;
my $account = $params { 'account' };
my $uniqid = $params { 'uniqid' } || $ENV { 'UNIQID' };
my $loghome = $params { 'loghome' }; # only used for proxyhttp
my $custom = $params { 'custom' }; # only used for proxyhttp, not pushed to sql
my $fnret ;
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# if we get a valid account, we'll try to log to its files, if enabled.
# if not, or if its invalid, still try to log what we can (hence don't return here)
my ( $remoteaccount , $sysaccount );
if ( defined $account ) {
2021-01-08 01:11:43 +08:00
if ( $fnret = OVH :: Bastion :: is_bastion_account_valid_and_existing ( account => $account , accountType => " normal " )) {
# normal accounts
;
}
elsif ( $fnret = OVH :: Bastion :: is_bastion_account_valid_and_existing ( account => $account , accountType => " realm " )) {
# $account can also be a realm_xyz account, in case osh.pl is erroring early and couldn't resolve the
# proper realm-qualified account name (realm/user)
;
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
}
else {
undef $account ;
}
2021-01-08 01:11:43 +08:00
if ( $fnret ) {
$account = $fnret -> value -> { 'account' };
$sysaccount = $fnret -> value -> { 'sysaccount' };
$remoteaccount = $fnret -> value -> { 'remoteaccount' };
}
2020-10-16 00:32:37 +08:00
}
$loghome ||= $sysaccount ;
$params { 'account' } = $account ;
$params { 'loghome' } = $loghome ;
if ( not defined $uniqid ) {
return R ( 'ERR_MISSING_PARAMETER' , msg => " Missing parameter 'uniqid' " );
}
if ( not defined $params { 'bastionhost' } and defined $params { 'bastionip' }) {
$params { 'bastionhost' } = OVH :: Bastion :: ip2host ( $params { 'bastionip' }) -> value ;
}
if ( not defined $params { 'hostto' } and defined $params { 'ipto' }) {
$params { 'hostto' } = OVH :: Bastion :: ip2host ( $params { 'ipto' }) -> value ;
}
if ( not defined $params { 'hostfrom' } and defined $params { 'ipfrom' }) {
$params { 'hostfrom' } = OVH :: Bastion :: ip2host ( $params { 'ipfrom' }) -> value ;
}
my ( $timestamp , $timestampusec ) = Time :: HiRes :: gettimeofday ();
$params { 'timestamp' } = $timestamp ;
$params { 'timestampusec' } = $timestampusec ;
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# 1/3) log into sql databases if enabled
my @ localtime = localtime ( time ());
2020-10-16 00:32:37 +08:00
my ( $insert_id , $db_name );
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# first, log in account db
my $accountsql = 'no' ;
2020-10-16 00:32:37 +08:00
if ( OVH :: Bastion :: config ( 'enableAccountSqlLog' ) -> value ()) {
2021-01-08 01:11:43 +08:00
if ( $loghome && - d " /home/ $loghome " ) {
2021-01-13 00:23:28 +08:00
my $sqlfile = sprintf ( " /home/%s/%s-log-%04d%02d.sqlite " , $loghome , $remoteaccount || $loghome , $localtime [ 5 ] + 1900 , $localtime [ 4 ] + 1 );
2021-01-08 01:11:43 +08:00
$fnret = _sql_log_insert_file (
% params ,
sqltype => " local " ,
2021-01-13 00:23:28 +08:00
file => $sqlfile
2021-01-08 01:11:43 +08:00
);
if ( $fnret ) {
2021-01-13 00:23:28 +08:00
( $insert_id , $db_name ) = ( $fnret -> value -> { 'id' }, $sqlfile );
2021-01-08 01:11:43 +08:00
$accountsql = 'ok' ;
}
else {
$accountsql = 'error ' . $fnret -> msg ;
}
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
}
else {
2021-01-08 01:11:43 +08:00
warn_syslog ( " Attempted to log to account $account sqlite database without a valid loghome ( $loghome ) " );
2020-10-16 00:32:37 +08:00
}
}
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# then global db
my $globalsql = 'no' ;
if ( OVH :: Bastion :: config ( 'enableGlobalSqlLog' ) -> value ()) {
$fnret = _sql_log_insert_file ( % params , sqltype => " global " , file => sprintf ( " /home/logkeeper/global-log-%04d%02d.sqlite " , $localtime [ 5 ] + 1900 , $localtime [ 4 ] + 1 ));
$globalsql = $fnret ? 'ok' : 'error ' . $fnret -> msg ;
}
# 2/3) push to syslog, we'll also say if any sqlinsert failed through globalsql/accountsql
# even if syslog is disabled, syslogFormatted() will return a oneline $msg that we'll use afterwards
2020-10-16 00:32:37 +08:00
my @ fields = (
[ 'account' , $account ],
[ 'cmdtype' , $params { 'cmdtype' }],
[ 'allowed' , ( $params { 'allowed' } ? 'true' : 'false' )],
[ 'ip_from' , $params { 'ipfrom' }],
[ 'port_from' , $params { 'portfrom' }],
[ 'host_from' , $params { 'hostfrom' }],
[ 'ip_bastion' , $params { 'bastionip' }],
[ 'port_bastion' , $params { 'bastionport' }],
[ 'host_bastion' , $params { 'bastionhost' }],
[ 'user' , $params { 'user' }],
[ 'ip_to' , $params { 'ipto' }],
[ 'port_to' , $params { 'portto' }],
[ 'host_to' , $params { 'hostto' }],
[ 'plugin' , $params { 'plugin' }],
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
[ 'globalsql' , $globalsql ],
[ 'accountsql' , $accountsql ],
2020-10-16 00:32:37 +08:00
[ 'comment' , $params { 'comment' }],
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
[ 'params' , $params { 'params' }],
2020-10-16 00:32:37 +08:00
);
if ( ref $custom eq 'ARRAY' ) {
foreach my $item ( @ $custom ) {
push @ fields , $item if ( ref $item eq 'ARRAY' && @ $item == 2 );
}
}
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
$fnret = syslogFormatted (
2020-10-16 00:32:37 +08:00
criticity => 'info' ,
type => 'open' ,
fields => \ @ fields ,
);
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# if syslogFormatted failed, we'll log its error message (->msg) instead of the message it couldn't assemble (->value)
my $msg = $fnret ? $fnret -> value : $fnret -> msg ;
2020-10-16 00:32:37 +08:00
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# 3/3) push to our own local / global text logs, if enabled, using the $msg returned by syslogFormatted
_write_log ( account => $account , sysaccount => $loghome , remoteaccount => $remoteaccount , msg => $msg );
# save this for log_access_update()
my $cache = {
fields => \ @ fields ,
timestamp => $params { 'timestamp' },
timestampusec => $params { 'timestampusec' },
};
$ENV { 'LOG_CACHE_' . $params { 'uniqid' }} = encode_json ( $cache );
2020-10-16 00:32:37 +08:00
return R ( 'OK' , value => { insert_id => $insert_id , db_name => $db_name , uniq_id => $params { 'uniqid' }});
}
sub _sql_log_update_file {
my % params = @ _ ;
my $file = $params { 'file' };
my $id = $params { 'id' };
my $returnvalue = $params { 'returnvalue' };
my $comment = $params { 'comment' };
my $timestampend = $params { 'timestampend' };
my $timestampendusec = $params { 'timestampendusec' };
my $plugin_stdout = $params { 'plugin_stdout' };
my $plugin_stderr = $params { 'plugin_stderr' };
if ( not $file or not defined $id ) {
return R ( 'ERR_MISSING_PARAMETER' , msg => " Missing required parameter file or id " );
}
if ( !- w $file ) {
return R ( 'ERR_FILE_NOT_FOUND' , msg => " File $file should already exist " );
}
my ( $dbh , $sth , $result , $doing );
# retry block
foreach my $retry ( 0 .. 19 ) {
# if we're retrying, sleep a bit before, to ease concurrency
2020-12-15 18:20:08 +08:00
sleep ( $retry / 50 + rand () / 10 ) if $retry ;
2020-10-16 00:32:37 +08:00
# on each retry, clean those vars (undef $dbh disconnects if connected)
undef $dbh ;
undef $sth ;
undef $result ;
undef $doing ;
# connect to db
$dbh = DBI -> connect ( " dbi:SQLite:dbname= $file " , " " , " " , { PrintError => 0 , RaiseError => 0 });
if ( ! $dbh ) {
$doing = " opening database " ;
next ; # retry
}
my $prepare = " UPDATE connections SET timestampend=?, timestampendusec=?, returnvalue=? " ;
my @ execute = ( $timestampend , $timestampendusec , $returnvalue );
if ( defined $comment ) {
$prepare .= " , comment=? " ;
push @ execute , $comment ;
}
$prepare .= " WHERE id=? AND timestampend IS NULL " ;
push @ execute , $id ;
# prepare insertion on db
$sth = $dbh -> prepare ( $prepare );
if ( ! $sth ) {
$doing = " updating data (prepare) " ;
next ; # retry
}
# execute insertion
$result = $sth -> execute ( @ execute );
if ( ! $result ) {
$doing = " updating data (execute) " ;
next ; # retry
}
# if we're here, it worked, stop retrying
last ;
}
# if this is set, we probably reached max retry in previous loop without succeeding
if ( $DBI :: err ) {
2021-01-08 01:11:43 +08:00
warn_syslog ( " Failed after multiple retries [updating] err $DBI ::err while doing [ $doing ]: $DBI ::errstr ( $file ) " );
2020-10-16 00:32:37 +08:00
return R ( 'ERR_SQL_EXECUTE' , msg => " SQL error [updating] err $DBI ::err while doing [ $doing ]: $DBI ::errstr " );
}
# if we have plugin stdout or stderr, log it too
if ( defined $plugin_stdout or defined $plugin_stderr ) {
# retry block
foreach my $retry ( 0 .. 19 ) {
# if we're retrying, sleep a bit before, to ease concurrency
2020-12-15 18:20:08 +08:00
sleep ( $retry / 50 + rand () / 10 ) if $retry ;
2020-10-16 00:32:37 +08:00
# on each retry, clean those vars (undef $dbh disconnects if connected)
undef $dbh ;
undef $sth ;
undef $result ;
undef $doing ;
# connect to db
$dbh = DBI -> connect ( " dbi:SQLite:dbname= $file " , " " , " " , { PrintError => 0 , RaiseError => 0 });
if ( ! $dbh ) {
$doing = " opening database " ;
next ; # retry
}
$sth = $dbh -> prepare (
2020-12-15 18:20:08 +08:00
<< 'EOS'
CREATE TABLE IF NOT EXISTS plugincalls (
id INTEGER PRIMARY KEY ,
connection_id INTEGER UNIQUE ,
stdout TEXT ,
stderr TEXT )
EOS
2020-10-16 00:32:37 +08:00
);
if ( ! $sth ) {
$doing = " creating plugins table (prepare) " ;
next ; # retry
}
$result = $sth -> execute ();
if ( ! $result ) {
$doing = " creating plugins table (execute) " ;
next ; # retry
}
$sth = $dbh -> prepare ( " INSERT INTO plugincalls (connection_id, stdout, stderr) VALUES (?,?,?) " );
if ( ! $sth ) {
$doing = " inserting plugincall data (prepare) " ;
next ; # retry
}
$result = $sth -> execute ( $id , join ( " \n " , @ { $plugin_stdout || []}), join ( " \n " , @ { $plugin_stderr || []}));
if ( ! $result ) {
$doing = " inserting plugincall data (execute) " ;
next ; # retry
}
# if we're here, it worked, stop retrying
last ;
}
# if this is set, we probably reached max retry in previous loop without succeeding
if ( $DBI :: err ) {
2021-01-08 01:11:43 +08:00
warn_syslog ( " Failed after multiple retries [plugins] err $DBI ::err while doing [ $doing ]: $DBI ::errstr ( $file ) " );
2020-10-16 00:32:37 +08:00
return R ( 'ERR_SQL_EXECUTE' , msg => " SQL error [plugins] err $DBI ::err while doing [ $doing ]: $DBI ::errstr " );
}
}
return R ( 'OK' );
}
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# called by log_access_insert and log_access_update
# don't re-check the validity of our params, this has been done by our callers
sub _write_log {
my % params = @ _ ;
my @ localtime = localtime ( time ());
my @ date = ( $localtime [ 5 ] + 1900 , $localtime [ 4 ] + 1 );
if ( OVH :: Bastion :: config ( 'enableAccountAccessLog' ) -> value () && $params { 'sysaccount' }) {
if ( open ( my $log_acc , " >> " , sprintf ( " /home/%s/%s-log-%04d%02d.log " , $params { 'sysaccount' }, $params { 'remoteaccount' } || $params { 'sysaccount' }, @ date ))) {
print $log_acc localtime () . " " . $params { 'msg' } . " \n " ;
close ( $log_acc );
}
}
if ( OVH :: Bastion :: config ( 'enableGlobalAccessLog' ) -> value ()) {
2021-01-12 17:47:38 +08:00
my $file = sprintf ( " /home/logkeeper/global-log-%04d%02d.log " , @ date );
# if it doesn't exist yet, create the file ourselves, chgrp it to bastion-users and set the
# proper rights (0660). if the file already exists, we won't do anything.
OVH :: Bastion :: create_file_if_not_exists ( file => $file , perms => oct ( 660 ), group => " bastion-users " );
if ( open ( my $log_gen , " >> " , $file )) {
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
print $log_gen localtime () . " " . $params { 'msg' } . " \n " ;
close ( $log_gen );
}
}
return ;
}
2020-10-16 00:32:37 +08:00
sub log_access_update {
my % params = @ _ ;
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
my $account = $params { 'account' };
2020-10-16 00:32:37 +08:00
my $insert_id = $params { 'insert_id' };
my $db_name = $params { 'db_name' };
my $uniq_id = $params { 'uniq_id' };
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
my $fnret ;
# if we get a valid account, we'll try to log to its files, if enabled.
# if not, or if its invalid, still try to log what we can (hence don't return here)
my ( $remoteaccount , $sysaccount );
if ( defined $account ) {
if ( $fnret = OVH :: Bastion :: is_bastion_account_valid_and_existing ( account => $account )) {
$account = $fnret -> value -> { 'account' };
$sysaccount = $fnret -> value -> { 'sysaccount' };
$remoteaccount = $fnret -> value -> { 'remoteaccount' };
}
else {
undef $account ;
}
}
2020-10-16 00:32:37 +08:00
if ( not defined $params { 'timestampend' } or not defined $params { 'timestampendusec' }) {
( $params { 'timestampend' }, $params { 'timestampendusec' }) = Time :: HiRes :: gettimeofday ();
}
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# retrieve info from the corresponding "open" log so that we can include it to our log
my @ comments = $params { 'comment' } ? split ( " " , $params { 'comment' }) : ();
my @ fields ;
my $duration = - 1 ;
if ( exists $ENV { 'LOG_CACHE_' . $params { 'uniq_id' }}) {
my $cache ;
eval { $cache = decode_json ( delete $ENV { 'LOG_CACHE_' . $params { 'uniq_id' }}); };
if ( $cache ) {
@ fields = @ { $cache -> { 'fields' }};
$duration = ( $params { 'timestampend' } + $params { 'timestampendusec' } / 1_000_000 ) - ( $cache -> { 'timestamp' } + $cache -> { 'timestampusec' } / 1_000_000 );
}
}
if ( !@ fields ) {
push @ comments , " missing_open_log_info " ;
}
my $signal = '' ;
my $status = '' ;
if ( defined $params { 'returnvalue' }) {
$fnret = OVH :: Bastion :: sysret2human ( $params { 'returnvalue' });
if ( $fnret && $fnret -> value ) {
$signal = $fnret -> value -> { 'signal' };
$status = $fnret -> value -> { 'status' };
}
}
else {
# sql logs expect a returnvalue, so set one if we don't have any
$params { 'returnvalue' } = - 1 ;
}
# if an explicit signal value has been passed, use it
if ( $params { 'signal' }) {
$signal = delete $params { 'signal' };
}
push @ fields , [ 'sysret' , $status ], [ 'signal' , $signal ], [ 'comment_close' , join ( " " , @ comments )], [ 'duration' , sprintf ( " %.3f " , $duration )];
# 1/3) update the account sql db if enabled. note that the global sql db is NOT updated, as
# it contains less details that the account databases and doesn't have any field that requires
# to wait the end of a connection to be filled.
2021-01-13 00:23:28 +08:00
my $accountsql = 'no' ;
if ( OVH :: Bastion :: config ( 'enableAccountSqlLog' ) -> value ()) {
if ( $insert_id && $db_name ) {
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
$fnret = _sql_log_update_file ( % params , file => $db_name , id => $insert_id );
$accountsql = $fnret ? 'ok' : 'error ' . $fnret -> msg ;
}
else {
2021-01-13 00:23:28 +08:00
$accountsql = 'missing' ;
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
}
}
# 2/3) push to syslog, we'll also say if the sql update failed through accountsql
# even if syslog is disabled, syslogFormatted() will return a oneline $msg that we'll use afterwards
push @ fields , [ accountsql_close => $accountsql ];
$fnret = syslogFormatted (
2020-10-16 00:32:37 +08:00
criticity => 'info' ,
type => 'close' ,
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
fields => \ @ fields ,
2020-10-16 00:32:37 +08:00
);
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# if syslogFormatted failed, we'll log its error message (->msg) instead of the message it couldn't assemble (->value)
my $msg = $fnret ? $fnret -> value : $fnret -> msg ;
2020-10-16 00:32:37 +08:00
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# 3/3) push to our own local / global text logs, if enabled, using the $msg returned by syslogFormatted
_write_log ( account => $account , sysaccount => $sysaccount , remoteaccount => $remoteaccount , msg => $msg );
2020-10-16 00:32:37 +08:00
return R ( 'OK' );
}
sub log_access_get {
my % params = @ _ ;
my $account = $params { 'account' };
my $fnret ;
if ( not defined $account ) {
return R ( 'ERR_MISSING_PARAMETER' , msg => " Missing parameter 'account' " );
}
$fnret = OVH :: Bastion :: is_bastion_account_valid_and_existing ( account => $account );
$fnret or return $fnret ;
$account = $fnret -> value -> { 'account' };
$params { 'account' } = $account ;
my ( $timestamp , $timestampusec ) = Time :: HiRes :: gettimeofday ();
$params { 'timestamp' } = $timestamp ;
$params { 'timestampusec' } = $timestampusec ;
my @ localtime = localtime ( time ());
feat: revamp logs
All connections and plugin executions emit two logs, an 'open' and
a 'close' log. We now add all the details of the connection to
the 'close' logs, those that were previously only available in the
corresponding 'open' log. This way, it is no longer required to
correlate both logs with their uniqid to have all the data:
the 'close' log should suffice. The 'open' log is still there if
for some reason the 'close' log can't be emitted (kill -9, system
crash, etc.), or if the 'open' and the 'close' log are several
hours, days or months appart.
An additional field "duration" has been added to the 'close' logs,
this represents the number of seconds (with millisecond precision)
the connection lasted.
Two new fields "globalsql" and "accountsql" have been added to the
'open'-type logs. These will contain either "ok" if we successfully
logged to the corresponding log database, "no" if it is disabled,
or "error $aDetailedMessage" if we got an error trying to insert
the row. The 'close'-type log also has the new "accountsql_close"
field, but misses the "globalsql_close" field as we never update
the global database on this event. On the 'close' log, we can also
have the value "missing", indicating that we couldn't update the
access log row in the database, as the corresponding 'open' log
couldn't insert it.
The "ttyrecsize" log field for the 'close'-type logs has been removed,
as it was never completely implemented, and contains bogus data if
ttyrec log rotation occurs. It has also been removed from the sqlite
log databases.
The 'open' and 'close' events are now pushed to our own log files,
in addition to syslog, if logging to those files is enabled (see
``enableGlobalAccesssLog`` and ``enableAccountAccessLog``), previously
the 'close' events were only pushed to syslog.
The /home/osh.log is no longer used for ``enableGlobalAccessLog``, the
global log is instead written to /home/logkeeper/global-log-YYYYMM.log.
The global sql file, enabled with ``enableGlobalSqlLog``, is now
split by year-month instead of by year, to
/home/logkeeper/global-log-YYYYMM.sqlite.
2020-12-15 23:39:20 +08:00
# check in account sql file
2020-10-16 00:32:37 +08:00
if ( OVH :: Bastion :: config ( 'enableAccountSqlLog' ) -> value ()) {
2020-12-15 18:20:08 +08:00
$params { 'file' } = sprintf ( " /home/%s/%s-log-%04d%02d.sqlite " , $params { 'account' }, $params { 'account' }, $localtime [ 5 ] + 1900 , $localtime [ 4 ] + 1 );
$params { 'sqltype' } = 'local' ;
$fnret = _sql_log_fetch_from_file ( % params );
2020-10-16 00:32:37 +08:00
return $fnret ;
}
return R ( 'ERR_DISABLED' , msg => " This feature is not available " );
}
sub _sql_log_fetch_from_file {
# don't call me directly, use log_access_get() !
my % params = @ _ ;
my $file = $params { 'file' };
my $sqltype = $params { 'sqltype' };
my $limit = $params { 'limit' };
my $uniqid = $params { 'uniqid' };
my $account = $params { 'account' };
my $cmdtype = $params { 'cmdtype' };
my $allowed = $params { 'allowed' };
my $ipfrom = $params { 'ipfrom' };
my $ipto = $params { 'ipto' };
my $portto = $params { 'portto' };
my $bastionip = $params { 'bastionip' };
my $bastionport = $params { 'bastionport' };
my $user = $params { 'user' };
my $plugin = $params { 'plugin' };
my $before = $params { 'before' };
my $after = $params { 'after' };
foreach my $param ( qw { limit before after }) {
if ( defined $params { $param } and $params { $param } !~ /^ \d + $ / ) {
return R ( 'ERR_INVALID_PARAMETER' , msg => " Expected a numeric $param " );
}
}
my @ conditions ;
my @ execvalues ;
if ( $account ) {
push @ conditions , " account = ? " ;
push @ execvalues , $account ;
}
if ( $cmdtype ) {
push @ conditions , " cmdtype = ? " ;
push @ execvalues , $cmdtype ;
}
if ( defined $allowed ) {
push @ conditions , " allowed = ? " ;
push @ execvalues , $allowed ? 1 : 0 ;
}
if ( $uniqid ) {
push @ conditions , " uniqid = ? " ;
push @ execvalues , $uniqid ;
}
if ( $user ) {
push @ conditions , " user = ? " ;
push @ execvalues , $user ;
}
if ( $plugin ) {
push @ conditions , " plugin = ? " ;
push @ execvalues , $plugin ;
}
if ( $ipto ) {
push @ conditions , " ipto = ? " ;
push @ execvalues , $ipto ;
}
if ( $portto ) {
push @ conditions , " portto = ? " ;
push @ execvalues , $portto ;
}
if ( $ipfrom ) {
push @ conditions , " ipfrom = ? " ;
push @ execvalues , $ipfrom ;
}
if ( $bastionip ) {
push @ conditions , " bastionip = ? " ;
push @ execvalues , $bastionip ;
}
if ( $bastionport ) {
push @ conditions , " bastionport = ? " ;
push @ execvalues , $bastionport ;
}
if ( $before ) {
push @ conditions , " timestamp <= ? " ;
push @ execvalues , $before ;
}
if ( $after ) {
push @ conditions , " timestamp >= ? " ;
push @ execvalues , $after ;
}
my $tablename ;
if ( $sqltype eq 'local' ) {
$tablename = 'connections' ;
}
elsif ( $sqltype eq 'global' ) {
$tablename = 'connections_summary' ;
}
else {
return R ( 'ERR_INVALID_PARAMETER' , msg => " Unknown sqltype " );
}
my $prepare = " SELECT * FROM $tablename WHERE ( " . join ( ') AND (' , @ conditions ) . " ) ORDER BY id DESC " ;
if ( $limit ) {
$prepare .= " LIMIT ? " ;
push @ execvalues , $limit ;
}
my $openflags = { PrintError => 0 , RaiseError => 0 };
if ( $DBD :: SQLite :: VERSION ge '1.42' ) {
eval { $openflags -> { 'sqlite_open_flags' } = DBD :: SQLite :: OPEN_READONLY (); };
}
# big db-related retry block (see comment on _sql_log_insert_file)
my ( $dbh , $result , $doing );
foreach my $retry ( 0 .. 19 ) {
# if we're retrying, sleep a bit before, to ease concurrency
2020-12-15 18:20:08 +08:00
sleep ( $retry / 50 + rand () / 10 ) if $retry ;
2020-10-16 00:32:37 +08:00
# on each retry, clean those vars (undef $dbh disconnects if connected)
undef $dbh ;
undef $result ;
undef $doing ;
# connect to db
$dbh = DBI -> connect ( " dbi:SQLite:dbname= $file " , " " , " " , $openflags );
if ( ! $dbh ) {
$doing = " opening database " ;
next ; # retry
}
# fetch data
$result = $dbh -> selectall_hashref ( $prepare , 'id' , {}, @ execvalues );
if ( ! $result ) {
$doing = " querying data " ;
next ; # retry
}
# if we're here, it worked, stop retrying
last ;
}
# if this is set, we probably reached max retry in previous loop without succeeding
if ( $DBI :: err ) {
2021-01-08 01:11:43 +08:00
warn_syslog ( " Failed after multiple retries [ $sqltype ] err $DBI ::err while doing [ $doing ]: $DBI ::errstr ( $file ) " );
2020-10-16 00:32:37 +08:00
return R ( 'ERR_SQL_EXECUTE' , msg => " SQL error [ $sqltype ] err $DBI ::err while doing [ $doing ]: $DBI ::errstr " );
}
return R ( 'OK' , value => $result );
}
1 ;