#!/usr/bin/perl use IPC::Open3; use MIME::Base64; use Getopt::Long; use Pod::Usage; my $initsign=""; my $sign_max_lines=50; my $sign_max_time=30*60; my $key="/etc/ssl/private/logsign.pem"; my $output = undef; my $fd = \*STDOUT; my $initfile = undef; my $continue = 1; my $help = 0; my $logger = undef; GetOptions ("file=s" => \$output, "lastsig=s" => \$initsign, "loggercmd=s" => \$logger, "initfile=s" => \$initfile, "key=s" => \$key, "blocksize=i" => \$sign_max_lines, "timestamp=i" => \$sign_max_time, "help" => \$help, ); pod2usage(1) if($help); if(defined $output) { open($fd, ">>$output") or die ("Can't write to $output\n"); } if($initsign ne "") { $initsign .= "\n"; } if(defined $initfile) { $initsign = `tail -n1 $initfile`; } my $buffer = ""; my $lastsign = $initsign; $SIG{ALRM} = sub { sign(); alarm $sign_max_time; }; $SIG{HUP} = sub { sign(); if(defined $output) { close($fd); open($fd, ">>$output") or die ("Can't write to $output\n"); } }; alarm $sign_max_time; my $i=1; while(my $line=<>) { if(defined $logger) { system("$logger $line"); } else { print $fd $line; } $buffer .= $line; if($i++ % $sign_max_lines == 0) { sign(); } } sign(); sub sign { my ($stdin, $stdout, $stderr); my $pid = open3($stdin, $stdout, $stderr, "openssl dgst -sha512 -sign $key"); print $stdin $lastsign; print $stdin $buffer; close($stdin); local $/; my $signature = <$stdout>; my $error = <$stderr>; waitpid( $pid, 0 ); my $status = $?; if($status != 0) { print STDERR "ERROR : $error $signature\n"; print $buffer; print "--- signature error ---\n"; exit $status; } $buffer = ""; my $base64 = encode_base64($signature, ''); $lastsign = "--- DGST/SHA512/base64 --- $base64\n"; if(defined $logger) { system("$logger $lastsign"); } else { print $fd $lastsign; } } __END__ =head1 NAME signlogger - logger pipe used for signed syslog =head1 SYNOPSIS signlogger [options] Options --key|-k keyfile Use private key for signing (PEM encoded) --file|-f file Log to file instead of stdout --lastsig|-l line Use line as last signature (used for chaining) --initfile|-i file Use last line of file as last signature (used for chaining) --blocksize|-b lines Insert signature each "lines" count (default 50) --timestamp|-t seconds Insert signature each "seconds" seconds (default 30mn) --help|-h This help =head1 DESCRIPTION This program will read one line at a time and write it to stdout. Each time a limit (time or number of lines) is reached, a signature is inserted. The signature use public key cryptography to enable checking validity using only the corresponding public key. The resulting logs can't be modified without breaking its signature. Each signed block use the preceeding block as first line so that removing an entire bloc breaks the signature. Each log file can use the preceeding file's last line so that removing an entire file breaks the signature too. Please be aware that anybody who can access the private key can sign any content. To avoid it, you can use a Hardware Security Module or send the logs to secure place such as a syslog server or an unerasable medium. This program can be used with logrotate. If it receive a SIGHUP it will insert a signature line. And if an output file has been given, it will be reopened. =head1 SEE ALSO B Author: Benoit Peccatte (http://linux-attitude.fr) License : GPLv2 =cut